3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-11-25 17:59:25 +01:00

dpp: add support for initiating as a configurator

The difference between the existing code is that IWD will send the
authentication request, making it the initiator.

This handles the use case where IWD is provided a peers URI containing
its bootstrapping key rather than IWD always providing its own URI.
A new DBus API was added, ConfigureEnrollee().

Using ConfigureEnrollee() IWD will act as a configurator but begin by
traversing a channel list (URI provided or default) and waiting for
presence announcements (with one caveat). When an announcement is
received IWD will send an authentication request to the peer, receive
its reply, and send an authentication confirm.

As with being a responder, IWD only supports configuration to the
currently connected BSS and will request the enrollee switch to this
BSS's frequency to preserve network performance.

The caveat here is that only one driver (ath9k) supports multicast frame
registration which prevents presence frame from being received. In this
case it will be required the the peer URI contains a MAC and channel
information. This is because IWD will jump right into sending auth
requests rather than waiting for a presence announcement.
This commit is contained in:
James Prestwood 2022-02-22 14:28:42 -08:00 committed by Denis Kenzior
parent de411818f0
commit 82b808cdfe

496
src/dpp.c
View File

@ -86,15 +86,23 @@ struct dpp_sm {
uint8_t *own_asn1;
size_t own_asn1_len;
uint8_t *peer_asn1;
size_t peer_asn1_len;
uint8_t own_boot_hash[32];
uint8_t peer_boot_hash[32];
const struct l_ecc_curve *curve;
size_t key_len;
size_t nonce_len;
struct l_ecc_scalar *boot_private;
struct l_ecc_point *boot_public;
struct l_ecc_point *peer_boot_public;
enum dpp_state state;
/*
* List of frequencies to jump between. The presence of this list is
* also used to signify that a configurator is an initiator vs responder
*/
uint32_t *freqs;
size_t freqs_len;
size_t freqs_idx;
@ -110,7 +118,9 @@ struct dpp_sm {
uint8_t i_nonce[32];
uint8_t e_nonce[32];
struct l_ecc_scalar *m;
uint64_t ke[L_ECC_MAX_DIGITS];
uint64_t k1[L_ECC_MAX_DIGITS];
uint64_t k2[L_ECC_MAX_DIGITS];
uint64_t auth_tag[L_ECC_MAX_DIGITS];
@ -150,6 +160,16 @@ static void dpp_free_auth_data(struct dpp_sm *dpp)
l_ecc_point_free(dpp->peer_proto_public);
dpp->peer_proto_public = NULL;
}
if (dpp->peer_boot_public) {
l_ecc_point_free(dpp->peer_boot_public);
dpp->peer_boot_public = NULL;
}
if (dpp->m) {
l_ecc_scalar_free(dpp->m);
dpp->m = NULL;
}
}
static void dpp_reset(struct dpp_sm *dpp)
@ -184,6 +204,11 @@ static void dpp_reset(struct dpp_sm *dpp)
dpp->connect_scan_id = 0;
}
if (dpp->peer_asn1) {
l_free(dpp->peer_asn1);
dpp->peer_asn1 = NULL;
}
dpp->state = DPP_STATE_NOTHING;
dpp->new_freq = 0;
dpp->frame_retry = 0;
@ -193,6 +218,7 @@ static void dpp_reset(struct dpp_sm *dpp)
explicit_bzero(dpp->i_nonce, dpp->nonce_len);
explicit_bzero(dpp->e_nonce, dpp->nonce_len);
explicit_bzero(dpp->ke, dpp->key_len);
explicit_bzero(dpp->k1, dpp->key_len);
explicit_bzero(dpp->k2, dpp->key_len);
explicit_bzero(dpp->auth_tag, dpp->key_len);
@ -1268,21 +1294,69 @@ static void dpp_presence_announce(struct dpp_sm *dpp)
dpp_send_frame(dpp, iov, 2, dpp->current_freq);
}
static void dpp_send_authenticate_request(struct dpp_sm *dpp)
{
uint8_t hdr[32];
uint8_t attrs[256];
uint8_t *ptr = attrs;
uint64_t i_proto_key[L_ECC_MAX_DIGITS * 2];
uint8_t version = 2;
struct iovec iov[2];
struct station *station = station_find(netdev_get_ifindex(dpp->netdev));
struct scan_bss *bss = station_get_connected_bss(station);
l_ecc_point_get_data(dpp->own_proto_public, i_proto_key,
sizeof(i_proto_key));
iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev),
dpp->auth_addr,
DPP_FRAME_AUTHENTICATION_REQUEST, hdr);
iov[0].iov_base = hdr;
ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
dpp->peer_boot_hash, 32);
ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH,
dpp->own_boot_hash, 32);
ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_PROTOCOL_KEY,
i_proto_key, dpp->key_len * 2);
ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1);
if (dpp->current_freq != bss->frequency) {
uint8_t pair[2] = { 81,
band_freq_to_channel(bss->frequency, NULL) };
ptr += dpp_append_attr(ptr, DPP_ATTR_CHANNEL, pair, 2);
}
ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, ptr - attrs,
ptr, sizeof(attrs), dpp->k1, dpp->key_len, 2,
DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce,
DPP_ATTR_INITIATOR_CAPABILITIES, 1, &dpp->role);
iov[1].iov_base = attrs;
iov[1].iov_len = ptr - attrs;
dpp_send_frame(dpp, iov, 2, dpp->current_freq);
}
static void dpp_roc_started(void *user_data)
{
struct dpp_sm *dpp = user_data;
/*
* - If a configurator, nothing to do but wait for a request
* (unless multicast frame registration is unsupported in which case
* send an authenticate request now)
* - If in the presence state continue sending announcements.
* - If authenticating, and this is a result of a channel switch send
* the authenticate response now.
*/
if (dpp->role == DPP_CAPABILITY_CONFIGURATOR)
return;
switch (dpp->state) {
case DPP_STATE_PRESENCE:
if (dpp->role == DPP_CAPABILITY_CONFIGURATOR)
return;
if (dpp->pending) {
struct l_dbus_message *reply =
l_dbus_message_new_method_return(dpp->pending);
@ -1295,6 +1369,19 @@ static void dpp_roc_started(void *user_data)
dpp_presence_announce(dpp);
break;
case DPP_STATE_AUTHENTICATING:
/*
* No multicast frame registration support, jump right into
* sending auth frames. This diverges from the 2.0 spec, but in
* reality the the main path nearly all drivers will hit.
*/
if (dpp->role == DPP_CAPABILITY_CONFIGURATOR) {
if (dpp->mcast_support)
return;
dpp_send_authenticate_request(dpp);
return;
}
if (dpp->new_freq) {
dpp->current_freq = dpp->new_freq;
dpp->new_freq = 0;
@ -1552,9 +1639,6 @@ static void authenticate_request(struct dpp_sm *dpp, const uint8_t *from,
/* Derive keys k2, ke, and R-Auth for authentication response */
l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private,
&dpp->own_proto_public);
n = dpp_derive_k2(dpp->peer_proto_public, dpp->proto_private, dpp->k2);
if (!n)
goto auth_request_failed;
@ -1585,6 +1669,294 @@ auth_request_failed:
dpp_free_auth_data(dpp);
}
static void dpp_send_authenticate_confirm(struct dpp_sm *dpp)
{
uint8_t hdr[32];
struct iovec iov[2];
uint8_t attrs[256];
uint8_t *ptr = attrs;
uint8_t zero = 0;
iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev),
dpp->auth_addr,
DPP_FRAME_AUTHENTICATION_CONFIRM, hdr);
iov[0].iov_base = hdr;
ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &zero, 1);
ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH,
dpp->peer_boot_hash, 32);
ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, ptr - attrs, ptr,
sizeof(attrs), dpp->ke, dpp->key_len, 1,
DPP_ATTR_INITIATOR_AUTH_TAG, dpp->key_len,
dpp->auth_tag);
iov[1].iov_base = attrs;
iov[1].iov_len = ptr - attrs;
dpp_send_frame(dpp, iov, 2, dpp->current_freq);
}
static void authenticate_response(struct dpp_sm *dpp, const uint8_t *from,
const uint8_t *body, size_t body_len)
{
struct dpp_attr_iter iter;
enum dpp_attribute_type type;
size_t len;
const uint8_t *data;
int status = -1;
const void *r_boot_hash = NULL;
const void *r_proto = NULL;
size_t r_proto_len = 0;
const void *wrapped = NULL;
size_t wrapped_len;
_auto_(l_free) uint8_t *unwrapped1 = NULL;
_auto_(l_free) uint8_t *unwrapped2 = NULL;
const void *r_nonce = NULL;
const void *i_nonce = NULL;
const void *r_auth = NULL;
_auto_(l_ecc_point_free) struct l_ecc_point *r_proto_key = NULL;
_auto_(l_ecc_scalar_free) struct l_ecc_scalar *n = NULL;
const void *ad0 = body + 2;
const void *ad1 = body + 8;
uint64_t r_auth_derived[L_ECC_MAX_DIGITS];
l_debug("Authenticate response");
if (dpp->state != DPP_STATE_AUTHENTICATING)
return;
if (dpp->role != DPP_CAPABILITY_CONFIGURATOR)
return;
if (!dpp->freqs)
return;
if (memcmp(from, dpp->auth_addr, 6))
return;
dpp_attr_iter_init(&iter, body + 8, body_len - 8);
while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
switch (type) {
case DPP_ATTR_STATUS:
if (len != 1)
return;
status = l_get_u8(data);
break;
case DPP_ATTR_RESPONDER_BOOT_KEY_HASH:
r_boot_hash = data;
break;
case DPP_ATTR_RESPONDER_PROTOCOL_KEY:
r_proto = data;
r_proto_len = len;
break;
case DPP_ATTR_PROTOCOL_VERSION:
if (len != 1 || l_get_u8(data) != 2)
return;
break;
case DPP_ATTR_WRAPPED_DATA:
wrapped = data;
wrapped_len = len;
break;
default:
break;
}
}
if (status != DPP_STATUS_OK || !r_boot_hash || !r_proto ) {
l_debug("Auth response bad status or missing attributes");
return;
}
r_proto_key = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL,
r_proto, r_proto_len);
if (!r_proto_key) {
l_debug("Peers protocol key was invalid");
return;
}
n = dpp_derive_k2(r_proto_key, dpp->proto_private, dpp->k2);
unwrapped1 = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1, dpp->k2,
dpp->key_len, wrapped, wrapped_len,
&wrapped_len);
if (!unwrapped1) {
l_debug("Failed to unwrap primary data");
return;
}
wrapped = NULL;
dpp_attr_iter_init(&iter, unwrapped1, wrapped_len);
while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
switch (type) {
case DPP_ATTR_RESPONDER_NONCE:
if (len != dpp->nonce_len)
return;
r_nonce = data;
break;
case DPP_ATTR_INITIATOR_NONCE:
if (len != dpp->nonce_len)
return;
i_nonce = data;
break;
case DPP_ATTR_RESPONDER_CAPABILITIES:
break;
case DPP_ATTR_WRAPPED_DATA:
wrapped = data;
wrapped_len = len;
break;
default:
break;
}
}
if (!r_nonce || !i_nonce || !wrapped) {
l_debug("Wrapped data missing attributes");
return;
}
if (!dpp_derive_ke(i_nonce, r_nonce, dpp->m, n, dpp->ke)) {
l_debug("Failed to derive ke");
return;
}
unwrapped2 = dpp_unwrap_attr(NULL, 0, NULL, 0, dpp->ke, dpp->key_len,
wrapped, wrapped_len, &wrapped_len);
if (!unwrapped2) {
l_debug("Failed to unwrap secondary data");
return;
}
dpp_attr_iter_init(&iter, unwrapped2, wrapped_len);
while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
switch (type) {
case DPP_ATTR_RESPONDER_AUTH_TAG:
if (len != dpp->key_len)
return;
r_auth = data;
break;
default:
break;
}
}
if (!r_auth) {
l_debug("R-Auth was not in secondary wrapped data");
return;
}
if (!dpp_derive_r_auth(i_nonce, r_nonce, dpp->nonce_len,
dpp->own_proto_public, r_proto_key,
dpp->peer_boot_public, r_auth_derived)) {
l_debug("Failed to derive r_auth");
return;
}
if (memcmp(r_auth, r_auth_derived, dpp->key_len)) {
l_debug("R-Auth did not verify");
return;
}
if (!dpp_derive_i_auth(r_nonce, i_nonce, dpp->nonce_len,
r_proto_key, dpp->own_proto_public,
dpp->peer_boot_public, dpp->auth_tag)) {
l_debug("Could not derive I-Auth");
return;
}
dpp->current_freq = dpp->new_freq;
dpp_send_authenticate_confirm(dpp);
}
static void dpp_handle_presence_announcement(struct dpp_sm *dpp,
const uint8_t *from,
const uint8_t *body,
size_t body_len)
{
struct dpp_attr_iter iter;
enum dpp_attribute_type type;
size_t len;
const uint8_t *data;
const void *r_boot = NULL;
size_t r_boot_len = 0;
uint8_t hash[32];
l_debug("Presence announcement "MAC, MAC_STR(from));
/* Must be a configurator, in an initiator role, in PRESENCE state */
if (dpp->state != DPP_STATE_PRESENCE)
return;
if (dpp->role != DPP_CAPABILITY_CONFIGURATOR)
return;
if (!dpp->freqs)
return;
/*
* The URI may not have contained a MAC address, if this announcement
* verifies set auth_addr then.
*/
if (!l_memeqzero(dpp->auth_addr, 6) &&
memcmp(from, dpp->auth_addr, 6)) {
l_debug("Unexpected source "MAC" expected "MAC, MAC_STR(from),
MAC_STR(dpp->auth_addr));
return;
}
dpp_attr_iter_init(&iter, body + 8, body_len - 8);
while (dpp_attr_iter_next(&iter, &type, &len, &data)) {
switch (type) {
case DPP_ATTR_RESPONDER_BOOT_KEY_HASH:
r_boot = data;
r_boot_len = len;
break;
default:
break;
}
}
if (!r_boot || r_boot_len != 32) {
l_debug("No responder boot hash");
return;
}
/* Hash what we have for the peer and check its our enrollee */
dpp_hash(L_CHECKSUM_SHA256, hash, 2, "chirp", strlen("chirp"),
dpp->peer_asn1, dpp->peer_asn1_len);
if (memcmp(hash, r_boot, sizeof(hash))) {
l_debug("Peers boot hash did not match");
return;
}
/*
* This is the peer we expected, save away the address and derive the
* initial keys.
*/
memcpy(dpp->auth_addr, from, 6);
dpp->state = DPP_STATE_AUTHENTICATING;
dpp_send_authenticate_request(dpp);
/*
* Should we wait for an ACK then go offchannel?
*/
if (dpp->current_freq != dpp->new_freq)
dpp_start_offchannel(dpp, dpp->new_freq);
}
static void dpp_handle_frame(struct dpp_sm *dpp,
const struct mmpdu_header *frame,
const void *body, size_t body_len)
@ -1605,6 +1977,9 @@ static void dpp_handle_frame(struct dpp_sm *dpp,
case DPP_FRAME_AUTHENTICATION_REQUEST:
authenticate_request(dpp, frame->address_2, body, body_len);
break;
case DPP_FRAME_AUTHENTICATION_RESPONSE:
authenticate_response(dpp, frame->address_2, body, body_len);
break;
case DPP_FRAME_AUTHENTICATION_CONFIRM:
authenticate_confirm(dpp, frame->address_2, body, body_len);
break;
@ -1612,6 +1987,10 @@ static void dpp_handle_frame(struct dpp_sm *dpp,
dpp_handle_config_result_frame(dpp, frame->address_2,
body, body_len);
break;
case DPP_FRAME_PRESENCE_ANNOUNCEMENT:
dpp_handle_presence_announcement(dpp, frame->address_2,
body, body_len);
break;
default:
l_debug("Unhandled DPP frame %u", *ptr);
break;
@ -1917,6 +2296,9 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
dpp->state = DPP_STATE_PRESENCE;
dpp->role = DPP_CAPABILITY_ENROLLEE;
l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private,
&dpp->own_proto_public);
l_debug("DPP Start Enrollee: %s", dpp->uri);
dpp->pending = l_dbus_message_ref(message);
@ -1933,9 +2315,66 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus,
return NULL;
}
static struct l_dbus_message *dpp_dbus_start_configurator(struct l_dbus *dbus,
/*
* Set up the configurator for an initiator role. The configurator
* will go offchannel to frequencies advertised by the enrollees URI or,
* if no channels are provided, use a default channel list.
*/
static bool dpp_configurator_start_presence(struct dpp_sm *dpp, const char *uri)
{
_auto_(l_free) uint32_t *freqs = NULL;
size_t freqs_len = 0;
struct dpp_uri_info *info;
info = dpp_parse_uri(uri);
if (!info)
return false;
/*
* Very few drivers actually support registration of multicast frames.
* This renders the presence procedure impossible on most drivers.
* But not all is lost. If the URI contains the MAC and channel
* info we an start going through channels sending auth requests which
* is basically DPP 1.0. Otherwise DPP cannot start.
*/
if (!dpp->mcast_support &&
(l_memeqzero(info->mac, 6) || !info->freqs)) {
l_error("No multicast registration support, URI must contain "
"MAC and channel information");
dpp_free_uri_info(info);
return false;
}
if (!l_memeqzero(info->mac, 6))
memcpy(dpp->auth_addr, info->mac, 6);
if (info->freqs)
freqs = scan_freq_set_to_fixed_array(info->freqs, &freqs_len);
dpp->peer_boot_public = l_ecc_point_clone(info->boot_public);
dpp->peer_asn1 = dpp_point_to_asn1(info->boot_public,
&dpp->peer_asn1_len);
dpp_free_uri_info(info);
if (!dpp->peer_asn1) {
l_debug("Peer boot key did not convert to asn1");
return false;
}
dpp_hash(L_CHECKSUM_SHA256, dpp->peer_boot_hash, 1, dpp->peer_asn1,
dpp->peer_asn1_len);
dpp_start_presence(dpp, freqs, freqs_len);
return true;
}
static struct l_dbus_message *dpp_start_configurator_common(
struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
void *user_data,
bool responder)
{
struct dpp_sm *dpp = user_data;
struct l_dbus_message *reply;
@ -1944,6 +2383,7 @@ static struct l_dbus_message *dpp_dbus_start_configurator(struct l_dbus *dbus,
struct network *network;
struct l_settings *settings;
struct handshake_state *hs = netdev_get_handshake(dpp->netdev);
const char *uri;
/*
* For now limit the configurator to only configuring enrollees to the
@ -1967,13 +2407,33 @@ static struct l_dbus_message *dpp_dbus_start_configurator(struct l_dbus *dbus,
if (dpp->state != DPP_STATE_NOTHING)
return dbus_error_busy(message);
l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private,
&dpp->own_proto_public);
dpp->state = DPP_STATE_PRESENCE;
if (!responder) {
if (!l_dbus_message_get_arguments(message, "s", &uri))
return dbus_error_invalid_args(message);
if (!dpp_configurator_start_presence(dpp, uri))
return dbus_error_invalid_args(message);
/* Since we have the peer's URI generate the keys now */
l_getrandom(dpp->i_nonce, dpp->nonce_len);
dpp->m = dpp_derive_k1(dpp->peer_boot_public,
dpp->proto_private, dpp->k1);
if (!dpp->mcast_support)
dpp->state = DPP_STATE_AUTHENTICATING;
} else
dpp->current_freq = bss->frequency;
dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2,
netdev_get_address(dpp->netdev),
&bss->frequency, 1, NULL, NULL);
dpp->state = DPP_STATE_PRESENCE;
dpp->role = DPP_CAPABILITY_CONFIGURATOR;
dpp->current_freq = bss->frequency;
dpp->config = dpp_configuration_new(settings,
network_get_ssid(network),
hs->akm_suite);
@ -1989,6 +2449,20 @@ static struct l_dbus_message *dpp_dbus_start_configurator(struct l_dbus *dbus,
return reply;
}
static struct l_dbus_message *dpp_dbus_start_configurator(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
return dpp_start_configurator_common(dbus, message, user_data, true);
}
static struct l_dbus_message *dpp_dbus_configure_enrollee(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
{
return dpp_start_configurator_common(dbus, message, user_data, false);
}
static struct l_dbus_message *dpp_dbus_stop(struct l_dbus *dbus,
struct l_dbus_message *message,
void *user_data)
@ -2006,6 +2480,8 @@ static void dpp_setup_interface(struct l_dbus_interface *interface)
dpp_dbus_start_enrollee, "s", "", "uri");
l_dbus_interface_method(interface, "StartConfigurator", 0,
dpp_dbus_start_configurator, "s", "", "uri");
l_dbus_interface_method(interface, "ConfigureEnrollee", 0,
dpp_dbus_configure_enrollee, "", "s", "uri");
l_dbus_interface_method(interface, "Stop", 0,
dpp_dbus_stop, "", "");
}