DPP3: Private Peer Introduction protocol
Add a privacy protecting variant of the peer introduction protocol to allow the station device to hide its Connector from 3rd parties. The new wpa_supplicant network profile parameter dpp_connector_privacy=1 can be used to select this alternative mechanism to the peer introduction protocol added in the initial release of DPP. It should be noted that the new variant does not work with older DPP APs (i.e., requires support for release 3). As such, this new variant is disabled by default. Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
This commit is contained in:
parent
786ea402bc
commit
148de3e0dc
11 changed files with 694 additions and 27 deletions
|
@ -1958,6 +1958,25 @@ skip_proto_ver:
|
|||
}
|
||||
|
||||
|
||||
static bool hapd_dpp_connector_available(struct hostapd_data *hapd)
|
||||
{
|
||||
if (!hapd->wpa_auth ||
|
||||
!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
|
||||
!(hapd->conf->wpa & WPA_PROTO_RSN)) {
|
||||
wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
|
||||
!hapd->conf->dpp_csign) {
|
||||
wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
|
||||
const u8 *src,
|
||||
const u8 *buf, size_t len,
|
||||
|
@ -1971,20 +1990,12 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
|
|||
int expiration;
|
||||
enum dpp_status_error res;
|
||||
|
||||
os_memset(&intro, 0, sizeof(intro));
|
||||
|
||||
wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
|
||||
MAC2STR(src));
|
||||
if (!hapd->wpa_auth ||
|
||||
!(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
|
||||
!(hapd->conf->wpa & WPA_PROTO_RSN)) {
|
||||
wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
|
||||
if (!hapd_dpp_connector_available(hapd))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
|
||||
!hapd->conf->dpp_csign) {
|
||||
wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
|
||||
return;
|
||||
}
|
||||
|
||||
os_get_time(&now);
|
||||
|
||||
|
@ -2019,7 +2030,7 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
|
|||
wpa_printf(MSG_INFO,
|
||||
"DPP: Network Introduction protocol resulted in internal failure (peer "
|
||||
MACSTR ")", MAC2STR(src));
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
if (res != DPP_STATUS_OK) {
|
||||
wpa_printf(MSG_INFO,
|
||||
|
@ -2027,7 +2038,7 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
|
|||
MACSTR " status %d)", MAC2STR(src), res);
|
||||
hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
|
||||
res);
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DPP3
|
||||
|
@ -2047,7 +2058,7 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
|
|||
hostapd_dpp_send_peer_disc_resp(hapd, src, freq,
|
||||
trans_id[0],
|
||||
DPP_STATUS_NO_MATCH);
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_DPP3 */
|
||||
|
@ -2063,11 +2074,13 @@ static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
|
|||
intro.pmkid, expiration,
|
||||
WPA_KEY_MGMT_DPP) < 0) {
|
||||
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
|
||||
hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
|
||||
DPP_STATUS_OK);
|
||||
done:
|
||||
dpp_peer_intro_deinit(&intro);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2599,6 +2612,227 @@ hostapd_dpp_rx_pb_presence_announcement(struct hostapd_data *hapd,
|
|||
hostapd_dpp_pb_pkex_init(hapd, freq, src, r_hash);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
hostapd_dpp_rx_priv_peer_intro_query(struct hostapd_data *hapd, const u8 *src,
|
||||
const u8 *hdr, const u8 *buf, size_t len,
|
||||
unsigned int freq)
|
||||
{
|
||||
const u8 *trans_id, *version;
|
||||
u16 trans_id_len, version_len;
|
||||
struct wpabuf *msg;
|
||||
u8 ver = DPP_VERSION;
|
||||
int conn_ver;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Query from "
|
||||
MACSTR, MAC2STR(src));
|
||||
|
||||
if (!hapd_dpp_connector_available(hapd))
|
||||
return;
|
||||
|
||||
trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
|
||||
&trans_id_len);
|
||||
if (!trans_id || trans_id_len != 1) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Peer did not include Transaction ID");
|
||||
return;
|
||||
}
|
||||
|
||||
version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
|
||||
&version_len);
|
||||
if (!version || version_len != 1) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Peer did not include Protocol Version");
|
||||
return;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "DPP: Transaction ID %u, Version %u",
|
||||
trans_id[0], version[0]);
|
||||
|
||||
len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
|
||||
msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_NOTIFY, len);
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
/* Transaction ID */
|
||||
wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
|
||||
wpabuf_put_le16(msg, 1);
|
||||
wpabuf_put_u8(msg, trans_id[0]);
|
||||
|
||||
/* Protocol Version */
|
||||
conn_ver = dpp_get_connector_version(hapd->conf->dpp_connector);
|
||||
if (conn_ver > 0 && ver != conn_ver) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Use Connector version %d instead of current protocol version %d",
|
||||
conn_ver, ver);
|
||||
ver = conn_ver;
|
||||
}
|
||||
wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
|
||||
wpabuf_put_le16(msg, 1);
|
||||
wpabuf_put_u8(msg, ver);
|
||||
|
||||
/* DPP Connector */
|
||||
wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
|
||||
wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
|
||||
wpabuf_put_str(msg, hapd->conf->dpp_connector);
|
||||
|
||||
wpa_printf(MSG_DEBUG, "DPP: Send Private Peer Introduction Notify to "
|
||||
MACSTR, MAC2STR(src));
|
||||
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
|
||||
" freq=%u type=%d", MAC2STR(src), freq,
|
||||
DPP_PA_PRIV_PEER_INTRO_NOTIFY);
|
||||
hostapd_drv_send_action(hapd, freq, 0, src,
|
||||
wpabuf_head(msg), wpabuf_len(msg));
|
||||
wpabuf_free(msg);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
hostapd_dpp_rx_priv_peer_intro_update(struct hostapd_data *hapd, const u8 *src,
|
||||
const u8 *hdr, const u8 *buf, size_t len,
|
||||
unsigned int freq)
|
||||
{
|
||||
struct crypto_ec_key *own_key;
|
||||
const struct dpp_curve_params *curve;
|
||||
enum hpke_kem_id kem_id;
|
||||
enum hpke_kdf_id kdf_id;
|
||||
enum hpke_aead_id aead_id;
|
||||
const u8 *aad = hdr;
|
||||
size_t aad_len = DPP_HDR_LEN;
|
||||
struct wpabuf *pt;
|
||||
const u8 *trans_id, *wrapped, *version, *connector;
|
||||
u16 trans_id_len, wrapped_len, version_len, connector_len;
|
||||
struct os_time now;
|
||||
struct dpp_introduction intro;
|
||||
os_time_t expire;
|
||||
int expiration;
|
||||
enum dpp_status_error res;
|
||||
|
||||
os_memset(&intro, 0, sizeof(intro));
|
||||
|
||||
wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Update from "
|
||||
MACSTR, MAC2STR(src));
|
||||
|
||||
if (!hapd_dpp_connector_available(hapd))
|
||||
return;
|
||||
|
||||
os_get_time(&now);
|
||||
|
||||
if (hapd->conf->dpp_netaccesskey_expiry &&
|
||||
(os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
|
||||
wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
|
||||
return;
|
||||
}
|
||||
|
||||
trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
|
||||
&trans_id_len);
|
||||
if (!trans_id || trans_id_len != 1) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Peer did not include Transaction ID");
|
||||
return;
|
||||
}
|
||||
|
||||
wrapped = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
|
||||
&wrapped_len);
|
||||
if (!wrapped) {
|
||||
wpa_printf(MSG_DEBUG, "DPP: Peer did not include Wrapped Data");
|
||||
return;
|
||||
}
|
||||
|
||||
own_key = dpp_set_keypair(&curve,
|
||||
wpabuf_head(hapd->conf->dpp_netaccesskey),
|
||||
wpabuf_len(hapd->conf->dpp_netaccesskey));
|
||||
if (!own_key) {
|
||||
wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dpp_hpke_suite(curve->ike_group, &kem_id, &kdf_id, &aead_id) < 0) {
|
||||
wpa_printf(MSG_ERROR, "DPP: Unsupported curve %d",
|
||||
curve->ike_group);
|
||||
crypto_ec_key_deinit(own_key);
|
||||
return;
|
||||
}
|
||||
|
||||
pt = hpke_base_open(kem_id, kdf_id, aead_id, own_key, NULL, 0,
|
||||
aad, aad_len, wrapped, wrapped_len);
|
||||
crypto_ec_key_deinit(own_key);
|
||||
if (!pt) {
|
||||
wpa_printf(MSG_INFO, "DPP: Failed to decrypt Connector");
|
||||
return;
|
||||
}
|
||||
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE-Decrypted Wrapped Data", pt);
|
||||
|
||||
connector = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
|
||||
DPP_ATTR_CONNECTOR, &connector_len);
|
||||
if (!connector) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Peer did not include its Connector");
|
||||
goto done;
|
||||
}
|
||||
|
||||
version = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
|
||||
DPP_ATTR_PROTOCOL_VERSION, &version_len);
|
||||
if (!version || version_len < 1) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Peer did not include Protocol Version");
|
||||
goto done;
|
||||
}
|
||||
|
||||
res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
|
||||
wpabuf_head(hapd->conf->dpp_netaccesskey),
|
||||
wpabuf_len(hapd->conf->dpp_netaccesskey),
|
||||
wpabuf_head(hapd->conf->dpp_csign),
|
||||
wpabuf_len(hapd->conf->dpp_csign),
|
||||
connector, connector_len, &expire);
|
||||
if (res == 255) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"DPP: Network Introduction protocol resulted in internal failure (peer "
|
||||
MACSTR ")", MAC2STR(src));
|
||||
goto done;
|
||||
}
|
||||
if (res != DPP_STATUS_OK) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"DPP: Network Introduction protocol resulted in failure (peer "
|
||||
MACSTR " status %d)", MAC2STR(src), res);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (intro.peer_version && intro.peer_version >= 2) {
|
||||
u8 attr_version = 1;
|
||||
|
||||
if (version && version_len >= 1)
|
||||
attr_version = version[0];
|
||||
if (attr_version != intro.peer_version) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"DPP: Protocol version mismatch (Connector: %d Attribute: %d",
|
||||
intro.peer_version, attr_version);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
|
||||
expire = hapd->conf->dpp_netaccesskey_expiry;
|
||||
if (expire)
|
||||
expiration = expire - now.sec;
|
||||
else
|
||||
expiration = 0;
|
||||
|
||||
if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
|
||||
intro.pmkid, expiration,
|
||||
WPA_KEY_MGMT_DPP) < 0) {
|
||||
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
|
||||
goto done;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with "
|
||||
MACSTR, MAC2STR(src));
|
||||
|
||||
done:
|
||||
dpp_peer_intro_deinit(&intro);
|
||||
wpabuf_free(pt);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DPP3 */
|
||||
|
||||
|
||||
|
@ -2712,6 +2946,14 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
|||
hostapd_dpp_rx_pb_presence_announcement(hapd, src, hdr,
|
||||
buf, len, freq);
|
||||
break;
|
||||
case DPP_PA_PRIV_PEER_INTRO_QUERY:
|
||||
hostapd_dpp_rx_priv_peer_intro_query(hapd, src, hdr,
|
||||
buf, len, freq);
|
||||
break;
|
||||
case DPP_PA_PRIV_PEER_INTRO_UPDATE:
|
||||
hostapd_dpp_rx_priv_peer_intro_update(hapd, src, hdr,
|
||||
buf, len, freq);
|
||||
break;
|
||||
#endif /* CONFIG_DPP3 */
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG,
|
||||
|
|
|
@ -4100,7 +4100,7 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
|
|||
struct json_token *root = NULL, *netkey, *token;
|
||||
struct json_token *own_root = NULL;
|
||||
enum dpp_status_error ret = 255, res;
|
||||
struct crypto_ec_key *own_key = NULL, *peer_key = NULL;
|
||||
struct crypto_ec_key *own_key = NULL;
|
||||
struct wpabuf *own_key_pub = NULL;
|
||||
const struct dpp_curve_params *curve, *own_curve;
|
||||
struct dpp_signed_connector_info info;
|
||||
|
@ -4173,12 +4173,12 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
peer_key = dpp_parse_jwk(netkey, &curve);
|
||||
if (!peer_key) {
|
||||
intro->peer_key = dpp_parse_jwk(netkey, &curve);
|
||||
if (!intro->peer_key) {
|
||||
ret = DPP_STATUS_INVALID_CONNECTOR;
|
||||
goto fail;
|
||||
}
|
||||
dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
|
||||
dpp_debug_print_key("DPP: Received netAccessKey", intro->peer_key);
|
||||
|
||||
if (own_curve != curve) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
|
@ -4189,7 +4189,7 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
|
|||
}
|
||||
|
||||
/* ECDH: N = nk * PK */
|
||||
if (dpp_ecdh(own_key, peer_key, Nx, &Nx_len) < 0)
|
||||
if (dpp_ecdh(own_key, intro->peer_key, Nx, &Nx_len) < 0)
|
||||
goto fail;
|
||||
|
||||
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
|
||||
|
@ -4203,26 +4203,45 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
|
|||
intro->pmk_len = curve->hash_len;
|
||||
|
||||
/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
|
||||
if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) {
|
||||
if (dpp_derive_pmkid(curve, own_key, intro->peer_key, intro->pmkid) <
|
||||
0) {
|
||||
wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DPP3
|
||||
if (dpp_hpke_suite(curve->ike_group, &intro->kem_id, &intro->kdf_id,
|
||||
&intro->aead_id) < 0) {
|
||||
wpa_printf(MSG_ERROR, "DPP: Unsupported group %d",
|
||||
curve->ike_group);
|
||||
goto fail;
|
||||
}
|
||||
#endif /* CONFIG_DPP3 */
|
||||
|
||||
ret = DPP_STATUS_OK;
|
||||
fail:
|
||||
if (ret != DPP_STATUS_OK)
|
||||
os_memset(intro, 0, sizeof(*intro));
|
||||
dpp_peer_intro_deinit(intro);
|
||||
os_memset(Nx, 0, sizeof(Nx));
|
||||
os_free(info.payload);
|
||||
crypto_ec_key_deinit(own_key);
|
||||
wpabuf_free(own_key_pub);
|
||||
crypto_ec_key_deinit(peer_key);
|
||||
json_free(root);
|
||||
json_free(own_root);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void dpp_peer_intro_deinit(struct dpp_introduction *intro)
|
||||
{
|
||||
if (!intro)
|
||||
return;
|
||||
|
||||
crypto_ec_key_deinit(intro->peer_key);
|
||||
os_memset(intro, 0, sizeof(*intro));
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_DPP3
|
||||
int dpp_get_connector_version(const char *connector)
|
||||
{
|
||||
|
|
|
@ -58,6 +58,9 @@ enum dpp_public_action_frame_type {
|
|||
DPP_PA_PKEX_EXCHANGE_REQ = 18,
|
||||
DPP_PA_PB_PRESENCE_ANNOUNCEMENT = 19,
|
||||
DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP = 20,
|
||||
DPP_PA_PRIV_PEER_INTRO_QUERY = 21,
|
||||
DPP_PA_PRIV_PEER_INTRO_NOTIFY = 22,
|
||||
DPP_PA_PRIV_PEER_INTRO_UPDATE = 23,
|
||||
};
|
||||
|
||||
enum dpp_attribute_id {
|
||||
|
@ -413,6 +416,10 @@ struct dpp_introduction {
|
|||
u8 pmk[PMK_LEN_MAX];
|
||||
size_t pmk_len;
|
||||
int peer_version;
|
||||
struct crypto_ec_key *peer_key;
|
||||
enum hpke_kem_id kem_id;
|
||||
enum hpke_kdf_id kdf_id;
|
||||
enum hpke_aead_id aead_id;
|
||||
};
|
||||
|
||||
struct dpp_relay_config {
|
||||
|
@ -650,6 +657,7 @@ dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
|
|||
const u8 *csign_key, size_t csign_key_len,
|
||||
const u8 *peer_connector, size_t peer_connector_len,
|
||||
os_time_t *expiry);
|
||||
void dpp_peer_intro_deinit(struct dpp_introduction *intro);
|
||||
int dpp_get_connector_version(const char *connector);
|
||||
struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
|
||||
const u8 *own_mac,
|
||||
|
@ -687,6 +695,11 @@ struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
|
|||
int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len);
|
||||
void dpp_pfs_free(struct dpp_pfs *pfs);
|
||||
|
||||
struct crypto_ec_key * dpp_set_keypair(const struct dpp_curve_params **curve,
|
||||
const u8 *privkey, size_t privkey_len);
|
||||
int dpp_hpke_suite(int iana_group, enum hpke_kem_id *kem_id,
|
||||
enum hpke_kdf_id *kdf_id, enum hpke_aead_id *aead_id);
|
||||
|
||||
struct wpabuf * dpp_build_csr(struct dpp_authentication *auth,
|
||||
const char *name);
|
||||
int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr);
|
||||
|
|
|
@ -2367,6 +2367,7 @@ fail:
|
|||
|
||||
|
||||
#ifdef CONFIG_DPP3
|
||||
|
||||
int dpp_derive_auth_i(struct dpp_authentication *auth, u8 *auth_i)
|
||||
{
|
||||
int ret = -1, res;
|
||||
|
@ -2454,6 +2455,47 @@ fail:
|
|||
wpabuf_free(pex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int dpp_hpke_suite(int iana_group, enum hpke_kem_id *kem_id,
|
||||
enum hpke_kdf_id *kdf_id, enum hpke_aead_id *aead_id)
|
||||
{
|
||||
switch (iana_group) {
|
||||
case 19:
|
||||
*kem_id = HPKE_DHKEM_P256_HKDF_SHA256;
|
||||
*kdf_id = HPKE_KDF_HKDF_SHA256;
|
||||
*aead_id = HPKE_AEAD_AES_128_GCM;
|
||||
return 0;
|
||||
case 20:
|
||||
*kem_id = HPKE_DHKEM_P384_HKDF_SHA384;
|
||||
*kdf_id = HPKE_KDF_HKDF_SHA384;
|
||||
*aead_id = HPKE_AEAD_AES_256_GCM;
|
||||
return 0;
|
||||
case 21:
|
||||
*kem_id = HPKE_DHKEM_P521_HKDF_SHA512;
|
||||
*kdf_id = HPKE_KDF_HKDF_SHA512;
|
||||
*aead_id = HPKE_AEAD_AES_256_GCM;
|
||||
return 0;
|
||||
case 28:
|
||||
*kem_id = HPKE_DHKEM_P256_HKDF_SHA256;
|
||||
*kdf_id = HPKE_KDF_HKDF_SHA256;
|
||||
*aead_id = HPKE_AEAD_AES_128_GCM;
|
||||
return 0;
|
||||
case 29:
|
||||
*kem_id = HPKE_DHKEM_P384_HKDF_SHA384;
|
||||
*kdf_id = HPKE_KDF_HKDF_SHA384;
|
||||
*aead_id = HPKE_AEAD_AES_256_GCM;
|
||||
return 0;
|
||||
case 30:
|
||||
*kem_id = HPKE_DHKEM_P521_HKDF_SHA512;
|
||||
*kdf_id = HPKE_KDF_HKDF_SHA512;
|
||||
*aead_id = HPKE_AEAD_AES_256_GCM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DPP3 */
|
||||
|
||||
|
||||
|
|
|
@ -98,8 +98,6 @@ int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
|
|||
int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
|
||||
int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
|
||||
const u8 *privkey, size_t privkey_len);
|
||||
struct crypto_ec_key * dpp_set_keypair(const struct dpp_curve_params **curve,
|
||||
const u8 *privkey, size_t privkey_len);
|
||||
struct crypto_ec_key * dpp_gen_keypair(const struct dpp_curve_params *curve);
|
||||
int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len);
|
||||
int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len);
|
||||
|
|
|
@ -2630,6 +2630,7 @@ static const struct parse_data ssid_fields[] = {
|
|||
{ STR_LEN(dpp_csign) },
|
||||
{ STR_LEN(dpp_pp_key) },
|
||||
{ INT_RANGE(dpp_pfs, 0, 2) },
|
||||
{ INT_RANGE(dpp_connector_privacy, 0, 1) },
|
||||
#endif /* CONFIG_DPP */
|
||||
{ INT_RANGE(owe_group, 0, 65535) },
|
||||
{ INT_RANGE(owe_only, 0, 1) },
|
||||
|
|
|
@ -833,6 +833,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
|
|||
STR(dpp_csign);
|
||||
STR(dpp_pp_key);
|
||||
INT(dpp_pfs);
|
||||
INT(dpp_connector_privacy);
|
||||
#endif /* CONFIG_DPP */
|
||||
INT(owe_group);
|
||||
INT(owe_only);
|
||||
|
|
|
@ -1064,6 +1064,14 @@ struct wpa_ssid {
|
|||
*/
|
||||
int dpp_pfs_fallback;
|
||||
|
||||
/**
|
||||
* dpp_connector_privacy - Network introduction type
|
||||
* 0: unprotected variant from DPP R1
|
||||
* 1: privacy protecting (station Connector encrypted) variant from
|
||||
* DPP R3
|
||||
*/
|
||||
int dpp_connector_privacy;
|
||||
|
||||
/**
|
||||
* owe_group - OWE DH Group
|
||||
*
|
||||
|
|
|
@ -3260,6 +3260,274 @@ skip_hash_check:
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
wpas_dpp_tx_priv_intro_status(struct wpa_supplicant *wpa_s,
|
||||
unsigned int freq, const u8 *dst,
|
||||
const u8 *src, const u8 *bssid,
|
||||
const u8 *data, size_t data_len,
|
||||
enum offchannel_send_action_result result)
|
||||
{
|
||||
const char *res_txt;
|
||||
|
||||
res_txt = result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" :
|
||||
(result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" :
|
||||
"FAILED");
|
||||
wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR
|
||||
" result=%s (DPP Private Peer Introduction Update)",
|
||||
freq, MAC2STR(dst), res_txt);
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX_STATUS "dst=" MACSTR
|
||||
" freq=%u result=%s", MAC2STR(dst), freq, res_txt);
|
||||
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR " version=%u",
|
||||
MAC2STR(src), wpa_s->dpp_intro_peer_version);
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Try connection again after successful network introduction");
|
||||
if (wpa_supplicant_fast_associate(wpa_s) != 1) {
|
||||
wpa_supplicant_cancel_sched_scan(wpa_s);
|
||||
wpa_supplicant_req_scan(wpa_s, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
wpas_dpp_send_private_peer_intro_update(struct wpa_supplicant *wpa_s,
|
||||
struct dpp_introduction *intro,
|
||||
struct wpa_ssid *ssid,
|
||||
const u8 *dst, unsigned int freq)
|
||||
{
|
||||
struct wpabuf *pt, *msg, *enc_ct;
|
||||
size_t len;
|
||||
u8 ver = DPP_VERSION;
|
||||
int conn_ver;
|
||||
const u8 *aad;
|
||||
size_t aad_len;
|
||||
unsigned int wait_time;
|
||||
|
||||
wpa_printf(MSG_DEBUG, "HPKE(kem_id=%u kdf_id=%u aead_id=%u)",
|
||||
intro->kem_id, intro->kdf_id, intro->aead_id);
|
||||
|
||||
/* Plaintext for HPKE */
|
||||
len = 5 + 4 + os_strlen(ssid->dpp_connector);
|
||||
pt = wpabuf_alloc(len);
|
||||
if (!pt)
|
||||
return -1;
|
||||
|
||||
/* Protocol Version */
|
||||
conn_ver = dpp_get_connector_version(ssid->dpp_connector);
|
||||
if (conn_ver > 0 && ver != conn_ver) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Use Connector version %d instead of current protocol version %d",
|
||||
conn_ver, ver);
|
||||
ver = conn_ver;
|
||||
}
|
||||
wpabuf_put_le16(pt, DPP_ATTR_PROTOCOL_VERSION);
|
||||
wpabuf_put_le16(pt, 1);
|
||||
wpabuf_put_u8(pt, ver);
|
||||
|
||||
/* Connector */
|
||||
wpabuf_put_le16(pt, DPP_ATTR_CONNECTOR);
|
||||
wpabuf_put_le16(pt, os_strlen(ssid->dpp_connector));
|
||||
wpabuf_put_str(pt, ssid->dpp_connector);
|
||||
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Plaintext for HPKE", pt);
|
||||
|
||||
/* HPKE(pt) using AP's public key (from its Connector) */
|
||||
msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_UPDATE, 0);
|
||||
if (!msg) {
|
||||
wpabuf_free(pt);
|
||||
return -1;
|
||||
}
|
||||
aad = wpabuf_head_u8(msg) + 2; /* from the OUI field (inclusive) */
|
||||
aad_len = DPP_HDR_LEN; /* to the DPP Frame Type field (inclusive) */
|
||||
wpa_hexdump(MSG_MSGDUMP, "DPP: AAD for HPKE", aad, aad_len);
|
||||
|
||||
enc_ct = hpke_base_seal(intro->kem_id, intro->kdf_id, intro->aead_id,
|
||||
intro->peer_key, NULL, 0, aad, aad_len,
|
||||
wpabuf_head(pt), wpabuf_len(pt));
|
||||
wpabuf_free(pt);
|
||||
wpabuf_free(msg);
|
||||
if (!enc_ct) {
|
||||
wpa_printf(MSG_INFO, "DPP: HPKE Seal(Connector) failed");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE enc|ct", enc_ct);
|
||||
|
||||
/* HPKE(pt) to generate payload for Wrapped Data */
|
||||
len = 5 + 4 + wpabuf_len(enc_ct);
|
||||
msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_UPDATE, len);
|
||||
if (!msg) {
|
||||
wpabuf_free(enc_ct);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Transaction ID */
|
||||
wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
|
||||
wpabuf_put_le16(msg, 1);
|
||||
wpabuf_put_u8(msg, TRANSACTION_ID);
|
||||
|
||||
/* Wrapped Data */
|
||||
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
|
||||
wpabuf_put_le16(msg, wpabuf_len(enc_ct));
|
||||
wpabuf_put_buf(msg, enc_ct);
|
||||
wpabuf_free(enc_ct);
|
||||
|
||||
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Private Peer Intro Update", msg);
|
||||
|
||||
/* TODO: Timeout on AP response */
|
||||
wait_time = wpa_s->max_remain_on_chan;
|
||||
if (wait_time > 2000)
|
||||
wait_time = 2000;
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
|
||||
MAC2STR(dst), freq, DPP_PA_PRIV_PEER_INTRO_QUERY);
|
||||
offchannel_send_action(wpa_s, freq, dst, wpa_s->own_addr, broadcast,
|
||||
wpabuf_head(msg), wpabuf_len(msg),
|
||||
wait_time, wpas_dpp_tx_priv_intro_status, 0);
|
||||
wpabuf_free(msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
wpas_dpp_rx_priv_peer_intro_notify(struct wpa_supplicant *wpa_s,
|
||||
const u8 *src, const u8 *hdr,
|
||||
const u8 *buf, size_t len,
|
||||
unsigned int freq)
|
||||
{
|
||||
struct wpa_ssid *ssid;
|
||||
const u8 *connector, *trans_id, *version;
|
||||
u16 connector_len, trans_id_len, version_len;
|
||||
u8 peer_version = 1;
|
||||
struct dpp_introduction intro;
|
||||
struct rsn_pmksa_cache_entry *entry;
|
||||
struct os_time now;
|
||||
struct os_reltime rnow;
|
||||
os_time_t expiry;
|
||||
unsigned int seconds;
|
||||
enum dpp_status_error res;
|
||||
|
||||
os_memset(&intro, 0, sizeof(intro));
|
||||
|
||||
wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Notify from "
|
||||
MACSTR, MAC2STR(src));
|
||||
if (is_zero_ether_addr(wpa_s->dpp_intro_bssid) ||
|
||||
os_memcmp(src, wpa_s->dpp_intro_bssid, ETH_ALEN) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "DPP: Not waiting for response from "
|
||||
MACSTR " - drop", MAC2STR(src));
|
||||
return;
|
||||
}
|
||||
offchannel_send_action_done(wpa_s);
|
||||
|
||||
for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
|
||||
if (ssid == wpa_s->dpp_intro_network)
|
||||
break;
|
||||
}
|
||||
if (!ssid || !ssid->dpp_connector || !ssid->dpp_netaccesskey ||
|
||||
!ssid->dpp_csign) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Profile not found for network introduction");
|
||||
return;
|
||||
}
|
||||
|
||||
trans_id = dpp_get_attr(buf, len, DPP_ATTR_TRANSACTION_ID,
|
||||
&trans_id_len);
|
||||
if (!trans_id || trans_id_len != 1) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Peer did not include Transaction ID");
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
|
||||
" fail=missing_transaction_id", MAC2STR(src));
|
||||
goto fail;
|
||||
}
|
||||
if (trans_id[0] != TRANSACTION_ID) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Ignore frame with unexpected Transaction ID %u",
|
||||
trans_id[0]);
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
|
||||
" fail=transaction_id_mismatch", MAC2STR(src));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
connector = dpp_get_attr(buf, len, DPP_ATTR_CONNECTOR, &connector_len);
|
||||
if (!connector) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Peer did not include its Connector");
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
|
||||
" fail=missing_connector", MAC2STR(src));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
|
||||
&version_len);
|
||||
if (!version || version_len < 1) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Peer did not include valid Version");
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
|
||||
" fail=missing_version", MAC2STR(src));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = dpp_peer_intro(&intro, ssid->dpp_connector,
|
||||
ssid->dpp_netaccesskey,
|
||||
ssid->dpp_netaccesskey_len,
|
||||
ssid->dpp_csign,
|
||||
ssid->dpp_csign_len,
|
||||
connector, connector_len, &expiry);
|
||||
if (res != DPP_STATUS_OK) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"DPP: Network Introduction protocol resulted in failure");
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_INTRO "peer=" MACSTR
|
||||
" fail=peer_connector_validation_failed", MAC2STR(src));
|
||||
wpas_dpp_send_conn_status_result(wpa_s, res);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
peer_version = version[0];
|
||||
if (intro.peer_version && intro.peer_version >= 2 &&
|
||||
peer_version != intro.peer_version) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"DPP: Protocol version mismatch (Connector: %d Attribute: %d",
|
||||
intro.peer_version, peer_version);
|
||||
wpas_dpp_send_conn_status_result(wpa_s, DPP_STATUS_NO_MATCH);
|
||||
goto fail;
|
||||
}
|
||||
wpa_s->dpp_intro_peer_version = peer_version;
|
||||
|
||||
entry = os_zalloc(sizeof(*entry));
|
||||
if (!entry)
|
||||
goto fail;
|
||||
entry->dpp_pfs = peer_version >= 2;
|
||||
os_memcpy(entry->aa, src, ETH_ALEN);
|
||||
os_memcpy(entry->pmkid, intro.pmkid, PMKID_LEN);
|
||||
os_memcpy(entry->pmk, intro.pmk, intro.pmk_len);
|
||||
entry->pmk_len = intro.pmk_len;
|
||||
entry->akmp = WPA_KEY_MGMT_DPP;
|
||||
if (expiry) {
|
||||
os_get_time(&now);
|
||||
seconds = expiry - now.sec;
|
||||
} else {
|
||||
seconds = 86400 * 7;
|
||||
}
|
||||
|
||||
if (wpas_dpp_send_private_peer_intro_update(wpa_s, &intro, ssid, src,
|
||||
freq) < 0) {
|
||||
os_free(entry);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
os_get_reltime(&rnow);
|
||||
entry->expiration = rnow.sec + seconds;
|
||||
entry->reauth_time = rnow.sec + seconds;
|
||||
entry->network_ctx = ssid;
|
||||
wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
|
||||
|
||||
/* Association will be initiated from TX status handler for the Private
|
||||
* Peer Intro Update: wpas_dpp_tx_priv_intro_status() */
|
||||
|
||||
fail:
|
||||
dpp_peer_intro_deinit(&intro);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DPP3 */
|
||||
|
||||
|
||||
|
@ -3377,6 +3645,10 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
|
|||
wpas_dpp_rx_pb_presence_announcement_resp(wpa_s, src, hdr,
|
||||
buf, len, freq);
|
||||
break;
|
||||
case DPP_PA_PRIV_PEER_INTRO_NOTIFY:
|
||||
wpas_dpp_rx_priv_peer_intro_notify(wpa_s, src, hdr,
|
||||
buf, len, freq);
|
||||
break;
|
||||
#endif /* CONFIG_DPP3 */
|
||||
default:
|
||||
wpa_printf(MSG_DEBUG,
|
||||
|
@ -3635,6 +3907,63 @@ wpas_dpp_tx_introduction_status(struct wpa_supplicant *wpa_s,
|
|||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_DPP3
|
||||
static int wpas_dpp_start_private_peer_intro(struct wpa_supplicant *wpa_s,
|
||||
struct wpa_ssid *ssid,
|
||||
struct wpa_bss *bss)
|
||||
{
|
||||
struct wpabuf *msg;
|
||||
unsigned int wait_time;
|
||||
size_t len;
|
||||
u8 ver = DPP_VERSION;
|
||||
int conn_ver;
|
||||
|
||||
len = 5 + 5;
|
||||
msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_QUERY, len);
|
||||
if (!msg)
|
||||
return -1;
|
||||
|
||||
/* Transaction ID */
|
||||
wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
|
||||
wpabuf_put_le16(msg, 1);
|
||||
wpabuf_put_u8(msg, TRANSACTION_ID);
|
||||
|
||||
conn_ver = dpp_get_connector_version(ssid->dpp_connector);
|
||||
if (conn_ver > 0 && ver != conn_ver) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Use Connector version %d instead of current protocol version %d",
|
||||
conn_ver, ver);
|
||||
ver = conn_ver;
|
||||
}
|
||||
|
||||
/* Protocol Version */
|
||||
wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
|
||||
wpabuf_put_le16(msg, 1);
|
||||
wpabuf_put_u8(msg, ver);
|
||||
|
||||
wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Private Peer Intro Query", msg);
|
||||
|
||||
/* TODO: Timeout on AP response */
|
||||
wait_time = wpa_s->max_remain_on_chan;
|
||||
if (wait_time > 2000)
|
||||
wait_time = 2000;
|
||||
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
|
||||
MAC2STR(bss->bssid), bss->freq, DPP_PA_PRIV_PEER_INTRO_QUERY);
|
||||
offchannel_send_action(wpa_s, bss->freq, bss->bssid, wpa_s->own_addr,
|
||||
broadcast,
|
||||
wpabuf_head(msg), wpabuf_len(msg),
|
||||
wait_time, wpas_dpp_tx_introduction_status, 0);
|
||||
wpabuf_free(msg);
|
||||
|
||||
/* Request this connection attempt to terminate - new one will be
|
||||
* started when network introduction protocol completes */
|
||||
os_memcpy(wpa_s->dpp_intro_bssid, bss->bssid, ETH_ALEN);
|
||||
wpa_s->dpp_intro_network = ssid;
|
||||
return 1;
|
||||
}
|
||||
#endif /* CONFIG_DPP3 */
|
||||
|
||||
|
||||
int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
|
||||
struct wpa_bss *bss)
|
||||
{
|
||||
|
@ -3674,11 +4003,18 @@ int wpas_dpp_check_connect(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
|
|||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"DPP: Starting network introduction protocol to derive PMKSA for "
|
||||
MACSTR, MAC2STR(bss->bssid));
|
||||
"DPP: Starting %snetwork introduction protocol to derive PMKSA for "
|
||||
MACSTR,
|
||||
ssid->dpp_connector_privacy ? "private " : "",
|
||||
MAC2STR(bss->bssid));
|
||||
if (wpa_s->wpa_state == WPA_SCANNING)
|
||||
wpa_supplicant_set_state(wpa_s, wpa_s->scan_prev_wpa_state);
|
||||
|
||||
#ifdef CONFIG_DPP3
|
||||
if (ssid->dpp_connector_privacy)
|
||||
return wpas_dpp_start_private_peer_intro(wpa_s, ssid, bss);
|
||||
#endif /* CONFIG_DPP3 */
|
||||
|
||||
len = 5 + 4 + os_strlen(ssid->dpp_connector);
|
||||
#ifdef CONFIG_DPP2
|
||||
len += 5;
|
||||
|
|
|
@ -1493,6 +1493,12 @@ fast_reauth=1
|
|||
# 2: do not allow PFS to be used
|
||||
#dpp_pfs=0
|
||||
|
||||
# DPP Network introduction type
|
||||
# 0: unprotected variant from DPP R1 (default)
|
||||
# 1: privacy protecting (station Connector encrypted) variant from
|
||||
# DPP R3
|
||||
#dpp_connector_privacy=0
|
||||
|
||||
# Whether beacon protection is enabled
|
||||
# This depends on management frame protection (ieee80211w) being enabled and
|
||||
# beacon protection support indication from the driver.
|
||||
|
|
|
@ -1459,6 +1459,7 @@ struct wpa_supplicant {
|
|||
int dpp_gas_dialog_token;
|
||||
u8 dpp_intro_bssid[ETH_ALEN];
|
||||
void *dpp_intro_network;
|
||||
u8 dpp_intro_peer_version;
|
||||
struct dpp_pkex *dpp_pkex;
|
||||
struct dpp_bootstrap_info *dpp_pkex_bi;
|
||||
char *dpp_pkex_code;
|
||||
|
|
Loading…
Reference in a new issue