--- /dev/null
+/*
+ * DPP functionality shared between hostapd and wpa_supplicant
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/crypto.h"
+#include "dpp.h"
+
+
+static const struct dpp_curve_params dpp_curves[] = {
+ /* The mandatory to support and the default NIST P-256 curve needs to
+ * be the first entry on this list. */
+ { "prime256v1", 32, 32, 16, 32, "P-256" },
+ { "secp384r1", 48, 48, 24, 48, "P-384" },
+ { "secp521r1", 64, 64, 32, 66, "P-521" },
+ { "brainpoolP256r1", 32, 32, 16, 32, "BP-256R1" },
+ { "brainpoolP384r1", 48, 48, 24, 48, "BP-384R1" },
+ { "brainpoolP512r1", 64, 64, 32, 64, "BP-512R1" },
+ { NULL, 0, 0, 0, 0, NULL }
+};
+
+
+void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info)
+{
+ if (!info)
+ return;
+ os_free(info->uri);
+ os_free(info->info);
+ EVP_PKEY_free(info->pubkey);
+ os_free(info);
+}
+
+
+static int dpp_uri_valid_info(const char *info)
+{
+ while (*info) {
+ unsigned char val = *info++;
+
+ if (val < 0x20 || val > 0x7e || val == 0x3b)
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri)
+{
+ bi->uri = os_strdup(uri);
+ return bi->uri ? 0 : -1;
+}
+
+
+int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi,
+ const char *chan_list)
+{
+ const char *pos = chan_list;
+ int opclass, channel, freq;
+
+ while (pos && *pos && *pos != ';') {
+ opclass = atoi(pos);
+ if (opclass <= 0)
+ goto fail;
+ pos = os_strchr(pos, '/');
+ if (!pos)
+ goto fail;
+ pos++;
+ channel = atoi(pos);
+ if (channel <= 0)
+ goto fail;
+ while (*pos >= '0' && *pos <= '9')
+ pos++;
+ freq = ieee80211_chan_to_freq(NULL, opclass, channel);
+ wpa_printf(MSG_DEBUG,
+ "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d",
+ opclass, channel, freq);
+ if (freq < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)",
+ opclass, channel);
+ } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Too many channels in URI channel-list - ignore list");
+ bi->num_freq = 0;
+ break;
+ } else {
+ bi->freq[bi->num_freq++] = freq;
+ }
+
+ if (*pos == ';' || *pos == '\0')
+ break;
+ if (*pos != ',')
+ goto fail;
+ pos++;
+ }
+
+ return 0;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list");
+ return -1;
+}
+
+
+int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac)
+{
+ if (!mac)
+ return 0;
+
+ if (hwaddr_aton2(mac, bi->mac_addr) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr));
+
+ return 0;
+}
+
+
+int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info)
+{
+ const char *end;
+
+ if (!info)
+ return 0;
+
+ end = os_strchr(info, ';');
+ if (!end)
+ end = info + os_strlen(info);
+ bi->info = os_malloc(end - info + 1);
+ if (!bi->info)
+ return -1;
+ os_memcpy(bi->info, info, end - info);
+ bi->info[end - info] = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info);
+ if (!dpp_uri_valid_info(bi->info)) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_oid(const ASN1_OBJECT *poid)
+{
+ ASN1_OBJECT *oid;
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ oid = OBJ_txt2obj(dpp_curves[i].name, 0);
+ if (oid && OBJ_cmp(poid, oid) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static const struct dpp_curve_params * dpp_get_curve_nid(int nid)
+{
+ int i, tmp;
+
+ if (!nid)
+ return NULL;
+ for (i = 0; dpp_curves[i].name; i++) {
+ tmp = OBJ_txt2nid(dpp_curves[i].name);
+ if (tmp == nid)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
+{
+ const char *end;
+ u8 *data;
+ size_t data_len;
+ EVP_PKEY *pkey;
+ const unsigned char *p;
+ int res;
+ X509_PUBKEY *pub = NULL;
+ ASN1_OBJECT *ppkalg;
+ const unsigned char *pk;
+ int ppklen;
+ X509_ALGOR *pa;
+ ASN1_OBJECT *pa_oid;
+ const void *pval;
+ int ptype;
+ const ASN1_OBJECT *poid;
+ char buf[100];
+
+ end = os_strchr(info, ';');
+ if (!end)
+ return -1;
+
+ data = base64_decode((const unsigned char *) info, end - info,
+ &data_len);
+ if (!data) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Invalid base64 encoding on URI public-key");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key",
+ data, data_len);
+
+ if (sha256_vector(1, (const u8 **) &data, &data_len,
+ bi->pubkey_hash) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ return -1;
+ }
+
+ /* DER encoded ASN.1 SubjectPublicKeyInfo
+ *
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ *
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ *
+ * subjectPublicKey = compressed format public key per ANSI X9.63
+ * algorithm = ecPublicKey (1.2.840.10045.2.1)
+ * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
+ * prime256v1 (1.2.840.10045.3.1.7)
+ */
+
+ p = data;
+ pkey = d2i_PUBKEY(NULL, &p, data_len);
+ os_free(data);
+
+ if (!pkey) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
+ return -1;
+ }
+
+ if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo does not describe an EC key");
+ EVP_PKEY_free(pkey);
+ return -1;
+ }
+
+ res = X509_PUBKEY_set(&pub, pkey);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
+ goto fail;
+ }
+
+ res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters");
+ goto fail;
+ }
+ res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
+ if (os_strcmp(buf, "id-ecPublicKey") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+
+ X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
+ if (ptype != V_ASN1_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
+ goto fail;
+ }
+ poid = pval;
+ res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
+ bi->curve = dpp_get_curve_oid(poid);
+ if (!bi->curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
+ buf);
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
+
+ X509_PUBKEY_free(pub);
+ bi->pubkey = pkey;
+ return 0;
+fail:
+ X509_PUBKEY_free(pub);
+ EVP_PKEY_free(pkey);
+ return -1;
+}
+
+
+static struct dpp_bootstrap_info * dpp_parse_uri(const char *uri)
+{
+ const char *pos = uri;
+ const char *end;
+ const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL;
+ struct dpp_bootstrap_info *bi;
+
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri));
+
+ if (os_strncmp(pos, "DPP:", 4) != 0) {
+ wpa_printf(MSG_INFO, "DPP: Not a DPP URI");
+ return NULL;
+ }
+ pos += 4;
+
+ for (;;) {
+ end = os_strchr(pos, ';');
+ if (!end)
+ break;
+
+ if (end == pos) {
+ /* Handle terminating ";;" and ignore unexpected ";"
+ * for parsing robustness. */
+ pos++;
+ continue;
+ }
+
+ if (pos[0] == 'C' && pos[1] == ':' && !chan_list)
+ chan_list = pos + 2;
+ else if (pos[0] == 'M' && pos[1] == ':' && !mac)
+ mac = pos + 2;
+ else if (pos[0] == 'I' && pos[1] == ':' && !info)
+ info = pos + 2;
+ else if (pos[0] == 'K' && pos[1] == ':' && !pk)
+ pk = pos + 2;
+ else
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: Ignore unrecognized URI parameter",
+ pos, end - pos);
+ pos = end + 1;
+ }
+
+ if (!pk) {
+ wpa_printf(MSG_INFO, "DPP: URI missing public-key");
+ return NULL;
+ }
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return NULL;
+
+ if (dpp_clone_uri(bi, uri) < 0 ||
+ dpp_parse_uri_chan_list(bi, chan_list) < 0 ||
+ dpp_parse_uri_mac(bi, mac) < 0 ||
+ dpp_parse_uri_info(bi, info) < 0 ||
+ dpp_parse_uri_pk(bi, pk) < 0) {
+ dpp_bootstrap_info_free(bi);
+ bi = NULL;
+ }
+
+ return bi;
+}
+
+
+struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = dpp_parse_uri(uri);
+ if (bi)
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ return bi;
+}
+
+
+static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
+{
+ EC_KEY *eckey;
+ BIO *out;
+ size_t rlen;
+ char *txt;
+ int res;
+ unsigned char *der = NULL;
+ int der_len;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return;
+
+ EVP_PKEY_print_private(out, key, 0, NULL);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!eckey)
+ return;
+
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
+ OPENSSL_free(der);
+ if (der_len <= 0) {
+ der = NULL;
+ der_len = i2d_EC_PUBKEY(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
+ OPENSSL_free(der);
+ }
+
+ EC_KEY_free(eckey);
+}
+
+
+static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+ EVP_PKEY_CTX *kctx = NULL;
+ const EC_GROUP *group;
+ EC_KEY *ec_params;
+#else
+ EVP_PKEY_CTX *pctx, *kctx = NULL;
+#endif
+ EVP_PKEY *params = NULL, *key = NULL;
+ int nid;
+
+ wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+
+ nid = OBJ_txt2nid(curve->name);
+ if (nid == NID_undef) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
+ return NULL;
+ }
+#ifdef OPENSSL_IS_BORINGSSL
+ group = EC_GROUP_new_by_curve_name(nid);
+ ec_params = EC_KEY_new();
+ if (!ec_params || EC_KEY_set_group(ec_params, group) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EC_KEY parameters");
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+ params = EVP_PKEY_new();
+ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EVP_PKEY parameters");
+ goto fail;
+ }
+#else
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+ if (!pctx ||
+ EVP_PKEY_paramgen_init(pctx) != 1 ||
+ EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) != 1 ||
+ EVP_PKEY_CTX_set_ec_param_enc(pctx, OPENSSL_EC_NAMED_CURVE) != 1 ||
+ EVP_PKEY_paramgen(pctx, ¶ms) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EVP_PKEY parameters");
+ EVP_PKEY_CTX_free(pctx);
+ goto fail;
+ }
+ EVP_PKEY_CTX_free(pctx);
+#endif
+
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (!kctx ||
+ EVP_PKEY_keygen_init(kctx) != 1 ||
+ EVP_PKEY_keygen(kctx, &key) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
+ goto fail;
+ }
+
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("Own generated key", key);
+
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(kctx);
+ return key;
+fail:
+ EVP_PKEY_CTX_free(kctx);
+ EVP_PKEY_free(params);
+ return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_name(const char *name)
+{
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (os_strcmp(name, dpp_curves[i].name) == 0 ||
+ (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ EVP_PKEY *pkey;
+ EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ pkey = EVP_PKEY_new();
+ if (!pkey)
+ return NULL;
+ eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ group = EC_KEY_get0_group(eckey);
+ if (!group) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ nid = EC_GROUP_get_curve_name(group);
+ *curve = dpp_get_curve_nid(nid);
+ if (!*curve) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported curve (nid=%d) in pre-assigned key",
+ nid);
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+
+ if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ return pkey;
+}
+
+
+char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ unsigned char *base64 = NULL;
+ char *pos, *end;
+ size_t len;
+ unsigned char *der = NULL;
+ int der_len;
+ EC_KEY *eckey;
+
+ if (!curve) {
+ bi->curve = &dpp_curves[0];
+ } else {
+ bi->curve = dpp_get_curve_name(curve);
+ if (!bi->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s",
+ curve);
+ return NULL;
+ }
+ }
+ if (privkey)
+ bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
+ else
+ bi->pubkey = dpp_gen_keypair(bi->curve);
+ if (!bi->pubkey)
+ goto fail;
+ bi->own = 1;
+
+ /* Need to get the compressed form of the public key through EC_KEY, so
+ * cannot use the simpler i2d_PUBKEY() here. */
+ eckey = EVP_PKEY_get1_EC_KEY(bi->pubkey);
+ if (!eckey)
+ goto fail;
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
+ der_len = i2d_EC_PUBKEY(eckey, &der);
+ EC_KEY_free(eckey);
+ if (der_len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to build DER encoded public key");
+ goto fail;
+ }
+
+ len = der_len;
+ if (sha256_vector(1, (const u8 **) &der, &len, bi->pubkey_hash) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ goto fail;
+ }
+
+ base64 = base64_encode(der, der_len, &len);
+ OPENSSL_free(der);
+ if (!base64)
+ goto fail;
+ pos = (char *) base64;
+ end = pos + len;
+ for (;;) {
+ pos = os_strchr(pos, '\n');
+ if (!pos)
+ break;
+ os_memmove(pos, pos + 1, end - pos);
+ }
+ return (char *) base64;
+fail:
+ os_free(base64);
+ OPENSSL_free(der);
+ return NULL;
+}
--- /dev/null
+/*
+ * wpa_supplicant - DPP
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/dpp.h"
+#include "wpa_supplicant_i.h"
+#include "dpp_supplicant.h"
+
+
+static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s)
+{
+ struct dpp_bootstrap_info *bi;
+ unsigned int max_id = 0;
+
+ dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+ list) {
+ if (bi->id > max_id)
+ max_id = bi->id;
+ }
+ return max_id + 1;
+}
+
+
+/**
+ * wpas_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @cmd: DPP URI read from a QR Code
+ * Returns: Identifier of the stored info or -1 on failure
+ */
+int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = dpp_parse_qr_code(cmd);
+ if (!bi)
+ return -1;
+
+ bi->id = wpas_dpp_next_id(wpa_s);
+ dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+
+ return bi->id;
+}
+
+
+static char * get_param(const char *cmd, const char *param)
+{
+ const char *pos, *end;
+ char *val;
+ size_t len;
+
+ pos = os_strstr(cmd, param);
+ if (!pos)
+ return NULL;
+
+ pos += os_strlen(param);
+ end = os_strchr(pos, ' ');
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ val = os_malloc(len + 1);
+ if (!val)
+ return NULL;
+ os_memcpy(val, pos, len);
+ val[len] = '\0';
+ return val;
+}
+
+
+int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+ char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL;
+ char *key = NULL;
+ u8 *privkey = NULL;
+ size_t privkey_len = 0;
+ size_t len;
+ int ret = -1;
+ struct dpp_bootstrap_info *bi;
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ goto fail;
+
+ if (os_strstr(cmd, "type=qrcode"))
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ else
+ goto fail;
+
+ chan = get_param(cmd, " chan=");
+ mac = get_param(cmd, " mac=");
+ info = get_param(cmd, " info=");
+ curve = get_param(cmd, " curve=");
+ key = get_param(cmd, " key=");
+
+ if (key) {
+ privkey_len = os_strlen(key) / 2;
+ privkey = os_malloc(privkey_len);
+ if (!privkey ||
+ hexstr2bin(key, privkey, privkey_len) < 0)
+ goto fail;
+ }
+
+ pk = dpp_keygen(bi, curve, privkey, privkey_len);
+ if (!pk)
+ goto fail;
+
+ len = 4; /* "DPP:" */
+ if (chan) {
+ if (dpp_parse_uri_chan_list(bi, chan) < 0)
+ goto fail;
+ len += 3 + os_strlen(chan); /* C:...; */
+ }
+ if (mac) {
+ if (dpp_parse_uri_mac(bi, mac) < 0)
+ goto fail;
+ len += 3 + os_strlen(mac); /* M:...; */
+ }
+ if (info) {
+ if (dpp_parse_uri_info(bi, info) < 0)
+ goto fail;
+ len += 3 + os_strlen(info); /* I:...; */
+ }
+ len += 4 + os_strlen(pk);
+ bi->uri = os_malloc(len + 1);
+ if (!bi->uri)
+ goto fail;
+ os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
+ chan ? "C:" : "", chan ? chan : "", chan ? ";" : "",
+ mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
+ info ? "I:" : "", info ? info : "", info ? ";" : "",
+ pk);
+ bi->id = wpas_dpp_next_id(wpa_s);
+ dl_list_add(&wpa_s->dpp_bootstrap, &bi->list);
+ ret = bi->id;
+ bi = NULL;
+fail:
+ os_free(curve);
+ os_free(pk);
+ os_free(chan);
+ os_free(mac);
+ os_free(info);
+ str_clear_free(key);
+ bin_clear_free(privkey, privkey_len);
+ dpp_bootstrap_info_free(bi);
+ return ret;
+}
+
+
+static struct dpp_bootstrap_info *
+dpp_bootstrap_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+ struct dpp_bootstrap_info *bi;
+
+ dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info,
+ list) {
+ if (bi->id == id)
+ return bi;
+ }
+ return NULL;
+}
+
+
+static int dpp_bootstrap_del(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+ struct dpp_bootstrap_info *bi, *tmp;
+ int found = 0;
+
+ dl_list_for_each_safe(bi, tmp, &wpa_s->dpp_bootstrap,
+ struct dpp_bootstrap_info, list) {
+ if (id && bi->id != id)
+ continue;
+ found = 1;
+ dl_list_del(&bi->list);
+ dpp_bootstrap_info_free(bi);
+ }
+
+ if (id == 0)
+ return 0; /* flush succeeds regardless of entries found */
+ return found ? 0 : -1;
+}
+
+
+int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id)
+{
+ unsigned int id_val;
+
+ if (os_strcmp(id, "*") == 0) {
+ id_val = 0;
+ } else {
+ id_val = atoi(id);
+ if (id_val == 0)
+ return -1;
+ }
+
+ return dpp_bootstrap_del(wpa_s, id_val);
+}
+
+
+const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s,
+ unsigned int id)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = dpp_bootstrap_get_id(wpa_s, id);
+ if (!bi)
+ return NULL;
+ return bi->uri;
+}
+
+
+int wpas_dpp_init(struct wpa_supplicant *wpa_s)
+{
+ dl_list_init(&wpa_s->dpp_bootstrap);
+ wpa_s->dpp_init_done = 1;
+ return 0;
+}
+
+
+void wpas_dpp_deinit(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->dpp_init_done)
+ return;
+ dpp_bootstrap_del(wpa_s, 0);
+}