From 30d27b048ea257d6fea5709021b2b3cf9a1e1849 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 15 Jun 2017 21:18:12 +0300 Subject: [PATCH] DPP: Authentication exchange Add wpa_supplicant control interface commands for managing DPP Authentication exchange. Signed-off-by: Jouni Malinen --- src/common/dpp.c | 1894 +++++++++++++++++++++++++++++ src/common/dpp.h | 110 ++ src/common/wpa_ctrl.h | 6 + wpa_supplicant/Android.mk | 7 + wpa_supplicant/Makefile | 7 + wpa_supplicant/ctrl_iface.c | 12 + wpa_supplicant/dpp_supplicant.c | 588 +++++++++ wpa_supplicant/dpp_supplicant.h | 9 + wpa_supplicant/events.c | 18 + wpa_supplicant/wpa_supplicant_i.h | 6 + 10 files changed, 2657 insertions(+) diff --git a/src/common/dpp.c b/src/common/dpp.c index dbb3551ee..2ec4cb63b 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -12,7 +12,14 @@ #include "utils/common.h" #include "utils/base64.h" #include "common/ieee802_11_common.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" #include "crypto/crypto.h" +#include "crypto/random.h" +#include "crypto/aes.h" +#include "crypto/aes_siv.h" +#include "crypto/sha384.h" +#include "crypto/sha512.h" #include "dpp.h" @@ -29,6 +36,224 @@ static const struct dpp_curve_params dpp_curves[] = { }; +static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix) +{ + int len, res; + EC_KEY *eckey; + struct wpabuf *buf; + unsigned char *pos; + + eckey = EVP_PKEY_get1_EC_KEY(pkey); + if (!eckey) + return NULL; + EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED); + len = i2o_ECPublicKey(eckey, NULL); + if (len <= 0) { + wpa_printf(MSG_ERROR, + "DDP: Failed to determine public key encoding length"); + EC_KEY_free(eckey); + return NULL; + } + + buf = wpabuf_alloc(len); + if (!buf) { + EC_KEY_free(eckey); + return NULL; + } + + pos = wpabuf_put(buf, len); + res = i2o_ECPublicKey(eckey, &pos); + EC_KEY_free(eckey); + if (res != len) { + wpa_printf(MSG_ERROR, + "DDP: Failed to encode public key (res=%d/%d)", + res, len); + wpabuf_free(buf); + return NULL; + } + + if (!prefix) { + /* Remove 0x04 prefix to match DPP definition */ + pos = wpabuf_mhead(buf); + os_memmove(pos, pos + 1, len - 1); + buf->used--; + } + + return buf; +} + + +static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group, + const u8 *buf_x, const u8 *buf_y, + size_t len) +{ + EC_KEY *eckey = NULL; + BN_CTX *ctx; + EC_POINT *point = NULL; + BIGNUM *x = NULL, *y = NULL; + EVP_PKEY *pkey = NULL; + + ctx = BN_CTX_new(); + if (!ctx) { + wpa_printf(MSG_ERROR, "DPP: Out of memory"); + return NULL; + } + + point = EC_POINT_new(group); + x = BN_bin2bn(buf_x, len, NULL); + y = BN_bin2bn(buf_y, len, NULL); + if (!point || !x || !y) { + wpa_printf(MSG_ERROR, "DPP: Out of memory"); + goto fail; + } + + if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) { + wpa_printf(MSG_ERROR, + "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + if (!EC_POINT_is_on_curve(group, point, ctx) || + EC_POINT_is_at_infinity(group, point)) { + wpa_printf(MSG_ERROR, "DPP: Invalid point"); + goto fail; + } + + eckey = EC_KEY_new(); + if (!eckey || + EC_KEY_set_group(eckey, group) != 1 || + EC_KEY_set_public_key(eckey, point) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to set EC_KEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + + pkey = EVP_PKEY_new(); + if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { + wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY"); + goto fail; + } + +out: + BN_free(x); + BN_free(y); + EC_KEY_free(eckey); + EC_POINT_free(point); + BN_CTX_free(ctx); + return pkey; +fail: + EVP_PKEY_free(pkey); + pkey = NULL; + goto out; +} + + +static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, + const u8 *buf, size_t len) +{ + EC_KEY *eckey; + const EC_GROUP *group; + EVP_PKEY *pkey = NULL; + + if (len & 1) + return NULL; + + eckey = EVP_PKEY_get1_EC_KEY(group_key); + if (!eckey) { + wpa_printf(MSG_ERROR, + "DPP: Could not get EC_KEY from group_key"); + return NULL; + } + + group = EC_KEY_get0_group(eckey); + if (group) + pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2, + len / 2); + else + wpa_printf(MSG_ERROR, "DPP: Could not get EC group"); + + EC_KEY_free(eckey); + return pkey; +} + + +struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, + size_t len) +{ + struct wpabuf *msg; + + msg = wpabuf_alloc(7 + len); + if (!msg) + return NULL; + wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC); + wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); + wpabuf_put_be24(msg, OUI_WFA); + wpabuf_put_u8(msg, DPP_OUI_TYPE); + wpabuf_put_u8(msg, type); + return msg; +} + + +const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len) +{ + u16 id, alen; + const u8 *pos = buf, *end = buf + len; + + while (end - pos >= 4) { + id = WPA_GET_LE16(pos); + pos += 2; + alen = WPA_GET_LE16(pos); + pos += 2; + if (alen > end - pos) + return NULL; + if (id == req_id) { + *ret_len = alen; + return pos; + } + pos += alen; + } + + return NULL; +} + + +int dpp_check_attrs(const u8 *buf, size_t len) +{ + const u8 *pos, *end; + + pos = buf; + end = buf + len; + while (end - pos >= 4) { + u16 id, alen; + + id = WPA_GET_LE16(pos); + pos += 2; + alen = WPA_GET_LE16(pos); + pos += 2; + wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u", + id, alen); + if (alen > end - pos) { + wpa_printf(MSG_DEBUG, + "DPP: Truncated message - not enough room for the attribute - dropped"); + return -1; + } + pos += alen; + } + + if (end != pos) { + wpa_printf(MSG_DEBUG, + "DPP: Unexpected octets (%d) after the last attribute", + (int) (end - pos)); + return -1; + } + + return 0; +} + + void dpp_bootstrap_info_free(struct dpp_bootstrap_info *info) { if (!info) @@ -219,6 +444,8 @@ static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info) wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); return -1; } + wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", + bi->pubkey_hash, SHA256_MAC_LEN); /* DER encoded ASN.1 SubjectPublicKeyInfo * @@ -630,3 +857,1670 @@ fail: OPENSSL_free(der); return NULL; } + + +static int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, + unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + const char *info = "first intermediate key"; + int res = -1; + + /* k1 = HKDF(<>, "first intermediate key", M.x) */ + + /* HKDF-Extract(<>, M.x) */ + os_memset(salt, 0, hash_len); + if (hash_len == 32) { + if (hmac_sha256(salt, SHA256_MAC_LEN, Mx, Mx_len, prk) < 0) + return -1; + } else if (hash_len == 48) { + if (hmac_sha384(salt, SHA384_MAC_LEN, Mx, Mx_len, prk) < 0) + return -1; + } else if (hash_len == 64) { + if (hmac_sha512(salt, SHA512_MAC_LEN, Mx, Mx_len, prk) < 0) + return -1; + } else { + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + if (hash_len == 32) + res = hmac_sha256_kdf(prk, SHA256_MAC_LEN, NULL, + (const u8 *) info, os_strlen(info), + k1, SHA256_MAC_LEN); + else if (hash_len == 48) + res = hmac_sha384_kdf(prk, SHA384_MAC_LEN, NULL, + (const u8 *) info, os_strlen(info), + k1, SHA384_MAC_LEN); + else if (hash_len == 64) + res = hmac_sha512_kdf(prk, SHA512_MAC_LEN, NULL, + (const u8 *) info, os_strlen(info), + k1, SHA512_MAC_LEN); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)", + k1, hash_len); + return 0; +} + + +static int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, + unsigned int hash_len) +{ + u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; + const char *info = "second intermediate key"; + int res; + + /* k2 = HKDF(<>, "second intermediate key", N.x) */ + + /* HKDF-Extract(<>, N.x) */ + os_memset(salt, 0, hash_len); + if (hash_len == 32) + res = hmac_sha256(salt, SHA256_MAC_LEN, Nx, Nx_len, prk); + else if (hash_len == 48) + res = hmac_sha384(salt, SHA384_MAC_LEN, Nx, Nx_len, prk); + else if (hash_len == 64) + res = hmac_sha512(salt, SHA512_MAC_LEN, Nx, Nx_len, prk); + else + res = -1; + if (res < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + if (hash_len == 32) + res = hmac_sha256_kdf(prk, SHA256_MAC_LEN, NULL, + (const u8 *) info, os_strlen(info), + k2, SHA256_MAC_LEN); + else if (hash_len == 48) + res = hmac_sha384_kdf(prk, SHA384_MAC_LEN, NULL, + (const u8 *) info, os_strlen(info), + k2, SHA384_MAC_LEN); + else if (hash_len == 64) + res = hmac_sha512_kdf(prk, SHA512_MAC_LEN, NULL, + (const u8 *) info, os_strlen(info), + k2, SHA512_MAC_LEN); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)", + k2, hash_len); + return 0; +} + + +static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke, + unsigned int hash_len) +{ + size_t nonce_len; + u8 nonces[2 * DPP_MAX_NONCE_LEN]; + const char *info_ke = "DPP Key"; + u8 prk[DPP_MAX_HASH_LEN]; + int res; + const u8 *addr[3]; + size_t len[3]; + size_t num_elem = 0; + + /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */ + + /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */ + nonce_len = auth->curve->nonce_len; + os_memcpy(nonces, auth->i_nonce, nonce_len); + os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len); + addr[num_elem] = auth->Mx; + len[num_elem] = auth->secret_len; + num_elem++; + addr[num_elem] = auth->Nx; + len[num_elem] = auth->secret_len; + num_elem++; + if (auth->peer_bi && auth->own_bi) { + addr[num_elem] = auth->Lx; + len[num_elem] = auth->secret_len; + num_elem++; + } + if (hash_len == 32) + res = hmac_sha256_vector(nonces, 2 * nonce_len, + num_elem, addr, len, prk); + else if (hash_len == 48) + res = hmac_sha384_vector(nonces, 2 * nonce_len, + num_elem, addr, len, prk); + else if (hash_len == 64) + res = hmac_sha512_vector(nonces, 2 * nonce_len, + num_elem, addr, len, prk); + else + res = -1; + if (res < 0) + return -1; + wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)", + prk, hash_len); + + /* HKDF-Expand(PRK, info, L) */ + if (hash_len == 32) + res = hmac_sha256_kdf(prk, SHA256_MAC_LEN, NULL, + (const u8 *) info_ke, os_strlen(info_ke), + ke, SHA256_MAC_LEN); + else if (hash_len == 48) + res = hmac_sha384_kdf(prk, SHA384_MAC_LEN, NULL, + (const u8 *) info_ke, os_strlen(info_ke), + ke, SHA384_MAC_LEN); + else if (hash_len == 64) + res = hmac_sha512_kdf(prk, SHA512_MAC_LEN, NULL, + (const u8 *) info_ke, os_strlen(info_ke), + ke, SHA512_MAC_LEN); + os_memset(prk, 0, hash_len); + if (res < 0) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)", + ke, hash_len); + return 0; +} + + +struct dpp_authentication * dpp_auth_init(void *msg_ctx, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + int configurator) +{ + struct dpp_authentication *auth; + size_t nonce_len; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + struct wpabuf *msg, *pi = NULL; + u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1]; + u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE]; + u8 *pos; + const u8 *addr[1]; + size_t len[1], siv_len; + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + return NULL; + auth->msg_ctx = msg_ctx; + auth->initiator = 1; + auth->configurator = configurator; + auth->peer_bi = peer_bi; + auth->own_bi = own_bi; + auth->curve = peer_bi->curve; + + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->i_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len); + + auth->own_protocol_key = dpp_gen_keypair(auth->curve); + if (!auth->own_protocol_key) + goto fail; + + pi = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (!pi) + goto fail; + + /* ECDH: M = pI * BR */ + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + auth->secret_len = secret_len; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", + auth->Mx, auth->secret_len); + + if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, + auth->curve->hash_len) < 0) + goto fail; + + /* Build DPP Authentication Request frame attributes */ + msg = wpabuf_alloc(2 * (4 + SHA256_MAC_LEN) + 4 + wpabuf_len(pi) + + 4 + sizeof(wrapped_data)); + if (!msg) + goto fail; + auth->req_attr = msg; + + /* Responder Bootstrapping Key Hash */ + wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + + /* Initiator Bootstrapping Key Hash */ + wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + if (auth->own_bi) + wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN); + else + os_memset(wpabuf_put(msg, SHA256_MAC_LEN), 0, SHA256_MAC_LEN); + + /* Initiator Protocol Key */ + wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY); + wpabuf_put_le16(msg, wpabuf_len(pi)); + wpabuf_put_buf(msg, pi); + wpabuf_free(pi); + pi = NULL; + + /* Wrapped data ({I-nonce, I-capabilities}k1) */ + pos = clear; + /* I-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, auth->i_nonce, nonce_len); + pos += nonce_len; + /* I-capabilities */ + WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES); + pos += 2; + WPA_PUT_LE16(pos, 1); + pos += 2; + auth->i_capab = configurator ? DPP_CAPAB_CONFIGURATOR : + DPP_CAPAB_ENROLLEE; + *pos++ = auth->i_capab; + + addr[0] = wpabuf_head(msg); + len[0] = wpabuf_len(msg); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + siv_len = pos - clear; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); + if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len, + 1, addr, len, wrapped_data) < 0) + goto fail; + siv_len += AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, siv_len); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, siv_len); + wpabuf_put_data(msg, wrapped_data, siv_len); + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Request frame attributes", msg); + + return auth; +fail: + wpabuf_free(pi); + EVP_PKEY_CTX_free(ctx); + dpp_auth_deinit(auth); + return NULL; +} + + +static void dpp_auth_success(struct dpp_authentication *auth) +{ + wpa_printf(MSG_DEBUG, + "DPP: Authentication success - clear temporary keys"); + os_memset(auth->Mx, 0, sizeof(auth->Mx)); + os_memset(auth->Nx, 0, sizeof(auth->Nx)); + os_memset(auth->Lx, 0, sizeof(auth->Lx)); + os_memset(auth->k1, 0, sizeof(auth->k1)); + os_memset(auth->k2, 0, sizeof(auth->k2)); + + auth->auth_success = 1; +} + + +static int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth) +{ + struct wpabuf *pix, *prx, *bix, *brx; + const u8 *addr[7]; + size_t len[7]; + size_t i, num_elem = 0; + size_t nonce_len; + u8 zero = 0; + int res = -1; + + /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + nonce_len = auth->curve->nonce_len; + + if (auth->initiator) { + pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + if (auth->own_bi) + bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + else + bix = NULL; + brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + } else { + pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (auth->peer_bi) + bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + else + bix = NULL; + brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + } + if (!pix || !prx || !brx) + goto fail; + + addr[num_elem] = auth->i_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = auth->r_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = wpabuf_head(pix); + len[num_elem] = wpabuf_len(pix) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(prx); + len[num_elem] = wpabuf_len(prx) / 2; + num_elem++; + + if (bix) { + addr[num_elem] = wpabuf_head(bix); + len[num_elem] = wpabuf_len(bix) / 2; + num_elem++; + } + + addr[num_elem] = wpabuf_head(brx); + len[num_elem] = wpabuf_len(brx) / 2; + num_elem++; + + addr[num_elem] = &zero; + len[num_elem] = 1; + num_elem++; + + wpa_printf(MSG_DEBUG, "DPP: R-auth hash components"); + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); + if (auth->curve->hash_len == 32) + res = sha256_vector(num_elem, addr, len, r_auth); + else if (auth->curve->hash_len == 48) + res = sha384_vector(num_elem, addr, len, r_auth); + else if (auth->curve->hash_len == 64) + res = sha512_vector(num_elem, addr, len, r_auth); + else + res = -1; + if (res == 0) + wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth, + auth->curve->hash_len); +fail: + wpabuf_free(pix); + wpabuf_free(prx); + wpabuf_free(bix); + wpabuf_free(brx); + return res; +} + + +static int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth) +{ + struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL; + const u8 *addr[7]; + size_t len[7]; + size_t i, num_elem = 0; + size_t nonce_len; + u8 one = 1; + int res = -1; + + /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ + nonce_len = auth->curve->nonce_len; + + if (auth->initiator) { + pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + if (auth->own_bi) + bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + else + bix = NULL; + if (!auth->peer_bi) + goto fail; + brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + } else { + pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); + prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (auth->peer_bi) + bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); + else + bix = NULL; + if (!auth->own_bi) + goto fail; + brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); + } + if (!pix || !prx || !brx) + goto fail; + + addr[num_elem] = auth->r_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = auth->i_nonce; + len[num_elem] = nonce_len; + num_elem++; + + addr[num_elem] = wpabuf_head(prx); + len[num_elem] = wpabuf_len(prx) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(pix); + len[num_elem] = wpabuf_len(pix) / 2; + num_elem++; + + addr[num_elem] = wpabuf_head(brx); + len[num_elem] = wpabuf_len(brx) / 2; + num_elem++; + + if (bix) { + addr[num_elem] = wpabuf_head(bix); + len[num_elem] = wpabuf_len(bix) / 2; + num_elem++; + } + + addr[num_elem] = &one; + len[num_elem] = 1; + num_elem++; + + wpa_printf(MSG_DEBUG, "DPP: I-auth hash components"); + for (i = 0; i < num_elem; i++) + wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); + if (auth->curve->hash_len == 32) + res = sha256_vector(num_elem, addr, len, i_auth); + else if (auth->curve->hash_len == 48) + res = sha384_vector(num_elem, addr, len, i_auth); + else if (auth->curve->hash_len == 64) + res = sha512_vector(num_elem, addr, len, i_auth); + else + res = -1; + if (res == 0) + wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth, + auth->curve->hash_len); +fail: + wpabuf_free(pix); + wpabuf_free(prx); + wpabuf_free(bix); + wpabuf_free(brx); + return res; +} + + +static int dpp_auth_derive_l_responder(struct dpp_authentication *auth) +{ + const EC_GROUP *group; + EC_POINT *l = NULL; + EC_KEY *BI = NULL, *bR = NULL, *pR = NULL; + const EC_POINT *BI_point; + BN_CTX *bnctx; + BIGNUM *lx, *sum, *q; + const BIGNUM *bR_bn, *pR_bn; + int ret = -1; + int num_bytes, offset; + + /* L = ((bR + pR) modulo q) * BI */ + + bnctx = BN_CTX_new(); + sum = BN_new(); + q = BN_new(); + lx = BN_new(); + if (!bnctx || !sum || !q || !lx) + goto fail; + BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); + if (!BI) + goto fail; + BI_point = EC_KEY_get0_public_key(BI); + group = EC_KEY_get0_group(BI); + if (!group) + goto fail; + + bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); + pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key); + if (!bR || !pR) + goto fail; + bR_bn = EC_KEY_get0_private_key(bR); + pR_bn = EC_KEY_get0_private_key(pR); + if (!bR_bn || !pR_bn) + goto fail; + if (EC_GROUP_get_order(group, q, bnctx) != 1 || + BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1) + goto fail; + l = EC_POINT_new(group); + if (!l || + EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, + bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + num_bytes = BN_num_bytes(lx); + if ((size_t) num_bytes > auth->secret_len) + goto fail; + if (auth->secret_len > (size_t) num_bytes) + offset = auth->secret_len - num_bytes; + else + offset = 0; + + os_memset(auth->Lx, 0, offset); + BN_bn2bin(lx, auth->Lx + offset); + wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); + ret = 0; +fail: + EC_POINT_clear_free(l); + EC_KEY_free(BI); + EC_KEY_free(bR); + EC_KEY_free(pR); + BN_clear_free(lx); + BN_clear_free(sum); + BN_free(q); + BN_CTX_free(bnctx); + return ret; +} + + +static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth) +{ + const EC_GROUP *group; + EC_POINT *l = NULL, *sum = NULL; + EC_KEY *bI = NULL, *BR = NULL, *PR = NULL; + const EC_POINT *BR_point, *PR_point; + BN_CTX *bnctx; + BIGNUM *lx; + const BIGNUM *bI_bn; + int ret = -1; + int num_bytes, offset; + + /* L = bI * (BR + PR) */ + + bnctx = BN_CTX_new(); + lx = BN_new(); + if (!bnctx || !lx) + goto fail; + BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); + PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key); + if (!BR || !PR) + goto fail; + BR_point = EC_KEY_get0_public_key(BR); + PR_point = EC_KEY_get0_public_key(PR); + + bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); + if (!bI) + goto fail; + group = EC_KEY_get0_group(bI); + bI_bn = EC_KEY_get0_private_key(bI); + if (!group || !bI_bn) + goto fail; + sum = EC_POINT_new(group); + l = EC_POINT_new(group); + if (!sum || !l || + EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 || + EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 || + EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, + bnctx) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + num_bytes = BN_num_bytes(lx); + if ((size_t) num_bytes > auth->secret_len) + goto fail; + if (auth->secret_len > (size_t) num_bytes) + offset = auth->secret_len - num_bytes; + else + offset = 0; + + os_memset(auth->Lx, 0, offset); + BN_bn2bin(lx, auth->Lx + offset); + wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); + ret = 0; +fail: + EC_POINT_clear_free(l); + EC_KEY_free(bI); + EC_KEY_free(BR); + EC_KEY_free(PR); + BN_clear_free(lx); + BN_CTX_free(bnctx); + return ret; +} + + +static int dpp_auth_build_resp(struct dpp_authentication *auth) +{ + size_t nonce_len; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + struct wpabuf *msg, *pr = NULL; + u8 r_auth[4 + DPP_MAX_HASH_LEN]; + u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE]; +#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \ + 4 + sizeof(wrapped_r_auth) + size_t wrapped_r_auth_len; + u8 clear[DPP_AUTH_RESP_CLEAR_LEN]; + u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE]; + u8 *pos; + const u8 *addr[1]; + size_t len[1], siv_len; + + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); + + nonce_len = auth->curve->nonce_len; + if (random_get_bytes(auth->r_nonce, nonce_len)) { + wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len); + + auth->own_protocol_key = dpp_gen_keypair(auth->curve); + if (!auth->own_protocol_key) + goto fail; + + pr = dpp_get_pubkey_point(auth->own_protocol_key, 0); + if (!pr) + goto fail; + + /* ECDH: N = pR * PI */ + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, auth->peer_protocol_key) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", + auth->Nx, auth->secret_len); + + if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, + auth->curve->hash_len) < 0) + goto fail; + + if (auth->own_bi && auth->peer_bi) { + /* Mutual authentication */ + if (dpp_auth_derive_l_responder(auth) < 0) + goto fail; + } + + if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) + goto fail; + + /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG); + WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len); + if (dpp_gen_r_auth(auth, r_auth + 4) < 0 || + aes_siv_encrypt(auth->ke, auth->curve->hash_len, + r_auth, 4 + auth->curve->hash_len, + 0, NULL, NULL, wrapped_r_auth) < 0) + goto fail; + wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke", + wrapped_r_auth, wrapped_r_auth_len); + + /* Build DPP Authentication Response frame attributes */ + msg = wpabuf_alloc(4 + 1 + 2 * (4 + SHA256_MAC_LEN) + + 4 + wpabuf_len(pr) + 4 + sizeof(wrapped_data)); + if (!msg) + goto fail; + wpabuf_free(auth->resp_attr); + auth->resp_attr = msg; + + /* DPP Status */ + wpabuf_put_le16(msg, DPP_ATTR_STATUS); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, DPP_STATUS_OK); + + /* Responder Bootstrapping Key Hash */ + wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN); + + if (auth->peer_bi) { + /* Mutual authentication */ + /* Initiator Bootstrapping Key Hash */ + wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, + SHA256_MAC_LEN); + } + + /* Responder Protocol Key */ + wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY); + wpabuf_put_le16(msg, wpabuf_len(pr)); + wpabuf_put_buf(msg, pr); + wpabuf_free(pr); + pr = NULL; + + /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */ + pos = clear; + /* R-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, auth->r_nonce, nonce_len); + pos += nonce_len; + /* I-nonce */ + WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, auth->i_nonce, nonce_len); + pos += nonce_len; + /* R-capabilities */ + WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES); + pos += 2; + WPA_PUT_LE16(pos, 1); + pos += 2; + auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR : + DPP_CAPAB_ENROLLEE; + *pos++ = auth->r_capab; + /* {R-auth}ke */ + WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA); + pos += 2; + WPA_PUT_LE16(pos, wrapped_r_auth_len); + pos += 2; + os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len); + pos += wrapped_r_auth_len; + + addr[0] = wpabuf_head(msg); + len[0] = wpabuf_len(msg); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + siv_len = pos - clear; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); + if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, clear, siv_len, + 1, addr, len, wrapped_data) < 0) + goto fail; + siv_len += AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, siv_len); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, siv_len); + wpabuf_put_data(msg, wrapped_data, siv_len); + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Response frame attributes", msg); + + return 0; + +fail: + wpabuf_free(pr); + return -1; +} + + +static int dpp_auth_build_resp_status(struct dpp_authentication *auth, + enum dpp_status_error status) +{ + size_t nonce_len; + struct wpabuf *msg; +#define DPP_AUTH_RESP_CLEAR_LEN2 4 + DPP_MAX_NONCE_LEN + 4 + 1 + u8 clear[DPP_AUTH_RESP_CLEAR_LEN2]; + u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN2 + AES_BLOCK_SIZE]; + u8 *pos; + const u8 *addr[1]; + size_t len[1], siv_len; + + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); + + /* Build DPP Authentication Response frame attributes */ + msg = wpabuf_alloc(4 + 1 + 2 * (4 + SHA256_MAC_LEN) + + 4 + sizeof(wrapped_data)); + if (!msg) + goto fail; + wpabuf_free(auth->resp_attr); + auth->resp_attr = msg; + + /* DPP Status */ + wpabuf_put_le16(msg, DPP_ATTR_STATUS); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, status); + + /* Responder Bootstrapping Key Hash */ + wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN); + + if (auth->peer_bi) { + /* Mutual authentication */ + /* Initiator Bootstrapping Key Hash */ + wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, + SHA256_MAC_LEN); + } + + /* Wrapped data ({I-nonce, R-capabilities}k1) */ + pos = clear; + /* I-nonce */ + nonce_len = auth->curve->nonce_len; + WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); + pos += 2; + WPA_PUT_LE16(pos, nonce_len); + pos += 2; + os_memcpy(pos, auth->i_nonce, nonce_len); + pos += nonce_len; + /* R-capabilities */ + WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES); + pos += 2; + WPA_PUT_LE16(pos, 1); + pos += 2; + auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR : + DPP_CAPAB_ENROLLEE; + *pos++ = auth->r_capab; + + addr[0] = wpabuf_head(msg); + len[0] = wpabuf_len(msg); + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + siv_len = pos - clear; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); + if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len, + 1, addr, len, wrapped_data) < 0) + goto fail; + siv_len += AES_BLOCK_SIZE; + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, siv_len); + + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, siv_len); + wpabuf_put_data(msg, wrapped_data, siv_len); + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Response frame attributes", msg); + + return 0; + +fail: + return -1; +} + + +struct dpp_authentication * +dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + unsigned int freq, const u8 *attr_start, + const u8 *wrapped_data, u16 wrapped_data_len) +{ + EVP_PKEY *pi = NULL; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + const u8 *addr[1]; + size_t len[1]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *i_proto, *i_nonce, *i_capab, *i_bootstrap; + u16 i_proto_len, i_nonce_len, i_capab_len, i_bootstrap_len; + struct dpp_authentication *auth = NULL; + size_t attr_len; + + if (wrapped_data_len < AES_BLOCK_SIZE) + return NULL; + + attr_len = wrapped_data - 4 - attr_start; + + auth = os_zalloc(sizeof(*auth)); + if (!auth) + goto fail; + auth->msg_ctx = msg_ctx; + auth->peer_bi = peer_bi; + auth->own_bi = own_bi; + auth->curve = own_bi->curve; + auth->curr_freq = freq; + + i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY, + &i_proto_len); + if (!i_proto) { + wpa_printf(MSG_DEBUG, + "DPP: Missing required Initiator Protocol Key attribute"); + goto fail; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key", + i_proto, i_proto_len); + + /* M = bR * PI */ + pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len); + if (!pi) { + wpa_printf(MSG_DEBUG, "DPP: Invalid Initiator Protocol Key"); + goto fail; + } + dpp_debug_print_key("Peer (Initiator) Protocol Key", pi); + + ctx = EVP_PKEY_CTX_new(own_bi->pubkey, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pi) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + auth->secret_len = secret_len; + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", + auth->Mx, auth->secret_len); + + if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, + auth->curve->hash_len) < 0) + goto fail; + + addr[0] = attr_start; + len[0] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 1, addr, len, unwrapped) < 0) { + wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid attribute in unwrapped data"); + goto fail; + } + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + os_memcpy(auth->i_nonce, i_nonce, i_nonce_len); + + i_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_I_CAPABILITIES, + &i_capab_len); + if (!i_capab || i_capab_len < 1) { + wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-capabilities"); + goto fail; + } + auth->i_capab = i_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab); + + bin_clear_free(unwrapped, unwrapped_len); + unwrapped = NULL; + + switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) { + case DPP_CAPAB_ENROLLEE: + if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Configurator role"); + goto not_compatible; + } + wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); + auth->configurator = 1; + break; + case DPP_CAPAB_CONFIGURATOR: + if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) { + wpa_printf(MSG_DEBUG, + "DPP: Local policy does not allow Enrollee role"); + goto not_compatible; + } + wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); + auth->configurator = 0; + break; + default: + wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities"); + goto not_compatible; + } + + auth->peer_protocol_key = pi; + pi = NULL; + if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) { + char hex[SHA256_MAC_LEN * 2 + 1]; + + wpa_printf(MSG_DEBUG, + "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time"); + if (dpp_auth_build_resp_status(auth, + DPP_STATUS_RESPONSE_PENDING) < 0) + goto fail; + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) { + auth->response_pending = 1; + os_memcpy(auth->waiting_pubkey_hash, + i_bootstrap, i_bootstrap_len); + wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap, + i_bootstrap_len); + } else { + hex[0] = '\0'; + } + + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE + "%s", hex); + return auth; + } + if (dpp_auth_build_resp(auth) < 0) + goto fail; + + return auth; + +not_compatible: + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE + "i-capab=0x%02x", auth->i_capab); + if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) + auth->configurator = 1; + else + auth->configurator = 0; + auth->peer_protocol_key = pi; + pi = NULL; + if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0) + goto fail; + + auth->remove_on_tx_status = 1; + return auth; +fail: + bin_clear_free(unwrapped, unwrapped_len); + EVP_PKEY_free(pi); + EVP_PKEY_CTX_free(ctx); + dpp_auth_deinit(auth); + return NULL; +} + + +int dpp_notify_new_qr_code(struct dpp_authentication *auth, + struct dpp_bootstrap_info *peer_bi) +{ + if (!auth || !auth->response_pending || + os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) + return 0; + + wpa_printf(MSG_DEBUG, + "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with " + MACSTR, MAC2STR(auth->peer_mac_addr)); + auth->peer_bi = peer_bi; + + if (dpp_auth_build_resp(auth) < 0) + return -1; + + return 1; +} + + +static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth) +{ + struct wpabuf *msg; + u8 i_auth[4 + DPP_MAX_HASH_LEN]; + size_t i_auth_len; + const u8 *addr[1]; + size_t len[1]; + u8 *wrapped_i_auth; + + wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation"); + + i_auth_len = 4 + auth->curve->hash_len; + /* Build DPP Authentication Confirmation frame attributes */ + msg = wpabuf_alloc(4 + 1 + 2 * (4 + SHA256_MAC_LEN) + + 4 + i_auth_len + AES_BLOCK_SIZE); + if (!msg) + goto fail; + + /* DPP Status */ + wpabuf_put_le16(msg, DPP_ATTR_STATUS); + wpabuf_put_le16(msg, 1); + wpabuf_put_u8(msg, DPP_STATUS_OK); + + /* Responder Bootstrapping Key Hash */ + wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + + if (auth->own_bi) { + /* Mutual authentication */ + /* Initiator Bootstrapping Key Hash */ + wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); + wpabuf_put_le16(msg, SHA256_MAC_LEN); + wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN); + } + + addr[0] = wpabuf_head(msg); + len[0] = wpabuf_len(msg); + wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); + wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE); + wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE); + /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ + WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG); + WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len); + if (dpp_gen_i_auth(auth, i_auth + 4) < 0 || + aes_siv_encrypt(auth->ke, auth->curve->hash_len, + i_auth, i_auth_len, + 1, addr, len, wrapped_i_auth) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke", + wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE); + + wpa_hexdump_buf(MSG_DEBUG, + "DPP: Authentication Confirmation frame attributes", + msg); + dpp_auth_success(auth); + + return msg; + +fail: + return NULL; +} + + +static void +dpp_auth_resp_rx_status(struct dpp_authentication *auth, + const u8 *attr_start, size_t attr_len, + const u8 *wrapped_data, u16 wrapped_data_len, + enum dpp_status_error status) +{ + const u8 *addr[1]; + size_t len[1]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + const u8 *i_nonce, *r_capab; + u16 i_nonce_len, r_capab_len; + + if (status == DPP_STATUS_NOT_COMPATIBLE) { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported incompatible roles"); + } else if (status == DPP_STATUS_RESPONSE_PENDING) { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported more time needed"); + } else { + wpa_printf(MSG_DEBUG, + "DPP: Responder reported failure (status %d)", + status); + return; + } + + addr[0] = attr_start; + len[0] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 1, addr, len, unwrapped) < 0) { + wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid attribute in unwrapped data"); + goto fail; + } + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { + wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch"); + goto fail; + } + + r_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_R_CAPABILITIES, + &r_capab_len); + if (!r_capab || r_capab_len < 1) { + wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities"); + goto fail; + } + auth->r_capab = r_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); + if (status == DPP_STATUS_NOT_COMPATIBLE) { + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE + "r-capab=0x%02x", auth->r_capab); + } else if (status == DPP_STATUS_RESPONSE_PENDING) { + wpa_printf(MSG_DEBUG, + "DPP: Continue waiting for full DPP Authentication Response"); + wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_RESPONSE_PENDING); + } +fail: + bin_clear_free(unwrapped, unwrapped_len); +} + + +struct wpabuf * +dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *attr_start, + size_t attr_len) +{ + EVP_PKEY *pr; + EVP_PKEY_CTX *ctx = NULL; + size_t secret_len; + const u8 *addr[1]; + size_t len[1]; + u8 *unwrapped = NULL, *unwrapped2 = NULL; + size_t unwrapped_len = 0, unwrapped2_len = 0; + const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto, + *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth; + u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, + r_proto_len, r_nonce_len, i_nonce_len, r_capab_len, + wrapped2_len, r_auth_len; + u8 r_auth2[DPP_MAX_HASH_LEN]; + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, + "DPP: Missing required Wrapped data attribute"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + if (wrapped_data_len < AES_BLOCK_SIZE) + return NULL; + + attr_len = wrapped_data - 4 - attr_start; + + r_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return NULL; + } + wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, + "DPP: Expected Responder Bootstrapping Key Hash", + auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + return NULL; + } + + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap) { + if (i_bootstrap_len != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid Initiator Bootstrapping Key Hash attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, + "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + if (!auth->own_bi || + os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Initiator Bootstrapping Key Hash attribute did not match"); + return NULL; + } + } + + status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid required DPP Status attribute"); + return NULL; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + auth->auth_resp_status = status[0]; + if (status[0] != DPP_STATUS_OK) { + dpp_auth_resp_rx_status(auth, attr_start, + attr_len, wrapped_data, + wrapped_data_len, status[0]); + return NULL; + } + + r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY, + &r_proto_len); + if (!r_proto) { + wpa_printf(MSG_DEBUG, + "DPP: Missing required Responder Protocol Key attribute"); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key", + r_proto, r_proto_len); + + /* N = pI * PR */ + pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len); + if (!pr) { + wpa_printf(MSG_DEBUG, "DPP: Invalid Responder Protocol Key"); + return NULL; + } + dpp_debug_print_key("Peer (Responder) Protocol Key", pr); + + ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL); + if (!ctx || + EVP_PKEY_derive_init(ctx) != 1 || + EVP_PKEY_derive_set_peer(ctx, pr) != 1 || + EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 || + secret_len > DPP_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(ctx, auth->Nx, &secret_len) != 1) { + wpa_printf(MSG_ERROR, + "DPP: Failed to derive ECDH shared secret: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + EVP_PKEY_CTX_free(ctx); + ctx = NULL; + auth->peer_protocol_key = pr; + pr = NULL; + + wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", + auth->Nx, auth->secret_len); + + if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, + auth->curve->hash_len) < 0) + goto fail; + + addr[0] = attr_start; + len[0] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + goto fail; + if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 1, addr, len, unwrapped) < 0) { + wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid attribute in unwrapped data"); + goto fail; + } + + r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, + &r_nonce_len); + if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { + wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len); + os_memcpy(auth->r_nonce, r_nonce, r_nonce_len); + + i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, + &i_nonce_len); + if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { + wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); + if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { + wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch"); + goto fail; + } + + if (auth->own_bi && auth->peer_bi) { + /* Mutual authentication */ + if (dpp_auth_derive_l_initiator(auth) < 0) + goto fail; + } + + if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) + goto fail; + + r_capab = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_R_CAPABILITIES, + &r_capab_len); + if (!r_capab || r_capab_len < 1) { + wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities"); + goto fail; + } + auth->r_capab = r_capab[0]; + wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); + if ((auth->configurator && (auth->r_capab & DPP_CAPAB_CONFIGURATOR)) || + (!auth->configurator && (auth->r_capab & DPP_CAPAB_ENROLLEE))) { + wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection"); + goto fail; + } + + wrapped2 = dpp_get_attr(unwrapped, unwrapped_len, + DPP_ATTR_WRAPPED_DATA, &wrapped2_len); + if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid Secondary Wrapped Data"); + goto fail; + } + + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped2, wrapped2_len); + unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE; + unwrapped2 = os_malloc(unwrapped2_len); + if (!unwrapped2) + goto fail; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped2, wrapped2_len, + 0, NULL, NULL, unwrapped2) < 0) { + wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped2, unwrapped2_len); + + if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid attribute in secondary unwrapped data"); + goto fail; + } + + r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG, + &r_auth_len); + if (!r_auth || r_auth_len != auth->curve->hash_len) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid Responder Authenticating Tag"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag", + r_auth, r_auth_len); + /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ + if (dpp_gen_r_auth(auth, r_auth2) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag", + r_auth2, r_auth_len); + if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Mismatching Responder Authenticating Tag"); + goto fail; + } + + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + + return dpp_auth_build_conf(auth); + +fail: + bin_clear_free(unwrapped, unwrapped_len); + bin_clear_free(unwrapped2, unwrapped2_len); + EVP_PKEY_free(pr); + EVP_PKEY_CTX_free(ctx); + return NULL; +} + + +int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *attr_start, + size_t attr_len) +{ + const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth; + u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, + i_auth_len; + const u8 *addr[1]; + size_t len[1]; + u8 *unwrapped = NULL; + size_t unwrapped_len = 0; + u8 i_auth2[DPP_MAX_HASH_LEN]; + + wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, + "DPP: Missing required Wrapped data attribute"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + if (wrapped_data_len < AES_BLOCK_SIZE) + return -1; + + attr_len = wrapped_data - 4 - attr_start; + + r_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap > wrapped_data || + r_bootstrap_len != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, + "DPP: Expected Responder Bootstrapping Key Hash", + auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); + return -1; + } + + i_bootstrap = dpp_get_attr(attr_start, attr_len, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (i_bootstrap) { + if (i_bootstrap > wrapped_data || + i_bootstrap_len != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid Initiator Bootstrapping Key Hash attribute"); + return -1; + } + wpa_hexdump(MSG_MSGDUMP, + "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + if (!auth->peer_bi || + os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash, + SHA256_MAC_LEN) != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Initiator Bootstrapping Key Hash attribute did not match"); + return -1; + } + } + + status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, + &status_len); + if (!status || status_len < 1) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid required DPP Status attribute"); + return -1; + } + wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); + if (status[0] != DPP_STATUS_OK) { + wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); + return -1; + } + + addr[0] = attr_start; + len[0] = attr_len; + wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", + wrapped_data, wrapped_data_len); + unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; + unwrapped = os_malloc(unwrapped_len); + if (!unwrapped) + return -1; + if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, + wrapped_data, wrapped_data_len, + 1, addr, len, unwrapped) < 0) { + wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", + unwrapped, unwrapped_len); + + if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Invalid attribute in unwrapped data"); + goto fail; + } + + i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG, + &i_auth_len); + if (!i_auth || i_auth_len != auth->curve->hash_len) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid Initiator Authenticating Tag"); + goto fail; + } + wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag", + i_auth, i_auth_len); + /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ + if (dpp_gen_i_auth(auth, i_auth2) < 0) + goto fail; + wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag", + i_auth2, i_auth_len); + if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) { + wpa_printf(MSG_DEBUG, + "DPP: Mismatching Initiator Authenticating Tag"); + goto fail; + } + + bin_clear_free(unwrapped, unwrapped_len); + dpp_auth_success(auth); + return 0; +fail: + bin_clear_free(unwrapped, unwrapped_len); + return -1; +} + + +void dpp_auth_deinit(struct dpp_authentication *auth) +{ + if (!auth) + return; + EVP_PKEY_free(auth->own_protocol_key); + EVP_PKEY_free(auth->peer_protocol_key); + wpabuf_free(auth->req_attr); + wpabuf_free(auth->resp_attr); + bin_clear_free(auth, sizeof(*auth)); +} diff --git a/src/common/dpp.h b/src/common/dpp.h index 22c348dd4..a2ca9b98d 100644 --- a/src/common/dpp.h +++ b/src/common/dpp.h @@ -14,7 +14,64 @@ #include "utils/list.h" #include "crypto/sha256.h" +/* DPP Public Action frame identifiers - OUI_WFA */ +#define DPP_OUI_TYPE 0x1A + +enum dpp_public_action_frame_type { + DPP_PA_AUTHENTICATION_REQ = 0, + DPP_PA_AUTHENTICATION_RESP = 1, + DPP_PA_AUTHENTICATION_CONF = 2, + DPP_PA_PEER_DISCOVERY_REQ = 5, + DPP_PA_PEER_DISCOVERY_RESP = 6, + DPP_PA_PKEX_EXCHANGE_REQ = 7, + DPP_PA_PKEX_EXCHANGE_RESP = 8, + DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9, + DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10, +}; + +enum dpp_attribute_id { + DPP_ATTR_STATUS = 0x1000, + DPP_ATTR_I_BOOTSTRAP_KEY_HASH = 0x1001, + DPP_ATTR_R_BOOTSTRAP_KEY_HASH = 0x1002, + DPP_ATTR_I_PROTOCOL_KEY = 0x1003, + DPP_ATTR_WRAPPED_DATA = 0x1004, + DPP_ATTR_I_NONCE = 0x1005, + DPP_ATTR_I_CAPABILITIES = 0x1006, + DPP_ATTR_R_NONCE = 0x1007, + DPP_ATTR_R_CAPABILITIES = 0x1008, + DPP_ATTR_R_PROTOCOL_KEY = 0x1009, + DPP_ATTR_I_AUTH_TAG = 0x100A, + DPP_ATTR_R_AUTH_TAG = 0x100B, + DPP_ATTR_CONFIG_OBJ = 0x100C, + DPP_ATTR_CONNECTOR = 0x100D, + DPP_ATTR_CONFIG_ATTR_OBJ = 0x100E, + DPP_ATTR_BOOTSTRAP_KEY = 0x100F, + DPP_ATTR_PEER_NET_PK_HASH = 0x1010, + DPP_ATTR_OWN_NET_NK_HASH = 0x1011, + DPP_ATTR_FINITE_CYCLIC_GROUP = 0x1012, + DPP_ATTR_ENCRYPTED_KEY = 0x1013, + DPP_ATTR_ENROLLEE_NONCE = 0x1014, + DPP_ATTR_CODE_IDENTIFIER = 0x1015, +}; + +enum dpp_status_error { + DPP_STATUS_OK = 0, + DPP_STATUS_NOT_COMPATIBLE = 1, + DPP_STATUS_AUTH_FAILURE = 2, + DPP_STATUS_UNWRAP_FAILURE = 3, + DPP_STATUS_BAD_GROUP = 4, + DPP_STATUS_CONFIGURE_FAILURE = 5, + DPP_STATUS_RESPONSE_PENDING = 6, +}; + +#define DPP_CAPAB_ENROLLEE BIT(0) +#define DPP_CAPAB_CONFIGURATOR BIT(1) +#define DPP_CAPAB_ROLE_MASK (BIT(0) | BIT(1)) + #define DPP_BOOTSTRAP_MAX_FREQ 30 +#define DPP_MAX_NONCE_LEN 32 +#define DPP_MAX_HASH_LEN 64 +#define DPP_MAX_SHARED_SECRET_LEN 66 struct dpp_curve_params { const char *name; @@ -44,6 +101,37 @@ struct dpp_bootstrap_info { const struct dpp_curve_params *curve; }; +struct dpp_authentication { + void *msg_ctx; + const struct dpp_curve_params *curve; + struct dpp_bootstrap_info *peer_bi; + struct dpp_bootstrap_info *own_bi; + u8 waiting_pubkey_hash[SHA256_MAC_LEN]; + int response_pending; + enum dpp_status_error auth_resp_status; + u8 peer_mac_addr[ETH_ALEN]; + u8 i_nonce[DPP_MAX_NONCE_LEN]; + u8 r_nonce[DPP_MAX_NONCE_LEN]; + u8 i_capab; + u8 r_capab; + EVP_PKEY *own_protocol_key; + EVP_PKEY *peer_protocol_key; + struct wpabuf *req_attr; + struct wpabuf *resp_attr; + unsigned int curr_freq; + size_t secret_len; + u8 Mx[DPP_MAX_SHARED_SECRET_LEN]; + u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; + u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; + u8 k1[DPP_MAX_HASH_LEN]; + u8 k2[DPP_MAX_HASH_LEN]; + u8 ke[DPP_MAX_HASH_LEN]; + int initiator; + int configurator; + int remove_on_tx_status; + int auth_success; +}; + 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); @@ -52,5 +140,27 @@ 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); +struct dpp_authentication * dpp_auth_init(void *msg_ctx, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + int configurator); +struct dpp_authentication * +dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, + struct dpp_bootstrap_info *peer_bi, + struct dpp_bootstrap_info *own_bi, + unsigned int freq, const u8 *attr_start, + const u8 *wrapped_data, u16 wrapped_data_len); +struct wpabuf * +dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *attr_start, + size_t attr_len); +int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *attr_start, + size_t attr_len); +int dpp_notify_new_qr_code(struct dpp_authentication *auth, + struct dpp_bootstrap_info *peer_bi); +void dpp_auth_deinit(struct dpp_authentication *auth); +struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, + size_t len); +const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len); +int dpp_check_attrs(const u8 *buf, size_t len); #endif /* DPP_H */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 88f526d5d..64d8bb7db 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -143,6 +143,12 @@ extern "C" { #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS " #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG " +/* DPP events */ +#define DPP_EVENT_AUTH_SUCCESS "DPP-AUTH-SUCCESS " +#define DPP_EVENT_NOT_COMPATIBLE "DPP-NOT-COMPATIBLE " +#define DPP_EVENT_RESPONSE_PENDING "DPP-RESPONSE-PENDING " +#define DPP_EVENT_SCAN_PEER_QR_CODE "DPP-SCAN-PEER-QR-CODE " + /* MESH events */ #define MESH_GROUP_STARTED "MESH-GROUP-STARTED " #define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED " diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 7a641756b..509a88007 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -247,6 +247,13 @@ ifdef CONFIG_DPP L_CFLAGS += -DCONFIG_DPP OBJS += src/common/dpp.c OBJS += dpp_supplicant.c +NEED_AES_SIV=y +NEED_HMAC_SHA256_KDF=y +NEED_HMAC_SHA384_KDF=y +NEED_HMAC_SHA512_KDF=y +NEED_SHA256=y +NEED_SHA384=y +NEED_SHA512=y endif ifdef CONFIG_OWE diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index a28340c92..3a2595451 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -280,6 +280,13 @@ ifdef CONFIG_DPP CFLAGS += -DCONFIG_DPP OBJS += ../src/common/dpp.o OBJS += dpp_supplicant.o +NEED_AES_SIV=y +NEED_HMAC_SHA256_KDF=y +NEED_HMAC_SHA384_KDF=y +NEED_HMAC_SHA512_KDF=y +NEED_SHA256=y +NEED_SHA384=y +NEED_SHA512=y endif ifdef CONFIG_OWE diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index d3bd0cbff..247fd6479 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -7558,6 +7558,10 @@ static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s) wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; +#ifdef CONFIG_DPP + wpas_dpp_deinit(wpa_s); +#endif /* CONFIG_DPP */ + #ifdef CONFIG_TDLS #ifdef CONFIG_TDLS_TESTING tdls_testing = 0; @@ -10187,6 +10191,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (os_snprintf_error(reply_size, reply_len)) reply_len = -1; } + } else if (os_strncmp(buf, "DPP_AUTH_INIT ", 14) == 0) { + if (wpas_dpp_auth_init(wpa_s, buf + 13) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "DPP_LISTEN ", 11) == 0) { + if (wpas_dpp_listen(wpa_s, buf + 11) < 0) + reply_len = -1; + } else if (os_strcmp(buf, "DPP_STOP_LISTEN") == 0) { + wpas_dpp_listen_stop(wpa_s); #endif /* CONFIG_DPP */ } else { os_memcpy(reply, "UNKNOWN COMMAND\n", 16); diff --git a/wpa_supplicant/dpp_supplicant.c b/wpa_supplicant/dpp_supplicant.c index 5a2bfd635..8cae2486f 100644 --- a/wpa_supplicant/dpp_supplicant.c +++ b/wpa_supplicant/dpp_supplicant.c @@ -9,11 +9,25 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "common/dpp.h" #include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "offchannel.h" #include "dpp_supplicant.h" +static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s, + unsigned int freq); +static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result result); + +static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s) { struct dpp_bootstrap_info *bi; @@ -37,6 +51,7 @@ static unsigned int wpas_dpp_next_id(struct wpa_supplicant *wpa_s) int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd) { struct dpp_bootstrap_info *bi; + struct dpp_authentication *auth = wpa_s->dpp_auth; bi = dpp_parse_qr_code(cmd); if (!bi) @@ -45,6 +60,27 @@ int wpas_dpp_qr_code(struct wpa_supplicant *wpa_s, const char *cmd) bi->id = wpas_dpp_next_id(wpa_s); dl_list_add(&wpa_s->dpp_bootstrap, &bi->list); + if (auth && auth->response_pending && + dpp_notify_new_qr_code(auth, bi) == 1) { + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, + "DPP: Sending out pending authentication response"); + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, + wpabuf_len(auth->resp_attr)); + if (!msg) + goto out; + wpabuf_put_buf(msg, wpa_s->dpp_auth->resp_attr); + + offchannel_send_action(wpa_s, auth->curr_freq, + auth->peer_mac_addr, wpa_s->own_addr, + broadcast, + wpabuf_head(msg), wpabuf_len(msg), + 500, wpas_dpp_tx_status, 0); + wpabuf_free(msg); + } + +out: return bi->id; } @@ -215,6 +251,553 @@ const char * wpas_dpp_bootstrap_get_uri(struct wpa_supplicant *wpa_s, } +static void wpas_dpp_tx_status(struct wpa_supplicant *wpa_s, + unsigned int freq, const u8 *dst, + const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len, + enum offchannel_send_action_result result) +{ + wpa_printf(MSG_DEBUG, "DPP: TX status: freq=%u dst=" MACSTR + " result=%s", + freq, MAC2STR(dst), + result == OFFCHANNEL_SEND_ACTION_SUCCESS ? "SUCCESS" : + (result == OFFCHANNEL_SEND_ACTION_NO_ACK ? "no-ACK" : + "FAILED")); + + if (!wpa_s->dpp_auth) { + wpa_printf(MSG_DEBUG, + "DPP: Ignore TX status since there is no ongoing authentication exchange"); + return; + } + + if (wpa_s->dpp_auth->remove_on_tx_status) { + wpa_printf(MSG_DEBUG, + "DPP: Terminate authentication exchange due to an earlier error"); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; + return; + } + + if (!is_broadcast_ether_addr(dst) && + result != OFFCHANNEL_SEND_ACTION_SUCCESS) { + wpa_printf(MSG_DEBUG, + "DPP: Unicast DPP Action frame was not ACKed"); + /* TODO: In case of DPP Authentication Request frame, move to + * the next channel immediately */ + } +} + + +static void wpas_dpp_reply_wait_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + + if (!wpa_s->dpp_auth) + return; + wpa_printf(MSG_DEBUG, "DPP: Continue reply wait on channel %u MHz", + wpa_s->dpp_auth->curr_freq); + wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->curr_freq); +} + + +int wpas_dpp_auth_init(struct wpa_supplicant *wpa_s, const char *cmd) +{ + const char *pos; + struct dpp_bootstrap_info *peer_bi, *own_bi = NULL; + struct wpabuf *msg; + const u8 *dst; + int res; + int configurator = 1; + unsigned int wait_time; + + pos = os_strstr(cmd, " peer="); + if (!pos) + return -1; + pos += 6; + peer_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + if (!peer_bi) { + wpa_printf(MSG_INFO, + "DPP: Could not find bootstrapping info for the identified peer"); + return -1; + } + + pos = os_strstr(cmd, " own="); + if (pos) { + pos += 5; + own_bi = dpp_bootstrap_get_id(wpa_s, atoi(pos)); + if (!own_bi) { + wpa_printf(MSG_INFO, + "DPP: Could not find bootstrapping info for the identified local entry"); + return -1; + } + + if (peer_bi->curve != own_bi->curve) { + wpa_printf(MSG_INFO, + "DPP: Mismatching curves in bootstrapping info (peer=%s own=%s)", + peer_bi->curve->name, own_bi->curve->name); + return -1; + } + } + + pos = os_strstr(cmd, " role="); + if (pos) { + pos += 6; + if (os_strncmp(pos, "configurator", 12) == 0) + configurator = 1; + else if (os_strncmp(pos, "enrollee", 8) == 0) + configurator = 0; + else + return -1; + } + + if (wpa_s->dpp_auth) { + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + offchannel_send_action_done(wpa_s); + dpp_auth_deinit(wpa_s->dpp_auth); + } + wpa_s->dpp_auth = dpp_auth_init(wpa_s, peer_bi, own_bi, configurator); + if (!wpa_s->dpp_auth) + return -1; + + /* TODO: Support iteration over all frequencies and filtering of + * frequencies based on locally enabled channels that allow initiation + * of transmission. */ + if (peer_bi->num_freq > 0) + wpa_s->dpp_auth->curr_freq = peer_bi->freq[0]; + else + wpa_s->dpp_auth->curr_freq = 2412; + + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, + wpabuf_len(wpa_s->dpp_auth->req_attr)); + if (!msg) + return -1; + wpabuf_put_buf(msg, wpa_s->dpp_auth->req_attr); + + if (is_zero_ether_addr(peer_bi->mac_addr)) { + dst = broadcast; + } else { + dst = peer_bi->mac_addr; + os_memcpy(wpa_s->dpp_auth->peer_mac_addr, peer_bi->mac_addr, + ETH_ALEN); + } + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + wait_time = wpa_s->max_remain_on_chan; + if (wait_time > 2000) + wait_time = 2000; + eloop_register_timeout(wait_time / 1000, (wait_time % 1000) * 1000, + wpas_dpp_reply_wait_timeout, + wpa_s, NULL); + res = offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq, + dst, wpa_s->own_addr, broadcast, + wpabuf_head(msg), wpabuf_len(msg), + wait_time, wpas_dpp_tx_status, 0); + wpabuf_free(msg); + + return res; +} + + +struct wpas_dpp_listen_work { + unsigned int freq; + unsigned int duration; + struct wpabuf *probe_resp_ie; +}; + + +static void wpas_dpp_listen_work_free(struct wpas_dpp_listen_work *lwork) +{ + if (!lwork) + return; + os_free(lwork); +} + + +static void wpas_dpp_listen_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpas_dpp_listen_work *lwork; + + if (!wpa_s->dpp_listen_work) + return; + + lwork = wpa_s->dpp_listen_work->ctx; + wpas_dpp_listen_work_free(lwork); + radio_work_done(wpa_s->dpp_listen_work); + wpa_s->dpp_listen_work = NULL; +} + + +static void dpp_start_listen_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_supplicant *wpa_s = work->wpa_s; + struct wpas_dpp_listen_work *lwork = work->ctx; + + if (deinit) { + if (work->started) { + wpa_s->dpp_listen_work = NULL; + wpas_dpp_listen_stop(wpa_s); + } + wpas_dpp_listen_work_free(lwork); + return; + } + + wpa_s->dpp_listen_work = work; + + wpa_s->dpp_pending_listen_freq = lwork->freq; + + if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, + wpa_s->max_remain_on_chan) < 0) { + wpa_printf(MSG_DEBUG, + "DPP: Failed to request the driver to remain on channel (%u MHz) for listen", + lwork->freq); + wpas_dpp_listen_work_done(wpa_s); + wpa_s->dpp_pending_listen_freq = 0; + return; + } + wpa_s->off_channel_freq = 0; + wpa_s->roc_waiting_drv_freq = lwork->freq; +} + + +static int wpas_dpp_listen_start(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + struct wpas_dpp_listen_work *lwork; + + if (wpa_s->dpp_listen_work) { + wpa_printf(MSG_DEBUG, + "DPP: Reject start_listen since dpp_listen_work already exists"); + return -1; + } + + if (wpa_s->dpp_listen_freq) + wpas_dpp_listen_stop(wpa_s); + wpa_s->dpp_listen_freq = freq; + + lwork = os_zalloc(sizeof(*lwork)); + if (!lwork) + return -1; + lwork->freq = freq; + + if (radio_add_work(wpa_s, freq, "dpp-listen", 0, dpp_start_listen_cb, + lwork) < 0) { + wpas_dpp_listen_work_free(lwork); + return -1; + } + + return 0; +} + + +int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd) +{ + int freq; + + freq = atoi(cmd); + if (freq <= 0) + return -1; + + if (os_strstr(cmd, " role=configurator")) + wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR; + else if (os_strstr(cmd, " role=enrollee")) + wpa_s->dpp_allowed_roles = DPP_CAPAB_ENROLLEE; + else + wpa_s->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | + DPP_CAPAB_ENROLLEE; + wpa_s->dpp_qr_mutual = os_strstr(cmd, " qr=mutual") != NULL; + if (wpa_s->dpp_listen_freq == (unsigned int) freq) { + wpa_printf(MSG_DEBUG, "DPP: Already listening on %u MHz", + freq); + return 0; + } + + return wpas_dpp_listen_start(wpa_s, freq); +} + + +void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s) +{ + if (!wpa_s->dpp_listen_freq) + return; + + wpa_printf(MSG_DEBUG, "DPP: Stop listen on %u MHz", + wpa_s->dpp_listen_freq); + wpa_drv_cancel_remain_on_channel(wpa_s); + wpa_s->dpp_listen_freq = 0; + wpas_dpp_listen_work_done(wpa_s); +} + + +void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + if (!wpa_s->dpp_listen_freq && !wpa_s->dpp_pending_listen_freq) + return; + + wpa_printf(MSG_DEBUG, + "DPP: remain-on-channel callback (off_channel_freq=%u dpp_pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u)", + wpa_s->off_channel_freq, wpa_s->dpp_pending_listen_freq, + wpa_s->roc_waiting_drv_freq, freq); + if (wpa_s->off_channel_freq && + wpa_s->off_channel_freq == wpa_s->dpp_pending_listen_freq) { + wpa_printf(MSG_DEBUG, "DPP: Listen on %u MHz started", freq); + wpa_s->dpp_pending_listen_freq = 0; + } else { + wpa_printf(MSG_DEBUG, + "DPP: Ignore remain-on-channel callback (off_channel_freq=%u dpp_pending_listen_freq=%d freq=%u)", + wpa_s->off_channel_freq, + wpa_s->dpp_pending_listen_freq, freq); + } +} + + +void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq) +{ + wpas_dpp_listen_work_done(wpa_s); + + if (wpa_s->dpp_auth) { + /* Continue listen with a new remain-on-channel */ + wpa_printf(MSG_DEBUG, + "DPP: Continue wait on %u MHz for the ongoing DPP provisioning session", + wpa_s->dpp_auth->curr_freq); + wpas_dpp_listen_start(wpa_s, wpa_s->dpp_auth->curr_freq); + return; + } + + if (wpa_s->dpp_listen_freq) { + /* Continue listen with a new remain-on-channel */ + wpas_dpp_listen_start(wpa_s, wpa_s->dpp_listen_freq); + } +} + + +static void wpas_dpp_rx_auth_req(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *buf, size_t len, unsigned int freq) +{ + const u8 *r_bootstrap, *i_bootstrap, *wrapped_data; + u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len; + struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL; + struct wpabuf *msg; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR, + MAC2STR(src)); + + wrapped_data = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA, + &wrapped_data_len); + if (!wrapped_data) { + wpa_printf(MSG_DEBUG, + "DPP: Missing required Wrapped data attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped data", + wrapped_data, wrapped_data_len); + + r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, + &r_bootstrap_len); + if (!r_bootstrap || r_bootstrap > wrapped_data || + r_bootstrap_len != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", + r_bootstrap, r_bootstrap_len); + + i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, + &i_bootstrap_len); + if (!i_bootstrap || i_bootstrap > wrapped_data || + i_bootstrap_len != SHA256_MAC_LEN) { + wpa_printf(MSG_DEBUG, + "DPP: Missing or invalid required Initiator Bootstrapping Key Hash attribute"); + return; + } + wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", + i_bootstrap, i_bootstrap_len); + + /* Try to find own and peer bootstrapping key matches based on the + * received hash values */ + dl_list_for_each(bi, &wpa_s->dpp_bootstrap, struct dpp_bootstrap_info, + list) { + if (!own_bi && bi->own && + os_memcmp(bi->pubkey_hash, r_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching own bootstrapping information"); + own_bi = bi; + } + + if (!peer_bi && !bi->own && + os_memcmp(bi->pubkey_hash, i_bootstrap, + SHA256_MAC_LEN) == 0) { + wpa_printf(MSG_DEBUG, + "DPP: Found matching peer bootstrapping information"); + peer_bi = bi; + } + + if (own_bi && peer_bi) + break; + } + + if (!own_bi) { + wpa_printf(MSG_DEBUG, + "DPP: No matching own bootstrapping key found - ignore message"); + return; + } + + if (wpa_s->dpp_auth) { + wpa_printf(MSG_DEBUG, + "DPP: Already in DPP authentication exchange - ignore new one"); + return; + } + + wpa_s->dpp_auth = dpp_auth_req_rx(wpa_s, wpa_s->dpp_allowed_roles, + wpa_s->dpp_qr_mutual, + peer_bi, own_bi, freq, buf, + wrapped_data, wrapped_data_len); + if (!wpa_s->dpp_auth) { + wpa_printf(MSG_DEBUG, "DPP: No response generated"); + return; + } + os_memcpy(wpa_s->dpp_auth->peer_mac_addr, src, ETH_ALEN); + + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, + wpabuf_len(wpa_s->dpp_auth->resp_attr)); + if (!msg) + return; + wpabuf_put_buf(msg, wpa_s->dpp_auth->resp_attr); + + offchannel_send_action(wpa_s, wpa_s->dpp_auth->curr_freq, + src, wpa_s->own_addr, broadcast, + wpabuf_head(msg), wpabuf_len(msg), + 500, wpas_dpp_tx_status, 0); + wpabuf_free(msg); +} + + +static void wpas_dpp_rx_auth_resp(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + struct wpabuf *msg, *attr; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Response from " MACSTR, + MAC2STR(src)); + + if (!auth) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Authentication in progress - drop"); + return; + } + + if (!is_zero_ether_addr(auth->peer_mac_addr) && + os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + + attr = dpp_auth_resp_rx(auth, buf, len); + if (!attr) { + if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) { + wpa_printf(MSG_DEBUG, + "DPP: Start wait for full response"); + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_start(wpa_s, auth->curr_freq); + return; + } + wpa_printf(MSG_DEBUG, "DPP: No confirm generated"); + return; + } + os_memcpy(auth->peer_mac_addr, src, ETH_ALEN); + + msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, wpabuf_len(attr)); + if (!msg) { + wpabuf_free(attr); + return; + } + wpabuf_put_buf(msg, attr); + wpabuf_free(attr); + + offchannel_send_action(wpa_s, auth->curr_freq, + src, wpa_s->own_addr, broadcast, + wpabuf_head(msg), wpabuf_len(msg), + 500, wpas_dpp_tx_status, 0); + wpabuf_free(msg); + + wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=1"); +} + + +static void wpas_dpp_rx_auth_conf(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *buf, size_t len) +{ + struct dpp_authentication *auth = wpa_s->dpp_auth; + + wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation from " MACSTR, + MAC2STR(src)); + + if (!auth) { + wpa_printf(MSG_DEBUG, + "DPP: No DPP Authentication in progress - drop"); + return; + } + + if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected " + MACSTR ") - drop", MAC2STR(auth->peer_mac_addr)); + return; + } + + if (dpp_auth_conf_rx(auth, buf, len) < 0) { + wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); + return; + } + + wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); + wpa_msg(wpa_s, MSG_INFO, DPP_EVENT_AUTH_SUCCESS "init=0"); +} + + +void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *buf, size_t len, unsigned int freq) +{ + enum dpp_public_action_frame_type type; + + if (len < 1) + return; + type = buf[0]; + buf++; + len--; + + wpa_printf(MSG_DEBUG, + "DPP: Received DPP Public Action frame type %d from " + MACSTR " freq=%u", + type, MAC2STR(src), freq); + wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", buf, len); + if (dpp_check_attrs(buf, len) < 0) + return; + + switch (type) { + case DPP_PA_AUTHENTICATION_REQ: + wpas_dpp_rx_auth_req(wpa_s, src, buf, len, freq); + break; + case DPP_PA_AUTHENTICATION_RESP: + wpas_dpp_rx_auth_resp(wpa_s, src, buf, len); + break; + case DPP_PA_AUTHENTICATION_CONF: + wpas_dpp_rx_auth_conf(wpa_s, src, buf, len); + break; + default: + wpa_printf(MSG_DEBUG, + "DPP: Ignored unsupported frame subtype %d", type); + break; + } +} + + int wpas_dpp_init(struct wpa_supplicant *wpa_s) { dl_list_init(&wpa_s->dpp_bootstrap); @@ -227,5 +810,10 @@ void wpas_dpp_deinit(struct wpa_supplicant *wpa_s) { if (!wpa_s->dpp_init_done) return; + eloop_cancel_timeout(wpas_dpp_reply_wait_timeout, wpa_s, NULL); + offchannel_send_action_done(wpa_s); + wpas_dpp_listen_stop(wpa_s); dpp_bootstrap_del(wpa_s, 0); + dpp_auth_deinit(wpa_s->dpp_auth); + wpa_s->dpp_auth = NULL; } diff --git a/wpa_supplicant/dpp_supplicant.h b/wpa_supplicant/dpp_supplicant.h index 9ea888ce0..fc1e87808 100644 --- a/wpa_supplicant/dpp_supplicant.h +++ b/wpa_supplicant/dpp_supplicant.h @@ -14,6 +14,15 @@ 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_auth_init(struct wpa_supplicant *wpa_s, const char *cmd); +int wpas_dpp_listen(struct wpa_supplicant *wpa_s, const char *cmd); +void wpas_dpp_listen_stop(struct wpa_supplicant *wpa_s); +void wpas_dpp_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq); +void wpas_dpp_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s, + unsigned int freq); +void wpas_dpp_rx_action(struct wpa_supplicant *wpa_s, const u8 *src, + const u8 *buf, size_t len, unsigned int freq); int wpas_dpp_init(struct wpa_supplicant *wpa_s); void wpas_dpp_deinit(struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 3a2ec64e6..0bfd3b905 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -28,6 +28,7 @@ #include "notify.h" #include "common/ieee802_11_defs.h" #include "common/ieee802_11_common.h" +#include "common/dpp.h" #include "crypto/random.h" #include "blacklist.h" #include "wpas_glue.h" @@ -46,6 +47,7 @@ #include "mesh.h" #include "mesh_mpm.h" #include "wmm_ac.h" +#include "dpp_supplicant.h" #ifndef CONFIG_NO_SCAN_PROCESSING @@ -3582,6 +3584,18 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_FST */ +#ifdef CONFIG_DPP + if (category == WLAN_ACTION_PUBLIC && plen >= 5 && + payload[0] == WLAN_PA_VENDOR_SPECIFIC && + WPA_GET_BE24(&payload[1]) == OUI_WFA && + payload[4] == DPP_OUI_TYPE) { + payload += 5; + plen -= 5; + wpas_dpp_rx_action(wpa_s, mgmt->sa, payload, plen, freq); + return; + } +#endif /* CONFIG_DPP */ + wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid, category, payload, plen, freq); if (wpa_s->ifmsh) @@ -4171,6 +4185,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_OFFCHANNEL */ wpas_p2p_cancel_remain_on_channel_cb( wpa_s, data->remain_on_channel.freq); +#ifdef CONFIG_DPP + wpas_dpp_cancel_remain_on_channel_cb( + wpa_s, data->remain_on_channel.freq); +#endif /* CONFIG_DPP */ break; case EVENT_EAPOL_RX: wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src, diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 504439298..53c5e793e 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1160,6 +1160,12 @@ struct wpa_supplicant { #ifdef CONFIG_DPP struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */ int dpp_init_done; + struct dpp_authentication *dpp_auth; + struct wpa_radio_work *dpp_listen_work; + unsigned int dpp_pending_listen_freq; + unsigned int dpp_listen_freq; + u8 dpp_allowed_roles; + int dpp_qr_mutual; #endif /* CONFIG_DPP */ };