mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03:00
Merge branch 'feat/nan_gtk_support' into 'master'
feat(wifi_aware): advertise group data/mgmt protection (GTK/IGTK/BIGTK) and iOS compliance fixes See merge request espressif/esp-idf!49800
This commit is contained in:
@@ -67,8 +67,8 @@ typedef struct {
|
|||||||
uint16_t csid_bitmap; /**< Selected Cipher Suite ID bit (WIFI_NAN_CSID_BIT_*) */
|
uint16_t csid_bitmap; /**< Selected Cipher Suite ID bit (WIFI_NAN_CSID_BIT_*) */
|
||||||
uint8_t nd_pmk[ESP_WIFI_NAN_NDP_PMK_LEN]; /**< ND-PMK */
|
uint8_t nd_pmk[ESP_WIFI_NAN_NDP_PMK_LEN]; /**< ND-PMK */
|
||||||
uint8_t nd_pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN]; /**< ND-PMKID */
|
uint8_t nd_pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN]; /**< ND-PMKID */
|
||||||
uint8_t group_data_prot: 1; /**< Group addressed data frame protection. Reserved: not supported right now. */
|
uint8_t group_data_prot: 1; /**< Group addressed data frame protection (GTKSA): distribute a GTK on the secured NDP so multicast data frames are protected. */
|
||||||
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection. Reserved: not supported right now. */
|
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection (IGTKSA/BIGTKSA): BIP-protect multicast SDFs and Beacons. */
|
||||||
uint8_t reserved: 6; /**< Reserved */
|
uint8_t reserved: 6; /**< Reserved */
|
||||||
} wifi_nan_security_params_t;
|
} wifi_nan_security_params_t;
|
||||||
|
|
||||||
@@ -89,10 +89,11 @@ typedef struct {
|
|||||||
uint8_t num_pmkids; /**< Number of parsed PMKIDs */
|
uint8_t num_pmkids; /**< Number of parsed PMKIDs */
|
||||||
uint8_t pmkids[NAN_PEER_MAX_PMKIDS][ESP_WIFI_NAN_NDP_PMKID_LEN]; /**< Parsed ND-PMKIDs */
|
uint8_t pmkids[NAN_PEER_MAX_PMKIDS][ESP_WIFI_NAN_NDP_PMKID_LEN]; /**< Parsed ND-PMKIDs */
|
||||||
uint8_t group_data_prot: 1; /**< Peer advertises group data frame protection */
|
uint8_t group_data_prot: 1; /**< Peer advertises group data frame protection */
|
||||||
uint8_t group_mgmt_prot: 1; /**< Peer advertises group mgmt frame protection */
|
uint8_t group_mgmt_prot: 1; /**< Peer advertises IGTKSA (CSIA caps bits 1-2 != 0) */
|
||||||
uint8_t pairing_setup: 1; /**< Pairing setup: 0 - disabled, 1 - enabled */
|
uint8_t pairing_setup: 1; /**< Pairing setup: 0 - disabled, 1 - enabled */
|
||||||
uint8_t npk_nik_caching: 1; /**< NPK/NIK caching: 0 - disabled, 1 - enabled (valid if pairing_setup) */
|
uint8_t npk_nik_caching: 1; /**< NPK/NIK caching: 0 - disabled, 1 - enabled (valid if pairing_setup) */
|
||||||
uint8_t reserved: 4; /**< Reserved */
|
uint8_t group_bigtk_prot: 1; /**< Peer advertises BIGTKSA (CSIA caps bits 1-2 == 10) */
|
||||||
|
uint8_t reserved: 3; /**< Reserved */
|
||||||
} wifi_nan_peer_sdf_security_t;
|
} wifi_nan_peer_sdf_security_t;
|
||||||
|
|
||||||
/* NAN Peer info parsed from SDF */
|
/* NAN Peer info parsed from SDF */
|
||||||
@@ -230,9 +231,11 @@ struct nan_secure_dp_funcs {
|
|||||||
* with the given Key Data field length. */
|
* with the given Key Data field length. */
|
||||||
uint32_t (*get_shared_key_desc_attr_len)(uint16_t key_data_len);
|
uint32_t (*get_shared_key_desc_attr_len)(uint16_t key_data_len);
|
||||||
|
|
||||||
/* Byte length of the M4 Shared Key Descriptor including any
|
/* Exact byte length of the M4 (NDP Security Install) Shared Key Descriptor
|
||||||
* encrypted KDE payload (GTK/IGTK/BIGTK). */
|
* attribute for this NDP, including any encrypted KDE payload (GTK/IGTK/BIGTK).
|
||||||
int (*ndp_security_install_get_shared_desc_len)(void);
|
* @ndp_id + @peer_nmi identify the NDL so the host returns the exact length the
|
||||||
|
* builder will write (no over-reservation / on-air zero-padding). */
|
||||||
|
int (*ndp_security_install_get_shared_desc_len)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||||
|
|
||||||
uint8_t (*get_ndp_resp_num_pmkids)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
uint8_t (*get_ndp_resp_num_pmkids)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||||
uint32_t (*get_ndp_resp_shared_key_desc_len)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
uint32_t (*get_ndp_resp_shared_key_desc_len)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||||
@@ -362,10 +365,21 @@ struct nan_secure_dp_funcs {
|
|||||||
const wifi_nan_discovery_security_params_t *sec_cfg,
|
const wifi_nan_discovery_security_params_t *sec_cfg,
|
||||||
wifi_nan_security_params_t *out_derived);
|
wifi_nan_security_params_t *out_derived);
|
||||||
|
|
||||||
/* Returns the cipher-suite bitmap negotiated for an in-flight NDP,
|
/* Returns the full cipher-suite bitmap negotiated for an in-flight NDP
|
||||||
* or 0 if the NDP is open. Used by RX/TX paths to decide whether
|
* (including the group cipher NCS-GTK when group-addressed data protection
|
||||||
* to apply CCMP/GCMP and which key length to use. */
|
* is in use), or 0 if the NDP is open. The blob treats this as an opaque
|
||||||
|
* value passed straight to the host CSIA callbacks (construct_csia /
|
||||||
|
* get_csia_len) to build the on-air M1/M3 CSIA own-bitmap; it must NOT
|
||||||
|
* interpret individual CSID bits. Pairwise cipher selection and key install
|
||||||
|
* are host-driven and independent of this value. */
|
||||||
uint16_t (*get_ndp_security_csid)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
uint16_t (*get_ndp_security_csid)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||||
|
|
||||||
|
/* Exact byte length of the M3 (NDP Confirm) Shared Key Descriptor attribute for
|
||||||
|
* this NDP, including any encrypted KDE payload (GTK/IGTK/BIGTK). @ndp_id +
|
||||||
|
* @peer_nmi identify the NDL. Mirrors ndp_security_install_get_shared_desc_len
|
||||||
|
* for the initiator path. Appended at the end of the struct to preserve the
|
||||||
|
* layout of the fields above. */
|
||||||
|
int (*ndp_confirm_get_shared_desc_len)(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1195,7 +1209,7 @@ esp_err_t esp_wifi_disconnect_internal(void);
|
|||||||
uint32_t esp_nan_get_nira_len(void);
|
uint32_t esp_nan_get_nira_len(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct dummy NAN Identity Resolution Attribute (NIRA)
|
* @brief Construct NAN Identity Resolution Attribute (NIRA)
|
||||||
*
|
*
|
||||||
* @param[out] frm Buffer to write the attribute to
|
* @param[out] frm Buffer to write the attribute to
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -81,6 +81,42 @@ bool esp_wifi_is_if_ready_when_started(wifi_netif_driver_t ifx);
|
|||||||
*/
|
*/
|
||||||
esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t fn, void * arg);
|
esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t fn, void * arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Derive an IPv6 link-local address from a link-layer (MAC) address
|
||||||
|
*
|
||||||
|
* Computes fe80::/64 combined with the EUI-64 form of the given MAC (the 802
|
||||||
|
* group bit complemented) into an esp_ip6_addr_t (zone 0). Interface-agnostic.
|
||||||
|
*
|
||||||
|
* @param[out] ip6 destination, set to the derived IPv6 link-local address
|
||||||
|
* @param[in] mac source link-layer (MAC) address (6 bytes)
|
||||||
|
*/
|
||||||
|
void esp_wifi_netif_get_ip6_linklocal_from_mac(esp_ip6_addr_t *ip6, const uint8_t mac[6]);
|
||||||
|
|
||||||
|
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||||
|
/**
|
||||||
|
* @brief Pin (or remove) a static IPv6 link-local neighbor mapping on a wifi netif
|
||||||
|
*
|
||||||
|
* Installs a fixed link-local IPv6 -> MAC mapping for a peer reachable on the
|
||||||
|
* given wifi interface so that traffic to the peer bypasses Neighbor Discovery
|
||||||
|
* (no NS/NA exchanged), or removes a previously installed one. This layer owns
|
||||||
|
* both the netif lookup (from the interface type) and the derivation of the
|
||||||
|
* peer's link-local address from its MAC, so the caller only supplies the
|
||||||
|
* interface and the peer MAC. Only available when lwIP static ND6 entries are
|
||||||
|
* enabled.
|
||||||
|
*
|
||||||
|
* @param[in] wifi_if wifi interface the peer is reachable on
|
||||||
|
* @param[in] mac peer's link-layer (MAC) address
|
||||||
|
* @param[in] add true to add the mapping, false to remove it
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG if mac is NULL or wifi_if is out of range
|
||||||
|
* - ESP_ERR_INVALID_STATE if the interface's netif is not up
|
||||||
|
* - error code from the underlying esp_netif call otherwise
|
||||||
|
*/
|
||||||
|
esp_err_t esp_wifi_netif_set_static_neighbor(wifi_interface_t wifi_if, const uint8_t mac[6], bool add);
|
||||||
|
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -606,6 +606,7 @@ typedef struct {
|
|||||||
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
|
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
|
||||||
bool reset_current_nvs_creds; /**< Erase all NAN credentials (own NIK and cached peer NIK/NPK entries) saved in NVS before starting. */
|
bool reset_current_nvs_creds; /**< Erase all NAN credentials (own NIK and cached peer NIK/NPK entries) saved in NVS before starting. */
|
||||||
bool use_nvs_for_caching; /**< Persist newly-learned peer credentials (NIK/NPK) to NVS so they survive across reboots. */
|
bool use_nvs_for_caching; /**< Persist newly-learned peer credentials (NIK/NPK) to NVS so they survive across reboots. */
|
||||||
|
bool group_mgmt_prot; /**< Device-global group management protection (IGTKSA/BIGTKSA): BIP-protect Beacons + multicast SDFs. Forces GTKSA on all secured services (CSIA caps cannot encode IGTK/BIGTK without GTKSA). */
|
||||||
} wifi_nan_sync_config_t;
|
} wifi_nan_sync_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -932,9 +933,9 @@ typedef enum {
|
|||||||
WIFI_NAN_CSID_NCS_SK_256 = 2, /**< NCS-SK-256 (PSK/Passphrase). Reserved: not supported right now. */
|
WIFI_NAN_CSID_NCS_SK_256 = 2, /**< NCS-SK-256 (PSK/Passphrase). Reserved: not supported right now. */
|
||||||
WIFI_NAN_CSID_NCS_PK_2WDH_128 = 3, /**< NCS-PK-2WDH-128. Reserved: not supported right now. */
|
WIFI_NAN_CSID_NCS_PK_2WDH_128 = 3, /**< NCS-PK-2WDH-128. Reserved: not supported right now. */
|
||||||
WIFI_NAN_CSID_NCS_PK_2WDH_256 = 4, /**< NCS-PK-2WDH-256. Reserved: not supported right now. */
|
WIFI_NAN_CSID_NCS_PK_2WDH_256 = 4, /**< NCS-PK-2WDH-256. Reserved: not supported right now. */
|
||||||
WIFI_NAN_CSID_NCS_GTK_CCM_128 = 5,
|
WIFI_NAN_CSID_NCS_GTK_CCMP_128 = 5, /**< NCS-GTK-CCMP-128, the group-data cipher (GTKSA). Selected internally when group_data_prot is set; not user-selectable via csid_bitmap. */
|
||||||
WIFI_NAN_CSID_NCS_GTK_GCM_256 = 6,
|
WIFI_NAN_CSID_NCS_GTK_GCMP_256 = 6, /**< NCS-GTK-GCMP-256. Reserved: not supported right now. */
|
||||||
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128. Reserved: not supported right now. */
|
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128 (NAN Pairing). Requires CONFIG_ESP_WIFI_NAN_PAIRING and the Wi-Fi Aware component (esp-wifi-apps); not usable with stand-alone ESP-IDF. */
|
||||||
WIFI_NAN_CSID_NCS_PK_PASN_256 = 8, /**< NCS-PK-PASN-256. Reserved: not supported right now. */
|
WIFI_NAN_CSID_NCS_PK_PASN_256 = 8, /**< NCS-PK-PASN-256. Reserved: not supported right now. */
|
||||||
} wifi_nan_cipher_suite_id_t;
|
} wifi_nan_cipher_suite_id_t;
|
||||||
|
|
||||||
@@ -942,8 +943,8 @@ typedef enum {
|
|||||||
#define WIFI_NAN_CSID_BIT_NCS_SK_256 (1 << WIFI_NAN_CSID_NCS_SK_256)
|
#define WIFI_NAN_CSID_BIT_NCS_SK_256 (1 << WIFI_NAN_CSID_NCS_SK_256)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_128 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_128)
|
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_128 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_128)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_256 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_256)
|
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_256 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_256)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCM_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCM_128)
|
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCMP_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCMP_128)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCM_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCM_256)
|
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCMP_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCMP_256)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_128 (1 << WIFI_NAN_CSID_NCS_PK_PASN_128)
|
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_128 (1 << WIFI_NAN_CSID_NCS_PK_PASN_128)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_256 (1 << WIFI_NAN_CSID_NCS_PK_PASN_256)
|
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_256 (1 << WIFI_NAN_CSID_NCS_PK_PASN_256)
|
||||||
|
|
||||||
@@ -974,8 +975,8 @@ typedef struct {
|
|||||||
* is computed by the stack as the union of each credential's @c csid.
|
* is computed by the stack as the union of each credential's @c csid.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t group_data_prot: 1; /**< Group addressed data frame protection. Reserved: not supported right now. */
|
uint8_t group_data_prot: 1; /**< Group addressed data frame protection (GTKSA): distribute a GTK on the secured NDP so multicast data frames are protected. */
|
||||||
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection. Reserved: not supported right now. */
|
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection (IGTKSA/BIGTKSA): BIP-protect multicast SDFs and Beacons. */
|
||||||
uint8_t reserved: 6; /**< Reserved */
|
uint8_t reserved: 6; /**< Reserved */
|
||||||
uint8_t num_credentials; /**< Number of valid entries in @c creds (0..ESP_WIFI_NAN_MAX_CREDS_PER_SVC). 0 = open service. */
|
uint8_t num_credentials; /**< Number of valid entries in @c creds (0..ESP_WIFI_NAN_MAX_CREDS_PER_SVC). 0 = open service. */
|
||||||
wifi_nan_credential_t creds[ESP_WIFI_NAN_MAX_CREDS_PER_SVC]; /**< Credentials list. */
|
wifi_nan_credential_t creds[ESP_WIFI_NAN_MAX_CREDS_PER_SVC]; /**< Credentials list. */
|
||||||
|
|||||||
+1
-1
Submodule components/esp_wifi/lib updated: 5c5338ebbd...c9950ae98a
@@ -81,6 +81,42 @@ bool esp_wifi_is_if_ready_when_started(wifi_netif_driver_t ifx);
|
|||||||
*/
|
*/
|
||||||
esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t fn, void * arg);
|
esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t fn, void * arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Derive an IPv6 link-local address from a link-layer (MAC) address
|
||||||
|
*
|
||||||
|
* Computes fe80::/64 combined with the EUI-64 form of the given MAC (the 802
|
||||||
|
* group bit complemented) into an esp_ip6_addr_t (zone 0). Interface-agnostic.
|
||||||
|
*
|
||||||
|
* @param[out] ip6 destination, set to the derived IPv6 link-local address
|
||||||
|
* @param[in] mac source link-layer (MAC) address (6 bytes)
|
||||||
|
*/
|
||||||
|
void esp_wifi_netif_get_ip6_linklocal_from_mac(esp_ip6_addr_t *ip6, const uint8_t mac[6]);
|
||||||
|
|
||||||
|
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||||
|
/**
|
||||||
|
* @brief Pin (or remove) a static IPv6 link-local neighbor mapping on a wifi netif
|
||||||
|
*
|
||||||
|
* Installs a fixed link-local IPv6 -> MAC mapping for a peer reachable on the
|
||||||
|
* given wifi interface so that traffic to the peer bypasses Neighbor Discovery
|
||||||
|
* (no NS/NA exchanged), or removes a previously installed one. This layer owns
|
||||||
|
* both the netif lookup (from the interface type) and the derivation of the
|
||||||
|
* peer's link-local address from its MAC, so the caller only supplies the
|
||||||
|
* interface and the peer MAC. Only available when lwIP static ND6 entries are
|
||||||
|
* enabled.
|
||||||
|
*
|
||||||
|
* @param[in] wifi_if wifi interface the peer is reachable on
|
||||||
|
* @param[in] mac peer's link-layer (MAC) address
|
||||||
|
* @param[in] add true to add the mapping, false to remove it
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG if mac is NULL or wifi_if is out of range
|
||||||
|
* - ESP_ERR_INVALID_STATE if the interface's netif is not up
|
||||||
|
* - error code from the underlying esp_netif call otherwise
|
||||||
|
*/
|
||||||
|
esp_err_t esp_wifi_netif_set_static_neighbor(wifi_interface_t wifi_if, const uint8_t mac[6], bool add);
|
||||||
|
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -606,6 +606,7 @@ typedef struct {
|
|||||||
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
|
bool disable_random_mac;/**< Disable the MAC Randomisation in NAN */
|
||||||
bool reset_current_nvs_creds; /**< Erase all NAN credentials (own NIK and cached peer NIK/NPK entries) saved in NVS before starting. */
|
bool reset_current_nvs_creds; /**< Erase all NAN credentials (own NIK and cached peer NIK/NPK entries) saved in NVS before starting. */
|
||||||
bool use_nvs_for_caching; /**< Persist newly-learned peer credentials (NIK/NPK) to NVS so they survive across reboots. */
|
bool use_nvs_for_caching; /**< Persist newly-learned peer credentials (NIK/NPK) to NVS so they survive across reboots. */
|
||||||
|
bool group_mgmt_prot; /**< Device-global group management protection (IGTKSA/BIGTKSA): BIP-protect Beacons + multicast SDFs. Forces GTKSA on all secured services (CSIA caps cannot encode IGTK/BIGTK without GTKSA). */
|
||||||
} wifi_nan_sync_config_t;
|
} wifi_nan_sync_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -932,9 +933,9 @@ typedef enum {
|
|||||||
WIFI_NAN_CSID_NCS_SK_256 = 2, /**< NCS-SK-256 (PSK/Passphrase). Reserved: not supported right now. */
|
WIFI_NAN_CSID_NCS_SK_256 = 2, /**< NCS-SK-256 (PSK/Passphrase). Reserved: not supported right now. */
|
||||||
WIFI_NAN_CSID_NCS_PK_2WDH_128 = 3, /**< NCS-PK-2WDH-128. Reserved: not supported right now. */
|
WIFI_NAN_CSID_NCS_PK_2WDH_128 = 3, /**< NCS-PK-2WDH-128. Reserved: not supported right now. */
|
||||||
WIFI_NAN_CSID_NCS_PK_2WDH_256 = 4, /**< NCS-PK-2WDH-256. Reserved: not supported right now. */
|
WIFI_NAN_CSID_NCS_PK_2WDH_256 = 4, /**< NCS-PK-2WDH-256. Reserved: not supported right now. */
|
||||||
WIFI_NAN_CSID_NCS_GTK_CCM_128 = 5,
|
WIFI_NAN_CSID_NCS_GTK_CCMP_128 = 5, /**< NCS-GTK-CCMP-128, the group-data cipher (GTKSA). Selected internally when group_data_prot is set; not user-selectable via csid_bitmap. */
|
||||||
WIFI_NAN_CSID_NCS_GTK_GCM_256 = 6,
|
WIFI_NAN_CSID_NCS_GTK_GCMP_256 = 6, /**< NCS-GTK-GCMP-256. Reserved: not supported right now. */
|
||||||
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128. Reserved: not supported right now. */
|
WIFI_NAN_CSID_NCS_PK_PASN_128 = 7, /**< NCS-PK-PASN-128 (NAN Pairing). Requires CONFIG_WIFI_RMT_NAN_PAIRING and the Wi-Fi Aware component (esp-wifi-apps); not usable with stand-alone ESP-IDF. */
|
||||||
WIFI_NAN_CSID_NCS_PK_PASN_256 = 8, /**< NCS-PK-PASN-256. Reserved: not supported right now. */
|
WIFI_NAN_CSID_NCS_PK_PASN_256 = 8, /**< NCS-PK-PASN-256. Reserved: not supported right now. */
|
||||||
} wifi_nan_cipher_suite_id_t;
|
} wifi_nan_cipher_suite_id_t;
|
||||||
|
|
||||||
@@ -942,8 +943,8 @@ typedef enum {
|
|||||||
#define WIFI_NAN_CSID_BIT_NCS_SK_256 (1 << WIFI_NAN_CSID_NCS_SK_256)
|
#define WIFI_NAN_CSID_BIT_NCS_SK_256 (1 << WIFI_NAN_CSID_NCS_SK_256)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_128 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_128)
|
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_128 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_128)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_256 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_256)
|
#define WIFI_NAN_CSID_BIT_NCS_PK_2WDH_256 (1 << WIFI_NAN_CSID_NCS_PK_2WDH_256)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCM_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCM_128)
|
#define WIFI_NAN_CSID_BIT_NCS_GTK_CCMP_128 (1 << WIFI_NAN_CSID_NCS_GTK_CCMP_128)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCM_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCM_256)
|
#define WIFI_NAN_CSID_BIT_NCS_GTK_GCMP_256 (1 << WIFI_NAN_CSID_NCS_GTK_GCMP_256)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_128 (1 << WIFI_NAN_CSID_NCS_PK_PASN_128)
|
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_128 (1 << WIFI_NAN_CSID_NCS_PK_PASN_128)
|
||||||
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_256 (1 << WIFI_NAN_CSID_NCS_PK_PASN_256)
|
#define WIFI_NAN_CSID_BIT_NCS_PK_PASN_256 (1 << WIFI_NAN_CSID_NCS_PK_PASN_256)
|
||||||
|
|
||||||
@@ -974,8 +975,8 @@ typedef struct {
|
|||||||
* is computed by the stack as the union of each credential's @c csid.
|
* is computed by the stack as the union of each credential's @c csid.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t group_data_prot: 1; /**< Group addressed data frame protection. Reserved: not supported right now. */
|
uint8_t group_data_prot: 1; /**< Group addressed data frame protection (GTKSA): distribute a GTK on the secured NDP so multicast data frames are protected. */
|
||||||
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection. Reserved: not supported right now. */
|
uint8_t group_mgmt_prot: 1; /**< Group addressed management frame protection (IGTKSA/BIGTKSA): BIP-protect multicast SDFs and Beacons. */
|
||||||
uint8_t reserved: 6; /**< Reserved */
|
uint8_t reserved: 6; /**< Reserved */
|
||||||
uint8_t num_credentials; /**< Number of valid entries in @c creds (0..ESP_WIFI_NAN_MAX_CREDS_PER_SVC). 0 = open service. */
|
uint8_t num_credentials; /**< Number of valid entries in @c creds (0..ESP_WIFI_NAN_MAX_CREDS_PER_SVC). 0 = open service. */
|
||||||
wifi_nan_credential_t creds[ESP_WIFI_NAN_MAX_CREDS_PER_SVC]; /**< Credentials list. */
|
wifi_nan_credential_t creds[ESP_WIFI_NAN_MAX_CREDS_PER_SVC]; /**< Credentials list. */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@@ -184,6 +184,11 @@ static void wifi_default_action_nan_started(void *arg, esp_event_base_t base, in
|
|||||||
if (s_wifi_netifs[WIFI_IF_NAN] != NULL) {
|
if (s_wifi_netifs[WIFI_IF_NAN] != NULL) {
|
||||||
wifi_start(s_wifi_netifs[WIFI_IF_NAN], base, event_id, data);
|
wifi_start(s_wifi_netifs[WIFI_IF_NAN], base, event_id, data);
|
||||||
esp_nan_action_start(s_wifi_netifs[WIFI_IF_NAN]);
|
esp_nan_action_start(s_wifi_netifs[WIFI_IF_NAN]);
|
||||||
|
/* Bring the netif up before esp_netif_create_ip6_linklocal() (a no-op unless
|
||||||
|
* netif_is_up()). esp_netif_up() is private, so use the public action handler;
|
||||||
|
* NAN is non-DHCP, so it only calls esp_netif_up() and ignores the event args. */
|
||||||
|
esp_netif_action_connected(s_wifi_netifs[WIFI_IF_NAN], NULL, 0, NULL);
|
||||||
|
esp_netif_create_ip6_linklocal(s_wifi_netifs[WIFI_IF_NAN]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
#include <string.h>
|
||||||
#include "esp_wifi.h"
|
#include "esp_wifi.h"
|
||||||
#include "esp_netif.h"
|
#include "esp_netif.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
@@ -181,3 +182,45 @@ esp_err_t esp_wifi_register_if_rxcb(wifi_netif_driver_t ifx, esp_netif_receive_t
|
|||||||
}
|
}
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void esp_wifi_netif_get_ip6_linklocal_from_mac(esp_ip6_addr_t *ip6, const uint8_t mac[6])
|
||||||
|
{
|
||||||
|
if (ip6 == NULL || mac == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fe80::/64 + EUI-64 of the MAC (802 group bit complemented), laid out in
|
||||||
|
* network byte order straight into esp_ip6_addr_t. Zone stays 0. */
|
||||||
|
const uint8_t linklocal[16] = {
|
||||||
|
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
(uint8_t)(mac[0] ^ 0x02), mac[1], mac[2], 0xff,
|
||||||
|
0xfe, mac[3], mac[4], mac[5],
|
||||||
|
};
|
||||||
|
memset(ip6, 0, sizeof(*ip6));
|
||||||
|
memcpy(ip6->addr, linklocal, sizeof(linklocal));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||||
|
esp_err_t esp_wifi_netif_set_static_neighbor(wifi_interface_t wifi_if, const uint8_t mac[6], bool add)
|
||||||
|
{
|
||||||
|
if (mac == NULL || wifi_if >= MAX_WIFI_IFS) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The netif handle is owned by this layer (recorded when the interface's RX
|
||||||
|
* callback is registered), so callers only need to supply the interface and
|
||||||
|
* the peer MAC. */
|
||||||
|
esp_netif_t *esp_netif = s_wifi_netifs[wifi_if];
|
||||||
|
if (esp_netif == NULL) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Derive the peer's link-local address; esp_netif copies only the address
|
||||||
|
* words for static neighbor entries, so no zone handling is needed here. */
|
||||||
|
esp_ip6_addr_t addr6;
|
||||||
|
esp_wifi_netif_get_ip6_linklocal_from_mac(&addr6, mac);
|
||||||
|
|
||||||
|
return add ? esp_netif_add_static_neighbor(esp_netif, &addr6, mac)
|
||||||
|
: esp_netif_remove_static_neighbor(esp_netif, &addr6);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES */
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ extern "C" {
|
|||||||
.disable_random_mac = false, \
|
.disable_random_mac = false, \
|
||||||
.reset_current_nvs_creds = false, \
|
.reset_current_nvs_creds = false, \
|
||||||
.use_nvs_for_caching = false, \
|
.use_nvs_for_caching = false, \
|
||||||
|
.group_mgmt_prot = false, \
|
||||||
};
|
};
|
||||||
|
|
||||||
#define NDP_STATUS_ACCEPTED 1
|
#define NDP_STATUS_ACCEPTED 1
|
||||||
|
|||||||
@@ -264,13 +264,48 @@ static void nan_app_clear_one_peer_tks(const uint8_t *peer_nmi)
|
|||||||
key_rsc, sizeof(key_rsc),
|
key_rsc, sizeof(key_rsc),
|
||||||
NULL, 0, NAN_KEY_ND_TK);
|
NULL, 0, NAN_KEY_ND_TK);
|
||||||
|
|
||||||
|
/* Drop the peer's RX GTK (bound to the peer NDI) and wipe local GTK
|
||||||
|
* state. The TX GTK keyed on the local NDI is released by the blob
|
||||||
|
* when the NDI/interface is torn down. */
|
||||||
|
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||||
|
key_addr, ndl->gtk_keyid, 0,
|
||||||
|
key_rsc, sizeof(key_rsc),
|
||||||
|
NULL, 0, NAN_KEY_ND_GTK);
|
||||||
|
|
||||||
|
/* Drop the peer's RX IGTK/BIGTK (BIP-CMAC-128, installed against the
|
||||||
|
* peer NMI at NDP confirm) so no stale BIP keys linger in the blob. */
|
||||||
|
if (ndl->igtk_set) {
|
||||||
|
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
|
||||||
|
(uint8_t *)peer_nmi, ndl->igtk_keyid, 0,
|
||||||
|
key_rsc, 6,
|
||||||
|
NULL, 0, NAN_KEY_ND_IGTK);
|
||||||
|
}
|
||||||
|
if (ndl->bigtk_set) {
|
||||||
|
esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
|
||||||
|
(uint8_t *)peer_nmi, ndl->bigtk_keyid, 0,
|
||||||
|
key_rsc, 6,
|
||||||
|
NULL, 0, NAN_KEY_ND_BIGTK);
|
||||||
|
}
|
||||||
|
|
||||||
forced_memzero(ndl->nd_tk, sizeof(ndl->nd_tk));
|
forced_memzero(ndl->nd_tk, sizeof(ndl->nd_tk));
|
||||||
forced_memzero(ndl->nd_kck, sizeof(ndl->nd_kck));
|
forced_memzero(ndl->nd_kck, sizeof(ndl->nd_kck));
|
||||||
forced_memzero(ndl->nd_kek, sizeof(ndl->nd_kek));
|
forced_memzero(ndl->nd_kek, sizeof(ndl->nd_kek));
|
||||||
|
forced_memzero(ndl->gtk, sizeof(ndl->gtk));
|
||||||
|
forced_memzero(ndl->own_gtk, sizeof(ndl->own_gtk));
|
||||||
|
forced_memzero(ndl->igtk, sizeof(ndl->igtk));
|
||||||
|
forced_memzero(ndl->bigtk, sizeof(ndl->bigtk));
|
||||||
ndl->ptk_set = 0;
|
ndl->ptk_set = 0;
|
||||||
ndl->tk_len = 0;
|
ndl->tk_len = 0;
|
||||||
ndl->kck_len = 0;
|
ndl->kck_len = 0;
|
||||||
ndl->kek_len = 0;
|
ndl->kek_len = 0;
|
||||||
|
ndl->gtk_set = 0;
|
||||||
|
ndl->own_gtk_set = 0;
|
||||||
|
ndl->gtk_len = 0;
|
||||||
|
ndl->own_gtk_len = 0;
|
||||||
|
ndl->igtk_set = 0;
|
||||||
|
ndl->bigtk_set = 0;
|
||||||
|
ndl->igtk_len = 0;
|
||||||
|
ndl->bigtk_len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fallback when no NDL slot tracks peer_ndi yet. */
|
/* Fallback when no NDL slot tracks peer_ndi yet. */
|
||||||
@@ -337,20 +372,12 @@ void esp_wifi_nan_get_ipv6_linklocal_from_mac(ip6_addr_t *ip6, uint8_t *mac_addr
|
|||||||
if (ip6 == NULL || mac_addr == NULL) {
|
if (ip6 == NULL || mac_addr == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Link-local prefix. */
|
/* Reuse the interface-agnostic derivation in the esp_wifi netif layer, then
|
||||||
ip6->addr[0] = htonl(0xfe800000ul);
|
* copy the address words into the lwIP ip6_addr_t. The two structures share
|
||||||
ip6->addr[1] = 0;
|
* the same layout, which is how esp_netif converts between them. */
|
||||||
|
esp_ip6_addr_t esp_ip6;
|
||||||
/* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */
|
esp_wifi_netif_get_ip6_linklocal_from_mac(&esp_ip6, mac_addr);
|
||||||
ip6->addr[2] = htonl((((uint32_t)(mac_addr[0] ^ 0x02)) << 24) |
|
memcpy(ip6->addr, esp_ip6.addr, sizeof(ip6->addr));
|
||||||
((uint32_t)(mac_addr[1]) << 16) |
|
|
||||||
((uint32_t)(mac_addr[2]) << 8) |
|
|
||||||
(0xff));
|
|
||||||
ip6->addr[3] = htonl((uint32_t)(0xfeul << 24) |
|
|
||||||
((uint32_t)(mac_addr[3]) << 16) |
|
|
||||||
((uint32_t)(mac_addr[4]) << 8) |
|
|
||||||
(mac_addr[5]));
|
|
||||||
|
|
||||||
ip6->zone = IP6_NO_ZONE;
|
ip6->zone = IP6_NO_ZONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,6 +628,18 @@ static struct own_svc_info *nan_claim_own_svc_slot(uint8_t type, const char svc_
|
|||||||
forced_memzero(&p_svc->derived_security, sizeof(p_svc->derived_security));
|
forced_memzero(&p_svc->derived_security, sizeof(p_svc->derived_security));
|
||||||
if (security_cfg) {
|
if (security_cfg) {
|
||||||
memcpy(&p_svc->user_cfg, security_cfg, sizeof(*security_cfg));
|
memcpy(&p_svc->user_cfg, security_cfg, sizeof(*security_cfg));
|
||||||
|
/* Device-global group_mgmt_prot (IGTKSA/BIGTKSA) forces GTKSA on every
|
||||||
|
* secured service: the CSIA capability field has no "IGTK/BIGTK without
|
||||||
|
* GTKSA" encoding (§9.5.21.2 Table 122), so advertising group-management
|
||||||
|
* protection mandates advertising GTKSA. Warn and force it on if the app
|
||||||
|
* requested group_data_prot=0. Forcing support never blocks a peer that
|
||||||
|
* lacks protection — keys activate only after capability negotiation. */
|
||||||
|
if (s_nan_ctx.group_mgmt_prot && !p_svc->user_cfg.group_data_prot) {
|
||||||
|
ESP_LOGW(TAG, "group_data_prot forced ON for '%s': group_mgmt_prot is "
|
||||||
|
"enabled device-wide; GTKSA cannot be advertised without it",
|
||||||
|
svc_name);
|
||||||
|
p_svc->user_cfg.group_data_prot = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
#ifdef CONFIG_ESP_WIFI_NAN_PAIRING
|
||||||
if (pairing) {
|
if (pairing) {
|
||||||
@@ -681,6 +720,7 @@ static void nan_record_new_ndl(uint8_t ndp_id, uint8_t publish_id, uint8_t peer_
|
|||||||
if (ndl && reuse_slot) {
|
if (ndl && reuse_slot) {
|
||||||
ndl->ndp_id = ndp_id;
|
ndl->ndp_id = ndp_id;
|
||||||
ndl->own_role = own_role;
|
ndl->own_role = own_role;
|
||||||
|
ndl->device_caps = device_caps;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ndl) {
|
if (ndl) {
|
||||||
@@ -1024,7 +1064,6 @@ static void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_inf
|
|||||||
evt->subscribe_id = sub_id;
|
evt->subscribe_id = sub_id;
|
||||||
MACADDR_COPY(evt->sub_if_mac, sub_nmi);
|
MACADDR_COPY(evt->sub_if_mac, sub_nmi);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Sent Publish to Peer "MACSTR" [Peer Subscribe id - %d]", MAC2STR(sub_nmi), sub_id);
|
|
||||||
if (ssi && ssi_len) {
|
if (ssi && ssi_len) {
|
||||||
memcpy(evt->ssi, ssi, ssi_len);
|
memcpy(evt->ssi, ssi, ssi_len);
|
||||||
evt->ssi_len = ssi_len;
|
evt->ssi_len = ssi_len;
|
||||||
@@ -1036,8 +1075,8 @@ static void nan_app_replied_cb(uint8_t pub_id, struct nan_cb_peer_info *peer_inf
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_info,
|
static void nan_app_receive_cb(uint8_t svc_id, struct nan_cb_peer_info *peer_info,
|
||||||
uint8_t *shared_key_attr, uint16_t shared_key_attr_buf_len,
|
uint8_t *shared_key_attr, uint16_t shared_key_attr_buf_len,
|
||||||
struct nan_cb_npba_t *npba)
|
struct nan_cb_npba_t *npba)
|
||||||
{
|
{
|
||||||
if (!peer_info) {
|
if (!peer_info) {
|
||||||
return;
|
return;
|
||||||
@@ -1147,8 +1186,11 @@ static void nan_app_ndp_indication_cb(uint8_t pub_id, struct ndp_cb_peer_info *p
|
|||||||
|
|
||||||
nan_record_new_ndl(ndp_id, pub_id, peer_nmi, ESP_WIFI_NDP_ROLE_RESPONDER, device_caps);
|
nan_record_new_ndl(ndp_id, pub_id, peer_nmi, ESP_WIFI_NDP_ROLE_RESPONDER, device_caps);
|
||||||
|
|
||||||
if (!nan_find_peer_svc(pub_id, 0, peer_nmi)) {
|
struct peer_svc_info *p_peer_svc = nan_find_peer_svc(pub_id, 0, peer_nmi);
|
||||||
|
if (!p_peer_svc) {
|
||||||
nan_record_peer_svc(pub_id, 0, peer_nmi, device_caps);
|
nan_record_peer_svc(pub_id, 0, peer_nmi, device_caps);
|
||||||
|
} else {
|
||||||
|
p_peer_svc->device_caps = device_caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ndl_info *ndl = nan_find_ndl(ndp_id, peer_nmi);
|
struct ndl_info *ndl = nan_find_ndl(ndp_id, peer_nmi);
|
||||||
@@ -1176,8 +1218,13 @@ static void nan_app_ndp_indication_cb(uint8_t pub_id, struct ndp_cb_peer_info *p
|
|||||||
uint8_t own_bssid[6];
|
uint8_t own_bssid[6];
|
||||||
esp_err_t err = esp_wifi_get_mac(WIFI_IF_NAN, own_bssid);
|
esp_err_t err = esp_wifi_get_mac(WIFI_IF_NAN, own_bssid);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
|
/* Cannot build the auto-response: free the NDL slot and deny the
|
||||||
|
* peer so it does not wait indefinitely. Send outside the lock. */
|
||||||
|
ESP_LOGE(TAG, "get own NAN MAC failed, rc=0x%x; denying NDP ndp_id=%d", err, ndp_id);
|
||||||
|
nan_reset_ndl(ndp_id, false);
|
||||||
NAN_DATA_UNLOCK();
|
NAN_DATA_UNLOCK();
|
||||||
ESP_LOGE(TAG, "Cannot get own BSSID!");
|
ndp_resp.accept = false;
|
||||||
|
esp_nan_internal_datapath_resp(&ndp_resp, (uint8_t *)&own_ipv6.u_addr.ip6.addr[2]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
esp_wifi_nan_get_ipv6_linklocal_from_mac(&own_ipv6.u_addr.ip6, own_bssid);
|
esp_wifi_nan_get_ipv6_linklocal_from_mac(&own_ipv6.u_addr.ip6, own_bssid);
|
||||||
@@ -1359,10 +1406,6 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NAN_KEY_ND_TK
|
|
||||||
#define NAN_KEY_ND_TK 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||||
if (ndl->security_ctx.type == WIFI_NAN_SECURITY_ENCRYPTED) {
|
if (ndl->security_ctx.type == WIFI_NAN_SECURITY_ENCRYPTED) {
|
||||||
uint8_t key_rsc[8] = {0};
|
uint8_t key_rsc[8] = {0};
|
||||||
@@ -1375,12 +1418,103 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
|
|||||||
ndl->nd_tk,
|
ndl->nd_tk,
|
||||||
NAN_NCS_SK_128_TK_LEN,
|
NAN_NCS_SK_128_TK_LEN,
|
||||||
NAN_KEY_ND_TK);
|
NAN_KEY_ND_TK);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP("ND-TK", ndl->nd_tk, NAN_NCS_SK_128_TK_LEN, ESP_LOG_DEBUG);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
ESP_LOGE(TAG, "NDP confirm: failed to install NAN pairwise key (ndp_id=%d, ret=%d)", ndp_id, ret);
|
ESP_LOGE(TAG, "NDP confirm: failed to install ND-TK (ndp_id=%d, ret=%d)", ndp_id, ret);
|
||||||
os_free(evt);
|
os_free(evt);
|
||||||
nan_ndp_confirm_teardown(peer_nmi, ndp_id);
|
nan_ndp_confirm_teardown(peer_nmi, ndp_id);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
ret = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||||
|
peer_nmi,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
key_rsc,
|
||||||
|
sizeof(key_rsc),
|
||||||
|
ndl->nd_tk,
|
||||||
|
NAN_NCS_SK_128_TK_LEN,
|
||||||
|
NAN_KEY_NM_TK);
|
||||||
|
if (ret != 0) {
|
||||||
|
ESP_LOGE(TAG, "NDP confirm: failed to install NM-TK (ndp_id=%d, ret=%d)", ndp_id, ret);
|
||||||
|
os_free(evt);
|
||||||
|
nan_ndp_confirm_teardown(peer_nmi, ndp_id);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Group keys (ND-GTK) exchanged during NDP setup (§7.1.3.2): our GTK
|
||||||
|
* is the TX key bound to the local NDI; the peer's GTK is the RX key
|
||||||
|
* bound to the peer NDI. Best-effort — a GTK install failure must not
|
||||||
|
* tear down the working unicast datapath. */
|
||||||
|
if (ndl->own_gtk_set && ndl->own_gtk_len) {
|
||||||
|
int gret = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||||
|
own_ndi, ndl->own_gtk_keyid, 1,
|
||||||
|
ndl->own_gtk_rsc, NAN_KEY_RSC_LEN,
|
||||||
|
ndl->own_gtk, ndl->own_gtk_len,
|
||||||
|
NAN_KEY_ND_GTK);
|
||||||
|
if (gret != 0) {
|
||||||
|
ESP_LOGW(TAG, "NDP confirm: own GTK (TX) install failed (ndp_id=%d, ret=%d)", ndp_id, gret);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "NDP confirm: own GTK (TX) installed (keyid=%d)", ndl->own_gtk_keyid);
|
||||||
|
}
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP("ND-GTK", ndl->own_gtk, ndl->own_gtk_len, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
if (ndl->gtk_set && ndl->gtk_len) {
|
||||||
|
int gret = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_CCMP,
|
||||||
|
peer_ndi, ndl->gtk_keyid, 0,
|
||||||
|
ndl->gtk_rsc, NAN_KEY_RSC_LEN,
|
||||||
|
ndl->gtk, ndl->gtk_len,
|
||||||
|
NAN_KEY_ND_GTK);
|
||||||
|
if (gret != 0) {
|
||||||
|
ESP_LOGW(TAG, "NDP confirm: peer GTK (RX) install failed (ndp_id=%d, ret=%d)", ndp_id, gret);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "NDP confirm: peer GTK (RX) installed (keyid=%d)", ndl->gtk_keyid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Own IGTK/BIGTK (TX) are installed once at NAN start (see
|
||||||
|
* nan_security_install_own_group_integrity_keys); not re-installed here,
|
||||||
|
* to preserve the blob's monotonic BIPN/IPN across the session. Only the
|
||||||
|
* peer RX keys are bound at NDP confirm. §7.1.3.3/§7.1.3.4; NMI==NDI today.
|
||||||
|
* Peer IGTK/BIGTK install RX-only against the peer NMI, seeding the BIP
|
||||||
|
* RX replay counter with the peer's advertised IPN/BIPN from the KDE. */
|
||||||
|
if (ndl->igtk_set && ndl->igtk_len) {
|
||||||
|
if (ndl->igtk_len != NAN_ND_GTK_LEN) {
|
||||||
|
ESP_LOGW(TAG, "NDP confirm: peer IGTK len=%d unsupported (BIP-CMAC-128 only); skipping",
|
||||||
|
ndl->igtk_len);
|
||||||
|
} else {
|
||||||
|
int r = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
|
||||||
|
peer_nmi, ndl->igtk_keyid, 0,
|
||||||
|
ndl->igtk_ipn, 6,
|
||||||
|
ndl->igtk, ndl->igtk_len,
|
||||||
|
NAN_KEY_ND_IGTK);
|
||||||
|
if (r != 0) {
|
||||||
|
ESP_LOGW(TAG, "NDP confirm: peer IGTK (RX) install failed, rc=0x%x (ndp_id=%d)", r, ndp_id);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "NDP confirm: peer IGTK (RX) installed (keyid=%d)", ndl->igtk_keyid);
|
||||||
|
}
|
||||||
|
/* Peer IGTK bytes for sniffer MIC cross-check vs the peer's multicast SDFs. */
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP("PEER ND-IGTK", ndl->igtk, ndl->igtk_len, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ndl->bigtk_set && ndl->bigtk_len) {
|
||||||
|
if (ndl->bigtk_len != NAN_ND_GTK_LEN) {
|
||||||
|
ESP_LOGW(TAG, "NDP confirm: peer BIGTK len=%d unsupported (BIP-CMAC-128 only); skipping",
|
||||||
|
ndl->bigtk_len);
|
||||||
|
} else {
|
||||||
|
int r = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
|
||||||
|
peer_nmi, ndl->bigtk_keyid, 0,
|
||||||
|
ndl->bigtk_ipn, 6,
|
||||||
|
ndl->bigtk, ndl->bigtk_len,
|
||||||
|
NAN_KEY_ND_BIGTK);
|
||||||
|
if (r != 0) {
|
||||||
|
ESP_LOGW(TAG, "NDP confirm: peer BIGTK (RX) install failed, rc=0x%x (ndp_id=%d)", r, ndp_id);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "NDP confirm: peer BIGTK (RX) installed (keyid=%d)", ndl->bigtk_keyid);
|
||||||
|
}
|
||||||
|
/* Peer BIGTK bytes for sniffer MIC cross-check vs the peer's protected Beacons. */
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP("PEER ND-BIGTK", ndl->bigtk, ndl->bigtk_len, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
|
#endif /* CONFIG_ESP_WIFI_NAN_SECURITY */
|
||||||
evt->status = status;
|
evt->status = status;
|
||||||
@@ -1402,8 +1536,6 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
|
|||||||
ESP_LOG_BUFFER_HEXDUMP(TAG, ssi, ssi_len, ESP_LOG_DEBUG);
|
ESP_LOG_BUFFER_HEXDUMP(TAG, ssi, ssi_len, ESP_LOG_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_netif_action_connected(s_nan_ctx.nan_netif, WIFI_EVENT, WIFI_EVENT_NDP_CONFIRM, evt);
|
|
||||||
esp_netif_create_ip6_linklocal(s_nan_ctx.nan_netif);
|
|
||||||
NAN_DATA_UNLOCK();
|
NAN_DATA_UNLOCK();
|
||||||
|
|
||||||
ip6_addr_t peer_ip6 = {0};
|
ip6_addr_t peer_ip6 = {0};
|
||||||
@@ -1414,6 +1546,20 @@ static void nan_app_ndp_confirm_cb(uint8_t status, struct ndp_cb_peer_info *peer
|
|||||||
ESP_LOGI(TAG, "NDP confirmed with Peer "MACSTR" [NDP ID - %d, Peer IPv6 - %s]",
|
ESP_LOGI(TAG, "NDP confirmed with Peer "MACSTR" [NDP ID - %d, Peer IPv6 - %s]",
|
||||||
MAC2STR(peer_nmi), ndp_id, inet6_ntoa(peer_ip6));
|
MAC2STR(peer_nmi), ndp_id, inet6_ntoa(peer_ip6));
|
||||||
|
|
||||||
|
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||||
|
/* Pin the peer's link-local -> NDI mapping so traffic to the peer skips
|
||||||
|
* Neighbor Discovery (no NS/NA) on the NAN link. The esp_wifi netif layer
|
||||||
|
* owns the netif lookup and derives the peer's link-local from its NDI
|
||||||
|
* (the address the peer actually sources from). */
|
||||||
|
esp_err_t nbr_err = esp_wifi_netif_set_static_neighbor(WIFI_IF_NAN, peer_ndi, true);
|
||||||
|
if (nbr_err != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "static nbr ADD failed: %s", esp_err_to_name(nbr_err));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
esp_netif_action_connected(s_nan_ctx.nan_netif, WIFI_EVENT, WIFI_EVENT_NDP_CONFIRM, evt);
|
||||||
|
esp_netif_create_ip6_linklocal(s_nan_ctx.nan_netif);
|
||||||
|
#endif
|
||||||
|
|
||||||
os_event_group_set_bits(nan_event_group, NDP_ACCEPTED);
|
os_event_group_set_bits(nan_event_group, NDP_ACCEPTED);
|
||||||
nan_app_post_event(WIFI_EVENT_NDP_CONFIRM, evt, evt_data_len);
|
nan_app_post_event(WIFI_EVENT_NDP_CONFIRM, evt, evt_data_len);
|
||||||
os_free(evt);
|
os_free(evt);
|
||||||
@@ -1436,6 +1582,15 @@ static void nan_app_ndp_terminated_cb(uint8_t reason, uint8_t ndp_id, uint8_t in
|
|||||||
s_nan_ctx.event &= ~(NDP_INDICATION);
|
s_nan_ctx.event &= ~(NDP_INDICATION);
|
||||||
NAN_DATA_UNLOCK();
|
NAN_DATA_UNLOCK();
|
||||||
|
|
||||||
|
#if CONFIG_LWIP_ND6_SUPPORT_STATIC_ENTRIES
|
||||||
|
/* Drop the peer's static neighbor mapping added on NDP confirm. (It is also
|
||||||
|
* cleared automatically if the NAN netif goes down on the last datapath.) */
|
||||||
|
esp_err_t nbr_err = esp_wifi_netif_set_static_neighbor(WIFI_IF_NAN, init_ndi, false);
|
||||||
|
if (nbr_err != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "static nbr DEL failed: %s", esp_err_to_name(nbr_err));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
wifi_event_ndp_terminated_t *evt = (wifi_event_ndp_terminated_t *)os_zalloc(sizeof(wifi_event_ndp_terminated_t));
|
wifi_event_ndp_terminated_t *evt = (wifi_event_ndp_terminated_t *)os_zalloc(sizeof(wifi_event_ndp_terminated_t));
|
||||||
if (!evt) {
|
if (!evt) {
|
||||||
ESP_LOGE(TAG, "Failed to allocate for event");
|
ESP_LOGE(TAG, "Failed to allocate for event");
|
||||||
@@ -1506,6 +1661,7 @@ static struct nan_secure_dp_funcs s_nan_secure_dp_funcs = {
|
|||||||
.ndp_security_install_get_shared_desc_len = esp_nan_ndp_security_install_get_shared_desc_len,
|
.ndp_security_install_get_shared_desc_len = esp_nan_ndp_security_install_get_shared_desc_len,
|
||||||
.get_ndp_resp_num_pmkids = esp_nan_get_ndp_resp_num_pmkids,
|
.get_ndp_resp_num_pmkids = esp_nan_get_ndp_resp_num_pmkids,
|
||||||
.get_ndp_resp_shared_key_desc_len = esp_nan_get_ndp_resp_shared_key_desc_len,
|
.get_ndp_resp_shared_key_desc_len = esp_nan_get_ndp_resp_shared_key_desc_len,
|
||||||
|
.ndp_confirm_get_shared_desc_len = esp_nan_ndp_confirm_get_shared_desc_len,
|
||||||
|
|
||||||
/* CSIA / SCIA construction */
|
/* CSIA / SCIA construction */
|
||||||
.construct_csia = esp_nan_construct_csia,
|
.construct_csia = esp_nan_construct_csia,
|
||||||
@@ -1686,6 +1842,16 @@ void esp_nan_action_start(esp_netif_t *nan_netif)
|
|||||||
};
|
};
|
||||||
esp_nan_internal_register_callbacks(&nan_cb);
|
esp_nan_internal_register_callbacks(&nan_cb);
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||||
|
/* s_nan_ctx.group_mgmt_prot (device-global IGTKSA/BIGTKSA, one per NMI) was
|
||||||
|
* captured from the user's start config in esp_wifi_nan_sync_start().
|
||||||
|
* Install the device-global IGTK/BIGTK for TX now (when enabled) so Beacons
|
||||||
|
* (BIGTK) and group-addressed SDFs (IGTK) are BIP-protected from the first
|
||||||
|
* frame, like iOS. The blob gates beacon BIP-TX on an active BIGTK index
|
||||||
|
* only (no NDP state), so installing here is sufficient. */
|
||||||
|
nan_security_install_own_group_integrity_keys();
|
||||||
|
#endif
|
||||||
|
|
||||||
ESP_LOGI(TAG, "NAN Discovery started.");
|
ESP_LOGI(TAG, "NAN Discovery started.");
|
||||||
os_event_group_clear_bits(nan_event_group, NAN_STOPPED_BIT);
|
os_event_group_clear_bits(nan_event_group, NAN_STOPPED_BIT);
|
||||||
os_event_group_set_bits(nan_event_group, NAN_STARTED_BIT);
|
os_event_group_set_bits(nan_event_group, NAN_STARTED_BIT);
|
||||||
@@ -1713,6 +1879,13 @@ void esp_nan_action_stop(void)
|
|||||||
nan_app_clear_paired_peers();
|
nan_app_clear_paired_peers();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||||
|
/* Drop the device-global IGTK/BIGTK so the next start regenerates fresh
|
||||||
|
* keys instead of re-installing a stale key with IPN/BIPN=0 (which would
|
||||||
|
* reset the blob's replay counter) — see nan_security_reset_own_group_keys. */
|
||||||
|
nan_security_reset_own_group_keys();
|
||||||
|
#endif
|
||||||
|
|
||||||
esp_nan_internal_register_callbacks(NULL);
|
esp_nan_internal_register_callbacks(NULL);
|
||||||
os_event_group_clear_bits(nan_event_group, NAN_STARTED_BIT);
|
os_event_group_clear_bits(nan_event_group, NAN_STARTED_BIT);
|
||||||
os_event_group_set_bits(nan_event_group, NAN_STOPPED_BIT);
|
os_event_group_set_bits(nan_event_group, NAN_STOPPED_BIT);
|
||||||
@@ -1756,6 +1929,7 @@ esp_err_t esp_wifi_nan_sync_start(const wifi_nan_sync_config_t *nan_cfg)
|
|||||||
s_nan_ctx.num_peer_creds = 0;
|
s_nan_ctx.num_peer_creds = 0;
|
||||||
memset(s_nan_ctx.peer_creds, 0, sizeof(s_nan_ctx.peer_creds));
|
memset(s_nan_ctx.peer_creds, 0, sizeof(s_nan_ctx.peer_creds));
|
||||||
s_nan_ctx.use_nvs_for_caching = nan_cfg->use_nvs_for_caching;
|
s_nan_ctx.use_nvs_for_caching = nan_cfg->use_nvs_for_caching;
|
||||||
|
s_nan_ctx.group_mgmt_prot = nan_cfg->group_mgmt_prot;
|
||||||
s_nan_ctx.nik_lifetime = 0;
|
s_nan_ctx.nik_lifetime = 0;
|
||||||
|
|
||||||
if (nan_cfg->reset_current_nvs_creds) {
|
if (nan_cfg->reset_current_nvs_creds) {
|
||||||
@@ -2525,7 +2699,6 @@ esp_err_t esp_wifi_nan_datapath_resp(wifi_nan_datapath_resp_t *resp)
|
|||||||
ESP_LOGE(TAG, "Need NDP Indication before NDP Response can be sent");
|
ESP_LOGE(TAG, "Need NDP Indication before NDP Response can be sent");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MACADDR_EQUAL(resp->peer_mac, null_mac)) {
|
if (MACADDR_EQUAL(resp->peer_mac, null_mac)) {
|
||||||
MACADDR_COPY(resp->peer_mac, ndl->peer_nmi);
|
MACADDR_COPY(resp->peer_mac, ndl->peer_nmi);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,6 +130,40 @@ extern void *s_nan_data_lock;
|
|||||||
#define NAN_KEY_INFO_ENC_KEY BIT(12)
|
#define NAN_KEY_INFO_ENC_KEY BIT(12)
|
||||||
#define NAN_KEY_INFO_KEY_TYPE BIT(3) /* 1=Pairwise, 0=Group */
|
#define NAN_KEY_INFO_KEY_TYPE BIT(3) /* 1=Pairwise, 0=Group */
|
||||||
|
|
||||||
|
/* NAN KDE OUIs and Data Types carried in the Key Data field (Wi-Fi Aware
|
||||||
|
* v4.0 §9.5.21.5 Table 126; formats per 802.11 Fig 12-36/12-42/12-47). */
|
||||||
|
#define NAN_KDE_OUI_RSN 0x000FACUL
|
||||||
|
#define NAN_KDE_OUI_WFA 0x506F9AUL
|
||||||
|
#define NAN_KDE_TYPE_GTK 1 /* 00-0F-AC GTK KDE */
|
||||||
|
#define NAN_KDE_TYPE_MAC 3 /* 00-0F-AC MAC address KDE */
|
||||||
|
#define NAN_KDE_TYPE_IGTK 9 /* 00-0F-AC IGTK KDE */
|
||||||
|
#define NAN_KDE_TYPE_BIGTK 14 /* 00-0F-AC BIGTK KDE */
|
||||||
|
#define NAN_KDE_TYPE_NIK 36 /* 50-6F-9A NIK KDE */
|
||||||
|
#define NAN_KDE_TYPE_KEY_LIFE 37 /* 50-6F-9A NAN Key Lifetime KDE */
|
||||||
|
|
||||||
|
/* KDE inner-prefix lengths (bytes before the actual key material). */
|
||||||
|
#define NAN_KDE_HDR_LEN 6 /* DD(1) + len(1) + OUI(3) + DataType(1) */
|
||||||
|
#define NAN_GTK_KDE_PREFIX_LEN 2 /* KeyID/Tx(1) + Reserved(1) */
|
||||||
|
#define NAN_IGTK_KDE_PREFIX_LEN 8 /* KeyID(2) + IPN(6) */
|
||||||
|
#define NAN_BIGTK_KDE_PREFIX_LEN 8 /* KeyID(2) + BIPN(6) */
|
||||||
|
|
||||||
|
/* CSIA Capabilities group-SA support (§9.5.21.2 Table 122, bits 1-2, bit 2 high
|
||||||
|
* order). Mirrors hostap nan_defs.h NAN_CS_INFO_CAPA_GTK_SUPP_*. */
|
||||||
|
#define NAN_CSIA_CAP_GTK_SUPP_POS 1
|
||||||
|
#define NAN_CSIA_CAP_GTK_SUPP_MASK 0x06
|
||||||
|
#define NAN_CSIA_CAP_GTK_SUPP_NONE 0 /* 00: no group SA */
|
||||||
|
#define NAN_CSIA_CAP_GTK_SUPP_IGTK 1 /* 01: GTKSA + IGTKSA */
|
||||||
|
#define NAN_CSIA_CAP_GTK_SUPP_ALL 2 /* 10: GTKSA + IGTKSA + BIGTKSA */
|
||||||
|
|
||||||
|
/* ND-GTK is the data-path group key (CCMP-128). */
|
||||||
|
#define NAN_ND_GTK_LEN 16
|
||||||
|
/* Host-side bound for the group Key Data scratch buffers (plaintext + AES-wrap),
|
||||||
|
* sized for GTK+IGTK+BIGTK: GTK 24B + IGTK 30B + BIGTK 30B + optional Key Lifetime
|
||||||
|
* KDE(s), padded to a multiple of 8, + 8B NIST AES Key Wrap overhead (~104B worst
|
||||||
|
* case). NOT the descriptor-len reservation — the getters now return the EXACT
|
||||||
|
* per-NDP length, so the blob allocates exactly what the builder writes. */
|
||||||
|
#define NAN_GROUP_KEY_DATA_MAX 128
|
||||||
|
|
||||||
/* NCS-SK-128 only for now (Table 21): KCK 128 bits, KEK 128 bits, TK 128 bits, MIC 16 bytes */
|
/* NCS-SK-128 only for now (Table 21): KCK 128 bits, KEK 128 bits, TK 128 bits, MIC 16 bytes */
|
||||||
#define NAN_NCS_SK_128_KCK_LEN 16
|
#define NAN_NCS_SK_128_KCK_LEN 16
|
||||||
#define NAN_NCS_SK_128_KEK_LEN 16
|
#define NAN_NCS_SK_128_KEK_LEN 16
|
||||||
@@ -138,11 +172,16 @@ extern void *s_nan_data_lock;
|
|||||||
#define NAN_NCS_SK_128_PTK_LEN (NAN_NCS_SK_128_KCK_LEN + NAN_NCS_SK_128_KEK_LEN + NAN_NCS_SK_128_TK_LEN)
|
#define NAN_NCS_SK_128_PTK_LEN (NAN_NCS_SK_128_KCK_LEN + NAN_NCS_SK_128_KEK_LEN + NAN_NCS_SK_128_TK_LEN)
|
||||||
|
|
||||||
/* Internal key-install constants matching esp_wifi_set_sta_key_internal semantics. */
|
/* Internal key-install constants matching esp_wifi_set_sta_key_internal semantics. */
|
||||||
#define NAN_WIFI_WPA_ALG_CCMP 3
|
#define NAN_WIFI_WPA_ALG_CCMP 3
|
||||||
|
#define NAN_WIFI_WPA_ALG_BIP_CMAC_128 7 /* IGTK/BIGTK BIP = blob WIFI_WPA_ALG_IGTK; 4 is SMS4 */
|
||||||
#define NAN_KEY_FLAG_RX BIT(2)
|
#define NAN_KEY_FLAG_RX BIT(2)
|
||||||
#define NAN_KEY_FLAG_TX BIT(3)
|
#define NAN_KEY_FLAG_TX BIT(3)
|
||||||
#define NAN_KEY_FLAG_PAIRWISE BIT(5)
|
#define NAN_KEY_FLAG_PAIRWISE BIT(5)
|
||||||
|
|
||||||
|
/* NAN key-type selector (nan_key_type_t: NAN_KEY_ND_TK / ND_GTK / NM_TK / ND_IGTK /
|
||||||
|
* ND_BIGTK), passed as the last arg of esp_wifi_set_nan_key_internal, is defined in
|
||||||
|
* esp_wifi_driver.h (included above) and shared with the blob. */
|
||||||
|
|
||||||
/* Handshake state */
|
/* Handshake state */
|
||||||
enum nan_handshake_state {
|
enum nan_handshake_state {
|
||||||
NAN_HANDSHAKE_IDLE = 0,
|
NAN_HANDSHAKE_IDLE = 0,
|
||||||
@@ -195,6 +234,13 @@ struct peer_svc_info {
|
|||||||
* whose ND-PMKID matched the publisher SCIA. The initiator NDP-req path
|
* whose ND-PMKID matched the publisher SCIA. The initiator NDP-req path
|
||||||
* uses this to pick the right credential for M1's pair-PMKID. */
|
* uses this to pick the right credential for M1's pair-PMKID. */
|
||||||
uint8_t matched_cred_idx;
|
uint8_t matched_cred_idx;
|
||||||
|
/* Set at SDF match if the publisher advertised an NCS-GTK suite in its CSIA;
|
||||||
|
* the initiator NDP-req path uses it (with our own group_data_prot) to
|
||||||
|
* decide whether to distribute a GTK during NDP setup. */
|
||||||
|
uint8_t peer_group_data_cap: 1;
|
||||||
|
uint8_t peer_group_mgmt_cap: 1; /* peer advertised IGTKSA (CSIA caps bits 1-2 != 0) */
|
||||||
|
uint8_t peer_group_bigtk_cap: 1; /* peer advertised BIGTKSA (CSIA caps bits 1-2 == 10) */
|
||||||
|
uint8_t peer_sec_reserved: 5;
|
||||||
#endif
|
#endif
|
||||||
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
#if CONFIG_ESP_WIFI_NAN_PAIRING
|
||||||
/* Peer NIK / cipher version / lifetime extracted from a NAN Shared Key
|
/* Peer NIK / cipher version / lifetime extracted from a NAN Shared Key
|
||||||
@@ -273,20 +319,38 @@ struct ndl_info {
|
|||||||
|
|
||||||
uint8_t handshake_state;
|
uint8_t handshake_state;
|
||||||
|
|
||||||
/* Group key state (unsupported -- pairwise-only M1-M4 flow today;
|
/* Received peer group keys (RX GTKSA). GTK protects group-addressed data
|
||||||
* fields kept to match the spec-defined RSNA key descriptor layout
|
* frames the peer transmits; installed against the peer NDI. IGTK/BIGTK
|
||||||
* and stay forward-compatible). */
|
* protect group-addressed management/Beacon frames (NMI plane). */
|
||||||
uint8_t gtk[NAN_GTK_MAX_LEN];
|
uint8_t gtk[NAN_GTK_MAX_LEN];
|
||||||
uint8_t igtk[NAN_GTK_MAX_LEN];
|
uint8_t igtk[NAN_GTK_MAX_LEN];
|
||||||
uint8_t bigtk[NAN_GTK_MAX_LEN];
|
uint8_t bigtk[NAN_GTK_MAX_LEN];
|
||||||
uint8_t gtk_len;
|
uint8_t gtk_len;
|
||||||
uint8_t igtk_len;
|
uint8_t igtk_len;
|
||||||
uint8_t bigtk_len;
|
uint8_t bigtk_len;
|
||||||
|
uint8_t gtk_keyid; /* peer GTK Key ID (1 or 2) */
|
||||||
|
uint8_t igtk_keyid; /* peer IGTK Key ID (4 or 5) */
|
||||||
|
uint8_t bigtk_keyid; /* peer BIGTK Key ID (6 or 7) */
|
||||||
|
uint8_t gtk_rsc[NAN_KEY_RSC_LEN]; /* peer GTK RSC from Key RSC field */
|
||||||
|
uint8_t igtk_ipn[6]; /* peer IGTK IPN (seeds BIP RX replay counter) */
|
||||||
|
uint8_t bigtk_ipn[6]; /* peer BIGTK BIPN (seeds BIP RX replay counter) */
|
||||||
uint8_t gtk_set: 1;
|
uint8_t gtk_set: 1;
|
||||||
uint8_t igtk_set: 1;
|
uint8_t igtk_set: 1;
|
||||||
uint8_t bigtk_set: 1;
|
uint8_t bigtk_set: 1;
|
||||||
uint8_t group_keys_reserved: 5;
|
uint8_t group_keys_reserved: 5;
|
||||||
|
|
||||||
|
/* Own group key (TX GTKSA) distributed to the peer in M3 (initiator) or
|
||||||
|
* M4 (responder); installed against the local NDI as the TX group key. */
|
||||||
|
uint8_t own_gtk[NAN_ND_GTK_LEN];
|
||||||
|
uint8_t own_gtk_len;
|
||||||
|
uint8_t own_gtk_keyid; /* own GTK Key ID (1 or 2) */
|
||||||
|
uint8_t own_gtk_rsc[NAN_KEY_RSC_LEN];
|
||||||
|
uint8_t own_gtk_set: 1;
|
||||||
|
uint8_t gtk_required: 1; /* GTKSA negotiated for this NDP */
|
||||||
|
uint8_t igtk_required: 1; /* IGTKSA negotiated for this NDP */
|
||||||
|
uint8_t bigtk_required: 1; /* BIGTKSA negotiated for this NDP */
|
||||||
|
uint8_t own_group_reserved: 4;
|
||||||
|
|
||||||
uint8_t key_rsc[NAN_KEY_RSC_LEN];
|
uint8_t key_rsc[NAN_KEY_RSC_LEN];
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
@@ -304,6 +368,28 @@ typedef struct {
|
|||||||
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
#ifdef CONFIG_ESP_WIFI_NAN_SECURITY
|
||||||
uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN];
|
uint8_t own_nik[ESP_WIFI_NAN_NIK_LEN];
|
||||||
bool own_nik_valid;
|
bool own_nik_valid;
|
||||||
|
/* Device-global IGTKSA/BIGTKSA (one per NMI, §7.1.3.3/§7.1.3.4). Generated
|
||||||
|
* once via os_get_random and reused across all secured NDPs; copied into
|
||||||
|
* each M3/M4 and installed against the local NMI. BIP-CMAC-128 (16-byte).
|
||||||
|
* On ESP the NMI and NDI are the same MAC today. */
|
||||||
|
uint8_t own_igtk[NAN_ND_GTK_LEN];
|
||||||
|
uint8_t own_igtk_ipn[6];
|
||||||
|
uint8_t own_igtk_keyid; /* 4 or 5 */
|
||||||
|
bool own_igtk_set;
|
||||||
|
uint8_t own_bigtk[NAN_ND_GTK_LEN];
|
||||||
|
uint8_t own_bigtk_bipn[6];
|
||||||
|
uint8_t own_bigtk_keyid; /* 6 or 7 */
|
||||||
|
bool own_bigtk_set;
|
||||||
|
/* Device-global "support + use group management protection (IGTKSA/BIGTKSA)".
|
||||||
|
* One setting per NMI, not per service: Beacons are NMI-level and there is
|
||||||
|
* exactly one IGTKSA/BIGTKSA per NMI (§7.1.3.3/§7.1.3.4). Sourced from
|
||||||
|
* wifi_nan_sync_config_t.group_mgmt_prot at NAN start. When set it: forces
|
||||||
|
* GTKSA on every secured service (the CSIA caps field cannot encode IGTK/
|
||||||
|
* BIGTK without GTKSA, §9.5.21.2 Table 122), generates own IGTK+BIGTK,
|
||||||
|
* advertises caps 0x04, and BIP-protects Beacons + multicast SDFs.
|
||||||
|
* Advertising never blocks a non-supporting peer — keys and protection are
|
||||||
|
* set up only after capability negotiation (§7.1.3.5). */
|
||||||
|
bool group_mgmt_prot;
|
||||||
uint8_t cached_nira_nonce[8];
|
uint8_t cached_nira_nonce[8];
|
||||||
uint8_t cached_nira_tag[8];
|
uint8_t cached_nira_tag[8];
|
||||||
bool nira_cached;
|
bool nira_cached;
|
||||||
@@ -340,7 +426,8 @@ bool nan_compute_service_id(const char *service_name, uint8_t service_id[6]);
|
|||||||
uint32_t esp_nan_get_csia_len(uint16_t own_csid_bitmap, uint16_t peer_csid_bitmap);
|
uint32_t esp_nan_get_csia_len(uint16_t own_csid_bitmap, uint16_t peer_csid_bitmap);
|
||||||
uint32_t esp_nan_get_scia_len(uint8_t num_pmkids);
|
uint32_t esp_nan_get_scia_len(uint8_t num_pmkids);
|
||||||
uint32_t esp_nan_get_shared_key_desc_attr_len(uint16_t key_data_len);
|
uint32_t esp_nan_get_shared_key_desc_attr_len(uint16_t key_data_len);
|
||||||
int esp_nan_ndp_security_install_get_shared_desc_len(void);
|
int esp_nan_ndp_security_install_get_shared_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||||
|
int esp_nan_ndp_confirm_get_shared_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||||
uint8_t esp_nan_get_ndp_resp_num_pmkids(uint8_t ndp_id, const uint8_t *peer_nmi);
|
uint8_t esp_nan_get_ndp_resp_num_pmkids(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||||
uint32_t esp_nan_get_ndp_resp_shared_key_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi);
|
uint32_t esp_nan_get_ndp_resp_shared_key_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi);
|
||||||
|
|
||||||
@@ -428,6 +515,17 @@ esp_err_t nan_security_populate_initiator_ndl(struct ndl_info *ndl,
|
|||||||
const struct peer_svc_info *peer_svc,
|
const struct peer_svc_info *peer_svc,
|
||||||
const uint8_t *peer_nmi);
|
const uint8_t *peer_nmi);
|
||||||
|
|
||||||
|
/* Generate (once) and TX-install the device-global IGTK/BIGTK so Beacons and
|
||||||
|
* group-addressed SDFs are BIP-protected from NAN start (§7.1.3.3/§7.1.3.4).
|
||||||
|
* Call once at NAN start; never per-NDP (would reset the blob's BIPN counter). */
|
||||||
|
void nan_security_install_own_group_integrity_keys(void);
|
||||||
|
|
||||||
|
/* Clear the device-global IGTK/BIGTK state on NAN stop so the next NAN start
|
||||||
|
* regenerates fresh keys. Prevents re-installing a stale key with IPN/BIPN=0
|
||||||
|
* (which resets the blob's monotonic replay counter) and key reuse if the NMI
|
||||||
|
* is re-randomized on restart. */
|
||||||
|
void nan_security_reset_own_group_keys(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Match subscriber discovery security to a publisher's params.
|
* Match subscriber discovery security to a publisher's params.
|
||||||
* NCS-SK: Compare locally derived ND-PMKID (subscriber passphrase, publisher NMI) to
|
* NCS-SK: Compare locally derived ND-PMKID (subscriber passphrase, publisher NMI) to
|
||||||
|
|||||||
@@ -510,6 +510,11 @@ int esp_nan_construct_nira(uint8_t *frm)
|
|||||||
#define NAN_PASN_KDE_OUI_TYPE_LIFETIME 37
|
#define NAN_PASN_KDE_OUI_TYPE_LIFETIME 37
|
||||||
#define NAN_PASN_KEY_LIFETIME_NIK_BIT BIT(3)
|
#define NAN_PASN_KEY_LIFETIME_NIK_BIT BIT(3)
|
||||||
#define NAN_ATTR_ID_SHARED_KEY_DESC 0x24
|
#define NAN_ATTR_ID_SHARED_KEY_DESC 0x24
|
||||||
|
/* iOS sends its NIK follow-up ~2.5-2.6 s after we derive the NM-TK; a 2 s window
|
||||||
|
* fired first and destructively removed the peer, so the late NIK arrived after
|
||||||
|
* teardown and the follow-up went out unprotected -> peer never started the NDP.
|
||||||
|
* 5 s lets the NIK land in time, so the cancel in the store-NIK path keeps the
|
||||||
|
* peer SA intact. */
|
||||||
#define NAN_PAIRING_NIK_FUP_TIMEOUT_SEC 5
|
#define NAN_PAIRING_NIK_FUP_TIMEOUT_SEC 5
|
||||||
|
|
||||||
struct nan_pairing_fup_ctx {
|
struct nan_pairing_fup_ctx {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "esp_nan.h"
|
#include "esp_nan.h"
|
||||||
#include "utils/common.h"
|
#include "utils/common.h"
|
||||||
#include "crypto/sha256.h"
|
#include "crypto/sha256.h"
|
||||||
|
#include "crypto/aes_wrap.h"
|
||||||
#include "nan_i.h"
|
#include "nan_i.h"
|
||||||
|
|
||||||
static const char *TAG = "nan_sec";
|
static const char *TAG = "nan_sec";
|
||||||
@@ -56,6 +57,8 @@ static struct {
|
|||||||
static struct {
|
static struct {
|
||||||
bool has_pmkid;
|
bool has_pmkid;
|
||||||
bool has_csid;
|
bool has_csid;
|
||||||
|
bool peer_group_data; /* peer signalled group-data (GTK) support in its CSIA */
|
||||||
|
uint8_t peer_caps; /* raw CSIA Capabilities byte; IGTK/BIGTK gating derives bits 1-2 */
|
||||||
uint8_t pub_id;
|
uint8_t pub_id;
|
||||||
uint8_t pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN];
|
uint8_t pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN];
|
||||||
uint16_t csid_bitmap;
|
uint16_t csid_bitmap;
|
||||||
@@ -64,6 +67,12 @@ static struct {
|
|||||||
/* Spec Table 121: valid CSIDs are 1..8. Bit 0 and bits 9..15 are not assigned. */
|
/* Spec Table 121: valid CSIDs are 1..8. Bit 0 and bits 9..15 are not assigned. */
|
||||||
#define NAN_CSID_VALID_BITMAP ((uint16_t)0x01FE)
|
#define NAN_CSID_VALID_BITMAP ((uint16_t)0x01FE)
|
||||||
|
|
||||||
|
/* NCS-GTK cipher-suite bits (group-addressed data). Advertised when a service
|
||||||
|
* sets group_data_prot; the basic default we generate/advertise is CCMP-128. */
|
||||||
|
#define NAN_CSID_GTK_BITS (WIFI_NAN_CSID_BIT_NCS_GTK_CCMP_128 | \
|
||||||
|
WIFI_NAN_CSID_BIT_NCS_GTK_GCMP_256)
|
||||||
|
#define NAN_CSID_GTK_DEFAULT WIFI_NAN_CSID_BIT_NCS_GTK_CCMP_128
|
||||||
|
|
||||||
/* Sentinel for peer_svc->matched_cred_idx: no PMKID match remembered yet. */
|
/* Sentinel for peer_svc->matched_cred_idx: no PMKID match remembered yet. */
|
||||||
#define NAN_NO_MATCHED_CRED 0xFF
|
#define NAN_NO_MATCHED_CRED 0xFF
|
||||||
|
|
||||||
@@ -458,6 +467,34 @@ static bool nan_security_fill_from_paired_cache(struct ndl_info *ndl, const uint
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Early-reject: if the NDP Request carried an ND-PMKID, our cached ND-PMK must reproduce it (§7.1.3.5) before we commit. */
|
||||||
|
static const uint8_t zero_pmkid[ESP_WIFI_NAN_NDP_PMKID_LEN] = {0};
|
||||||
|
if (memcmp(ndl->security_ctx.nd_pmkid, zero_pmkid, ESP_WIFI_NAN_NDP_PMKID_LEN) != 0) {
|
||||||
|
uint8_t our_nmi[6];
|
||||||
|
uint8_t service_id[6];
|
||||||
|
struct own_svc_info *own_svc = nan_find_own_svc(ndl->publisher_id);
|
||||||
|
|
||||||
|
if (esp_wifi_get_mac(WIFI_IF_NAN, our_nmi) != ESP_OK || !own_svc ||
|
||||||
|
!own_svc->svc_name[0] || !nan_compute_service_id(own_svc->svc_name, service_id)) {
|
||||||
|
ESP_LOGW(TAG, "Paired ND-PMK: cannot verify peer ND-PMKID (own NMI/service unavailable for pub_id=%u), deferring to handshake MIC",
|
||||||
|
ndl->publisher_id);
|
||||||
|
} else {
|
||||||
|
uint8_t expected[ESP_WIFI_NAN_NDP_PMKID_LEN];
|
||||||
|
/* Spec order (Initiator=peer, Responder=us), then reversed for interop, mirroring nan_match_pmkid(). */
|
||||||
|
bool ok = (nan_derive_ndp_request_pmkid(paired->nd_pmk, peer_nmi, our_nmi, service_id, expected) &&
|
||||||
|
memcmp(expected, ndl->security_ctx.nd_pmkid, ESP_WIFI_NAN_NDP_PMKID_LEN) == 0) ||
|
||||||
|
(nan_derive_ndp_request_pmkid(paired->nd_pmk, our_nmi, peer_nmi, service_id, expected) &&
|
||||||
|
memcmp(expected, ndl->security_ctx.nd_pmkid, ESP_WIFI_NAN_NDP_PMKID_LEN) == 0);
|
||||||
|
if (!ok) {
|
||||||
|
ESP_LOGE(TAG, "Paired ND-PMK rejected for peer "MACSTR": NDP-Request ND-PMKID does not match cached pairing key (csid=%u), secured NDP setup aborted",
|
||||||
|
MAC2STR(peer_nmi), paired->ndp_csid);
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP(TAG, ndl->security_ctx.nd_pmkid, ESP_WIFI_NAN_NDP_PMKID_LEN, ESP_LOG_WARN);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Paired ND-PMK: peer ND-PMKID verified for "MACSTR, MAC2STR(peer_nmi));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
|
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
|
||||||
ndl->security_ctx.csid_bitmap = (uint16_t)(1u << paired->ndp_csid);
|
ndl->security_ctx.csid_bitmap = (uint16_t)(1u << paired->ndp_csid);
|
||||||
memcpy(ndl->security_ctx.nd_pmk, paired->nd_pmk, ESP_WIFI_NAN_NDP_PMK_LEN);
|
memcpy(ndl->security_ctx.nd_pmk, paired->nd_pmk, ESP_WIFI_NAN_NDP_PMK_LEN);
|
||||||
@@ -604,6 +641,357 @@ static int nan_build_rsna_key_descriptor(uint8_t *kd, uint16_t key_info_flags,
|
|||||||
return NAN_KEY_DESC_MIN_LEN;
|
return NAN_KEY_DESC_MIN_LEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
* Group Key Data (GTK/IGTK/BIGTK KDEs) — Wi-Fi Aware v4.0 §7.1.3.2/§7.1.3.5/
|
||||||
|
* §9.5.21.5. Initiator distributes in M3, responder in M4; KDEs are always
|
||||||
|
* KEK-wrapped (NIST AES Key Wrap), never in clear. Structure mirrors hostap
|
||||||
|
* src/nan/nan_sec.c nan_sec_add_kdes().
|
||||||
|
*-----------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/* True if a GTKSA was negotiated for this NDP. gtk_required is the explicit
|
||||||
|
* result of (our service has group_data_prot) AND (peer advertised NCS-GTK),
|
||||||
|
* computed at NDL finalize on both roles. We deliberately do NOT also gate on
|
||||||
|
* security_ctx.csid_bitmap: that field carries the advertised GTK bit on some
|
||||||
|
* paths and would over-fire when only one side enabled group protection. */
|
||||||
|
static bool nan_ndl_gtk_negotiated(const struct ndl_info *ndl)
|
||||||
|
{
|
||||||
|
return ndl->gtk_required;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IGTK/BIGTK negotiation gates. Per §7.1.3.5: include the IGTK/BIGTK KDE iff BOTH
|
||||||
|
* peers advertise the capability in their CSIA. igtk_required/bigtk_required are
|
||||||
|
* set at NDL finalize on both roles from (our service has group_mgmt_prot) AND
|
||||||
|
* (peer advertised IGTKSA/BIGTKSA in its CSIA caps byte). */
|
||||||
|
static bool nan_ndl_igtk_negotiated(const struct ndl_info *ndl)
|
||||||
|
{
|
||||||
|
return ndl->igtk_required;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool nan_ndl_bigtk_negotiated(const struct ndl_info *ndl)
|
||||||
|
{
|
||||||
|
return ndl->bigtk_required;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lazily generate the local data-path GTK (CCMP-128). Fresh GTK starts at RSC 0. */
|
||||||
|
static int nan_ensure_own_gtk(struct ndl_info *ndl)
|
||||||
|
{
|
||||||
|
if (ndl->own_gtk_set) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (os_get_random(ndl->own_gtk, NAN_ND_GTK_LEN) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ndl->own_gtk_len = NAN_ND_GTK_LEN;
|
||||||
|
ndl->own_gtk_keyid = 1; /* spec allows Key ID 1 or 2 */
|
||||||
|
memset(ndl->own_gtk_rsc, 0, NAN_KEY_RSC_LEN);
|
||||||
|
ndl->own_gtk_set = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lazily generate the device-global IGTK/BIGTK (BIP-CMAC-128, §7.1.3.3/§7.1.3.4).
|
||||||
|
* Exactly one key per local NMI, reused across all secured NDPs; IPN/BIPN start
|
||||||
|
* at 0. Generated by this device (transmitter) per spec. */
|
||||||
|
static int nan_ensure_own_igtk(void)
|
||||||
|
{
|
||||||
|
if (s_nan_ctx.own_igtk_set) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (os_get_random(s_nan_ctx.own_igtk, NAN_ND_GTK_LEN) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
s_nan_ctx.own_igtk_keyid = 4; /* spec allows Key ID 4 or 5 */
|
||||||
|
memset(s_nan_ctx.own_igtk_ipn, 0, sizeof(s_nan_ctx.own_igtk_ipn));
|
||||||
|
s_nan_ctx.own_igtk_set = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nan_ensure_own_bigtk(void)
|
||||||
|
{
|
||||||
|
if (s_nan_ctx.own_bigtk_set) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (os_get_random(s_nan_ctx.own_bigtk, NAN_ND_GTK_LEN) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
s_nan_ctx.own_bigtk_keyid = 6; /* spec allows Key ID 6 or 7 */
|
||||||
|
memset(s_nan_ctx.own_bigtk_bipn, 0, sizeof(s_nan_ctx.own_bigtk_bipn));
|
||||||
|
s_nan_ctx.own_bigtk_set = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate (once) and TX-install the device-global IGTK/BIGTK at NAN start, so
|
||||||
|
* Beacons (BIGTK) and group-addressed SDFs (IGTK) are BIP-protected from the
|
||||||
|
* first frame — matching peers (e.g. iOS) that protect from NAN start, not just
|
||||||
|
* after the first NDP. The keys are device-global per §7.1.3.3/§7.1.3.4 and the
|
||||||
|
* same bytes are later distributed KEK-wrapped in M3/M4. Install MUST happen
|
||||||
|
* only here (not per-NDP), else re-installing with IPN/BIPN=0 would reset the
|
||||||
|
* blob's monotonic replay counter mid-session. Best-effort: a failed install
|
||||||
|
* warns but does not block NAN start. */
|
||||||
|
void nan_security_install_own_group_integrity_keys(void)
|
||||||
|
{
|
||||||
|
uint8_t own_nmi[6];
|
||||||
|
if (!s_nan_ctx.group_mgmt_prot) {
|
||||||
|
return; /* group-management protection disabled device-wide */
|
||||||
|
}
|
||||||
|
if (esp_wifi_get_mac(WIFI_IF_NAN, own_nmi) != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "NAN start: get NMI failed; own IGTK/BIGTK TX not installed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (nan_ensure_own_igtk() == 0 && s_nan_ctx.own_igtk_set) {
|
||||||
|
int r = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
|
||||||
|
own_nmi, s_nan_ctx.own_igtk_keyid, 1,
|
||||||
|
s_nan_ctx.own_igtk_ipn, sizeof(s_nan_ctx.own_igtk_ipn),
|
||||||
|
s_nan_ctx.own_igtk, NAN_ND_GTK_LEN,
|
||||||
|
NAN_KEY_ND_IGTK);
|
||||||
|
if (r != 0) {
|
||||||
|
ESP_LOGW(TAG, "NAN start: own IGTK (TX) install failed, rc=0x%x", r);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "NAN start: own IGTK (TX) installed (keyid=%d)", s_nan_ctx.own_igtk_keyid);
|
||||||
|
}
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP("ND-IGTK", s_nan_ctx.own_igtk, NAN_ND_GTK_LEN, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
if (nan_ensure_own_bigtk() == 0 && s_nan_ctx.own_bigtk_set) {
|
||||||
|
int r = esp_wifi_set_nan_key_internal(NAN_WIFI_WPA_ALG_BIP_CMAC_128,
|
||||||
|
own_nmi, s_nan_ctx.own_bigtk_keyid, 1,
|
||||||
|
s_nan_ctx.own_bigtk_bipn, sizeof(s_nan_ctx.own_bigtk_bipn),
|
||||||
|
s_nan_ctx.own_bigtk, NAN_ND_GTK_LEN,
|
||||||
|
NAN_KEY_ND_BIGTK);
|
||||||
|
if (r != 0) {
|
||||||
|
ESP_LOGW(TAG, "NAN start: own BIGTK (TX) install failed, rc=0x%x", r);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "NAN start: own BIGTK (TX) installed (keyid=%d)", s_nan_ctx.own_bigtk_keyid);
|
||||||
|
}
|
||||||
|
ESP_LOG_BUFFER_HEXDUMP("ND-BIGTK", s_nan_ctx.own_bigtk, NAN_ND_GTK_LEN, ESP_LOG_DEBUG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nan_security_reset_own_group_keys(void)
|
||||||
|
{
|
||||||
|
forced_memzero(s_nan_ctx.own_igtk, sizeof(s_nan_ctx.own_igtk));
|
||||||
|
memset(s_nan_ctx.own_igtk_ipn, 0, sizeof(s_nan_ctx.own_igtk_ipn));
|
||||||
|
s_nan_ctx.own_igtk_keyid = 0;
|
||||||
|
s_nan_ctx.own_igtk_set = false;
|
||||||
|
|
||||||
|
forced_memzero(s_nan_ctx.own_bigtk, sizeof(s_nan_ctx.own_bigtk));
|
||||||
|
memset(s_nan_ctx.own_bigtk_bipn, 0, sizeof(s_nan_ctx.own_bigtk_bipn));
|
||||||
|
s_nan_ctx.own_bigtk_keyid = 0;
|
||||||
|
s_nan_ctx.own_bigtk_set = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 00-0F-AC RSN OUI written into every NAN KDE header. */
|
||||||
|
static const uint8_t nan_kde_rsn_oui[3] = {0x00, 0x0f, 0xac};
|
||||||
|
|
||||||
|
/* NAN KDE header (802.11 Fig 12-34: DD len OUI(3) DataType(1)); mirrors hostap
|
||||||
|
* nan_add_kde_hdr(). data_len excludes the DD/len/OUI/type bytes. */
|
||||||
|
static uint8_t *nan_kde_put_hdr(uint8_t *p, uint8_t data_type, uint8_t data_len)
|
||||||
|
{
|
||||||
|
*p++ = 0xDD;
|
||||||
|
*p++ = (uint8_t)(4 + data_len); /* OUI(3) + DataType(1) + data */
|
||||||
|
memcpy(p, nan_kde_rsn_oui, sizeof(nan_kde_rsn_oui));
|
||||||
|
p += sizeof(nan_kde_rsn_oui);
|
||||||
|
*p++ = data_type;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* IGTK KDE (§9.5.21.5: KeyID(2 LE) IPN(6) IGTK); mirrors hostap nan_sec_igtk_kde().
|
||||||
|
* Carries the device-global IGTK (BIP-CMAC-128); the peer installs it RX-only to
|
||||||
|
* verify our group-addressed management frames. */
|
||||||
|
static int nan_append_igtk_kde(struct ndl_info *ndl, uint8_t **p, const uint8_t *end)
|
||||||
|
{
|
||||||
|
if (!nan_ndl_igtk_negotiated(ndl)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((size_t)(end - *p) < NAN_KDE_HDR_LEN + NAN_IGTK_KDE_PREFIX_LEN + NAN_ND_GTK_LEN) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uint8_t *q = nan_kde_put_hdr(*p, NAN_KDE_TYPE_IGTK,
|
||||||
|
NAN_IGTK_KDE_PREFIX_LEN + NAN_ND_GTK_LEN);
|
||||||
|
*q++ = s_nan_ctx.own_igtk_keyid & 0xFF; *q++ = 0; /* KeyID (2, LE) — 4/5 */
|
||||||
|
memcpy(q, s_nan_ctx.own_igtk_ipn, 6); q += 6; /* IPN (6) */
|
||||||
|
memcpy(q, s_nan_ctx.own_igtk, NAN_ND_GTK_LEN); q += NAN_ND_GTK_LEN;
|
||||||
|
*p = q;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BIGTK KDE (§9.5.21.5: KeyID(2 LE) BIPN(6) BIGTK); mirrors hostap nan_sec_bigtk_kde().
|
||||||
|
* Carries the device-global BIGTK (BIP-CMAC-128); the peer installs it RX-only to
|
||||||
|
* verify our protected Beacon frames. */
|
||||||
|
static int nan_append_bigtk_kde(struct ndl_info *ndl, uint8_t **p, const uint8_t *end)
|
||||||
|
{
|
||||||
|
if (!nan_ndl_bigtk_negotiated(ndl)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((size_t)(end - *p) < NAN_KDE_HDR_LEN + NAN_BIGTK_KDE_PREFIX_LEN + NAN_ND_GTK_LEN) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uint8_t *q = nan_kde_put_hdr(*p, NAN_KDE_TYPE_BIGTK,
|
||||||
|
NAN_BIGTK_KDE_PREFIX_LEN + NAN_ND_GTK_LEN);
|
||||||
|
*q++ = s_nan_ctx.own_bigtk_keyid & 0xFF; *q++ = 0; /* KeyID (2, LE) — 6/7 */
|
||||||
|
memcpy(q, s_nan_ctx.own_bigtk_bipn, 6); q += 6; /* BIPN (6) */
|
||||||
|
memcpy(q, s_nan_ctx.own_bigtk, NAN_ND_GTK_LEN); q += NAN_ND_GTK_LEN;
|
||||||
|
*p = q;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* GTK KDE (§7.1.3.2/§9.5.21.5, 802.11 Fig 12-36); mirrors hostap nan_sec_gtk_kde().
|
||||||
|
* Tx bit stays 0: the peer installs this as an RX-only group key. */
|
||||||
|
static int nan_append_gtk_kde(struct ndl_info *ndl, uint8_t **p, const uint8_t *end)
|
||||||
|
{
|
||||||
|
if (!ndl->own_gtk_set || !ndl->own_gtk_len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ndl->own_gtk_keyid < 1 || ndl->own_gtk_keyid > 2) { /* §7.1.3.2: GTK Key ID is 1 or 2 */
|
||||||
|
ESP_LOGW(TAG, "GTK: invalid Key ID %u", ndl->own_gtk_keyid);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if ((size_t)(end - *p) < NAN_KDE_HDR_LEN + NAN_GTK_KDE_PREFIX_LEN + ndl->own_gtk_len) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uint8_t *q = nan_kde_put_hdr(*p, NAN_KDE_TYPE_GTK,
|
||||||
|
NAN_GTK_KDE_PREFIX_LEN + ndl->own_gtk_len);
|
||||||
|
*q++ = ndl->own_gtk_keyid & 0x03; /* KeyID (b0-1); Tx (b2)=0; b3-7 reserved */
|
||||||
|
*q++ = 0; /* Reserved */
|
||||||
|
memcpy(q, ndl->own_gtk, ndl->own_gtk_len);
|
||||||
|
*p = q + ndl->own_gtk_len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NIST AES Key Wrap of Key Data with the ND-KEK. Pads the plaintext to >=16
|
||||||
|
* octets and a multiple of 8 (0xDD then 0x00, per §12.7.2). Cipher = padded+8. */
|
||||||
|
static int nan_kek_wrap_key_data(struct ndl_info *ndl, const uint8_t *plain,
|
||||||
|
size_t plain_len, uint8_t *out, size_t out_cap,
|
||||||
|
size_t *out_len)
|
||||||
|
{
|
||||||
|
uint8_t pad[NAN_GROUP_KEY_DATA_MAX];
|
||||||
|
size_t padded = plain_len;
|
||||||
|
if (padded < 16) {
|
||||||
|
padded = 16;
|
||||||
|
}
|
||||||
|
if (padded % 8) {
|
||||||
|
padded = (padded + 7) & ~((size_t)7);
|
||||||
|
}
|
||||||
|
if (padded > sizeof(pad) || (padded + 8) > out_cap) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
memcpy(pad, plain, plain_len);
|
||||||
|
if (padded > plain_len) {
|
||||||
|
pad[plain_len] = 0xDD;
|
||||||
|
memset(pad + plain_len + 1, 0, padded - plain_len - 1);
|
||||||
|
}
|
||||||
|
int rc = aes_wrap(ndl->nd_kek, ndl->kek_len, (int)(padded / 8), pad, out);
|
||||||
|
forced_memzero(pad, sizeof(pad)); /* scrub plaintext group keys */
|
||||||
|
if (rc != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*out_len = padded + 8;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NIST AES Key Unwrap of received Key Data with the ND-KEK. Plain = cipher-8. */
|
||||||
|
static int nan_kek_unwrap_key_data(struct ndl_info *ndl, const uint8_t *cipher,
|
||||||
|
size_t cipher_len, uint8_t *out, size_t out_cap,
|
||||||
|
size_t *out_len)
|
||||||
|
{
|
||||||
|
if (cipher_len < 24 || (cipher_len % 8) != 0) { /* >=16 plaintext + 8 wrap */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
size_t plain_len = cipher_len - 8;
|
||||||
|
if (plain_len > out_cap) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (aes_unwrap(ndl->nd_kek, ndl->kek_len, (int)(plain_len / 8), cipher, out) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*out_len = plain_len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Build the local group Key Data (KDE order IGTK, BIGTK, GTK per upstream),
|
||||||
|
* KEK-wrap it, and patch Encrypted-Data bit + Key Data Length + Key RSC into
|
||||||
|
* the 95-byte descriptor in place. Returns the extended descriptor length, or
|
||||||
|
* NAN_KEY_DESC_MIN_LEN (pairwise-only) on negotiation-off or any error so the
|
||||||
|
* handshake still completes. Mirrors hostap nan_sec_add_kdes(); §7.1.3.5: KDEs
|
||||||
|
* are sent only KEK-wrapped, never in clear. */
|
||||||
|
static int nan_append_own_group_kdes(struct ndl_info *ndl, uint8_t *key_desc, size_t desc_cap)
|
||||||
|
{
|
||||||
|
bool want_gtk = nan_ndl_gtk_negotiated(ndl);
|
||||||
|
bool want_igtk = nan_ndl_igtk_negotiated(ndl);
|
||||||
|
bool want_bigtk = nan_ndl_bigtk_negotiated(ndl);
|
||||||
|
if (!want_gtk && !want_igtk && !want_bigtk) {
|
||||||
|
return NAN_KEY_DESC_MIN_LEN;
|
||||||
|
}
|
||||||
|
if (!ndl->ptk_set) {
|
||||||
|
ESP_LOGW(TAG, "Group KDEs [%s%s%s]: PTK/KEK not set; sending without group keys",
|
||||||
|
want_gtk ? "GTK " : "", want_igtk ? "IGTK " : "", want_bigtk ? "BIGTK " : "");
|
||||||
|
return NAN_KEY_DESC_MIN_LEN;
|
||||||
|
}
|
||||||
|
if (want_gtk && nan_ensure_own_gtk(ndl) != 0) {
|
||||||
|
ESP_LOGW(TAG, "GTK: own key generation failed; sending without group keys");
|
||||||
|
return NAN_KEY_DESC_MIN_LEN;
|
||||||
|
}
|
||||||
|
if (want_igtk && nan_ensure_own_igtk() != 0) {
|
||||||
|
ESP_LOGW(TAG, "IGTK: own key generation failed; sending without group keys");
|
||||||
|
return NAN_KEY_DESC_MIN_LEN;
|
||||||
|
}
|
||||||
|
if (want_bigtk && nan_ensure_own_bigtk() != 0) {
|
||||||
|
ESP_LOGW(TAG, "BIGTK: own key generation failed; sending without group keys");
|
||||||
|
return NAN_KEY_DESC_MIN_LEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t plain[NAN_GROUP_KEY_DATA_MAX];
|
||||||
|
uint8_t *p = plain;
|
||||||
|
const uint8_t *end = plain + sizeof(plain);
|
||||||
|
size_t plain_len = 0;
|
||||||
|
size_t wrapped_len = 0;
|
||||||
|
uint16_t key_info = 0;
|
||||||
|
int ret = NAN_KEY_DESC_MIN_LEN;
|
||||||
|
|
||||||
|
if (nan_append_igtk_kde(ndl, &p, end) < 0 ||
|
||||||
|
nan_append_bigtk_kde(ndl, &p, end) < 0 ||
|
||||||
|
nan_append_gtk_kde(ndl, &p, end) < 0) {
|
||||||
|
ESP_LOGW(TAG, "Group KDEs [%s%s%s]: assembly failed; sending without group keys",
|
||||||
|
want_gtk ? "GTK " : "", want_igtk ? "IGTK " : "", want_bigtk ? "BIGTK " : "");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
plain_len = (size_t)(p - plain);
|
||||||
|
if (plain_len == 0) {
|
||||||
|
goto out; /* nothing negotiated to send */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nan_kek_wrap_key_data(ndl, plain, plain_len,
|
||||||
|
&key_desc[NAN_KEY_DESC_DATA_OFF],
|
||||||
|
desc_cap > NAN_KEY_DESC_DATA_OFF ?
|
||||||
|
desc_cap - NAN_KEY_DESC_DATA_OFF : 0,
|
||||||
|
&wrapped_len) != 0) {
|
||||||
|
ESP_LOGW(TAG, "Group KDEs [%s%s%s]: KEK wrap failed or no headroom; sending without group keys",
|
||||||
|
want_gtk ? "GTK " : "", want_igtk ? "IGTK " : "", want_bigtk ? "BIGTK " : "");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
key_info = (key_desc[NAN_KEY_DESC_KEY_INFO_OFF] << 8) |
|
||||||
|
key_desc[NAN_KEY_DESC_KEY_INFO_OFF + 1];
|
||||||
|
key_info |= NAN_KEY_INFO_ENC_KEY;
|
||||||
|
key_desc[NAN_KEY_DESC_KEY_INFO_OFF] = (key_info >> 8) & 0xFF;
|
||||||
|
key_desc[NAN_KEY_DESC_KEY_INFO_OFF + 1] = key_info & 0xFF;
|
||||||
|
key_desc[NAN_KEY_DESC_DATA_LEN_OFF] = (wrapped_len >> 8) & 0xFF;
|
||||||
|
key_desc[NAN_KEY_DESC_DATA_LEN_OFF + 1] = wrapped_len & 0xFF;
|
||||||
|
|
||||||
|
/* Key RSC carries the GTK's RSC only when a GTK KDE is present (§7.1.3.5). */
|
||||||
|
if (want_gtk) {
|
||||||
|
memcpy(&key_desc[NAN_KEY_DESC_RSC_OFF], ndl->own_gtk_rsc, NAN_KEY_RSC_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Group Key Data wrapped: %u plain -> %u wrapped bytes, KDEs=[%s%s%s]",
|
||||||
|
(unsigned)plain_len, (unsigned)wrapped_len,
|
||||||
|
want_gtk ? "GTK " : "", want_igtk ? "IGTK " : "", want_bigtk ? "BIGTK " : "");
|
||||||
|
ret = NAN_KEY_DESC_MIN_LEN + (int)wrapped_len;
|
||||||
|
|
||||||
|
out:
|
||||||
|
forced_memzero(plain, sizeof(plain)); /* scrub assembled plaintext group keys */
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Internal SCIA builder shared by Publish SDF and NDP-Response paths.
|
* Internal SCIA builder shared by Publish SDF and NDP-Response paths.
|
||||||
* Per-entry layout: Len(2) + Type(1) + PubID(1) + Value(PMKID_LEN) = 20 bytes.
|
* Per-entry layout: Len(2) + Type(1) + PubID(1) + Value(PMKID_LEN) = 20 bytes.
|
||||||
@@ -636,7 +1024,8 @@ static int nan_build_scia_attr(uint8_t *frm, uint8_t pub_id,
|
|||||||
p += ESP_WIFI_NAN_NDP_PMKID_LEN;
|
p += ESP_WIFI_NAN_NDP_PMKID_LEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int)(p - frm);
|
int written = (int)(p - frm);
|
||||||
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -871,10 +1260,24 @@ esp_err_t nan_derive_security_params(const char *service_name,
|
|||||||
}
|
}
|
||||||
out_derived[i].type = WIFI_NAN_SECURITY_ENCRYPTED;
|
out_derived[i].type = WIFI_NAN_SECURITY_ENCRYPTED;
|
||||||
out_derived[i].csid_bitmap = (uint16_t)(1u << cred->csid);
|
out_derived[i].csid_bitmap = (uint16_t)(1u << cred->csid);
|
||||||
|
/* Advertise the basic-default NCS-GTK suite alongside the credential's
|
||||||
|
* PMK cipher when the service enables group-addressed data protection.
|
||||||
|
* The blob unions derived_security[].csid_bitmap into the on-air CSIA,
|
||||||
|
* so this is what makes us announce GTK support (§7.1.3.5). The bit is
|
||||||
|
* masked back out for the pairwise/link cipher query (see
|
||||||
|
* nan_get_ndp_security_csid). */
|
||||||
|
if (sec_cfg->group_data_prot) {
|
||||||
|
out_derived[i].csid_bitmap |= NAN_CSID_GTK_DEFAULT;
|
||||||
|
}
|
||||||
out_derived[i].group_data_prot = sec_cfg->group_data_prot;
|
out_derived[i].group_data_prot = sec_cfg->group_data_prot;
|
||||||
out_derived[i].group_mgmt_prot = sec_cfg->group_mgmt_prot;
|
out_derived[i].group_mgmt_prot = sec_cfg->group_mgmt_prot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sec_cfg->group_data_prot) {
|
||||||
|
ESP_LOGI(TAG, "NAN GTK: advertising NCS-GTK-CCMP-128 (group_data_prot=1) for svc='%s'",
|
||||||
|
service_name);
|
||||||
|
}
|
||||||
|
|
||||||
/* Mirror the derived material into the host's own_svc_info slot for this
|
/* Mirror the derived material into the host's own_svc_info slot for this
|
||||||
* service (claimed before the blob's publish/subscribe call). This lets
|
* service (claimed before the blob's publish/subscribe call). This lets
|
||||||
* host paths — nan_match_pmkid (responder M1) and the NDL seeding at
|
* host paths — nan_match_pmkid (responder M1) and the NDL seeding at
|
||||||
@@ -913,6 +1316,16 @@ uint16_t nan_get_ndp_security_csid(uint8_t ndp_id, const uint8_t *peer_nmi)
|
|||||||
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
|
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
|
||||||
uint16_t csid = ndl ? ndl->security_ctx.csid_bitmap : 0;
|
uint16_t csid = ndl ? ndl->security_ctx.csid_bitmap : 0;
|
||||||
NAN_DATA_UNLOCK();
|
NAN_DATA_UNLOCK();
|
||||||
|
/* Return the FULL negotiated bitmap, INCLUDING NCS-GTK when group-addressed
|
||||||
|
* data protection is in use, so the on-air NDP Request/Response CSIA
|
||||||
|
* advertises the group cipher and the peer can detect our group-data support
|
||||||
|
* (required for symmetric GTK distribution and third-party/iOS/Android
|
||||||
|
* interop). Safe to include NCS-GTK here: the blob treats this value as an
|
||||||
|
* opaque bitmap that it passes straight to the host CSIA callbacks
|
||||||
|
* (construct_csia / get_csia_len) and never interprets individual bits.
|
||||||
|
* Pairwise/link cipher selection and ND-TK install are
|
||||||
|
* host-driven and do not read this field; the group key has its own install
|
||||||
|
* path (NAN_KEY_ND_GTK) and gtk_required gate. */
|
||||||
return csid;
|
return csid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -933,6 +1346,19 @@ bool nan_security_service_match(const struct own_svc_info *own_svc,
|
|||||||
|
|
||||||
const wifi_nan_discovery_security_params_t *cfg = &own_svc->user_cfg;
|
const wifi_nan_discovery_security_params_t *cfg = &own_svc->user_cfg;
|
||||||
|
|
||||||
|
/* Remember whether this publisher signalled group-data (GTK) support, so the
|
||||||
|
* initiator NDP-req path can gate GTK distribution on mutual support. The
|
||||||
|
* spec-correct signal is the CSIA Capabilities byte (parsed into
|
||||||
|
* group_data_prot by esp_nan_parse_publish_security); an NCS-GTK cipher-suite
|
||||||
|
* ID is the legacy Android/ESP signal and is OR'd in for interop. */
|
||||||
|
peer_svc->peer_group_data_cap = (peer_sec->group_data_prot ||
|
||||||
|
(peer_sec->csid_bitmap & NAN_CSID_GTK_BITS)) ? 1 : 0;
|
||||||
|
/* IGTK/BIGTK (group management) support comes from the CSIA Capabilities
|
||||||
|
* byte (parsed into group_mgmt_prot/group_bigtk_prot by
|
||||||
|
* esp_nan_parse_publish_security): IGTKSA = bits 1-2 != 0, BIGTKSA = 10. */
|
||||||
|
peer_svc->peer_group_mgmt_cap = peer_sec->group_mgmt_prot ? 1 : 0;
|
||||||
|
peer_svc->peer_group_bigtk_cap = peer_sec->group_bigtk_prot ? 1 : 0;
|
||||||
|
|
||||||
if (cfg->num_credentials == 0 ||
|
if (cfg->num_credentials == 0 ||
|
||||||
cfg->num_credentials > ESP_WIFI_NAN_MAX_CREDS_PER_SVC) {
|
cfg->num_credentials > ESP_WIFI_NAN_MAX_CREDS_PER_SVC) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1117,6 +1543,35 @@ esp_err_t nan_security_populate_initiator_ndl(struct ndl_info *ndl,
|
|||||||
|
|
||||||
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
|
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
|
||||||
ndl->security_ctx.csid_bitmap = (uint16_t)(1u << ndp_csid);
|
ndl->security_ctx.csid_bitmap = (uint16_t)(1u << ndp_csid);
|
||||||
|
/* Distribute a GTK in M3 only if we enabled group_data_prot AND the
|
||||||
|
* publisher advertised an NCS-GTK suite (recorded at SDF match). */
|
||||||
|
ndl->gtk_required = (cfg->group_data_prot && peer_svc &&
|
||||||
|
peer_svc->peer_group_data_cap) ? 1 : 0;
|
||||||
|
/* Advertise NCS-GTK in the NDP-Request CSIA so any responder (incl.
|
||||||
|
* third-party/iOS/Android) can detect our group-data support and
|
||||||
|
* reciprocate. Carried in ndl->security_ctx.csid_bitmap, which
|
||||||
|
* nan_get_ndp_security_csid() returns verbatim to the blob for the on-air
|
||||||
|
* M1/M2 CSIA. Pairwise cipher selection / ND-TK install do not read this
|
||||||
|
* field, so including the group bit here is safe. */
|
||||||
|
if (ndl->gtk_required) {
|
||||||
|
ndl->security_ctx.csid_bitmap |= NAN_CSID_GTK_DEFAULT;
|
||||||
|
}
|
||||||
|
if (cfg->group_data_prot) {
|
||||||
|
ESP_LOGI(TAG, "NDP GTK: %s (initiator) - own group_prot=1, peer NCS-GTK=%d",
|
||||||
|
ndl->gtk_required ? "negotiated" : "NOT negotiated",
|
||||||
|
(peer_svc && peer_svc->peer_group_data_cap) ? 1 : 0);
|
||||||
|
}
|
||||||
|
/* IGTK distributed in M3 iff we enabled group_mgmt_prot AND the publisher
|
||||||
|
* advertised IGTKSA; BIGTK additionally requires BIGTKSA (§7.1.3.5). */
|
||||||
|
bool peer_igtk = peer_svc && peer_svc->peer_group_mgmt_cap;
|
||||||
|
bool peer_bigtk = peer_svc && peer_svc->peer_group_bigtk_cap;
|
||||||
|
ndl->igtk_required = (s_nan_ctx.group_mgmt_prot && peer_igtk) ? 1 : 0;
|
||||||
|
ndl->bigtk_required = (s_nan_ctx.group_mgmt_prot && peer_bigtk) ? 1 : 0;
|
||||||
|
if (s_nan_ctx.group_mgmt_prot) {
|
||||||
|
ESP_LOGI(TAG, "NDP IGTK/BIGTK: igtk=%s bigtk=%s (initiator) - peer igtk=%d bigtk=%d",
|
||||||
|
ndl->igtk_required ? "yes" : "no", ndl->bigtk_required ? "yes" : "no",
|
||||||
|
peer_igtk, peer_bigtk);
|
||||||
|
}
|
||||||
memcpy(ndl->security_ctx.nd_pmk, pmk, ESP_WIFI_NAN_NDP_PMK_LEN);
|
memcpy(ndl->security_ctx.nd_pmk, pmk, ESP_WIFI_NAN_NDP_PMK_LEN);
|
||||||
memcpy(ndl->security_ctx.nd_pmkid, pair_pmkid, ESP_WIFI_NAN_NDP_PMKID_LEN);
|
memcpy(ndl->security_ctx.nd_pmkid, pair_pmkid, ESP_WIFI_NAN_NDP_PMKID_LEN);
|
||||||
|
|
||||||
@@ -1153,8 +1608,27 @@ int esp_nan_construct_csia(uint8_t *frm, uint8_t pub_id, uint16_t own_csid_bitma
|
|||||||
*p++ = attr_len & 0xFF;
|
*p++ = attr_len & 0xFF;
|
||||||
*p++ = (attr_len >> 8) & 0xFF;
|
*p++ = (attr_len >> 8) & 0xFF;
|
||||||
|
|
||||||
/* Capabilities byte (0x4 set for iOS interop) */
|
/* Capabilities group-SA bits (§9.5.21.2 Table 122) are strictly nested:
|
||||||
*p++ = 0x4;
|
* 00 = none, 01 = GTKSA+IGTKSA, 10 = GTKSA+IGTKSA+BIGTKSA.
|
||||||
|
* There is no "IGTK/BIGTK without GTKSA" codepoint, so:
|
||||||
|
* - device-global group_mgmt_prot set -> 10 (BIGTK forces GTK+IGTK; we also
|
||||||
|
* force GTKSA on every secured service, see nan_claim_own_svc_slot);
|
||||||
|
* - else if this CSIA carries a GTK suite -> 01 (GTKSA mandates IGTKSA);
|
||||||
|
* - else -> 00.
|
||||||
|
* Advertising support never blocks a peer that lacks protection: the keys and
|
||||||
|
* frame protection are set up only AFTER mutual capability negotiation — the
|
||||||
|
* IGTK/BIGTK/GTK KDEs are exchanged iff BOTH peers advertise support
|
||||||
|
* (§7.1.3.5), and a non-supporting peer ignores the unknown MME elements and
|
||||||
|
* still connects. */
|
||||||
|
uint8_t grp_caps;
|
||||||
|
if (s_nan_ctx.group_mgmt_prot) {
|
||||||
|
grp_caps = (uint8_t)(NAN_CSIA_CAP_GTK_SUPP_ALL << NAN_CSIA_CAP_GTK_SUPP_POS); /* 0x04 (10) */
|
||||||
|
} else if (own_csid_bitmap & NAN_CSID_GTK_DEFAULT) {
|
||||||
|
grp_caps = (uint8_t)(NAN_CSIA_CAP_GTK_SUPP_IGTK << NAN_CSIA_CAP_GTK_SUPP_POS); /* 0x02 (01) */
|
||||||
|
} else {
|
||||||
|
grp_caps = NAN_CSIA_CAP_GTK_SUPP_NONE; /* 0x00 (00) */
|
||||||
|
}
|
||||||
|
*p++ = grp_caps;
|
||||||
|
|
||||||
/* Cipher Suite ID list: [CSID(1)] [Publish ID(1)] */
|
/* Cipher Suite ID list: [CSID(1)] [Publish ID(1)] */
|
||||||
for (uint8_t csid = 1; csid <= 8; csid++) {
|
for (uint8_t csid = 1; csid <= 8; csid++) {
|
||||||
@@ -1164,7 +1638,8 @@ int esp_nan_construct_csia(uint8_t *frm, uint8_t pub_id, uint16_t own_csid_bitma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int)(p - frm);
|
int written = (int)(p - frm);
|
||||||
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
int esp_nan_construct_scia_publish(uint8_t *frm, uint8_t pub_id,
|
int esp_nan_construct_scia_publish(uint8_t *frm, uint8_t pub_id,
|
||||||
@@ -1228,8 +1703,6 @@ uint32_t esp_nan_get_csia_len(uint16_t own_csid_bitmap, uint16_t peer_csid_bitma
|
|||||||
}
|
}
|
||||||
uint8_t num_csids = __builtin_popcount(csid_bitmap);
|
uint8_t num_csids = __builtin_popcount(csid_bitmap);
|
||||||
uint32_t len = 3 + 1 + 2 * num_csids;
|
uint32_t len = 3 + 1 + 2 * num_csids;
|
||||||
ESP_LOGD(TAG, "GET CSIA LEN: %lu (own=0x%04x, peer=0x%04x, num_csids=%d)",
|
|
||||||
len, own_csid_bitmap, peer_csid_bitmap, num_csids);
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1256,9 +1729,112 @@ uint32_t esp_nan_get_shared_key_desc_attr_len(uint16_t key_data_len)
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
int esp_nan_ndp_security_install_get_shared_desc_len(void)
|
/* Which group KDEs a key descriptor carries (for sizing + logging). */
|
||||||
|
#define NAN_KDE_PRESENT_GTK BIT(0)
|
||||||
|
#define NAN_KDE_PRESENT_IGTK BIT(1)
|
||||||
|
#define NAN_KDE_PRESENT_BIGTK BIT(2)
|
||||||
|
|
||||||
|
/* Exact wrapped group Key Data length this NDL will emit — mirrors what
|
||||||
|
* nan_append_own_group_kdes() writes, with NO side effects (does not generate the
|
||||||
|
* GTK). Sets *kde_mask to the included KDEs. 0 = no group keys (pairwise-only).
|
||||||
|
* Caller holds the lock. Keep in lockstep with nan_append_{igtk,bigtk,gtk}_kde(). */
|
||||||
|
static size_t nan_group_key_data_wrapped_len(const struct ndl_info *ndl, uint8_t *kde_mask)
|
||||||
{
|
{
|
||||||
return (int)esp_nan_get_shared_key_desc_attr_len(0);
|
uint8_t mask = 0;
|
||||||
|
size_t plain = 0;
|
||||||
|
if (!ndl || !ndl->ptk_set) {
|
||||||
|
if (kde_mask) {
|
||||||
|
*kde_mask = 0;
|
||||||
|
}
|
||||||
|
return 0; /* no KEK yet -> nothing can be wrapped */
|
||||||
|
}
|
||||||
|
/* Order matches the builder: IGTK, BIGTK, GTK. Each branch contributes iff
|
||||||
|
* its negotiation gate (igtk/bigtk/gtk_required) fired for this NDP. */
|
||||||
|
if (nan_ndl_igtk_negotiated(ndl)) {
|
||||||
|
plain += NAN_KDE_HDR_LEN + NAN_IGTK_KDE_PREFIX_LEN + NAN_ND_GTK_LEN;
|
||||||
|
mask |= NAN_KDE_PRESENT_IGTK;
|
||||||
|
}
|
||||||
|
if (nan_ndl_bigtk_negotiated(ndl)) {
|
||||||
|
plain += NAN_KDE_HDR_LEN + NAN_BIGTK_KDE_PREFIX_LEN + NAN_ND_GTK_LEN;
|
||||||
|
mask |= NAN_KDE_PRESENT_BIGTK;
|
||||||
|
}
|
||||||
|
if (nan_ndl_gtk_negotiated(ndl)) {
|
||||||
|
plain += NAN_KDE_HDR_LEN + NAN_GTK_KDE_PREFIX_LEN + NAN_ND_GTK_LEN;
|
||||||
|
mask |= NAN_KDE_PRESENT_GTK;
|
||||||
|
}
|
||||||
|
if (kde_mask) {
|
||||||
|
*kde_mask = mask;
|
||||||
|
}
|
||||||
|
if (plain == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
size_t padded = plain < 16 ? 16 : plain; /* NIST AES Key Wrap minimum */
|
||||||
|
if (padded % 8) {
|
||||||
|
padded = (padded + 7) & ~((size_t)7);
|
||||||
|
}
|
||||||
|
return padded + 8; /* + AES-Key-Wrap overhead */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* INFO log of the descriptor length and which group KDEs it carries, so on-device
|
||||||
|
* logs show what was sized/sent (not a silent length). */
|
||||||
|
static void nan_log_group_desc_len(const char *msg, uint8_t ndp_id, int ret,
|
||||||
|
uint16_t key_data_len, uint8_t kde_mask,
|
||||||
|
bool found, bool ptk_set)
|
||||||
|
{
|
||||||
|
if (!found) {
|
||||||
|
ESP_LOGW(TAG, "%s desc len: no NDL (ndp_id=%d) -> len=%d (no Key Data)",
|
||||||
|
msg, ndp_id, ret);
|
||||||
|
} else if (key_data_len == 0) {
|
||||||
|
ESP_LOGI(TAG, "%s desc len=%d (ndp_id=%d): no group KDEs (ptk_set=%d)",
|
||||||
|
msg, ret, ndp_id, ptk_set);
|
||||||
|
} else {
|
||||||
|
ESP_LOGI(TAG, "%s desc len=%d (ndp_id=%d): Key Data=%u KDEs=[%s%s%s]",
|
||||||
|
msg, ret, ndp_id, key_data_len,
|
||||||
|
(kde_mask & NAN_KDE_PRESENT_GTK) ? "GTK " : "",
|
||||||
|
(kde_mask & NAN_KDE_PRESENT_IGTK) ? "IGTK " : "",
|
||||||
|
(kde_mask & NAN_KDE_PRESENT_BIGTK) ? "BIGTK " : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exact M4 (NDP Security Install) Shared Key Descriptor length for this NDP, so the
|
||||||
|
* blob allocates exactly what the builder writes (no on-air zero-padding). */
|
||||||
|
int esp_nan_ndp_security_install_get_shared_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi)
|
||||||
|
{
|
||||||
|
uint16_t key_data_len = 0;
|
||||||
|
uint8_t kde_mask = 0;
|
||||||
|
bool found = false, ptk = false;
|
||||||
|
NAN_DATA_LOCK();
|
||||||
|
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
|
||||||
|
if (ndl) {
|
||||||
|
found = true;
|
||||||
|
ptk = ndl->ptk_set;
|
||||||
|
key_data_len = (uint16_t)nan_group_key_data_wrapped_len(ndl, &kde_mask);
|
||||||
|
}
|
||||||
|
NAN_DATA_UNLOCK();
|
||||||
|
|
||||||
|
int ret = (int)esp_nan_get_shared_key_desc_attr_len(key_data_len);
|
||||||
|
nan_log_group_desc_len("M4 Security Install", ndp_id, ret, key_data_len, kde_mask, found, ptk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exact M3 (NDP Confirm) length for the initiator path; same contract as M4. */
|
||||||
|
int esp_nan_ndp_confirm_get_shared_desc_len(uint8_t ndp_id, const uint8_t *peer_nmi)
|
||||||
|
{
|
||||||
|
uint16_t key_data_len = 0;
|
||||||
|
uint8_t kde_mask = 0;
|
||||||
|
bool found = false, ptk = false;
|
||||||
|
NAN_DATA_LOCK();
|
||||||
|
struct ndl_info *ndl = nan_find_ndl(ndp_id, (uint8_t *)peer_nmi);
|
||||||
|
if (ndl) {
|
||||||
|
found = true;
|
||||||
|
ptk = ndl->ptk_set;
|
||||||
|
key_data_len = (uint16_t)nan_group_key_data_wrapped_len(ndl, &kde_mask);
|
||||||
|
}
|
||||||
|
NAN_DATA_UNLOCK();
|
||||||
|
|
||||||
|
int ret = (int)esp_nan_get_shared_key_desc_attr_len(key_data_len);
|
||||||
|
nan_log_group_desc_len("M3 Confirm", ndp_id, ret, key_data_len, kde_mask, found, ptk);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int esp_nan_get_ndp_resp_shared_key_desc(uint8_t *buf, size_t buf_len, uint8_t ndp_id, const uint8_t *peer_nmi)
|
int esp_nan_get_ndp_resp_shared_key_desc(uint8_t *buf, size_t buf_len, uint8_t ndp_id, const uint8_t *peer_nmi)
|
||||||
@@ -1377,14 +1953,12 @@ int esp_nan_get_ndp_security_install_key_desc(uint8_t *buf, size_t buf_len, uint
|
|||||||
|
|
||||||
uint8_t *p = buf;
|
uint8_t *p = buf;
|
||||||
*p++ = NAN_ATTR_ID_SHARED_KEY_DESC;
|
*p++ = NAN_ATTR_ID_SHARED_KEY_DESC;
|
||||||
{
|
uint8_t *len_field = p; /* backpatched once Key Data length is known */
|
||||||
uint16_t body_len = 1 + NAN_KEY_DESC_MIN_LEN;
|
p += 2;
|
||||||
*p++ = body_len & 0xFF;
|
|
||||||
*p++ = (body_len >> 8) & 0xFF;
|
|
||||||
}
|
|
||||||
*p++ = ndl->publisher_id;
|
*p++ = ndl->publisher_id;
|
||||||
|
|
||||||
int n = nan_build_rsna_key_descriptor(p,
|
uint8_t *key_desc = p;
|
||||||
|
int n = nan_build_rsna_key_descriptor(key_desc,
|
||||||
NAN_KEY_INFO_MIC |
|
NAN_KEY_INFO_MIC |
|
||||||
NAN_KEY_INFO_SECURE |
|
NAN_KEY_INFO_SECURE |
|
||||||
NAN_KEY_INFO_INSTALL,
|
NAN_KEY_INFO_INSTALL,
|
||||||
@@ -1396,11 +1970,18 @@ int esp_nan_get_ndp_security_install_key_desc(uint8_t *buf, size_t buf_len, uint
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = 3 + 1 + n;
|
/* Responder distributes its GTK in M4 (§7.1.3.2). Buffer headroom comes
|
||||||
|
* from esp_nan_ndp_security_install_get_shared_desc_len(); falls back to
|
||||||
|
* pairwise-only if the blob under-allocated the buffer. */
|
||||||
|
n = nan_append_own_group_kdes(ndl, key_desc, buf_len - 4);
|
||||||
|
|
||||||
|
uint16_t body_len = 1 + (uint16_t)n;
|
||||||
|
len_field[0] = body_len & 0xFF;
|
||||||
|
len_field[1] = (body_len >> 8) & 0xFF;
|
||||||
|
|
||||||
NAN_DATA_UNLOCK();
|
NAN_DATA_UNLOCK();
|
||||||
ESP_LOGD(TAG, "NDP M4 Key Desc: built for ndp_id=%d", ndp_id);
|
ESP_LOGD(TAG, "NDP M4 Key Desc: built (key_desc=%d bytes) for ndp_id=%d", n, ndp_id);
|
||||||
return n;
|
return 3 + 1 + n;
|
||||||
}
|
}
|
||||||
|
|
||||||
int esp_nan_update_ndp_resp_mic(uint8_t *m2_body, size_t body_len, uint8_t *key_desc_attr,
|
int esp_nan_update_ndp_resp_mic(uint8_t *m2_body, size_t body_len, uint8_t *key_desc_attr,
|
||||||
@@ -1456,6 +2037,24 @@ void esp_nan_parse_ndp_csia(void *frm, size_t buf_len, wifi_nan_security_params_
|
|||||||
s_pending_scia.has_csid = true;
|
s_pending_scia.has_csid = true;
|
||||||
s_pending_scia.pub_id = pub_id;
|
s_pending_scia.pub_id = pub_id;
|
||||||
s_pending_scia.csid_bitmap = accum;
|
s_pending_scia.csid_bitmap = accum;
|
||||||
|
/* Peer wants group-data (GTK) protection if it advertises GTKSA
|
||||||
|
* support in the CSIA Capabilities byte (body[0] bits 1-2,
|
||||||
|
* §9.5.21.2 Table 122 — how iOS signals it) or lists an NCS-GTK
|
||||||
|
* cipher suite (how Android/ESP signal it). The Capabilities byte
|
||||||
|
* is the spec-correct indicator; an NCS-GTK CSID only selects WHICH
|
||||||
|
* group cipher and need not be present. */
|
||||||
|
s_pending_scia.peer_group_data =
|
||||||
|
((body[0] & NAN_CSIA_CAP_GTK_SUPP_MASK) ||
|
||||||
|
(accum & NAN_CSID_GTK_BITS)) ? true : false;
|
||||||
|
if (s_pending_scia.peer_group_data) {
|
||||||
|
param->group_data_prot = 1;
|
||||||
|
}
|
||||||
|
/* Store the raw CSIA Capabilities byte; IGTK/BIGTK gating derives
|
||||||
|
* bits 1-2 (01=IGTKSA, 10=+BIGTKSA) independently at NDL finalize. */
|
||||||
|
s_pending_scia.peer_caps = body[0];
|
||||||
|
if (body[0] & NAN_CSIA_CAP_GTK_SUPP_MASK) {
|
||||||
|
param->group_mgmt_prot = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1612,57 +2211,106 @@ void esp_nan_parse_ndp_key_desc(void *frm, size_t buf_len, uint8_t ndp_id, const
|
|||||||
|
|
||||||
memcpy(ndl->key_rsc, key_rsc, NAN_KEY_RSC_LEN);
|
memcpy(ndl->key_rsc, key_rsc, NAN_KEY_RSC_LEN);
|
||||||
|
|
||||||
/* Parse Key Data (KDEs) - only when not encrypted */
|
/* Parse Key Data (KDEs). Per §7.1.3.5 group keys are always carried
|
||||||
if (key_data_len > 0 && (NAN_KEY_DESC_DATA_OFF + key_data_len <= key_desc_len) && !has_enc_key) {
|
* KEK-wrapped, so decrypt with the ND-KEK before walking. The plaintext
|
||||||
uint8_t *key_data = &key_desc[NAN_KEY_DESC_DATA_OFF];
|
* may carry 0xDD/0x00 AES-Key-Wrap padding after the last KDE. */
|
||||||
|
if (key_data_len > 0 && (NAN_KEY_DESC_DATA_OFF + key_data_len <= key_desc_len)) {
|
||||||
|
uint8_t plain[NAN_GROUP_KEY_DATA_MAX];
|
||||||
|
const uint8_t *kde_buf = &key_desc[NAN_KEY_DESC_DATA_OFF];
|
||||||
|
size_t kde_len = key_data_len;
|
||||||
|
|
||||||
|
if (has_enc_key) {
|
||||||
|
size_t unwrapped = 0;
|
||||||
|
if (!ndl->ptk_set) {
|
||||||
|
ESP_LOGW(TAG, "NDP Key Desc: encrypted Key Data but PTK/KEK not set; skipping KDEs");
|
||||||
|
kde_len = 0;
|
||||||
|
} else if (nan_kek_unwrap_key_data(ndl, &key_desc[NAN_KEY_DESC_DATA_OFF],
|
||||||
|
key_data_len, plain, sizeof(plain),
|
||||||
|
&unwrapped) != 0) {
|
||||||
|
ESP_LOGW(TAG, "NDP Key Desc: KEK unwrap of Key Data failed; skipping KDEs");
|
||||||
|
kde_len = 0;
|
||||||
|
} else {
|
||||||
|
kde_buf = plain;
|
||||||
|
kde_len = unwrapped;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* §7.1.3.5 / 802.11 §12.7.2: group KDEs are only ever carried
|
||||||
|
* KEK-wrapped; ignore any Key Data presented in the clear. */
|
||||||
|
ESP_LOGW(TAG, "NDP Key Desc: Key Data not encrypted; ignoring KDEs");
|
||||||
|
kde_len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t kd_offset = 0;
|
uint16_t kd_offset = 0;
|
||||||
|
while (kd_offset + 2 <= kde_len) {
|
||||||
while (kd_offset + 2 <= key_data_len) {
|
uint8_t kde_id = kde_buf[kd_offset];
|
||||||
uint8_t kde_type = key_data[kd_offset];
|
uint8_t kde_flen = kde_buf[kd_offset + 1];
|
||||||
uint8_t kde_len = key_data[kd_offset + 1];
|
/* All KDEs start with 0xDD; a 0xDD/0x00 pad (or any short
|
||||||
|
* element) terminates the meaningful list. */
|
||||||
if (kd_offset + 2 + kde_len > key_data_len) {
|
if (kde_id != 0xDD || kde_flen < 4 ||
|
||||||
|
kd_offset + 2 + kde_flen > kde_len) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kde_type == 0xDD && kde_len >= 4) {
|
uint32_t oui = (kde_buf[kd_offset + 2] << 16) |
|
||||||
uint32_t oui = (key_data[kd_offset + 2] << 16) | (key_data[kd_offset + 3] << 8) | key_data[kd_offset + 4];
|
(kde_buf[kd_offset + 3] << 8) | kde_buf[kd_offset + 4];
|
||||||
uint8_t data_type = key_data[kd_offset + 5];
|
uint8_t data_type = kde_buf[kd_offset + 5];
|
||||||
uint8_t *data = &key_data[kd_offset + 6];
|
const uint8_t *body = &kde_buf[kd_offset + 6];
|
||||||
uint8_t data_len = kde_len - 4;
|
uint8_t body_len = kde_flen - 4; /* after OUI(3) + DataType(1) */
|
||||||
|
|
||||||
if (oui == 0x000FAC) { /* WFA OUI */
|
if (oui == NAN_KDE_OUI_RSN) {
|
||||||
if (data_type == 1 && data_len <= NAN_GTK_MAX_LEN) {
|
if (data_type == NAN_KDE_TYPE_GTK && body_len > NAN_GTK_KDE_PREFIX_LEN) {
|
||||||
memcpy(ndl->gtk, data, data_len);
|
/* GTK KDE: KeyID/Tx(1) Reserved(1) GTK(var) */
|
||||||
ndl->gtk_len = data_len;
|
uint8_t gtk_len = body_len - NAN_GTK_KDE_PREFIX_LEN;
|
||||||
|
if (gtk_len <= NAN_GTK_MAX_LEN) {
|
||||||
|
ndl->gtk_keyid = body[0] & 0x03;
|
||||||
|
memcpy(ndl->gtk, body + NAN_GTK_KDE_PREFIX_LEN, gtk_len);
|
||||||
|
ndl->gtk_len = gtk_len;
|
||||||
|
memcpy(ndl->gtk_rsc, key_rsc, NAN_KEY_RSC_LEN);
|
||||||
ndl->gtk_set = 1;
|
ndl->gtk_set = 1;
|
||||||
ESP_LOGI(TAG, "KDE: GTK stored (len=%d)", data_len);
|
ESP_LOGI(TAG, "KDE: peer GTK stored (keyid=%d, len=%d)",
|
||||||
} else if (data_type == 3 && data_len >= 6) {
|
ndl->gtk_keyid, gtk_len);
|
||||||
ESP_LOGI(TAG, "KDE: MAC Address: " MACSTR, MAC2STR(data));
|
|
||||||
} else if (data_type == 4 && data_len <= NAN_GTK_MAX_LEN) {
|
|
||||||
memcpy(ndl->igtk, data, data_len);
|
|
||||||
ndl->igtk_len = data_len;
|
|
||||||
ndl->igtk_set = 1;
|
|
||||||
ESP_LOGI(TAG, "KDE: IGTK stored (len=%d)", data_len);
|
|
||||||
} else if (data_type == 5 && data_len <= NAN_GTK_MAX_LEN) {
|
|
||||||
memcpy(ndl->bigtk, data, data_len);
|
|
||||||
ndl->bigtk_len = data_len;
|
|
||||||
ndl->bigtk_set = 1;
|
|
||||||
ESP_LOGI(TAG, "KDE: BIGTK stored (len=%d)", data_len);
|
|
||||||
}
|
}
|
||||||
} else if (oui == 0x506F9A) { /* NAN OUI */
|
} else if (data_type == NAN_KDE_TYPE_MAC && body_len >= 6) {
|
||||||
if (data_type == 36 && data_len >= 1) {
|
ESP_LOGI(TAG, "KDE: MAC Address: " MACSTR, MAC2STR(body));
|
||||||
ESP_LOGI(TAG, "KDE: NIK (Cipher Ver: %d)", data[0]);
|
} else if (data_type == NAN_KDE_TYPE_IGTK && body_len > NAN_IGTK_KDE_PREFIX_LEN) {
|
||||||
} else if (data_type == 37 && data_len >= 6) {
|
/* IGTK KDE: KeyID(2 LE) IPN(6) IGTK(var) */
|
||||||
uint16_t key_bitmap = data[0] | (data[1] << 8);
|
uint8_t igtk_len = body_len - NAN_IGTK_KDE_PREFIX_LEN;
|
||||||
uint32_t lifetime = (data[2] << 24) | (data[3] << 16) | (data[4] << 8) | data[5];
|
if (igtk_len <= NAN_GTK_MAX_LEN) {
|
||||||
ESP_LOGI(TAG, "KDE: NAN Key Lifetime: %lu s, Bitmap: 0x%04x",
|
ndl->igtk_keyid = body[0];
|
||||||
(unsigned long)lifetime, key_bitmap);
|
memcpy(ndl->igtk_ipn, body + 2, 6); /* IPN follows KeyID(2) */
|
||||||
|
memcpy(ndl->igtk, body + NAN_IGTK_KDE_PREFIX_LEN, igtk_len);
|
||||||
|
ndl->igtk_len = igtk_len;
|
||||||
|
ndl->igtk_set = 1;
|
||||||
|
ESP_LOGI(TAG, "KDE: peer IGTK stored (keyid=%d, len=%d)",
|
||||||
|
ndl->igtk_keyid, igtk_len);
|
||||||
|
}
|
||||||
|
} else if (data_type == NAN_KDE_TYPE_BIGTK && body_len > NAN_BIGTK_KDE_PREFIX_LEN) {
|
||||||
|
/* BIGTK KDE: KeyID(2 LE) BIPN(6) BIGTK(var) */
|
||||||
|
uint8_t bigtk_len = body_len - NAN_BIGTK_KDE_PREFIX_LEN;
|
||||||
|
if (bigtk_len <= NAN_GTK_MAX_LEN) {
|
||||||
|
ndl->bigtk_keyid = body[0];
|
||||||
|
memcpy(ndl->bigtk_ipn, body + 2, 6); /* BIPN follows KeyID(2) */
|
||||||
|
memcpy(ndl->bigtk, body + NAN_BIGTK_KDE_PREFIX_LEN, bigtk_len);
|
||||||
|
ndl->bigtk_len = bigtk_len;
|
||||||
|
ndl->bigtk_set = 1;
|
||||||
|
ESP_LOGI(TAG, "KDE: peer BIGTK stored (keyid=%d, len=%d)",
|
||||||
|
ndl->bigtk_keyid, bigtk_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (oui == NAN_KDE_OUI_WFA) {
|
||||||
|
if (data_type == NAN_KDE_TYPE_NIK && body_len >= 1) {
|
||||||
|
ESP_LOGI(TAG, "KDE: NIK (Cipher Ver: %d)", body[0]);
|
||||||
|
} else if (data_type == NAN_KDE_TYPE_KEY_LIFE && body_len >= 6) {
|
||||||
|
uint16_t key_bitmap = body[0] | (body[1] << 8);
|
||||||
|
uint32_t lifetime = (body[2] << 24) | (body[3] << 16) |
|
||||||
|
(body[4] << 8) | body[5];
|
||||||
|
ESP_LOGI(TAG, "KDE: NAN Key Lifetime: %lu s, Bitmap: 0x%04x",
|
||||||
|
(unsigned long)lifetime, key_bitmap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
kd_offset += 2 + kde_len;
|
kd_offset += 2 + kde_flen;
|
||||||
}
|
}
|
||||||
|
forced_memzero(plain, sizeof(plain)); /* scrub decrypted group keys */
|
||||||
}
|
}
|
||||||
} else if (msg_type == 1) {
|
} else if (msg_type == 1) {
|
||||||
/* M1 received but NDL not created yet — store as pending */
|
/* M1 received but NDL not created yet — store as pending */
|
||||||
@@ -1698,7 +2346,19 @@ esp_err_t esp_nan_parse_publish_security(const uint8_t *attrs, size_t attrs_len,
|
|||||||
ESP_LOGD(TAG, "Found CSIA in Publish SDF, len=%d", csia_len);
|
ESP_LOGD(TAG, "Found CSIA in Publish SDF, len=%d", csia_len);
|
||||||
ESP_LOG_BUFFER_HEXDUMP(TAG, csia, csia_len, ESP_LOG_DEBUG);
|
ESP_LOG_BUFFER_HEXDUMP(TAG, csia, csia_len, ESP_LOG_DEBUG);
|
||||||
|
|
||||||
/* Skip Capabilities byte; iterate [CSID(1)] [Publish ID(1)] entries */
|
/* Capabilities byte (csia[0]) bits 1-2 = GTKSA/IGTKSA/BIGTKSA support
|
||||||
|
* (§9.5.21.2 Table 122): 01 = GTKSA+IGTKSA, 10 = +BIGTKSA. This — not an
|
||||||
|
* NCS-GTK cipher-suite ID — is how a peer (notably iOS) advertises it. */
|
||||||
|
uint8_t grp_supp = csia[0] & NAN_CSIA_CAP_GTK_SUPP_MASK;
|
||||||
|
if (grp_supp) {
|
||||||
|
security->group_data_prot = 1;
|
||||||
|
security->group_mgmt_prot = 1; /* IGTKSA */
|
||||||
|
}
|
||||||
|
if (grp_supp == (NAN_CSIA_CAP_GTK_SUPP_ALL << NAN_CSIA_CAP_GTK_SUPP_POS)) {
|
||||||
|
security->group_bigtk_prot = 1; /* BIGTKSA */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* iterate [CSID(1)] [Publish ID(1)] entries after the Capabilities byte */
|
||||||
size_t offset = 1;
|
size_t offset = 1;
|
||||||
while (offset + 2 <= csia_len) {
|
while (offset + 2 <= csia_len) {
|
||||||
uint8_t csid = csia[offset];
|
uint8_t csid = csia[offset];
|
||||||
@@ -1710,6 +2370,12 @@ esp_err_t esp_nan_parse_publish_security(const uint8_t *attrs, size_t attrs_len,
|
|||||||
|
|
||||||
offset += 2;
|
offset += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Android/ESP peers advertise group-data support by listing an NCS-GTK
|
||||||
|
* cipher suite instead of (or with) the Capabilities byte bits. */
|
||||||
|
if (security->csid_bitmap & NAN_CSID_GTK_BITS) {
|
||||||
|
security->group_data_prot = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2. SCIA — PMKIDs */
|
/* 2. SCIA — PMKIDs */
|
||||||
@@ -1762,6 +2428,33 @@ void nan_security_apply_pending(struct ndl_info *ndl,
|
|||||||
(s_pending_scia.has_csid || s_pending_scia.has_pmkid)) {
|
(s_pending_scia.has_csid || s_pending_scia.has_pmkid)) {
|
||||||
|
|
||||||
if (s_pending_scia.has_csid) {
|
if (s_pending_scia.has_csid) {
|
||||||
|
/* GTK is exchanged only if both sides support it: our service must
|
||||||
|
* enable group_data_prot AND the peer must have signalled group-data
|
||||||
|
* support in its NDP-Request CSIA (Capabilities byte for iOS, or an
|
||||||
|
* NCS-GTK cipher suite for Android/ESP — captured in peer_group_data
|
||||||
|
* by esp_nan_parse_ndp_csia). */
|
||||||
|
ndl->gtk_required = (p_own_svc && p_own_svc->user_cfg.group_data_prot &&
|
||||||
|
s_pending_scia.peer_group_data) ? 1 : 0;
|
||||||
|
if (p_own_svc && p_own_svc->user_cfg.group_data_prot) {
|
||||||
|
ESP_LOGI(TAG, "NDP GTK: %s (responder) - own group_prot=1, peer group_data=%d",
|
||||||
|
ndl->gtk_required ? "negotiated" : "NOT negotiated",
|
||||||
|
s_pending_scia.peer_group_data);
|
||||||
|
}
|
||||||
|
/* IGTK distributed in M4 iff we enabled group_mgmt_prot AND the peer
|
||||||
|
* advertised IGTKSA; BIGTK additionally requires BIGTKSA (§7.1.3.5). */
|
||||||
|
{
|
||||||
|
bool own_mgmt = s_nan_ctx.group_mgmt_prot;
|
||||||
|
uint8_t grp = s_pending_scia.peer_caps & NAN_CSIA_CAP_GTK_SUPP_MASK;
|
||||||
|
bool peer_igtk = grp != 0;
|
||||||
|
bool peer_bigtk = grp == (NAN_CSIA_CAP_GTK_SUPP_ALL << NAN_CSIA_CAP_GTK_SUPP_POS);
|
||||||
|
ndl->igtk_required = (own_mgmt && peer_igtk) ? 1 : 0;
|
||||||
|
ndl->bigtk_required = (own_mgmt && peer_bigtk) ? 1 : 0;
|
||||||
|
if (own_mgmt) {
|
||||||
|
ESP_LOGI(TAG, "NDP IGTK/BIGTK: igtk=%s bigtk=%s (responder) - peer caps=0x%02x",
|
||||||
|
ndl->igtk_required ? "yes" : "no", ndl->bigtk_required ? "yes" : "no",
|
||||||
|
s_pending_scia.peer_caps);
|
||||||
|
}
|
||||||
|
}
|
||||||
ndl->security_ctx.csid_bitmap = s_pending_scia.csid_bitmap;
|
ndl->security_ctx.csid_bitmap = s_pending_scia.csid_bitmap;
|
||||||
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
|
ndl->security_ctx.type = WIFI_NAN_SECURITY_ENCRYPTED;
|
||||||
}
|
}
|
||||||
@@ -2105,14 +2798,12 @@ int esp_nan_get_ndp_confirm_shared_key_desc(uint8_t *buf, size_t buf_len, uint8_
|
|||||||
|
|
||||||
uint8_t *p = buf;
|
uint8_t *p = buf;
|
||||||
*p++ = NAN_ATTR_ID_SHARED_KEY_DESC;
|
*p++ = NAN_ATTR_ID_SHARED_KEY_DESC;
|
||||||
{
|
uint8_t *len_field = p; /* backpatched once Key Data length is known */
|
||||||
uint16_t body_len = 1 + NAN_KEY_DESC_MIN_LEN;
|
p += 2;
|
||||||
*p++ = body_len & 0xFF;
|
|
||||||
*p++ = (body_len >> 8) & 0xFF;
|
|
||||||
}
|
|
||||||
*p++ = ndl->publisher_id;
|
*p++ = ndl->publisher_id;
|
||||||
|
|
||||||
int n = nan_build_rsna_key_descriptor(p,
|
uint8_t *key_desc = p;
|
||||||
|
int n = nan_build_rsna_key_descriptor(key_desc,
|
||||||
NAN_KEY_INFO_MIC |
|
NAN_KEY_INFO_MIC |
|
||||||
NAN_KEY_INFO_SECURE |
|
NAN_KEY_INFO_SECURE |
|
||||||
NAN_KEY_INFO_ACK,
|
NAN_KEY_INFO_ACK,
|
||||||
@@ -2124,12 +2815,19 @@ int esp_nan_get_ndp_confirm_shared_key_desc(uint8_t *buf, size_t buf_len, uint8_
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
n = 3 + 1 + n;
|
/* Initiator distributes its GTK in M3 (§7.1.3.2). Needs the blob to size
|
||||||
|
* the M3 buffer with GTK headroom (ndp_confirm_get_shared_desc_len);
|
||||||
|
* falls back to pairwise-only otherwise. */
|
||||||
|
n = nan_append_own_group_kdes(ndl, key_desc, buf_len - 4);
|
||||||
|
|
||||||
|
uint16_t body_len = 1 + (uint16_t)n;
|
||||||
|
len_field[0] = body_len & 0xFF;
|
||||||
|
len_field[1] = (body_len >> 8) & 0xFF;
|
||||||
|
|
||||||
NAN_DATA_UNLOCK();
|
NAN_DATA_UNLOCK();
|
||||||
/* State transition to M3_SENT happens in host TX-confirm hook */
|
/* State transition to M3_SENT happens in host TX-confirm hook */
|
||||||
ESP_LOGD(TAG, "NDP M3 Key Desc: built (MIC=0, driver must call esp_nan_update_ndp_confirm_mic) for ndp_id=%d", ndp_id);
|
ESP_LOGD(TAG, "NDP M3 Key Desc: built (key_desc=%d bytes, MIC=0; driver must call esp_nan_update_ndp_confirm_mic) for ndp_id=%d", n, ndp_id);
|
||||||
return n;
|
return 3 + 1 + n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -238,6 +238,8 @@ typedef enum {
|
|||||||
NAN_KEY_ND_TK = 0,
|
NAN_KEY_ND_TK = 0,
|
||||||
NAN_KEY_ND_GTK,
|
NAN_KEY_ND_GTK,
|
||||||
NAN_KEY_NM_TK,
|
NAN_KEY_NM_TK,
|
||||||
|
NAN_KEY_ND_IGTK, /* 3 - NAN Integrity Group Temporal Key (BIP-CMAC-128) */
|
||||||
|
NAN_KEY_ND_BIGTK, /* 4 - NAN Beacon Integrity Group Temporal Key (BIP-CMAC-128) */
|
||||||
} nan_key_type_t;
|
} nan_key_type_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -35,6 +35,16 @@ menu "Example Configuration"
|
|||||||
the same credential (passphrase or PMK).
|
the same credential (passphrase or PMK).
|
||||||
Disable to advertise an open (unencrypted) service.
|
Disable to advertise an open (unencrypted) service.
|
||||||
|
|
||||||
|
config EXAMPLE_NAN_GROUP_DATA_PROT
|
||||||
|
bool "Protect group-addressed datapath traffic (ND-GTK)"
|
||||||
|
depends on EXAMPLE_NAN_SECURITY_ENABLED
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Negotiate and install an ND-GTK so group-addressed (multicast/
|
||||||
|
broadcast) frames on the NAN datapath are encrypted. This is
|
||||||
|
required for IPv6 over the secured datapath (Neighbor Discovery,
|
||||||
|
MLD). Both peers must enable this for group keys to be exchanged.
|
||||||
|
|
||||||
choice EXAMPLE_NAN_SECURITY_METHOD
|
choice EXAMPLE_NAN_SECURITY_METHOD
|
||||||
prompt "Security Method"
|
prompt "Security Method"
|
||||||
depends on EXAMPLE_NAN_SECURITY_ENABLED
|
depends on EXAMPLE_NAN_SECURITY_ENABLED
|
||||||
|
|||||||
@@ -133,6 +133,9 @@ void wifi_nan_publish(void)
|
|||||||
};
|
};
|
||||||
#ifdef CONFIG_EXAMPLE_NAN_SECURITY_ENABLED
|
#ifdef CONFIG_EXAMPLE_NAN_SECURITY_ENABLED
|
||||||
wifi_nan_discovery_security_params_t security_cfg = {
|
wifi_nan_discovery_security_params_t security_cfg = {
|
||||||
|
#ifdef CONFIG_EXAMPLE_NAN_GROUP_DATA_PROT
|
||||||
|
.group_data_prot = 1, /* distribute/accept ND-GTK for group-addressed data */
|
||||||
|
#endif
|
||||||
.num_credentials = 1,
|
.num_credentials = 1,
|
||||||
.creds = {
|
.creds = {
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,6 +45,16 @@ menu "Example Configuration"
|
|||||||
(passphrase or PMK) and sets up an encrypted NDP.
|
(passphrase or PMK) and sets up an encrypted NDP.
|
||||||
Disable to discover open (unencrypted) services.
|
Disable to discover open (unencrypted) services.
|
||||||
|
|
||||||
|
config EXAMPLE_NAN_GROUP_DATA_PROT
|
||||||
|
bool "Protect group-addressed datapath traffic (ND-GTK)"
|
||||||
|
depends on EXAMPLE_NAN_SECURITY_ENABLED
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
Negotiate and install an ND-GTK so group-addressed (multicast/
|
||||||
|
broadcast) frames on the NAN datapath are encrypted. This is
|
||||||
|
required for IPv6 over the secured datapath (Neighbor Discovery,
|
||||||
|
MLD). Both peers must enable this for group keys to be exchanged.
|
||||||
|
|
||||||
choice EXAMPLE_NAN_SECURITY_METHOD
|
choice EXAMPLE_NAN_SECURITY_METHOD
|
||||||
prompt "Security Method"
|
prompt "Security Method"
|
||||||
depends on EXAMPLE_NAN_SECURITY_ENABLED
|
depends on EXAMPLE_NAN_SECURITY_ENABLED
|
||||||
|
|||||||
@@ -227,6 +227,9 @@ void wifi_nan_subscribe(void)
|
|||||||
};
|
};
|
||||||
#ifdef CONFIG_EXAMPLE_NAN_SECURITY_ENABLED
|
#ifdef CONFIG_EXAMPLE_NAN_SECURITY_ENABLED
|
||||||
wifi_nan_discovery_security_params_t security_cfg = {
|
wifi_nan_discovery_security_params_t security_cfg = {
|
||||||
|
#ifdef CONFIG_EXAMPLE_NAN_GROUP_DATA_PROT
|
||||||
|
.group_data_prot = 1, /* distribute/accept ND-GTK for group-addressed data */
|
||||||
|
#endif
|
||||||
.num_credentials = 1,
|
.num_credentials = 1,
|
||||||
.creds = {
|
.creds = {
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user