DPP2: Reconfig Authentication Response processing and Confirm generation
Extend Configurator functionality to process Reconfig Authentication Response message, derive the needed keys, and generate Reconfig Authentication Confirm message. Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
parent
65e94351dc
commit
24b01c706b
7 changed files with 497 additions and 3 deletions
|
@ -1300,6 +1300,50 @@ hostapd_dpp_rx_reconfig_announcement(struct hostapd_data *hapd, const u8 *src,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
hostapd_dpp_rx_reconfig_auth_resp(struct hostapd_data *hapd, const u8 *src,
|
||||||
|
const u8 *hdr, const u8 *buf, size_t len,
|
||||||
|
unsigned int freq)
|
||||||
|
{
|
||||||
|
struct dpp_authentication *auth = hapd->dpp_auth;
|
||||||
|
struct wpabuf *conf;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response from "
|
||||||
|
MACSTR, MAC2STR(src));
|
||||||
|
|
||||||
|
if (!auth || !auth->reconfig || !auth->configurator) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DPP: No DPP Reconfig Authentication in progress - drop");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
|
||||||
|
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
|
||||||
|
if (!conf)
|
||||||
|
return;
|
||||||
|
|
||||||
|
eloop_cancel_timeout(hostapd_dpp_reconfig_reply_wait_timeout,
|
||||||
|
hapd, NULL);
|
||||||
|
|
||||||
|
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
|
||||||
|
" freq=%u type=%d",
|
||||||
|
MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_CONF);
|
||||||
|
if (hostapd_drv_send_action(hapd, freq, 500, src,
|
||||||
|
wpabuf_head(conf), wpabuf_len(conf)) < 0) {
|
||||||
|
wpabuf_free(conf);
|
||||||
|
dpp_auth_deinit(hapd->dpp_auth);
|
||||||
|
hapd->dpp_auth = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wpabuf_free(conf);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_DPP2 */
|
#endif /* CONFIG_DPP2 */
|
||||||
|
|
||||||
|
|
||||||
|
@ -1770,6 +1814,10 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
|
||||||
hostapd_dpp_rx_reconfig_announcement(hapd, src, hdr, buf, len,
|
hostapd_dpp_rx_reconfig_announcement(hapd, src, hdr, buf, len,
|
||||||
freq);
|
freq);
|
||||||
break;
|
break;
|
||||||
|
case DPP_PA_RECONFIG_AUTH_RESP:
|
||||||
|
hostapd_dpp_rx_reconfig_auth_resp(hapd, src, hdr, buf, len,
|
||||||
|
freq);
|
||||||
|
break;
|
||||||
#endif /* CONFIG_DPP2 */
|
#endif /* CONFIG_DPP2 */
|
||||||
default:
|
default:
|
||||||
wpa_printf(MSG_DEBUG,
|
wpa_printf(MSG_DEBUG,
|
||||||
|
@ -1799,7 +1847,7 @@ hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
|
||||||
struct wpabuf *resp;
|
struct wpabuf *resp;
|
||||||
|
|
||||||
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
|
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR, MAC2STR(sa));
|
||||||
if (!auth || !auth->auth_success ||
|
if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
|
||||||
os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
|
os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
|
||||||
#ifdef CONFIG_DPP2
|
#ifdef CONFIG_DPP2
|
||||||
if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
|
if (dpp_relay_rx_gas_req(hapd->iface->interfaces->dpp, sa, data,
|
||||||
|
|
|
@ -3462,7 +3462,8 @@ dpp_build_conf_obj_dpp(struct dpp_authentication *auth,
|
||||||
#ifdef CONFIG_TESTING_OPTIONS
|
#ifdef CONFIG_TESTING_OPTIONS
|
||||||
skip_groups:
|
skip_groups:
|
||||||
#endif /* CONFIG_TESTING_OPTIONS */
|
#endif /* CONFIG_TESTING_OPTIONS */
|
||||||
if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
|
if (!auth->peer_protocol_key ||
|
||||||
|
dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL,
|
||||||
auth->curve) < 0) {
|
auth->curve) < 0) {
|
||||||
wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
|
wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
|
@ -106,6 +106,12 @@ enum dpp_status_error {
|
||||||
DPP_STATUS_CSR_BAD = 13,
|
DPP_STATUS_CSR_BAD = 13,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* DPP Reconfig Flags object - connectorKey values */
|
||||||
|
enum dpp_connector_key {
|
||||||
|
DPP_CONFIG_REUSEKEY = 0,
|
||||||
|
DPP_CONFIG_REPLACEKEY = 1,
|
||||||
|
};
|
||||||
|
|
||||||
#define DPP_CAPAB_ENROLLEE BIT(0)
|
#define DPP_CAPAB_ENROLLEE BIT(0)
|
||||||
#define DPP_CAPAB_CONFIGURATOR BIT(1)
|
#define DPP_CAPAB_CONFIGURATOR BIT(1)
|
||||||
#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
|
#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1))
|
||||||
|
@ -282,6 +288,7 @@ struct dpp_authentication {
|
||||||
int waiting_conf_result;
|
int waiting_conf_result;
|
||||||
int waiting_conn_status_result;
|
int waiting_conn_status_result;
|
||||||
int auth_success;
|
int auth_success;
|
||||||
|
bool reconfig_success;
|
||||||
struct wpabuf *conf_req;
|
struct wpabuf *conf_req;
|
||||||
const struct wpabuf *conf_resp; /* owned by GAS server */
|
const struct wpabuf *conf_resp; /* owned by GAS server */
|
||||||
struct dpp_configuration *conf_ap;
|
struct dpp_configuration *conf_ap;
|
||||||
|
@ -651,6 +658,9 @@ dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
|
||||||
const u8 *csign_key, size_t csign_key_len,
|
const u8 *csign_key, size_t csign_key_len,
|
||||||
unsigned int freq, const u8 *hdr,
|
unsigned int freq, const u8 *hdr,
|
||||||
const u8 *attr_start, size_t attr_len);
|
const u8 *attr_start, size_t attr_len);
|
||||||
|
struct wpabuf *
|
||||||
|
dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
|
||||||
|
const u8 *attr_start, size_t attr_len);
|
||||||
|
|
||||||
#endif /* CONFIG_DPP */
|
#endif /* CONFIG_DPP */
|
||||||
#endif /* DPP_H */
|
#endif /* DPP_H */
|
||||||
|
|
|
@ -2344,6 +2344,97 @@ fail:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
|
||||||
|
const u8 *r_proto, u16 r_proto_len,
|
||||||
|
struct json_token *net_access_key)
|
||||||
|
{
|
||||||
|
BN_CTX *bnctx = NULL;
|
||||||
|
EVP_PKEY *pr = NULL, *peer_key = NULL;
|
||||||
|
EC_POINT *sum = NULL, *m = NULL;
|
||||||
|
BIGNUM *mx = NULL;
|
||||||
|
const EC_KEY *cI, *CR, *PR;
|
||||||
|
const EC_GROUP *group;
|
||||||
|
const EC_POINT *CR_point, *PR_point;
|
||||||
|
const BIGNUM *cI_bn;
|
||||||
|
u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
|
||||||
|
u8 prk[DPP_MAX_HASH_LEN];
|
||||||
|
int res = -1;
|
||||||
|
const struct dpp_curve_params *curve;
|
||||||
|
|
||||||
|
pr = dpp_set_pubkey_point(auth->conf->connector_key,
|
||||||
|
r_proto, r_proto_len);
|
||||||
|
if (!pr) {
|
||||||
|
dpp_auth_fail(auth, "Invalid Responder Protocol Key");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
|
||||||
|
EVP_PKEY_free(auth->peer_protocol_key);
|
||||||
|
auth->peer_protocol_key = pr;
|
||||||
|
pr = NULL;
|
||||||
|
|
||||||
|
peer_key = dpp_parse_jwk(net_access_key, &curve);
|
||||||
|
if (!peer_key)
|
||||||
|
goto fail;
|
||||||
|
dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
|
||||||
|
if (auth->curve != curve) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DPP: Mismatching netAccessKey curves (%s != %s)",
|
||||||
|
auth->curve->name, curve->name);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* M = cI * { CR + PR } */
|
||||||
|
cI = EVP_PKEY_get0_EC_KEY(auth->conf->connector_key);
|
||||||
|
cI_bn = EC_KEY_get0_private_key(cI);
|
||||||
|
group = EC_KEY_get0_group(cI);
|
||||||
|
bnctx = BN_CTX_new();
|
||||||
|
sum = EC_POINT_new(group);
|
||||||
|
m = EC_POINT_new(group);
|
||||||
|
mx = BN_new();
|
||||||
|
CR = EVP_PKEY_get0_EC_KEY(peer_key);
|
||||||
|
PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
|
||||||
|
CR_point = EC_KEY_get0_public_key(CR);
|
||||||
|
PR_point = EC_KEY_get0_public_key(PR);
|
||||||
|
if (!bnctx || !sum || !m || !mx ||
|
||||||
|
EC_POINT_add(group, sum, CR_point, PR_point, bnctx) != 1 ||
|
||||||
|
EC_POINT_mul(group, m, NULL, sum, cI_bn, bnctx) != 1 ||
|
||||||
|
EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
|
||||||
|
bnctx) != 1 ||
|
||||||
|
dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
|
||||||
|
|
||||||
|
/* ke = HKDF(I-nonce, "dpp reconfig key", M.x) */
|
||||||
|
|
||||||
|
/* HKDF-Extract(I-nonce, M.x) */
|
||||||
|
if (dpp_hmac(curve->hash_len, auth->i_nonce, curve->nonce_len,
|
||||||
|
Mx, curve->prime_len, prk) < 0)
|
||||||
|
goto fail;
|
||||||
|
wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
|
||||||
|
|
||||||
|
/* HKDF-Expand(PRK, "dpp reconfig key", L) */
|
||||||
|
if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
|
||||||
|
"dpp reconfig key", auth->ke, curve->hash_len) < 0)
|
||||||
|
goto fail;
|
||||||
|
wpa_hexdump_key(MSG_DEBUG,
|
||||||
|
"DPP: ke = HKDF(I-nonce, \"dpp reconfig key\", M.x)",
|
||||||
|
auth->ke, curve->hash_len);
|
||||||
|
|
||||||
|
res = 0;
|
||||||
|
fail:
|
||||||
|
forced_memzero(prk, sizeof(prk));
|
||||||
|
forced_memzero(Mx, sizeof(Mx));
|
||||||
|
EVP_PKEY_free(pr);
|
||||||
|
EVP_PKEY_free(peer_key);
|
||||||
|
EC_POINT_clear_free(sum);
|
||||||
|
EC_POINT_clear_free(m);
|
||||||
|
BN_clear_free(mx);
|
||||||
|
BN_CTX_free(bnctx);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
dpp_build_jws_prot_hdr(struct dpp_configurator *conf, size_t *signed1_len)
|
dpp_build_jws_prot_hdr(struct dpp_configurator *conf, size_t *signed1_len)
|
||||||
{
|
{
|
||||||
|
|
|
@ -123,6 +123,9 @@ int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
|
||||||
const u8 *net_access_key,
|
const u8 *net_access_key,
|
||||||
size_t net_access_key_len,
|
size_t net_access_key_len,
|
||||||
struct json_token *peer_net_access_key);
|
struct json_token *peer_net_access_key);
|
||||||
|
int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
|
||||||
|
const u8 *r_proto, u16 r_proto_len,
|
||||||
|
struct json_token *net_access_key);
|
||||||
char * dpp_sign_connector(struct dpp_configurator *conf,
|
char * dpp_sign_connector(struct dpp_configurator *conf,
|
||||||
const struct wpabuf *dppcon);
|
const struct wpabuf *dppcon);
|
||||||
int dpp_test_gen_invalid_key(struct wpabuf *msg,
|
int dpp_test_gen_invalid_key(struct wpabuf *msg,
|
||||||
|
|
|
@ -461,4 +461,297 @@ fail:
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static struct wpabuf *
|
||||||
|
dpp_build_reconfig_flags(enum dpp_connector_key connector_key)
|
||||||
|
{
|
||||||
|
struct wpabuf *json;
|
||||||
|
|
||||||
|
json = wpabuf_alloc(100);
|
||||||
|
if (!json)
|
||||||
|
return NULL;
|
||||||
|
json_start_object(json, NULL);
|
||||||
|
json_add_int(json, "connectorKey", connector_key);
|
||||||
|
json_end_object(json);
|
||||||
|
wpa_hexdump_ascii(MSG_DEBUG, "DPP: Reconfig-Flags JSON",
|
||||||
|
wpabuf_head(json), wpabuf_len(json));
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct wpabuf *
|
||||||
|
dpp_reconfig_build_conf(struct dpp_authentication *auth)
|
||||||
|
{
|
||||||
|
struct wpabuf *msg = NULL, *clear = NULL, *reconfig_flags;
|
||||||
|
u8 *attr_start, *attr_end;
|
||||||
|
size_t clear_len, attr_len, len[2];
|
||||||
|
const u8 *addr[2];
|
||||||
|
u8 *wrapped;
|
||||||
|
|
||||||
|
reconfig_flags = dpp_build_reconfig_flags(DPP_CONFIG_REPLACEKEY);
|
||||||
|
if (!reconfig_flags)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* Build DPP Reconfig Authentication Confirm frame attributes */
|
||||||
|
clear_len = 4 + 1 + 4 + 1 + 2 * (4 + auth->curve->nonce_len) +
|
||||||
|
4 + wpabuf_len(reconfig_flags);
|
||||||
|
clear = wpabuf_alloc(clear_len);
|
||||||
|
if (!clear)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
/* Transaction ID */
|
||||||
|
wpabuf_put_le16(clear, DPP_ATTR_TRANSACTION_ID);
|
||||||
|
wpabuf_put_le16(clear, 1);
|
||||||
|
wpabuf_put_u8(clear, auth->transaction_id);
|
||||||
|
|
||||||
|
/* Protocol Version */
|
||||||
|
wpabuf_put_le16(clear, DPP_ATTR_PROTOCOL_VERSION);
|
||||||
|
wpabuf_put_le16(clear, 1);
|
||||||
|
wpabuf_put_u8(clear, auth->peer_version);
|
||||||
|
|
||||||
|
/* I-nonce (wrapped) */
|
||||||
|
wpabuf_put_le16(clear, DPP_ATTR_I_NONCE);
|
||||||
|
wpabuf_put_le16(clear, auth->curve->nonce_len);
|
||||||
|
wpabuf_put_data(clear, auth->i_nonce, auth->curve->nonce_len);
|
||||||
|
|
||||||
|
/* R-nonce (wrapped) */
|
||||||
|
wpabuf_put_le16(clear, DPP_ATTR_R_NONCE);
|
||||||
|
wpabuf_put_le16(clear, auth->curve->nonce_len);
|
||||||
|
wpabuf_put_data(clear, auth->r_nonce, auth->curve->nonce_len);
|
||||||
|
|
||||||
|
/* Reconfig-Flags (wrapped) */
|
||||||
|
wpabuf_put_le16(clear, DPP_ATTR_RECONFIG_FLAGS);
|
||||||
|
wpabuf_put_le16(clear, wpabuf_len(reconfig_flags));
|
||||||
|
wpabuf_put_buf(clear, reconfig_flags);
|
||||||
|
|
||||||
|
attr_len = 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
|
||||||
|
msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_CONF, attr_len);
|
||||||
|
if (!msg)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
attr_start = wpabuf_put(msg, 0);
|
||||||
|
attr_end = wpabuf_put(msg, 0);
|
||||||
|
|
||||||
|
/* OUI, OUI type, Crypto Suite, DPP frame type */
|
||||||
|
addr[0] = wpabuf_head_u8(msg) + 2;
|
||||||
|
len[0] = 3 + 1 + 1 + 1;
|
||||||
|
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
|
||||||
|
|
||||||
|
/* Attributes before Wrapped Data */
|
||||||
|
addr[1] = attr_start;
|
||||||
|
len[1] = attr_end - attr_start;
|
||||||
|
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
|
||||||
|
|
||||||
|
/* Wrapped Data */
|
||||||
|
wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
|
||||||
|
wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
|
||||||
|
wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
|
||||||
|
|
||||||
|
wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
|
||||||
|
if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
|
||||||
|
wpabuf_head(clear), wpabuf_len(clear),
|
||||||
|
2, addr, len, wrapped) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
wpa_hexdump_buf(MSG_DEBUG,
|
||||||
|
"DPP: Reconfig Authentication Confirm frame attributes",
|
||||||
|
msg);
|
||||||
|
|
||||||
|
out:
|
||||||
|
wpabuf_free(reconfig_flags);
|
||||||
|
wpabuf_free(clear);
|
||||||
|
return msg;
|
||||||
|
fail:
|
||||||
|
wpabuf_free(msg);
|
||||||
|
msg = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct wpabuf *
|
||||||
|
dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
|
||||||
|
const u8 *attr_start, size_t attr_len)
|
||||||
|
{
|
||||||
|
const u8 *trans_id, *version, *r_connector, *r_proto, *wrapped_data,
|
||||||
|
*i_nonce, *r_nonce, *conn_status;
|
||||||
|
u16 trans_id_len, version_len, r_connector_len, r_proto_len,
|
||||||
|
wrapped_data_len, i_nonce_len, r_nonce_len, conn_status_len;
|
||||||
|
struct wpabuf *conf = NULL;
|
||||||
|
char *signed_connector = NULL;
|
||||||
|
struct dpp_signed_connector_info info;
|
||||||
|
enum dpp_status_error res;
|
||||||
|
struct json_token *root = NULL, *token, *conn_status_json = NULL;
|
||||||
|
const u8 *addr[2];
|
||||||
|
size_t len[2];
|
||||||
|
u8 *unwrapped = NULL;
|
||||||
|
size_t unwrapped_len = 0;
|
||||||
|
|
||||||
|
os_memset(&info, 0, sizeof(info));
|
||||||
|
|
||||||
|
if (!auth->reconfig || !auth->configurator)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
|
||||||
|
&wrapped_data_len);
|
||||||
|
if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
|
||||||
|
dpp_auth_fail(auth,
|
||||||
|
"Missing or invalid required Wrapped Data attribute");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
|
||||||
|
wrapped_data, wrapped_data_len);
|
||||||
|
attr_len = wrapped_data - 4 - attr_start;
|
||||||
|
|
||||||
|
trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
|
||||||
|
&trans_id_len);
|
||||||
|
if (!trans_id || trans_id_len != 1) {
|
||||||
|
dpp_auth_fail(auth, "Peer did not include Transaction ID");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
if (trans_id[0] != auth->transaction_id) {
|
||||||
|
dpp_auth_fail(auth, "Transaction ID mismatch");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
|
||||||
|
&version_len);
|
||||||
|
if (!version || version_len < 1 || version[0] < 2) {
|
||||||
|
dpp_auth_fail(auth,
|
||||||
|
"Missing or invalid Protocol Version attribute");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
auth->peer_version = version[0];
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
|
||||||
|
auth->peer_version);
|
||||||
|
|
||||||
|
r_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
|
||||||
|
&r_connector_len);
|
||||||
|
if (!r_connector) {
|
||||||
|
dpp_auth_fail(auth, " Missing R-Connector attribute");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
wpa_hexdump_ascii(MSG_DEBUG, "DPP: R-Connector",
|
||||||
|
r_connector, r_connector_len);
|
||||||
|
|
||||||
|
r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
|
||||||
|
&r_proto_len);
|
||||||
|
if (!r_proto) {
|
||||||
|
dpp_auth_fail(auth,
|
||||||
|
"Missing required Responder Protocol Key attribute");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
|
||||||
|
r_proto, r_proto_len);
|
||||||
|
|
||||||
|
signed_connector = os_malloc(r_connector_len + 1);
|
||||||
|
if (!signed_connector)
|
||||||
|
goto fail;
|
||||||
|
os_memcpy(signed_connector, r_connector, r_connector_len);
|
||||||
|
signed_connector[r_connector_len] = '\0';
|
||||||
|
|
||||||
|
res = dpp_process_signed_connector(&info, auth->conf->csign,
|
||||||
|
signed_connector);
|
||||||
|
if (res != DPP_STATUS_OK) {
|
||||||
|
dpp_auth_fail(auth, "Invalid R-Connector");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
root = json_parse((const char *) info.payload, info.payload_len);
|
||||||
|
if (!root) {
|
||||||
|
dpp_auth_fail(auth, "Invalid Connector payload");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do not check netAccessKey expiration for reconfiguration to allow
|
||||||
|
* expired Connector to be updated. */
|
||||||
|
|
||||||
|
token = json_get_member(root, "netAccessKey");
|
||||||
|
if (!token || token->type != JSON_OBJECT) {
|
||||||
|
dpp_auth_fail(auth, "No netAccessKey object found");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dpp_reconfig_derive_ke_initiator(auth, r_proto, r_proto_len,
|
||||||
|
token) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
addr[0] = hdr;
|
||||||
|
len[0] = DPP_HDR_LEN;
|
||||||
|
addr[1] = attr_start;
|
||||||
|
len[1] = attr_len;
|
||||||
|
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
|
||||||
|
wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
|
||||||
|
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
|
||||||
|
wrapped_data, wrapped_data_len);
|
||||||
|
unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
|
||||||
|
unwrapped = os_malloc(unwrapped_len);
|
||||||
|
if (!unwrapped)
|
||||||
|
goto fail;
|
||||||
|
if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
|
||||||
|
wrapped_data, wrapped_data_len,
|
||||||
|
2, addr, len, unwrapped) < 0) {
|
||||||
|
dpp_auth_fail(auth, "AES-SIV decryption failed");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
|
||||||
|
unwrapped, unwrapped_len);
|
||||||
|
|
||||||
|
if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
|
||||||
|
dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
|
||||||
|
&i_nonce_len);
|
||||||
|
if (!i_nonce || i_nonce_len != auth->curve->nonce_len ||
|
||||||
|
os_memcmp(i_nonce, auth->i_nonce, i_nonce_len) != 0) {
|
||||||
|
dpp_auth_fail(auth, "Missing or invalid I-nonce");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
|
||||||
|
|
||||||
|
r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
|
||||||
|
&r_nonce_len);
|
||||||
|
if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
|
||||||
|
dpp_auth_fail(auth, "Missing or invalid R-nonce");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
|
||||||
|
os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
|
||||||
|
|
||||||
|
conn_status = dpp_get_attr(unwrapped, unwrapped_len,
|
||||||
|
DPP_ATTR_CONN_STATUS, &conn_status_len);
|
||||||
|
if (!conn_status) {
|
||||||
|
dpp_auth_fail(auth, "Missing Connection Status attribute");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus",
|
||||||
|
conn_status, conn_status_len);
|
||||||
|
|
||||||
|
conn_status_json = json_parse((const char *) conn_status,
|
||||||
|
conn_status_len);
|
||||||
|
if (!conn_status_json) {
|
||||||
|
dpp_auth_fail(auth, "Could not parse connStatus");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
/* TODO: use connStatus information */
|
||||||
|
|
||||||
|
conf = dpp_reconfig_build_conf(auth);
|
||||||
|
if (conf)
|
||||||
|
auth->reconfig_success = true;
|
||||||
|
|
||||||
|
out:
|
||||||
|
json_free(root);
|
||||||
|
json_free(conn_status_json);
|
||||||
|
bin_clear_free(unwrapped, unwrapped_len);
|
||||||
|
os_free(info.payload);
|
||||||
|
os_free(signed_connector);
|
||||||
|
return conf;
|
||||||
|
fail:
|
||||||
|
wpabuf_free(conf);
|
||||||
|
conf = NULL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_DPP2 */
|
#endif /* CONFIG_DPP2 */
|
||||||
|
|
|
@ -1962,6 +1962,51 @@ wpas_dpp_rx_reconfig_auth_req(struct wpa_supplicant *wpa_s, const u8 *src,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
wpas_dpp_rx_reconfig_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src,
|
||||||
|
const u8 *hdr, const u8 *buf, size_t len,
|
||||||
|
unsigned int freq)
|
||||||
|
{
|
||||||
|
struct dpp_authentication *auth = wpa_s->dpp_auth;
|
||||||
|
struct wpabuf *conf;
|
||||||
|
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response from "
|
||||||
|
MACSTR, MAC2STR(src));
|
||||||
|
|
||||||
|
if (!auth || !auth->reconfig || !auth->configurator) {
|
||||||
|
wpa_printf(MSG_DEBUG,
|
||||||
|
"DPP: No DPP Reconfig Authentication in progress - drop");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
|
||||||
|
wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
|
||||||
|
MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
|
||||||
|
if (!conf)
|
||||||
|
return;
|
||||||
|
|
||||||
|
eloop_cancel_timeout(wpas_dpp_reconfig_reply_wait_timeout, wpa_s, NULL);
|
||||||
|
|
||||||
|
wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
|
||||||
|
MAC2STR(src), freq, DPP_PA_RECONFIG_AUTH_CONF);
|
||||||
|
if (offchannel_send_action(wpa_s, freq, src, wpa_s->own_addr, broadcast,
|
||||||
|
wpabuf_head(conf), wpabuf_len(conf),
|
||||||
|
500, wpas_dpp_tx_status, 0) < 0) {
|
||||||
|
wpabuf_free(conf);
|
||||||
|
dpp_auth_deinit(wpa_s->dpp_auth);
|
||||||
|
wpa_s->dpp_auth = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wpabuf_free(conf);
|
||||||
|
|
||||||
|
wpas_dpp_start_gas_server(wpa_s);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_DPP2 */
|
#endif /* CONFIG_DPP2 */
|
||||||
|
|
||||||
|
|
||||||
|
@ -2530,6 +2575,9 @@ void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src,
|
||||||
case DPP_PA_RECONFIG_AUTH_REQ:
|
case DPP_PA_RECONFIG_AUTH_REQ:
|
||||||
wpas_dpp_rx_reconfig_auth_req(wpa_s, src, hdr, buf, len, freq);
|
wpas_dpp_rx_reconfig_auth_req(wpa_s, src, hdr, buf, len, freq);
|
||||||
break;
|
break;
|
||||||
|
case DPP_PA_RECONFIG_AUTH_RESP:
|
||||||
|
wpas_dpp_rx_reconfig_auth_resp(wpa_s, src, hdr, buf, len, freq);
|
||||||
|
break;
|
||||||
#endif /* CONFIG_DPP2 */
|
#endif /* CONFIG_DPP2 */
|
||||||
default:
|
default:
|
||||||
wpa_printf(MSG_DEBUG,
|
wpa_printf(MSG_DEBUG,
|
||||||
|
@ -2560,7 +2608,7 @@ wpas_dpp_gas_req_handler(void *ctx, const u8 *sa, const u8 *query,
|
||||||
|
|
||||||
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR,
|
wpa_printf(MSG_DEBUG, "DPP: GAS request from " MACSTR,
|
||||||
MAC2STR(sa));
|
MAC2STR(sa));
|
||||||
if (!auth || !auth->auth_success ||
|
if (!auth || (!auth->auth_success && !auth->reconfig_success) ||
|
||||||
os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
|
os_memcmp(sa, auth->peer_mac_addr, ETH_ALEN) != 0) {
|
||||||
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
|
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
Loading…
Reference in a new issue