From patchwork Wed Nov 8 17:21:46 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13450345 Received: from mail-vk1-f179.google.com (mail-vk1-f179.google.com [209.85.221.179]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 50C70328BC for ; Wed, 8 Nov 2023 17:22:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="gJ9fu91K" Received: by mail-vk1-f179.google.com with SMTP id 71dfb90a1353d-4ac023c8f82so2437648e0c.1 for ; Wed, 08 Nov 2023 09:22:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1699464120; x=1700068920; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=baBHlgeWoEeHHkzdXBsF7vv7Ib3zJzqzJRDqOKc/rbE=; b=gJ9fu91KfZNSQdZ2cCQRSGBjxiRZWewihWSGYG4JiNSEDYTDExc1f8zs+JguzZ0ILo VUtxr7hJGWAUOEy+5EdIPz8Jr5/KFqLf7I+4rkSzBb9ZDWWtGG7f//8LL6N+z8Hq2pQx Ttql8M3JxYaulcqt6ckfZqzPbTbHD1tGP46U+ZAJT/2/34p46sm4/t9W1spZKj7RlMo6 DyE1AnoaJysn8LIzFRmseQjO+b4zOoM2ZGz+CykqbwkCdPcYkE5Fuoo8fQItWH7WMb0Q An5L9K89DugrH6VgNPxpWjrsnAB78XvkPtXQNnQA3HXe5IPHyyg0D+Al00AzJoKT1Bep tG7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699464120; x=1700068920; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=baBHlgeWoEeHHkzdXBsF7vv7Ib3zJzqzJRDqOKc/rbE=; b=pif1qCSEHj8vuCkn9rwnyWNuzwYRCkpb0BfgQoRkhxVAtW8T5xuMLMLePvFenZwUr5 7VrBX47qTLMHo8nk6sltYcDg7er4OAx/Kr2bTJQ8bHz0qkyRCsl9Yijxi20A7D8jvkaf uq/9RVTkRpITZVAhOSBEZYKLBJCFk7X/WrVqBuV94srGuw0SwbjMEAqUaQRBc6uFk3gy G3AISy5L5i5CDazN5/wra+alteB+7+lmUYs8BNT4DHDikPPrTkq/4lXioU1AcuDbLaFN YgFR/ILJLiS7RL4cBujH1t4be4rnNN6pw+JY35VTpWQBIVyFnDBVOf82ddYYvlIg6Py5 iROw== X-Gm-Message-State: AOJu0YxSl9rpecwH2caOKhJ3Rg1mzf71jjOUMZAKx3IfxxH/teZhjOIG vC3RJMoz+IT4WJ7/GKz3GLnhpy7YtHo= X-Google-Smtp-Source: AGHT+IFrBzsfviAvwhhiN1j/kitZz+o3hG8UimLN93/4QNg7a80HhDgGgmj/c+X5irAbSeH3fRGs9w== X-Received: by 2002:a1f:a812:0:b0:490:e790:a15b with SMTP id r18-20020a1fa812000000b00490e790a15bmr2514837vke.10.1699464119896; Wed, 08 Nov 2023 09:21:59 -0800 (PST) Received: from LOCLAP699.rst-02.locus (50-78-19-50-static.hfc.comcastbusiness.net. [50.78.19.50]) by smtp.gmail.com with ESMTPSA id qb1-20020ad44701000000b0065d03eae7a0sm1277003qvb.82.2023.11.08.09.21.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Nov 2023 09:21:59 -0800 (PST) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH v5 01/10] dpp-util: fix typo, 'REQUST' Date: Wed, 8 Nov 2023 09:21:46 -0800 Message-Id: <20231108172155.2129509-2-prestwoj@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231108172155.2129509-1-prestwoj@gmail.com> References: <20231108172155.2129509-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- src/dpp-util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dpp-util.h b/src/dpp-util.h index 5f9a2ff4..053caf8c 100644 --- a/src/dpp-util.h +++ b/src/dpp-util.h @@ -40,7 +40,7 @@ enum dpp_frame_type { /* 3 - 4 reserved */ DPP_FRAME_PEER_DISCOVERY_REQUEST = 5, DPP_FRAME_PEER_DISCOVERY_RESPONSE = 6, - DPP_FRAME_PKEX_VERSION1_XCHG_REQUST = 7, + DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST = 7, DPP_FRAME_PKEX_XCHG_RESPONSE = 8, DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST = 9, DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE = 10, From patchwork Wed Nov 8 17:21:47 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13450346 Received: from mail-yb1-f177.google.com (mail-yb1-f177.google.com [209.85.219.177]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2434F31A87 for ; Wed, 8 Nov 2023 17:22:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fqJWWw5L" Received: by mail-yb1-f177.google.com with SMTP id 3f1490d57ef6-d9caf5cc948so7429122276.0 for ; Wed, 08 Nov 2023 09:22:02 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1699464121; x=1700068921; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=HaCYpNp2WNuykiGKrkz90qJEIbZBIenbERXR6ftYk7I=; b=fqJWWw5LcHooAjYMRYFq/twlL0VrHau7H6Vtht/9YCblUoBqn+/Au0afhQv/OlmKnj C1mZc7GXBBITCp1GPDtOD3bVo8cRXewjKZnXOdjaKuitp+87TNob1D+73QRCsz6j0Gu4 iz5c2B8uKQul3hymXSp3YRJMwEIU3EeERraXcGMLx0ew4bv+C/2RVj2unuZwXZ7zLg3j UHS2YY7YdRL469wnnYgzoLIfvGXUPHeuNty0VU7l/Gzab8AvX3aXKKHAAJYYkTUQ4P02 2BHPX/qVXtQ/0W4U0PeA+4cUYeSLFJN6oq0EQ78uUVeHsELIEGKZeZBqdb+4FTQwllqF Cluw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699464121; x=1700068921; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=HaCYpNp2WNuykiGKrkz90qJEIbZBIenbERXR6ftYk7I=; b=FkUTriEE7qBtkkXq+Waq5uVZLLg4PoR5V0CoM0RieOetUuPq+02nawISDXi5Lr1gUA 7CyS1mSRxOZcS50klp2FAb98L4SvQFwjwFMU647BNsFP68I7FDDWm2J1IM+765Iq9xD6 GWovmSePECRqFHqiurQyxER9PGwW6tXBhD4C9cS195wnJdLu3sOsIVBQD6mMnXhGJMIz AThybMbXeWnFrEDcbTiQ1faTSQQ42rT8EsGvoJHzptM/1WEGcFrI7qL41gjltegm80hH YAeQUkQuyXPAb/xLqKN3wS94c7r3qYdX+tqtmE7iMkgEM7Peq+AkefuXj311gXgT2ZH6 RCsg== X-Gm-Message-State: AOJu0YxWg3XAJkcq381hA3NvQfeUWjwSWAh/wWkltf2JgSYOxPxBrsdD 7m4KiC9wIrBiLIhjotD1cna+DieuibA= X-Google-Smtp-Source: AGHT+IGNt4foHc3zd8t0WmkpLvSvgcuBTD304ikAnGJQKaGAIGdpx/nwO77Elk1NMeol098jo4GE7A== X-Received: by 2002:a25:2f8e:0:b0:d99:5613:213d with SMTP id v136-20020a252f8e000000b00d995613213dmr2078972ybv.61.1699464121009; Wed, 08 Nov 2023 09:22:01 -0800 (PST) Received: from LOCLAP699.rst-02.locus (50-78-19-50-static.hfc.comcastbusiness.net. [50.78.19.50]) by smtp.gmail.com with ESMTPSA id qb1-20020ad44701000000b0065d03eae7a0sm1277003qvb.82.2023.11.08.09.22.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Nov 2023 09:22:00 -0800 (PST) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH v5 02/10] dpp: introduce dpp_interface type, prep for PKEX Date: Wed, 8 Nov 2023 09:21:47 -0800 Message-Id: <20231108172155.2129509-3-prestwoj@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231108172155.2129509-1-prestwoj@gmail.com> References: <20231108172155.2129509-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 PKEX and DPP will share the same state machine since the DPP protocol follows PKEX. This does pose an issue with the DBus interfaces because we don't want DPP initiated by the SharedCode interface to start setting properties on the DeviceProvisioning interface. To handle this a dpp_interface enum is being introduced which binds the dpp_sm object to a particular interface, for the life of the protocol run. Once the protocol finishes the dpp_sm can be unbound allowing either interface to use it again later. --- src/dpp.c | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/src/dpp.c b/src/dpp.c index b0a79361..7a7301e2 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -79,6 +79,12 @@ enum dpp_capability { DPP_CAPABILITY_CONFIGURATOR = 0x02, }; +enum dpp_interface { + DPP_INTERFACE_UNBOUND, + DPP_INTERFACE_DPP, + DPP_INTERFACE_PKEX, +}; + struct dpp_sm { struct netdev *netdev; char *uri; @@ -100,6 +106,7 @@ struct dpp_sm { struct l_ecc_point *peer_boot_public; enum dpp_state state; + enum dpp_interface interface; /* * List of frequencies to jump between. The presence of this list is @@ -159,7 +166,8 @@ static bool dpp_get_started(struct l_dbus *dbus, void *user_data) { struct dpp_sm *dpp = user_data; - bool started = (dpp->state != DPP_STATE_NOTHING); + bool started = (dpp->state != DPP_STATE_NOTHING && + dpp->interface == DPP_INTERFACE_DPP); l_dbus_message_builder_append_basic(builder, 'b', &started); @@ -174,7 +182,8 @@ static bool dpp_get_role(struct l_dbus *dbus, struct dpp_sm *dpp = user_data; const char *role; - if (dpp->state == DPP_STATE_NOTHING) + if (dpp->state == DPP_STATE_NOTHING || + dpp->interface != DPP_INTERFACE_DPP) return false; switch (dpp->role) { @@ -199,7 +208,8 @@ static bool dpp_get_uri(struct l_dbus *dbus, { struct dpp_sm *dpp = user_data; - if (dpp->state == DPP_STATE_NOTHING) + if (dpp->state == DPP_STATE_NOTHING || + dpp->interface != DPP_INTERFACE_DPP) return false; l_dbus_message_builder_append_basic(builder, 's', dpp->uri); @@ -210,12 +220,18 @@ static void dpp_property_changed_notify(struct dpp_sm *dpp) { const char *path = netdev_get_path(dpp->netdev); - l_dbus_property_changed(dbus_get_bus(), path, IWD_DPP_INTERFACE, - "Started"); - l_dbus_property_changed(dbus_get_bus(), path, IWD_DPP_INTERFACE, - "Role"); - l_dbus_property_changed(dbus_get_bus(), path, IWD_DPP_INTERFACE, - "URI"); + switch (dpp->interface) { + case DPP_INTERFACE_DPP: + l_dbus_property_changed(dbus_get_bus(), path, IWD_DPP_INTERFACE, + "Started"); + l_dbus_property_changed(dbus_get_bus(), path, IWD_DPP_INTERFACE, + "Role"); + l_dbus_property_changed(dbus_get_bus(), path, IWD_DPP_INTERFACE, + "URI"); + break; + default: + break; + } } static void *dpp_serialize_iovec(struct iovec *iov, size_t iov_len, @@ -333,6 +349,8 @@ static void dpp_reset(struct dpp_sm *dpp) dpp_free_auth_data(dpp); dpp_property_changed_notify(dpp); + + dpp->interface = DPP_INTERFACE_UNBOUND; } static void dpp_free(struct dpp_sm *dpp) @@ -2416,6 +2434,7 @@ static void dpp_create(struct netdev *netdev) dpp->netdev = netdev; dpp->state = DPP_STATE_NOTHING; + dpp->interface = DPP_INTERFACE_UNBOUND; dpp->wdev_id = wdev_id; dpp->curve = l_ecc_curve_from_ike_group(19); dpp->key_len = l_ecc_curve_get_scalar_bytes(dpp->curve); @@ -2535,7 +2554,8 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus, uint32_t freq = band_channel_to_freq(6, BAND_FREQ_2_4_GHZ); struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); - if (dpp->state != DPP_STATE_NOTHING) + if (dpp->state != DPP_STATE_NOTHING || + dpp->interface != DPP_INTERFACE_UNBOUND) return dbus_error_busy(message); /* @@ -2554,6 +2574,7 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus, dpp->state = DPP_STATE_PRESENCE; dpp->role = DPP_CAPABILITY_ENROLLEE; + dpp->interface = DPP_INTERFACE_DPP; l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private, &dpp->own_proto_public); @@ -2663,7 +2684,8 @@ static struct l_dbus_message *dpp_start_configurator_common( if (network_get_security(network) != SECURITY_PSK) return dbus_error_not_supported(message); - if (dpp->state != DPP_STATE_NOTHING) + if (dpp->state != DPP_STATE_NOTHING || + dpp->interface != DPP_INTERFACE_UNBOUND) return dbus_error_busy(message); l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private, @@ -2695,6 +2717,7 @@ static struct l_dbus_message *dpp_start_configurator_common( netdev_get_address(dpp->netdev), &bss->frequency, 1, NULL, NULL); dpp->role = DPP_CAPABILITY_CONFIGURATOR; + dpp->interface = DPP_INTERFACE_DPP; dpp->config = dpp_configuration_new(settings, network_get_ssid(network), hs->akm_suite); @@ -2730,6 +2753,9 @@ static struct l_dbus_message *dpp_dbus_stop(struct l_dbus *dbus, { struct dpp_sm *dpp = user_data; + if (dpp->interface != DPP_INTERFACE_DPP) + return dbus_error_not_found(message); + dpp_reset(dpp); return l_dbus_message_new_method_return(message); From patchwork Wed Nov 8 17:21:48 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13450348 Received: from mail-qv1-f48.google.com (mail-qv1-f48.google.com [209.85.219.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B728331A8F for ; Wed, 8 Nov 2023 17:22:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="i/wDvLnN" Received: by mail-qv1-f48.google.com with SMTP id 6a1803df08f44-66d0169cf43so46370476d6.3 for ; Wed, 08 Nov 2023 09:22:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1699464122; x=1700068922; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ocbSyIuDMHivK+9rbtk74SkXgk6QhMWhxVluwON7zFs=; b=i/wDvLnNupyVtYwihOCjildTOPS7Z3G8DS8KaRph3QSLQ9hRVReNS2oF++bC+p8GTF jJ8KD1KLc0PAJuTwdaq6txBlKng8nXaTKLtdEVpxMaGPKKM6ieiwtWe/QZJaV3KMQlr8 LXPqTFMU+PnDrrM0e3jBs5G4UG05Enssd9Zmbk1tmkJGHtgB8pZNJznK5yjwfOexbyx/ DuTwfKG0SLtZVsAjU3+gxdFCqnLym+gx8wc9AXF6Vc77Yd8UVIioEe3q0VS9zxY8H3rG KM2XOSvNpSRXjrtkhx94YYtLkedfETBcDH2GO1Vakb2qvAQzyvxFUXNY4EYVPZv1adPp 4FbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699464122; x=1700068922; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ocbSyIuDMHivK+9rbtk74SkXgk6QhMWhxVluwON7zFs=; b=Lr6zcVwJgRUm7MXd/mHSpVs3rwwhbMxpMJCCZU8UteMiNDj+eHxk+1xedZ7CDvZAWJ ZAOFZVEOwjeuBUokrTeApxeIcUEK9lj5kdOrbHa9Wcu+78caUKgJVpNoWE4DAlRJX7sz z2ZJKMVtrNYDtG1oPdYzYcL2je7bCcfdisIEh4fRUCnqWq8k8bO8tuBxD26Kczn3SwrM iGGnJ09s8e6VoXtqTNaTBKZDJKmtmGgk01qtVS29KaGO84yX2a04CCJQJuDcPZHvLNB4 rsZqcuXwTkkUi17gOP2YndAczJKG8Pn08sD2kfCJkoTD3Eu2gLzVmcasM81DQIEIzKJt BtEQ== X-Gm-Message-State: AOJu0YypvGl5+8tqzIeIWn+j0c2vasiJ1VioQt7QEwYOBicWDp2/0ziO PnN/uSymd3sCiYNnisqMNcQXowkynaE= X-Google-Smtp-Source: AGHT+IHo9UK+e91Zk3QOMQNu5a8lShRu7DqzUMVDmg6yMRT0IUfWaszBTfFtnW/QSlFiLttsfGlflg== X-Received: by 2002:a05:6214:2386:b0:658:3680:5185 with SMTP id fw6-20020a056214238600b0065836805185mr2621826qvb.36.1699464122284; Wed, 08 Nov 2023 09:22:02 -0800 (PST) Received: from LOCLAP699.rst-02.locus (50-78-19-50-static.hfc.comcastbusiness.net. [50.78.19.50]) by smtp.gmail.com with ESMTPSA id qb1-20020ad44701000000b0065d03eae7a0sm1277003qvb.82.2023.11.08.09.22.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Nov 2023 09:22:01 -0800 (PST) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH v5 03/10] auto-t: fix testDPP after Stop() change Date: Wed, 8 Nov 2023 09:21:48 -0800 Message-Id: <20231108172155.2129509-4-prestwoj@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231108172155.2129509-1-prestwoj@gmail.com> References: <20231108172155.2129509-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Stop() will now return NotFound if DPP is not running. This causes the DPP test to fail since it calls this regardless if the protocol already stopped. Ignore this exception since tests end in various states, some stopped and some not. --- autotests/testDPP/connection_test.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/autotests/testDPP/connection_test.py b/autotests/testDPP/connection_test.py index 0cab5ff1..f72a412d 100644 --- a/autotests/testDPP/connection_test.py +++ b/autotests/testDPP/connection_test.py @@ -148,8 +148,13 @@ class Test(unittest.TestCase): self.rule0.drop = True def tearDown(self): - self.device.disconnect() - self.device.dpp_stop() + # Tests end in various states, don't fail when tearing down. + try: + self.device.disconnect() + self.device.dpp_stop() + except: + pass + self.wpas.dpp_configurator_remove() self.wpas.clean_up() From patchwork Wed Nov 8 17:21:49 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13450347 Received: from mail-qv1-f45.google.com (mail-qv1-f45.google.com [209.85.219.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B941B31A93 for ; Wed, 8 Nov 2023 17:22:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="bdCicUvf" Received: by mail-qv1-f45.google.com with SMTP id 6a1803df08f44-66d134a019cso48730036d6.3 for ; Wed, 08 Nov 2023 09:22:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1699464123; x=1700068923; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=b+D6jqKPRoAnLv15gBofvXqWmRGAKDWcecg64WMooVg=; b=bdCicUvfYWnCziccA8pM89Drz6q+R2XR3FU4wCEMZA1JLy8wGtXUrJECqbuvqrg21T +BbU38YnN7LCNsGK8/KVlSwoVVrcq6NzGmpZg9UgNPKnydGqWaQS3w3aXpFq33p5626J G3daXcNHibN0M0BitpDtKnTCNTW59+5S4mUXGkjP2xYAjMBmFkMzrS7suM4rEIkiON6x ScgECt2dJwcKE34jueVXoBCBN45aVVyuar/OTpiF0SvIjSCvQPxWt4BkxTu3Tlr9kGlK r/1W8BYv3Gq1WfOrYDLcbT4LICtlC3tU540UEUSAMOiLP5NIDFNB0AKCt8rPZrVw0tNn v95w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699464123; x=1700068923; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=b+D6jqKPRoAnLv15gBofvXqWmRGAKDWcecg64WMooVg=; b=MeXjBDVnP9AwtSRcMDPW9GZI2uyyG3fMuoce113T4YgzQcqQZAU6u9QX+jJ7iElz0M 8+4eECl5m6kuxIuZ5Pcxb8Uz+YSP+0uFmiLWwGeSJ1DQ9Fdh8RwD6bRizda50sfeo4Jj tp0JehP9xbe6t2rKvW46hbO4Sy5BW/iacddCglllhm1jZtkKPK4u7xKHUyDq7EoAabtV mG+EGvKycprUagzB8qBZGFdknTBTnbLvZTWY36MS+JXecTwatPNNLZ06oQYmqgBg0neA 65pvebrpwrcEssJ2NTA9bxCHg4hzlVlzHDRXqgohtqKFcilkxiYNcRnzUKNuDjQfBpD9 v3NA== X-Gm-Message-State: AOJu0YyuNq6kT88IQVPAtviJmBewB0KesgtectpuCRNTBCGE/IHYjM7w FlhZcuVXPFrUAnwCRnLjE9M54UuUaNQ= X-Google-Smtp-Source: AGHT+IH3aTWbtLYGRxEcmBREv+FPez6AfxnYXrLO+/ECC8WutBzrsaep2PLTGZq4OVlLOOsTB+MHPw== X-Received: by 2002:ad4:5d4a:0:b0:675:6ede:f376 with SMTP id jk10-20020ad45d4a000000b006756edef376mr3030642qvb.36.1699464123384; Wed, 08 Nov 2023 09:22:03 -0800 (PST) Received: from LOCLAP699.rst-02.locus (50-78-19-50-static.hfc.comcastbusiness.net. [50.78.19.50]) by smtp.gmail.com with ESMTPSA id qb1-20020ad44701000000b0065d03eae7a0sm1277003qvb.82.2023.11.08.09.22.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Nov 2023 09:22:03 -0800 (PST) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH v5 04/10] doc: document Stop() correctly for both DPP interfaces Date: Wed, 8 Nov 2023 09:21:49 -0800 Message-Id: <20231108172155.2129509-5-prestwoj@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231108172155.2129509-1-prestwoj@gmail.com> References: <20231108172155.2129509-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Stop() was separated more clearly between the two interfaces and will now return NotFound if called on an interface that isn't currently running. --- doc/device-provisioning-api.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/device-provisioning-api.txt b/doc/device-provisioning-api.txt index 2a34d4ae..9994aef5 100644 --- a/doc/device-provisioning-api.txt +++ b/doc/device-provisioning-api.txt @@ -18,11 +18,9 @@ Methods string StartEnrollee() void Stop() - Stop an enrollee. + Stop an enrollee or configurator that is running. - Possible errors: net.connman.iwd.Busy - net.connman.iwd.Failed - net.connman.iwd.InvalidArguments + Possible errors: net.connman.iwd.NotFound string StartConfigurator() @@ -168,6 +166,8 @@ Object path /net/connman/iwd/{phy0,phy1,...}/{1,2,...} Calling Stop() if DPP was started via the DeviceProvisioning interface will not stop it. + Possible errors: net.connman.iwd.NotFound + Properties boolean Started [readonly] True if shared code device provisioning is currently From patchwork Wed Nov 8 17:21:50 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13450349 Received: from mail-qv1-f48.google.com (mail-qv1-f48.google.com [209.85.219.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 16ACB31A87 for ; Wed, 8 Nov 2023 17:22:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="LBqw68V+" Received: by mail-qv1-f48.google.com with SMTP id 6a1803df08f44-6754b4091b6so44096536d6.3 for ; Wed, 08 Nov 2023 09:22:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1699464125; x=1700068925; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=9fS2tDIIChIsgrCOlKIwKzy5GgPPGt8mFpfl8FeTA0s=; b=LBqw68V+/6EltCVMVWsvAqS+46DE7EMLkR7VqXQZCzxJgLi1aCSKYC4sTUtJBrzVfg G1iPdvQdMfOzahUNTCwo3uCHZbH/gOxhV2ZQuE+uuqXzn4KIc+ethRrWo+PHXsI+dPG3 BxNH2dXdCPLKhx+LiXxG4reEe9aC62hBVz5JWrezZi5BH85qb0rkM+xYo8bVGnz1uGWA FzeKQjyocvY9pTrdjGcaT6dggzvGw8nxCjLLRqdTZrVakx/yQX3/iUR2/l1/J0rB6PYI utSIe+Hp9DxK3rSj72WI8sRivd8N33SZtJOn/1eGLcksLFVwxSLCQq7WT57UDePnb311 NUqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699464125; x=1700068925; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9fS2tDIIChIsgrCOlKIwKzy5GgPPGt8mFpfl8FeTA0s=; b=DKfi0ZAP5f9juzCe/NxMtmqumu0PRRwZsAnvMnfv0h1boq89Tv1LC8wQ01XFSQYigO pUECOrTD4doZaGqnX4YyCbZTOPmTxBK3s2rIz3edM0rghY+4k4Ofr3W29yzXhYn366xb JIfmbUyJKxS/3Ybi+BWhSfkSG1++lYi+FabS5njdf2LnAzHD/K+0/1Ot/F26aqljzlEG VpZb5j5vHkhZTWsgygmO8eTJhnFBIHqDD631Wpyc+3WEF8FAWNyMmmDYVGfgn8ZzBh0I BP0onzFe51m8DyNGA37LIXjdQFsf1zI60SPHzxR8nE3sMhLhA7VmQZMyiTrMekP/MBD8 XDHw== X-Gm-Message-State: AOJu0Yy5r4x7qGBSBjgNmhevIFyTTDqLi+sEdnH1A5M3p0LZ7RHo7nUe 3T4+R2kPdKAbRi8Mxs84sQeppLi5JiU= X-Google-Smtp-Source: AGHT+IER7Fk1pg558u2Sg59IuM0gzsFuiU6SBQKMfLXZYsLseIzUzlZbjy+JKW4OtR2JYYh9IcQ1sg== X-Received: by 2002:a05:6214:d4a:b0:66d:1d3f:17d7 with SMTP id 10-20020a0562140d4a00b0066d1d3f17d7mr2857973qvr.8.1699464124600; Wed, 08 Nov 2023 09:22:04 -0800 (PST) Received: from LOCLAP699.rst-02.locus (50-78-19-50-static.hfc.comcastbusiness.net. [50.78.19.50]) by smtp.gmail.com with ESMTPSA id qb1-20020ad44701000000b0065d03eae7a0sm1277003qvb.82.2023.11.08.09.22.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Nov 2023 09:22:04 -0800 (PST) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH v5 05/10] dpp: initial version of PKEX enrollee support Date: Wed, 8 Nov 2023 09:21:50 -0800 Message-Id: <20231108172155.2129509-6-prestwoj@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231108172155.2129509-1-prestwoj@gmail.com> References: <20231108172155.2129509-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This is the initial support for PKEX enrollees acting as the initiator. A PKEX initiator starts the protocol by broadcasting the PKEX exchange request. This request contains a key encrypted with the pre-shared PKEX code. If accepted the peer sends back the exchange response with its own encrypted key. The enrollee decrypts this and performs some crypto/hashing in order to establish an ephemeral key used to encrypt its own boostrapping key. The boostrapping key is encrypted and sent to the peer in the PKEX commit-reveal request. The peer then does the same thing, encrypting its own bootstrapping key and sending to the initiator as the PKEX commit-reveal response. After this, both peers have exchanged their boostrapping keys securely and can begin DPP authentication, then configuration. For now the enrollee will only iterate the default channel list from the Easy Connect spec. Future upates will need to include some way of discovering non-default channel configurators, but the protocol needs to be ironed out first. --- src/dpp.c | 811 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 800 insertions(+), 11 deletions(-) diff --git a/src/dpp.c b/src/dpp.c index 7a7301e2..ee2cd903 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -57,6 +57,7 @@ #define DPP_FRAME_MAX_RETRIES 5 #define DPP_FRAME_RETRY_TIMEOUT 1 #define DPP_AUTH_PROTO_TIMEOUT 10 +#define DPP_PKEX_PROTO_TIMEOUT 120 static uint32_t netdev_watch; static struct l_genl_family *nl80211; @@ -70,6 +71,8 @@ static uint8_t dpp_prefix[] = { 0x04, 0x09, 0x50, 0x6f, 0x9a, 0x1a, 0x01 }; enum dpp_state { DPP_STATE_NOTHING, DPP_STATE_PRESENCE, + DPP_STATE_PKEX_EXCHANGE, + DPP_STATE_PKEX_COMMIT_REVEAL, DPP_STATE_AUTHENTICATING, DPP_STATE_CONFIGURING, }; @@ -89,6 +92,7 @@ struct dpp_sm { struct netdev *netdev; char *uri; uint8_t role; + int refcount; uint64_t wdev_id; @@ -154,12 +158,73 @@ struct dpp_sm { struct l_dbus_message *pending; + /* PKEX-specific values */ + char *pkex_id; + char *pkex_key; + uint8_t pkex_version; + struct l_ecc_point *pkex_m; + /* Ephemeral key Y' or X' for enrollee or configurator */ + struct l_ecc_point *y_or_x; + /* Ephemeral key pair y/Y or x/X */ + struct l_ecc_point *pkex_public; + struct l_ecc_scalar *pkex_private; + uint8_t z[L_ECC_SCALAR_MAX_BYTES]; + size_t z_len; + uint8_t u[L_ECC_SCALAR_MAX_BYTES]; + size_t u_len; + bool mcast_support : 1; bool roc_started : 1; bool channel_switch : 1; bool mutual_auth : 1; }; +static const char *dpp_role_to_string(enum dpp_capability role) +{ + switch (role) { + case DPP_CAPABILITY_ENROLLEE: + return "enrollee"; + case DPP_CAPABILITY_CONFIGURATOR: + return "configurator"; + default: + return NULL; + } +} + +static bool dpp_pkex_get_started(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + struct dpp_sm *dpp = user_data; + bool started = (dpp->state != DPP_STATE_NOTHING && + dpp->interface == DPP_INTERFACE_PKEX); + + l_dbus_message_builder_append_basic(builder, 'b', &started); + + return true; +} + +static bool dpp_pkex_get_role(struct l_dbus *dbus, + struct l_dbus_message *message, + struct l_dbus_message_builder *builder, + void *user_data) +{ + struct dpp_sm *dpp = user_data; + const char *role; + + if (dpp->state == DPP_STATE_NOTHING || + dpp->interface != DPP_INTERFACE_PKEX) + return false; + + role = dpp_role_to_string(dpp->role); + if (L_WARN_ON(!role)) + return false; + + l_dbus_message_builder_append_basic(builder, 's', role); + return true; +} + static bool dpp_get_started(struct l_dbus *dbus, struct l_dbus_message *message, struct l_dbus_message_builder *builder, @@ -186,16 +251,9 @@ static bool dpp_get_role(struct l_dbus *dbus, dpp->interface != DPP_INTERFACE_DPP) return false; - switch (dpp->role) { - case DPP_CAPABILITY_ENROLLEE: - role = "enrollee"; - break; - case DPP_CAPABILITY_CONFIGURATOR: - role = "configurator"; - break; - default: + role = dpp_role_to_string(dpp->role); + if (L_WARN_ON(!role)) return false; - } l_dbus_message_builder_append_basic(builder, 's', role); return true; @@ -229,6 +287,12 @@ static void dpp_property_changed_notify(struct dpp_sm *dpp) l_dbus_property_changed(dbus_get_bus(), path, IWD_DPP_INTERFACE, "URI"); break; + case DPP_INTERFACE_PKEX: + l_dbus_property_changed(dbus_get_bus(), path, IWD_DPP_PKEX_INTERFACE, + "Started"); + l_dbus_property_changed(dbus_get_bus(), path, IWD_DPP_PKEX_INTERFACE, + "Role"); + break; default: break; } @@ -258,6 +322,21 @@ static void *dpp_serialize_iovec(struct iovec *iov, size_t iov_len, return ret; } +static void dpp_free_pending_pkex_data(struct dpp_sm *dpp) +{ + if (dpp->pkex_id) { + l_free(dpp->pkex_id); + dpp->pkex_id = NULL; + } + + if (dpp->pkex_key) { + l_free(dpp->pkex_key); + dpp->pkex_key = NULL; + } + + memset(dpp->peer_addr, 0, sizeof(dpp->peer_addr)); +} + static void dpp_free_auth_data(struct dpp_sm *dpp) { if (dpp->own_proto_public) { @@ -284,6 +363,27 @@ static void dpp_free_auth_data(struct dpp_sm *dpp) l_ecc_scalar_free(dpp->m); dpp->m = NULL; } + + if (dpp->pkex_m) { + l_ecc_point_free(dpp->pkex_m); + dpp->pkex_m = NULL; + } + + if (dpp->y_or_x) { + l_ecc_point_free(dpp->y_or_x); + dpp->y_or_x = NULL; + } + + if (dpp->pkex_public) { + l_ecc_point_free(dpp->pkex_public); + dpp->pkex_public = NULL; + } + + if (dpp->pkex_private) { + l_ecc_scalar_free(dpp->pkex_private); + dpp->pkex_private = NULL; + } + } static void dpp_reset(struct dpp_sm *dpp) @@ -337,6 +437,7 @@ static void dpp_reset(struct dpp_sm *dpp) dpp->new_freq = 0; dpp->frame_retry = 0; dpp->frame_cookie = 0; + dpp->pkex_version = 0; explicit_bzero(dpp->r_nonce, dpp->nonce_len); explicit_bzero(dpp->i_nonce, dpp->nonce_len); @@ -345,6 +446,10 @@ static void dpp_reset(struct dpp_sm *dpp) explicit_bzero(dpp->k1, dpp->key_len); explicit_bzero(dpp->k2, dpp->key_len); explicit_bzero(dpp->auth_tag, dpp->key_len); + explicit_bzero(dpp->z, dpp->key_len); + explicit_bzero(dpp->u, dpp->u_len); + + dpp_free_pending_pkex_data(dpp); dpp_free_auth_data(dpp); @@ -1530,6 +1635,71 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp) return true; } +static void dpp_send_pkex_exchange_request(struct dpp_sm *dpp) +{ + uint8_t hdr[32]; + uint8_t attrs[256]; + uint8_t *ptr = attrs; + uint64_t m_data[L_ECC_MAX_DIGITS * 2]; + uint16_t group; + struct iovec iov[2]; + const uint8_t *own_mac = netdev_get_address(dpp->netdev); + + l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group); + + iov[0].iov_len = dpp_build_header(own_mac, broadcast, + DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST, hdr); + iov[0].iov_base = hdr; + + ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, + &dpp->pkex_version, 1); + ptr += dpp_append_attr(ptr, DPP_ATTR_FINITE_CYCLIC_GROUP, + &group, 2); + + if (dpp->pkex_id) + ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER, + dpp->pkex_id, strlen(dpp->pkex_id)); + + l_ecc_point_get_data(dpp->pkex_m, m_data, sizeof(m_data)); + + ptr += dpp_append_attr(ptr, DPP_ATTR_ENCRYPTED_KEY, + m_data, dpp->key_len * 2); + + iov[1].iov_base = attrs; + iov[1].iov_len = ptr - attrs; + + dpp_send_frame(dpp, iov, 2, dpp->current_freq); +} + +static void dpp_send_commit_reveal_request(struct dpp_sm *dpp) +{ + struct iovec iov[2]; + uint8_t hdr[41]; + uint8_t attrs[512]; + uint8_t *ptr = attrs; + uint8_t zero = 0; + uint8_t a_pub[L_ECC_POINT_MAX_BYTES]; + ssize_t a_len; + + a_len = l_ecc_point_get_data(dpp->boot_public, a_pub, sizeof(a_pub)); + + iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev), + dpp->peer_addr, + DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST, + hdr); + iov[0].iov_base = hdr; + + ptr += dpp_append_wrapped_data(hdr + 26, 6, &zero, 1, ptr, + sizeof(attrs), dpp->z, dpp->z_len, 2, + DPP_ATTR_BOOTSTRAPPING_KEY, a_len, a_pub, + DPP_ATTR_INITIATOR_AUTH_TAG, dpp->u_len, dpp->u); + + 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; @@ -1593,6 +1763,16 @@ static void dpp_roc_started(void *user_data) send_authenticate_response(dpp); } + break; + case DPP_STATE_PKEX_EXCHANGE: + if (dpp->role == DPP_CAPABILITY_ENROLLEE) + dpp_send_pkex_exchange_request(dpp); + + break; + case DPP_STATE_PKEX_COMMIT_REVEAL: + if (dpp->role == DPP_CAPABILITY_ENROLLEE) + dpp_send_commit_reveal_request(dpp); + break; default: break; @@ -1621,6 +1801,7 @@ static void dpp_offchannel_timeout(int error, void *user_data) goto protocol_failed; switch (dpp->state) { + case DPP_STATE_PKEX_EXCHANGE: case DPP_STATE_PRESENCE: break; case DPP_STATE_NOTHING: @@ -1628,6 +1809,7 @@ static void dpp_offchannel_timeout(int error, void *user_data) return; case DPP_STATE_AUTHENTICATING: case DPP_STATE_CONFIGURING: + case DPP_STATE_PKEX_COMMIT_REVEAL: goto next_roc; } @@ -2190,6 +2372,382 @@ static void dpp_handle_presence_announcement(struct dpp_sm *dpp, dpp->channel_switch = true; } +static void dpp_pkex_bad_group(struct dpp_sm *dpp, uint16_t group) +{ + uint16_t own_group = l_ecc_curve_get_ike_group(dpp->curve); + + /* + * TODO: The spec allows group negotiation, but it is not yet + * implemented. + */ + if (!group) + return; + /* + * Section 5.6.2 + * "If the Responder's offered group offers less security + * than the Initiator's offered group, then the Initiator should + * ignore this message" + */ + if (group < own_group) { + l_debug("Offered group %u is less secure, ignoring", + group); + return; + } + /* + * Section 5.6.2 + * "If the Responder's offered group offers equivalent or better + * security than the Initiator's offered group, then the + * Initiator may choose to abort its original request and try + * another exchange using the group offered by the Responder" + */ + if (group >= own_group) { + l_debug("Offered group %u is the same or more secure, " + " but group negotiation is not supported", group); + return; + } +} + +static void dpp_pkex_bad_code(struct dpp_sm *dpp) +{ + _auto_(l_ecc_point_free) struct l_ecc_point *qr = NULL; + + qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id, + netdev_get_address(dpp->netdev)); + if (!qr || l_ecc_point_is_infinity(qr)) { + l_debug("Qr computed to zero, new code should be provisioned"); + return; + } + + l_debug("Qr computed successfully but responder indicated otherwise"); +} + +static void dpp_handle_pkex_exchange_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; + const uint8_t *status = NULL; + uint8_t version = 0; + const void *identifier = NULL; + size_t identifier_len = 0; + const void *key = NULL; + size_t key_len = 0; + uint16_t group = 0; + _auto_(l_ecc_point_free) struct l_ecc_point *n = NULL; + _auto_(l_ecc_point_free) struct l_ecc_point *j = NULL; + _auto_(l_ecc_point_free) struct l_ecc_point *qr = NULL; + _auto_(l_ecc_point_free) struct l_ecc_point *k = NULL; + const uint8_t *own_addr = netdev_get_address(dpp->netdev); + + l_debug("PKEX response "MAC, MAC_STR(from)); + + if (dpp->state != DPP_STATE_PKEX_EXCHANGE) + return; + + if (dpp->role != DPP_CAPABILITY_ENROLLEE) + return; + + memcpy(dpp->peer_addr, from, 6); + + 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 = data; + break; + case DPP_ATTR_PROTOCOL_VERSION: + if (len != 1) + return; + + version = l_get_u8(data); + break; + case DPP_ATTR_CODE_IDENTIFIER: + identifier = data; + identifier_len = len; + break; + case DPP_ATTR_ENCRYPTED_KEY: + if (len != dpp->key_len * 2) + return; + + key = data; + key_len = len; + break; + case DPP_ATTR_FINITE_CYCLIC_GROUP: + if (len != 2) + return; + + group = l_get_le16(data); + break; + default: + break; + } + } + + if (!status) { + l_debug("No status attribute, ignoring"); + return; + } + + if (!key) { + l_debug("No encrypted key, ignoring"); + return; + } + + if (*status != DPP_STATUS_OK) + goto handle_status; + + if (dpp->pkex_id) { + if (!identifier || identifier_len != strlen(dpp->pkex_id) || + memcmp(dpp->pkex_id, identifier, + identifier_len)) { + l_debug("mismatch identifier, ignoring"); + return; + } + } + + if (version && version != dpp->pkex_version) { + l_debug("PKEX version does not match, igoring"); + return; + } + + n = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL, + key, key_len); + if (!n) { + l_debug("failed to parse peers encrypted key"); + goto failed; + } + + qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id, + dpp->peer_addr); + if (!qr) + goto failed; + + dpp->y_or_x = l_ecc_point_new(dpp->curve); + + /* Y' = N - Qr */ + l_ecc_point_inverse(qr); + l_ecc_point_add(dpp->y_or_x, n, qr); + + /* + * "The resulting ephemeral key, denoted Y’, is then checked whether it + * is the point-at-infinity. If it is not valid, the protocol ends + * unsuccessfully" + */ + if (l_ecc_point_is_infinity(dpp->y_or_x)) { + l_debug("Y' computed to infinity, failing"); + goto failed; + } + + k = l_ecc_point_new(dpp->curve); + + /* K = Y' * x */ + l_ecc_point_multiply(k, dpp->pkex_private, dpp->y_or_x); + + dpp_derive_z(own_addr, dpp->peer_addr, n, dpp->pkex_m, k, + dpp->pkex_key, dpp->pkex_id, + dpp->z, &dpp->z_len); + + /* J = a * Y' */ + j = l_ecc_point_new(dpp->curve); + + l_ecc_point_multiply(j, dpp->boot_private, dpp->y_or_x); + + if (!dpp_derive_u(j, own_addr, dpp->boot_public, dpp->y_or_x, + dpp->pkex_public, dpp->u, &dpp->u_len)) { + l_debug("failed to compute u"); + goto failed; + } + + /* + * Now that a response was successfully received, start another + * offchannel with more time for the remainder of the protocol. After + * PKEX, authentication will begin which handles the protocol timeout. + * If the remainder of PKEX (commit-reveal exchange) cannot complete in + * this time it will fail. + */ + dpp->dwell = (dpp->max_roc < 2000) ? dpp->max_roc : 2000; + dpp->state = DPP_STATE_PKEX_COMMIT_REVEAL; + + dpp_property_changed_notify(dpp); + + dpp_start_offchannel(dpp, dpp->current_freq); + + return; + +handle_status: + switch (*status) { + case DPP_STATUS_BAD_GROUP: + dpp_pkex_bad_group(dpp, group); + break; + case DPP_STATUS_BAD_CODE: + dpp_pkex_bad_code(dpp); + break; + default: + l_debug("Unhandled status %u", *status); + break; + } + +failed: + dpp_reset(dpp); +} + +static bool dpp_pkex_start_authentication(struct dpp_sm *dpp) +{ + dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2, + netdev_get_address(dpp->netdev), + &dpp->current_freq, 1, NULL, NULL); + + l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private, + &dpp->own_proto_public); + + l_getrandom(dpp->i_nonce, dpp->nonce_len); + + dpp->peer_asn1 = dpp_point_to_asn1(dpp->peer_boot_public, + &dpp->peer_asn1_len); + + dpp->m = dpp_derive_k1(dpp->peer_boot_public, dpp->proto_private, + dpp->k1); + + dpp_hash(L_CHECKSUM_SHA256, dpp->peer_boot_hash, 1, dpp->peer_asn1, + dpp->peer_asn1_len); + + dpp->state = DPP_STATE_AUTHENTICATING; + dpp->mutual_auth = true; + + dpp_property_changed_notify(dpp); + + if (dpp->role == DPP_CAPABILITY_ENROLLEE) { + dpp->new_freq = dpp->current_freq; + + return dpp_send_authenticate_request(dpp); + } + + return true; +} + +static void dpp_handle_pkex_commit_reveal_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; + const uint8_t *wrapped = NULL; + size_t wrapped_len = 0; + uint8_t one = 1; + _auto_(l_free) uint8_t *unwrapped = NULL; + size_t unwrapped_len = 0; + const uint8_t *boot_key = NULL; + size_t boot_key_len = 0; + const uint8_t *r_auth = NULL; + uint8_t v[L_ECC_SCALAR_MAX_BYTES]; + size_t v_len; + _auto_(l_ecc_point_free) struct l_ecc_point *l = NULL; + + l_debug("PKEX commit reveal "MAC, MAC_STR(from)); + + if (dpp->state != DPP_STATE_PKEX_COMMIT_REVEAL) + return; + + if (dpp->role != DPP_CAPABILITY_ENROLLEE) + return; + + /* + * The URI may not have contained a MAC address, if this announcement + * verifies set peer_addr then. + */ + if (memcmp(from, dpp->peer_addr, 6)) { + l_debug("Unexpected source "MAC" expected "MAC, MAC_STR(from), + MAC_STR(dpp->peer_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_WRAPPED_DATA: + wrapped = data; + wrapped_len = len; + break; + default: + break; + } + } + + if (!wrapped) { + l_debug("No wrapped data"); + return; + } + + unwrapped = dpp_unwrap_attr(body + 2, 6, &one, 1, dpp->z, dpp->z_len, + wrapped, wrapped_len, &unwrapped_len); + if (!unwrapped) { + l_debug("Failed to unwrap Reveal-Commit message"); + return; + } + + dpp_attr_iter_init(&iter, unwrapped, unwrapped_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_BOOTSTRAPPING_KEY: + if (len != dpp->key_len * 2) + return; + + boot_key = data; + boot_key_len = len; + break; + case DPP_ATTR_RESPONDER_AUTH_TAG: + if (len != 32) + return; + + r_auth = data; + break; + default: + break; + } + } + + dpp->peer_boot_public = l_ecc_point_from_data(dpp->curve, + L_ECC_POINT_TYPE_FULL, + boot_key, boot_key_len); + if (!dpp->peer_boot_public) { + l_debug("Peer public bootstrapping key was invalid"); + goto failed; + } + + /* L = b * X' */ + l = l_ecc_point_new(dpp->curve); + + l_ecc_point_multiply(l, dpp->pkex_private, dpp->peer_boot_public); + + if (!dpp_derive_v(l, dpp->peer_addr, dpp->peer_boot_public, + dpp->pkex_public, dpp->y_or_x, v, &v_len)) { + l_debug("Failed to derive v"); + goto failed; + } + + if (memcmp(v, r_auth, v_len)) { + l_debug("Bootstrapping data did not verify"); + goto failed; + } + + if (dpp_pkex_start_authentication(dpp)) + return; + +failed: + dpp_reset(dpp); +} + static void dpp_handle_frame(struct dpp_sm *dpp, const struct mmpdu_header *frame, const void *body, size_t body_len) @@ -2224,6 +2782,14 @@ static void dpp_handle_frame(struct dpp_sm *dpp, dpp_handle_presence_announcement(dpp, frame->address_2, body, body_len); break; + case DPP_FRAME_PKEX_XCHG_RESPONSE: + dpp_handle_pkex_exchange_response(dpp, frame->address_2, body, + body_len); + break; + case DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE: + dpp_handle_pkex_commit_reveal_response(dpp, frame->address_2, + body, body_len); + break; default: l_debug("Unhandled DPP frame %u", *ptr); break; @@ -2280,10 +2846,16 @@ static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data) if (!dpp) return; - if (dpp->state <= DPP_STATE_PRESENCE) + /* + * Don't retransmit for presence or PKEX exchange if an enrollee, both + * are broadcast frames which don't expect an ack. + */ + if (dpp->state == DPP_STATE_NOTHING || + dpp->state == DPP_STATE_PRESENCE || + (dpp->state == DPP_STATE_PKEX_EXCHANGE && + dpp->role == DPP_CAPABILITY_ENROLLEE)) return; - if (dpp->frame_cookie != cookie) return; @@ -2454,6 +3026,16 @@ static void dpp_create(struct netdev *netdev) l_dbus_object_add_interface(dbus, netdev_get_path(netdev), IWD_DPP_INTERFACE, dpp); + l_dbus_object_add_interface(dbus, netdev_get_path(netdev), + IWD_DPP_PKEX_INTERFACE, dpp); + /* + * Since both interfaces share the dpp_sm set this to 2. Currently both + * interfaces are added/removed in unison so we _could_ simply omit the + * destroy callback on one of them. But for consistency and future + * proofing use a reference count and the final interface being removed + * will destroy the dpp_sm. + */ + dpp->refcount = 2; dpp_frame_watch(dpp, 0x00d0, dpp_prefix, sizeof(dpp_prefix)); @@ -2484,6 +3066,9 @@ static void dpp_netdev_watch(struct netdev *netdev, l_dbus_object_remove_interface(dbus_get_bus(), netdev_get_path(netdev), IWD_DPP_INTERFACE); + l_dbus_object_remove_interface(dbus_get_bus(), + netdev_get_path(netdev), + IWD_DPP_PKEX_INTERFACE); break; default: break; @@ -2756,9 +3341,182 @@ static struct l_dbus_message *dpp_dbus_stop(struct l_dbus *dbus, if (dpp->interface != DPP_INTERFACE_DPP) return dbus_error_not_found(message); + return l_dbus_message_new_method_return(message); +} + +/* + * Section 5.6.1 + * In lieu of specific channel information obtained in a manner outside + * the scope of this specification, PKEX responders shall select one of + * the following channels: + * - 2.4 GHz: Channel 6 (2.437 GHz) + * - 5 GHz: Channel 44 (5.220 GHz) if local regulations permit + * operation only in the 5.150 - 5.250 GHz band and Channel + * 149 (5.745 GHz) otherwise + */ +static uint32_t *dpp_default_freqs(struct dpp_sm *dpp, size_t *out_len) +{ + struct wiphy *wiphy = wiphy_find_by_wdev(dpp->wdev_id); + uint32_t default_channels[3] = { 2437, 5220, 5745 }; + uint32_t *freqs_out; + size_t len = 0; + + if ((wiphy_get_supported_bands(wiphy) & BAND_FREQ_2_4_GHZ) && + scan_get_band_rank_modifier(BAND_FREQ_2_4_GHZ) != 0) + default_channels[len++] = 2437; + + if ((wiphy_get_supported_bands(wiphy) & BAND_FREQ_5_GHZ) && + scan_get_band_rank_modifier(BAND_FREQ_5_GHZ) != 0) { + default_channels[len++] = 5220; + default_channels[len++] = 5745; + } + + if (!len) { + l_warn("No bands are allowed, check BandModifier* settings!"); + return NULL; + } + + freqs_out = l_memdup(default_channels, sizeof(uint32_t) * len); + *out_len = len; + + return freqs_out; +} + +static bool dpp_start_pkex_enrollee(struct dpp_sm *dpp, const char *key, + const char *identifier) +{ + struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); + _auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL; + + if (station && station_get_connected_network(station)) { + l_debug("Already connected, disconnect before enrolling"); + return false; + } + + if (identifier) + dpp->pkex_id = l_strdup(identifier); + + dpp->pkex_key = l_strdup(key); + memcpy(dpp->peer_addr, broadcast, 6); + dpp->role = DPP_CAPABILITY_ENROLLEE; + dpp->state = DPP_STATE_PKEX_EXCHANGE; + dpp->interface = DPP_INTERFACE_PKEX; + /* + * In theory a driver could support a lesser duration than 200ms. This + * complicates things since we would need to tack on additional + * offchannel requests to meet the 200ms requirement. This could be done + * but for now use max_roc or 200ms, whichever is less. + */ + dpp->dwell = (dpp->max_roc < 200) ? dpp->max_roc : 200; + /* "DPP R2 devices are expected to use PKEXv1 by default" */ + dpp->pkex_version = 1; + + if (!l_ecdh_generate_key_pair(dpp->curve, &dpp->pkex_private, + &dpp->pkex_public)) + goto failed; + + /* + * "If Qi is the point-at-infinity, the code shall be deleted and the + * user should be notified to provision a new code" + */ + qi = dpp_derive_qi(dpp->curve, dpp->pkex_key, dpp->pkex_id, + netdev_get_address(dpp->netdev)); + if (!qi || l_ecc_point_is_infinity(qi)) { + l_debug("Cannot derive Qi, provision a new code"); + goto failed; + } + + dpp->pkex_m = l_ecc_point_new(dpp->curve); + + if (!l_ecc_point_add(dpp->pkex_m, dpp->pkex_public, qi)) + goto failed; + + dpp_property_changed_notify(dpp); + + dpp->freqs = dpp_default_freqs(dpp, &dpp->freqs_len); + if (!dpp->freqs) + goto failed; + + dpp->current_freq = dpp->freqs[dpp->freqs_idx]; + + dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT); + + l_debug("PKEX start enrollee (id=%s)", dpp->pkex_id ?: "unset"); + + dpp_start_offchannel(dpp, dpp->current_freq); + + return true; + +failed: dpp_reset(dpp); + return false; +} + +static bool dpp_parse_pkex_args(struct l_dbus_message *message, + const char **key_out, + const char **id_out) +{ + struct l_dbus_message_iter iter; + struct l_dbus_message_iter variant; + const char *dict_key; + const char *key = NULL; + const char *id = NULL; + + if (!l_dbus_message_get_arguments(message, "a{sv}", &iter)) + return false; + + while (l_dbus_message_iter_next_entry(&iter, &dict_key, &variant)) { + if (!strcmp(dict_key, "Code")) { + if (!l_dbus_message_iter_get_variant(&variant, "s", + &key)) + return false; + } else if (!strcmp(dict_key, "Identifier")) { + if (!l_dbus_message_iter_get_variant(&variant, "s", + &id)) + return false; + } + } + + if (!key) + return false; + + if (id && strlen(id) > 80) + return false; + + *key_out = key; + *id_out = id; + + return true; +} + +static struct l_dbus_message *dpp_dbus_pkex_start_enrollee(struct l_dbus *dbus, + struct l_dbus_message *message, + void *user_data) +{ + struct dpp_sm *dpp = user_data; + const char *key; + const char *id; + struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); + + l_debug(""); + + if (dpp->state != DPP_STATE_NOTHING || + dpp->interface != DPP_INTERFACE_UNBOUND) + return dbus_error_busy(message); + + if (station_get_connected_network(station)) + return dbus_error_busy(message); + + if (!dpp_parse_pkex_args(message, &key, &id)) + goto invalid_args; + + if (!dpp_start_pkex_enrollee(dpp, key, id)) + goto invalid_args; return l_dbus_message_new_method_return(message); + +invalid_args: + return dbus_error_invalid_args(message); } static void dpp_setup_interface(struct l_dbus_interface *interface) @@ -2779,10 +3537,37 @@ static void dpp_setup_interface(struct l_dbus_interface *interface) l_dbus_interface_property(interface, "URI", 0, "s", dpp_get_uri, NULL); } +static struct l_dbus_message *dpp_dbus_pkex_stop(struct l_dbus *dbus, + struct l_dbus_message *message, void *user_data) +{ + struct dpp_sm *dpp = user_data; + + if (dpp->interface != DPP_INTERFACE_PKEX) + return dbus_error_not_found(message); + + return l_dbus_message_new_method_return(message); +} + +static void dpp_setup_pkex_interface(struct l_dbus_interface *interface) +{ + l_dbus_interface_method(interface, "StartEnrollee", 0, + dpp_dbus_pkex_start_enrollee, "", "a{sv}", "args"); + l_dbus_interface_method(interface, "Stop", 0, + dpp_dbus_pkex_stop, "", ""); + + l_dbus_interface_property(interface, "Started", 0, "b", + dpp_pkex_get_started, NULL); + l_dbus_interface_property(interface, "Role", 0, "s", + dpp_pkex_get_role, NULL); +} + static void dpp_destroy_interface(void *user_data) { struct dpp_sm *dpp = user_data; + if (--dpp->refcount) + return; + l_queue_remove(dpp_list, dpp); dpp_free(dpp); @@ -2801,6 +3586,9 @@ static int dpp_init(void) l_dbus_register_interface(dbus_get_bus(), IWD_DPP_INTERFACE, dpp_setup_interface, dpp_destroy_interface, false); + l_dbus_register_interface(dbus_get_bus(), IWD_DPP_PKEX_INTERFACE, + dpp_setup_pkex_interface, + dpp_destroy_interface, false); mlme_watch = l_genl_family_register(nl80211, "mlme", dpp_mlme_notify, NULL, NULL); @@ -2820,6 +3608,7 @@ static void dpp_exit(void) l_debug(""); l_dbus_unregister_interface(dbus_get_bus(), IWD_DPP_INTERFACE); + l_dbus_unregister_interface(dbus_get_bus(), IWD_DPP_PKEX_INTERFACE); netdev_watch_remove(netdev_watch); From patchwork Wed Nov 8 17:21:51 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13450350 Received: from mail-vk1-f178.google.com (mail-vk1-f178.google.com [209.85.221.178]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2D65531A7F for ; Wed, 8 Nov 2023 17:22:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dOQOx3e8" Received: by mail-vk1-f178.google.com with SMTP id 71dfb90a1353d-4ac4cd60370so1492871e0c.3 for ; Wed, 08 Nov 2023 09:22:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1699464126; x=1700068926; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=x0iH8wk4TqsfNIpzn8dM/PDkTBSen/pnOk178gMK+70=; b=dOQOx3e8Pr8ASq4LgGonvQrEEkjHNTboNfnHB/emsf2NTb3RDdOgRcHlC/2x2YTTPl 43dpP1kOK/revRe5NOFPVryhWfG9J1a06FzDDSUTf0apGfnr3QwC4Dnk+sp473aTlX7q jjCMhknq4Xh7PYJsDjzkfCxeCI81FUWsdtDaJWczec0fooiKtsCNQaBRH3PGysf36JFK Mohhn1qIXh9WLT2fXq1MhuUfvZKBZ+fmAAtmQmDLKh78qceoBKkwjRASIE+EzJvwSMZO 1pq72DmNhJ7UgnG4a9cdNcnq7AYh7fuLI2LfTQrrb9uX3cbunk51pPwgx5Qd1IEx05My oPfw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699464126; x=1700068926; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=x0iH8wk4TqsfNIpzn8dM/PDkTBSen/pnOk178gMK+70=; b=XaT+pfxD11mtFdvAJWdHPTsjjHCGUbDC6ZxBfmBPYXakhTFPi4e10LEyUBjWyECVnR 6SKIRPhdyieRKlS7wq6LyoIcU8gV4CLFIHcfrVi+wEPojVHjd89BKQqdCPQQ85FysnCJ 3fLjix+z758hBFY9Pn8H3tNoV7sL9IwaDeLaJyiVWWQHZA7vmiGARkchMnWOUVrhysML by737ZYi38l2RekXhsfOXsPe29B6W4PjEIykkot131praRJtesYGAHOk8sUcZ1/MwLBH oDpybsIsRlyoqaNc0L8+476WzDa3iyhO4dUylKuXjFk1xT1PetyCKF3AXIwVj+D0yVcl upgw== X-Gm-Message-State: AOJu0YyeR4Aci3qDPvPkQ8R+LFZKjzRCd/LkLiUsjNRahmaXb8BV/M+y 9C3Y/UPZRv0pHyNXHC3DGoqEwBKryV0= X-Google-Smtp-Source: AGHT+IFJf/ZRpMZuWP8G5cMSiygsxMrmu0GRLLqkIQNV6Bqa/fhEgDXOeUUfN7ZdO4khRGmbNLHlOg== X-Received: by 2002:a1f:2415:0:b0:49d:c1f5:d491 with SMTP id k21-20020a1f2415000000b0049dc1f5d491mr2438670vkk.15.1699464125723; Wed, 08 Nov 2023 09:22:05 -0800 (PST) Received: from LOCLAP699.rst-02.locus (50-78-19-50-static.hfc.comcastbusiness.net. [50.78.19.50]) by smtp.gmail.com with ESMTPSA id qb1-20020ad44701000000b0065d03eae7a0sm1277003qvb.82.2023.11.08.09.22.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Nov 2023 09:22:05 -0800 (PST) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH v5 06/10] dpp: initial version of PKEX configurator support Date: Wed, 8 Nov 2023 09:21:51 -0800 Message-Id: <20231108172155.2129509-7-prestwoj@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231108172155.2129509-1-prestwoj@gmail.com> References: <20231108172155.2129509-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The PKEX configurator role is currently limited to being a responder. When started the configurator will listen on its current operating channel for a PKEX exchange request. Once received it and the encrypted key is properly decrypted it treats this peer as the enrollee and won't allow configurations from other peers unless PKEX is restarted. The configurator will encrypt and send its encrypted ephemeral key in the PKEX exchange response. The enrollee then sends its encrypted bootstrapping key (as commit-reveal request) then the same for the configurator (as commit-reveal response). After this, PKEX authentication begins. The enrollee is expected to send the authenticate request, since its the initiator. --- src/dpp.c | 505 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 504 insertions(+), 1 deletion(-) diff --git a/src/dpp.c b/src/dpp.c index ee2cd903..90de6945 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -1889,7 +1889,8 @@ static void authenticate_request(struct dpp_sm *dpp, const uint8_t *from, if (util_is_broadcast_address(from)) return; - if (dpp->state != DPP_STATE_PRESENCE) + if (dpp->state != DPP_STATE_PRESENCE && + dpp->state != DPP_STATE_AUTHENTICATING) return; l_debug("authenticate request"); @@ -2748,6 +2749,435 @@ failed: dpp_reset(dpp); } +static void dpp_send_bad_group(struct dpp_sm *dpp, const uint8_t *addr) +{ + uint8_t hdr[32]; + uint8_t attrs[256]; + uint8_t *ptr = attrs; + uint16_t group; + uint8_t status = DPP_STATUS_BAD_GROUP; + struct iovec iov[2]; + const uint8_t *own_mac = netdev_get_address(dpp->netdev); + + l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group); + + iov[0].iov_len = dpp_build_header(own_mac, addr, + DPP_FRAME_PKEX_XCHG_RESPONSE, hdr); + iov[0].iov_base = hdr; + + ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1); + ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, + &dpp->pkex_version, 1); + ptr += dpp_append_attr(ptr, DPP_ATTR_FINITE_CYCLIC_GROUP, &group, 2); + + iov[1].iov_base = attrs; + iov[1].iov_len = ptr - attrs; + + dpp_send_frame(dpp, iov, 2, dpp->current_freq); +} + +static void dpp_send_bad_code(struct dpp_sm *dpp, const uint8_t *addr) +{ + uint8_t hdr[32]; + uint8_t attrs[256]; + uint8_t *ptr = attrs; + uint8_t status = DPP_STATUS_BAD_CODE; + struct iovec iov[2]; + const uint8_t *own_mac = netdev_get_address(dpp->netdev); + + iov[0].iov_len = dpp_build_header(own_mac, addr, + DPP_FRAME_PKEX_XCHG_RESPONSE, hdr); + iov[0].iov_base = hdr; + + ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1); + ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, + &dpp->pkex_version, 1); + if (dpp->pkex_id) + ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER, + dpp->pkex_id, strlen(dpp->pkex_id)); + + iov[1].iov_base = attrs; + iov[1].iov_len = ptr - attrs; + + dpp_send_frame(dpp, iov, 2, dpp->current_freq); +} + +static void dpp_send_pkex_exchange_response(struct dpp_sm *dpp, + struct l_ecc_point *n) +{ + uint8_t hdr[32]; + uint8_t attrs[256]; + uint8_t *ptr = attrs; + uint64_t n_data[L_ECC_MAX_DIGITS * 2]; + uint16_t group; + uint8_t status = DPP_STATUS_OK; + struct iovec iov[2]; + const uint8_t *own_mac = netdev_get_address(dpp->netdev); + + l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group); + + iov[0].iov_len = dpp_build_header(own_mac, dpp->peer_addr, + DPP_FRAME_PKEX_XCHG_RESPONSE, hdr); + iov[0].iov_base = hdr; + + ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1); + + if (dpp->pkex_id) + ptr += dpp_append_attr(ptr, DPP_ATTR_CODE_IDENTIFIER, + dpp->pkex_id, strlen(dpp->pkex_id)); + + l_ecc_point_get_data(n, n_data, sizeof(n_data)); + + ptr += dpp_append_attr(ptr, DPP_ATTR_ENCRYPTED_KEY, + n_data, dpp->key_len * 2); + + iov[1].iov_base = attrs; + iov[1].iov_len = ptr - attrs; + + dpp->state = DPP_STATE_PKEX_COMMIT_REVEAL; + + dpp_property_changed_notify(dpp); + + dpp_send_frame(dpp, iov, 2, dpp->current_freq); +} + +static void dpp_process_pkex_exchange_request(struct dpp_sm *dpp, + struct l_ecc_point *m) +{ + _auto_(l_ecc_point_free) struct l_ecc_point *n = NULL; + _auto_(l_ecc_point_free) struct l_ecc_point *qr = NULL; + _auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL; + _auto_(l_ecc_point_free) struct l_ecc_point *k = NULL; + const uint8_t *own_addr = netdev_get_address(dpp->netdev); + + /* Qi = H(MAC-Initiator | [identifier | ] code) * Pi */ + qi = dpp_derive_qi(dpp->curve, dpp->pkex_key, dpp->pkex_id, + dpp->peer_addr); + if (!qi) { + l_debug("could not derive Qi"); + return; + } + + /* X' = M - Qi */ + dpp->y_or_x = l_ecc_point_new(dpp->curve); + + l_ecc_point_inverse(qi); + l_ecc_point_add(dpp->y_or_x, m, qi); + + /* + * "The resulting ephemeral key, denoted X’, is checked whether it is + * the point-at-infinity. If it is not valid, the protocol silently + * fails" + */ + if (l_ecc_point_is_infinity(dpp->y_or_x)) { + l_debug("X' is at infinity, ignore message"); + dpp_reset(dpp); + return; + } + + qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id, own_addr); + if (!qr || l_ecc_point_is_infinity(qr)) { + l_debug("Qr did not derive"); + l_ecc_point_free(dpp->y_or_x); + dpp->y_or_x = NULL; + goto bad_code; + } + + /* + * "The Responder then generates a random ephemeral keypair, y/Y, + * encrypts Y with Qr to obtain the result, denoted N." + */ + l_ecdh_generate_key_pair(dpp->curve, &dpp->pkex_private, + &dpp->pkex_public); + + /* N = Y + Qr */ + n = l_ecc_point_new(dpp->curve); + + l_ecc_point_add(n, dpp->pkex_public, qr); + + /* K = y * X' */ + + k = l_ecc_point_new(dpp->curve); + + l_ecc_point_multiply(k, dpp->pkex_private, dpp->y_or_x); + + /* z = HKDF(<>, info | M.x | N.x | code, K.x) */ + dpp_derive_z(dpp->peer_addr, own_addr, n, m, k, dpp->pkex_key, + dpp->pkex_id, dpp->z, &dpp->z_len); + + dpp_send_pkex_exchange_response(dpp, n); + + return; + +bad_code: + dpp_send_bad_code(dpp, dpp->peer_addr); + return; +} + +static void dpp_handle_pkex_exchange_request(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; + uint8_t version = 0; + uint16_t group = 0; + const void *id = NULL; + size_t id_len = 0; + const void *key = NULL; + size_t key_len = 0; + _auto_(l_ecc_point_free) struct l_ecc_point *m = NULL; + + l_debug("PKEX exchange request "MAC, MAC_STR(from)); + + if (dpp->state != DPP_STATE_PKEX_EXCHANGE) + return; + + if (dpp->role != DPP_CAPABILITY_CONFIGURATOR) + return; + + if (!l_memeqzero(dpp->peer_addr, 6)) { + l_debug("Already configuring enrollee, ignoring"); + 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_PROTOCOL_VERSION: + if (len != 1) + return; + + version = l_get_u8(data); + break; + case DPP_ATTR_FINITE_CYCLIC_GROUP: + if (len != 2) + return; + + group = l_get_le16(data); + break; + case DPP_ATTR_CODE_IDENTIFIER: + id = data; + id_len = len; + break; + case DPP_ATTR_ENCRYPTED_KEY: + key = data; + key_len = len; + break; + default: + break; + } + } + + if (!key || !group) { + l_debug("initiator did not provide group or key, ignoring"); + return; + } + + if (group != l_ecc_curve_get_ike_group(dpp->curve)) { + l_debug("initiator is not using the same group"); + goto bad_group; + } + + /* + * If the group isn't the same the key length won't match, so check + * this here after we've determined the groups are equal + */ + if (key_len != dpp->key_len * 2) { + l_debug("Unexpected encrypted key length"); + return; + } + + if (version && version != dpp->pkex_version) { + l_debug("initiator is not using the same version, ignoring"); + return; + } + + if (dpp->pkex_id) { + if (!id || id_len != strlen(dpp->pkex_id) || + memcmp(dpp->pkex_id, id, id_len)) { + l_debug("mismatch identifier, ignoring"); + return; + } + } + + m = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL, + key, key_len); + if (!m) { + l_debug("could not parse key from initiator, ignoring"); + return; + } + + memcpy(dpp->peer_addr, from, 6); + + dpp_process_pkex_exchange_request(dpp, m); + + return; + +bad_group: + dpp_send_bad_group(dpp, from); +} + +static void dpp_send_commit_reveal_response(struct dpp_sm *dpp, + const uint8_t *v, size_t v_len) +{ + uint8_t hdr[32]; + uint8_t attrs[256]; + uint8_t *ptr = attrs; + uint8_t one = 1; + struct iovec iov[2]; + const uint8_t *own_mac = netdev_get_address(dpp->netdev); + uint8_t b_pub[L_ECC_POINT_MAX_BYTES]; + size_t b_len; + + b_len = l_ecc_point_get_data(dpp->boot_public, b_pub, sizeof(b_pub)); + + + iov[0].iov_len = dpp_build_header(own_mac, dpp->peer_addr, + DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE, hdr); + iov[0].iov_base = hdr; + + ptr += dpp_append_wrapped_data(hdr + 26, 6, &one, 1, ptr, + sizeof(attrs), dpp->z, dpp->z_len, 2, + DPP_ATTR_BOOTSTRAPPING_KEY, b_len, b_pub, + DPP_ATTR_RESPONDER_AUTH_TAG, v_len, v); + + iov[1].iov_base = attrs; + iov[1].iov_len = ptr - attrs; + + dpp_send_frame(dpp, iov, 2, dpp->current_freq); +} + +static void dpp_handle_pkex_commit_reveal_request(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 *wrapped = NULL; + size_t wrapped_len = 0; + _auto_(l_free) uint8_t *unwrapped = NULL; + size_t unwrapped_len; + uint8_t zero = 0; + const void *key = 0; + size_t key_len = 0; + const void *i_auth = NULL; + size_t i_auth_len = 0; + _auto_(l_ecc_point_free) struct l_ecc_point *j = NULL; + _auto_(l_ecc_point_free) struct l_ecc_point *l = NULL; + uint8_t u[L_ECC_SCALAR_MAX_BYTES]; + size_t u_len = 0; + uint8_t v[L_ECC_SCALAR_MAX_BYTES]; + size_t v_len = 0; + const uint8_t *own_addr = netdev_get_address(dpp->netdev); + + l_debug("PKEX commit-reveal request "MAC, MAC_STR(from)); + + if (dpp->state != DPP_STATE_PKEX_COMMIT_REVEAL) + return; + + if (dpp->role != DPP_CAPABILITY_CONFIGURATOR) + 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_WRAPPED_DATA: + wrapped = data; + wrapped_len = len; + break; + default: + break; + } + } + + if (!wrapped) { + l_debug("No wrapped data"); + return; + } + + unwrapped = dpp_unwrap_attr(body + 2, 6, &zero, 1, dpp->z, dpp->z_len, + wrapped, wrapped_len, &unwrapped_len); + if (!unwrapped) { + l_debug("Failed to unwrap attributes"); + return; + } + + dpp_attr_iter_init(&iter, unwrapped, unwrapped_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_BOOTSTRAPPING_KEY: + if (len != dpp->key_len * 2) + return; + + key = data; + key_len = len; + break; + case DPP_ATTR_INITIATOR_AUTH_TAG: + if (len != 32) + return; + + i_auth = data; + i_auth_len = len; + break; + default: + break; + } + } + + if (!key || !i_auth) { + l_debug("missing attributes"); + return; + } + + dpp->peer_boot_public = l_ecc_point_from_data(dpp->curve, + L_ECC_POINT_TYPE_FULL, key, key_len); + if (!dpp->peer_boot_public) { + l_debug("peers boostrapping key did not validate"); + goto failed; + } + + /* J' = y * A' */ + j = l_ecc_point_new(dpp->curve); + + l_ecc_point_multiply(j, dpp->pkex_private, dpp->peer_boot_public); + + dpp_derive_u(j, dpp->peer_addr, dpp->peer_boot_public, dpp->pkex_public, + dpp->y_or_x, u, &u_len); + + if (memcmp(u, i_auth, i_auth_len)) { + l_debug("Initiator auth tag did not verify"); + goto failed; + } + + /* L' = x * B' */ + l = l_ecc_point_new(dpp->curve); + + l_ecc_point_multiply(l, dpp->boot_private, dpp->y_or_x); + + if (!dpp_derive_v(l, own_addr, dpp->boot_public, dpp->y_or_x, + dpp->pkex_public, v, &v_len)) { + l_debug("Failed to derive v"); + goto failed; + } + + dpp_send_commit_reveal_response(dpp, v, v_len); + + dpp_pkex_start_authentication(dpp); + + return; + +failed: + dpp_reset(dpp); +} + static void dpp_handle_frame(struct dpp_sm *dpp, const struct mmpdu_header *frame, const void *body, size_t body_len) @@ -2790,6 +3220,14 @@ static void dpp_handle_frame(struct dpp_sm *dpp, dpp_handle_pkex_commit_reveal_response(dpp, frame->address_2, body, body_len); break; + case DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST: + dpp_handle_pkex_exchange_request(dpp, frame->address_2, body, + body_len); + break; + case DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST: + dpp_handle_pkex_commit_reveal_request(dpp, frame->address_2, + body, body_len); + break; default: l_debug("Unhandled DPP frame %u", *ptr); break; @@ -3519,6 +3957,69 @@ invalid_args: return dbus_error_invalid_args(message); } +static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp, + const char *key, const char *identifier, + struct l_dbus_message *message) +{ + struct handshake_state *hs = netdev_get_handshake(dpp->netdev); + struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); + struct network *network = station_get_connected_network(station); + struct scan_bss *bss = station_get_connected_bss(station); + const struct l_settings *settings; + + if (dpp->state != DPP_STATE_NOTHING || + dpp->interface != DPP_INTERFACE_UNBOUND) + return dbus_error_busy(message); + + if (!network || !bss) + return dbus_error_not_connected(message); + + settings = network_get_settings(network); + if (!settings) { + l_debug("No settings for network, is this a known network?"); + return dbus_error_not_configured(message); + } + + if (identifier) + dpp->pkex_id = l_strdup(identifier); + + if (key) + dpp->pkex_key = l_strdup(key); + + dpp->role = DPP_CAPABILITY_CONFIGURATOR; + dpp->state = DPP_STATE_PKEX_EXCHANGE; + dpp->interface = DPP_INTERFACE_PKEX; + dpp->current_freq = bss->frequency; + dpp->pkex_version = 1; + dpp->config = dpp_configuration_new(network_get_settings(network), + network_get_ssid(network), + hs->akm_suite); + + dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT); + dpp_property_changed_notify(dpp); + + l_debug("Starting PKEX configurator for single enrollee"); + + return l_dbus_message_new_method_return(message); +} + +static struct l_dbus_message *dpp_dbus_pkex_configure_enrollee( + struct l_dbus *dbus, + struct l_dbus_message *message, + void *user_data) +{ + struct dpp_sm *dpp = user_data; + const char *key; + const char *id; + + l_debug(""); + + if (!dpp_parse_pkex_args(message, &key, &id)) + return dbus_error_invalid_args(message); + + return dpp_start_pkex_configurator(dpp, key, id, message); +} + static void dpp_setup_interface(struct l_dbus_interface *interface) { l_dbus_interface_method(interface, "StartEnrollee", 0, @@ -3554,6 +4055,8 @@ static void dpp_setup_pkex_interface(struct l_dbus_interface *interface) dpp_dbus_pkex_start_enrollee, "", "a{sv}", "args"); l_dbus_interface_method(interface, "Stop", 0, dpp_dbus_pkex_stop, "", ""); + l_dbus_interface_method(interface, "ConfigureEnrollee", 0, + dpp_dbus_pkex_configure_enrollee, "", "a{sv}", "args"); l_dbus_interface_property(interface, "Started", 0, "b", dpp_pkex_get_started, NULL); From patchwork Wed Nov 8 17:21:52 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13450351 Received: from mail-qv1-f50.google.com (mail-qv1-f50.google.com [209.85.219.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 71C443033B for ; Wed, 8 Nov 2023 17:22:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="NmW583RH" Received: by mail-qv1-f50.google.com with SMTP id 6a1803df08f44-66d190a8f87so45024906d6.0 for ; Wed, 08 Nov 2023 09:22:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1699464127; x=1700068927; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=j56D32wso/LyK+sfGtLSBLwZsm4Uwy4vUz6r3IxZcCQ=; b=NmW583RHziuTyZitXrvnfqfXkpDMsQn9UCfDJVYWoP8IBSXx9wyVm2bT6ylqdc82e/ 4sZZdLG0J5NYuOVug7tGFaU1Ymi8q02a0TCbIBeTXvfcMPPBNWCFn+Agnvj0nzJhBZkI LBi78gNU99YCcJ+PwfBtCw3GMBILSMrzCt/IoJafbOs7cOBFxzW/5lWKfmY8zYKiwzND 6Ua7FmqAmzV0ATYmmODz7Udb+u8PH9B770Z5hJ633fmjXYHH5MZ/OhQurzUUkIJ3wSVI FpwONQAw2cpipdc6OP6JLtkHr1aQVb5FWDUBPhmFowwmguJKEAeDmVrYQo3peVrcZ873 A0FA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699464127; x=1700068927; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=j56D32wso/LyK+sfGtLSBLwZsm4Uwy4vUz6r3IxZcCQ=; b=J/L8uwnfAWazBoi9vgx2osxpTO1h2sSAzijAR0pu1mJkLpZSdcHWhy6outrMw0BwVR KAMz5dep5qZBGwwiU91enm/C5kKnbTSFdS0IJ92EhoSbYWnhiHPHn9IQUmq0DGV/J+ce gkCJ9N3MqgVIkOWbsPc19zbEBrGamfmfBW3go54qOfMhrvDV+HU95rolrltQb4wE9Lml hHVzKBkMB1HL1dCS996ddx/UhMxdV4ADVK0VdzG/aorYdJJKGKRLI+amn8CzQkvD4g8V KDyu7svcfMuLCoZ5Hy8XY2VkA59yR9iMtz0xfvuSeqCt1zorz2ugjZhMcZVdC/f4TUQs /sXg== X-Gm-Message-State: AOJu0YxxtunimZDUVTtY8L9E7qeXf3Ic8OnvbHQpYYGdJTwwNlc8JZdN v1X94PNLIxuc2xsnaiETCoAg+8BidK4= X-Google-Smtp-Source: AGHT+IEuay80FHc92AhYvgE/6f4Eoa9GNhUPjSuJvjlHI3XZgiUGACsg6v71PJb/9bilhAcNneOEFg== X-Received: by 2002:a05:6214:2b0d:b0:66d:368e:24f5 with SMTP id jx13-20020a0562142b0d00b0066d368e24f5mr2871694qvb.46.1699464126900; Wed, 08 Nov 2023 09:22:06 -0800 (PST) Received: from LOCLAP699.rst-02.locus (50-78-19-50-static.hfc.comcastbusiness.net. [50.78.19.50]) by smtp.gmail.com with ESMTPSA id qb1-20020ad44701000000b0065d03eae7a0sm1277003qvb.82.2023.11.08.09.22.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Nov 2023 09:22:06 -0800 (PST) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH v5 07/10] dpp: Add StartConfigurator, PKEX agent support Date: Wed, 8 Nov 2023 09:21:52 -0800 Message-Id: <20231108172155.2129509-8-prestwoj@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231108172155.2129509-1-prestwoj@gmail.com> References: <20231108172155.2129509-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Adds a configurator variant to be used along side an agent. When called the configurator will start and wait for an initial PKEX exchange message from an enrollee at which point it will request the code from an agent. This provides more flexibility for configurators that are capable of configuring multiple enrollees with different identifiers/codes. Note that the timing requirements per the DPP spec still apply so this is not meant to be used with a human configurator but within an automated agent which does a quick lookup of potential identifiers/codes and can reply within the 200ms window. --- src/dpp.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 222 insertions(+), 2 deletions(-) diff --git a/src/dpp.c b/src/dpp.c index 90de6945..6a27cf43 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -88,6 +88,13 @@ enum dpp_interface { DPP_INTERFACE_PKEX, }; +struct pkex_agent { + char *owner; + char *path; + unsigned int disconnect_watch; + uint32_t pending_id; +}; + struct dpp_sm { struct netdev *netdev; char *uri; @@ -112,6 +119,8 @@ struct dpp_sm { enum dpp_state state; enum dpp_interface interface; + struct pkex_agent *agent; + /* * List of frequencies to jump between. The presence of this list is * also used to signify that a configurator is an initiator vs responder @@ -162,6 +171,7 @@ struct dpp_sm { char *pkex_id; char *pkex_key; uint8_t pkex_version; + struct l_ecc_point *peer_encr_key; struct l_ecc_point *pkex_m; /* Ephemeral key Y' or X' for enrollee or configurator */ struct l_ecc_point *y_or_x; @@ -335,6 +345,68 @@ static void dpp_free_pending_pkex_data(struct dpp_sm *dpp) } memset(dpp->peer_addr, 0, sizeof(dpp->peer_addr)); + + if (dpp->peer_encr_key) { + l_ecc_point_free(dpp->peer_encr_key); + dpp->peer_encr_key = NULL; + } +} + +static void pkex_agent_free(void *data) +{ + struct pkex_agent *agent = data; + + l_free(agent->owner); + l_free(agent->path); + l_dbus_remove_watch(dbus_get_bus(), agent->disconnect_watch); + l_free(agent); +} + +static void dpp_agent_cancel(struct dpp_sm *dpp) +{ + struct l_dbus_message *msg; + + const char *reason = "shutdown"; + + msg = l_dbus_message_new_method_call(dbus_get_bus(), + dpp->agent->owner, + dpp->agent->path, + IWD_SHARED_CODE_AGENT_INTERFACE, + "Cancel"); + l_dbus_message_set_arguments(msg, "s", reason); + l_dbus_message_set_no_reply(msg, true); + l_dbus_send(dbus_get_bus(), msg); +} + +static void dpp_agent_release(struct dpp_sm *dpp) +{ + struct l_dbus_message *msg; + + msg = l_dbus_message_new_method_call(dbus_get_bus(), + dpp->agent->owner, + dpp->agent->path, + IWD_SHARED_CODE_AGENT_INTERFACE, + "Release"); + l_dbus_message_set_no_reply(msg, true); + l_dbus_send(dbus_get_bus(), msg); +} + +static void dpp_destroy_agent(struct dpp_sm *dpp) +{ + if (!dpp->agent) + return; + + if (dpp->agent->pending_id) { + dpp_agent_cancel(dpp); + l_dbus_cancel(dbus_get_bus(), dpp->agent->pending_id); + } + + dpp_agent_release(dpp); + + l_debug("Released SharedCodeAgent on path %s", dpp->agent->path); + + pkex_agent_free(dpp->agent); + dpp->agent = NULL; } static void dpp_free_auth_data(struct dpp_sm *dpp) @@ -449,6 +521,8 @@ static void dpp_reset(struct dpp_sm *dpp) explicit_bzero(dpp->z, dpp->key_len); explicit_bzero(dpp->u, dpp->u_len); + dpp_destroy_agent(dpp); + dpp_free_pending_pkex_data(dpp); dpp_free_auth_data(dpp); @@ -1802,6 +1876,20 @@ static void dpp_offchannel_timeout(int error, void *user_data) switch (dpp->state) { case DPP_STATE_PKEX_EXCHANGE: + if (dpp->role != DPP_CAPABILITY_CONFIGURATOR || !dpp->agent) + break; + + /* + * We have a pending agent request but it did not arrive in + * time, we cant assume the enrollee will be waiting around + * for our response so cancel the request and continue waiting + * for another request + */ + if (dpp->agent->pending_id) { + dpp_free_pending_pkex_data(dpp); + dpp_agent_cancel(dpp); + } + /* Fall through */ case DPP_STATE_PRESENCE: break; case DPP_STATE_NOTHING: @@ -2914,6 +3002,63 @@ bad_code: return; } +static void dpp_pkex_agent_reply(struct l_dbus_message *message, + void *user_data) +{ + struct dpp_sm *dpp = user_data; + const char *error, *text; + const char *code; + + dpp->agent->pending_id = 0; + + l_debug("SharedCodeAgent %s path %s replied", dpp->agent->owner, + dpp->agent->path); + + if (l_dbus_message_get_error(message, &error, &text)) { + l_error("RequestSharedCode(%s) returned %s(\"%s\")", + dpp->pkex_id, error, text); + goto reset; + } + + if (!l_dbus_message_get_arguments(message, "s", &code)) { + l_debug("Invalid arguments, check SharedCodeAgent!"); + goto reset; + } + + dpp->pkex_key = l_strdup(code); + dpp_process_pkex_exchange_request(dpp, dpp->peer_encr_key); + + return; + +reset: + dpp_free_pending_pkex_data(dpp); +} + +static bool dpp_pkex_agent_request(struct dpp_sm *dpp) +{ + struct l_dbus_message *msg; + + if (!dpp->agent) + return false; + + if (L_WARN_ON(dpp->agent->pending_id)) + return false; + + msg = l_dbus_message_new_method_call(dbus_get_bus(), + dpp->agent->owner, + dpp->agent->path, + IWD_SHARED_CODE_AGENT_INTERFACE, + "RequestSharedCode"); + l_dbus_message_set_arguments(msg, "s", dpp->pkex_id); + + + dpp->agent->pending_id = l_dbus_send_with_reply(dbus_get_bus(), + msg, + dpp_pkex_agent_reply, + dpp, NULL); + return dpp->agent->pending_id != 0; +} + static void dpp_handle_pkex_exchange_request(struct dpp_sm *dpp, const uint8_t *from, const uint8_t *body, size_t body_len) @@ -3013,6 +3158,33 @@ static void dpp_handle_pkex_exchange_request(struct dpp_sm *dpp, memcpy(dpp->peer_addr, from, 6); + if (!dpp->pkex_key) { + /* + * "If an optional code identifier is used, it shall be a UTF-8 + * string not greater than eighty (80) octets" + */ + if (!id || id_len > 80 || !l_utf8_validate(id, id_len, NULL)) { + l_debug("Configurator started with agent but enrollee " + "sent invalid or no identifier, ignoring"); + return; + } + + dpp->pkex_id = l_strndup(id, id_len); + + /* Need to obtain code from agent */ + if (!dpp_pkex_agent_request(dpp)) { + l_debug("Failed to request code from agent!"); + dpp_free_pending_pkex_data(dpp); + return; + } + + /* Save the encrypted key/identifier for the agent callback */ + + dpp->peer_encr_key = l_steal_ptr(m); + + return; + } + dpp_process_pkex_exchange_request(dpp, m); return; @@ -3957,8 +4129,34 @@ invalid_args: return dbus_error_invalid_args(message); } +static void pkex_agent_disconnect(struct l_dbus *dbus, void *user_data) +{ + struct dpp_sm *dpp = user_data; + + l_debug("SharedCodeAgent %s disconnected", dpp->agent->path); + + dpp_reset(dpp); +} + +static void dpp_create_agent(struct dpp_sm *dpp, const char *path, + struct l_dbus_message *message) +{ + const char *sender = l_dbus_message_get_sender(message); + + dpp->agent = l_new(struct pkex_agent, 1); + dpp->agent->owner = l_strdup(sender); + dpp->agent->path = l_strdup(path); + dpp->agent->disconnect_watch = l_dbus_add_disconnect_watch(dbus_get_bus(), + sender, + pkex_agent_disconnect, + dpp, NULL); + + l_debug("Registered a SharedCodeAgent on path %s", path); +} + static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp, const char *key, const char *identifier, + const char *agent_path, struct l_dbus_message *message) { struct handshake_state *hs = netdev_get_handshake(dpp->netdev); @@ -3986,6 +4184,9 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp, if (key) dpp->pkex_key = l_strdup(key); + if (agent_path) + dpp_create_agent(dpp, agent_path, message); + dpp->role = DPP_CAPABILITY_CONFIGURATOR; dpp->state = DPP_STATE_PKEX_EXCHANGE; dpp->interface = DPP_INTERFACE_PKEX; @@ -3998,7 +4199,10 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp, dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT); dpp_property_changed_notify(dpp); - l_debug("Starting PKEX configurator for single enrollee"); + if (dpp->pkex_key) + l_debug("Starting PKEX configurator for single enrollee"); + else + l_debug("Starting PKEX configurator with agent"); return l_dbus_message_new_method_return(message); } @@ -4017,7 +4221,21 @@ static struct l_dbus_message *dpp_dbus_pkex_configure_enrollee( if (!dpp_parse_pkex_args(message, &key, &id)) return dbus_error_invalid_args(message); - return dpp_start_pkex_configurator(dpp, key, id, message); + return dpp_start_pkex_configurator(dpp, key, id, NULL, message); +} + +static struct l_dbus_message *dpp_dbus_pkex_start_configurator( + struct l_dbus *dbus, + struct l_dbus_message *message, + void *user_data) +{ + struct dpp_sm *dpp = user_data; + const char *path; + + if (!l_dbus_message_get_arguments(message, "o", &path)) + return dbus_error_invalid_args(message); + + return dpp_start_pkex_configurator(dpp, NULL, NULL, path, message); } static void dpp_setup_interface(struct l_dbus_interface *interface) @@ -4057,6 +4275,8 @@ static void dpp_setup_pkex_interface(struct l_dbus_interface *interface) dpp_dbus_pkex_stop, "", ""); l_dbus_interface_method(interface, "ConfigureEnrollee", 0, dpp_dbus_pkex_configure_enrollee, "", "a{sv}", "args"); + l_dbus_interface_method(interface, "StartConfigurator", 0, + dpp_dbus_pkex_start_configurator, "", "o", "path"); l_dbus_interface_property(interface, "Started", 0, "b", dpp_pkex_get_started, NULL); From patchwork Wed Nov 8 17:21:53 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13450352 Received: from mail-qv1-f50.google.com (mail-qv1-f50.google.com [209.85.219.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4CBF031A98 for ; Wed, 8 Nov 2023 17:22:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="SfttIBnG" Received: by mail-qv1-f50.google.com with SMTP id 6a1803df08f44-66d09b6d007so48734716d6.1 for ; Wed, 08 Nov 2023 09:22:09 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1699464128; x=1700068928; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ALaZJ6XsrsPVtrQi96mwWume01S/yCvLr5bXcq2qj94=; b=SfttIBnGDbSieqUiKE0YQjcMt1zkGlfh6NbFNe/fy189GV4P7MY20c3UYCpv7cjphC bIUPXDjyzfrwWM8vBEBu6Q5aSkYL2yQD31v2lyHVYp3OQdXCIHy6uIltI35ZnmBs/P43 s6IzWvCOZ5/fu7onumteDfOmhzgoLHpyxUseKvtZvR9NI9LA2FmZGSREHlgdci0U7ynK aamL5eCVEN/pJ7E+wEGHVBfnSpEn/dSy6hpvNUa0G1HvE1kaMFtUmdu8CMsYIwGXzDSC Ni9Y+7ABZkKVStzvyjywlMwfNCcdlH1bUL1gArB3vhuNLE93x+KNy9gycJbr56pLscMC /jqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699464128; x=1700068928; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ALaZJ6XsrsPVtrQi96mwWume01S/yCvLr5bXcq2qj94=; b=V8KX7WP26VAuv9cSz0vY10PG/NAF3h7Hf4WR1i8u90DFrjhcp2yXfZ6jrBFaBxz39v kP0skls2tA0U/g5wTl1Do62a7sz+4rLdFGZctolPiG8al1H4gHlCELdCr8nK3QaKdd6l YN41jqHm9B43Lzf26NURsrV6t8zejzJA0fKR/CS7xsGoHd0sRbqp9lNUPDCjV7ikxK5b 4o0AiXcXwj0PSyiKQhLqOe5tl+DK02fVYYrhyYD8QYMkFpuDs6E9sGy+rDMHJ6T3JcS+ D2j4/OmKeitDMkPfJHS8uEPBjbeLzDCX6lw/oZWJ/DexufruqBF7mKEccqvuTVXfrRMe Ju/A== X-Gm-Message-State: AOJu0Yzu7xmdCUE3wCgpioK4Q70OC/mvVybSwX+xPEv0vRrxjU/OaU/i 5t7mm+2hYRlKtf5Gjlzc+I/aDvW452c= X-Google-Smtp-Source: AGHT+IGmetNbzDXFZuaE0gtthCrha1bNkyquZOVoUlewKMMwtpEzAl8+MnHrpgufQVkG7wDQZLqJ/g== X-Received: by 2002:a05:6214:1c8d:b0:65b:ed3:9a02 with SMTP id ib13-20020a0562141c8d00b0065b0ed39a02mr2560267qvb.17.1699464128031; Wed, 08 Nov 2023 09:22:08 -0800 (PST) Received: from LOCLAP699.rst-02.locus (50-78-19-50-static.hfc.comcastbusiness.net. [50.78.19.50]) by smtp.gmail.com with ESMTPSA id qb1-20020ad44701000000b0065d03eae7a0sm1277003qvb.82.2023.11.08.09.22.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Nov 2023 09:22:07 -0800 (PST) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH v5 08/10] auto-t: add utils for wpa_supplicant PKEX Date: Wed, 8 Nov 2023 09:21:53 -0800 Message-Id: <20231108172155.2129509-9-prestwoj@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231108172155.2129509-1-prestwoj@gmail.com> References: <20231108172155.2129509-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- autotests/util/wpas.py | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/autotests/util/wpas.py b/autotests/util/wpas.py index 442f0dce..eab08b43 100644 --- a/autotests/util/wpas.py +++ b/autotests/util/wpas.py @@ -273,13 +273,17 @@ class Wpas: return self._dpp_uri - def dpp_configurator_create(self, uri): + def dpp_configurator_create(self, uri=None): self._rx_data = [] self._ctrl_request('DPP_CONFIGURATOR_ADD') self._dpp_conf_id = self.wait_for_result() while not self._dpp_conf_id.isnumeric(): self._dpp_conf_id = self.wait_for_result() + if not uri: + print("DPP Configurator ID: %s", self._dpp_conf_id) + return + self._rx_data = [] self._ctrl_request('DPP_QR_CODE ' + uri) self._dpp_qr_id = self.wait_for_result() @@ -302,6 +306,40 @@ class Wpas: self.wait_for_event('DPP-AUTH-SUCCESS', timeout=30) self.wait_for_event('DPP-CONF-SENT') + def dpp_bootstrap_gen(self, type='qrcode', curve=None): + cmd = f'DPP_BOOTSTRAP_GEN type={type}' + + if curve: + cmd += f' curve={curve}' + + self._rx_data = [] + self._ctrl_request(cmd) + self._dpp_id = self.wait_for_result() + + def dpp_pkex_add(self, code, identifier=None, version=None, initiator=False, role=None): + cmd = f'DPP_PKEX_ADD own={self._dpp_id}' + + if identifier: + cmd += f' identifier={identifier}' + + if initiator: + cmd += f' init=1' + + if version: + cmd += f' ver={version}' + + if role: + cmd += f' role={role}' + + cmd += f' code={code}' + + self._rx_data = [] + self._ctrl_request(cmd) + + def dpp_listen(self, freq): + self._rx_data = [] + self._ctrl_request(f'DPP_LISTEN {freq}') + def dpp_configurator_remove(self): self._ctrl_request('DPP_CONFIGURATOR_REMOVE *') self.wait_for_result() From patchwork Wed Nov 8 17:21:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13450353 Received: from mail-qv1-f50.google.com (mail-qv1-f50.google.com [209.85.219.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7BE1031A7F for ; Wed, 8 Nov 2023 17:22:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="e+dHa/dF" Received: by mail-qv1-f50.google.com with SMTP id 6a1803df08f44-671357f0209so46445126d6.0 for ; Wed, 08 Nov 2023 09:22:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1699464129; x=1700068929; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=IBeroSzBX6viLWO4InxSFlNOHYAqDC1wQME5BKDhn48=; b=e+dHa/dFVWO1FRboHDVlNXyOYmwyLDlGI0CwG2StkVF5lZcT+TFmmWGKsYCGPbR8qB Pg6rjdLwx1hLL9myHArk3FcXDBuuwroRmGFKtKs3sSXZyc/0k7H6Nt6M891YXH3PZEvV OC8othfH9dAggQC4z9EK37giqw2FTVxr1s8Qd/22qgabLExH9RYJ2vEmuJGFActHgwP2 4frzApd3hYwIFV93Kojgd7cbsMRBUH94UKIe6EvFjiENmW2IIs4izqc57T487jaVOC4q 5e3fjda/ElBO0E2J9WqK0Pi8nu5bs+rwjxMGBBqS31CKHOizFcFNgskHW8cPx5VolS1h qDOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699464129; x=1700068929; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=IBeroSzBX6viLWO4InxSFlNOHYAqDC1wQME5BKDhn48=; b=lZ4exI0FC1jZy1wf4c5pHnKgZ6R5xcpq5x3ZnuoewKOnk3qmCVw6sNbvTS+xap4EWa WTHex9IHPUCVGKZSzfwTIgiSHDoDbpjD6ekpgwa3d2fEaq7/yq+iBUrIZ54VY8YuWKYp YP//4/y8tPeQOp+C9fgfKVfIU4SFDuC/ZJ/ONZry3AQz5coXH8x9wChI3vSMD9zPFi7J MM7Bwxzv/4MgB3pnxFaw+cYqGXu85p6pbtsHRfM4F5mB3ejmwYsm2BI8bonY8ZWCWEJj 7vuLdzZNzP+1QC8Fvm/SE36mH3SfmJ2mfJQw9kP/Bqhsyqmt0wO7wFPRgIKeLYykH3CN 2H0Q== X-Gm-Message-State: AOJu0YxN7GaU46ggeEc+Bv+sLIW/ayHFMY0rKuahbRUmcfKF/QNtGiaH iywPrNJ0Laa/0Lv+tUHi5WIccyZP2eo= X-Google-Smtp-Source: AGHT+IFFLDduH9jYCMjP+Tt6RnzlP+/rCpzSaRG1QQh2IFv4xny1XFYrkBaXSYfJthQgMxpT5kyCbA== X-Received: by 2002:a05:6214:1bc6:b0:66d:5c10:cab7 with SMTP id m6-20020a0562141bc600b0066d5c10cab7mr3052282qvc.46.1699464129126; Wed, 08 Nov 2023 09:22:09 -0800 (PST) Received: from LOCLAP699.rst-02.locus (50-78-19-50-static.hfc.comcastbusiness.net. [50.78.19.50]) by smtp.gmail.com with ESMTPSA id qb1-20020ad44701000000b0065d03eae7a0sm1277003qvb.82.2023.11.08.09.22.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Nov 2023 09:22:08 -0800 (PST) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH v5 09/10] auto-t: add APIs for PKEX Date: Wed, 8 Nov 2023 09:21:54 -0800 Message-Id: <20231108172155.2129509-10-prestwoj@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231108172155.2129509-1-prestwoj@gmail.com> References: <20231108172155.2129509-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Also added some sanity checks to the existing DPP APIs to make sure started/role gets set correctly. --- autotests/util/iwd.py | 153 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 3 deletions(-) diff --git a/autotests/util/iwd.py b/autotests/util/iwd.py index 5fa12444..c0ce0817 100755 --- a/autotests/util/iwd.py +++ b/autotests/util/iwd.py @@ -42,6 +42,8 @@ IWD_P2P_SERVICE_MANAGER_INTERFACE = 'net.connman.iwd.p2p.ServiceManager' IWD_P2P_WFD_INTERFACE = 'net.connman.iwd.p2p.Display' IWD_STATION_DEBUG_INTERFACE = 'net.connman.iwd.StationDebug' IWD_DPP_INTERFACE = 'net.connman.iwd.DeviceProvisioning' +IWD_DPP_PKEX_INTERFACE = 'net.connman.iwd.SharedCodeDeviceProvisioning' +IWD_SHARED_CODE_AGENT_INTERFACE = 'net.connman.iwd.SharedCodeAgent' IWD_AGENT_MANAGER_PATH = '/net/connman/iwd' IWD_TOP_LEVEL_PATH = '/' @@ -212,6 +214,33 @@ class SignalAgent(dbus.service.Object): def handle_new_level(self, path, level): pass +class SharedCodeAgent(dbus.service.Object): + def __init__(self, codes = {}): + self._path = '/test/agent/' + str(int(round(time.time() * 1000))) + self._codes = codes + + dbus.service.Object.__init__(self, ctx.get_bus(), self._path) + + @property + def path(self): + return self._path + + @dbus.service.method(IWD_SHARED_CODE_AGENT_INTERFACE, + in_signature='', out_signature='') + def Release(self): + print("SharedCodeAgent released") + + @dbus.service.method(IWD_SHARED_CODE_AGENT_INTERFACE, + in_signature='s', out_signature='s') + def RequestSharedCode(self, identifier): + print("SharedCodeAgent request for %s" % identifier) + + code = self._codes.get(identifier, None) + if not code: + return NotFoundEx("No code found for %s" % identifier) + + return code + class AdHocDevice(IWDDBusAbstract): ''' Class represents an AdHoc device object: net.connman.iwd.AdHoc @@ -299,6 +328,49 @@ class DeviceProvisioning(IWDDBusAbstract): def role(self): return self._properties['Role'] +class SharedCodeDeviceProvisioning(IWDDBusAbstract): + _iface_name = IWD_DPP_PKEX_INTERFACE + + def start_enrollee(self, code, identifier=None): + args = { + "Code": code + } + + if identifier: + args["Identifier"] = identifier + + self._iface.StartEnrollee(args) + + def start_configurator(self, path): + self._iface.StartConfigurator(dbus.ObjectPath(path)) + + def configure_enrollee(self, code, identifier=None): + args = { + "Code": code + } + + if identifier: + args["Identifier"] = identifier + + self._iface.ConfigureEnrollee(args) + + def stop(self): + self._iface.Stop() + + def register_agent(self, path): + self._iface.RegisterSharedCodeAgent(path) + + def unregister_agent(self): + self._iface.UnregisterSharedCodeAgent() + + @property + def started(self): + return self._properties['Started'] + + @property + def role(self): + return self._properties['Role'] + class AccessPointDevice(IWDDBusAbstract): ''' Class represents net.connman.iwd.AccessPoint @@ -375,6 +447,7 @@ class Device(IWDDBusAbstract): self._station_props = None self._station_debug_obj = None self._dpp_obj = None + self._sc_dpp_obj = None self._ap_obj = None IWDDBusAbstract.__init__(self, *args, **kwargs) @@ -407,6 +480,17 @@ class Device(IWDDBusAbstract): return self._dpp_obj + @property + def _sc_device_provisioning(self): + if self._properties['Mode'] != 'station': + self._prop_proxy.Set(IWD_DEVICE_INTERFACE, 'Mode', 'station') + + if self._sc_dpp_obj is None: + self._sc_dpp_obj = SharedCodeDeviceProvisioning( + object_path=self._object_path, + namespace=self._namespace) + return self._sc_dpp_obj + @property def _station_debug(self): if self._properties['Mode'] != 'station': @@ -774,13 +858,76 @@ class Device(IWDDBusAbstract): self._station_debug.wait_for_event(event, timeout) def dpp_start_enrollee(self): - return self._device_provisioning.start_enrollee() + ret = self._device_provisioning.start_enrollee() + + condition = 'obj.started == True' + IWD._wait_for_object_condition(self._device_provisioning, condition) + condition = 'obj.role == "enrollee"' + IWD._wait_for_object_condition(self._device_provisioning, condition) + + return ret def dpp_start_configurator(self, uri=None): - return self._device_provisioning.start_configurator(uri) + ret = self._device_provisioning.start_configurator(uri) + + condition = 'obj.started == True' + IWD._wait_for_object_condition(self._device_provisioning, condition) + condition = 'obj.role == "configurator"' + IWD._wait_for_object_condition(self._device_provisioning, condition) + + return ret + + def dpp_pkex_enroll(self, *args, **kwargs): + ret = self._sc_device_provisioning.start_enrollee(*args, **kwargs) + + condition = 'obj.started == True' + IWD._wait_for_object_condition(self._sc_device_provisioning, condition) + condition = 'obj.role == "enrollee"' + IWD._wait_for_object_condition(self._sc_device_provisioning, condition) + + return ret + + def dpp_pkex_start_configurator(self, *args, **kwargs): + ret = self._sc_device_provisioning.start_configurator(*args, **kwargs) + + condition = 'obj.started == True' + IWD._wait_for_object_condition(self._sc_device_provisioning, condition) + condition = 'obj.role == "configurator"' + IWD._wait_for_object_condition(self._sc_device_provisioning, condition) + + return ret + + def dpp_pkex_configure_enrollee(self, *args, **kwargs): + ret = self._sc_device_provisioning.configure_enrollee(*args, **kwargs) + + condition = 'obj.started == True' + IWD._wait_for_object_condition(self._sc_device_provisioning, condition) + condition = 'obj.role == "configurator"' + IWD._wait_for_object_condition(self._sc_device_provisioning, condition) + + return ret + + def dpp_pkex_stop(self): + ret = self._sc_device_provisioning.stop() + + condition = 'obj.started == False' + IWD._wait_for_object_condition(self._sc_device_provisioning, condition) + + return ret + + def dpp_pkex_register_agent(self, path): + self._sc_device_provisioning.register_agent(path) + + def dpp_pkex_unregister_agent(self): + self._sc_device_provisioning.unregister_agent() def dpp_stop(self): - return self._device_provisioning.stop() + ret = self._device_provisioning.stop() + + condition = 'obj.started == False' + IWD._wait_for_object_condition(self._device_provisioning, condition) + + return ret def __str__(self, prefix = ''): s = prefix + 'Device: ' + self.device_path + '\n'\ From patchwork Wed Nov 8 17:21:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13450354 Received: from mail-qv1-f49.google.com (mail-qv1-f49.google.com [209.85.219.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B133F31A98 for ; Wed, 8 Nov 2023 17:22:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Nf5xiklr" Received: by mail-qv1-f49.google.com with SMTP id 6a1803df08f44-66d2f3bb312so43877306d6.0 for ; Wed, 08 Nov 2023 09:22:11 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1699464130; x=1700068930; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=+95VtRm+fHyfO/FXjON6dIw+MC2yYlRNoqPvueJrcmw=; b=Nf5xiklru41jh7DX8SHZ8yO/x5VJcViB5fsUmzlkhEfcjPDj5xY181F8AiX+WQlj7K fs4ye5CYoNyxkzD668R8APG+VLw4oFd/4759vMfSEDNKxfutm68xllLwG+2WKapc1AND qo35zwmopGKoftZ38sr6duXYufr12iki/4DAcsMUv4sFXPVTn4IWmnVt3iJfqkoXSki7 i3VVs9tALirOESn2nGxdg+EFgsDuO+JCwblBzss8J8WaOYYqeGPs3q7WRRMHpyUL43bY rSkskuMaYkHE+mJ7rZD/KAobytnxKY0sN26ID15r8auYSAY8MGGfDMJgVQdM9yecKLbi Et5Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1699464130; x=1700068930; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=+95VtRm+fHyfO/FXjON6dIw+MC2yYlRNoqPvueJrcmw=; b=RcAL8F45IS2epmAIC54l3Klqmw2m6YCUP4l7E+dVMyba/sEyDOln1w04Mm0az3XOQK D93anJPiRRMgXeuKuHJsqWvvzORXQ0kzvJtblzIeatJ8hVUYiq27m8h995QhrrDWhQ69 VD57lF8crbusqhET2RtpvYvghDC4FMPQK6ck8FELx98ZxOpZe2g2uFGLCNYOW0gCWaCj SslyrKc7ju95ptRgXHeksOxNf0FeN7M8lJAyogdm8mq3dA3v4BnirV8/Cdjhj2rvxg3J PujjEdMWeytopOKwXK2FVR+3TpKFTkIzVUAyJj9lJioHmlU/4hzZc6BXFsd/XGoFkkw4 wAVw== X-Gm-Message-State: AOJu0Yztb/0CcpMLvw1p8iNQtXr706CurcVZYzUQDqG16u6x2hueRXtK hQjzBVObDWN5fyEKOjGWIIeo6sk6Rv4= X-Google-Smtp-Source: AGHT+IEYeGzh7Rqpvq/UzRCjmSyaT1N4RYte5KHhGT7uejte+4RT0mTyCcohGnEIbAmPsQAJTqKABQ== X-Received: by 2002:ad4:576f:0:b0:66d:618a:b347 with SMTP id r15-20020ad4576f000000b0066d618ab347mr2716104qvx.14.1699464130192; Wed, 08 Nov 2023 09:22:10 -0800 (PST) Received: from LOCLAP699.rst-02.locus (50-78-19-50-static.hfc.comcastbusiness.net. [50.78.19.50]) by smtp.gmail.com with ESMTPSA id qb1-20020ad44701000000b0065d03eae7a0sm1277003qvb.82.2023.11.08.09.22.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 08 Nov 2023 09:22:10 -0800 (PST) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH v5 10/10] auto-t: add DPP PKEX tests Date: Wed, 8 Nov 2023 09:21:55 -0800 Message-Id: <20231108172155.2129509-11-prestwoj@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20231108172155.2129509-1-prestwoj@gmail.com> References: <20231108172155.2129509-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- autotests/testDPP/hostapd.conf | 2 +- autotests/testDPP/hw.conf | 5 +- autotests/testDPP/pkex_test.py | 209 +++++++++++++++++++++++++++++++++ autotests/testDPP/ssidCCMP.psk | 12 ++ autotests/util/iwd.py | 11 +- 5 files changed, 231 insertions(+), 8 deletions(-) create mode 100644 autotests/testDPP/pkex_test.py diff --git a/autotests/testDPP/hostapd.conf b/autotests/testDPP/hostapd.conf index 074e8228..3611933c 100644 --- a/autotests/testDPP/hostapd.conf +++ b/autotests/testDPP/hostapd.conf @@ -1,5 +1,5 @@ hw_mode=g -channel=1 +channel=6 ssid=ssidCCMP wpa=2 diff --git a/autotests/testDPP/hw.conf b/autotests/testDPP/hw.conf index 923595ea..a2b1470e 100644 --- a/autotests/testDPP/hw.conf +++ b/autotests/testDPP/hw.conf @@ -1,5 +1,5 @@ [SETUP] -num_radios=3 +num_radios=4 start_iwd=0 hwsim_medium=yes @@ -8,3 +8,6 @@ rad0=wpas.conf [HOSTAPD] rad1=hostapd.conf + +[NameSpaces] +ns0=rad2 diff --git a/autotests/testDPP/pkex_test.py b/autotests/testDPP/pkex_test.py new file mode 100644 index 00000000..a568e619 --- /dev/null +++ b/autotests/testDPP/pkex_test.py @@ -0,0 +1,209 @@ +#!/usr/bin/python3 + +import unittest +import sys + +sys.path.append('../util') +from iwd import IWD, SharedCodeAgent +from iwd import DeviceProvisioning +from wpas import Wpas +from hostapd import HostapdCLI +from hwsim import Hwsim +from config import ctx +from time import time +import os + +class Test(unittest.TestCase): + def start_wpas_pkex(self, code, curve=None, **kwargs): + self.wpas.dpp_bootstrap_gen(type='pkex', curve=curve) + self.wpas.dpp_pkex_add(code=code, **kwargs) + if kwargs.get('role', 'configurator') == 'configurator': + self.wpas.dpp_configurator_create() + self.wpas.dpp_listen(2437) + + def start_iwd_pkex_configurator(self, device, agent=False): + self.hapd.reload() + self.hapd.wait_for_event('AP-ENABLED') + + IWD.copy_to_storage('ssidCCMP.psk') + device.autoconnect = True + + condition = 'obj.state == DeviceState.connected' + self.wd.wait_for_object_condition(device, condition) + + if agent: + self.agent = SharedCodeAgent(codes = {"test": "secret123"}) + + device.dpp_pkex_start_configurator(self.agent.path) + else: + device.dpp_pkex_configure_enrollee('secret123', identifier="test") + + def test_pkex_iwd_as_enrollee(self): + self.start_wpas_pkex('secret123', identifier="test") + + self.device[0].dpp_pkex_enroll('secret123', identifier="test") + + self.wpas.wait_for_event("DPP-AUTH-SUCCESS") + + def test_pkex_iwd_as_enrollee_retransmit(self): + self.rule_reveal_req.enabled = True + + self.start_wpas_pkex('secret123', identifier="test") + + self.device[0].dpp_pkex_enroll('secret123', identifier="test") + + self.wpas.wait_for_event("DPP-AUTH-SUCCESS") + + def test_pkex_unsupported_version(self): + self.start_wpas_pkex('secret123', identifier="test", version=2) + + now = time() + self.device[0].dpp_pkex_enroll('secret123', identifier="test") + + condition = "obj.started == False" + self.wd.wait_for_object_condition(self.device[0]._sc_device_provisioning, + condition, max_wait=125) + + # Check the enrollee stopped after 2 minutes + elapsed = time() - now + self.assertLess(elapsed, 125) + + def test_pkex_configurator_timeout(self): + self.start_iwd_pkex_configurator(self.device[0]) + + now = time() + + condition = "obj.started == False" + self.wd.wait_for_object_condition(self.device[0]._sc_device_provisioning, + condition, max_wait=125) + + # Check the enrollee stopped after 2 minutes + elapsed = time() - now + self.assertLess(elapsed, 125) + + def test_pkex_iwd_as_configurator(self): + self.start_iwd_pkex_configurator(self.device[0]) + + self.start_wpas_pkex('secret123', identifier="test", initiator=True, + role='enrollee') + + self.wpas.wait_for_event("DPP-AUTH-SUCCESS") + self.wpas.wait_for_event("DPP-CONF-RECEIVED") + + def test_pkex_iwd_as_configurator_retransmit(self): + self.rule_xchg_resp.enabled = True + self.rule_reveal_resp.enabled = True + + self.start_iwd_pkex_configurator(self.device[0]) + + self.start_wpas_pkex('secret123', identifier="test", initiator=True, + role='enrollee') + + self.wpas.wait_for_event("DPP-AUTH-SUCCESS") + self.wpas.wait_for_event("DPP-CONF-RECEIVED") + + def test_pkex_iwd_as_configurator_bad_group(self): + self.start_iwd_pkex_configurator(self.device[0]) + + self.start_wpas_pkex('secret123', identifier="test", initiator=True, + role='enrollee', curve='P-384') + + self.wpas.wait_for_event(f"DPP-RX src={self.device[0].address} freq=2437 type=8") + self.wpas.wait_for_event("DPP-FAIL") + + def test_pkex_iwd_to_iwd(self): + self.start_iwd_pkex_configurator(self.device[0]) + + self.device[1].dpp_pkex_enroll('secret123', identifier="test") + + self.device[1].autoconnect = True + + condition = 'obj.state == DeviceState.connected' + self.wd.wait_for_object_condition(self.device[1], condition) + + self.assertTrue(os.path.exists('/tmp/ns0/ssidCCMP.psk')) + + with open('/tmp/ns0/ssidCCMP.psk') as f: + data = f.read() + + self.assertIn("SendHostname", data) + self.assertIn("SharedCode=secret123", data) + self.assertIn("ExactConfig=true", data) + + def test_pkex_configurator_with_agent(self): + self.start_iwd_pkex_configurator(self.device[0], agent=True) + + self.device[1].dpp_pkex_enroll('secret123', identifier="test") + + self.device[1].autoconnect = True + + condition = 'obj.state == DeviceState.connected' + self.wd.wait_for_object_condition(self.device[1], condition) + + self.agent = None + + def setUp(self): + ns0 = ctx.get_namespace('ns0') + self.wpas = Wpas('wpas.conf') + + self.wd = IWD(True) + self.wd_ns0 = IWD(True, iwd_storage_dir='/tmp/ns0', namespace=ns0) + self.device = [] + self.device.append(self.wd.list_devices(1)[0]) + self.device.append(self.wd_ns0.list_devices(1)[0]) + self.hapd = HostapdCLI('hostapd.conf') + self.hapd.disable() + self.hwsim = Hwsim() + + self.rule_xchg_resp = self.hwsim.rules.create() + self.rule_xchg_resp.prefix = 'd0' + self.rule_xchg_resp.match_offset = 24 + self.rule_xchg_resp.match = '04 09 50 6f 9a 1a 01 08' + self.rule_xchg_resp.match_times = 1 + self.rule_xchg_resp.drop = True + + self.rule_reveal_resp = self.hwsim.rules.create() + self.rule_reveal_resp.prefix = 'd0' + self.rule_reveal_resp.match_offset = 24 + self.rule_reveal_resp.match = '04 09 50 6f 9a 1a 01 0a' + self.rule_reveal_resp.match_times = 1 + self.rule_reveal_resp.drop = True + + self.rule_reveal_req = self.hwsim.rules.create() + self.rule_reveal_req.prefix = 'd0' + self.rule_reveal_req.match_offset = 24 + self.rule_reveal_req.match = '04 09 50 6f 9a 1a 01 09' + self.rule_reveal_req.match_times = 1 + self.rule_reveal_req.drop = True + + def tearDown(self): + # Tests end in various states, don't fail when tearing down. + try: + self.device[0].disconnect() + self.device[0].dpp_pkex_stop() + self.device[1].disconnect() + self.device[1].dpp_pkex_stop() + except: + pass + + self.wpas.dpp_configurator_remove() + self.wpas.clean_up() + + self.wd = None + self.wd_ns0 = None + self.device = None + self.wpas = None + self.hapd = None + self.rule_xchg_resp = None + IWD.clear_storage() + + @classmethod + def setUpClass(cls): + pass + + @classmethod + def tearDownClass(cls): + pass + +if __name__ == '__main__': + unittest.main(exit=True) \ No newline at end of file diff --git a/autotests/testDPP/ssidCCMP.psk b/autotests/testDPP/ssidCCMP.psk index abafdb66..d428fd34 100644 --- a/autotests/testDPP/ssidCCMP.psk +++ b/autotests/testDPP/ssidCCMP.psk @@ -1,2 +1,14 @@ [Security] Passphrase=secret123 + +[DeviceProvisioning] +SharedCode=secret123 +SharedCodeIdentifier=test +ExactConfig=true + +[IPv4] +SendHostname=true + +[Settings] +AutoConnect=true +Hidden=false diff --git a/autotests/util/iwd.py b/autotests/util/iwd.py index c0ce0817..3f200beb 100755 --- a/autotests/util/iwd.py +++ b/autotests/util/iwd.py @@ -230,6 +230,11 @@ class SharedCodeAgent(dbus.service.Object): def Release(self): print("SharedCodeAgent released") + @dbus.service.method(IWD_SHARED_CODE_AGENT_INTERFACE, + in_signature='s', out_signature='') + def Cancel(self, reason): + print("SharedCodeAgent canceled (%s)" % reason) + @dbus.service.method(IWD_SHARED_CODE_AGENT_INTERFACE, in_signature='s', out_signature='s') def RequestSharedCode(self, identifier): @@ -357,12 +362,6 @@ class SharedCodeDeviceProvisioning(IWDDBusAbstract): def stop(self): self._iface.Stop() - def register_agent(self, path): - self._iface.RegisterSharedCodeAgent(path) - - def unregister_agent(self): - self._iface.UnregisterSharedCodeAgent() - @property def started(self): return self._properties['Started']