|
@@ -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));
|
|
|
+}
|