DPP: Bootstrap information management
Add wpa_supplicant control interface commands for parsing the bootstrap info URI from a QR Code (get peer public key) and to generate a new bootstrap info with private key for local use. The optional key=<hexdump> argument to the DPP_BOOTSTRAP_GEN command can be used to specify the bootstrapping private key in OpenSSL ECPrivateKey DER encoding format. This results in the local bootstrapping information entry being created with the specified key instead of generating a new random one. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
d4d76d9835
commit
be27e185b7
9 changed files with 1005 additions and 0 deletions
632
src/common/dpp.c
Normal file
632
src/common/dpp.c
Normal file
|
@ -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 <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;
|
||||
}
|
56
src/common/dpp.h
Normal file
56
src/common/dpp.h
Normal file
|
@ -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 <openssl/x509.h>
|
||||
|
||||
#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 */
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
231
wpa_supplicant/dpp_supplicant.c
Normal file
231
wpa_supplicant/dpp_supplicant.c
Normal file
|
@ -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);
|
||||
}
|
20
wpa_supplicant/dpp_supplicant.h
Normal file
20
wpa_supplicant/dpp_supplicant.h
Normal file
|
@ -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 */
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue