diff --git a/src/common/dpp.c b/src/common/dpp.c new file mode 100644 index 000000000..dbb3551ee --- /dev/null +++ b/src/common/dpp.c @@ -0,0 +1,632 @@ +/* + * 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 + +#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; +} diff --git a/src/common/dpp.h b/src/common/dpp.h new file mode 100644 index 000000000..22c348dd4 --- /dev/null +++ b/src/common/dpp.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef DPP_H +#define DPP_H + +#include + +#include "utils/list.h" +#include "crypto/sha256.h" + +#define DPP_BOOTSTRAP_MAX_FREQ 30 + +struct dpp_curve_params { + const char *name; + size_t hash_len; + size_t aes_siv_key_len; + size_t nonce_len; + size_t prime_len; + const char *jwk_crv; +}; + +enum dpp_bootstrap_type { + DPP_BOOTSTRAP_QR_CODE, +}; + +struct dpp_bootstrap_info { + struct dl_list list; + unsigned int id; + enum dpp_bootstrap_type type; + char *uri; + u8 mac_addr[ETH_ALEN]; + char *info; + unsigned int freq[DPP_BOOTSTRAP_MAX_FREQ]; + unsigned int num_freq; + int own; + EVP_PKEY *pubkey; + u8 pubkey_hash[SHA256_MAC_LEN]; + const struct dpp_curve_params *curve; +}; + +void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info); +int dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, + const char *chan_list); +int dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac); +int dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info); +struct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri); +char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve, + const u8 *privkey, size_t privkey_len); + +#endif /* DPP_H */ diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 6af5bcf83..7a641756b 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -243,6 +243,12 @@ NEED_ECC=y NEED_DH_GROUPS=y endif +ifdef CONFIG_DPP +L_CFLAGS += -DCONFIG_DPP +OBJS += src/common/dpp.c +OBJS += dpp_supplicant.c +endif + ifdef CONFIG_OWE L_CFLAGS += -DCONFIG_OWE NEED_ECC=y diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index f3f9e1f8f..a28340c92 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -276,6 +276,12 @@ NEED_ECC=y NEED_DH_GROUPS=y endif +ifdef CONFIG_DPP +CFLAGS += -DCONFIG_DPP +OBJS += ../src/common/dpp.o +OBJS += dpp_supplicant.o +endif + ifdef CONFIG_OWE CFLAGS += -DCONFIG_OWE NEED_ECC=y diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 10a23ca7d..d3bd0cbff 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -52,6 +52,7 @@ #include "offchannel.h" #include "drivers/driver.h" #include "mesh.h" +#include "dpp_supplicant.h" static int wpa_supplicant_global_iface_list(struct wpa_global *global, char *buf, int len); @@ -10149,6 +10150,44 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strcmp(buf, "FILS_HLP_REQ_FLUSH") == 0) { wpas_flush_fils_hlp_req(wpa_s); #endif /* CONFIG_FILS */ +#ifdef CONFIG_DPP + } else if (os_strncmp(buf, "DPP_QR_CODE ", 12) == 0) { + int res; + + res = wpas_dpp_qr_code(wpa_s, buf + 12); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GEN ", 18) == 0) { + int res; + + res = wpas_dpp_bootstrap_gen(wpa_s, buf + 18); + if (res < 0) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%d", res); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_REMOVE ", 21) == 0) { + if (wpas_dpp_bootstrap_remove(wpa_s, buf + 21) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_BOOTSTRAP_GET_URI ", 22) == 0) { + const char *uri; + + uri = wpas_dpp_bootstrap_get_uri(wpa_s, atoi(buf + 22)); + if (!uri) { + reply_len = -1; + } else { + reply_len = os_snprintf(reply, reply_size, "%s", uri); + if (os_snprintf_error(reply_size, reply_len)) + reply_len = -1; + } +#endif /* CONFIG_DPP */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c new file mode 100644 index 000000000..5a2bfd635 --- /dev/null +++ b/wpa_supplicant/dpp_supplicant.c @@ -0,0 +1,231 @@ +/* + * 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); +} diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h new file mode 100644 index 000000000..9ea888ce0 --- /dev/null +++ b/wpa_supplicant/dpp_supplicant.h @@ -0,0 +1,20 @@ +/* + * 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. + */ + +#ifndef DPP_SUPPLICANT_H +#define DPP_SUPPLICANT_H + +int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_dpp_bootstrap_gen(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_dpp_bootstrap_remove(struct wpa_supplicant *wpa_s, const char *id); +const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s, + unsigned int id); +int wpas_dpp_init(struct wpa_supplicant *wpa_s); +void wpas_dpp_deinit(struct wpa_supplicant *wpa_s); + +#endif /* DPP_SUPPLICANT_H */ diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 6c5a32048..f0bf35f00 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -59,6 +59,7 @@ #include "wnm_sta.h" #include "wpas_kay.h" #include "mesh.h" +#include "dpp_supplicant.h" const char *const wpa_supplicant_version = "wpa_supplicant v" VERSION_STR "\n" @@ -624,6 +625,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpabuf_free(wpa_s->ric_ies); wpa_s->ric_ies = NULL; + +#ifdef CONFIG_DPP + wpas_dpp_deinit(wpa_s); +#endif /* CONFIG_DPP */ } @@ -5178,6 +5183,11 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s, if (wpas_wps_init(wpa_s)) return -1; +#ifdef CONFIG_DPP + if (wpas_dpp_init(wpa_s) < 0) + return -1; +#endif /* CONFIG_DPP */ + if (wpa_supplicant_init_eapol(wpa_s) < 0) return -1; wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 9c0500191..504439298 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1156,6 +1156,11 @@ struct wpa_supplicant { /* RIC elements for FT protocol */ struct wpabuf *ric_ies; + +#ifdef CONFIG_DPP + struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ + int dpp_init_done; +#endif /* CONFIG_DPP */ };