From: James Prestwood <prestwoj at gmail.com>
To: iwd at lists.01.org
Subject: [PATCH v2 02/10] dpp-util: add URI parsing
Date: Thu, 20 Jan 2022 15:44:07 -0800 [thread overview]
Message-ID: <20220120234415.2469451-2-prestwoj@gmail.com> (raw)
In-Reply-To: 20220120234415.2469451-1-prestwoj@gmail.com
[-- Attachment #1: Type: text/plain, Size: 6687 bytes --]
Parses K (key), M (mac), C (class/channels), and V (version) tokens
into a new structure dpp_uri_info. H/I are not parsed since there
currently isn't any use for them.
---
src/dpp-util.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++++
src/dpp-util.h | 13 +++
2 files changed, 230 insertions(+)
diff --git a/src/dpp-util.c b/src/dpp-util.c
index 4a5bd046..6add2ded 100644
--- a/src/dpp-util.c
+++ b/src/dpp-util.c
@@ -865,3 +865,220 @@ struct l_ecc_point *dpp_point_from_asn1(const uint8_t *asn1, size_t len)
return l_ecc_point_from_data(curve, key_data[1],
key_data + 2, elen - 2);
}
+
+/*
+ * Advances 'p' to the next character 'sep' plus one. strchr can be trusted to
+ * find the next character, but we do need to check that the next character + 1
+ * isn't the NULL terminator, i.e. that data actually exists past this point.
+ */
+#define TOKEN_NEXT(p, sep) \
+({ \
+ const char *_next = strchr((p), (sep)); \
+ if (_next) { \
+ if (*(_next + 1) == '\0') \
+ _next = NULL; \
+ else \
+ _next++; \
+ } \
+ _next; \
+})
+
+/*
+ * Finds the length of the current token (characters until next 'sep'). If no
+ * 'sep' is found zero is returned.
+ */
+#define TOKEN_LEN(p, sep) \
+({ \
+ const char *_next = strchr((p), (sep)); \
+ if (!_next) \
+ _next = (p); \
+ (_next - (p)); \
+})
+
+/*
+ * Ensures 'p' points to something resembling a single character followed by
+ * ':' followed by at least one non-null byte of data. This allows the parse
+ * loop to safely advance the pointer to each tokens data (pos + 2)
+ */
+#define TOKEN_OK(p) \
+ ((p) && (p)[0] != '\0' && (p)[1] == ':' && (p)[2] != '\0') \
+
+static struct scan_freq_set *dpp_parse_class_and_channel(const char *token)
+{
+ const char *pos = token;
+ char *end;
+ struct scan_freq_set *freqs = scan_freq_set_new();
+
+ /* Checking for <operclass>/<channel>,<operclass>/<channel>,... */
+ for (; pos; pos = TOKEN_NEXT(pos, ',')) {
+ uint8_t channel;
+ uint8_t oper_class = strtol(pos, &end, 10);
+
+ if (!end || end == pos || (end && *end != '/'))
+ goto free_set;
+
+ pos = end + 1;
+ channel = strtol(pos, &end, 10);
+
+ /* Either another pair (,) or end of this token (;) */
+ if (!end || end == pos || (*end != ',' && *end != ';'))
+ goto free_set;
+
+ scan_freq_set_add(freqs, oci_to_frequency(oper_class, channel));
+ }
+
+ if (scan_freq_set_isempty(freqs)) {
+free_set:
+ scan_freq_set_free(freqs);
+ return NULL;
+ }
+
+ return freqs;
+}
+
+static int dpp_parse_mac(const char *str, uint8_t *mac_out)
+{
+ uint8_t mac[6];
+ unsigned int i;
+
+ for (i = 0; i < 12; i += 2) {
+ if (!l_ascii_isxdigit(str[i]))
+ return -EINVAL;
+
+ if (!l_ascii_isxdigit(str[i + 1]))
+ return -EINVAL;
+ }
+
+ if (sscanf(str, "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
+ &mac[0], &mac[1], &mac[2],
+ &mac[3], &mac[4], &mac[5]) != 6)
+ return -EINVAL;
+
+ if (!util_is_valid_sta_address(mac))
+ return -EINVAL;
+
+ memcpy(mac_out, mac, 6);
+
+ return 0;
+}
+
+static int dpp_parse_version(const char *str, uint8_t *version_out)
+{
+ char *end;
+ uint8_t version;
+ size_t len = TOKEN_LEN(str, ';');
+
+ if (len != 1)
+ return -EINVAL;
+
+ version = strtol(str, &end, 10);
+
+ if (version != 1 && version != 2)
+ return -EINVAL;
+
+ *version_out = version;
+
+ return 0;
+}
+
+static struct l_ecc_point *dpp_parse_key(const char *str)
+{
+ _auto_(l_free) uint8_t *decoded = NULL;
+ size_t decoded_len;
+ size_t len = TOKEN_LEN(str, ';');
+
+ if (!len)
+ return NULL;
+
+ decoded = l_base64_decode(str, len, &decoded_len);
+ if (!decoded)
+ return NULL;
+
+ return dpp_point_from_asn1(decoded, decoded_len);
+}
+
+/*
+ * Parse a bootstrapping URI. This parses the tokens defined in the Easy Connect
+ * spec, and verifies they are the correct syntax. Some values have extra
+ * verification:
+ * - The bootstrapping key is base64 decoded and converted to an l_ecc_point
+ * - The operating class and channels are checked against the OCI table.
+ * - The version is checked to be either 1 or 2, as defined by the spec.
+ * - The MAC is verified to be a valid station address.
+ */
+struct dpp_uri_info *dpp_parse_uri(const char *uri)
+{
+ struct dpp_uri_info *info;
+ const char *pos = uri;
+ const char *end = uri + strlen(uri) - 1;
+ int ret = 0;
+
+ if (strncmp(pos, "DPP:", 4))
+ return NULL;
+
+ info = l_new(struct dpp_uri_info, 1);
+
+ pos += 4;
+
+ /* EasyConnect 5.2.1 - Bootstrapping information format */
+ for (; TOKEN_OK(pos); pos = TOKEN_NEXT(pos, ';')) {
+ switch (*pos) {
+ case 'C':
+ info->freqs = dpp_parse_class_and_channel(pos + 2);
+ if (!info->freqs)
+ goto free_info;
+ break;
+ case 'M':
+ ret = dpp_parse_mac(pos + 2, info->mac);
+ if (ret < 0)
+ goto free_info;
+ break;
+ case 'V':
+ ret = dpp_parse_version(pos + 2, &info->version);
+ if (ret < 0)
+ goto free_info;
+ break;
+ case 'K':
+ info->boot_public = dpp_parse_key(pos + 2);
+ if (!info->boot_public)
+ goto free_info;
+ break;
+ case 'H':
+ case 'I':
+ break;
+ default:
+ goto free_info;
+ }
+ }
+
+ /* Extra data found after last token */
+ if (pos != end)
+ goto free_info;
+
+ /* The public bootstrapping key is the only required token */
+ if (!info->boot_public)
+ goto free_info;
+
+ return info;
+
+free_info:
+ dpp_free_uri_info(info);
+ return NULL;
+}
+
+void dpp_free_uri_info(struct dpp_uri_info *info)
+{
+ if (info->freqs)
+ scan_freq_set_free(info->freqs);
+
+ if (info->boot_public)
+ l_ecc_point_free(info->boot_public);
+
+ if (info->information)
+ l_free(info->information);
+
+ if (info->host)
+ l_free(info->host);
+
+ l_free(info);
+}
diff --git a/src/dpp-util.h b/src/dpp-util.h
index 82535ff8..a3ddd452 100644
--- a/src/dpp-util.h
+++ b/src/dpp-util.h
@@ -22,6 +22,16 @@
struct l_ecc_point;
struct l_ecc_scalar;
enum ie_rsn_akm_suite;
+struct scan_freq_set;
+
+struct dpp_uri_info {
+ struct scan_freq_set *freqs;
+ struct l_ecc_point *boot_public;
+ uint8_t mac[6];
+ char *information;
+ uint8_t version;
+ char *host;
+};
enum dpp_frame_type {
DPP_FRAME_AUTHENTICATION_REQUEST = 0,
@@ -168,3 +178,6 @@ bool dpp_derive_ke(const uint8_t *i_nonce, const uint8_t *r_nonce,
uint8_t *dpp_point_to_asn1(const struct l_ecc_point *p, size_t *len_out);
struct l_ecc_point *dpp_point_from_asn1(const uint8_t *asn1, size_t len);
+
+struct dpp_uri_info *dpp_parse_uri(const char *uri);
+void dpp_free_uri_info(struct dpp_uri_info *info);
--
2.31.1
reply other threads:[~2022-01-20 23:44 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220120234415.2469451-2-prestwoj@gmail.com \
--to=iwd@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox