From patchwork Wed Mar 27 15:19:49 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13606771 Received: from mail-ot1-f53.google.com (mail-ot1-f53.google.com [209.85.210.53]) (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 D83E528DDC for ; Wed, 27 Mar 2024 15:20:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552807; cv=none; b=KcI1crAfAK3u2E41Gfpz2zI1+iRODctpw9GoHy8vX2E+0po/KcJu3c/2Mq4n7HFtC5NODI/qOa0rYcfMs5Gs7e2jTW0wT9MMcDKhEUiV8rqT+I5OG3i8y+zxEKN3RTWQykrzbWtwbne+OfMj/PoRz0DfDQGP9ul8nY02cWLSdbU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552807; c=relaxed/simple; bh=jJ5esnnRNgXe3z3MIu5zboM+GnUmYAxAdp1sy3EHfXo=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=eH/JA+KZVFb7z8T1ElUIWeL1WiS+iQbw77G//b3Fp2gTpm13IPI6KPDg2kdjjyMEr9JiH1X+LcgX8I5VJqteGZUqCNpol2X8bOKz9gF9jj1JFs+XphApJNe7wd6+XsFQt+HYDHKg6SRTZ5wlqEburlQNVakaepOoZd7eJD4jcOY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=OuoAKYIf; arc=none smtp.client-ip=209.85.210.53 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="OuoAKYIf" Received: by mail-ot1-f53.google.com with SMTP id 46e09a7af769-6e6a5bd015dso3174687a34.2 for ; Wed, 27 Mar 2024 08:20:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711552802; x=1712157602; darn=lists.linux.dev; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=4JYrgMBb05/WBhrrCym7IMqYhMQgQYQ/GI1tbANiwLc=; b=OuoAKYIfaEM25uIGO2y9nNxLxmgFm0hs9EFKp4ZORypMAzielLRUZewj0Uc8tC3uXC HVVNL/hS89Di+f8chnSrMVy4n9i5j+H3jVN+tNZGbc6KlvNX/HOSkN6cMBwZ5ae4L0+Y vb094r0oRMKqbu30izktGKLQw+10s1HzXX2HnULK225J0uXKZKYRADtvQexrlMdixA0J YFd3tT2G4Wy+1+Z9bhWLztp8cX8LBY7bQULV032vAkLJOEiZwSTcHmYU9Zi9ICFR7o+j 43YuIaUtWPCA/d/nheKb+fYfmlzi5qJ7Qtapa0fMAK48O6KjuVCSdqGjLtTEZJtUxxHO u9Cw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711552802; x=1712157602; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=4JYrgMBb05/WBhrrCym7IMqYhMQgQYQ/GI1tbANiwLc=; b=TsfieqKoW+GUeeDkHw6vF8UhJKVLC8wtnph+5CwwCsLeh/7Qg7Ou5DkiLcZWaxi6yA ORKV6qjzv1F3aoia6B99smmwx5S2inhM+H9QcedmUE+6dL6HsoZk4mfiYeAU4J3Bx18b 7bMrAGb0qJxsMktbnrW06+KbqE3cugCZYKdU25yqSH9OTYyIuQZDuYy2aDV9kxoNpUO/ JqTvN+v8fB9NM2W3f8BLpMgkyyUQhCA/lExEaQ7ZGjFIl+RGEyw9NZEh9N3mvR1fS+IB 3ReInhY4LM4d30rSDl6hCnUxzuSIHogKe8G/EVbwZQfCxcV/DZyT9dBNsS2+kVp23INV yIug== X-Gm-Message-State: AOJu0YzUxyQWrIbMsYPJxTTJaL3Th1lYeDAY4afdgDsaFeikHxKILmtS lsNHS5hCykbm8/1pWek84psODVyabP+MSBjfE7uqP1PRvGyMwsXLVX4SsDzQ X-Google-Smtp-Source: AGHT+IF8beWLdeA7yyKpqG8zCMLCVWR0eAJbF5Vk8aFs2WBmlISK20IV+g2y+tQ0sbzRh2muzPUncg== X-Received: by 2002:a05:6808:13cc:b0:3c3:cde0:735e with SMTP id d12-20020a05680813cc00b003c3cde0735emr259864oiw.14.1711552802456; Wed, 27 Mar 2024 08:20:02 -0700 (PDT) Received: from LOCLAP699.rst-02.locus ([208.195.13.130]) by smtp.gmail.com with ESMTPSA id 7-20020a056214202700b006968ce99b7esm3520782qvf.34.2024.03.27.08.20.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Mar 2024 08:20:02 -0700 (PDT) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH 1/9] dpp: prep for moving AAD within dpp_append_wrapped_data Date: Wed, 27 Mar 2024 08:19:49 -0700 Message-Id: <20240327151957.1446149-1-prestwoj@gmail.com> X-Mailer: git-send-email 2.34.1 Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The AAD pointers for DPP are specific to the frame type. This is currently sorted out by the caller within the respective frame building functions but its quite unreadable. There are some comments but lots of magic numbers. This should be moved within the dpp_append_wrapped_data utility but the first step is to make the frame buffer continuous. This will allow the entire frame to be passed and dpp_append_wrapped_data can calculate the AAD offsets itself. --- src/dpp.c | 239 +++++++++++++++++++++++++----------------------------- 1 file changed, 112 insertions(+), 127 deletions(-) diff --git a/src/dpp.c b/src/dpp.c index 567fe8d2..5aac22a7 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -648,7 +648,7 @@ static void dpp_frame_retry(struct dpp_sm *dpp) static size_t dpp_build_header(const uint8_t *src, const uint8_t *dest, enum dpp_frame_type type, - uint8_t buf[static 32]) + uint8_t *buf) { uint8_t *ptr = buf + 24; @@ -672,7 +672,7 @@ static size_t dpp_build_header(const uint8_t *src, const uint8_t *dest, static size_t dpp_build_config_header(const uint8_t *src, const uint8_t *dest, uint8_t diag_token, - uint8_t buf[static 37]) + uint8_t *buf) { uint8_t *ptr = buf + 24; @@ -753,22 +753,21 @@ static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr) { const char *json = "{\"name\":\"IWD\",\"wi-fi_tech\":\"infra\"," "\"netRole\":\"sta\"}"; - struct iovec iov[3]; - uint8_t hdr[37]; - uint8_t attrs[512]; + struct iovec iov; + uint8_t frame[512]; size_t json_len = strlen(json); - uint8_t *ptr = attrs; + uint8_t *ptr = frame; + uint8_t *lptr; l_getrandom(&dpp->diag_token, 1); - iov[0].iov_len = dpp_build_config_header( - netdev_get_address(dpp->netdev), - addr, dpp->diag_token, hdr); - iov[0].iov_base = hdr; + ptr += dpp_build_config_header(netdev_get_address(dpp->netdev), + addr, dpp->diag_token, ptr); l_getrandom(dpp->e_nonce, dpp->nonce_len); /* length */ + lptr = ptr; ptr += 2; /* @@ -780,42 +779,39 @@ static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr) * In this case there is no query request/response fields, nor any * attributes besides wrapped data meaning zero AD components. */ - ptr += dpp_append_wrapped_data(NULL, 0, NULL, 0, ptr, sizeof(attrs), + ptr += dpp_append_wrapped_data(NULL, 0, NULL, 0, ptr, sizeof(frame), dpp->ke, dpp->key_len, 2, DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce, DPP_ATTR_CONFIGURATION_REQUEST, json_len, json); - l_put_le16(ptr - attrs - 2, attrs); + l_put_le16(ptr - lptr - 2, lptr); - iov[1].iov_base = attrs; - iov[1].iov_len = ptr - attrs; + iov.iov_base = frame; + iov.iov_len = ptr - frame; dpp->state = DPP_STATE_CONFIGURING; - dpp_send_frame(dpp, iov, 2, dpp->current_freq); + dpp_send_frame(dpp, &iov, 1, dpp->current_freq); } static void send_config_result(struct dpp_sm *dpp, const uint8_t *to) { - uint8_t hdr[32]; - struct iovec iov[2]; - uint8_t attrs[256]; - uint8_t *ptr = attrs; + struct iovec iov; + uint8_t frame[256]; + uint8_t *ptr = frame; uint8_t zero = 0; - iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev), to, - DPP_FRAME_CONFIGURATION_RESULT, hdr); - iov[0].iov_base = hdr; - - ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, 0, ptr, - sizeof(attrs), dpp->ke, dpp->key_len, 2, + ptr += dpp_build_header(netdev_get_address(dpp->netdev), to, + DPP_FRAME_CONFIGURATION_RESULT, ptr); + ptr += dpp_append_wrapped_data(frame + 26, 6, ptr, 0, ptr, + sizeof(frame), dpp->ke, dpp->key_len, 2, DPP_ATTR_STATUS, (size_t) 1, &zero, DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce); - iov[1].iov_base = attrs; - iov[1].iov_len = ptr - attrs; + iov.iov_base = frame; + iov.iov_len = ptr - frame; - dpp_send_frame(dpp, iov, 2, dpp->current_freq); + dpp_send_frame(dpp, &iov, 1, dpp->current_freq); } static void dpp_write_config(struct dpp_configuration *config, @@ -1162,18 +1158,20 @@ static void dpp_handle_config_response_frame(const struct mmpdu_header *frame, static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status) { _auto_(l_free) char *json = NULL; - struct iovec iov[3]; - uint8_t hdr[41]; - uint8_t attrs[512]; + struct iovec iov; + uint8_t frame[512]; size_t json_len; - uint8_t *ptr = hdr + 24; + uint8_t *ptr = frame; + uint8_t *lptr; + + memset(frame, 0, sizeof(frame)); - memset(hdr, 0, sizeof(hdr)); + l_put_le16(0x00d0, ptr); + memcpy(ptr + 4, dpp->peer_addr, 6); + memcpy(ptr + 10, netdev_get_address(dpp->netdev), 6); + memcpy(ptr + 16, broadcast, 6); - l_put_le16(0x00d0, hdr); - memcpy(hdr + 4, dpp->peer_addr, 6); - memcpy(hdr + 10, netdev_get_address(dpp->netdev), 6); - memcpy(hdr + 16, broadcast, 6); + ptr += 24; *ptr++ = 0x04; *ptr++ = 0x0b; @@ -1192,11 +1190,7 @@ static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status) *ptr++ = 0x1a; *ptr++ = 1; - iov[0].iov_base = hdr; - iov[0].iov_len = ptr - hdr; - - ptr = attrs; - + lptr = ptr; ptr += 2; /* length */ ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1); @@ -1211,26 +1205,26 @@ static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status) json = dpp_configuration_to_json(dpp->config); json_len = strlen(json); - ptr += dpp_append_wrapped_data(attrs + 2, ptr - attrs - 2, - NULL, 0, ptr, sizeof(attrs), + ptr += dpp_append_wrapped_data(lptr + 2, ptr - lptr - 2, + NULL, 0, ptr, sizeof(frame), dpp->ke, dpp->key_len, 2, DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce, DPP_ATTR_CONFIGURATION_OBJECT, json_len, json); } else - ptr += dpp_append_wrapped_data(attrs + 2, ptr - attrs - 2, - NULL, 0, ptr, sizeof(attrs), + ptr += dpp_append_wrapped_data(lptr + 2, ptr - lptr - 2, + NULL, 0, ptr, sizeof(frame), dpp->ke, dpp->key_len, 2, DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce); - l_put_le16(ptr - attrs - 2, attrs); + l_put_le16(ptr - lptr - 2, lptr); - iov[1].iov_base = attrs; - iov[1].iov_len = ptr - attrs; + iov.iov_base = frame; + iov.iov_len = ptr - frame; - dpp_send_frame(dpp, iov, 2, dpp->current_freq); + dpp_send_frame(dpp, &iov, 1, dpp->current_freq); } static bool dpp_check_config_header(const uint8_t *ptr) @@ -1498,13 +1492,13 @@ static void dpp_handle_config_result_frame(struct dpp_sm *dpp, */ static void send_authenticate_response(struct dpp_sm *dpp) { - uint8_t hdr[32]; - uint8_t attrs[512]; - uint8_t *ptr = attrs; + uint8_t frame[512]; + uint8_t *ptr = frame; + uint8_t *attrs; uint8_t status = DPP_STATUS_OK; uint64_t r_proto_key[L_ECC_MAX_DIGITS * 2]; uint8_t version = 2; - struct iovec iov[3]; + struct iovec iov; uint8_t wrapped2_plaintext[dpp->key_len + 4]; uint8_t wrapped2[dpp->key_len + 16 + 8]; size_t wrapped2_len; @@ -1512,11 +1506,10 @@ static void send_authenticate_response(struct dpp_sm *dpp) l_ecc_point_get_data(dpp->own_proto_public, r_proto_key, sizeof(r_proto_key)); - iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev), + ptr += dpp_build_header(netdev_get_address(dpp->netdev), dpp->peer_addr, - DPP_FRAME_AUTHENTICATION_RESPONSE, hdr); - iov[0].iov_base = hdr; - + DPP_FRAME_AUTHENTICATION_RESPONSE, ptr); + attrs = ptr; ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1); ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, dpp->own_boot_hash, 32); @@ -1545,17 +1538,17 @@ static void send_authenticate_response(struct dpp_sm *dpp) wrapped2_len += 16; - ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, ptr - attrs, - ptr, sizeof(attrs), dpp->k2, dpp->key_len, 4, + ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs, + ptr, sizeof(frame), dpp->k2, dpp->key_len, 4, DPP_ATTR_RESPONDER_NONCE, dpp->nonce_len, dpp->r_nonce, DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, DPP_ATTR_RESPONDER_CAPABILITIES, (size_t) 1, &dpp->role, DPP_ATTR_WRAPPED_DATA, wrapped2_len, wrapped2); - iov[1].iov_base = attrs; - iov[1].iov_len = ptr - attrs; + iov.iov_base = frame; + iov.iov_len = ptr - frame; - dpp_send_frame(dpp, iov, 2, dpp->current_freq); + dpp_send_frame(dpp, &iov, 1, dpp->current_freq); } static void authenticate_confirm(struct dpp_sm *dpp, const uint8_t *from, @@ -1699,34 +1692,33 @@ static void dpp_auth_request_failed(struct dpp_sm *dpp, enum dpp_status status, void *k1) { - uint8_t hdr[32]; - uint8_t attrs[128]; - uint8_t *ptr = attrs; + uint8_t frame[128]; + uint8_t *ptr = frame; + uint8_t *attrs; uint8_t version = 2; uint8_t s = status; - struct iovec iov[2]; + struct iovec iov; - iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev), + ptr += dpp_build_header(netdev_get_address(dpp->netdev), dpp->peer_addr, - DPP_FRAME_AUTHENTICATION_RESPONSE, hdr); - iov[0].iov_base = hdr; - + DPP_FRAME_AUTHENTICATION_RESPONSE, ptr); + attrs = ptr; ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &s, 1); ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, dpp->own_boot_hash, 32); ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1); - ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, ptr - attrs, - ptr, sizeof(attrs) - (ptr - attrs), k1, dpp->key_len, 2, + ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs, + ptr, sizeof(frame) - (ptr - attrs), k1, dpp->key_len, 2, DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, DPP_ATTR_RESPONDER_CAPABILITIES, (size_t) 1, &dpp->role); - iov[1].iov_base = attrs; - iov[1].iov_len = ptr - attrs; + iov.iov_base = frame; + iov.iov_len = ptr - frame; - dpp_send_frame(dpp, iov, 2, dpp->current_freq); + dpp_send_frame(dpp, &iov, 1, dpp->current_freq); } static bool dpp_check_roles(struct dpp_sm *dpp, uint8_t peer_capa) @@ -1771,12 +1763,12 @@ static void dpp_presence_announce(struct dpp_sm *dpp) static bool dpp_send_authenticate_request(struct dpp_sm *dpp) { - uint8_t hdr[32]; - uint8_t attrs[256]; - uint8_t *ptr = attrs; + uint8_t frame[256]; + uint8_t *ptr = frame; + uint8_t *attrs; uint64_t i_proto_key[L_ECC_MAX_DIGITS * 2]; uint8_t version = 2; - struct iovec iov[2]; + struct iovec iov; struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); struct scan_bss *bss = station_get_connected_bss(station); @@ -1789,10 +1781,10 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp) l_ecc_point_get_data(dpp->own_proto_public, i_proto_key, sizeof(i_proto_key)); - iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev), + ptr += dpp_build_header(netdev_get_address(dpp->netdev), dpp->peer_addr, - DPP_FRAME_AUTHENTICATION_REQUEST, hdr); - iov[0].iov_base = hdr; + DPP_FRAME_AUTHENTICATION_REQUEST, ptr); + attrs = ptr; ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, dpp->peer_boot_hash, 32); @@ -1810,16 +1802,16 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp) ptr += dpp_append_attr(ptr, DPP_ATTR_CHANNEL, pair, 2); } - ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, ptr - attrs, - ptr, sizeof(attrs), dpp->k1, dpp->key_len, 2, + ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs, + ptr, sizeof(frame), dpp->k1, dpp->key_len, 2, DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, DPP_ATTR_INITIATOR_CAPABILITIES, (size_t) 1, &dpp->role); - iov[1].iov_base = attrs; - iov[1].iov_len = ptr - attrs; + iov.iov_base = frame; + iov.iov_len = ptr - frame; - dpp_send_frame(dpp, iov, 2, dpp->current_freq); + dpp_send_frame(dpp, &iov, 1, dpp->current_freq); return true; } @@ -1862,31 +1854,28 @@ static void dpp_send_pkex_exchange_request(struct dpp_sm *dpp) 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; + struct iovec iov; + uint8_t frame[512]; + uint8_t *ptr = frame; 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), + ptr += 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, + ptr); + ptr += dpp_append_wrapped_data(frame + 26, 6, &zero, 1, ptr, + sizeof(frame), 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; + iov.iov_base = frame; + iov.iov_len = ptr - frame; - dpp_send_frame(dpp, iov, 2, dpp->current_freq); + dpp_send_frame(dpp, &iov, 1, dpp->current_freq); } static void dpp_roc_started(void *user_data) @@ -2272,17 +2261,16 @@ auth_request_failed: static void dpp_send_authenticate_confirm(struct dpp_sm *dpp) { - uint8_t hdr[32]; - struct iovec iov[2]; - uint8_t attrs[256]; - uint8_t *ptr = attrs; + struct iovec iov; + uint8_t frame[256]; + uint8_t *ptr = frame; + uint8_t *attrs; uint8_t zero = 0; - iov[0].iov_len = dpp_build_header(netdev_get_address(dpp->netdev), + ptr += dpp_build_header(netdev_get_address(dpp->netdev), dpp->peer_addr, - DPP_FRAME_AUTHENTICATION_CONFIRM, hdr); - iov[0].iov_base = hdr; - + DPP_FRAME_AUTHENTICATION_CONFIRM, ptr); + attrs = ptr; ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &zero, 1); ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, dpp->peer_boot_hash, 32); @@ -2290,15 +2278,15 @@ static void dpp_send_authenticate_confirm(struct dpp_sm *dpp) ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, dpp->own_boot_hash, 32); - ptr += dpp_append_wrapped_data(hdr + 26, 6, attrs, ptr - attrs, ptr, - sizeof(attrs), dpp->ke, dpp->key_len, 1, + ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs, ptr, + sizeof(frame), dpp->ke, dpp->key_len, 1, DPP_ATTR_INITIATOR_AUTH_TAG, dpp->key_len, dpp->auth_tag); - iov[1].iov_base = attrs; - iov[1].iov_len = ptr - attrs; + iov.iov_base = frame; + iov.iov_len = ptr - frame; - dpp_send_frame(dpp, iov, 2, dpp->current_freq); + dpp_send_frame(dpp, &iov, 1, dpp->current_freq); } static void authenticate_response(struct dpp_sm *dpp, const uint8_t *from, @@ -3311,11 +3299,10 @@ bad_group: 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 frame[256]; + uint8_t *ptr = frame; uint8_t one = 1; - struct iovec iov[2]; + struct iovec iov; const uint8_t *own_mac = netdev_get_address(dpp->netdev); uint8_t b_pub[L_ECC_POINT_MAX_BYTES]; size_t b_len; @@ -3323,19 +3310,17 @@ static void dpp_send_commit_reveal_response(struct dpp_sm *dpp, 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, + ptr += dpp_build_header(own_mac, dpp->peer_addr, + DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE, ptr); + ptr += dpp_append_wrapped_data(frame + 26, 6, &one, 1, ptr, + sizeof(frame), 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; + iov.iov_base = frame; + iov.iov_len = ptr - frame; - dpp_send_frame(dpp, iov, 2, dpp->current_freq); + dpp_send_frame(dpp, &iov, 1, dpp->current_freq); } static void dpp_handle_pkex_commit_reveal_request(struct dpp_sm *dpp, From patchwork Wed Mar 27 15:19:50 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13606772 Received: from mail-qv1-f44.google.com (mail-qv1-f44.google.com [209.85.219.44]) (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 29B921EF0D for ; Wed, 27 Mar 2024 15:20:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552807; cv=none; b=pOIBGgZpbFo1jo09XagRzOeqXlDd7FqyBJxsq7Lwpt3j8dF95Ahm1XBTaQo/R7+RjPB/wjJSm/U1td7hxYYnx7r0yWf9y1Ewe2xIyiA8j2DNue+FxXUqfrqleYI3qANVhRIwUAS5dgoqU3IgFrroDxuubpKckOgd3/voCXnTQ+U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552807; c=relaxed/simple; bh=sFTQKNf5ZO84V2j7VJzumOiE8EsS7sMgoo117zfDSZQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=oyGKhh3IV1ztEsAZRuTqG7GUI0nd4Bv30ahvp7HBv6dqbR9XAEN7hmAIArMjt3VdJaCHjnFZ59NA05I5Yw8qv9TB++CPupn3OamHFMK2edqOO8MB/CQ+14vTAw+M2oP/ZUtBC0K2ZoG+yvTUkVaKfv0v384y9KjCRSd+MJgla9g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=O9nQGMSz; arc=none smtp.client-ip=209.85.219.44 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="O9nQGMSz" Received: by mail-qv1-f44.google.com with SMTP id 6a1803df08f44-695df25699fso56883776d6.2 for ; Wed, 27 Mar 2024 08:20:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711552804; x=1712157604; 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=I2pOd3LND5n34HOza4BfVKn2HqMkXWPIWVe5Ll/LzXg=; b=O9nQGMSzUDrCpU941xoa9aLeXNYXlcoQlRBK7jPGdRIpSmAJFrI3H+e06pkGQbFSyp BWOYIQQ0KwDzf/pYwRVELoT4hrwkP4wEn6LzEojlVEgtiRaC1n90dR8CCsKPTe2PVD37 VXcdC7QyN0sGJDrFSBr1SmznxVi9RA65+N4iVRLgeT2EltEAcYFzq7Kvk646hHvGyU9W Oj+ECdOWb+YgHODpVLIJ8c3Dulv3XAZJFGJBGZ98PUhgeUsyVnWQmUgoFJGEXwUvPLwQ wdnpNlkpZhZrZvMr7cFEx8pWUNxdXtxqhX2K09Lwew0g7aSRiEczA7/TsFZiqmkI1EEG 8jXA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711552804; x=1712157604; 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=I2pOd3LND5n34HOza4BfVKn2HqMkXWPIWVe5Ll/LzXg=; b=kKRpO5tU8SokOSsYt0pitMu/IOfFap1sGW0yye/1iQATB9oD/R849aoZ0cF3Ruap7t is5SioMicp0Mla4mZKYe4pZs9YtJxNeZCvYUjJo5HrikDdChTYifGOS8Wenw5U8LjC1x 3ftd7FTfZMXF9FwxbTRuOwEdP8429KwIvmte5ZMxGJ839GLq9h0L/swoEowR5OITUzPW 3dsZqiUOrQFJxmnCwcX3A68IsxMT8IxbHZ+cWXCUIlti32vF01AIpjxGJEdNmpuOPt3k VfNNIac/mluJbK4e9OxVwRTv3VnloAk25CNa/Xo5hGOrg9TXlE2THu1vgm3nwSY+MkYJ wIbw== X-Gm-Message-State: AOJu0Yw6i7mI6dOPh4Y4nuJm6kh5QdH9qvoz/wwl/yW/0ijIMkpBejUO e18gzZfcIlwPGowhsP4Qzi0xR6PtW+JMAx1OIzaXI+4vdus05KCptW1qLNMP X-Google-Smtp-Source: AGHT+IGLsHRM1dBcFmblVWE7TW3WX6chzrjvwWMcfVu3e/xMP2nhto7jeGxBb4OxoSnIoRM7O+HbvQ== X-Received: by 2002:a05:6214:1c49:b0:690:a66b:268e with SMTP id if9-20020a0562141c4900b00690a66b268emr1878466qvb.57.1711552803711; Wed, 27 Mar 2024 08:20:03 -0700 (PDT) Received: from LOCLAP699.rst-02.locus ([208.195.13.130]) by smtp.gmail.com with ESMTPSA id 7-20020a056214202700b006968ce99b7esm3520782qvf.34.2024.03.27.08.20.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Mar 2024 08:20:03 -0700 (PDT) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH 2/9] dpp-util: move AAD logic within dpp_append_wrapped_attributes Date: Wed, 27 Mar 2024 08:19:50 -0700 Message-Id: <20240327151957.1446149-2-prestwoj@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240327151957.1446149-1-prestwoj@gmail.com> References: <20240327151957.1446149-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Leaving it up to the caller to calcluate the AAD resulted in lots of magic values, and any comments associated are spread out within dpp.c. The AAD values can be calculated entirely by the frame contents so move that within dpp_append_wrapped_data. The caller now only needs to pass the frame (after the mpdu header), the length, and the offset to where the wrapped data should start. The new AAD calculation includes all relavent comments so magic offsets are documented. The reason the entire mmpdu_body is not passed to dpp_append_wrapped_attributes (and one byte further) is to future proof for DPP encapsulation using TCP. For this, the category byte is omitted and only the action byte and further is encapsulated. Having dpp_append_wrapped_attributes start at the action byte allows it to work regardless of 8021x or TCP encapsulation. --- src/dpp-util.c | 167 +++++++++++++++++++++++++++++++++++++++++++++---- src/dpp-util.h | 5 +- src/dpp.c | 99 +++++++++++++++++------------ 3 files changed, 218 insertions(+), 53 deletions(-) diff --git a/src/dpp-util.c b/src/dpp-util.c index cfdedbdd..ada7ed96 100644 --- a/src/dpp-util.c +++ b/src/dpp-util.c @@ -39,6 +39,11 @@ #include "ell/asn1-private.h" #include "src/ie.h" +#define DPP_ACTION_VENDOR_SPECIFIC 0x09 +#define DPP_ACTION_GAS_REQUEST 0x0a +#define DPP_ACTION_GAS_RESPONSE 0x0b +#define DPP_HDR_LEN 6 + /* WFA Easy Connect v3.0 C.1 Role-specific Elements for NIST p256 */ static const uint8_t dpp_pkex_initiator_p256[64] = { /* X */ @@ -463,22 +468,91 @@ uint8_t *dpp_unwrap_attr(const void *ad0, size_t ad0_len, const void *ad1, return unwrapped; } +static bool dpp_aad(const uint8_t *frame, size_t frame_len, uint8_t *to, + const uint8_t **ad0, size_t *ad0_len, + const uint8_t **ad1, size_t *ad1_len) +{ + /* For PKEX frames */ + static uint8_t zero = 0; + static uint8_t one = 1; + enum dpp_frame_type type; + /* OUI field (inclusive) */ + const uint8_t *start = frame + 1; + + if (frame_len < 6) + return false; + + type = l_get_u8(frame + 6); + + switch (type) { + + case DPP_FRAME_AUTHENTICATION_REQUEST: + case DPP_FRAME_AUTHENTICATION_RESPONSE: + case DPP_FRAME_AUTHENTICATION_CONFIRM: + case DPP_FRAME_CONFIGURATION_RESULT: + /* + * Section 6.3.1.4 Protocol Conventions + * All other invocations of AES-SIV in the DPP Authentication + * protocol shall pass a vector of AAD having two components of + * AAD in the following order: + * (1) the DPP header, as defined in Table 34, from the OUI + * field (inclusive) to the DPP Frame Type field + * (inclusive); and + * (2) all octets in a DPP Public Action frame after the DPP + * Frame Type field up to and including the last octet + * of the last attribute before the Wrapped Data + * attribute + * + * Note: The configuration result frame uses identical wordage + * but is in Section 6.4.1 + */ + *ad0 = start; + *ad0_len = DPP_HDR_LEN; + *ad1 = start + DPP_HDR_LEN; + *ad1_len = to - start - DPP_HDR_LEN; + return true; + case DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST: + /* + * The AAD for this operation shall consist of two components: + * (1) the DPP header, as defined in Table 34, from the OUI + * field (inclusive) to the DPP Frame Type field + * (inclusive); and + * (2) a single octet of the value zero + */ + *ad0 = start; + *ad0_len = DPP_HDR_LEN; + *ad1 = &zero; + *ad1_len = 1; + return true; + case DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE: + /* + * The AAD for this operation shall consist of two components: + * (1) the DPP header, as defined in Table 34, from the OUI + * field (inclusive) to the DPP Frame Type field + * (inclusive); and + * (2) a single octet of the value one + */ + *ad0 = start; + *ad0_len = DPP_HDR_LEN; + *ad1 = &one; + *ad1_len = 1; + return true; + default: + return false; + } +} + /* - * Encrypt DPP attributes encapsulated in DPP wrapped data. - * - * ad0/ad0_len - frame specific AD0 component - * ad1/ad0_len - frame specific AD1 component - * to - buffer to encrypt data. - * to_len - size of 'to' + * frame - start of action frame (excluding mpdu header and category) + * frame_len - total frame buffer size + * to - current position of DPP attributes (where wrapped data will start) * key - key used to encrypt * key_len - size of 'key' * num_attrs - number of attributes listed (type, length, data triplets) * ... - List of attributes, Type, Length, and data */ -size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len, - const void *ad1, size_t ad1_len, - uint8_t *to, size_t to_len, - const void *key, size_t key_len, +size_t dpp_append_wrapped_data(const uint8_t *frame, size_t frame_len, + uint8_t *to, const void *key, size_t key_len, size_t num_attrs, ...) { size_t i; @@ -488,6 +562,77 @@ size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len, struct iovec ad[2]; size_t ad_size = 0; va_list va; + uint8_t action; + const uint8_t *ad0 = NULL; + const uint8_t *ad1 = NULL; + size_t ad0_len, ad1_len; + + /* + * First determine the frame type. This could be passed in but due to + * The config protocol using GAS request/response frames not all frames + * map to a dpp_frame_type enum. Due to this, minimal parsing is done + * on the frame to determine the type, and in turn the AAD + * offsets/lengths. + */ + if (frame_len < 1) + return 0; + + action = *frame; + + switch (action) { + case DPP_ACTION_VENDOR_SPECIFIC: + if (!dpp_aad(frame, frame_len, to, &ad0, &ad0_len, + &ad1, &ad1_len)) + return 0; + + break; + /* + * Section 6.4.1 Overview + * + * "AAD for use with AES-SIV for protected messages in the DPP + * Configuration protocol shall consist of all octets in the + * Query Request and Query Response fields up to the first octet + * of the Wrapped Data attribute, which is the last attribute in a DPP + * Configuration frame. When the number of octets of AAD is zero, the + * number of components of AAD passed to AES-SIV is zero + */ + case DPP_ACTION_GAS_REQUEST: + /* + * 8.3.2 DPP Configuration Request frame + * The attributes begin 14 bytes after the action (inclusive) + */ + if (frame_len < 14) + return 0; + + /* Start of query request */ + ad0 = frame + 14; + /* "up to the first octet of the Wrapped Data attribute" */ + ad0_len = to - frame - 14; + + if (!ad0_len) + ad0 = NULL; + + break; + case DPP_ACTION_GAS_RESPONSE: + /* + * 8.3.3 DPP Configuration Response frame + * The attributes begin 18 bytes after the action (inclusive) + */ + if (frame_len < 18) + return 0; + + /* Start of query response */ + ad0 = frame + 18; + /* "up to the first octet of the Wrapped Data attribute" */ + ad0_len = to - frame - 18; + + if (!ad0_len) + ad0 = NULL; + + break; + default: + return 0; + } va_start(va, num_attrs); @@ -500,7 +645,7 @@ size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len, va_end(va); - if (to_len < attrs_len + 4 + 16) + if (frame_len - (to - frame) < attrs_len + 4 + 16) return false; plaintext = l_malloc(attrs_len); diff --git a/src/dpp-util.h b/src/dpp-util.h index dc8a894b..387750aa 100644 --- a/src/dpp-util.h +++ b/src/dpp-util.h @@ -148,9 +148,8 @@ uint8_t *dpp_unwrap_attr(const void *ad0, size_t ad0_len, const void *ad1, size_t *unwrapped_len); size_t dpp_append_attr(uint8_t *to, enum dpp_attribute_type type, void *attr, size_t attr_len); -size_t dpp_append_wrapped_data(const void *ad0, size_t ad0_len, const void *ad1, - size_t ad1_len, uint8_t *to, size_t to_len, - const void *key, size_t key_len, +size_t dpp_append_wrapped_data(const uint8_t *frame, size_t frame_len, + uint8_t *to, const void *key, size_t key_len, size_t num_attrs, ...); char *dpp_generate_uri(const uint8_t *asn1, size_t asn1_len, uint8_t version, diff --git a/src/dpp.c b/src/dpp.c index 5aac22a7..d710aa98 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -758,6 +758,9 @@ static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr) size_t json_len = strlen(json); uint8_t *ptr = frame; uint8_t *lptr; + struct mmpdu_header *hdr = (struct mmpdu_header *)frame; + + memset(frame, 0, sizeof(frame)); l_getrandom(&dpp->diag_token, 1); @@ -779,7 +782,8 @@ static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr) * In this case there is no query request/response fields, nor any * attributes besides wrapped data meaning zero AD components. */ - ptr += dpp_append_wrapped_data(NULL, 0, NULL, 0, ptr, sizeof(frame), + ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, + sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, dpp->ke, dpp->key_len, 2, DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce, DPP_ATTR_CONFIGURATION_REQUEST, json_len, json); @@ -800,11 +804,15 @@ static void send_config_result(struct dpp_sm *dpp, const uint8_t *to) uint8_t frame[256]; uint8_t *ptr = frame; uint8_t zero = 0; + struct mmpdu_header *hdr = (struct mmpdu_header *)frame; + + memset(frame, 0, sizeof(frame)); ptr += dpp_build_header(netdev_get_address(dpp->netdev), to, DPP_FRAME_CONFIGURATION_RESULT, ptr); - ptr += dpp_append_wrapped_data(frame + 26, 6, ptr, 0, ptr, - sizeof(frame), dpp->ke, dpp->key_len, 2, + ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, + sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, + dpp->ke, dpp->key_len, 2, DPP_ATTR_STATUS, (size_t) 1, &zero, DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce); @@ -1163,6 +1171,7 @@ static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status) size_t json_len; uint8_t *ptr = frame; uint8_t *lptr; + struct mmpdu_header *hdr = (struct mmpdu_header *)frame; memset(frame, 0, sizeof(frame)); @@ -1205,19 +1214,19 @@ static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status) json = dpp_configuration_to_json(dpp->config); json_len = strlen(json); - ptr += dpp_append_wrapped_data(lptr + 2, ptr - lptr - 2, - NULL, 0, ptr, sizeof(frame), - dpp->ke, dpp->key_len, 2, - DPP_ATTR_ENROLLEE_NONCE, - dpp->nonce_len, dpp->e_nonce, - DPP_ATTR_CONFIGURATION_OBJECT, - json_len, json); + ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, + sizeof(frame) - mmpdu_header_len(hdr) - 1, + ptr, dpp->ke, dpp->key_len, 2, + DPP_ATTR_ENROLLEE_NONCE, + dpp->nonce_len, dpp->e_nonce, + DPP_ATTR_CONFIGURATION_OBJECT, + json_len, json); } else - ptr += dpp_append_wrapped_data(lptr + 2, ptr - lptr - 2, - NULL, 0, ptr, sizeof(frame), - dpp->ke, dpp->key_len, 2, - DPP_ATTR_ENROLLEE_NONCE, - dpp->nonce_len, dpp->e_nonce); + ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, + sizeof(frame) - mmpdu_header_len(hdr) - 1, + ptr, dpp->ke, dpp->key_len, 2, + DPP_ATTR_ENROLLEE_NONCE, + dpp->nonce_len, dpp->e_nonce); l_put_le16(ptr - lptr - 2, lptr); @@ -1494,7 +1503,6 @@ static void send_authenticate_response(struct dpp_sm *dpp) { uint8_t frame[512]; uint8_t *ptr = frame; - uint8_t *attrs; uint8_t status = DPP_STATUS_OK; uint64_t r_proto_key[L_ECC_MAX_DIGITS * 2]; uint8_t version = 2; @@ -1502,6 +1510,9 @@ static void send_authenticate_response(struct dpp_sm *dpp) uint8_t wrapped2_plaintext[dpp->key_len + 4]; uint8_t wrapped2[dpp->key_len + 16 + 8]; size_t wrapped2_len; + struct mmpdu_header *hdr = (struct mmpdu_header *)frame; + + memset(frame, 0, sizeof(frame)); l_ecc_point_get_data(dpp->own_proto_public, r_proto_key, sizeof(r_proto_key)); @@ -1509,7 +1520,6 @@ static void send_authenticate_response(struct dpp_sm *dpp) ptr += dpp_build_header(netdev_get_address(dpp->netdev), dpp->peer_addr, DPP_FRAME_AUTHENTICATION_RESPONSE, ptr); - attrs = ptr; ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1); ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, dpp->own_boot_hash, 32); @@ -1538,8 +1548,9 @@ static void send_authenticate_response(struct dpp_sm *dpp) wrapped2_len += 16; - ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs, - ptr, sizeof(frame), dpp->k2, dpp->key_len, 4, + ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, + sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, + dpp->k2, dpp->key_len, 4, DPP_ATTR_RESPONDER_NONCE, dpp->nonce_len, dpp->r_nonce, DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, DPP_ATTR_RESPONDER_CAPABILITIES, (size_t) 1, &dpp->role, @@ -1694,23 +1705,25 @@ static void dpp_auth_request_failed(struct dpp_sm *dpp, { uint8_t frame[128]; uint8_t *ptr = frame; - uint8_t *attrs; uint8_t version = 2; uint8_t s = status; struct iovec iov; + struct mmpdu_header *hdr = (struct mmpdu_header *)frame; + + memset(frame, 0, sizeof(frame)); ptr += dpp_build_header(netdev_get_address(dpp->netdev), dpp->peer_addr, DPP_FRAME_AUTHENTICATION_RESPONSE, ptr); - attrs = ptr; ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &s, 1); ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, dpp->own_boot_hash, 32); ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1); - ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs, - ptr, sizeof(frame) - (ptr - attrs), k1, dpp->key_len, 2, + ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, + sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, + k1, dpp->key_len, 2, DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, DPP_ATTR_RESPONDER_CAPABILITIES, (size_t) 1, &dpp->role); @@ -1765,12 +1778,14 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp) { uint8_t frame[256]; uint8_t *ptr = frame; - uint8_t *attrs; uint64_t i_proto_key[L_ECC_MAX_DIGITS * 2]; uint8_t version = 2; struct iovec iov; struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); struct scan_bss *bss = station_get_connected_bss(station); + struct mmpdu_header *hdr = (struct mmpdu_header *)frame; + + memset(frame, 0, sizeof(frame)); /* Got disconnected by the time the peer was discovered */ if (dpp->role == DPP_CAPABILITY_CONFIGURATOR && !bss) { @@ -1784,8 +1799,6 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp) ptr += dpp_build_header(netdev_get_address(dpp->netdev), dpp->peer_addr, DPP_FRAME_AUTHENTICATION_REQUEST, ptr); - attrs = ptr; - ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, dpp->peer_boot_hash, 32); ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, @@ -1802,8 +1815,9 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp) ptr += dpp_append_attr(ptr, DPP_ATTR_CHANNEL, pair, 2); } - ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs, - ptr, sizeof(frame), dpp->k1, dpp->key_len, 2, + ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, + sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, + dpp->k1, dpp->key_len, 2, DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, DPP_ATTR_INITIATOR_CAPABILITIES, (size_t) 1, &dpp->role); @@ -1857,9 +1871,11 @@ static void dpp_send_commit_reveal_request(struct dpp_sm *dpp) struct iovec iov; uint8_t frame[512]; uint8_t *ptr = frame; - uint8_t zero = 0; uint8_t a_pub[L_ECC_POINT_MAX_BYTES]; ssize_t a_len; + struct mmpdu_header *hdr = (struct mmpdu_header *)frame; + + memset(frame, 0, sizeof(frame)); a_len = l_ecc_point_get_data(dpp->boot_public, a_pub, sizeof(a_pub)); @@ -1867,8 +1883,9 @@ static void dpp_send_commit_reveal_request(struct dpp_sm *dpp) dpp->peer_addr, DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST, ptr); - ptr += dpp_append_wrapped_data(frame + 26, 6, &zero, 1, ptr, - sizeof(frame), dpp->z, dpp->z_len, 2, + ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, + sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, + dpp->z, dpp->z_len, 2, DPP_ATTR_BOOTSTRAPPING_KEY, a_len, a_pub, DPP_ATTR_INITIATOR_AUTH_TAG, dpp->u_len, dpp->u); @@ -2264,13 +2281,14 @@ static void dpp_send_authenticate_confirm(struct dpp_sm *dpp) struct iovec iov; uint8_t frame[256]; uint8_t *ptr = frame; - uint8_t *attrs; uint8_t zero = 0; + struct mmpdu_header *hdr = (struct mmpdu_header *)frame; + + memset(frame, 0, sizeof(frame)); ptr += dpp_build_header(netdev_get_address(dpp->netdev), dpp->peer_addr, DPP_FRAME_AUTHENTICATION_CONFIRM, ptr); - attrs = ptr; ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &zero, 1); ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, dpp->peer_boot_hash, 32); @@ -2278,8 +2296,9 @@ static void dpp_send_authenticate_confirm(struct dpp_sm *dpp) ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, dpp->own_boot_hash, 32); - ptr += dpp_append_wrapped_data(frame + 26, 6, attrs, ptr - attrs, ptr, - sizeof(frame), dpp->ke, dpp->key_len, 1, + ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, + sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, + dpp->ke, dpp->key_len, 1, DPP_ATTR_INITIATOR_AUTH_TAG, dpp->key_len, dpp->auth_tag); @@ -3301,19 +3320,21 @@ static void dpp_send_commit_reveal_response(struct dpp_sm *dpp, { uint8_t frame[256]; uint8_t *ptr = frame; - uint8_t one = 1; struct iovec iov; const uint8_t *own_mac = netdev_get_address(dpp->netdev); uint8_t b_pub[L_ECC_POINT_MAX_BYTES]; size_t b_len; + struct mmpdu_header *hdr = (struct mmpdu_header *)frame; - b_len = l_ecc_point_get_data(dpp->boot_public, b_pub, sizeof(b_pub)); + memset(frame, 0, sizeof(frame)); + b_len = l_ecc_point_get_data(dpp->boot_public, b_pub, sizeof(b_pub)); ptr += dpp_build_header(own_mac, dpp->peer_addr, DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE, ptr); - ptr += dpp_append_wrapped_data(frame + 26, 6, &one, 1, ptr, - sizeof(frame), dpp->z, dpp->z_len, 2, + ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, + sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, + dpp->z, dpp->z_len, 2, DPP_ATTR_BOOTSTRAPPING_KEY, b_len, b_pub, DPP_ATTR_RESPONDER_AUTH_TAG, v_len, v); From patchwork Wed Mar 27 15:19:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13606773 Received: from mail-qv1-f43.google.com (mail-qv1-f43.google.com [209.85.219.43]) (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 7E05D3A8E4 for ; Wed, 27 Mar 2024 15:20:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.43 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552808; cv=none; b=SXSx5UuvmloCNlYvuYFae8DpHMuSKJSZBVosM0ra3hw+RbhcElaQ7/e87GrEi7ZpieTHpZN6bHvw2MGPVSfc1ZiG4sIsju0PvzLFIUKPAN8m1VCPKb1eVV3rpMBh9xahlHojjBkh1ETLrvhuH3xEGsgmZL0G4hNamJFETRIvIuk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552808; c=relaxed/simple; bh=lb9/VaPQdNylr3CnZS/warLCHD/03kqFpnOXWC0cQeg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=UMqGXgMkNwazWPGN+gV74TeKrSAvaP0kdCVU7Mct/MYZWfWSfbU2fTpov9uefIPD55X0vAiT2KDpbzQsCWWQx5chGXfN6ccwUUSZYXwcm0tP10SEyriIPKtDwrblOodX7sRzGNi2irhAIRgRfGAoGuAxJ7/gXB+UxIwfXaBB8Lg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=bGdqFnxs; arc=none smtp.client-ip=209.85.219.43 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="bGdqFnxs" Received: by mail-qv1-f43.google.com with SMTP id 6a1803df08f44-690de619293so41902146d6.0 for ; Wed, 27 Mar 2024 08:20:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711552805; x=1712157605; 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=TfggQUFqh8R0OL7pLM6apbCSjclCuBMPEFy6joovcww=; b=bGdqFnxsBqMdD+f+3Urh/Kof8pEt4sUo4Z+mxXRdupEY9uU50AWIZl8yufkPOu0OF3 gaVlSfMW5EGqvnsFQBPtvatp0TAVpFl/7AcQ8hpuE3+9/aoDkQ4bPJGuMfstZMkpDk+Q NDmbd3atkxOSmJA+YoAYXuKREXBDsCp/1/Dn1KObO4wogQD1Q5m968fiQ2aWwh+UaNEE fz/t+KkTN+YE5e/tCv3+vmipSJl0uSBpVlJnyqlV3/fC5zWZ5FalGigEZMkucGxDZmax kScRXrgOv0WYAlXXWYl01+koZyGNIvF8QPss+TVTIvKw6CtbAzyHxZ5KUdo7gUEAKwK8 e3tg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711552805; x=1712157605; 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=TfggQUFqh8R0OL7pLM6apbCSjclCuBMPEFy6joovcww=; b=keP/MDaDdE1fjOwrNb2arjKx3hI/3S6/OPemi9QpdnTU9rFan1kFWn60xUgu52fKUG tHRhc6qJnS3CuOt4zin/S+ZqjDrpxuFnmEttq0/+/so0Kz2Qk+nO6yOIMqINp2VRlzIJ h3hDa0ggjTxj7ikDgdRsxOMyFEHkjrJaTEj2S8GLrvXjJ2mGOVX6NxrXfle5MhiHgsN7 6nwZR+pDEroSTYx9Flq0bSY9WPA1v7eErpWKmxP291EV+ixFSnhCBkP0+hvrKJLjf7qV tG9HiPl6X0+lGE7FW2W1NfSs49QDFrQNKLqHvGqa589YB70LFojPugU2sqipSxJKrYQ8 bBIg== X-Gm-Message-State: AOJu0YwybdzXpA6U2NHXko7PYrUrmL3S61Mo5cBOhgaW87IYEccP5NTh fEKiwIhxBpcm/qsgO63w1psyjIlXGTKEWvLK1Yba9Jg1KVeJTv/edAGCur7T X-Google-Smtp-Source: AGHT+IH+DI5AkaUkwX100hC82MKw3Ag9kFlupsU6GXlMs9jceXW9T4BeZS79fvPp9dBi/Wtp8zRrVw== X-Received: by 2002:ad4:5e8c:0:b0:696:8a5d:8e79 with SMTP id jl12-20020ad45e8c000000b006968a5d8e79mr5870477qvb.30.1711552805081; Wed, 27 Mar 2024 08:20:05 -0700 (PDT) Received: from LOCLAP699.rst-02.locus ([208.195.13.130]) by smtp.gmail.com with ESMTPSA id 7-20020a056214202700b006968ce99b7esm3520782qvf.34.2024.03.27.08.20.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Mar 2024 08:20:04 -0700 (PDT) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH 3/9] dpp-util: add dpp_append_point Date: Wed, 27 Mar 2024 08:19:51 -0700 Message-Id: <20240327151957.1446149-3-prestwoj@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240327151957.1446149-1-prestwoj@gmail.com> References: <20240327151957.1446149-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Any time DPP needs to append a point it has to use a temporary buffer to copy into, then copy that into the message buffer. Instead of this add a special purpose API to add the type/length then copy the data directly into the message buffer. --- src/dpp-util.c | 13 +++++++++++++ src/dpp-util.h | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/dpp-util.c b/src/dpp-util.c index ada7ed96..81f34047 100644 --- a/src/dpp-util.c +++ b/src/dpp-util.c @@ -697,6 +697,19 @@ size_t dpp_append_wrapped_data(const uint8_t *frame, size_t frame_len, return attrs_len + 4 + 16; } +size_t dpp_append_point(uint8_t *to, enum dpp_attribute_type type, + struct l_ecc_point *point) +{ + const struct l_ecc_curve *c = l_ecc_point_get_curve(point); + size_t len = l_ecc_curve_get_scalar_bytes(c) * 2; + + l_put_le16(type, to); + l_put_le16(len, to + 2); + l_ecc_point_get_data(point, to + 4, len); + + return len + 4; +} + /* * EasyConnect 2.0 Table 3. Key and Nonce Length Dependency on Prime Length */ diff --git a/src/dpp-util.h b/src/dpp-util.h index 387750aa..1ff9004d 100644 --- a/src/dpp-util.h +++ b/src/dpp-util.h @@ -151,6 +151,8 @@ size_t dpp_append_attr(uint8_t *to, enum dpp_attribute_type type, size_t dpp_append_wrapped_data(const uint8_t *frame, size_t frame_len, uint8_t *to, const void *key, size_t key_len, size_t num_attrs, ...); +size_t dpp_append_point(uint8_t *to, enum dpp_attribute_type type, + struct l_ecc_point *point); char *dpp_generate_uri(const uint8_t *asn1, size_t asn1_len, uint8_t version, const uint8_t *mac, const uint32_t *freqs, From patchwork Wed Mar 27 15:19:52 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13606774 Received: from mail-qt1-f175.google.com (mail-qt1-f175.google.com [209.85.160.175]) (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 864D454770 for ; Wed, 27 Mar 2024 15:20:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.160.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552809; cv=none; b=Ah19o8RUwBn8WNOWhzIfGcByRRr0s2hyyFSmNm6qM5pk737YOq71pyIeewAbwz/QD45iNyOC2rsBjJsLij/DtKw67y/xlL+ErUXxO72p1yx7TA2RGtKYLsz3HxEeppAPHJ617iHXrp9tVY2NrG12jFncC3qa45UVnZscHCBSmqE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552809; c=relaxed/simple; bh=SkVB6yaCtiY19HRA+P1Ou0mgmy5p+loI4m5zMOeA9OE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=Y0EeeF6Z6H3ox+rtrp1QAlnB1BGShRAfZn1oYvkHZ9uorbSzIdjyIA9TdlHR6gRehaSK+ySWHZrRLpKGx74aDWfDdWMKhU7Y0KMyKw6QTsxlcPBA1Aq5zk1d2kEPN48FrFSOfeGjGLqT8C1B6z6NtIVbHcXjRIZb7jO0HKh9EPI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=TfP3Em7X; arc=none smtp.client-ip=209.85.160.175 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="TfP3Em7X" Received: by mail-qt1-f175.google.com with SMTP id d75a77b69052e-430a65e973bso42634711cf.3 for ; Wed, 27 Mar 2024 08:20:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711552806; x=1712157606; 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=2I6EtzXK2xSu8dxZO7eznX3LUgn7LoxAObVVvvJZ4uM=; b=TfP3Em7XpMoT5DUNawBBKA5NVivEZhZgJ8FMH+zLyHICmEi8P90V+zZxfOylD7gRNi ozYk0bcj+HNHWdrCE3P1+ItKYMnvaQYdAZX3+f4wXDc5ggbV9b3/bJ+gYXql1+i/ffZu UOWaPF2Kfs694c+ez1k4YyF0tN8CaX3OZEW32CdpxophYT8FNlcmI20q1kMyytyblP05 DyDjp645ItsyPk427wCiKaYFNs9OcJiDCIdNgA9ZReK6CR8/iMFaXn4GHwWrz6wTgpQc QqGO6XG/dG8IdUYWAeetMK7BoQ9DmADpIiO2/nus+kcnSJXTNBXD8fSX8UlEMaVcvhw/ IBPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711552806; x=1712157606; 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=2I6EtzXK2xSu8dxZO7eznX3LUgn7LoxAObVVvvJZ4uM=; b=LTVIQAzn9l1wu99RDoWJ2ukgPyXGbaaCuG767PTFZYSHHBisK7yAZCYgob69cpwD6e Ug4R3QNWmolgUuPBLaCGtV+NyNU4mC06cDPMQXPVReFnBhWdvMVq+CxTWX043w9NKp5T ByAQ0hk1nRhdgQa6Pu2MZN8X2bmIp/gFg1tWtliQz+bGgv1XUHUbn3yOysp/5RzKKS9j uQF2j+W6S3qMcT1JDxBs4orm0QXrVU6BPzkp5eQbSlS1JT7IxuF3GCShtPUNweq4CzDO Q2dDgT1SsQqZW0ux8DgWXrAO4XeiCi6y6iCYqg1JTxf9Vk0Hw7WSPllf0ij/40Vy4jOP g71A== X-Gm-Message-State: AOJu0Yxlm7cH8mkGD0Z9U/Mh5CCIPDK2tO9Fokqdm2L2+hpGttf1IQGq CEr/392MYETCXdNMicay0sDzOwP1nQWfUupZUZX+MM3j0iffj7hnqOFDfiIs X-Google-Smtp-Source: AGHT+IECeaW7etGg70oBgmSmUFXWsGQUmia6XbxCIfZUC8ESKbdHESMEy8NVlKd7QY3Dl/tG+Dn0fQ== X-Received: by 2002:a05:6214:27e2:b0:696:9df4:58e5 with SMTP id jt2-20020a05621427e200b006969df458e5mr1766225qvb.1.1711552806242; Wed, 27 Mar 2024 08:20:06 -0700 (PDT) Received: from LOCLAP699.rst-02.locus ([208.195.13.130]) by smtp.gmail.com with ESMTPSA id 7-20020a056214202700b006968ce99b7esm3520782qvf.34.2024.03.27.08.20.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Mar 2024 08:20:05 -0700 (PDT) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH 4/9] dpp: use dpp_append_point Date: Wed, 27 Mar 2024 08:19:52 -0700 Message-Id: <20240327151957.1446149-4-prestwoj@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240327151957.1446149-1-prestwoj@gmail.com> References: <20240327151957.1446149-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Use dpp_append_point where possible to avoid temporarily copying data --- src/dpp.c | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/dpp.c b/src/dpp.c index d710aa98..47ebd495 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -1504,7 +1504,6 @@ static void send_authenticate_response(struct dpp_sm *dpp) uint8_t frame[512]; uint8_t *ptr = frame; uint8_t status = DPP_STATUS_OK; - uint64_t r_proto_key[L_ECC_MAX_DIGITS * 2]; uint8_t version = 2; struct iovec iov; uint8_t wrapped2_plaintext[dpp->key_len + 4]; @@ -1514,9 +1513,6 @@ static void send_authenticate_response(struct dpp_sm *dpp) memset(frame, 0, sizeof(frame)); - l_ecc_point_get_data(dpp->own_proto_public, r_proto_key, - sizeof(r_proto_key)); - ptr += dpp_build_header(netdev_get_address(dpp->netdev), dpp->peer_addr, DPP_FRAME_AUTHENTICATION_RESPONSE, ptr); @@ -1526,8 +1522,8 @@ static void send_authenticate_response(struct dpp_sm *dpp) if (dpp->mutual_auth) ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, dpp->peer_boot_hash, 32); - ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_PROTOCOL_KEY, - r_proto_key, dpp->key_len * 2); + ptr += dpp_append_point(ptr, DPP_ATTR_RESPONDER_PROTOCOL_KEY, + dpp->own_proto_public); ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1); /* Wrap up secondary data (R-Auth) */ @@ -1778,7 +1774,6 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp) { uint8_t frame[256]; uint8_t *ptr = frame; - uint64_t i_proto_key[L_ECC_MAX_DIGITS * 2]; uint8_t version = 2; struct iovec iov; struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); @@ -1793,9 +1788,6 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp) return false; } - l_ecc_point_get_data(dpp->own_proto_public, i_proto_key, - sizeof(i_proto_key)); - ptr += dpp_build_header(netdev_get_address(dpp->netdev), dpp->peer_addr, DPP_FRAME_AUTHENTICATION_REQUEST, ptr); @@ -1803,8 +1795,8 @@ static bool dpp_send_authenticate_request(struct dpp_sm *dpp) dpp->peer_boot_hash, 32); ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, dpp->own_boot_hash, 32); - ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_PROTOCOL_KEY, - i_proto_key, dpp->key_len * 2); + ptr += dpp_append_point(ptr, DPP_ATTR_INITIATOR_PROTOCOL_KEY, + dpp->own_proto_public); ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1); if (dpp->role == DPP_CAPABILITY_CONFIGURATOR && @@ -1835,7 +1827,6 @@ 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); @@ -1855,10 +1846,7 @@ static void dpp_send_pkex_exchange_request(struct dpp_sm *dpp) 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); + ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, dpp->pkex_m); iov[1].iov_base = attrs; iov[1].iov_len = ptr - attrs; @@ -3018,7 +3006,6 @@ static void dpp_send_pkex_exchange_response(struct dpp_sm *dpp, 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]; @@ -3036,10 +3023,7 @@ static void dpp_send_pkex_exchange_response(struct dpp_sm *dpp, 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); + ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, n); iov[1].iov_base = attrs; iov[1].iov_len = ptr - attrs; From patchwork Wed Mar 27 15:19:53 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13606775 Received: from mail-qv1-f41.google.com (mail-qv1-f41.google.com [209.85.219.41]) (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 2CF725FB84 for ; Wed, 27 Mar 2024 15:20:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552811; cv=none; b=Xun1aN36+uVcHutArldrZGi90k9ox+2Gw6HhzDnKwUIhLmx2gc5pEoZ90s1Te5MMaGs1pslm6cP/86Fpl/5XfpA1FXlL4NVJlN2oyttxzIqY5TDehNHe2tza7QWnTQqbqWkc5uVnxffl7ALX086K9CGMQAZOWwX8+BmRPQqSESU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552811; c=relaxed/simple; bh=ClXy9qyAilY+1sthlXLbr+G6uVC+ss4wOBO076vphmo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=gYaD9VYi3FNZjGQ+cZ3GeAk2BZ9JLGUszk3HZhKCM/foOD3Y49qBXOk67Qr6lU3blt8SY3QEdsFGHuwyiUY+vz9rED0+Ge5Q+ZLTgICDYErA9XvfULUAf0XXXCprPNmfEcdecqnwOg5TFCuhh/J3agpgE2c/51nLvgE9YDOyzV0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=E0gFskf2; arc=none smtp.client-ip=209.85.219.41 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="E0gFskf2" Received: by mail-qv1-f41.google.com with SMTP id 6a1803df08f44-6963c0c507eso9771826d6.1 for ; Wed, 27 Mar 2024 08:20:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711552808; x=1712157608; 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=JzhEE0tIfJF6KncVz6SHZKUHXYziV5j8UEptcv4dQIk=; b=E0gFskf289CbB39ZthA5cS5SZBv+685eLY5ujPr4aogSrq2V7bkr73Q6KM2suSWMDo m5r8j6uxP9zQCGQfK3sBDkDE1DkCDgyMrhPF2hOhKUqbo5nA27loEJPQ9cHBxrDZ19pX R+100/TfBMQ5O5ly0l0mQvmOO0wQ/aVCoqv4GGO9mN0dG1M/LE6+xgyOp5ZBknkGWN52 UZa6uV0+PiysxZ6clPW67K2XTgyG8QoBC54oarMmOvNRC2inL7KDwVsxi5pAhpEgwbtc BRr85RkB3cQ1cR8TvZSrPJkNnE26HdLZ9XLuqLRm9PShX9hcjre9OGOCCEXzRJ+T/uZW Btrw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711552808; x=1712157608; 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=JzhEE0tIfJF6KncVz6SHZKUHXYziV5j8UEptcv4dQIk=; b=G44jnoJEB1pt+rE/aRGY4YQBSAXWe9ySWbuEUSNKksgCLv2+Tr0ZwTDxITq/fGIZrC ea2XGhEp1FjfXnQIeaZx+mMraafYTT73KqU0v5LIaPSDbKUdU4biKT/ivfGYKHJa42rh QXrG5BXFpcTAw4dFGYoXYaesJxOfjFgH7Qn9Ei2mtCCXhNnyhIF49HW5MMbqMrVMZfpf gJH5MogVkereY6PEw2UK5+Tw0/8kxEY5Iiw6y9rwKeIEq5ImnLXCA4ksbyGePuX77o/T AI/8vrsVwJw5HoRSYSOAYoNoH9RI/P3jp0U7s8vrh5uubocViKjux5blmDVzQrufdQLA bGeA== X-Gm-Message-State: AOJu0YywrF69s4QZ4E3p4hAvu8A2TTcpa5xoAeHRESW5p9rsd9bLxxyT tkJi4ytv1EbM1i77m1OBZg09pzSwOkWlphAplLbd5ocU+qf/e5MUEU7PrnmG X-Google-Smtp-Source: AGHT+IFMicWkMItNc4da8+EkTdH8OFS7lW5sv9T3XDUGQRyscOM6ZsWvBg6FJiqmmC/QBjot236P4g== X-Received: by 2002:a05:6214:5605:b0:692:494f:f0aa with SMTP id mg5-20020a056214560500b00692494ff0aamr445446qvb.9.1711552807528; Wed, 27 Mar 2024 08:20:07 -0700 (PDT) Received: from LOCLAP699.rst-02.locus ([208.195.13.130]) by smtp.gmail.com with ESMTPSA id 7-20020a056214202700b006968ce99b7esm3520782qvf.34.2024.03.27.08.20.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Mar 2024 08:20:07 -0700 (PDT) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH 5/9] dpp-common: Skeleton for common DPP module Date: Wed, 27 Mar 2024 08:19:53 -0700 Message-Id: <20240327151957.1446149-5-prestwoj@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240327151957.1446149-1-prestwoj@gmail.com> References: <20240327151957.1446149-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 In order to expand the features for DPP e.g. TCP or a DPP controller the state machine needs to be both transport agnostic and buildable for other apps outside of IWD-core. To do this the core DPP state machine is being moved into a common module. The consumers of the common SM will create a dpp_sm object for each protocol run, and destroy it afterwards. The SM will take a write callback, event callback, the boostrapping keys, and expose a read handler: - The write callback will be called by the SM whenever there is a frame to be sent out. In order to support 802.11 encapsulation dpp_sm_set_write_handler can be used to notify the SM that writes are allowed or disallowed (by passing NULL). This is done to allow the 802.11 encapsulating module to prepare any offchannel requests before sending out frames. - The event callback is called by the SM whenever there are events requiring some handling by the encapsulating protocol, or when DPP has succeeded or failed. - The read handler can be called by the encapsulating protocol in order to RX frames to the SM. Making the SM common will allow both pure-802.11 DPP and DPP-over-TCP to utilize the same state machine. In addition a DPP-controller can now be implemented using the common SM but be entirely standalone from IWD. Since a lot of the code was moved from dpp.c the original copyright was retained. --- src/dpp-common.c | 518 +++++++++++++++++++++++++++++++++++++++++++++++ src/dpp-common.h | 110 ++++++++++ 2 files changed, 628 insertions(+) create mode 100644 src/dpp-common.c create mode 100644 src/dpp-common.h diff --git a/src/dpp-common.c b/src/dpp-common.c new file mode 100644 index 00000000..45099d12 --- /dev/null +++ b/src/dpp-common.c @@ -0,0 +1,518 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2021 Intel Corporation. All rights reserved. + * Copyright (C) 2024 Locus Robotics + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include "linux/nl80211.h" + +#include "src/missing.h" +#include "src/dbus.h" +#include "src/netdev.h" +#include "src/module.h" +#include "src/dpp-util.h" +#include "src/band.h" +#include "src/frame-xchg.h" +#include "src/offchannel.h" +#include "src/wiphy.h" +#include "src/ie.h" +#include "src/iwd.h" +#include "src/util.h" +#include "src/crypto.h" +#include "src/mpdu.h" +#include "ell/useful.h" +#include "src/common.h" +#include "src/json.h" +#include "src/storage.h" +#include "src/station.h" +#include "src/scan.h" +#include "src/network.h" +#include "src/handshake.h" +#include "src/nl80211util.h" +#include "src/knownnetworks.h" +#include "src/dpp-common.h" + +#define DPP_HDR_LEN 6 +#define DPP_ACTION_VENDOR_SPECIFIC 0x09 +#define DPP_ACTION_GAS_REQUEST 0x0a +#define DPP_ACTION_GAS_RESPONSE 0x0b +#define DPP_AUTH_PROTO_TIMEOUT 10 +#define DPP_PKEX_PROTO_TIMEOUT 120 + +struct dpp_sm { + char *uri; + uint8_t role; + + uint8_t *own_asn1; + size_t own_asn1_len; + uint8_t *peer_asn1; + size_t peer_asn1_len; + uint8_t own_boot_hash[32]; + uint8_t peer_boot_hash[32]; + uint8_t own_chirp_hash[32]; + const struct l_ecc_curve *curve; + size_t key_len; + size_t nonce_len; + struct l_ecc_scalar *boot_private; + struct l_ecc_point *boot_public; + struct l_ecc_point *peer_boot_public; + + enum dpp_state state; + + uint8_t r_nonce[32]; + uint8_t i_nonce[32]; + uint8_t e_nonce[32]; + + struct l_ecc_scalar *m; + uint64_t ke[L_ECC_MAX_DIGITS]; + uint64_t k1[L_ECC_MAX_DIGITS]; + uint64_t k2[L_ECC_MAX_DIGITS]; + uint64_t auth_tag[L_ECC_MAX_DIGITS]; + + struct l_ecc_scalar *proto_private; + struct l_ecc_point *own_proto_public; + + struct l_ecc_point *peer_proto_public; + + uint8_t diag_token; + + /* Timeout of auth/config/PKEX protocols */ + uint64_t proto_timeout; + struct l_timeout *timeout; + + struct dpp_configuration *config; + + /* PKEX-specific values */ + 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; + /* 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; + uint8_t pkex_own_mac[6]; + uint8_t pkex_peer_mac[6]; + /* Set to either own/peer mac depending on configuration */ + const uint8_t *mac_initiator; + const uint8_t *mac_responder; + + /* + * Since the authenticate frame may request a channel switch we do need + * to expose this detail within the common code. + */ + uint8_t channel[2]; + + uint8_t *frame_pending; + size_t frame_len; + dpp_write_cb_t write; + dpp_event_cb_t event_cb; + void *user_data; + + bool skip_presence : 1; + bool mutual_auth : 1; + bool initiator : 1; +}; + +static void dpp_failed(struct dpp_sm *dpp) +{ + if (dpp->event_cb) + dpp->event_cb(DPP_EVENT_FAILED, NULL, dpp->user_data); +} + +static void dpp_protocol_timeout(struct l_timeout *timeout, void *user_data) +{ + struct dpp_sm *dpp = user_data; + + l_debug("DPP timed out"); + + dpp_failed(dpp); +} + +static void dpp_reset_protocol_timer(struct dpp_sm *dpp, uint64_t time) +{ + if (dpp->timeout) + l_timeout_modify(dpp->timeout, time); + else + dpp->timeout = l_timeout_create(time, dpp_protocol_timeout, + dpp, NULL); +} + +struct dpp_sm *dpp_sm_new(dpp_event_cb_t event, + const struct l_ecc_point *boot_public, + const struct l_ecc_scalar *boot_private, + void *user_data) +{ + struct dpp_sm *dpp = l_new(struct dpp_sm, 1); + + dpp->state = DPP_STATE_NOTHING; + dpp->curve = l_ecc_point_get_curve(boot_public); + dpp->key_len = l_ecc_curve_get_scalar_bytes(dpp->curve); + dpp->nonce_len = dpp_nonce_len_from_key_len(dpp->key_len); + dpp->boot_public = l_ecc_point_clone(boot_public); + dpp->boot_private = l_ecc_scalar_clone(boot_private); + dpp->own_asn1 = dpp_point_to_asn1(dpp->boot_public, &dpp->own_asn1_len); + dpp->event_cb = event; + dpp->user_data = user_data; + + dpp_hash(L_CHECKSUM_SHA256, dpp->own_boot_hash, 1, dpp->own_asn1, + dpp->own_asn1_len); + dpp_hash(L_CHECKSUM_SHA256, dpp->own_chirp_hash, 2, "chirp", + strlen("chirp"), dpp->own_asn1, dpp->own_asn1_len); + + return dpp; +} + +void dpp_sm_set_write_handler(struct dpp_sm *dpp, dpp_write_cb_t write) +{ + dpp->write = write; + + if (!dpp->write) + return; + + /* Handle writing frame */ +} + +static void dpp_free_auth_data(struct dpp_sm *dpp) +{ + if (dpp->own_proto_public) { + l_ecc_point_free(dpp->own_proto_public); + dpp->own_proto_public = NULL; + } + + if (dpp->proto_private) { + l_ecc_scalar_free(dpp->proto_private); + dpp->proto_private = NULL; + } + + if (dpp->peer_proto_public) { + l_ecc_point_free(dpp->peer_proto_public); + dpp->peer_proto_public = NULL; + } + + if (dpp->peer_boot_public) { + l_ecc_point_free(dpp->peer_boot_public); + dpp->peer_boot_public = NULL; + } + + if (dpp->m) { + l_ecc_scalar_free(dpp->m); + dpp->m = NULL; + } + + 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_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; + } + + if (dpp->peer_encr_key) { + l_ecc_point_free(dpp->peer_encr_key); + dpp->peer_encr_key = NULL; + } +} + +void dpp_sm_free(struct dpp_sm *dpp) +{ + if (dpp->boot_public) { + l_ecc_point_free(dpp->boot_public); + dpp->boot_public = NULL; + } + + if (dpp->boot_private) { + l_ecc_scalar_free(dpp->boot_private); + dpp->boot_private = NULL; + } + + if (dpp->timeout) { + l_timeout_remove(dpp->timeout); + dpp->timeout = NULL; + } + + if (dpp->config) { + dpp_configuration_free(dpp->config); + dpp->config = NULL; + } + + if (dpp->peer_asn1) { + l_free(dpp->peer_asn1); + dpp->peer_asn1 = NULL; + } + + if (dpp->own_asn1) { + l_free(dpp->own_asn1); + dpp->own_asn1 = NULL; + } + + if (dpp->frame_pending) { + l_free(dpp->frame_pending); + dpp->frame_pending = NULL; + } + + dpp->state = DPP_STATE_NOTHING; + dpp->pkex_version = 0; + + explicit_bzero(dpp->r_nonce, dpp->nonce_len); + explicit_bzero(dpp->i_nonce, dpp->nonce_len); + explicit_bzero(dpp->e_nonce, dpp->nonce_len); + explicit_bzero(dpp->ke, dpp->key_len); + explicit_bzero(dpp->k1, dpp->key_len); + explicit_bzero(dpp->k2, dpp->key_len); + explicit_bzero(dpp->auth_tag, dpp->key_len); + 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); + + l_free(dpp); +} + +static bool dpp_start_authentication(struct dpp_sm *dpp) +{ + l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private, + &dpp->own_proto_public); + + l_getrandom(dpp->i_nonce, dpp->nonce_len); + + dpp->m = dpp_derive_k1(dpp->peer_boot_public, + dpp->proto_private, dpp->k1); + + dpp->state = DPP_STATE_AUTHENTICATING; + + return true; +} + +void dpp_sm_set_peer_bootstrap(struct dpp_sm *dpp, + struct l_ecc_point *public) +{ + dpp->peer_boot_public = l_ecc_point_clone(public); + dpp->peer_asn1 = dpp_point_to_asn1(public, &dpp->peer_asn1_len); + + dpp_hash(L_CHECKSUM_SHA256, dpp->peer_boot_hash, 1, dpp->peer_asn1, + dpp->peer_asn1_len); +} + +const uint8_t *dpp_sm_get_own_asn1(struct dpp_sm *dpp, size_t *len) +{ + *len = dpp->own_asn1_len; + + return dpp->own_asn1; +} + +void dpp_sm_set_configuration(struct dpp_sm *dpp, + struct dpp_configuration *config) +{ + dpp->config = config; +} + +const struct dpp_configuration *dpp_sm_get_configuration(struct dpp_sm *dpp) +{ + return dpp->config; +} + +void dpp_sm_set_role(struct dpp_sm *dpp, enum dpp_capability role) +{ + dpp->role = role; +} + +void dpp_sm_set_skip_presence(struct dpp_sm *dpp, bool skip) +{ + dpp->skip_presence = skip; +} + +void dpp_sm_set_channel(struct dpp_sm *dpp, uint8_t oper_class, uint8_t channel) +{ + dpp->channel[0] = oper_class; + dpp->channel[1] = channel; +} + +enum dpp_state dpp_sm_get_state(struct dpp_sm *dpp) +{ + return dpp->state; +} + +bool dpp_sm_start_initiator(struct dpp_sm *dpp) +{ + if (L_WARN_ON(!dpp->peer_boot_public)) + return false; + + dpp->initiator = true; + + if (dpp->skip_presence) { + dpp_start_authentication(dpp); + dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT); + } else + dpp->state = DPP_STATE_PRESENCE; + + return true; +} + +bool dpp_sm_start_responder(struct dpp_sm *dpp) +{ + l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private, + &dpp->own_proto_public); + dpp->initiator = false; + + dpp->state = DPP_STATE_PRESENCE; + + return true; +} + +void dpp_sm_set_pkex_identifier(struct dpp_sm *dpp, const char *identifier) +{ + dpp->pkex_id = l_strdup(identifier); +} + +void dpp_sm_set_pkex_key(struct dpp_sm *dpp, const char *key) +{ + dpp->pkex_key = l_strdup(key); +} + +void dpp_sm_set_pkex_own_mac(struct dpp_sm *dpp, const uint8_t *mac) +{ + memcpy(dpp->pkex_own_mac, mac, 6); +} + +void dpp_sm_set_pkex_peer_mac(struct dpp_sm *dpp, const uint8_t *mac) +{ + memcpy(dpp->pkex_peer_mac, mac, 6); + + if (dpp->initiator) { + dpp->mac_responder = dpp->pkex_peer_mac; + dpp->mac_initiator = dpp->pkex_own_mac; + } else { + dpp->mac_initiator = dpp->pkex_peer_mac; + dpp->mac_responder = dpp->pkex_own_mac; + } +} + +bool dpp_sm_pkex_start_initiator(struct dpp_sm *dpp) +{ + _auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL; + + if (!dpp->pkex_key) + return false; + + /* + * "DPP R2 devices are expected to use PKEXv1 by default" + * + * TODO: Support setting version (v2 required for TCP encapsulation) + */ + dpp->pkex_version = 1; + + if (!l_ecdh_generate_key_pair(dpp->curve, &dpp->pkex_private, + &dpp->pkex_public)) + return false; + + /* + * "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, + dpp->pkex_own_mac); + 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->initiator = true; + + dpp->mac_initiator = dpp->pkex_own_mac; + /* Won't know until we receive a response */ + dpp->mac_responder = NULL; + + dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT); + + /* Send exchange request */ + + return true; + +failed: + return false; +} + +bool dpp_sm_pkex_start_responder(struct dpp_sm *dpp) +{ + dpp->initiator = false; + + dpp->mac_responder = dpp->pkex_own_mac; + /* Won't know until we receive the first frame */ + dpp->mac_initiator = NULL; + + dpp->state = DPP_STATE_PKEX_EXCHANGE; + dpp->pkex_version = 1; + + dpp_reset_protocol_timer(dpp, DPP_PKEX_PROTO_TIMEOUT); + + return true; +} + +void dpp_handle_rx(struct dpp_sm *dpp, const uint8_t *data, size_t len) +{ + /* Handle frame */ +} diff --git a/src/dpp-common.h b/src/dpp-common.h new file mode 100644 index 00000000..52075cd6 --- /dev/null +++ b/src/dpp-common.h @@ -0,0 +1,110 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2024 Locus Robotics + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct dpp_sm; +struct l_ecc_point; +struct l_ecc_scalar; +struct dpp_configuration; + +enum dpp_state { + DPP_STATE_NOTHING, + DPP_STATE_PRESENCE, + DPP_STATE_PKEX_EXCHANGE, + DPP_STATE_PKEX_COMMIT_REVEAL, + DPP_STATE_AUTHENTICATING, + DPP_STATE_CONFIGURING, +}; + +enum dpp_capability { + DPP_CAPABILITY_ENROLLEE = 0x01, + DPP_CAPABILITY_CONFIGURATOR = 0x02, +}; + +enum dpp_event { + /* + * PEER_ACCEPTED indicates an initial DPP frame has been received and + * accepted. This could be either a PKEX or an auth frame when DPP is + * running as either a configurator or enrollee. This should be + * used by the encapsulating protocol to note the peer address (MAC + * or IP) and from then on only accept frames from this peer until DPP + * has completed. + */ + DPP_EVENT_PEER_ACCEPTED, + /* + * The authenticate request frame included a channel attribute. The + * encapsulating protocol must switch to this channel to continue the + * protocol (only applicable to 802.11 encapsulation). Event data is + * two bytes: [oper_class, channel] + */ + DPP_EVENT_CHANNEL_SWITCH, + /* + * A key corresponding to an identifier (set in event_data) is now + * required. The encapsulating protocol must retrieve the key and + * notify using dpp_sm_set_pkex_key(). + */ + DPP_EVENT_PKEX_KEY_REQUESTED, + DPP_EVENT_SUCCESS, + DPP_EVENT_FAILED, +}; + +typedef void (*dpp_event_cb_t)(enum dpp_event event, const void *event_data, + void *user_data); + +typedef void (*dpp_write_cb_t)(const uint8_t *data, size_t len, + void *user_data); + +struct dpp_sm *dpp_sm_new(dpp_event_cb_t event, + const struct l_ecc_point *boot_public, + const struct l_ecc_scalar *boot_private, + void *user_data); + +void dpp_sm_free(struct dpp_sm *dpp); + +void dpp_handle_rx(struct dpp_sm *dpp, const uint8_t *data, size_t len); +void dpp_sm_set_write_handler(struct dpp_sm *dpp, dpp_write_cb_t write); + +void dpp_sm_set_peer_bootstrap(struct dpp_sm *dpp, + struct l_ecc_point *public); +void dpp_sm_set_own_bootstrap(struct dpp_sm *dpp, struct l_ecc_point *public, + struct l_ecc_scalar *private); +const uint8_t *dpp_sm_get_own_asn1(struct dpp_sm *dpp, size_t *len); + +void dpp_sm_set_configuration(struct dpp_sm *dpp, + struct dpp_configuration *config); +const struct dpp_configuration *dpp_sm_get_configuration(struct dpp_sm *dpp); +void dpp_sm_set_role(struct dpp_sm *dpp, enum dpp_capability role); +void dpp_sm_set_skip_presence(struct dpp_sm *dpp, bool skip); + +void dpp_sm_set_channel(struct dpp_sm *dpp, uint8_t oper_class, + uint8_t channel); +enum dpp_state dpp_sm_get_state(struct dpp_sm *dpp); + +bool dpp_sm_start_initiator(struct dpp_sm *dpp); +bool dpp_sm_start_responder(struct dpp_sm *dpp); + +void dpp_sm_set_pkex_identifier(struct dpp_sm *dpp, const char *identifier); +void dpp_sm_set_pkex_key(struct dpp_sm *dpp, const char *key); +void dpp_sm_set_pkex_own_mac(struct dpp_sm *dpp, const uint8_t *mac); +void dpp_sm_set_pkex_peer_mac(struct dpp_sm *dpp, const uint8_t *mac); + +bool dpp_sm_pkex_start_initiator(struct dpp_sm *dpp); +bool dpp_sm_pkex_start_responder(struct dpp_sm *dpp); From patchwork Wed Mar 27 15:19:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13606777 Received: from mail-oi1-f176.google.com (mail-oi1-f176.google.com [209.85.167.176]) (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 4F2CB12E1CC for ; Wed, 27 Mar 2024 15:20:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552815; cv=none; b=IT+yqT9eG+DsDarYOjYoQ8YGHjW8s9fUVZnPTRcAM34CYT7Rimy1MNL1TrpWZWa/PK3dxqaD9KHxlFMskF9/TFMPsDErhabC10Ry0R6TICiTAehl/1P1Eu5oPhk9zk96zsRmqY4MSc9mOQLcIGxxI6cqjBq6obyR3wV8GHe6riI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552815; c=relaxed/simple; bh=/sc2x9NpCYTFXIKF891XrFgbqBfUiodCbjWK5ksMQ+Y=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=PIe+O6XeCiY6jR7Lnf2WojOZ4Badt33j2XgG933Y/xfQ+ZNlcpwJ3XHl4oTBX8g4CW72cmvPi9idZZ20kcsF8JE3DYafEsh7J1OXCAGdAXcHzFX5uIg+CKm4or/EcH19VPhtiBAiDR8RS+Pu5J+kncmuX1/sZWvZtr/Onsw/qA8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Rzgpwx2u; arc=none smtp.client-ip=209.85.167.176 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="Rzgpwx2u" Received: by mail-oi1-f176.google.com with SMTP id 5614622812f47-3c3d3710018so1698554b6e.0 for ; Wed, 27 Mar 2024 08:20:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711552810; x=1712157610; 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=Y+Q/NZxWwa5d2q9Z4A/2mZAv1aQQ4mf1FvEPhVFz4IE=; b=Rzgpwx2uh6E9UUWizp9+FgBbT6of+2QJ5PIc6/R6rA8ZI6I0VSdh68b5uMcwUvSBYj gBUqr8yUI42X1fo+LLy0Aepnxth6mn1BvoPcAtTwd2QerYQ6aP9k+pkBbG9u+Sp5WzaU e3VyefZweS/OntyaYvxywAoKEu+pYOCRBbDyaaZtlesdAad3s4YecxfJj1QPBMMjpML0 oWSOTZJdwn5UTqPl3bA6bdAGxKIWm1Q2N7b8kVUDqqBTLFoKdhNZOiJvAjfGLBFrzRWz NCp9IY4DKPX+4Ijq6/JxidRRP7ZHJxvvMMMLHIh6K867FWS369YrlGr3kSEwveuVvr5L fkAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711552810; x=1712157610; 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=Y+Q/NZxWwa5d2q9Z4A/2mZAv1aQQ4mf1FvEPhVFz4IE=; b=TKp0PFc2ha3zBOjdYtQJVX0kFftbLL5IYwiLWSJwXmwr72+RTp5qNlDS+pd1lo/7tM DhRLLpOjlrr60fYLIunPKNhzCTxjsvqWYn2isRWHqw587paCmYre4WuTmMqH6IoARqIc bvjuHPhfEXibkthH3ZmjER4z0W0zkupB5i9VIzOQEUOr+CVR+Sch0cVMafL7oeM4cWW8 nWzE6Ornsl1HJ5HDgsN7jsdA2h/fhjx45DYLAJu0AWA4Mt2k3MY32pquNYapliuUxkRg q9h7XI4hJFLmet5mhUL8bNZB/FqBWV5Zl3hoG7XG4i2IdnADsDTAhnXJKZMbvTye0Afk GDrg== X-Gm-Message-State: AOJu0YyXE4AqYwjfSe0vrn/u6ugWoApqlVJYt7G76UlsIP9cO1R2+Flq asv1G+ji2H3N3zgwV3POLXzuAhzfQMk0yQzNhfJAXBlPxf0XJnQ4/di/QPcy X-Google-Smtp-Source: AGHT+IHKAqoQo0Sbqq4UCVDzC0F32F9hwow+YPJvJX0BLxO5KX02TesEq4As88yctAJzIlXGjBxoFQ== X-Received: by 2002:a05:6808:2124:b0:3c3:dd65:bdaa with SMTP id r36-20020a056808212400b003c3dd65bdaamr317204oiw.6.1711552809140; Wed, 27 Mar 2024 08:20:09 -0700 (PDT) Received: from LOCLAP699.rst-02.locus ([208.195.13.130]) by smtp.gmail.com with ESMTPSA id 7-20020a056214202700b006968ce99b7esm3520782qvf.34.2024.03.27.08.20.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Mar 2024 08:20:08 -0700 (PDT) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH 6/9] dpp-common: add TX/RX handlers to dpp_sm Date: Wed, 27 Mar 2024 08:19:54 -0700 Message-Id: <20240327151957.1446149-6-prestwoj@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240327151957.1446149-1-prestwoj@gmail.com> References: <20240327151957.1446149-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 This copies the bulk of the protocol handling from dpp.c into dpp-common.c. There are no logical changes but some adaptations needed to be made in order to isolate the encapsulating protocol from the common protocol. - The DPP header is now reduced to not include the mmpdu header or catagory byte. - A channel switch must now notify the encapsulating protocol since DPP common has no concept of frequency/channels - When a peer is accepted its up to the encapsulating protocol to filter frames only from this peer for the remainder of the protocol. With PKEX the peer/own MACs actually do need to be communicated to dpp-common due to some of the key generation. - When a PKEX key is not set into the SM its up to the encapsulating protocol to provide the key after a PKEX_KEY_REQUESTED event. Once the key is obtained the SM can resume the PKEX protocol. Other changes were made for readability. Sending/handler function names were made consistent. The DPP config header is now built and parsed with common functions (as opposed to manually). Extra validation here and there was added as it was noticed (checks for required attributes, return value checks, etc). --- src/dpp-common.c | 2246 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 2242 insertions(+), 4 deletions(-) diff --git a/src/dpp-common.c b/src/dpp-common.c index 45099d12..2d4bbb31 100644 --- a/src/dpp-common.c +++ b/src/dpp-common.c @@ -145,6 +145,70 @@ struct dpp_sm { bool initiator : 1; }; +/* + * Builds only the core DPP header. This is shared between DPP over 80211 and + * over TCP. Those protocols are required to encapsulate the core message as + * required by the spec. + */ +static size_t dpp_build_header(enum dpp_frame_type type, uint8_t *buf) +{ + uint8_t *ptr = buf; + + *ptr++ = DPP_ACTION_VENDOR_SPECIFIC; + memcpy(ptr, wifi_alliance_oui, 3); + ptr += 3; + *ptr++ = 0x1a; /* WiFi Alliance DPP OI type */ + *ptr++ = 1; /* Cryptosuite */ + *ptr++ = type; + + return ptr - buf; +} + +/* + * For some reason the DPP spec decided to use GAS frame formats only for the + * configuration request and response. There is no explicit DPP frame type for + * these messages, hence the boolean 'request' parameter. The header will be + * formatted either for a configuration request or configuration response. + * + * See Section 8.3 DPP Generic Advertisement Service (GAS) frames + * + * As with the more generic header above, the specific protocol handlers will + * be required to add extra header information (80211 or TCP). + */ +static size_t dpp_build_config_header(bool request, uint8_t diag_token, + uint8_t *buf) +{ + uint8_t *ptr = buf; + + *ptr++ = request ? DPP_ACTION_GAS_REQUEST: DPP_ACTION_GAS_RESPONSE; + *ptr++ = diag_token; + + if (!request) { + /* Status */ + l_put_le16(0, ptr); + ptr += 2; + /* Not fragmented */ + l_put_le16(0, ptr); + ptr += 2; + } + + *ptr++ = IE_TYPE_ADVERTISEMENT_PROTOCOL; + *ptr++ = 8; /* len */ + if (request) + *ptr++ = 0x7f; + else + *ptr++ = 0x00; + + *ptr++ = IE_TYPE_VENDOR_SPECIFIC; + *ptr++ = 5; + memcpy(ptr, wifi_alliance_oui, 3); + ptr += 3; + *ptr++ = 0x1a; + *ptr++ = 1; + + return ptr - buf; +} + static void dpp_failed(struct dpp_sm *dpp) { if (dpp->event_cb) @@ -169,6 +233,503 @@ static void dpp_reset_protocol_timer(struct dpp_sm *dpp, uint64_t time) dpp, NULL); } +static bool dpp_check_roles(struct dpp_sm *dpp, uint8_t peer_capa) +{ + if (dpp->role == DPP_CAPABILITY_ENROLLEE && + !(peer_capa & DPP_CAPABILITY_CONFIGURATOR)) + return false; + else if (dpp->role == DPP_CAPABILITY_CONFIGURATOR && + !(peer_capa & DPP_CAPABILITY_ENROLLEE)) + return false; + + return true; +} + +static void dpp_try_write(struct dpp_sm *dpp, const uint8_t *frame, + size_t frame_len) +{ + if (!dpp->write) { + if (dpp->frame_pending) { + l_free(dpp->frame_pending); + dpp->frame_pending = NULL; + } + + l_debug("No write handler, waiting to send"); + dpp->frame_pending = l_memdup(frame, frame_len); + dpp->frame_len = frame_len; + return; + } + + dpp->write(frame, frame_len, dpp->user_data); +} + +static void dpp_send_presence(struct dpp_sm *dpp) +{ + uint8_t frame[DPP_HDR_LEN + 64]; + uint8_t *ptr = frame; + + ptr += dpp_build_header(DPP_FRAME_PRESENCE_ANNOUNCEMENT, ptr); + ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, + dpp->own_chirp_hash, + sizeof(dpp->own_chirp_hash)); + + dpp->state = DPP_STATE_PRESENCE; + + l_debug("sending presence announcement"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_auth_request(struct dpp_sm *dpp) +{ + uint8_t frame[DPP_HDR_LEN + 256]; + uint8_t *ptr = frame; + uint8_t version = 2; + + ptr += dpp_build_header(DPP_FRAME_AUTHENTICATION_REQUEST, ptr); + ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, + dpp->peer_boot_hash, 32); + ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, + dpp->own_boot_hash, 32); + ptr += dpp_append_point(ptr, DPP_ATTR_INITIATOR_PROTOCOL_KEY, + dpp->own_proto_public); + ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1); + + if (dpp->role == DPP_CAPABILITY_CONFIGURATOR && dpp->channel[0]) + ptr += dpp_append_attr(ptr, DPP_ATTR_CHANNEL, dpp->channel, 2); + + ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr, + dpp->k1, dpp->key_len, 2, + DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, + DPP_ATTR_INITIATOR_CAPABILITIES, + (size_t) 1, &dpp->role); + + dpp->state = DPP_STATE_AUTHENTICATING; + + l_debug("sending auth request"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_auth_response(struct dpp_sm *dpp) +{ + uint8_t frame[DPP_HDR_LEN + 512]; + uint8_t *ptr = frame; + uint8_t status = DPP_STATUS_OK; + uint8_t version = 2; + uint8_t wrapped2_plaintext[dpp->key_len + 4]; + uint8_t wrapped2[dpp->key_len + 16 + 8]; + size_t wrapped2_len; + + ptr += dpp_build_header(DPP_FRAME_AUTHENTICATION_RESPONSE, ptr); + ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1); + ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, + dpp->own_boot_hash, 32); + if (dpp->mutual_auth) + ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, + dpp->peer_boot_hash, 32); + ptr += dpp_append_point(ptr, DPP_ATTR_RESPONDER_PROTOCOL_KEY, + dpp->own_proto_public); + ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1); + + /* Wrap up secondary data (R-Auth) */ + wrapped2_len = dpp_append_attr(wrapped2_plaintext, + DPP_ATTR_RESPONDER_AUTH_TAG, + dpp->auth_tag, dpp->key_len); + /* + * "Invocations of AES-SIV in the DPP Authentication protocol that + * produce ciphertext that is part of an additional AES-SIV invocation + * do not use AAD; in other words, the number of AAD components is set + * to zero." + */ + if (!aes_siv_encrypt(dpp->ke, dpp->key_len, wrapped2_plaintext, + dpp->key_len + 4, NULL, 0, wrapped2)) { + l_error("Failed to encrypt wrapped data"); + return; + } + + wrapped2_len += 16; + + ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr, + dpp->k2, dpp->key_len, 4, + DPP_ATTR_RESPONDER_NONCE, dpp->nonce_len, dpp->r_nonce, + DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, + DPP_ATTR_RESPONDER_CAPABILITIES, (size_t) 1, &dpp->role, + DPP_ATTR_WRAPPED_DATA, wrapped2_len, wrapped2); + + l_debug("send auth response"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_auth_request_failed(struct dpp_sm *dpp, + enum dpp_status status, + void *k1) +{ + uint8_t frame[DPP_HDR_LEN + 128]; + uint8_t *ptr = frame; + uint8_t version = 2; + uint8_t s = status; + + ptr += dpp_build_header(DPP_FRAME_AUTHENTICATION_RESPONSE, ptr); + ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &s, 1); + ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, + dpp->own_boot_hash, 32); + ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1); + ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr, + k1, dpp->key_len, 2, + DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, + DPP_ATTR_RESPONDER_CAPABILITIES, + (size_t) 1, &dpp->role); + + l_debug("sending auth request failed"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_auth_confirm(struct dpp_sm *dpp) +{ + uint8_t frame[DPP_HDR_LEN + 256]; + uint8_t *ptr = frame; + uint8_t zero = 0; + + ptr += dpp_build_header(DPP_FRAME_AUTHENTICATION_CONFIRM, ptr); + ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &zero, 1); + ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, + dpp->peer_boot_hash, 32); + if (dpp->mutual_auth) + ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, + dpp->own_boot_hash, 32); + + ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr, + dpp->ke, dpp->key_len, 1, + DPP_ATTR_INITIATOR_AUTH_TAG, dpp->key_len, + dpp->auth_tag); + + l_debug("sending auth confirm"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_config_request(struct dpp_sm *dpp) +{ + const char *json = "{\"name\":\"IWD\",\"wi-fi_tech\":\"infra\"," + "\"netRole\":\"sta\"}"; + size_t json_len = strlen(json); + uint8_t frame[DPP_HDR_LEN + 512]; + uint8_t *ptr = frame; + uint8_t *lptr; + + l_getrandom(&dpp->diag_token, 1); + l_getrandom(dpp->e_nonce, dpp->nonce_len); + + ptr += dpp_build_config_header(true, dpp->diag_token, ptr); + + /* Save length location */ + lptr = ptr; + ptr += 2; + + ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr, + dpp->ke, dpp->key_len, 2, + DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce, + DPP_ATTR_CONFIGURATION_REQUEST, json_len, json); + + l_put_le16(ptr - lptr - 2, lptr); + + dpp->state = DPP_STATE_CONFIGURING; + + l_debug("sending config request"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status) +{ + _auto_(l_free) char *json = NULL; + uint8_t frame[512]; + size_t json_len; + uint8_t *ptr = frame; + uint8_t *lptr; + + ptr += dpp_build_config_header(false, dpp->diag_token, ptr); + + /* Save length location */ + lptr = ptr; + ptr += 2; + + ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1); + + /* + * There are several failure status codes that can be used (defined in + * 6.4.3.1), each with their own set of attributes that should be + * included. For now IWD's basic DPP implementation will assume + * STATUS_CONFIGURE_FAILURE which only includes the E-Nonce. + */ + if (status == DPP_STATUS_OK) { + json = dpp_configuration_to_json(dpp->config); + json_len = strlen(json); + + ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr, + dpp->ke, dpp->key_len, 2, + DPP_ATTR_ENROLLEE_NONCE, + dpp->nonce_len, dpp->e_nonce, + DPP_ATTR_CONFIGURATION_OBJECT, + json_len, json); + } else + ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr, + dpp->ke, dpp->key_len, 2, + DPP_ATTR_ENROLLEE_NONCE, + dpp->nonce_len, dpp->e_nonce); + + l_put_le16(ptr - lptr - 2, lptr); + + l_debug("sending config response"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_config_result(struct dpp_sm *dpp) +{ + uint8_t frame[DPP_HDR_LEN + 256]; + uint8_t *ptr = frame; + uint8_t zero = 0; + + memset(frame, 0, sizeof(frame)); + + ptr += dpp_build_header(DPP_FRAME_CONFIGURATION_RESULT, ptr); + ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr, + dpp->ke, dpp->key_len, 2, + DPP_ATTR_STATUS, (size_t) 1, &zero, + DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce); + + l_debug("sending config result"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_pkex_exchange_request(struct dpp_sm *dpp) +{ + uint8_t frame[DPP_HDR_LEN + 256]; + uint8_t *ptr = frame; + uint16_t group; + + memset(frame, 0, sizeof(frame)); + + l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group); + + ptr += dpp_build_header(DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST, ptr); + + 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)); + + ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, dpp->pkex_m); + + dpp->state = DPP_STATE_PKEX_EXCHANGE; + + l_debug("sending PKEX v1 exchange request"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_pkex_exchange_response(struct dpp_sm *dpp, + struct l_ecc_point *n) +{ + uint8_t frame[DPP_HDR_LEN + 256]; + uint8_t *ptr = frame; + uint16_t group; + uint8_t status = DPP_STATUS_OK; + + memset(frame, 0, sizeof(frame)); + + l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group); + + ptr += dpp_build_header(DPP_FRAME_PKEX_XCHG_RESPONSE, ptr); + 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)); + + ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, n); + + dpp->state = DPP_STATE_PKEX_COMMIT_REVEAL; + + l_debug("sending PKEX exchange response"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_pkex_bad_group(struct dpp_sm *dpp) +{ + uint8_t frame[DPP_HDR_LEN + 256]; + uint8_t *ptr = frame; + uint16_t group; + uint8_t status = DPP_STATUS_BAD_GROUP; + + memset(frame, 0, sizeof(frame)); + + l_put_le16(l_ecc_curve_get_ike_group(dpp->curve), &group); + + ptr += dpp_build_header(DPP_FRAME_PKEX_XCHG_RESPONSE, ptr); + 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); + + l_debug("sending PKEX bad group"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_pkex_bad_code(struct dpp_sm *dpp) +{ + uint8_t frame[DPP_HDR_LEN + 256]; + uint8_t *ptr = frame; + uint8_t status = DPP_STATUS_BAD_CODE; + + memset(frame, 0, sizeof(frame)); + + ptr += dpp_build_header(DPP_FRAME_PKEX_XCHG_RESPONSE, ptr); + 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)); + + l_debug("sending PKEX bad code"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_pkex_commit_reveal_request(struct dpp_sm *dpp) +{ + uint8_t frame[DPP_HDR_LEN + 512]; + uint8_t *ptr = frame; + uint8_t a_pub[L_ECC_POINT_MAX_BYTES]; + ssize_t a_len; + + memset(frame, 0, sizeof(frame)); + + a_len = l_ecc_point_get_data(dpp->boot_public, a_pub, sizeof(a_pub)); + + ptr += dpp_build_header(DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST, ptr); + + ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr, + dpp->z, dpp->z_len, 2, + DPP_ATTR_BOOTSTRAPPING_KEY, a_len, a_pub, + DPP_ATTR_INITIATOR_AUTH_TAG, dpp->u_len, dpp->u); + + dpp->state = DPP_STATE_PKEX_COMMIT_REVEAL; + + l_debug("sending PKEX commit reveal request"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +static void dpp_send_pkex_commit_reveal_response(struct dpp_sm *dpp, + const uint8_t *v, size_t v_len) +{ + uint8_t frame[DPP_HDR_LEN + 512]; + uint8_t *ptr = frame; + uint8_t b_pub[L_ECC_POINT_MAX_BYTES]; + size_t b_len; + + memset(frame, 0, sizeof(frame)); + + b_len = l_ecc_point_get_data(dpp->boot_public, b_pub, sizeof(b_pub)); + + ptr += dpp_build_header(DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE, ptr); + ptr += dpp_append_wrapped_data(frame, sizeof(frame), ptr, + dpp->z, dpp->z_len, 2, + DPP_ATTR_BOOTSTRAPPING_KEY, b_len, b_pub, + DPP_ATTR_RESPONDER_AUTH_TAG, v_len, v); + + l_debug("sending PKEX commit reveal response"); + + dpp_try_write(dpp, frame, ptr - frame); +} + +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; + + l_debug(""); + + /* Qi = H(MAC-Initiator | [identifier | ] code) * Pi */ + qi = dpp_derive_qi(dpp->curve, dpp->pkex_key, dpp->pkex_id, + dpp->mac_initiator); + 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_failed(dpp); + return; + } + + qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id, + dpp->mac_responder); + 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->mac_initiator, dpp->mac_responder, 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_pkex_bad_code(dpp); +} + struct dpp_sm *dpp_sm_new(dpp_event_cb_t event, const struct l_ecc_point *boot_public, const struct l_ecc_scalar *boot_private, @@ -201,7 +762,25 @@ void dpp_sm_set_write_handler(struct dpp_sm *dpp, dpp_write_cb_t write) if (!dpp->write) return; - /* Handle writing frame */ + /* + * Presence/exchange are special cases because 802.11 encapsulation + * requires going off channel for specific amounts of time. We cannot + * easily start a timer within the common code and try and remained + * synced with the offchannel callbacks. + * Instead let the offchannel callbacks dictate when these frames + * are sent out. All other write operations are driven by the reception + * of other frames. + */ + if (dpp->state == DPP_STATE_PRESENCE && !dpp->initiator) + dpp_send_presence(dpp); + else if ( dpp->state == DPP_STATE_PKEX_EXCHANGE && dpp->initiator) + dpp_send_pkex_exchange_request(dpp); + else if (dpp->frame_pending) { + dpp->write(dpp->frame_pending, dpp->frame_len, dpp->user_data); + + l_free(dpp->frame_pending); + dpp->frame_pending = NULL; + } } static void dpp_free_auth_data(struct dpp_sm *dpp) @@ -339,6 +918,9 @@ static bool dpp_start_authentication(struct dpp_sm *dpp) dpp->state = DPP_STATE_AUTHENTICATING; + if (dpp->initiator) + dpp_send_auth_request(dpp); + return true; } @@ -413,6 +995,16 @@ bool dpp_sm_start_responder(struct dpp_sm *dpp) &dpp->own_proto_public); dpp->initiator = false; + /* + * An Enrollee acting in a Responder role uses DPP Presence + * Announcement to signal a potential Configurator that it is + * ready to engage in a DPP exchange + */ + if (dpp->role == DPP_CAPABILITY_ENROLLEE) { + if (!dpp->skip_presence) + dpp_send_presence(dpp); + } + dpp->state = DPP_STATE_PRESENCE; return true; @@ -426,6 +1018,10 @@ void dpp_sm_set_pkex_identifier(struct dpp_sm *dpp, const char *identifier) void dpp_sm_set_pkex_key(struct dpp_sm *dpp, const char *key) { dpp->pkex_key = l_strdup(key); + + /* This was set after the key was requested, send the response now */ + if (dpp->state == DPP_STATE_PKEX_EXCHANGE) + dpp_process_pkex_exchange_request(dpp, dpp->peer_encr_key); } void dpp_sm_set_pkex_own_mac(struct dpp_sm *dpp, const uint8_t *mac) @@ -488,7 +1084,7 @@ bool dpp_sm_pkex_start_initiator(struct dpp_sm *dpp) dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT); - /* Send exchange request */ + dpp_send_pkex_exchange_request(dpp); return true; @@ -512,7 +1108,1649 @@ bool dpp_sm_pkex_start_responder(struct dpp_sm *dpp) return true; } -void dpp_handle_rx(struct dpp_sm *dpp, const uint8_t *data, size_t len) +static void dpp_handle_presence_announcement(struct dpp_sm *dpp, + const uint8_t *frame, + size_t frame_len) +{ + struct dpp_attr_iter iter; + enum dpp_attribute_type type; + size_t len; + const uint8_t *data; + const void *r_boot = NULL; + size_t r_boot_len = 0; + uint8_t hash[32]; + + l_debug("Presence announcement"); + + /* Must be a configurator, in an initiator role, in PRESENCE state */ + if (dpp->state != DPP_STATE_PRESENCE) + return; + + if (dpp->role != DPP_CAPABILITY_CONFIGURATOR) + return; + + dpp_attr_iter_init(&iter, frame + 6, frame_len - 6); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_RESPONDER_BOOT_KEY_HASH: + r_boot = data; + r_boot_len = len; + break; + default: + break; + } + } + + if (!r_boot || r_boot_len != 32) { + l_debug("No responder boot hash"); + return; + } + + /* Hash what we have for the peer and check its our enrollee */ + dpp_hash(L_CHECKSUM_SHA256, hash, 2, "chirp", strlen("chirp"), + dpp->peer_asn1, dpp->peer_asn1_len); + + if (memcmp(hash, r_boot, sizeof(hash))) { + l_debug("Peers boot hash did not match"); + return; + } + + if (dpp->event_cb) + dpp->event_cb(DPP_EVENT_PEER_ACCEPTED, NULL, dpp->user_data); + + dpp->state = DPP_STATE_AUTHENTICATING; + + dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT); + + dpp_start_authentication(dpp); +} + +static void dpp_handle_auth_request(struct dpp_sm *dpp, + const uint8_t *frame, size_t frame_len) { - /* Handle frame */ + struct dpp_attr_iter iter; + enum dpp_attribute_type type; + size_t len; + const uint8_t *data; + const uint8_t *r_boot = NULL; + const uint8_t *i_boot = NULL; + const uint8_t *i_proto = NULL; + const void *wrapped = NULL; + const uint8_t *i_nonce = NULL; + uint8_t i_capa = 0; + size_t r_boot_len = 0, i_proto_len = 0, wrapped_len = 0; + size_t i_nonce_len = 0; + _auto_(l_free) uint8_t *unwrapped = NULL; + _auto_(l_ecc_scalar_free) struct l_ecc_scalar *m = NULL; + _auto_(l_ecc_scalar_free) struct l_ecc_scalar *n = NULL; + _auto_(l_ecc_point_free) struct l_ecc_point *l = NULL; + struct l_ecc_point *bi = NULL; + uint64_t k1[L_ECC_MAX_DIGITS]; + const void *ad0 = frame; + const void *ad1 = frame + 6; + + if (dpp->state != DPP_STATE_PRESENCE && + dpp->state != DPP_STATE_AUTHENTICATING) + return; + + l_debug("authenticate request"); + + dpp_attr_iter_init(&iter, frame + 6, frame_len - 6); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_INITIATOR_BOOT_KEY_HASH: + i_boot = data; + /* + * This attribute is required by the spec, but only + * used for mutual authentication. + */ + break; + case DPP_ATTR_RESPONDER_BOOT_KEY_HASH: + r_boot = data; + r_boot_len = len; + break; + case DPP_ATTR_INITIATOR_PROTOCOL_KEY: + i_proto = data; + i_proto_len = len; + break; + case DPP_ATTR_WRAPPED_DATA: + /* I-Nonce/I-Capabilities part of wrapped data */ + wrapped = data; + wrapped_len = len; + break; + + /* Optional attributes */ + case DPP_ATTR_PROTOCOL_VERSION: + if (l_get_u8(data) != 2) { + l_debug("Protocol version did not match"); + return; + } + + break; + + case DPP_ATTR_CHANNEL: + if (len != 2) + return; + + /* + * Not part of the spec, but IWD puts a requirement on + * enrollees that they must come to the configurators + * channel to preserve performance + */ + if (dpp->role == DPP_CAPABILITY_CONFIGURATOR) + break; + + if (dpp->event_cb) + dpp->event_cb(DPP_EVENT_CHANNEL_SWITCH, data, + dpp->user_data); + + break; + default: + break; + } + } + + if (!r_boot || !i_boot || !i_proto || !wrapped) { + l_debug("missing attributes"); + goto auth_request_failed; + } + + if (r_boot_len != 32 || memcmp(dpp->own_boot_hash, + r_boot, r_boot_len)) { + l_debug("Responder boot key hash failed to verify"); + goto auth_request_failed; + } + + dpp->peer_proto_public = l_ecc_point_from_data(dpp->curve, + L_ECC_POINT_TYPE_FULL, + i_proto, i_proto_len); + if (!dpp->peer_proto_public) { + l_debug("Initiators protocol key invalid"); + goto auth_request_failed; + } + + m = dpp_derive_k1(dpp->peer_proto_public, dpp->boot_private, k1); + if (!m) { + l_debug("deriving k1/m failed"); + goto auth_request_failed; + } + + unwrapped = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1, + k1, dpp->key_len, wrapped, wrapped_len, &wrapped_len); + if (!unwrapped) { + l_debug("failed to unwrap data"); + goto auth_request_failed; + } + + dpp_attr_iter_init(&iter, unwrapped, wrapped_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_INITIATOR_NONCE: + i_nonce = data; + i_nonce_len = len; + break; + case DPP_ATTR_INITIATOR_CAPABILITIES: + /* + * "If the Responder is not capable of supporting the + * role indicated by the Initiator, it shall respond + * with a DPP Authentication Response frame indicating + * failure by adding the DPP Status field set to + * STATUS_NOT_COMPATIBLE" + */ + i_capa = l_get_u8(data); + + if (!dpp_check_roles(dpp, i_capa)) { + l_debug("Peer does not support required role"); + dpp_send_auth_request_failed(dpp, + DPP_STATUS_NOT_COMPATIBLE, k1); + goto auth_request_failed; + } + + break; + default: + break; + } + } + + if (i_nonce_len != dpp->nonce_len) { + l_debug("I-Nonce has unexpected length %zu", i_nonce_len); + goto auth_request_failed; + } + + memcpy(dpp->i_nonce, i_nonce, i_nonce_len); + + if (dpp->mutual_auth) { + l = dpp_derive_lr(dpp->boot_private, dpp->proto_private, + dpp->peer_boot_public); + bi = dpp->peer_boot_public; + } + + /* Derive keys k2, ke, and R-Auth for authentication response */ + + n = dpp_derive_k2(dpp->peer_proto_public, dpp->proto_private, dpp->k2); + if (!n) { + l_debug("deriving k2/n failed"); + goto auth_request_failed; + } + + l_getrandom(dpp->r_nonce, dpp->nonce_len); + + if (!dpp_derive_ke(dpp->i_nonce, dpp->r_nonce, m, n, l, dpp->ke)) { + l_debug("deriving ke failed"); + goto auth_request_failed; + } + + if (!dpp_derive_r_auth(dpp->i_nonce, dpp->r_nonce, dpp->nonce_len, + dpp->peer_proto_public, dpp->own_proto_public, + bi, dpp->boot_public, dpp->auth_tag)) { + l_debug("deriving R_auth failed"); + goto auth_request_failed; + } + + dpp->state = DPP_STATE_AUTHENTICATING; + + dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT); + + dpp_send_auth_response(dpp); + + return; + +auth_request_failed: + l_debug("handle auth request failed"); + dpp->state = DPP_STATE_PRESENCE; + dpp_free_auth_data(dpp); +} + +static void dpp_handle_auth_response(struct dpp_sm *dpp, + const uint8_t *frame, size_t frame_len) +{ + struct dpp_attr_iter iter; + enum dpp_attribute_type type; + size_t len; + const uint8_t *data; + int status = -1; + const void *r_boot_hash = NULL; + const void *r_proto = NULL; + size_t r_proto_len = 0; + const void *wrapped = NULL; + size_t wrapped_len; + _auto_(l_free) uint8_t *unwrapped1 = NULL; + _auto_(l_free) uint8_t *unwrapped2 = NULL; + const void *r_nonce = NULL; + const void *i_nonce = NULL; + const void *r_auth = NULL; + _auto_(l_ecc_point_free) struct l_ecc_point *r_proto_key = NULL; + _auto_(l_ecc_scalar_free) struct l_ecc_scalar *n = NULL; + _auto_(l_ecc_point_free) struct l_ecc_point *l = NULL; + struct l_ecc_point *bi = NULL; + const void *ad0 = frame; + const void *ad1 = frame + 6; + uint64_t r_auth_derived[L_ECC_MAX_DIGITS]; + + l_debug("Authenticate response"); + + if (dpp->state != DPP_STATE_AUTHENTICATING) + return; + + dpp_attr_iter_init(&iter, frame + 6, frame_len - 6); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_STATUS: + if (len != 1) + return; + + status = l_get_u8(data); + break; + case DPP_ATTR_RESPONDER_BOOT_KEY_HASH: + r_boot_hash = data; + break; + case DPP_ATTR_RESPONDER_PROTOCOL_KEY: + r_proto = data; + r_proto_len = len; + break; + case DPP_ATTR_PROTOCOL_VERSION: + if (len != 1 || l_get_u8(data) != 2) + return; + break; + case DPP_ATTR_WRAPPED_DATA: + wrapped = data; + wrapped_len = len; + break; + default: + break; + } + } + + if (status != DPP_STATUS_OK || !r_boot_hash || !r_proto || !wrapped) { + l_debug("Auth response bad status or missing attributes"); + return; + } + + r_proto_key = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL, + r_proto, r_proto_len); + if (!r_proto_key) { + l_debug("Peers protocol key was invalid"); + return; + } + + n = dpp_derive_k2(r_proto_key, dpp->proto_private, dpp->k2); + + unwrapped1 = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1, dpp->k2, + dpp->key_len, wrapped, wrapped_len, + &wrapped_len); + if (!unwrapped1) { + l_debug("Failed to unwrap primary data"); + return; + } + + wrapped = NULL; + + dpp_attr_iter_init(&iter, unwrapped1, wrapped_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_RESPONDER_NONCE: + if (len != dpp->nonce_len) + return; + + r_nonce = data; + break; + case DPP_ATTR_INITIATOR_NONCE: + if (len != dpp->nonce_len) + return; + + i_nonce = data; + break; + case DPP_ATTR_RESPONDER_CAPABILITIES: + break; + case DPP_ATTR_WRAPPED_DATA: + wrapped = data; + wrapped_len = len; + break; + default: + break; + } + } + + if (!r_nonce || !i_nonce || !wrapped) { + l_debug("Wrapped data missing attributes"); + return; + } + + if (dpp->mutual_auth) { + l = dpp_derive_li(dpp->peer_boot_public, r_proto_key, + dpp->boot_private); + bi = dpp->boot_public; + } + + if (!dpp_derive_ke(i_nonce, r_nonce, dpp->m, n, l, dpp->ke)) { + l_debug("Failed to derive ke"); + return; + } + + unwrapped2 = dpp_unwrap_attr(NULL, 0, NULL, 0, dpp->ke, dpp->key_len, + wrapped, wrapped_len, &wrapped_len); + if (!unwrapped2) { + l_debug("Failed to unwrap secondary data"); + return; + } + + dpp_attr_iter_init(&iter, unwrapped2, wrapped_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_RESPONDER_AUTH_TAG: + if (len != dpp->key_len) + return; + + r_auth = data; + break; + default: + break; + } + } + + if (!r_auth) { + l_debug("R-Auth was not in secondary wrapped data"); + return; + } + + if (!dpp_derive_r_auth(i_nonce, r_nonce, dpp->nonce_len, + dpp->own_proto_public, r_proto_key, bi, + dpp->peer_boot_public, r_auth_derived)) { + l_debug("Failed to derive r_auth"); + return; + } + + if (memcmp(r_auth, r_auth_derived, dpp->key_len)) { + l_debug("R-Auth did not verify"); + return; + } + + if (!dpp_derive_i_auth(r_nonce, i_nonce, dpp->nonce_len, + r_proto_key, dpp->own_proto_public, + dpp->peer_boot_public, bi, dpp->auth_tag)) { + l_debug("Could not derive I-Auth"); + return; + } + + dpp_send_auth_confirm(dpp); + + if (dpp->role == DPP_CAPABILITY_ENROLLEE) + dpp_send_config_request(dpp); +} + +static void dpp_handle_auth_confirm(struct dpp_sm *dpp, + const uint8_t *frame, size_t frame_len) +{ + struct dpp_attr_iter iter; + enum dpp_attribute_type type; + size_t len; + const uint8_t *data; + int status = -1; + const uint8_t *r_boot_hash = NULL; + const void *wrapped = NULL; + const uint8_t *i_auth = NULL; + size_t i_auth_len; + _auto_(l_free) uint8_t *unwrapped = NULL; + size_t wrapped_len = 0; + uint64_t i_auth_check[L_ECC_MAX_DIGITS]; + const void *unwrap_key; + const void *ad0 = frame; + const void *ad1 = frame + 6; + struct l_ecc_point *bi = NULL; + + if (dpp->state != DPP_STATE_AUTHENTICATING) + return; + + l_debug("authenticate confirm"); + + dpp_attr_iter_init(&iter, frame + 6, frame_len - 6); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_STATUS: + status = l_get_u8(data); + break; + case DPP_ATTR_RESPONDER_BOOT_KEY_HASH: + r_boot_hash = data; + /* + * Spec requires this, but does not mention if anything + * is to be done with it. + */ + break; + case DPP_ATTR_INITIATOR_BOOT_KEY_HASH: + /* No mutual authentication */ + break; + case DPP_ATTR_WRAPPED_DATA: + wrapped = data; + wrapped_len = len; + break; + default: + break; + } + } + + if (!r_boot_hash || !wrapped) { + l_debug("Attributes missing from authenticate confirm"); + return; + } + + /* + * "The Responder obtains the DPP Authentication Confirm frame and + * checks the value of the DPP Status field. If the value of the DPP + * Status field is STATUS_NOT_COMPATIBLE or STATUS_AUTH_FAILURE, the + * Responder unwraps the wrapped data portion of the frame using k2" + */ + if (status == DPP_STATUS_OK) + unwrap_key = dpp->ke; + else if (status == DPP_STATUS_NOT_COMPATIBLE || + status == DPP_STATUS_AUTH_FAILURE) + unwrap_key = dpp->k2; + else + goto auth_confirm_failed; + + unwrapped = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1, + unwrap_key, dpp->key_len, wrapped, wrapped_len, + &wrapped_len); + if (!unwrapped) + goto auth_confirm_failed; + + if (status != DPP_STATUS_OK) { + /* + * "If unwrapping is successful, the Responder should generate + * an alert indicating the reason for the protocol failure." + */ + l_debug("Authentication failed due to status %s", + status == DPP_STATUS_NOT_COMPATIBLE ? + "NOT_COMPATIBLE" : "AUTH_FAILURE"); + goto auth_confirm_failed; + } + + dpp_attr_iter_init(&iter, unwrapped, wrapped_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_INITIATOR_AUTH_TAG: + i_auth = data; + i_auth_len = len; + break; + case DPP_ATTR_RESPONDER_NONCE: + /* Only if error */ + break; + default: + break; + } + } + + if (!i_auth || i_auth_len != dpp->key_len) { + l_debug("I-Auth missing from wrapped data"); + goto auth_confirm_failed; + } + + if (dpp->mutual_auth) + bi = dpp->peer_boot_public; + + dpp_derive_i_auth(dpp->r_nonce, dpp->i_nonce, dpp->nonce_len, + dpp->own_proto_public, dpp->peer_proto_public, + dpp->boot_public, bi, i_auth_check); + + if (memcmp(i_auth, i_auth_check, i_auth_len)) { + l_error("I-Auth did not verify"); + goto auth_confirm_failed; + } + + l_debug("Authentication successful"); + + if (dpp->role == DPP_CAPABILITY_ENROLLEE) + dpp_send_config_request(dpp); + + return; + +auth_confirm_failed: + dpp->state = DPP_STATE_PRESENCE; + dpp_free_auth_data(dpp); +} + +/* Parses the config header (GAS) and returns the start of the payload */ +static const uint8_t *dpp_parse_config_header(uint8_t action, + const uint8_t *data, size_t len, + uint8_t *diag_token_out, + uint16_t *status_out, + uint16_t *comeback_out, + size_t *attr_len_out) +{ + uint8_t adv_protocol_id[] = { 0xDD, 0x05, 0x50, 0x6F, + 0x9A, 0x1A, 0x01 }; + uint8_t diag_token; + uint16_t status = 0; + uint16_t comeback = 0; + uint16_t attr_len; + const uint8_t *ptr = data; + + if (len < 1) + return NULL; + + diag_token = *ptr++; + + switch (action) { + case DPP_ACTION_GAS_REQUEST: + if (len < 12) + return NULL; + break; + case DPP_ACTION_GAS_RESPONSE: + if (len < 14) + return NULL; + + status = l_get_le16(ptr); + ptr += 2; + comeback = l_get_le16(ptr); + ptr += 2; + + break; + default: + return NULL; + } + + if (*ptr++ != IE_TYPE_ADVERTISEMENT_PROTOCOL) + return NULL; + + /* Length of advertisement protocol fields */ + if (*ptr++ != 0x08) + return NULL; + /* + * Unfortunately wpa_supplicant hard codes 0x7f as the Query Response + * Info so we need to handle both cases. + */ + if (*ptr != 0x7f && *ptr != 0x00) + return NULL; + ptr++; + + if (memcmp(ptr, adv_protocol_id, sizeof(adv_protocol_id))) + return NULL; + + ptr += sizeof(adv_protocol_id); + + attr_len = l_get_le16(ptr); + ptr += 2; + + /* Check the attribute length matches the total length */ + if (attr_len > len - (ptr - data)) + return false; + + if (diag_token_out) + *diag_token_out = diag_token; + + if (action == DPP_ACTION_GAS_RESPONSE) { + if (status_out) + *status_out = status; + if (comeback_out) + *comeback_out = comeback; + } + + if (attr_len_out) + *attr_len_out = attr_len; + + return ptr; +} + +static void dpp_handle_config_request(struct dpp_sm *dpp, const uint8_t *frame, + size_t frame_len) +{ + const uint8_t *attrs; + size_t attrs_len; + struct dpp_attr_iter iter; + enum dpp_attribute_type type; + const uint8_t *data; + size_t len; + const char *json = NULL; + size_t json_len = 0; + struct json_contents *c; + const uint8_t *wrapped = NULL; + const uint8_t *e_nonce = NULL; + size_t wrapped_len = 0; + _auto_(l_free) uint8_t *unwrapped = NULL; + struct json_iter jsiter; + _auto_(l_free) char *tech = NULL; + _auto_(l_free) char *role = NULL; + + attrs = dpp_parse_config_header(DPP_ACTION_GAS_REQUEST, frame, + frame_len, &dpp->diag_token, + NULL, NULL, &attrs_len); + if (!attrs) + return; + + if (dpp->state != DPP_STATE_AUTHENTICATING) { + l_debug("Configuration request in wrong state"); + return; + } + + if (dpp->role != DPP_CAPABILITY_CONFIGURATOR) + return; + + dpp_attr_iter_init(&iter, attrs, attrs_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_WRAPPED_DATA: + wrapped = data; + wrapped_len = len; + break; + default: + /* Wrapped data should be only attribute */ + return; + } + } + + if (!wrapped) { + l_debug("Wrapped data missing"); + return; + } + + unwrapped = dpp_unwrap_attr(NULL, 0, NULL, 0, dpp->ke, + dpp->key_len, wrapped, wrapped_len, + &wrapped_len); + if (!unwrapped) { + l_debug("Failed to unwrap"); + return; + } + + dpp_attr_iter_init(&iter, unwrapped, wrapped_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_ENROLLEE_NONCE: + if (len != dpp->nonce_len) + break; + + e_nonce = data; + break; + case DPP_ATTR_CONFIGURATION_REQUEST: + json = (const char *)data; + json_len = len; + break; + default: + break; + } + } + + if (!json || !e_nonce) { + l_debug("No configuration object in response"); + return; + } + + c = json_contents_new(json, json_len); + if (!c) { + json_contents_free(c); + return; + } + + json_iter_init(&jsiter, c); + + /* + * Check mandatory values (Table 7). There isn't much that can be done + * with these, but the spec requires they be included. + */ + if (!json_iter_parse(&jsiter, + JSON_MANDATORY("name", JSON_STRING, NULL), + JSON_MANDATORY("wi-fi_tech", JSON_STRING, &tech), + JSON_MANDATORY("netRole", JSON_STRING, &role), + JSON_UNDEFINED)) + goto configure_failure; + + if (strcmp(tech, "infra")) + goto configure_failure; + + if (strcmp(role, "sta")) + goto configure_failure; + + json_contents_free(c); + + memcpy(dpp->e_nonce, e_nonce, dpp->nonce_len); + + dpp->state = DPP_STATE_CONFIGURING; + + dpp_send_config_response(dpp, DPP_STATUS_OK); + + return; + +configure_failure: + dpp_send_config_response(dpp, DPP_STATUS_CONFIGURE_FAILURE); + /* + * The other peer is still authenticated, and can potentially send + * additional requests so keep this session alive. + */ +} + +static void dpp_handle_config_response(struct dpp_sm *dpp, const uint8_t *frame, + size_t frame_len) +{ + const uint8_t *attrs; + size_t attrs_len; + uint16_t status; + uint8_t diag_token; + uint16_t comeback; + struct dpp_attr_iter iter; + enum dpp_attribute_type type; + size_t len; + const uint8_t *data; + const char *json = NULL; + size_t json_len = 0; + int dstatus = -1; + const uint8_t *wrapped = NULL; + const uint8_t *e_nonce = NULL; + size_t wrapped_len = 0; + _auto_(l_free) uint8_t *unwrapped = NULL; + + l_debug("config response"); + + attrs = dpp_parse_config_header(DPP_ACTION_GAS_RESPONSE, frame, + frame_len, &diag_token, &status, + &comeback, &attrs_len); + if (!attrs || diag_token != dpp->diag_token || status != 0) { + l_debug("failed to parse header"); + return; + } + + /* TODO: comeback delay */ + if (comeback != 0) { + l_debug("comeback not zero"); + return; + } + + if (dpp->state != DPP_STATE_CONFIGURING) { + l_debug("state not configuring"); + return; + } + + + dpp_attr_iter_init(&iter, attrs, attrs_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_STATUS: + dstatus = l_get_u8(data); + break; + case DPP_ATTR_WRAPPED_DATA: + wrapped = data; + wrapped_len = len; + break; + default: + /* + * TODO: CSR Attribute + */ + break; + } + } + + if (dstatus != DPP_STATUS_OK || !wrapped) { + l_debug("Bad status or missing attributes"); + return; + } + + unwrapped = dpp_unwrap_attr(attrs, wrapped - attrs - 4, NULL, 0, dpp->ke, + dpp->key_len, wrapped, wrapped_len, + &wrapped_len); + if (!unwrapped) { + l_debug("Failed to unwrap"); + return; + } + + dpp_attr_iter_init(&iter, unwrapped, wrapped_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_ENROLLEE_NONCE: + if (len != dpp->nonce_len) + break; + + if (memcmp(data, dpp->e_nonce, dpp->nonce_len)) + break; + + e_nonce = data; + break; + case DPP_ATTR_CONFIGURATION_OBJECT: + json = (const char *)data; + json_len = len; + break; + default: + break; + } + } + + if (!json || !e_nonce) { + l_debug("No configuration object in response"); + return; + } + + dpp->config = dpp_parse_configuration_object(json, json_len); + if (!dpp->config) { + l_error("Configuration object did not parse"); + return; + } + + dpp_send_config_result(dpp); + + l_timeout_remove(dpp->timeout); + dpp->timeout = NULL; + + if (dpp->event_cb) + dpp->event_cb(DPP_EVENT_SUCCESS, NULL, dpp->user_data); +} + +static void dpp_handle_config_result_frame(struct dpp_sm *dpp, + const uint8_t *frame, + size_t frame_len) +{ + struct dpp_attr_iter iter; + enum dpp_attribute_type type; + size_t len; + const uint8_t *data; + int status = -1; + const void *e_nonce = NULL; + const void *wrapped = NULL; + size_t wrapped_len; + _auto_(l_free) void *unwrapped = NULL; + const void *ad0 = frame; + const void *ad1 = frame + 6; + + if (dpp->state != DPP_STATE_CONFIGURING) + return; + + dpp_attr_iter_init(&iter, frame + 6, frame_len - 6); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_WRAPPED_DATA: + wrapped = data; + wrapped_len = len; + break; + default: + /* Wrapped data should be only attribute */ + return; + } + } + + if (!wrapped) { + l_debug("No wrapped data in config result"); + return; + } + + unwrapped = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1, + dpp->ke, dpp->key_len, wrapped, + wrapped_len, &wrapped_len); + if (!unwrapped) { + l_debug("Failed to unwrap DPP configuration result"); + return; + } + + dpp_attr_iter_init(&iter, unwrapped, wrapped_len); + + while (dpp_attr_iter_next(&iter, &type, &len, &data)) { + switch (type) { + case DPP_ATTR_STATUS: + status = l_get_u8(data); + break; + case DPP_ATTR_ENROLLEE_NONCE: + e_nonce = data; + break; + default: + break; + } + } + + l_timeout_remove(dpp->timeout); + dpp->timeout = NULL; + + if (!dpp->event_cb) + return; + + if (status != DPP_STATUS_OK || !e_nonce) + dpp_failed(dpp); + else + dpp->event_cb(DPP_EVENT_SUCCESS, NULL, dpp->user_data); +} + +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, + dpp->mac_responder); + 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 *frame, + size_t frame_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; + + l_debug("PKEX exchange response"); + + if (dpp->state != DPP_STATE_PKEX_EXCHANGE) + return; + + if (dpp->role != DPP_CAPABILITY_ENROLLEE) + return; + + dpp_attr_iter_init(&iter, frame + 6, frame_len - 6); + + 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; + } + + /* + * TODO: PKEX protocol version 2 does not require the initiator or + * responder MAC addresses. If using protocol version 2 there will + * be no requirement for the encapsulating protocol to set this. + * + * Until then (and always for protocol 1) the encapsulating protocol + * must set the peers address in the PEER_ACCEPTED callback for PKEX. + */ + if (dpp->event_cb) + dpp->event_cb(DPP_EVENT_PEER_ACCEPTED, NULL, dpp->user_data); + + if (L_WARN_ON(!dpp->mac_responder)) + goto failed; + + qr = dpp_derive_qr(dpp->curve, dpp->pkex_key, dpp->pkex_id, + dpp->mac_responder); + 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(dpp->mac_initiator, dpp->mac_responder, 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, dpp->mac_initiator, dpp->boot_public, dpp->y_or_x, + dpp->pkex_public, dpp->u, &dpp->u_len)) { + l_debug("failed to compute u"); + goto failed; + } + + dpp_send_pkex_commit_reveal_request(dpp); + + 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_failed(dpp); +} + +static void dpp_handle_pkex_v1_exchange_request(struct dpp_sm *dpp, + const uint8_t *frame, size_t frame_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"); + + if (dpp->state != DPP_STATE_PKEX_EXCHANGE) + return; + + if (dpp->role != DPP_CAPABILITY_CONFIGURATOR) + return; + + dpp_attr_iter_init(&iter, frame + 6, frame_len - 6); + + 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; + } + + if (dpp->event_cb) + dpp->event_cb(DPP_EVENT_PEER_ACCEPTED, NULL, dpp->user_data); + + 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); + + if (dpp->event_cb) + dpp->event_cb(DPP_EVENT_PKEX_KEY_REQUESTED, + dpp->pkex_id, dpp->user_data); + + /* 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; + +bad_group: + dpp_send_pkex_bad_group(dpp); +} + +static void dpp_handle_pkex_commit_reveal_request(struct dpp_sm *dpp, + const uint8_t *frame, size_t frame_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; + _auto_(l_ecc_point_free) struct l_ecc_point *peer_public = 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; + + l_debug("PKEX commit-reveal request"); + + if (dpp->state != DPP_STATE_PKEX_COMMIT_REVEAL) + return; + + if (dpp->role != DPP_CAPABILITY_CONFIGURATOR) + return; + + dpp_attr_iter_init(&iter, frame + 6, frame_len - 6); + + 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(frame, 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; + } + + peer_public = l_ecc_point_from_data(dpp->curve, + L_ECC_POINT_TYPE_FULL, key, key_len); + if (!peer_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, peer_public); + + if (!dpp_derive_u(j, dpp->mac_initiator, peer_public, + dpp->pkex_public, dpp->y_or_x, u, &u_len)) { + l_debug("Failed to derive u"); + goto failed; + } + + 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, dpp->mac_responder, dpp->boot_public, dpp->y_or_x, + dpp->pkex_public, v, &v_len)) { + l_debug("Failed to derive v"); + goto failed; + } + + dpp_sm_set_peer_bootstrap(dpp, peer_public); + + dpp_send_pkex_commit_reveal_response(dpp, v, v_len); + + dpp->mutual_auth = true; + dpp_start_authentication(dpp); + + return; + +failed: + dpp_failed(dpp); +} + +static void dpp_handle_pkex_commit_reveal_response(struct dpp_sm *dpp, + const uint8_t *frame, size_t frame_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; + _auto_(l_ecc_point_free) struct l_ecc_point *peer_boot = NULL; + + l_debug("PKEX commit reveal response"); + + if (dpp->state != DPP_STATE_PKEX_COMMIT_REVEAL) + return; + + if (dpp->role != DPP_CAPABILITY_ENROLLEE) + return; + + dpp_attr_iter_init(&iter, frame + 6, frame_len - 6); + + 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(frame, 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; + } + } + + peer_boot = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL, + boot_key, boot_key_len); + if (!peer_boot) { + 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, peer_boot); + + if (!dpp_derive_v(l, dpp->mac_responder, peer_boot, + 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; + } + + dpp_sm_set_peer_bootstrap(dpp, peer_boot); + + dpp->mutual_auth = true; + dpp_start_authentication(dpp); + + return; + +failed: + dpp_failed(dpp); +} + +static bool dpp_validate_header(const uint8_t *data, size_t len, + enum dpp_frame_type *type) +{ + /* Ensure header + message type */ + if (len < 6) + return false; + + if (memcmp(data, wifi_alliance_oui, 3)) + return false; + + /* WiFi Allicance DPP OI type */ + if (data[3] != 0x1a) + return false; + + /* Cryptosuite */ + if (data[4] != 1) + return false; + + if (type) + *type = data[5]; + + return true; +} + +static void dpp_handle_frame(struct dpp_sm *dpp, + const uint8_t *data, size_t len) +{ + enum dpp_frame_type type; + + if (!dpp_validate_header(data, len, &type)) { + l_debug("header did not validate"); + return; + } + + switch (type) { + case DPP_FRAME_AUTHENTICATION_REQUEST: + dpp_handle_auth_request(dpp, data, len); + break; + case DPP_FRAME_AUTHENTICATION_RESPONSE: + dpp_handle_auth_response(dpp, data, len); + break; + case DPP_FRAME_AUTHENTICATION_CONFIRM: + dpp_handle_auth_confirm(dpp, data, len); + break; + case DPP_FRAME_CONFIGURATION_RESULT: + dpp_handle_config_result_frame(dpp, data, len); + break; + case DPP_FRAME_PRESENCE_ANNOUNCEMENT: + dpp_handle_presence_announcement(dpp, data, len); + break; + case DPP_FRAME_PKEX_XCHG_RESPONSE: + dpp_handle_pkex_exchange_response(dpp, data, len); + break; + case DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE: + dpp_handle_pkex_commit_reveal_response(dpp, data, len); + break; + case DPP_FRAME_PKEX_VERSION1_XCHG_REQUEST: + dpp_handle_pkex_v1_exchange_request(dpp, data, len); + break; + case DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST: + dpp_handle_pkex_commit_reveal_request(dpp, data, len); + break; + default: + l_debug("Unhandled DPP frame %u", type); + break; + } +} + +void dpp_handle_rx(struct dpp_sm *dpp, const uint8_t *data, size_t len) +{ + if (len < 1) + return; + + switch (*data) { + case DPP_ACTION_VENDOR_SPECIFIC: + dpp_handle_frame(dpp, data + 1, len - 1); + break; + /* For the GAS frames save the action byte to validate the frame */ + case DPP_ACTION_GAS_REQUEST: + dpp_handle_config_request(dpp, data + 1, len - 1); + break; + case DPP_ACTION_GAS_RESPONSE: + dpp_handle_config_response(dpp, data + 1, len - 1); + break; + default: + break; + } } From patchwork Wed Mar 27 15:19:55 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13606776 Received: from mail-ot1-f49.google.com (mail-ot1-f49.google.com [209.85.210.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 9D0BA12E1F4 for ; Wed, 27 Mar 2024 15:20:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552813; cv=none; b=afBkzGq6xh1FbmGUFueCh3UrtPeswTr6aepqJpzSgyiq5obnbUi2nX4zRnEE2oodhrnLYS5CpMFXRZVYye7dkbP5AxHEyBN2xRWrzY396Ifb9KjDH6FqiIdA+EiZih5I0aHOqsuHexLpz0dzrVEkaT6/sZ6NnGeAID+n5rFlj1k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552813; c=relaxed/simple; bh=qH7OW1nI7s51hIOMmEexgrPbRzsK10IRp9nvVO0EQiU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=rnELuL1iUmE4J+ZOZY9WjBqSp6SlmfN1pVOf6jByN6OBrbA9FnfPsYFcTADFMn4j7FGDjsJQHxSZv3goYY26U7nHg6qAZ48pMjlXdYjwBHrRS5nV/pVEHjxEF3T+DEKiG4rsoUwU6NzO0LIrw+wfB3ZO0xh/3rXvsim9VoQmDU4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=FzcbSsJF; arc=none smtp.client-ip=209.85.210.49 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="FzcbSsJF" Received: by mail-ot1-f49.google.com with SMTP id 46e09a7af769-6e6e37d33c5so1228193a34.2 for ; Wed, 27 Mar 2024 08:20:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711552810; x=1712157610; 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=xk9QlwTkGVz8zuUU9zzjxqyAOXZ89ug2efi/CErU4NE=; b=FzcbSsJFIaQYT6zGfKe+KAzx9lTUvkdlkzzxkvZf7GJ5ESd1o8xqC6+32EBI9eI9lC jWLDvK3mhw7SS/MOfbXmdcD2450UaounCBxAH5yGYepAQ+dZG+J5iCAnSFWDzhorqNk1 xwnCuJ63u8Gjck949cuiziRxheJ2NcjtLE8Iq7/Q1r3kaMMlu7Z68Aoj3G6dSlYVB/i4 JJZK8asEv3wUtuxpnauQOylz4XxwIt5dbLNHgbhSDYWTpHo5GyZ15oTznbaSmFxAL/ts htVreo7xQ7MnvSK11OcIXz7HXOmgFH3vi1jTVVX6DGNPTFMMwGOVHIWTUvo44xsLLr8+ 5JZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711552810; x=1712157610; 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=xk9QlwTkGVz8zuUU9zzjxqyAOXZ89ug2efi/CErU4NE=; b=qvec8+zygQWjbsIUJm40ifAJf4XVw2vrF2mHzmlyre5dsK0HU74C0D8iOduyLNC5aq jJXjVSibUxHinphl1pw9fZUPQH3wQXt81UrNKpl3PLWX8VCnMUgbcqSKMAIhjtCBaynF x7VWnJLThokNXm2cy/c6NyAf+M69kMPsrzBpCMgLYojjV0ehh4UXHqIORheyPDyhLv84 rE7ryEiXLyu+sjmfQTOTT7uUAEhK50MrQHGIHQQkWPITv4o8B3AyLhUfAftSh1OpdUrd IrIaMcJh/WHg25e/BEhTtuIo9GLO1TLITRWGuwGIn8mnRD+j9K1mz/u95YGfw/MvjmH0 wnaQ== X-Gm-Message-State: AOJu0YwTajC6D7t2twVp8vbZtEA7UkgYh35zf9GVcXZyen7C1GJ9NRqG a+8eWPAQ+ZRRmfYE/n1KFC+Do/0OrHU1vNUr2AJFdlzyg6peq/60ABjiG2oC X-Google-Smtp-Source: AGHT+IEX+13ZUTzS+RYnLDbXjNqeoFZiGcl7Bx7c7ioFHDhT1PJUF8O47rjwuWuYluJzwhkuN4APmw== X-Received: by 2002:a05:6830:410d:b0:6e6:d042:87dd with SMTP id w13-20020a056830410d00b006e6d04287ddmr344638ott.35.1711552810458; Wed, 27 Mar 2024 08:20:10 -0700 (PDT) Received: from LOCLAP699.rst-02.locus ([208.195.13.130]) by smtp.gmail.com with ESMTPSA id 7-20020a056214202700b006968ce99b7esm3520782qvf.34.2024.03.27.08.20.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Mar 2024 08:20:10 -0700 (PDT) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH 7/9] build: add dpp-common.{c,h} Date: Wed, 27 Mar 2024 08:19:55 -0700 Message-Id: <20240327151957.1446149-7-prestwoj@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240327151957.1446149-1-prestwoj@gmail.com> References: <20240327151957.1446149-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index be287572..e9f690f2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -265,6 +265,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h src/missing.h \ src/dpp-util.h src/dpp-util.c \ src/json.h src/json.c \ src/dpp.c \ + src/dpp-common.h src/dpp-common.c \ $(eap_sources) \ $(builtin_sources) From patchwork Wed Mar 27 15:19:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13606779 Received: from mail-qk1-f172.google.com (mail-qk1-f172.google.com [209.85.222.172]) (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 6943F12F393 for ; Wed, 27 Mar 2024 15:20:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552819; cv=none; b=PO2jz0fg15Xcqy1CCqbd8zORWLaFZndQSmzZiAs/WLKY4Tbh31XsvS+Kv26QLh38lOX1N8ZayRMo9mSXzP6SY1FTVyoyEEYIiE/2DMJJi9E9Sp/bq23w6f/8mc0D/0FdS+WPOH5Fk3fY4AT7k4zR3FycJXCF2YA3cnHZM/RpNB4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552819; c=relaxed/simple; bh=5cFsGsqBM38Int8bQPikeWCclmUwKM1nCJsxlKuHw1w=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=RtnMY/kKGfX2GNaO33U+XkV9CFMHpHIosRvjNbrTy7gIEVnIf2Iwta/bR1BMS6WrSKSYXiSNcN29rN5GsvBJCWeSbin08LDF1UmF/jasOZNqurIuZtBN2GyYyq90kI+CmV/JgKEu/GAsVPJK4thSb+a6Z7dUWOlQ1PDYssSq0Aw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=DikZuMY4; arc=none smtp.client-ip=209.85.222.172 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="DikZuMY4" Received: by mail-qk1-f172.google.com with SMTP id af79cd13be357-789db18e24eso439002285a.1 for ; Wed, 27 Mar 2024 08:20:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711552813; x=1712157613; 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=clUZyXE3vVtQZt5f/CiHXTNrFxzqu+qzMUhSAOQCX3Y=; b=DikZuMY43swE5AAUtYZGIRl0n75zG6aGNmIn9/1hdE7nEYXAcKArristUfrW2Z3wsU RVCTMgZrZ74omds0WPUJ80iCNXAaH1qF6fk4bgN/edZ3JCwKLwcuAn5QnCOXW8SgsMVq Zt3ErgxKdyOlDcydBxt4pYc/PbzWOJmnBHo7JIKjFr7HGkYm6Rd6bW8xs/2mXOueBliU l+JqCxnutarnaT39qEHS0XhSq1gelyazG2LStxIG9ZVJJTSHT0TBKSRuWsfgj2TN+Jsl iO/K1cStX9D11KOAVpndESix92F09rzCghsAtoks8wNpEQqKP59W8/tSJ00Henl2tvhv 0K+g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711552813; x=1712157613; 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=clUZyXE3vVtQZt5f/CiHXTNrFxzqu+qzMUhSAOQCX3Y=; b=bqvoiQNkPuuRgSG1XKpQMEOCuP6dI/Or6w8sVi19raztAYcRRhB+rz12UWONQ3E/20 30iQNMKhRkMwIN2gDZu4ubJKzqd4fpYCTio+1kO5S5qRnJqPh3oyDvTloHZpF1oeCUYc kT8kjTz31LD9dECvCfgSZj0XslFhLR5qBhFxRp4H2TJmy9uK5tOuB6XHpD4OivoKVvZu w3IgHZVN3aAToOMMqGWiXesjxyWRxVKPTkCxCLiUZLf/ocZJ6M7ZocS0jbCTAf1UYgnv 12qXHs3n9p/KZ/LapWFzvsUcoWAvRnLrIzgkFC9w5T0+Qec2Boi9+DasTt4tgWIympOQ yqzA== X-Gm-Message-State: AOJu0Yz47yZe10ANVqNuAXOj1/kDne+QInV9XWFaMxjW6f9aX/CTyRRv Q3TiNbBTy98m5m43OcdL/PFVW+CsGVYpnCjkuJxuMzK/Dv63+zxexEcAQ40E X-Google-Smtp-Source: AGHT+IEHXvOyRSPE9YBfTsiem31IX2aELLjF6dz2jbA7IVFiHdjvUzI586+xdng65HRJisLWNRW34w== X-Received: by 2002:a05:6214:2585:b0:696:4207:a6c9 with SMTP id fq5-20020a056214258500b006964207a6c9mr5049664qvb.35.1711552811925; Wed, 27 Mar 2024 08:20:11 -0700 (PDT) Received: from LOCLAP699.rst-02.locus ([208.195.13.130]) by smtp.gmail.com with ESMTPSA id 7-20020a056214202700b006968ce99b7esm3520782qvf.34.2024.03.27.08.20.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Mar 2024 08:20:11 -0700 (PDT) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH 8/9] dpp: remove most crypto/frame processing Date: Wed, 27 Mar 2024 08:19:56 -0700 Message-Id: <20240327151957.1446149-8-prestwoj@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240327151957.1446149-1-prestwoj@gmail.com> References: <20240327151957.1446149-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 To prep for using dpp-common all (or most) of the crypto and frame processing has been removed from dpp.c. This commit only serves as a temporary transition to using dpp-common in order to make the patches more readable. DPP is entirely broken with only this patch. --- src/dpp.c | 3232 ++++------------------------------------------------- 1 file changed, 235 insertions(+), 2997 deletions(-) diff --git a/src/dpp.c b/src/dpp.c index 47ebd495..31d0ef28 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -57,8 +57,6 @@ #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; @@ -96,7 +94,7 @@ struct pkex_agent { uint32_t pending_id; }; -struct dpp_sm { +struct dpp { struct netdev *netdev; char *uri; uint8_t role; @@ -106,18 +104,8 @@ struct dpp_sm { uint64_t wdev_id; - uint8_t *own_asn1; - size_t own_asn1_len; - uint8_t *peer_asn1; - size_t peer_asn1_len; - uint8_t own_boot_hash[32]; - uint8_t peer_boot_hash[32]; - const struct l_ecc_curve *curve; - size_t key_len; - size_t nonce_len; struct l_ecc_scalar *boot_private; struct l_ecc_point *boot_public; - struct l_ecc_point *peer_boot_public; enum dpp_state state; enum dpp_interface interface; @@ -140,22 +128,6 @@ struct dpp_sm { uint32_t offchannel_id; uint8_t peer_addr[6]; - uint8_t r_nonce[32]; - uint8_t i_nonce[32]; - uint8_t e_nonce[32]; - - struct l_ecc_scalar *m; - uint64_t ke[L_ECC_MAX_DIGITS]; - uint64_t k1[L_ECC_MAX_DIGITS]; - uint64_t k2[L_ECC_MAX_DIGITS]; - uint64_t auth_tag[L_ECC_MAX_DIGITS]; - - struct l_ecc_scalar *proto_private; - struct l_ecc_point *own_proto_public; - - struct l_ecc_point *peer_proto_public; - - uint8_t diag_token; /* Timeout of either auth/config protocol */ struct l_timeout *timeout; @@ -175,24 +147,12 @@ struct dpp_sm { /* PKEX-specific values */ 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; - /* 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; + uint32_t pkex_scan_id; 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) @@ -212,7 +172,7 @@ static bool dpp_pkex_get_started(struct l_dbus *dbus, struct l_dbus_message_builder *builder, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; bool started = (dpp->state != DPP_STATE_NOTHING && dpp->interface == DPP_INTERFACE_PKEX); @@ -226,7 +186,7 @@ static bool dpp_pkex_get_role(struct l_dbus *dbus, struct l_dbus_message_builder *builder, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; const char *role; if (dpp->state == DPP_STATE_NOTHING || @@ -246,7 +206,7 @@ static bool dpp_get_started(struct l_dbus *dbus, struct l_dbus_message_builder *builder, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; bool started = (dpp->state != DPP_STATE_NOTHING && dpp->interface == DPP_INTERFACE_DPP); @@ -260,7 +220,7 @@ static bool dpp_get_role(struct l_dbus *dbus, struct l_dbus_message_builder *builder, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; const char *role; if (dpp->state == DPP_STATE_NOTHING || @@ -280,7 +240,7 @@ static bool dpp_get_uri(struct l_dbus *dbus, struct l_dbus_message_builder *builder, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; if (dpp->state == DPP_STATE_NOTHING || dpp->interface != DPP_INTERFACE_DPP) @@ -290,7 +250,7 @@ static bool dpp_get_uri(struct l_dbus *dbus, return true; } -static void dpp_property_changed_notify(struct dpp_sm *dpp) +static void dpp_property_changed_notify(struct dpp *dpp) { const char *path = netdev_get_path(dpp->netdev); @@ -340,26 +300,6 @@ 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)); - - 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; @@ -370,7 +310,7 @@ static void pkex_agent_free(void *data) l_free(agent); } -static void dpp_agent_cancel(struct dpp_sm *dpp) +static void dpp_agent_cancel(struct dpp *dpp) { struct l_dbus_message *msg; @@ -386,7 +326,7 @@ static void dpp_agent_cancel(struct dpp_sm *dpp) l_dbus_send(dbus_get_bus(), msg); } -static void dpp_agent_release(struct dpp_sm *dpp) +static void dpp_agent_release(struct dpp *dpp) { struct l_dbus_message *msg; @@ -400,7 +340,7 @@ static void dpp_agent_release(struct dpp_sm *dpp) l_dbus_send(dbus_get_bus(), msg); } -static void dpp_destroy_agent(struct dpp_sm *dpp) +static void dpp_destroy_agent(struct dpp *dpp) { if (!dpp->agent) return; @@ -418,56 +358,7 @@ static void dpp_destroy_agent(struct dpp_sm *dpp) dpp->agent = NULL; } -static void dpp_free_auth_data(struct dpp_sm *dpp) -{ - if (dpp->own_proto_public) { - l_ecc_point_free(dpp->own_proto_public); - dpp->own_proto_public = NULL; - } - - if (dpp->proto_private) { - l_ecc_scalar_free(dpp->proto_private); - dpp->proto_private = NULL; - } - - if (dpp->peer_proto_public) { - l_ecc_point_free(dpp->peer_proto_public); - dpp->peer_proto_public = NULL; - } - - if (dpp->peer_boot_public) { - l_ecc_point_free(dpp->peer_boot_public); - dpp->peer_boot_public = NULL; - } - - if (dpp->m) { - l_ecc_scalar_free(dpp->m); - dpp->m = NULL; - } - - 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) +static void dpp_reset(struct dpp *dpp) { if (dpp->uri) { l_free(dpp->uri); @@ -499,11 +390,6 @@ static void dpp_reset(struct dpp_sm *dpp) dpp->connect_scan_id = 0; } - if (dpp->peer_asn1) { - l_free(dpp->peer_asn1); - dpp->peer_asn1 = NULL; - } - if (dpp->frame_pending) { l_free(dpp->frame_pending); dpp->frame_pending = NULL; @@ -524,44 +410,23 @@ static void dpp_reset(struct dpp_sm *dpp) dpp->connect_idle = NULL; } - dpp->state = DPP_STATE_NOTHING; 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); - explicit_bzero(dpp->e_nonce, dpp->nonce_len); - explicit_bzero(dpp->ke, dpp->key_len); - explicit_bzero(dpp->k1, dpp->key_len); - explicit_bzero(dpp->k2, dpp->key_len); - explicit_bzero(dpp->auth_tag, dpp->key_len); - 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); - dpp_property_changed_notify(dpp); dpp->interface = DPP_INTERFACE_UNBOUND; } -static void dpp_free(struct dpp_sm *dpp) +static void dpp_free(struct dpp *dpp) { struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); dpp_reset(dpp); - if (dpp->own_asn1) { - l_free(dpp->own_asn1); - dpp->own_asn1 = NULL; - } - if (dpp->boot_public) { l_ecc_point_free(dpp->boot_public); dpp->boot_public = NULL; @@ -586,7 +451,7 @@ static void dpp_free(struct dpp_sm *dpp) static void dpp_send_frame_cb(struct l_genl_msg *msg, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; int err = l_genl_msg_get_error(msg); if (err < 0) { @@ -599,7 +464,7 @@ static void dpp_send_frame_cb(struct l_genl_msg *msg, void *user_data) l_error("Error parsing frame cookie"); } -static void dpp_send_frame(struct dpp_sm *dpp, +static void dpp_send_frame(struct dpp *dpp, struct iovec *iov, size_t iov_len, uint32_t freq) { @@ -632,7 +497,7 @@ static void dpp_send_frame(struct dpp_sm *dpp, } } -static void dpp_frame_retry(struct dpp_sm *dpp) +static void dpp_frame_retry(struct dpp *dpp) { struct iovec iov; @@ -645,183 +510,6 @@ static void dpp_frame_retry(struct dpp_sm *dpp) dpp->frame_pending = NULL; } - -static size_t dpp_build_header(const uint8_t *src, const uint8_t *dest, - enum dpp_frame_type type, - uint8_t *buf) -{ - uint8_t *ptr = buf + 24; - - memset(buf, 0, 32); - - l_put_le16(0x00d0, buf); - memcpy(buf + 4, dest, 6); - memcpy(buf + 10, src, 6); - memcpy(buf + 16, broadcast, 6); - - *ptr++ = 0x04; /* Category: Public */ - *ptr++ = 0x09; /* Action: Vendor specific usage */ - memcpy(ptr, wifi_alliance_oui, 3); - ptr += 3; - *ptr++ = 0x1a; /* WiFi Alliance DPP OI type */ - *ptr++ = 1; /* Cryptosuite */ - *ptr++ = type; - - return ptr - buf; -} - -static size_t dpp_build_config_header(const uint8_t *src, const uint8_t *dest, - uint8_t diag_token, - uint8_t *buf) -{ - uint8_t *ptr = buf + 24; - - memset(buf, 0, 37); - - l_put_le16(0x00d0, buf); - memcpy(buf + 4, dest, 6); - memcpy(buf + 10, src, 6); - memcpy(buf + 16, broadcast, 6); - - *ptr++ = 0x04; /* Public */ - *ptr++ = 0x0a; /* Action */ - *ptr++ = diag_token; - - *ptr++ = IE_TYPE_ADVERTISEMENT_PROTOCOL; - *ptr++ = 8; /* len */ - *ptr++ = 0x00; - *ptr++ = IE_TYPE_VENDOR_SPECIFIC; - *ptr++ = 5; - memcpy(ptr, wifi_alliance_oui, 3); - ptr += 3; - *ptr++ = 0x1a; - *ptr++ = 1; - - return ptr - buf; -} - -static void dpp_protocol_timeout(struct l_timeout *timeout, void *user_data) -{ - struct dpp_sm *dpp = user_data; - - l_debug("DPP timed out"); - - dpp_reset(dpp); -} - -static void dpp_reset_protocol_timer(struct dpp_sm *dpp, uint32_t time) -{ - if (dpp->timeout) - l_timeout_modify(dpp->timeout, time); - else - dpp->timeout = l_timeout_create(time, dpp_protocol_timeout, - dpp, NULL); -} - -/* - * The configuration protocols use of AD components is somewhat confusing - * since the request/response frames are of a different format than the rest. - * In addition there are situations where the components length is zero yet it - * is still passed as such to AES-SIV. - * - * For the configuration request/response frames: - * - * "AAD for use with AES-SIV for protected messages in the DPP Configuration - * protocol shall consist of all octets in the Query Request and Query Response - * fields up to the first octet of the Wrapped Data attribute, which is the last - * attribute in a DPP Configuration frame. When the number of octets of AAD is - * zero, the number of components of AAD passed to AES-SIV is zero." - * - * - For configuration requests the optional query request field is not - * included, therefore no AAD data is passed. (dpp_configuration_start) - * - * - The configuration response does contain a query response field which is - * 5 bytes. (dpp_handle_config_response_frame) - * - * For the configuration result/status, the same rules are used as the - * authentication protocol. This is reiterated in section 6.4.1. - * - * - For the configuration result there is some confusion as to exactly how the - * second AAD component should be passed (since the spec specifically - * mentions using two components). There are no attributes prior to the - * wrapped data component meaning the length would be zero. - * Hostapd/wpa_supplicant pass a zero length AAD component to AES-SIV which - * does effect the resulting encryption/decryption so this is also what IWD - * will do to remain compliant with it. - */ -static void dpp_configuration_start(struct dpp_sm *dpp, const uint8_t *addr) -{ - const char *json = "{\"name\":\"IWD\",\"wi-fi_tech\":\"infra\"," - "\"netRole\":\"sta\"}"; - struct iovec iov; - uint8_t frame[512]; - size_t json_len = strlen(json); - uint8_t *ptr = frame; - uint8_t *lptr; - struct mmpdu_header *hdr = (struct mmpdu_header *)frame; - - memset(frame, 0, sizeof(frame)); - - l_getrandom(&dpp->diag_token, 1); - - ptr += dpp_build_config_header(netdev_get_address(dpp->netdev), - addr, dpp->diag_token, ptr); - - l_getrandom(dpp->e_nonce, dpp->nonce_len); - - /* length */ - lptr = ptr; - ptr += 2; - - /* - * "AAD for use with AES-SIV for protected messages in the DPP - * Configuration protocol shall consist of all octets in the Query - * Request and Query Response fields up to the first octet of the - * Wrapped Data attribute" - * - * In this case there is no query request/response fields, nor any - * attributes besides wrapped data meaning zero AD components. - */ - ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, - sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, - dpp->ke, dpp->key_len, 2, - DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce, - DPP_ATTR_CONFIGURATION_REQUEST, json_len, json); - - l_put_le16(ptr - lptr - 2, lptr); - - iov.iov_base = frame; - iov.iov_len = ptr - frame; - - dpp->state = DPP_STATE_CONFIGURING; - - dpp_send_frame(dpp, &iov, 1, dpp->current_freq); -} - -static void send_config_result(struct dpp_sm *dpp, const uint8_t *to) -{ - struct iovec iov; - uint8_t frame[256]; - uint8_t *ptr = frame; - uint8_t zero = 0; - struct mmpdu_header *hdr = (struct mmpdu_header *)frame; - - memset(frame, 0, sizeof(frame)); - - ptr += dpp_build_header(netdev_get_address(dpp->netdev), to, - DPP_FRAME_CONFIGURATION_RESULT, ptr); - ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, - sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, - dpp->ke, dpp->key_len, 2, - DPP_ATTR_STATUS, (size_t) 1, &zero, - DPP_ATTR_ENROLLEE_NONCE, dpp->nonce_len, dpp->e_nonce); - - iov.iov_base = frame; - iov.iov_len = ptr - frame; - - dpp_send_frame(dpp, &iov, 1, dpp->current_freq); -} - static void dpp_write_config(struct dpp_configuration *config, struct network *network) { @@ -862,2600 +550,250 @@ static void dpp_scan_triggered(int err, void *user_data) static void dpp_start_connect(struct l_idle *idle, void *user_data) { - struct dpp_sm *dpp = user_data; - struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); - struct scan_bss *bss; - struct network *network; - int ret; - - network = station_network_find(station, dpp->config->ssid, - SECURITY_PSK); - - dpp_reset(dpp); - - if (!network) { - l_debug("Network was not found!"); - return; - } - - l_debug("connecting to %s from DPP", network_get_ssid(network)); - - bss = network_bss_select(network, true); - ret = network_autoconnect(network, bss); - if (ret < 0) - l_warn("failed to connect after DPP (%d) %s", ret, - strerror(-ret)); -} - -static bool dpp_scan_results(int err, struct l_queue *bss_list, - const struct scan_freq_set *freqs, - void *userdata) -{ - struct dpp_sm *dpp = userdata; - struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); - - if (err < 0) - goto reset; - - if (!bss_list || l_queue_length(bss_list) == 0) - goto reset; - - /* - * The station watch _should_ detect this and reset, which cancels the - * scan. But just in case... - */ - if (L_WARN_ON(station_get_connected_network(station))) - goto reset; - - station_set_scan_results(station, bss_list, freqs, false); - - dpp_start_connect(NULL, dpp); - - return true; - -reset: - return false; -} - -static void dpp_scan_destroy(void *userdata) -{ - struct dpp_sm *dpp = userdata; - - dpp->connect_scan_id = 0; - dpp_reset(dpp); -} - -static void dpp_known_network_watch(enum known_networks_event event, - const struct network_info *info, - void *user_data) -{ - struct dpp_sm *dpp = user_data; - - /* - * Check the following - * - DPP is enrolling - * - DPP finished (dpp->config is set) - * - This is for the network DPP just configured - * - DPP isn't already trying to connect (e.g. if the profile was - * immediately modified after DPP synced it). - * - DPP didn't start a scan for the network. - */ - if (dpp->role != DPP_CAPABILITY_ENROLLEE) - return; - if (!dpp->config) - return; - if (strcmp(info->ssid, dpp->config->ssid)) - return; - if (dpp->connect_idle) - return; - if (dpp->connect_scan_id) - return; - - switch (event) { - case KNOWN_NETWORKS_EVENT_ADDED: - case KNOWN_NETWORKS_EVENT_UPDATED: - /* - * network.c takes care of updating the settings for the - * network. This callback just tells us to begin the connection. - * We do have use an idle here because there is no strict - * guarantee of ordering between known network events, e.g. DPP - * could have been called into prior to network and the network - * object isn't updated yet. - */ - dpp->connect_idle = l_idle_create(dpp_start_connect, dpp, NULL); - break; - case KNOWN_NETWORKS_EVENT_REMOVED: - l_warn("profile was removed before DPP could connect"); - break; - } -} - -static void dpp_handle_config_response_frame(const struct mmpdu_header *frame, - const void *body, size_t body_len, - int rssi, void *user_data) -{ - struct dpp_sm *dpp = user_data; - const uint8_t *ptr = body; - uint16_t status; - uint16_t fragmented; /* Fragmented/Comeback delay field */ - uint8_t adv_protocol_element[] = { 0x6C, 0x08, 0x7F }; - uint8_t adv_protocol_id[] = { 0xDD, 0x05, 0x50, 0x6F, - 0x9A, 0x1A, 0x01 }; - uint16_t query_len; - struct dpp_attr_iter iter; - enum dpp_attribute_type type; - size_t len; - const uint8_t *data; - const char *json = NULL; - size_t json_len = 0; - int dstatus = -1; - const uint8_t *wrapped = NULL; - const uint8_t *e_nonce = NULL; - size_t wrapped_len = 0; - _auto_(l_free) uint8_t *unwrapped = NULL; - struct dpp_configuration *config; + struct dpp *dpp = user_data; struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); - struct network *network = NULL; - struct scan_bss *bss = NULL; - - if (dpp->state != DPP_STATE_CONFIGURING) - return; - - /* - * Can a configuration request come from someone other than who you - * authenticated to? - */ - if (memcmp(dpp->peer_addr, frame->address_2, 6)) - return; - - if (body_len < 19) - return; - - ptr += 2; - - if (*ptr++ != dpp->diag_token) - return; - - status = l_get_le16(ptr); - ptr += 2; - - if (status != 0) { - l_debug("Bad configuration status %u", status); - return; - } - - fragmented = l_get_le16(ptr); - ptr += 2; - - /* - * TODO: handle 0x0001 (fragmented), as well as comeback delay. - */ - if (fragmented != 0) { - l_debug("Fragmented messages not currently supported"); - return; - } - - if (memcmp(ptr, adv_protocol_element, sizeof(adv_protocol_element))) { - l_debug("Invalid Advertisement protocol element"); - return; - } - - ptr += sizeof(adv_protocol_element); - - if (memcmp(ptr, adv_protocol_id, sizeof(adv_protocol_id))) { - l_debug("Invalid Advertisement protocol ID"); - return; - } - - ptr += sizeof(adv_protocol_id); - - query_len = l_get_le16(ptr); - ptr += 2; - - if (query_len > body_len - 19) - return; - - dpp_attr_iter_init(&iter, ptr, query_len); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_STATUS: - dstatus = l_get_u8(data); - break; - case DPP_ATTR_WRAPPED_DATA: - wrapped = data; - wrapped_len = len; - break; - default: - /* - * TODO: CSR Attribute - */ - break; - } - } - - if (dstatus != DPP_STATUS_OK || !wrapped) { - l_debug("Bad status or missing attributes"); - return; - } - - unwrapped = dpp_unwrap_attr(ptr, wrapped - ptr - 4, NULL, 0, dpp->ke, - dpp->key_len, wrapped, wrapped_len, - &wrapped_len); - if (!unwrapped) { - l_debug("Failed to unwrap"); - return; - } - - dpp_attr_iter_init(&iter, unwrapped, wrapped_len); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_ENROLLEE_NONCE: - if (len != dpp->nonce_len) - break; - - if (memcmp(data, dpp->e_nonce, dpp->nonce_len)) - break; - - e_nonce = data; - break; - case DPP_ATTR_CONFIGURATION_OBJECT: - json = (const char *)data; - json_len = len; - break; - default: - break; - } - } - - if (!json || !e_nonce) { - l_debug("No configuration object in response"); - return; - } - - config = dpp_parse_configuration_object(json, json_len); - if (!config) { - l_error("Configuration object did not parse"); - return; - } - - /* - * We should have a station device, but if not DPP can write the - * credentials out and be done - */ - if (station) { - network = station_network_find(station, config->ssid, - SECURITY_PSK); - if (network) - bss = network_bss_select(network, true); - } - - dpp_write_config(config, network); - - send_config_result(dpp, dpp->peer_addr); - - offchannel_cancel(dpp->wdev_id, dpp->offchannel_id); - - if (network && bss) { - l_debug("delaying connect until settings are synced"); - dpp->config = config; - return; - } else if (station) { - struct scan_parameters params = {0}; - - params.ssid = (void *) config->ssid; - params.ssid_len = config->ssid_len; - - l_debug("Scanning for %s", config->ssid); - - dpp->connect_scan_id = scan_active_full(dpp->wdev_id, ¶ms, - dpp_scan_triggered, - dpp_scan_results, dpp, - dpp_scan_destroy); - if (dpp->connect_scan_id) { - dpp->config = config; - return; - } - } - - dpp_configuration_free(config); - dpp_reset(dpp); -} - -static void dpp_send_config_response(struct dpp_sm *dpp, uint8_t status) -{ - _auto_(l_free) char *json = NULL; - struct iovec iov; - uint8_t frame[512]; - size_t json_len; - uint8_t *ptr = frame; - uint8_t *lptr; - struct mmpdu_header *hdr = (struct mmpdu_header *)frame; - - memset(frame, 0, sizeof(frame)); - - l_put_le16(0x00d0, ptr); - memcpy(ptr + 4, dpp->peer_addr, 6); - memcpy(ptr + 10, netdev_get_address(dpp->netdev), 6); - memcpy(ptr + 16, broadcast, 6); - - ptr += 24; - - *ptr++ = 0x04; - *ptr++ = 0x0b; - *ptr++ = dpp->diag_token; - l_put_le16(0, ptr); /* status */ - ptr += 2; - l_put_le16(0, ptr); /* fragmented (no) */ - ptr += 2; - *ptr++ = IE_TYPE_ADVERTISEMENT_PROTOCOL; - *ptr++ = 0x08; - *ptr++ = 0x7f; - *ptr++ = IE_TYPE_VENDOR_SPECIFIC; - *ptr++ = 5; - memcpy(ptr, wifi_alliance_oui, sizeof(wifi_alliance_oui)); - ptr += sizeof(wifi_alliance_oui); - *ptr++ = 0x1a; - *ptr++ = 1; - - lptr = ptr; - ptr += 2; /* length */ - - ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1); - - /* - * There are several failure status codes that can be used (defined in - * 6.4.3.1), each with their own set of attributes that should be - * included. For now IWD's basic DPP implementation will assume - * STATUS_CONFIGURE_FAILURE which only includes the E-Nonce. - */ - if (status == DPP_STATUS_OK) { - json = dpp_configuration_to_json(dpp->config); - json_len = strlen(json); - - ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, - sizeof(frame) - mmpdu_header_len(hdr) - 1, - ptr, dpp->ke, dpp->key_len, 2, - DPP_ATTR_ENROLLEE_NONCE, - dpp->nonce_len, dpp->e_nonce, - DPP_ATTR_CONFIGURATION_OBJECT, - json_len, json); - } else - ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, - sizeof(frame) - mmpdu_header_len(hdr) - 1, - ptr, dpp->ke, dpp->key_len, 2, - DPP_ATTR_ENROLLEE_NONCE, - dpp->nonce_len, dpp->e_nonce); - - l_put_le16(ptr - lptr - 2, lptr); - - iov.iov_base = frame; - iov.iov_len = ptr - frame; - - dpp_send_frame(dpp, &iov, 1, dpp->current_freq); -} - -static bool dpp_check_config_header(const uint8_t *ptr) -{ - /* - * Table 58. General Format of DPP Configuration Request frame - * - * Unfortunately wpa_supplicant hard codes 0x7f as the Query Response - * Info so we need to handle both cases. - */ - return ptr[0] == IE_TYPE_ADVERTISEMENT_PROTOCOL && - ptr[1] == 0x08 && - (ptr[2] == 0x7f || ptr[2] == 0x00) && - ptr[3] == IE_TYPE_VENDOR_SPECIFIC && - ptr[4] == 5; -} - -static void dpp_handle_config_request_frame(const struct mmpdu_header *frame, - const void *body, size_t body_len, - int rssi, void *user_data) -{ - struct dpp_sm *dpp = user_data; - const uint8_t *ptr = body; - struct dpp_attr_iter iter; - enum dpp_attribute_type type; - size_t len; - const uint8_t *data; - const char *json = NULL; - size_t json_len = 0; - struct json_contents *c; - const uint8_t *wrapped = NULL; - const uint8_t *e_nonce = NULL; - size_t wrapped_len = 0; - _auto_(l_free) uint8_t *unwrapped = NULL; - struct json_iter jsiter; - _auto_(l_free) char *tech = NULL; - _auto_(l_free) char *role = NULL; - - if (dpp->state != DPP_STATE_AUTHENTICATING) { - l_debug("Configuration request in wrong state"); - return; - } - - if (dpp->role != DPP_CAPABILITY_CONFIGURATOR) - return; - - if (memcmp(dpp->peer_addr, frame->address_2, 6)) { - l_debug("Configuration request not from authenticated peer"); - return; - } - - if (body_len < 15) { - l_debug("Configuration request data not long enough"); - return; - } - - ptr += 2; - - dpp->diag_token = *ptr++; - - if (!dpp_check_config_header(ptr)) - return; - - ptr += 5; - - if (memcmp(ptr, wifi_alliance_oui, sizeof(wifi_alliance_oui))) - return; - - ptr += sizeof(wifi_alliance_oui); - - if (*ptr != 0x1a && *(ptr + 1) != 1) - return; - - ptr += 2; - - len = l_get_le16(ptr); - ptr += 2; - - if (len > body_len - 15) - return; - - dpp_attr_iter_init(&iter, ptr, len); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_WRAPPED_DATA: - wrapped = data; - wrapped_len = len; - break; - default: - /* Wrapped data should be only attribute */ - return; - } - } - - if (!wrapped) { - l_debug("Wrapped data missing"); - return; - } - - unwrapped = dpp_unwrap_attr(NULL, 0, NULL, 0, dpp->ke, - dpp->key_len, wrapped, wrapped_len, - &wrapped_len); - if (!unwrapped) { - l_debug("Failed to unwrap"); - return; - } - - dpp_attr_iter_init(&iter, unwrapped, wrapped_len); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_ENROLLEE_NONCE: - if (len != dpp->nonce_len) - break; - - e_nonce = data; - break; - case DPP_ATTR_CONFIGURATION_REQUEST: - json = (const char *)data; - json_len = len; - break; - default: - break; - } - } - - if (!json || !e_nonce) { - l_debug("No configuration object in response"); - return; - } - - c = json_contents_new(json, json_len); - if (!c) { - json_contents_free(c); - return; - } - - json_iter_init(&jsiter, c); - - /* - * Check mandatory values (Table 7). There isn't much that can be done - * with these, but the spec requires they be included. - */ - if (!json_iter_parse(&jsiter, - JSON_MANDATORY("name", JSON_STRING, NULL), - JSON_MANDATORY("wi-fi_tech", JSON_STRING, &tech), - JSON_MANDATORY("netRole", JSON_STRING, &role), - JSON_UNDEFINED)) - goto configure_failure; - - if (strcmp(tech, "infra")) - goto configure_failure; - - if (strcmp(role, "sta")) - goto configure_failure; - - json_contents_free(c); - - memcpy(dpp->e_nonce, e_nonce, dpp->nonce_len); - - dpp->state = DPP_STATE_CONFIGURING; - - dpp_send_config_response(dpp, DPP_STATUS_OK); - - return; - -configure_failure: - dpp_send_config_response(dpp, DPP_STATUS_CONFIGURE_FAILURE); - /* - * The other peer is still authenticated, and can potentially send - * additional requests so keep this session alive. - */ -} - -static void dpp_handle_config_result_frame(struct dpp_sm *dpp, - const uint8_t *from, const void *body, - size_t body_len) -{ - struct dpp_attr_iter iter; - enum dpp_attribute_type type; - size_t len; - const uint8_t *data; - int status = -1; - const void *e_nonce = NULL; - const void *wrapped = NULL; - size_t wrapped_len; - _auto_(l_free) void *unwrapped = NULL; - - if (dpp->state != DPP_STATE_CONFIGURING) - 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: - /* Wrapped data should be only attribute */ - return; - } - } - - if (!wrapped) - return; - - unwrapped = dpp_unwrap_attr(body + 2, wrapped - body - 6, wrapped, 0, - dpp->ke, dpp->key_len, wrapped, - wrapped_len, &wrapped_len); - if (!unwrapped) { - l_debug("Failed to unwrap DPP configuration result"); - return; - } - - dpp_attr_iter_init(&iter, unwrapped, wrapped_len); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_STATUS: - status = l_get_u8(data); - break; - case DPP_ATTR_ENROLLEE_NONCE: - e_nonce = data; - break; - default: - break; - } - } - - if (status != DPP_STATUS_OK || !e_nonce) - l_debug("Enrollee signaled a failed configuration"); - else - l_debug("Configuration success"); - - dpp_reset(dpp); -} - -/* - * The Authentication protocol has a consistent use of AD components, and this - * use is defined in 6.3.1.4: - * - * "Invocations of AES-SIV in the DPP Authentication protocol that produce - * ciphertext that is part of an additional AES-SIV invocation do not use AAD; - * in other words, the number of AAD components is set to zero. All other - * invocations of AES-SIV in the DPP Authentication protocol shall pass a vector - * of AAD having two components of AAD in the following order: (1) the DPP - * header, as defined in Table 30, from the OUI field (inclusive) to the DPP - * Frame Type field (inclusive); and (2) all octets in a DPP Public Action frame - * after the DPP Frame Type field up to and including the last octet of the last - * attribute before the Wrapped Data attribute" - * - * In practice you see this as AD0 being some offset in the frame (offset to the - * OUI). For outgoing packets this is 26 bytes offset since the header is built - * manually. For incoming packets the offset is 2 bytes. The length is always - * 6 bytes for AD0. - * - * The AD1 data is always the start of the attributes, and length is the number - * of bytes from these attributes to wrapped data. e.g. - * - * ad1 = attrs - * ad1_len = ptr - attrs - */ -static void send_authenticate_response(struct dpp_sm *dpp) -{ - uint8_t frame[512]; - uint8_t *ptr = frame; - uint8_t status = DPP_STATUS_OK; - uint8_t version = 2; - struct iovec iov; - uint8_t wrapped2_plaintext[dpp->key_len + 4]; - uint8_t wrapped2[dpp->key_len + 16 + 8]; - size_t wrapped2_len; - struct mmpdu_header *hdr = (struct mmpdu_header *)frame; - - memset(frame, 0, sizeof(frame)); - - ptr += dpp_build_header(netdev_get_address(dpp->netdev), - dpp->peer_addr, - DPP_FRAME_AUTHENTICATION_RESPONSE, ptr); - ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &status, 1); - ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, - dpp->own_boot_hash, 32); - if (dpp->mutual_auth) - ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, - dpp->peer_boot_hash, 32); - ptr += dpp_append_point(ptr, DPP_ATTR_RESPONDER_PROTOCOL_KEY, - dpp->own_proto_public); - ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1); - - /* Wrap up secondary data (R-Auth) */ - wrapped2_len = dpp_append_attr(wrapped2_plaintext, - DPP_ATTR_RESPONDER_AUTH_TAG, - dpp->auth_tag, dpp->key_len); - /* - * "Invocations of AES-SIV in the DPP Authentication protocol that - * produce ciphertext that is part of an additional AES-SIV invocation - * do not use AAD; in other words, the number of AAD components is set - * to zero."" - */ - if (!aes_siv_encrypt(dpp->ke, dpp->key_len, wrapped2_plaintext, - dpp->key_len + 4, NULL, 0, wrapped2)) { - l_error("Failed to encrypt wrapped data"); - return; - } - - wrapped2_len += 16; - - ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, - sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, - dpp->k2, dpp->key_len, 4, - DPP_ATTR_RESPONDER_NONCE, dpp->nonce_len, dpp->r_nonce, - DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, - DPP_ATTR_RESPONDER_CAPABILITIES, (size_t) 1, &dpp->role, - DPP_ATTR_WRAPPED_DATA, wrapped2_len, wrapped2); - - iov.iov_base = frame; - iov.iov_len = ptr - frame; - - dpp_send_frame(dpp, &iov, 1, dpp->current_freq); -} - -static void authenticate_confirm(struct dpp_sm *dpp, const uint8_t *from, - const uint8_t *body, size_t body_len) -{ - struct dpp_attr_iter iter; - enum dpp_attribute_type type; - size_t len; - const uint8_t *data; - int status = -1; - const uint8_t *r_boot_hash = NULL; - const void *wrapped = NULL; - const uint8_t *i_auth = NULL; - size_t i_auth_len; - _auto_(l_free) uint8_t *unwrapped = NULL; - size_t wrapped_len = 0; - uint64_t i_auth_check[L_ECC_MAX_DIGITS]; - const void *unwrap_key; - const void *ad0 = body + 2; - const void *ad1 = body + 8; - struct l_ecc_point *bi = NULL; - - if (dpp->state != DPP_STATE_AUTHENTICATING) - return; - - if (memcmp(from, dpp->peer_addr, 6)) - return; - - l_debug("authenticate confirm"); - - 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: - status = l_get_u8(data); - break; - case DPP_ATTR_RESPONDER_BOOT_KEY_HASH: - r_boot_hash = data; - /* - * Spec requires this, but does not mention if anything - * is to be done with it. - */ - break; - case DPP_ATTR_INITIATOR_BOOT_KEY_HASH: - /* No mutual authentication */ - break; - case DPP_ATTR_WRAPPED_DATA: - wrapped = data; - wrapped_len = len; - break; - default: - break; - } - } - - if (!r_boot_hash || !wrapped) { - l_debug("Attributes missing from authenticate confirm"); - return; - } - - /* - * "The Responder obtains the DPP Authentication Confirm frame and - * checks the value of the DPP Status field. If the value of the DPP - * Status field is STATUS_NOT_COMPATIBLE or STATUS_AUTH_FAILURE, the - * Responder unwraps the wrapped data portion of the frame using k2" - */ - if (status == DPP_STATUS_OK) - unwrap_key = dpp->ke; - else if (status == DPP_STATUS_NOT_COMPATIBLE || - status == DPP_STATUS_AUTH_FAILURE) - unwrap_key = dpp->k2; - else - goto auth_confirm_failed; - - unwrapped = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1, - unwrap_key, dpp->key_len, wrapped, wrapped_len, - &wrapped_len); - if (!unwrapped) - goto auth_confirm_failed; - - if (status != DPP_STATUS_OK) { - /* - * "If unwrapping is successful, the Responder should generate - * an alert indicating the reason for the protocol failure." - */ - l_debug("Authentication failed due to status %s", - status == DPP_STATUS_NOT_COMPATIBLE ? - "NOT_COMPATIBLE" : "AUTH_FAILURE"); - goto auth_confirm_failed; - } - - dpp_attr_iter_init(&iter, unwrapped, wrapped_len); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_INITIATOR_AUTH_TAG: - i_auth = data; - i_auth_len = len; - break; - case DPP_ATTR_RESPONDER_NONCE: - /* Only if error */ - break; - default: - break; - } - } - - if (!i_auth || i_auth_len != dpp->key_len) { - l_debug("I-Auth missing from wrapped data"); - goto auth_confirm_failed; - } - - if (dpp->mutual_auth) - bi = dpp->peer_boot_public; - - dpp_derive_i_auth(dpp->r_nonce, dpp->i_nonce, dpp->nonce_len, - dpp->own_proto_public, dpp->peer_proto_public, - dpp->boot_public, bi, i_auth_check); - - if (memcmp(i_auth, i_auth_check, i_auth_len)) { - l_error("I-Auth did not verify"); - goto auth_confirm_failed; - } - - l_debug("Authentication successful"); - - dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT); - - if (dpp->role == DPP_CAPABILITY_ENROLLEE) - dpp_configuration_start(dpp, from); - - return; - -auth_confirm_failed: - dpp->state = DPP_STATE_PRESENCE; - dpp_free_auth_data(dpp); -} - -static void dpp_auth_request_failed(struct dpp_sm *dpp, - enum dpp_status status, - void *k1) -{ - uint8_t frame[128]; - uint8_t *ptr = frame; - uint8_t version = 2; - uint8_t s = status; - struct iovec iov; - struct mmpdu_header *hdr = (struct mmpdu_header *)frame; - - memset(frame, 0, sizeof(frame)); - - ptr += dpp_build_header(netdev_get_address(dpp->netdev), - dpp->peer_addr, - DPP_FRAME_AUTHENTICATION_RESPONSE, ptr); - ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &s, 1); - ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, - dpp->own_boot_hash, 32); - - ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1); - - ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, - sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, - k1, dpp->key_len, 2, - DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, - DPP_ATTR_RESPONDER_CAPABILITIES, - (size_t) 1, &dpp->role); - - iov.iov_base = frame; - iov.iov_len = ptr - frame; - - dpp_send_frame(dpp, &iov, 1, dpp->current_freq); -} - -static bool dpp_check_roles(struct dpp_sm *dpp, uint8_t peer_capa) -{ - if (dpp->role == DPP_CAPABILITY_ENROLLEE && - !(peer_capa & DPP_CAPABILITY_CONFIGURATOR)) - return false; - else if (dpp->role == DPP_CAPABILITY_CONFIGURATOR && - !(peer_capa & DPP_CAPABILITY_ENROLLEE)) - return false; - - return true; -} - -static void dpp_presence_announce(struct dpp_sm *dpp) -{ - struct netdev *netdev = dpp->netdev; - uint8_t hdr[32]; - uint8_t attrs[32 + 4]; - uint8_t hash[32]; - uint8_t *ptr = attrs; - const uint8_t *addr = netdev_get_address(netdev); - struct iovec iov[2]; - - iov[0].iov_len = dpp_build_header(addr, broadcast, - DPP_FRAME_PRESENCE_ANNOUNCEMENT, hdr); - iov[0].iov_base = hdr; - - dpp_hash(L_CHECKSUM_SHA256, hash, 2, "chirp", strlen("chirp"), - dpp->own_asn1, dpp->own_asn1_len); - - ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, hash, 32); - - iov[1].iov_base = attrs; - iov[1].iov_len = ptr - attrs; - - l_debug("Sending presence announcement on frequency %u and waiting %u", - dpp->current_freq, dpp->dwell); - - dpp_send_frame(dpp, iov, 2, dpp->current_freq); -} - -static bool dpp_send_authenticate_request(struct dpp_sm *dpp) -{ - uint8_t frame[256]; - uint8_t *ptr = frame; - uint8_t version = 2; - struct iovec iov; - struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); - struct scan_bss *bss = station_get_connected_bss(station); - struct mmpdu_header *hdr = (struct mmpdu_header *)frame; - - memset(frame, 0, sizeof(frame)); - - /* Got disconnected by the time the peer was discovered */ - if (dpp->role == DPP_CAPABILITY_CONFIGURATOR && !bss) { - dpp_reset(dpp); - return false; - } - - ptr += dpp_build_header(netdev_get_address(dpp->netdev), - dpp->peer_addr, - DPP_FRAME_AUTHENTICATION_REQUEST, ptr); - ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, - dpp->peer_boot_hash, 32); - ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, - dpp->own_boot_hash, 32); - ptr += dpp_append_point(ptr, DPP_ATTR_INITIATOR_PROTOCOL_KEY, - dpp->own_proto_public); - ptr += dpp_append_attr(ptr, DPP_ATTR_PROTOCOL_VERSION, &version, 1); - - if (dpp->role == DPP_CAPABILITY_CONFIGURATOR && - dpp->current_freq != bss->frequency) { - uint8_t pair[2] = { 81, - band_freq_to_channel(bss->frequency, NULL) }; - - ptr += dpp_append_attr(ptr, DPP_ATTR_CHANNEL, pair, 2); - } - - ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, - sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, - dpp->k1, dpp->key_len, 2, - DPP_ATTR_INITIATOR_NONCE, dpp->nonce_len, dpp->i_nonce, - DPP_ATTR_INITIATOR_CAPABILITIES, - (size_t) 1, &dpp->role); - - iov.iov_base = frame; - iov.iov_len = ptr - frame; - - dpp_send_frame(dpp, &iov, 1, dpp->current_freq); - - 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; - 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)); - - ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, dpp->pkex_m); - - 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; - uint8_t frame[512]; - uint8_t *ptr = frame; - uint8_t a_pub[L_ECC_POINT_MAX_BYTES]; - ssize_t a_len; - struct mmpdu_header *hdr = (struct mmpdu_header *)frame; - - memset(frame, 0, sizeof(frame)); - - a_len = l_ecc_point_get_data(dpp->boot_public, a_pub, sizeof(a_pub)); - - ptr += dpp_build_header(netdev_get_address(dpp->netdev), - dpp->peer_addr, - DPP_FRAME_PKEX_COMMIT_REVEAL_REQUEST, - ptr); - ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, - sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, - 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.iov_base = frame; - iov.iov_len = ptr - frame; - - dpp_send_frame(dpp, &iov, 1, dpp->current_freq); -} - -static void dpp_roc_started(void *user_data) -{ - struct dpp_sm *dpp = user_data; - - /* - * - If a configurator, nothing to do but wait for a request - * (unless multicast frame registration is unsupported in which case - * send an authenticate request now) - * - If in the presence state continue sending announcements. - * - If authenticating, and this is a result of a channel switch send - * the authenticate response now. - */ - - dpp->roc_started = true; - - /* - * The retry timer indicates a frame was not acked in which case we - * should not change any state or send any frames until that expires. - */ - if (dpp->retry_timeout) - return; - - if (dpp->frame_pending) { - dpp_frame_retry(dpp); - return; - } - - switch (dpp->state) { - case DPP_STATE_PRESENCE: - if (dpp->role == DPP_CAPABILITY_CONFIGURATOR) - return; - - if (dpp->pending) { - struct l_dbus_message *reply = - l_dbus_message_new_method_return(dpp->pending); - - l_dbus_message_set_arguments(reply, "s", dpp->uri); - - dbus_pending_reply(&dpp->pending, reply); - } - - dpp_presence_announce(dpp); - break; - case DPP_STATE_AUTHENTICATING: - /* - * No multicast frame registration support, jump right into - * sending auth frames. This diverges from the 2.0 spec, but in - * reality the the main path nearly all drivers will hit. - */ - if (dpp->role == DPP_CAPABILITY_CONFIGURATOR) { - if (dpp->mcast_support) - return; - - dpp_send_authenticate_request(dpp); - return; - } - - if (dpp->new_freq) { - dpp->current_freq = dpp->new_freq; - dpp->new_freq = 0; - 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; - } -} - -static void dpp_start_offchannel(struct dpp_sm *dpp, uint32_t freq); - -static void dpp_offchannel_timeout(int error, void *user_data) -{ - struct dpp_sm *dpp = user_data; - - dpp->offchannel_id = 0; - dpp->roc_started = false; - - /* - * If cancelled this is likely due to netdev going down or from Stop(). - * Otherwise there was some other problem which is probably not - * recoverable. - */ - if (error == -ECANCELED) - return; - else if (error == -EIO) - goto next_roc; - else if (error < 0) - goto protocol_failed; - - 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: - /* Protocol already terminated */ - return; - case DPP_STATE_AUTHENTICATING: - case DPP_STATE_CONFIGURING: - case DPP_STATE_PKEX_COMMIT_REVEAL: - goto next_roc; - } - - dpp->freqs_idx++; - - if (dpp->freqs_idx >= dpp->freqs_len) { - l_debug("Max retries offchannel"); - dpp->freqs_idx = 0; - } - - dpp->current_freq = dpp->freqs[dpp->freqs_idx]; - - l_debug("Offchannel timeout, moving to next frequency %u, duration %u", - dpp->current_freq, dpp->dwell); - -next_roc: - dpp_start_offchannel(dpp, dpp->current_freq); - - return; - -protocol_failed: - dpp_reset(dpp); -} - -static void dpp_start_offchannel(struct dpp_sm *dpp, uint32_t freq) -{ - /* - * This needs to be handled carefully for a few reasons: - * - * First, the next offchannel operation needs to be started prior to - * canceling an existing one. This is so the offchannel work can - * continue uninterrupted without any other work items starting in - * between canceling and starting the next (e.g. if a scan request is - * sitting in the queue). - * - * Second, dpp_offchannel_timeout resets dpp->offchannel_id to zero - * which is why the new ID is saved and only set to dpp->offchannel_id - * once the previous offchannel work is cancelled (i.e. destroy() has - * been called). - */ - uint32_t id = offchannel_start(netdev_get_wdev_id(dpp->netdev), - WIPHY_WORK_PRIORITY_OFFCHANNEL, - freq, dpp->dwell, dpp_roc_started, - dpp, dpp_offchannel_timeout); - - if (dpp->offchannel_id) - offchannel_cancel(dpp->wdev_id, dpp->offchannel_id); - - dpp->offchannel_id = id; -} - -static void authenticate_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 uint8_t *r_boot = NULL; - const uint8_t *i_boot = NULL; - const uint8_t *i_proto = NULL; - const void *wrapped = NULL; - const uint8_t *i_nonce = NULL; - uint8_t i_capa = 0; - size_t r_boot_len = 0, i_proto_len = 0, wrapped_len = 0; - size_t i_nonce_len = 0; - _auto_(l_free) uint8_t *unwrapped = NULL; - _auto_(l_ecc_scalar_free) struct l_ecc_scalar *m = NULL; - _auto_(l_ecc_scalar_free) struct l_ecc_scalar *n = NULL; - _auto_(l_ecc_point_free) struct l_ecc_point *l = NULL; - struct l_ecc_point *bi = NULL; - uint64_t k1[L_ECC_MAX_DIGITS]; - const void *ad0 = body + 2; - const void *ad1 = body + 8; - uint32_t freq; - - if (util_is_broadcast_address(from)) - return; - - if (dpp->state != DPP_STATE_PRESENCE && - dpp->state != DPP_STATE_AUTHENTICATING) - return; - - l_debug("authenticate request"); - - dpp_attr_iter_init(&iter, body + 8, body_len - 8); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_INITIATOR_BOOT_KEY_HASH: - i_boot = data; - /* - * This attribute is required by the spec, but only - * used for mutual authentication. - */ - break; - case DPP_ATTR_RESPONDER_BOOT_KEY_HASH: - r_boot = data; - r_boot_len = len; - break; - case DPP_ATTR_INITIATOR_PROTOCOL_KEY: - i_proto = data; - i_proto_len = len; - break; - case DPP_ATTR_WRAPPED_DATA: - /* I-Nonce/I-Capabilities part of wrapped data */ - wrapped = data; - wrapped_len = len; - break; - - /* Optional attributes */ - case DPP_ATTR_PROTOCOL_VERSION: - if (l_get_u8(data) != 2) { - l_debug("Protocol version did not match"); - return; - } - - break; - - case DPP_ATTR_CHANNEL: - if (len != 2) - return; - - freq = oci_to_frequency(l_get_u8(data), - l_get_u8(data + 1)); - - if (freq == dpp->current_freq) - break; - - /* - * Configurators are already connected to a network, so - * to preserve wireless performance the enrollee will - * be required to be on this channel, not a channel it - * requests. - */ - if (dpp->role == DPP_CAPABILITY_CONFIGURATOR) - return; - - /* - * Otherwise, as an enrollee, we can jump to whatever - * channel the configurator requests - */ - dpp->new_freq = freq; - - l_debug("Configurator requested a new frequency %u", - dpp->new_freq); - - dpp_start_offchannel(dpp, dpp->new_freq); - - break; - default: - break; - } - } - - if (!r_boot || !i_boot || !i_proto || !wrapped) - goto auth_request_failed; - - if (r_boot_len != 32 || memcmp(dpp->own_boot_hash, - r_boot, r_boot_len)) { - l_debug("Responder boot key hash failed to verify"); - goto auth_request_failed; - } - - dpp->peer_proto_public = l_ecc_point_from_data(dpp->curve, - L_ECC_POINT_TYPE_FULL, - i_proto, i_proto_len); - if (!dpp->peer_proto_public) { - l_debug("Initiators protocol key invalid"); - goto auth_request_failed; - } - - m = dpp_derive_k1(dpp->peer_proto_public, dpp->boot_private, k1); - if (!m) - goto auth_request_failed; - - unwrapped = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1, - k1, dpp->key_len, wrapped, wrapped_len, &wrapped_len); - if (!unwrapped) - goto auth_request_failed; - - dpp_attr_iter_init(&iter, unwrapped, wrapped_len); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_INITIATOR_NONCE: - i_nonce = data; - i_nonce_len = len; - break; - case DPP_ATTR_INITIATOR_CAPABILITIES: - /* - * "If the Responder is not capable of supporting the - * role indicated by the Initiator, it shall respond - * with a DPP Authentication Response frame indicating - * failure by adding the DPP Status field set to - * STATUS_NOT_COMPATIBLE" - */ - i_capa = l_get_u8(data); - - if (!dpp_check_roles(dpp, i_capa)) { - l_debug("Peer does not support required role"); - dpp_auth_request_failed(dpp, - DPP_STATUS_NOT_COMPATIBLE, k1); - goto auth_request_failed; - } - - break; - default: - break; - } - } - - if (i_nonce_len != dpp->nonce_len) { - l_debug("I-Nonce has unexpected length %zu", i_nonce_len); - goto auth_request_failed; - } - - memcpy(dpp->i_nonce, i_nonce, i_nonce_len); - - if (dpp->mutual_auth) { - l = dpp_derive_lr(dpp->boot_private, dpp->proto_private, - dpp->peer_boot_public); - bi = dpp->peer_boot_public; - } - - /* Derive keys k2, ke, and R-Auth for authentication response */ - - n = dpp_derive_k2(dpp->peer_proto_public, dpp->proto_private, dpp->k2); - if (!n) - goto auth_request_failed; - - l_getrandom(dpp->r_nonce, dpp->nonce_len); - - if (!dpp_derive_ke(dpp->i_nonce, dpp->r_nonce, m, n, l, dpp->ke)) - goto auth_request_failed; - - if (!dpp_derive_r_auth(dpp->i_nonce, dpp->r_nonce, dpp->nonce_len, - dpp->peer_proto_public, dpp->own_proto_public, - bi, dpp->boot_public, dpp->auth_tag)) - goto auth_request_failed; - - memcpy(dpp->peer_addr, from, 6); - - dpp->state = DPP_STATE_AUTHENTICATING; - dpp_reset_protocol_timer(dpp, DPP_AUTH_PROTO_TIMEOUT); - - /* Don't send if the frequency is changing */ - if (!dpp->new_freq) - send_authenticate_response(dpp); - - return; - -auth_request_failed: - dpp->state = DPP_STATE_PRESENCE; - dpp_free_auth_data(dpp); -} - -static void dpp_send_authenticate_confirm(struct dpp_sm *dpp) -{ - struct iovec iov; - uint8_t frame[256]; - uint8_t *ptr = frame; - uint8_t zero = 0; - struct mmpdu_header *hdr = (struct mmpdu_header *)frame; - - memset(frame, 0, sizeof(frame)); - - ptr += dpp_build_header(netdev_get_address(dpp->netdev), - dpp->peer_addr, - DPP_FRAME_AUTHENTICATION_CONFIRM, ptr); - ptr += dpp_append_attr(ptr, DPP_ATTR_STATUS, &zero, 1); - ptr += dpp_append_attr(ptr, DPP_ATTR_RESPONDER_BOOT_KEY_HASH, - dpp->peer_boot_hash, 32); - if (dpp->mutual_auth) - ptr += dpp_append_attr(ptr, DPP_ATTR_INITIATOR_BOOT_KEY_HASH, - dpp->own_boot_hash, 32); - - ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, - sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, - dpp->ke, dpp->key_len, 1, - DPP_ATTR_INITIATOR_AUTH_TAG, dpp->key_len, - dpp->auth_tag); - - iov.iov_base = frame; - iov.iov_len = ptr - frame; - - dpp_send_frame(dpp, &iov, 1, dpp->current_freq); -} - -static void authenticate_response(struct dpp_sm *dpp, const uint8_t *from, - const uint8_t *body, size_t body_len) -{ - struct dpp_attr_iter iter; - enum dpp_attribute_type type; - size_t len; - const uint8_t *data; - int status = -1; - const void *r_boot_hash = NULL; - const void *r_proto = NULL; - size_t r_proto_len = 0; - const void *wrapped = NULL; - size_t wrapped_len; - _auto_(l_free) uint8_t *unwrapped1 = NULL; - _auto_(l_free) uint8_t *unwrapped2 = NULL; - const void *r_nonce = NULL; - const void *i_nonce = NULL; - const void *r_auth = NULL; - _auto_(l_ecc_point_free) struct l_ecc_point *r_proto_key = NULL; - _auto_(l_ecc_scalar_free) struct l_ecc_scalar *n = NULL; - _auto_(l_ecc_point_free) struct l_ecc_point *l = NULL; - struct l_ecc_point *bi = NULL; - const void *ad0 = body + 2; - const void *ad1 = body + 8; - uint64_t r_auth_derived[L_ECC_MAX_DIGITS]; - - l_debug("Authenticate response"); - - if (dpp->state != DPP_STATE_AUTHENTICATING) - return; - - if (!dpp->freqs) - return; - - if (memcmp(from, dpp->peer_addr, 6)) - return; - - dpp_attr_iter_init(&iter, body + 8, body_len - 8); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_STATUS: - if (len != 1) - return; - - status = l_get_u8(data); - break; - case DPP_ATTR_RESPONDER_BOOT_KEY_HASH: - r_boot_hash = data; - break; - case DPP_ATTR_RESPONDER_PROTOCOL_KEY: - r_proto = data; - r_proto_len = len; - break; - case DPP_ATTR_PROTOCOL_VERSION: - if (len != 1 || l_get_u8(data) != 2) - return; - break; - case DPP_ATTR_WRAPPED_DATA: - wrapped = data; - wrapped_len = len; - break; - default: - break; - } - } - - if (status != DPP_STATUS_OK || !r_boot_hash || !r_proto || !wrapped) { - l_debug("Auth response bad status or missing attributes"); - return; - } - - r_proto_key = l_ecc_point_from_data(dpp->curve, L_ECC_POINT_TYPE_FULL, - r_proto, r_proto_len); - if (!r_proto_key) { - l_debug("Peers protocol key was invalid"); - return; - } - - n = dpp_derive_k2(r_proto_key, dpp->proto_private, dpp->k2); - - unwrapped1 = dpp_unwrap_attr(ad0, 6, ad1, wrapped - 4 - ad1, dpp->k2, - dpp->key_len, wrapped, wrapped_len, - &wrapped_len); - if (!unwrapped1) { - l_debug("Failed to unwrap primary data"); - return; - } - - wrapped = NULL; - - dpp_attr_iter_init(&iter, unwrapped1, wrapped_len); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_RESPONDER_NONCE: - if (len != dpp->nonce_len) - return; - - r_nonce = data; - break; - case DPP_ATTR_INITIATOR_NONCE: - if (len != dpp->nonce_len) - return; - - i_nonce = data; - break; - case DPP_ATTR_RESPONDER_CAPABILITIES: - break; - case DPP_ATTR_WRAPPED_DATA: - wrapped = data; - wrapped_len = len; - break; - default: - break; - } - } - - if (!r_nonce || !i_nonce || !wrapped) { - l_debug("Wrapped data missing attributes"); - return; - } - - if (dpp->mutual_auth) { - l = dpp_derive_li(dpp->peer_boot_public, r_proto_key, - dpp->boot_private); - bi = dpp->boot_public; - } - - if (!dpp_derive_ke(i_nonce, r_nonce, dpp->m, n, l, dpp->ke)) { - l_debug("Failed to derive ke"); - return; - } - - unwrapped2 = dpp_unwrap_attr(NULL, 0, NULL, 0, dpp->ke, dpp->key_len, - wrapped, wrapped_len, &wrapped_len); - if (!unwrapped2) { - l_debug("Failed to unwrap secondary data"); - return; - } - - dpp_attr_iter_init(&iter, unwrapped2, wrapped_len); - - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_RESPONDER_AUTH_TAG: - if (len != dpp->key_len) - return; - - r_auth = data; - break; - default: - break; - } - } - - if (!r_auth) { - l_debug("R-Auth was not in secondary wrapped data"); - return; - } - - if (!dpp_derive_r_auth(i_nonce, r_nonce, dpp->nonce_len, - dpp->own_proto_public, r_proto_key, bi, - dpp->peer_boot_public, r_auth_derived)) { - l_debug("Failed to derive r_auth"); - return; - } - - if (memcmp(r_auth, r_auth_derived, dpp->key_len)) { - l_debug("R-Auth did not verify"); - return; - } - - if (!dpp_derive_i_auth(r_nonce, i_nonce, dpp->nonce_len, - r_proto_key, dpp->own_proto_public, - dpp->peer_boot_public, bi, dpp->auth_tag)) { - l_debug("Could not derive I-Auth"); - return; - } - - dpp->channel_switch = false; - dpp->current_freq = dpp->new_freq; - - dpp_send_authenticate_confirm(dpp); - - if (dpp->role == DPP_CAPABILITY_ENROLLEE) - dpp_configuration_start(dpp, from); - -} - -static void dpp_handle_presence_announcement(struct dpp_sm *dpp, - const uint8_t *from, - const uint8_t *body, - size_t body_len) -{ - struct dpp_attr_iter iter; - enum dpp_attribute_type type; - size_t len; - const uint8_t *data; - const void *r_boot = NULL; - size_t r_boot_len = 0; - uint8_t hash[32]; - - l_debug("Presence announcement "MAC, MAC_STR(from)); - - /* Must be a configurator, in an initiator role, in PRESENCE state */ - if (dpp->state != DPP_STATE_PRESENCE) - return; - - if (dpp->role != DPP_CAPABILITY_CONFIGURATOR) - return; - - if (!dpp->freqs) - return; - - /* - * The URI may not have contained a MAC address, if this announcement - * verifies set peer_addr then. - */ - if (!l_memeqzero(dpp->peer_addr, 6) && - 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_RESPONDER_BOOT_KEY_HASH: - r_boot = data; - r_boot_len = len; - break; - default: - break; - } - } - - if (!r_boot || r_boot_len != 32) { - l_debug("No responder boot hash"); - return; - } - - /* Hash what we have for the peer and check its our enrollee */ - dpp_hash(L_CHECKSUM_SHA256, hash, 2, "chirp", strlen("chirp"), - dpp->peer_asn1, dpp->peer_asn1_len); - - if (memcmp(hash, r_boot, sizeof(hash))) { - l_debug("Peers boot hash did not match"); - return; - } - - /* - * This is the peer we expected, save away the address and derive the - * initial keys. - */ - memcpy(dpp->peer_addr, from, 6); - - dpp->state = DPP_STATE_AUTHENTICATING; - - if (!dpp_send_authenticate_request(dpp)) - return; - - /* - * Requested the peer to move to another channel for the remainder of - * the protocol. IWD's current logic prohibits a configurator from - * running while not connected, so we can assume here that the new - * frequency is the same of the connected BSS. Wait until an ACK is - * received for the auth request then cancel the offchannel request. - */ - if (dpp->current_freq != dpp->new_freq) - 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_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; - 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)); - - ptr += dpp_append_point(ptr, DPP_ATTR_ENCRYPTED_KEY, n); - - 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_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) -{ - 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; - } + struct scan_bss *bss; + struct network *network; + int ret; - if (group != l_ecc_curve_get_ike_group(dpp->curve)) { - l_debug("initiator is not using the same group"); - goto bad_group; - } + network = station_network_find(station, dpp->config->ssid, + SECURITY_PSK); - /* - * 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; - } + dpp_reset(dpp); - if (version && version != dpp->pkex_version) { - l_debug("initiator is not using the same version, ignoring"); + if (!network) { + l_debug("Network was not found!"); 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; - } - } + l_debug("connecting to %s from DPP", network_get_ssid(network)); - 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; - } + bss = network_bss_select(network, true); + ret = network_autoconnect(network, bss); + if (ret < 0) + l_warn("failed to connect after DPP (%d) %s", ret, + strerror(-ret)); +} - memcpy(dpp->peer_addr, from, 6); +static bool dpp_scan_results(int err, struct l_queue *bss_list, + const struct scan_freq_set *freqs, + void *userdata) +{ + struct dpp *dpp = userdata; + struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); - 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; - } + if (err < 0) + goto reset; - dpp->pkex_id = l_strndup(id, id_len); + if (!bss_list || l_queue_length(bss_list) == 0) + goto reset; - /* 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; - } + /* + * The station watch _should_ detect this and reset, which cancels the + * scan. But just in case... + */ + if (L_WARN_ON(station_get_connected_network(station))) + goto reset; - /* Save the encrypted key/identifier for the agent callback */ + station_set_scan_results(station, bss_list, freqs, false); - dpp->peer_encr_key = l_steal_ptr(m); + dpp_start_connect(NULL, dpp); - return; - } + return true; - dpp_process_pkex_exchange_request(dpp, m); +reset: + return false; +} - return; +static void dpp_scan_destroy(void *userdata) +{ + struct dpp *dpp = userdata; -bad_group: - dpp_send_bad_group(dpp, from); + dpp->connect_scan_id = 0; + dpp_reset(dpp); } -static void dpp_send_commit_reveal_response(struct dpp_sm *dpp, - const uint8_t *v, size_t v_len) +static void dpp_known_network_watch(enum known_networks_event event, + const struct network_info *info, + void *user_data) { - uint8_t frame[256]; - uint8_t *ptr = frame; - struct iovec iov; - const uint8_t *own_mac = netdev_get_address(dpp->netdev); - uint8_t b_pub[L_ECC_POINT_MAX_BYTES]; - size_t b_len; - struct mmpdu_header *hdr = (struct mmpdu_header *)frame; + struct dpp *dpp = user_data; - memset(frame, 0, sizeof(frame)); + /* + * Check the following + * - DPP is enrolling + * - DPP finished (dpp->config is set) + * - This is for the network DPP just configured + * - DPP isn't already trying to connect (e.g. if the profile was + * immediately modified after DPP synced it). + * - DPP didn't start a scan for the network. + */ + if (dpp->role != DPP_CAPABILITY_ENROLLEE) + return; + if (!dpp->config) + return; + if (strcmp(info->ssid, dpp->config->ssid)) + return; + if (dpp->connect_idle) + return; + if (dpp->connect_scan_id) + return; - b_len = l_ecc_point_get_data(dpp->boot_public, b_pub, sizeof(b_pub)); + switch (event) { + case KNOWN_NETWORKS_EVENT_ADDED: + case KNOWN_NETWORKS_EVENT_UPDATED: + /* + * network.c takes care of updating the settings for the + * network. This callback just tells us to begin the connection. + * We do have use an idle here because there is no strict + * guarantee of ordering between known network events, e.g. DPP + * could have been called into prior to network and the network + * object isn't updated yet. + */ + dpp->connect_idle = l_idle_create(dpp_start_connect, dpp, NULL); + break; + case KNOWN_NETWORKS_EVENT_REMOVED: + l_warn("profile was removed before DPP could connect"); + break; + } +} - ptr += dpp_build_header(own_mac, dpp->peer_addr, - DPP_FRAME_PKEX_COMMIT_REVEAL_RESPONSE, ptr); - ptr += dpp_append_wrapped_data(mmpdu_body(hdr) + 1, - sizeof(frame) - mmpdu_header_len(hdr) - 1, ptr, - dpp->z, dpp->z_len, 2, - DPP_ATTR_BOOTSTRAPPING_KEY, b_len, b_pub, - DPP_ATTR_RESPONDER_AUTH_TAG, v_len, v); +static void dpp_handle_config_frame(const struct mmpdu_header *frame, + const void *body, size_t body_len, + int rssi, void *user_data) +{ + struct dpp *dpp = user_data; + struct dpp_configuration *config = NULL; + struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); + struct network *network = NULL; + struct scan_bss *bss = NULL; - iov.iov_base = frame; - iov.iov_len = ptr - frame; + dpp_write_config(config, network); - dpp_send_frame(dpp, &iov, 1, dpp->current_freq); -} + offchannel_cancel(dpp->wdev_id, dpp->offchannel_id); -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) + if (network && bss) { + l_debug("delaying connect until settings are synced"); + dpp->config = config; return; + } else if (station) { + struct scan_parameters params = {0}; - if (dpp->role != DPP_CAPABILITY_CONFIGURATOR) - return; + params.ssid = (void *) config->ssid; + params.ssid_len = config->ssid_len; - dpp_attr_iter_init(&iter, body + 8, body_len - 8); + l_debug("Scanning for %s", config->ssid); - while (dpp_attr_iter_next(&iter, &type, &len, &data)) { - switch (type) { - case DPP_ATTR_WRAPPED_DATA: - wrapped = data; - wrapped_len = len; - break; - default: - break; + dpp->connect_scan_id = scan_active_full(dpp->wdev_id, ¶ms, + dpp_scan_triggered, + dpp_scan_results, dpp, + dpp_scan_destroy); + if (dpp->connect_scan_id) { + dpp->config = config; + return; } } - 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; +static void dpp_roc_started(void *user_data) +{ + struct dpp *dpp = user_data; - key = data; - key_len = len; - break; - case DPP_ATTR_INITIATOR_AUTH_TAG: - if (len != 32) - return; + /* + * - If a configurator, nothing to do but wait for a request + * (unless multicast frame registration is unsupported in which case + * send an authenticate request now) + * - If in the presence state continue sending announcements. + * - If authenticating, and this is a result of a channel switch send + * the authenticate response now. + */ - i_auth = data; - i_auth_len = len; - break; - default: - break; - } - } + dpp->roc_started = true; - if (!key || !i_auth) { - l_debug("missing attributes"); + /* + * The retry timer indicates a frame was not acked in which case we + * should not change any state or send any frames until that expires. + */ + if (dpp->retry_timeout) 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; + if (dpp->frame_pending) { + dpp_frame_retry(dpp); + return; } +} - /* J' = y * A' */ - j = l_ecc_point_new(dpp->curve); - - l_ecc_point_multiply(j, dpp->pkex_private, dpp->peer_boot_public); +static void dpp_start_offchannel(struct dpp *dpp, uint32_t freq); - dpp_derive_u(j, dpp->peer_addr, dpp->peer_boot_public, dpp->pkex_public, - dpp->y_or_x, u, &u_len); +static void dpp_offchannel_timeout(int error, void *user_data) +{ + struct dpp *dpp = user_data; - if (memcmp(u, i_auth, i_auth_len)) { - l_debug("Initiator auth tag did not verify"); - goto failed; - } + dpp->offchannel_id = 0; + dpp->roc_started = false; - /* L' = x * B' */ - l = l_ecc_point_new(dpp->curve); + /* + * If cancelled this is likely due to netdev going down or from Stop(). + * Otherwise there was some other problem which is probably not + * recoverable. + */ + if (error == -ECANCELED) + return; + else if (error == -EIO) + goto next_roc; + else if (error < 0) + goto protocol_failed; - l_ecc_point_multiply(l, dpp->boot_private, dpp->y_or_x); + dpp->freqs_idx++; - 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; + if (dpp->freqs_idx >= dpp->freqs_len) { + l_debug("Max retries offchannel"); + dpp->freqs_idx = 0; } - dpp_send_commit_reveal_response(dpp, v, v_len); + dpp->current_freq = dpp->freqs[dpp->freqs_idx]; + + l_debug("Offchannel timeout, moving to next frequency %u, duration %u", + dpp->current_freq, dpp->dwell); - dpp_pkex_start_authentication(dpp); +next_roc: + dpp_start_offchannel(dpp, dpp->current_freq); return; -failed: +protocol_failed: dpp_reset(dpp); } -static void dpp_handle_frame(struct dpp_sm *dpp, +static void dpp_start_offchannel(struct dpp *dpp, uint32_t freq) +{ + /* + * This needs to be handled carefully for a few reasons: + * + * First, the next offchannel operation needs to be started prior to + * canceling an existing one. This is so the offchannel work can + * continue uninterrupted without any other work items starting in + * between canceling and starting the next (e.g. if a scan request is + * sitting in the queue). + * + * Second, dpp_offchannel_timeout resets dpp->offchannel_id to zero + * which is why the new ID is saved and only set to dpp->offchannel_id + * once the previous offchannel work is cancelled (i.e. destroy() has + * been called). + */ + uint32_t id = offchannel_start(netdev_get_wdev_id(dpp->netdev), + WIPHY_WORK_PRIORITY_OFFCHANNEL, + freq, dpp->dwell, dpp_roc_started, + dpp, dpp_offchannel_timeout); + + if (dpp->offchannel_id) + offchannel_cancel(dpp->wdev_id, dpp->offchannel_id); + + dpp->offchannel_id = id; +} + +static void dpp_handle_frame(struct dpp *dpp, const struct mmpdu_header *frame, const void *body, size_t body_len) { @@ -3472,39 +810,6 @@ static void dpp_handle_frame(struct dpp_sm *dpp, ptr = body + 7; switch (*ptr) { - case DPP_FRAME_AUTHENTICATION_REQUEST: - authenticate_request(dpp, frame->address_2, body, body_len); - break; - case DPP_FRAME_AUTHENTICATION_RESPONSE: - authenticate_response(dpp, frame->address_2, body, body_len); - break; - case DPP_FRAME_AUTHENTICATION_CONFIRM: - authenticate_confirm(dpp, frame->address_2, body, body_len); - break; - case DPP_FRAME_CONFIGURATION_RESULT: - dpp_handle_config_result_frame(dpp, frame->address_2, - body, body_len); - break; - case DPP_FRAME_PRESENCE_ANNOUNCEMENT: - dpp_handle_presence_announcement(dpp, frame->address_2, - body, body_len); - break; - 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; - 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; @@ -3513,7 +818,7 @@ static void dpp_handle_frame(struct dpp_sm *dpp, static bool match_wdev(const void *a, const void *b) { - const struct dpp_sm *dpp = a; + const struct dpp *dpp = a; const uint64_t *wdev_id = b; return *wdev_id == dpp->wdev_id; @@ -3521,7 +826,7 @@ static bool match_wdev(const void *a, const void *b) static void dpp_frame_timeout(struct l_timeout *timeout, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; l_timeout_remove(timeout); dpp->retry_timeout = NULL; @@ -3540,7 +845,7 @@ static void dpp_frame_timeout(struct l_timeout *timeout, void *user_data) static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data) { - struct dpp_sm *dpp; + struct dpp *dpp; uint64_t wdev_id = 0; uint64_t cookie = 0; bool ack = false; @@ -3620,7 +925,7 @@ retransmit: static void dpp_unicast_notify(struct l_genl_msg *msg, void *user_data) { - struct dpp_sm *dpp; + struct dpp *dpp; const uint64_t *wdev_id = NULL; struct l_genl_attr attr; uint16_t type, len, frame_len; @@ -3692,7 +997,7 @@ static void dpp_frame_watch_cb(struct l_genl_msg *msg, void *user_data) * require multicast support. This is only supported by ath9k, so adding * general support to frame-xchg isn't desireable. */ -static void dpp_frame_watch(struct dpp_sm *dpp, uint16_t frame_type, +static void dpp_frame_watch(struct dpp *dpp, uint16_t frame_type, const uint8_t *prefix, size_t prefix_len) { struct l_genl_msg *msg; @@ -3728,7 +1033,7 @@ static void dpp_frame_watch(struct dpp_sm *dpp, uint16_t frame_type, */ static void dpp_station_state_watch(enum station_state state, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; if (dpp->state == DPP_STATE_NOTHING) return; @@ -3779,7 +1084,7 @@ static void dpp_station_state_watch(enum station_state state, void *user_data) static void dpp_create(struct netdev *netdev) { struct l_dbus *dbus = dbus_get_bus(); - struct dpp_sm *dpp = l_new(struct dpp_sm, 1); + struct dpp *dpp = l_new(struct dpp, 1); uint8_t dpp_conf_response_prefix[] = { 0x04, 0x0b }; uint8_t dpp_conf_request_prefix[] = { 0x04, 0x0a }; uint64_t wdev_id = netdev_get_wdev_id(netdev); @@ -3789,32 +1094,22 @@ static void dpp_create(struct 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); - dpp->nonce_len = dpp_nonce_len_from_key_len(dpp->key_len); + dpp->max_roc = wiphy_get_max_roc_duration(wiphy_find_by_wdev(wdev_id)); dpp->mcast_support = wiphy_has_ext_feature( wiphy_find_by_wdev(dpp->wdev_id), NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS); - l_ecdh_generate_key_pair(dpp->curve, &dpp->boot_private, - &dpp->boot_public); - - dpp->own_asn1 = dpp_point_to_asn1(dpp->boot_public, &dpp->own_asn1_len); - - dpp_hash(L_CHECKSUM_SHA256, dpp->own_boot_hash, 1, - dpp->own_asn1, dpp->own_asn1_len); - 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 + * Since both interfaces share the dpp 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. + * will destroy the dpp. */ dpp->refcount = 2; @@ -3823,11 +1118,11 @@ static void dpp_create(struct netdev *netdev) frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0, dpp_conf_response_prefix, sizeof(dpp_conf_response_prefix), - dpp_handle_config_response_frame, dpp, NULL); + dpp_handle_config_frame, dpp, NULL); frame_watch_add(netdev_get_wdev_id(netdev), 0, 0x00d0, dpp_conf_request_prefix, sizeof(dpp_conf_request_prefix), - dpp_handle_config_request_frame, dpp, NULL); + dpp_handle_config_frame, dpp, NULL); dpp->station_watch = station_add_state_watch(station, dpp_station_state_watch, dpp, NULL); @@ -3864,7 +1159,7 @@ static void dpp_netdev_watch(struct netdev *netdev, /* * EasyConnect 2.0 - 6.2.2 */ -static uint32_t *dpp_add_default_channels(struct dpp_sm *dpp, size_t *len_out) +static uint32_t *dpp_add_default_channels(struct dpp *dpp, size_t *len_out) { struct wiphy *wiphy = wiphy_find_by_wdev( netdev_get_wdev_id(dpp->netdev)); @@ -3901,7 +1196,7 @@ static uint32_t *dpp_add_default_channels(struct dpp_sm *dpp, size_t *len_out) * procedure is not being fully implemented. In reality doing this would result * in quite terrible DPP performance anyways. */ -static void dpp_start_presence(struct dpp_sm *dpp, uint32_t *limit_freqs, +static void dpp_start_presence(struct dpp *dpp, uint32_t *limit_freqs, size_t limit_len) { if (limit_freqs) { @@ -3921,7 +1216,7 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; uint32_t freq = band_channel_to_freq(6, BAND_FREQ_2_4_GHZ); struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); @@ -3939,17 +1234,10 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus, } else if (!station) l_debug("No station device, continuing anyways..."); - dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2, - netdev_get_address(dpp->netdev), &freq, - 1, NULL, NULL); - 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); - l_debug("DPP Start Enrollee: %s", dpp->uri); dpp->pending = l_dbus_message_ref(message); @@ -3971,7 +1259,7 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus, * will go offchannel to frequencies advertised by the enrollees URI or, * if no channels are provided, use a default channel list. */ -static bool dpp_configurator_start_presence(struct dpp_sm *dpp, const char *uri) +static bool dpp_configurator_start_presence(struct dpp *dpp, const char *uri) { _auto_(l_free) uint32_t *freqs = NULL; size_t freqs_len = 0; @@ -4002,20 +1290,8 @@ static bool dpp_configurator_start_presence(struct dpp_sm *dpp, const char *uri) if (info->freqs) freqs = scan_freq_set_to_fixed_array(info->freqs, &freqs_len); - dpp->peer_boot_public = l_ecc_point_clone(info->boot_public); - dpp->peer_asn1 = dpp_point_to_asn1(info->boot_public, - &dpp->peer_asn1_len); - dpp_free_uri_info(info); - if (!dpp->peer_asn1) { - l_debug("Peer boot key did not convert to asn1"); - return false; - } - - dpp_hash(L_CHECKSUM_SHA256, dpp->peer_boot_hash, 1, dpp->peer_asn1, - dpp->peer_asn1_len); - dpp_start_presence(dpp, freqs, freqs_len); return true; @@ -4027,7 +1303,7 @@ static struct l_dbus_message *dpp_start_configurator_common( void *user_data, bool responder) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; struct l_dbus_message *reply; struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); struct scan_bss *bss; @@ -4059,9 +1335,6 @@ static struct l_dbus_message *dpp_start_configurator_common( dpp->interface != DPP_INTERFACE_UNBOUND) return dbus_error_busy(message); - l_ecdh_generate_key_pair(dpp->curve, &dpp->proto_private, - &dpp->own_proto_public); - dpp->state = DPP_STATE_PRESENCE; if (!responder) { @@ -4071,12 +1344,6 @@ static struct l_dbus_message *dpp_start_configurator_common( if (!dpp_configurator_start_presence(dpp, uri)) return dbus_error_invalid_args(message); - /* Since we have the peer's URI generate the keys now */ - l_getrandom(dpp->i_nonce, dpp->nonce_len); - - dpp->m = dpp_derive_k1(dpp->peer_boot_public, - dpp->proto_private, dpp->k1); - if (!dpp->mcast_support) dpp->state = DPP_STATE_AUTHENTICATING; @@ -4084,9 +1351,6 @@ static struct l_dbus_message *dpp_start_configurator_common( } else dpp->current_freq = bss->frequency; - dpp->uri = dpp_generate_uri(dpp->own_asn1, dpp->own_asn1_len, 2, - netdev_get_address(dpp->netdev), - &bss->frequency, 1, NULL, NULL); dpp->role = DPP_CAPABILITY_CONFIGURATOR; dpp->interface = DPP_INTERFACE_DPP; dpp->config = dpp_configuration_new(settings, @@ -4122,7 +1386,7 @@ static struct l_dbus_message *dpp_dbus_stop(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; l_debug(""); @@ -4136,7 +1400,7 @@ static struct l_dbus_message *dpp_dbus_stop(struct l_dbus *dbus, static void dpp_pkex_scan_trigger(int err, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; if (err < 0) dpp_reset(dpp); @@ -4152,7 +1416,7 @@ static void dpp_pkex_scan_trigger(int err, void *user_data) * 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) +static uint32_t *dpp_default_freqs(struct dpp *dpp, size_t *out_len) { struct wiphy *wiphy = wiphy_find_by_wdev(dpp->wdev_id); uint32_t default_channels[3] = { 2437, 5220, 5745 }; @@ -4184,7 +1448,7 @@ static bool dpp_pkex_scan_notify(int err, struct l_queue *bss_list, const struct scan_freq_set *freqs, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; const struct l_queue_entry *e; _auto_(scan_freq_set_free) struct scan_freq_set *freq_set = NULL; @@ -4216,8 +1480,6 @@ static bool dpp_pkex_scan_notify(int err, struct l_queue *bss_list, start: dpp->current_freq = dpp->freqs[0]; - 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); @@ -4231,12 +1493,12 @@ failed: static void dpp_pkex_scan_destroy(void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; dpp->pkex_scan_id = 0; } -static bool dpp_start_pkex_enrollee(struct dpp_sm *dpp, const char *key, +static bool dpp_start_pkex_enrollee(struct dpp *dpp, const char *key, const char *identifier) { _auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL; @@ -4256,28 +1518,6 @@ static bool dpp_start_pkex_enrollee(struct dpp_sm *dpp, const char *key, * 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); @@ -4349,7 +1589,7 @@ 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; + struct dpp *dpp = user_data; const char *key; const char *id; struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); @@ -4377,14 +1617,14 @@ invalid_args: static void pkex_agent_disconnect(struct l_dbus *dbus, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *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, +static void dpp_create_agent(struct dpp *dpp, const char *path, struct l_dbus_message *message) { const char *sender = l_dbus_message_get_sender(message); @@ -4401,7 +1641,7 @@ static void dpp_create_agent(struct dpp_sm *dpp, const char *path, l_debug("Registered a SharedCodeAgent on path %s", path); } -static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp, +static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp *dpp, const char *key, const char *identifier, const char *agent_path, struct l_dbus_message *message) @@ -4444,12 +1684,10 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp_sm *dpp, 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); if (dpp->pkex_key) @@ -4465,7 +1703,7 @@ static struct l_dbus_message *dpp_dbus_pkex_configure_enrollee( struct l_dbus_message *message, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; const char *key; const char *id; @@ -4482,7 +1720,7 @@ static struct l_dbus_message *dpp_dbus_pkex_start_configurator( struct l_dbus_message *message, void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; const char *path; if (!l_dbus_message_get_arguments(message, "o", &path)) @@ -4512,7 +1750,7 @@ static void dpp_setup_interface(struct l_dbus_interface *interface) 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; + struct dpp *dpp = user_data; l_debug(""); @@ -4543,7 +1781,7 @@ static void dpp_setup_pkex_interface(struct l_dbus_interface *interface) static void dpp_destroy_interface(void *user_data) { - struct dpp_sm *dpp = user_data; + struct dpp *dpp = user_data; if (--dpp->refcount) return; From patchwork Wed Mar 27 15:19:57 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Prestwood X-Patchwork-Id: 13606778 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 A921C12FB18 for ; Wed, 27 Mar 2024 15:20:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552817; cv=none; b=CKksKDMvGeCNebj66EpHghul0Ke7pgjk4XjBSkeQ/X/GAXTnttpGDuymUTR0cUNAI5WfSWaiHUdmkaT6LYl3s3hfd9fQ32wOw/AQbTrQpto+1dqAMM19GYxKCjnyhVgAF1yviX3CdOtu5IlyNGU0ychCdXn1/kvRiBIufcWSnlA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1711552817; c=relaxed/simple; bh=yQi6FfMuUCzHLSM65lRQStCk9TVGUr2kieEcJqftLaw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=KeMpGRhFbAHIWpaCjtrIl+Mhzk8LVnAKQ0b8C7g4A+4ZwhKErKP8kOinJOyCc5TWnSXgESQm3LTNHIASAc32Q7KiRZhsW5Ci5U9VuM9QaWlqLR1DTh+JO9I1aQ2cDs2C8u3nr4GWP6aa40hNJusct100RekeSJEd04Bhtwf0Ass= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=kpOQwI8V; arc=none smtp.client-ip=209.85.219.49 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="kpOQwI8V" Received: by mail-qv1-f49.google.com with SMTP id 6a1803df08f44-6963cf14771so56810416d6.3 for ; Wed, 27 Mar 2024 08:20:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1711552813; x=1712157613; 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=1ZcqpiDFd4jrZd1DF6gcjTOmyC++EjqhfcHKBrpnkaM=; b=kpOQwI8VpNrYVY+RbF1GR830p7YqFCFB1Jb+qA21XKlLIvk9UsosD5jeLliPX8RjIE 5DNVwTWI50T6dOPzQhnZZY958fNKvnc4yf/mVABXE0gtV4FljkeAT7aO3AJjcvWrBFbq Oua0mfYly2noGJD2E7HOa3z6OD/LB7xs9/5DpxyyAyyOcn1dOhl+6gkpnvIyZVztMJVN TSydIZvMLrXIskCtVUlFYnth905RkHCfmK2HgK/vrfPPOBn49SH5y/mDg/p/PpSzJju7 vqw1GfIYrqQBrcU88abqbnhjQsboYI8VQJwAJWH1SMbCmf5ub/p+iSWH9Yo9xp3oyJ9P jt2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1711552813; x=1712157613; 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=1ZcqpiDFd4jrZd1DF6gcjTOmyC++EjqhfcHKBrpnkaM=; b=p43H0XeJImB3R2SalxOqL+uNhxvgcU3nHqF/pBolibHHjRcy/UmCYUlKlJPCJ3lr9J jLnZ2rXKpjqxt3wWhSMkD7pF1G1YbJLZzcbVRYELFkgMJOORVrVwJRDhdLTgwb6IWDDE 13aPJkz9QlXwvWNwngaiLkdO2nvTm2BDQH0kpeJBd0S8br6J7Ss+feAWbatxfZMdUWbt TPR/pbtuh4q9TcE0x4lfKlNse0sqh0zlr79bOGJVdWO6EzMQeSIDmDDOSspabSQsAcFr Ba4U43RYi9tdl739QOzcv9Gk2l2lOBhR9HgOj9N8XsiDzT0LMwB8LfuxQiDYaeGzkhvt fFrA== X-Gm-Message-State: AOJu0YwdFUcekO5NUafmlPmjBQKTvAqVwNgSxi9Sl6MuheXASEjf6bHB 6vP1qgGVdcB6K/ij3GEht3gVsWgHFIcCacynOKDRuPBEjDFBH8gTGE1Ci5+y X-Google-Smtp-Source: AGHT+IE3wA9AWNIxtu6eNhBOkKmptyPKHqelZ6CTk+bM7ojdpYfQmYIrK/FpV+iSJsjr56B0P+OvsA== X-Received: by 2002:a05:6214:cac:b0:691:43ce:b76f with SMTP id s12-20020a0562140cac00b0069143ceb76fmr3478991qvs.64.1711552813101; Wed, 27 Mar 2024 08:20:13 -0700 (PDT) Received: from LOCLAP699.rst-02.locus ([208.195.13.130]) by smtp.gmail.com with ESMTPSA id 7-20020a056214202700b006968ce99b7esm3520782qvf.34.2024.03.27.08.20.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 Mar 2024 08:20:12 -0700 (PDT) From: James Prestwood To: iwd@lists.linux.dev Cc: James Prestwood Subject: [PATCH 9/9] dpp: use common state machine Date: Wed, 27 Mar 2024 08:19:57 -0700 Message-Id: <20240327151957.1446149-9-prestwoj@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240327151957.1446149-1-prestwoj@gmail.com> References: <20240327151957.1446149-1-prestwoj@gmail.com> Precedence: bulk X-Mailing-List: iwd@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Begins using the common state machine within dpp-common.c. All the message flow/handling is now taken care of by the SM. The dpp.c module now only handles a specific set of functions required by 802.11 encapsulation (and DBus): - Encapsulating and writing out frames for the SM. - Decapsulating and reading frames into the SM - Offchannel management; setting/unsetting the write handler as the offchannel work is ready or timed out. - DBus API. The dpp.c module can now create its minimal dpp object on init and the dpp_sm object lifetime follows the protocol. When DPP is started dpp.c will set any options required for the configuration, role, inititor vs responder, etc. DPP can then be started within the SM, and any offchannel work should be queued. The basic flow after that is quite simple: - If using offchannel the write handler must be set or unset as needed when frames can/can't be sent out. Offchannel frequencies are tracked by dpp.c, including presence/PKEX exchange frequency iteration. - Receved frames should be validated minimally: Prefix should be some type of DPP frame, and the source should be either a broadcast or from the expected peer (if known). - Received frames should be stripped of the mpdu header and category byte. - If the SM calls into the write handler the frame should be encapsulated with a mpdu header and category byte, then sent. - Upon receiving DPP events: DPP_EVENT_FAILED: The SM should be destroyed DPP_EVENT_PEER_ACCEPTED The MAC for the last message received should be tracked and only messages from the MAC should be passed to the SM going forward. DPP_EVENT_CHANNEL_SWITCH The channel should be switched to a new one, as communicated by the authentication request DPP_EVENT_PKEX_KEY_REQUESTED The PKEX key should be obtained and set into the SM using dpp_sm_set_pkex_key(). DPP_EVENT_SUCCESS DPP completed. As an enrollee the configuration can now be obtained. The SM should be destroyed. --- src/dpp.c | 496 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 353 insertions(+), 143 deletions(-) diff --git a/src/dpp.c b/src/dpp.c index 31d0ef28..cd7facbf 100644 --- a/src/dpp.c +++ b/src/dpp.c @@ -54,6 +54,7 @@ #include "src/handshake.h" #include "src/nl80211util.h" #include "src/knownnetworks.h" +#include "src/dpp-common.h" #define DPP_FRAME_MAX_RETRIES 5 #define DPP_FRAME_RETRY_TIMEOUT 1 @@ -67,20 +68,6 @@ static uint32_t unicast_watch; 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, -}; - -enum dpp_capability { - DPP_CAPABILITY_ENROLLEE = 0x01, - DPP_CAPABILITY_CONFIGURATOR = 0x02, -}; - enum dpp_interface { DPP_INTERFACE_UNBOUND, DPP_INTERFACE_DPP, @@ -95,6 +82,7 @@ struct pkex_agent { }; struct dpp { + struct dpp_sm *sm; struct netdev *netdev; char *uri; uint8_t role; @@ -104,10 +92,10 @@ struct dpp { uint64_t wdev_id; + /* Could probably do away with storing these here */ struct l_ecc_scalar *boot_private; struct l_ecc_point *boot_public; - enum dpp_state state; enum dpp_interface interface; struct pkex_agent *agent; @@ -121,18 +109,17 @@ struct dpp { size_t freqs_idx; uint32_t dwell; uint32_t current_freq; - uint32_t new_freq; struct scan_freq_set *presence_list; uint32_t max_roc; uint32_t offchannel_id; uint8_t peer_addr[6]; + bool peer_accepted; /* Timeout of either auth/config protocol */ struct l_timeout *timeout; - struct dpp_configuration *config; uint32_t connect_scan_id; uint64_t frame_cookie; uint8_t frame_retry; @@ -140,19 +127,12 @@ struct dpp { size_t frame_size; struct l_timeout *retry_timeout; - struct l_dbus_message *pending; - struct l_idle *connect_idle; - /* PKEX-specific values */ - char *pkex_id; - char *pkex_key; - uint32_t pkex_scan_id; bool mcast_support : 1; bool roc_started : 1; - bool channel_switch : 1; }; static const char *dpp_role_to_string(enum dpp_capability role) @@ -173,8 +153,7 @@ static bool dpp_pkex_get_started(struct l_dbus *dbus, void *user_data) { struct dpp *dpp = user_data; - bool started = (dpp->state != DPP_STATE_NOTHING && - dpp->interface == DPP_INTERFACE_PKEX); + bool started = (dpp->sm && dpp->interface == DPP_INTERFACE_PKEX); l_dbus_message_builder_append_basic(builder, 'b', &started); @@ -189,8 +168,7 @@ static bool dpp_pkex_get_role(struct l_dbus *dbus, struct dpp *dpp = user_data; const char *role; - if (dpp->state == DPP_STATE_NOTHING || - dpp->interface != DPP_INTERFACE_PKEX) + if (!dpp->sm || dpp->interface != DPP_INTERFACE_PKEX) return false; role = dpp_role_to_string(dpp->role); @@ -207,8 +185,7 @@ static bool dpp_get_started(struct l_dbus *dbus, void *user_data) { struct dpp *dpp = user_data; - bool started = (dpp->state != DPP_STATE_NOTHING && - dpp->interface == DPP_INTERFACE_DPP); + bool started = (dpp->sm && dpp->interface == DPP_INTERFACE_DPP); l_dbus_message_builder_append_basic(builder, 'b', &started); @@ -223,8 +200,7 @@ static bool dpp_get_role(struct l_dbus *dbus, struct dpp *dpp = user_data; const char *role; - if (dpp->state == DPP_STATE_NOTHING || - dpp->interface != DPP_INTERFACE_DPP) + if (!dpp->sm || dpp->interface != DPP_INTERFACE_DPP) return false; role = dpp_role_to_string(dpp->role); @@ -242,8 +218,7 @@ static bool dpp_get_uri(struct l_dbus *dbus, { struct dpp *dpp = user_data; - if (dpp->state == DPP_STATE_NOTHING || - dpp->interface != DPP_INTERFACE_DPP) + if (!dpp->sm || dpp->interface != DPP_INTERFACE_DPP) return false; l_dbus_message_builder_append_basic(builder, 's', dpp->uri); @@ -380,11 +355,6 @@ static void dpp_reset(struct dpp *dpp) dpp->timeout = NULL; } - if (dpp->config) { - dpp_configuration_free(dpp->config); - dpp->config = NULL; - } - if (dpp->connect_scan_id) { scan_cancel(dpp->wdev_id, dpp->connect_scan_id); dpp->connect_scan_id = 0; @@ -410,7 +380,6 @@ static void dpp_reset(struct dpp *dpp) dpp->connect_idle = NULL; } - dpp->new_freq = 0; dpp->frame_retry = 0; dpp->frame_cookie = 0; @@ -419,6 +388,11 @@ static void dpp_reset(struct dpp *dpp) dpp_property_changed_notify(dpp); dpp->interface = DPP_INTERFACE_UNBOUND; + + if (dpp->sm) { + dpp_sm_free(dpp->sm); + dpp->sm = NULL; + } } static void dpp_free(struct dpp *dpp) @@ -510,7 +484,27 @@ static void dpp_frame_retry(struct dpp *dpp) dpp->frame_pending = NULL; } -static void dpp_write_config(struct dpp_configuration *config, +static size_t dpp_build_mpdu_header(const uint8_t *src, + const uint8_t *dest, uint8_t *buf) +{ + struct mmpdu_header *hdr = (struct mmpdu_header *)buf; + uint8_t *body; + + hdr->fc.protocol_version = 0; + hdr->fc.type = MPDU_TYPE_MANAGEMENT; + hdr->fc.subtype = MPDU_MANAGEMENT_SUBTYPE_ACTION; + memcpy(hdr->address_1, dest, 6); + memcpy(hdr->address_2, src, 6); + memcpy(hdr->address_3, broadcast, 6); + + body = buf + mmpdu_header_len(hdr); + /* Category: Public */ + *body++ = 0x04; + + return body - buf; +} + +static void dpp_write_config(const struct dpp_configuration *config, struct network *network) { _auto_(l_settings_free) struct l_settings *settings = l_settings_new(); @@ -555,9 +549,13 @@ static void dpp_start_connect(struct l_idle *idle, void *user_data) struct scan_bss *bss; struct network *network; int ret; + const struct dpp_configuration *config; - network = station_network_find(station, dpp->config->ssid, - SECURITY_PSK); + config = dpp_sm_get_configuration(dpp->sm); + if (L_WARN_ON(!config)) + return; + + network = station_network_find(station, config->ssid, SECURITY_PSK); dpp_reset(dpp); @@ -618,7 +616,8 @@ static void dpp_known_network_watch(enum known_networks_event event, void *user_data) { struct dpp *dpp = user_data; - + struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); + const struct dpp_configuration *config; /* * Check the following * - DPP is enrolling @@ -628,16 +627,23 @@ static void dpp_known_network_watch(enum known_networks_event event, * immediately modified after DPP synced it). * - DPP didn't start a scan for the network. */ + if (!dpp->sm) + return; + + config = dpp_sm_get_configuration(dpp->sm); + if (dpp->role != DPP_CAPABILITY_ENROLLEE) return; - if (!dpp->config) + if (!config) return; - if (strcmp(info->ssid, dpp->config->ssid)) + if (strcmp(info->ssid, config->ssid)) return; if (dpp->connect_idle) return; if (dpp->connect_scan_id) return; + if (!station || station_get_connected_network(station)) + return; switch (event) { case KNOWN_NETWORKS_EVENT_ADDED: @@ -663,54 +669,40 @@ static void dpp_handle_config_frame(const struct mmpdu_header *frame, int rssi, void *user_data) { struct dpp *dpp = user_data; - struct dpp_configuration *config = NULL; - struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); - struct network *network = NULL; - struct scan_bss *bss = NULL; - dpp_write_config(config, network); + if (!dpp->sm) + return; - offchannel_cancel(dpp->wdev_id, dpp->offchannel_id); + dpp_handle_rx(dpp->sm, body + 1, body_len - 1); +} - if (network && bss) { - l_debug("delaying connect until settings are synced"); - dpp->config = config; - return; - } else if (station) { - struct scan_parameters params = {0}; +static void dpp_tx_frame(const uint8_t *data, size_t len, + void *user_data) +{ + struct dpp *dpp = user_data; + struct iovec iov[2]; + uint8_t hdr[36]; - params.ssid = (void *) config->ssid; - params.ssid_len = config->ssid_len; + memset(hdr, 0, sizeof(hdr)); - l_debug("Scanning for %s", config->ssid); + iov[0].iov_len = dpp_build_mpdu_header(netdev_get_address(dpp->netdev), + dpp->peer_addr, hdr); + iov[0].iov_base = hdr; - dpp->connect_scan_id = scan_active_full(dpp->wdev_id, ¶ms, - dpp_scan_triggered, - dpp_scan_results, dpp, - dpp_scan_destroy); - if (dpp->connect_scan_id) { - dpp->config = config; - return; - } - } + iov[1].iov_base = (void *)data; + iov[1].iov_len = len; + dpp_send_frame(dpp, iov, 2, dpp->current_freq); } static void dpp_roc_started(void *user_data) { struct dpp *dpp = user_data; - /* - * - If a configurator, nothing to do but wait for a request - * (unless multicast frame registration is unsupported in which case - * send an authenticate request now) - * - If in the presence state continue sending announcements. - * - If authenticating, and this is a result of a channel switch send - * the authenticate response now. - */ - dpp->roc_started = true; + dpp_sm_set_write_handler(dpp->sm, dpp_tx_frame); + /* * The retry timer indicates a frame was not acked in which case we * should not change any state or send any frames until that expires. @@ -733,6 +725,8 @@ static void dpp_offchannel_timeout(int error, void *user_data) dpp->offchannel_id = 0; dpp->roc_started = false; + dpp_sm_set_write_handler(dpp->sm, NULL); + /* * If cancelled this is likely due to netdev going down or from Stop(). * Otherwise there was some other problem which is probably not @@ -790,6 +784,8 @@ static void dpp_start_offchannel(struct dpp *dpp, uint32_t freq) if (dpp->offchannel_id) offchannel_cancel(dpp->wdev_id, dpp->offchannel_id); + l_debug("starting offchannel on frequency %u, dwell=%u", freq, dpp->dwell); + dpp->offchannel_id = id; } @@ -797,23 +793,16 @@ static void dpp_handle_frame(struct dpp *dpp, const struct mmpdu_header *frame, const void *body, size_t body_len) { - const uint8_t *ptr; + if (!dpp->sm) + return; - /* - * Both handlers offset by 8 bytes to reach the beginning of the DPP - * attributes. Easier checking this in one place, which also covers the - * frame type byte. - */ - if (body_len < 8) + /* Frame from a different device after DPP has already started */ + if (dpp->peer_accepted && memcmp(dpp->peer_addr, frame->address_2, 6)) return; - ptr = body + 7; + memcpy(dpp->peer_addr, frame->address_2, 6); - switch (*ptr) { - default: - l_debug("Unhandled DPP frame %u", *ptr); - break; - } + dpp_handle_rx(dpp->sm, body + 1, body_len - 1); } static bool match_wdev(const void *a, const void *b) @@ -851,6 +840,7 @@ static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data) bool ack = false; struct iovec iov; uint8_t cmd = l_genl_msg_get_command(msg); + enum dpp_state state; if (cmd != NL80211_CMD_FRAME_TX_STATUS) return; @@ -863,16 +853,17 @@ static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data) return; dpp = l_queue_find(dpp_list, match_wdev, &wdev_id); - if (!dpp) + if (!dpp || !dpp->sm) return; + state = dpp_sm_get_state(dpp->sm); + /* * 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 && + if (state == DPP_STATE_PRESENCE || + (state == DPP_STATE_PKEX_EXCHANGE && dpp->role == DPP_CAPABILITY_ENROLLEE)) return; @@ -886,20 +877,6 @@ static void dpp_mlme_notify(struct l_genl_msg *msg, void *user_data) if (!ack) goto retransmit; - /* - * Special handling for a channel transition when acting as a - * configurator. The auth request was sent offchannel so we need to - * wait for the ACK before going back to the connected channel. - */ - if (dpp->channel_switch) { - if (dpp->offchannel_id) { - offchannel_cancel(dpp->wdev_id, dpp->offchannel_id); - dpp->offchannel_id = 0; - } - - dpp->channel_switch = false; - } - return; retransmit: @@ -1035,7 +1012,7 @@ static void dpp_station_state_watch(enum station_state state, void *user_data) { struct dpp *dpp = user_data; - if (dpp->state == DPP_STATE_NOTHING) + if (!dpp->sm) return; switch (state) { @@ -1089,17 +1066,20 @@ static void dpp_create(struct netdev *netdev) uint8_t dpp_conf_request_prefix[] = { 0x04, 0x0a }; uint64_t wdev_id = netdev_get_wdev_id(netdev); struct station *station = station_find(netdev_get_ifindex(netdev)); + const struct l_ecc_curve *curve = l_ecc_curve_from_ike_group(19); dpp->netdev = netdev; - dpp->state = DPP_STATE_NOTHING; dpp->interface = DPP_INTERFACE_UNBOUND; dpp->wdev_id = wdev_id; - dpp->max_roc = wiphy_get_max_roc_duration(wiphy_find_by_wdev(wdev_id)); dpp->mcast_support = wiphy_has_ext_feature( wiphy_find_by_wdev(dpp->wdev_id), NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS); + /* TODO: Support a user-provided key pair */ + l_ecdh_generate_key_pair(curve, &dpp->boot_private, + &dpp->boot_public); + l_dbus_object_add_interface(dbus, netdev_get_path(netdev), IWD_DPP_INTERFACE, dpp); l_dbus_object_add_interface(dbus, netdev_get_path(netdev), @@ -1212,6 +1192,175 @@ static void dpp_start_presence(struct dpp *dpp, uint32_t *limit_freqs, dpp_start_offchannel(dpp, dpp->current_freq); } +static void dpp_event_success(struct dpp *dpp) +{ + struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); + struct network *network = NULL; + struct scan_bss *bss = NULL; + const struct dpp_configuration *config; + + config = dpp_sm_get_configuration(dpp->sm); + if (L_WARN_ON(!config)) + goto reset; + + /* + * We should have a station device, but if not DPP can write the + * credentials out and be done + */ + if (station) { + network = station_network_find(station, config->ssid, + SECURITY_PSK); + if (network) + bss = network_bss_select(network, true); + } + + dpp_write_config(config, network); + + offchannel_cancel(dpp->wdev_id, dpp->offchannel_id); + dpp->offchannel_id = 0; + + if (network && bss) { + l_debug("delaying connect until settings are synced"); + return; + } else if (station) { + struct scan_parameters params = {0}; + + params.ssid = (void *) config->ssid; + params.ssid_len = config->ssid_len; + + l_debug("Scanning for %s", config->ssid); + + dpp->connect_scan_id = scan_active_full(dpp->wdev_id, ¶ms, + dpp_scan_triggered, + dpp_scan_results, dpp, + dpp_scan_destroy); + if (dpp->connect_scan_id) + return; + } + +reset: + dpp_reset(dpp); +} + +static void dpp_event_channel_switch(struct dpp *dpp, + const uint8_t *attr) +{ + uint32_t freq; + + freq = oci_to_frequency(attr[0], attr[1]); + + if (freq == dpp->current_freq) + return; + + dpp->current_freq = freq; + + dpp_start_offchannel(dpp, dpp->current_freq); + + /* + * The common code will attempt to write after this, so ensure that + * gets delayed until we are on the right channel + */ + dpp_sm_set_write_handler(dpp->sm, NULL); +} + +static void dpp_pkex_agent_reply(struct l_dbus_message *message, + void *user_data) +{ + struct dpp *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() returned %s(\"%s\")", + error, text); + return; + } + + if (!l_dbus_message_get_arguments(message, "s", &code)) { + l_debug("Invalid arguments, check SharedCodeAgent!"); + return; + } + + dpp_sm_set_pkex_key(dpp->sm, code); +} + +static bool dpp_event_pkex_key_requested(struct dpp *dpp, + const char *identifier) +{ + 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", identifier); + + 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_event_peer_accepted(struct dpp *dpp) +{ + dpp->peer_accepted = true; + + if (dpp_sm_get_state(dpp->sm) != DPP_STATE_PKEX_EXCHANGE) + return; + + dpp_sm_set_pkex_peer_mac(dpp->sm, dpp->peer_addr); + + /* + * PKEX dictates a 200ms timeout waiting for the exchange + * response. After this there is no time requirement for the + * remainder of the protocol. Increase the dwell time so we + * have the best chance of receiving frames since we will now + * remain on a single frequency. + */ + if (dpp->role == DPP_CAPABILITY_ENROLLEE) + dpp->dwell = (dpp->max_roc < 2000) ? dpp->max_roc : 2000; +} + +static void dpp_event(enum dpp_event event, const void *event_data, + void *user_data) +{ + struct dpp *dpp = user_data; + + switch (event) { + case DPP_EVENT_PEER_ACCEPTED: + dpp_event_peer_accepted(dpp); + break; + case DPP_EVENT_CHANNEL_SWITCH: + dpp_event_channel_switch(dpp, event_data); + break; + case DPP_EVENT_PKEX_KEY_REQUESTED: + dpp_event_pkex_key_requested(dpp, event_data); + break; + case DPP_EVENT_FAILED: + dpp_reset(dpp); + break; + case DPP_EVENT_SUCCESS: + if (dpp->role == DPP_CAPABILITY_ENROLLEE) + dpp_event_success(dpp); + else + dpp_reset(dpp); + break; + } +} + static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus, struct l_dbus_message *message, void *user_data) @@ -1219,9 +1368,11 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus, struct dpp *dpp = user_data; uint32_t freq = band_channel_to_freq(6, BAND_FREQ_2_4_GHZ); struct station *station = station_find(netdev_get_ifindex(dpp->netdev)); + struct l_dbus_message *reply; + const uint8_t *own_asn1; + size_t own_asn1_len; - if (dpp->state != DPP_STATE_NOTHING || - dpp->interface != DPP_INTERFACE_UNBOUND) + if (dpp->sm || dpp->interface != DPP_INTERFACE_UNBOUND) return dbus_error_busy(message); /* @@ -1234,14 +1385,23 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus, } else if (!station) l_debug("No station device, continuing anyways..."); - dpp->state = DPP_STATE_PRESENCE; + dpp->sm = dpp_sm_new(dpp_event, dpp->boot_public, + dpp->boot_private, dpp); + + dpp_sm_set_role(dpp->sm, DPP_CAPABILITY_ENROLLEE); + dpp_sm_start_responder(dpp->sm); + + own_asn1 = dpp_sm_get_own_asn1(dpp->sm, &own_asn1_len); + + dpp->uri = dpp_generate_uri(own_asn1, own_asn1_len, 2, + netdev_get_address(dpp->netdev), &freq, + 1, NULL, NULL); + dpp->role = DPP_CAPABILITY_ENROLLEE; dpp->interface = DPP_INTERFACE_DPP; l_debug("DPP Start Enrollee: %s", dpp->uri); - dpp->pending = l_dbus_message_ref(message); - /* * Going off spec here. Select a single channel to send presence * announcements on. This will be advertised in the URI. The full @@ -1251,7 +1411,13 @@ static struct l_dbus_message *dpp_dbus_start_enrollee(struct l_dbus *dbus, dpp_property_changed_notify(dpp); - return NULL; + memcpy(dpp->peer_addr, broadcast, 6); + + reply = l_dbus_message_new_method_return(message); + + l_dbus_message_set_arguments(reply, "s", dpp->uri); + + return reply; } /* @@ -1284,12 +1450,16 @@ static bool dpp_configurator_start_presence(struct dpp *dpp, const char *uri) return false; } - if (!l_memeqzero(info->mac, 6)) + if (!l_memeqzero(info->mac, 6)) { memcpy(dpp->peer_addr, info->mac, 6); + /* Set now so we restrict to only frames from this MAC */ + dpp->peer_accepted = true; + } if (info->freqs) freqs = scan_freq_set_to_fixed_array(info->freqs, &freqs_len); + dpp_sm_set_peer_bootstrap(dpp->sm, info->boot_public); dpp_free_uri_info(info); dpp_start_presence(dpp, freqs, freqs_len); @@ -1311,6 +1481,9 @@ static struct l_dbus_message *dpp_start_configurator_common( struct l_settings *settings; struct handshake_state *hs = netdev_get_handshake(dpp->netdev); const char *uri; + struct dpp_configuration *config; + const uint8_t *own_asn1; + size_t own_asn1_len; /* * For now limit the configurator to only configuring enrollees to the @@ -1331,11 +1504,11 @@ 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 || - dpp->interface != DPP_INTERFACE_UNBOUND) + if (dpp->sm || dpp->interface != DPP_INTERFACE_UNBOUND) return dbus_error_busy(message); - dpp->state = DPP_STATE_PRESENCE; + dpp->sm = dpp_sm_new(dpp_event, dpp->boot_public, + dpp->boot_private, dpp); if (!responder) { if (!l_dbus_message_get_arguments(message, "s", &uri)) @@ -1345,26 +1518,47 @@ static struct l_dbus_message *dpp_start_configurator_common( return dbus_error_invalid_args(message); if (!dpp->mcast_support) - dpp->state = DPP_STATE_AUTHENTICATING; - - dpp->new_freq = bss->frequency; + dpp_sm_set_skip_presence(dpp->sm, true); } else dpp->current_freq = bss->frequency; - dpp->role = DPP_CAPABILITY_CONFIGURATOR; - dpp->interface = DPP_INTERFACE_DPP; - dpp->config = dpp_configuration_new(settings, - network_get_ssid(network), + config = dpp_configuration_new(settings, network_get_ssid(network), hs->akm_suite); + dpp_sm_set_configuration(dpp->sm, config); + dpp_sm_set_role(dpp->sm, DPP_CAPABILITY_CONFIGURATOR); + + own_asn1 = dpp_sm_get_own_asn1(dpp->sm, &own_asn1_len); + + dpp->uri = dpp_generate_uri(own_asn1, own_asn1_len, 2, + netdev_get_address(dpp->netdev), + &bss->frequency, 1, NULL, NULL); + dpp->interface = DPP_INTERFACE_DPP; + + dpp->dwell = (dpp->max_roc < 2000) ? dpp->max_roc : 2000; + dpp_property_changed_notify(dpp); + dpp->role = DPP_CAPABILITY_CONFIGURATOR; + + memcpy(dpp->peer_addr, broadcast, 6); + l_debug("DPP Start Configurator: %s", dpp->uri); reply = l_dbus_message_new_method_return(message); l_dbus_message_set_arguments(reply, "s", dpp->uri); + /* + * Configurators acting as responders are always on channel so + * the writes can be done at any point. + */ + if (responder) { + dpp_sm_set_write_handler(dpp->sm, dpp_tx_frame); + dpp_sm_start_responder(dpp->sm); + } else + dpp_sm_start_initiator(dpp->sm); + return reply; } @@ -1480,10 +1674,12 @@ static bool dpp_pkex_scan_notify(int err, struct l_queue *bss_list, start: dpp->current_freq = dpp->freqs[0]; - l_debug("PKEX start enrollee (id=%s)", dpp->pkex_id ?: "unset"); + l_debug("PKEX start enrollee"); dpp_start_offchannel(dpp, dpp->current_freq); + dpp_sm_pkex_start_initiator(dpp->sm); + return false; failed: @@ -1503,14 +1699,20 @@ static bool dpp_start_pkex_enrollee(struct dpp *dpp, const char *key, { _auto_(l_ecc_point_free) struct l_ecc_point *qi = NULL; + dpp->sm = dpp_sm_new(dpp_event, dpp->boot_public, + dpp->boot_private, dpp); + if (identifier) - dpp->pkex_id = l_strdup(identifier); + dpp_sm_set_pkex_identifier(dpp->sm, identifier); + + dpp_sm_set_pkex_key(dpp->sm, key); + dpp_sm_set_pkex_own_mac(dpp->sm, netdev_get_address(dpp->netdev)); + dpp_sm_set_role(dpp->sm, DPP_CAPABILITY_ENROLLEE); - 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 @@ -1596,8 +1798,7 @@ static struct l_dbus_message *dpp_dbus_pkex_start_enrollee(struct l_dbus *dbus, l_debug(""); - if (dpp->state != DPP_STATE_NOTHING || - dpp->interface != DPP_INTERFACE_UNBOUND) + if (dpp->sm || dpp->interface != DPP_INTERFACE_UNBOUND) return dbus_error_busy(message); if (station && station_get_connected_network(station)) @@ -1651,9 +1852,9 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp *dpp, struct network *network = station_get_connected_network(station); struct scan_bss *bss = station_get_connected_bss(station); const struct l_settings *settings; + struct dpp_configuration *config; - if (dpp->state != DPP_STATE_NOTHING || - dpp->interface != DPP_INTERFACE_UNBOUND) + if (dpp->sm || dpp->interface != DPP_INTERFACE_UNBOUND) return dbus_error_busy(message); if (!dpp->mcast_support) { @@ -1671,30 +1872,39 @@ static struct l_dbus_message *dpp_start_pkex_configurator(struct dpp *dpp, return dbus_error_not_configured(message); } + dpp->sm = dpp_sm_new(dpp_event, dpp->boot_public, + dpp->boot_private, dpp); + if (identifier) - dpp->pkex_id = l_strdup(identifier); + dpp_sm_set_pkex_identifier(dpp->sm, identifier); if (key) - dpp->pkex_key = l_strdup(key); + dpp_sm_set_pkex_key(dpp->sm, 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; dpp->current_freq = bss->frequency; - dpp->config = dpp_configuration_new(network_get_settings(network), + config = dpp_configuration_new(network_get_settings(network), network_get_ssid(network), hs->akm_suite); + dpp_sm_set_role(dpp->sm, DPP_CAPABILITY_CONFIGURATOR); + dpp_sm_set_configuration(dpp->sm, config); + dpp_sm_set_pkex_own_mac(dpp->sm, netdev_get_address(dpp->netdev)); + dpp_sm_set_write_handler(dpp->sm, dpp_tx_frame); + dpp_property_changed_notify(dpp); - if (dpp->pkex_key) + if (key) l_debug("Starting PKEX configurator for single enrollee"); else l_debug("Starting PKEX configurator with agent"); + dpp_sm_pkex_start_responder(dpp->sm); + return l_dbus_message_new_method_return(message); }