]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - src/common/dpp.c
DPP: Add DPP_CONFIGURATOR_SIGN support to hostapd
[thirdparty/hostap.git] / src / common / dpp.c
index 3848524c939bf4f4eb5a680ad0e6ecc41ccc178e..74cdddac757d5cc0d7a0cacbc8ebdfdb001e9a26 100644 (file)
 
 #ifdef CONFIG_TESTING_OPTIONS
 enum dpp_test_behavior dpp_test = DPP_TEST_DISABLED;
+u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_ephemeral_key_override[600];
+size_t dpp_pkex_ephemeral_key_override_len = 0;
 
 static int dpp_test_gen_invalid_key(struct wpabuf *msg,
                                    const struct dpp_curve_params *curve);
@@ -282,6 +286,39 @@ static const u8 pkex_resp_y_bp_p512r1[64] = {
 };
 
 
+static void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+                                 const EC_POINT *point)
+{
+       BIGNUM *x, *y;
+       BN_CTX *ctx;
+       char *x_str = NULL, *y_str = NULL;
+
+       if (!wpa_debug_show_keys)
+               return;
+
+       ctx = BN_CTX_new();
+       x = BN_new();
+       y = BN_new();
+       if (!ctx || !x || !y ||
+           EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
+               goto fail;
+
+       x_str = BN_bn2hex(x);
+       y_str = BN_bn2hex(y);
+       if (!x_str || !y_str)
+               goto fail;
+
+       wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
+
+fail:
+       OPENSSL_free(x_str);
+       OPENSSL_free(y_str);
+       BN_free(x);
+       BN_free(y);
+       BN_CTX_free(ctx);
+}
+
+
 static int dpp_hash_vector(const struct dpp_curve_params *curve,
                           size_t num_elem, const u8 *addr[], const size_t *len,
                           u8 *mac)
@@ -442,6 +479,7 @@ static EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
                wpa_printf(MSG_ERROR, "DPP: Invalid point");
                goto fail;
        }
+       dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
 
        eckey = EC_KEY_new();
        if (!eckey ||
@@ -977,6 +1015,8 @@ static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
        int res;
        unsigned char *der = NULL;
        int der_len;
+       const EC_GROUP *group;
+       const EC_POINT *point;
 
        out = BIO_new(BIO_s_mem());
        if (!out)
@@ -999,6 +1039,11 @@ static void dpp_debug_print_key(const char *title, EVP_PKEY *key)
        if (!eckey)
                return;
 
+       group = EC_KEY_get0_group(eckey);
+       point = EC_KEY_get0_public_key(eckey);
+       if (group && point)
+               dpp_debug_print_point(title, group, point);
+
        der_len = i2d_ECPrivateKey(eckey, &der);
        if (der_len > 0)
                wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
@@ -1054,7 +1099,9 @@ static EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
        if (!pctx ||
            EVP_PKEY_paramgen_init(pctx) != 1 ||
            EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) != 1 ||
+#ifdef EVP_PKEY_CTX_set_ec_param_enc
            EVP_PKEY_CTX_set_ec_param_enc(pctx, OPENSSL_EC_NAMED_CURVE) != 1 ||
+#endif
            EVP_PKEY_paramgen(pctx, &params) != 1) {
                wpa_printf(MSG_ERROR,
                           "DPP: Failed to generate EVP_PKEY parameters");
@@ -1197,6 +1244,7 @@ static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
        point = EC_KEY_get0_public_key(eckey);
        if (!group || !point)
                goto fail;
+       dpp_debug_print_point("DPP: bootstrap public key", group, point);
        nid = EC_GROUP_get_curve_name(group);
 
        bootstrap = DPP_BOOTSTRAPPING_KEY_new();
@@ -1436,6 +1484,40 @@ static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
 }
 
 
+static void dpp_build_attr_status(struct wpabuf *msg,
+                                 enum dpp_status_error status)
+{
+       wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
+       wpabuf_put_le16(msg, DPP_ATTR_STATUS);
+       wpabuf_put_le16(msg, 1);
+       wpabuf_put_u8(msg, status);
+}
+
+
+static void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg,
+                                               const u8 *hash)
+{
+       if (hash) {
+               wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash");
+               wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
+               wpabuf_put_le16(msg, SHA256_MAC_LEN);
+               wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+       }
+}
+
+
+static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
+                                               const u8 *hash)
+{
+       if (hash) {
+               wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
+               wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+               wpabuf_put_le16(msg, SHA256_MAC_LEN);
+               wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+       }
+}
+
+
 static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
                                          const struct wpabuf *pi,
                                          size_t nonce_len,
@@ -1458,7 +1540,7 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
                attr_len += 4 + 2;
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
-               attr_len += 4;
+               attr_len += 5;
 #endif /* CONFIG_TESTING_OPTIONS */
        msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
        if (!msg)
@@ -1467,18 +1549,10 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
        attr_start = wpabuf_put(msg, 0);
 
        /* Responder Bootstrapping Key Hash */
-       if (r_pubkey_hash) {
-               wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
-               wpabuf_put_le16(msg, SHA256_MAC_LEN);
-               wpabuf_put_data(msg, r_pubkey_hash, SHA256_MAC_LEN);
-       }
+       dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
 
        /* Initiator Bootstrapping Key Hash */
-       if (i_pubkey_hash) {
-               wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
-               wpabuf_put_le16(msg, SHA256_MAC_LEN);
-               wpabuf_put_data(msg, i_pubkey_hash, SHA256_MAC_LEN);
-       }
+       dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
 
        /* Initiator Protocol Key */
        if (pi) {
@@ -1521,6 +1595,16 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
                wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
                goto skip_i_nonce;
        }
+       if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
+               WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+               pos += 2;
+               WPA_PUT_LE16(pos, nonce_len - 1);
+               pos += 2;
+               os_memcpy(pos, auth->i_nonce, nonce_len - 1);
+               pos += nonce_len - 1;
+               goto skip_i_nonce;
+       }
 #endif /* CONFIG_TESTING_OPTIONS */
 
        /* I-nonce */
@@ -1584,8 +1668,7 @@ skip_i_capab:
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
                wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
-               wpabuf_put_le16(msg, DPP_ATTR_TESTING);
-               wpabuf_put_le16(msg, 0);
+               dpp_build_attr_status(msg, DPP_STATUS_OK);
        }
 skip_wrapped_data:
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -1625,7 +1708,7 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
                4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
-               attr_len += 4;
+               attr_len += 5;
 #endif /* CONFIG_TESTING_OPTIONS */
        msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
        if (!msg)
@@ -1634,27 +1717,14 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
        attr_start = wpabuf_put(msg, 0);
 
        /* DPP Status */
-       if (status != 255) {
-               wpa_printf(MSG_DEBUG, "DPP: Status %d", status);
-               wpabuf_put_le16(msg, DPP_ATTR_STATUS);
-               wpabuf_put_le16(msg, 1);
-               wpabuf_put_u8(msg, status);
-       }
+       if (status != 255)
+               dpp_build_attr_status(msg, status);
 
        /* Responder Bootstrapping Key Hash */
-       if (r_pubkey_hash) {
-               wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
-               wpabuf_put_le16(msg, SHA256_MAC_LEN);
-               wpabuf_put_data(msg, r_pubkey_hash, SHA256_MAC_LEN);
-       }
+       dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
 
-       /* Initiator Bootstrapping Key Hash */
-       if (i_pubkey_hash) {
-               /* Mutual authentication */
-               wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
-               wpabuf_put_le16(msg, SHA256_MAC_LEN);
-               wpabuf_put_data(msg, i_pubkey_hash, SHA256_MAC_LEN);
-       }
+       /* Initiator Bootstrapping Key Hash (mutual authentication) */
+       dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
 
        /* Responder Protocol Key */
        if (pr) {
@@ -1771,8 +1841,7 @@ skip_r_capab:
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
-               wpabuf_put_le16(msg, DPP_ATTR_TESTING);
-               wpabuf_put_le16(msg, 0);
+               dpp_build_attr_status(msg, DPP_STATUS_OK);
        }
 skip_wrapped_data:
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -1941,6 +2010,45 @@ static int dpp_prepare_channel_list(struct dpp_authentication *auth,
 }
 
 
+static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
+{
+       struct dpp_bootstrap_info *bi;
+       char *pk = NULL;
+       size_t len;
+
+       if (auth->own_bi)
+               return 0; /* already generated */
+
+       bi = os_zalloc(sizeof(*bi));
+       if (!bi)
+               return -1;
+       bi->type = DPP_BOOTSTRAP_QR_CODE;
+       pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0);
+       if (!pk)
+               goto fail;
+
+       len = 4; /* "DPP:" */
+       len += 4 + os_strlen(pk);
+       bi->uri = os_malloc(len + 1);
+       if (!bi->uri)
+               goto fail;
+       os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk);
+       wpa_printf(MSG_DEBUG,
+                  "DPP: Auto-generated own bootstrapping key info: URI %s",
+                  bi->uri);
+
+       auth->tmp_own_bi = auth->own_bi = bi;
+
+       os_free(pk);
+
+       return 0;
+fail:
+       os_free(pk);
+       dpp_bootstrap_info_free(bi);
+       return -1;
+}
+
+
 struct dpp_authentication * dpp_auth_init(void *msg_ctx,
                                          struct dpp_bootstrap_info *peer_bi,
                                          struct dpp_bootstrap_info *own_bi,
@@ -1954,7 +2062,6 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx,
        EVP_PKEY_CTX *ctx = NULL;
        size_t secret_len;
        struct wpabuf *pi = NULL;
-       u8 zero[SHA256_MAC_LEN];
        const u8 *r_pubkey_hash, *i_pubkey_hash;
 #ifdef CONFIG_TESTING_OPTIONS
        u8 test_hash[SHA256_MAC_LEN];
@@ -1972,7 +2079,8 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx,
        auth->own_bi = own_bi;
        auth->curve = peer_bi->curve;
 
-       if (dpp_prepare_channel_list(auth, own_modes, num_modes) < 0)
+       if (dpp_autogen_bootstrap_key(auth) < 0 ||
+           dpp_prepare_channel_list(auth, own_modes, num_modes) < 0)
                goto fail;
 
        nonce_len = auth->curve->nonce_len;
@@ -2015,13 +2123,7 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx,
                goto fail;
 
        r_pubkey_hash = auth->peer_bi->pubkey_hash;
-
-       if (auth->own_bi) {
-               i_pubkey_hash = auth->own_bi->pubkey_hash;
-       } else {
-               os_memset(zero, 0, SHA256_MAC_LEN);
-               i_pubkey_hash = zero;
-       }
+       i_pubkey_hash = auth->own_bi->pubkey_hash;
 
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
@@ -2097,7 +2199,7 @@ struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
        attr_len = 4 + clear_len + AES_BLOCK_SIZE;
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ)
-               attr_len += 4;
+               attr_len += 5;
 #endif /* CONFIG_TESTING_OPTIONS */
        msg = wpabuf_alloc(attr_len);
        if (!clear || !msg)
@@ -2152,8 +2254,7 @@ skip_conf_attr_obj:
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) {
                wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
-               wpabuf_put_le16(msg, DPP_ATTR_TESTING);
-               wpabuf_put_le16(msg, 0);
+               dpp_build_attr_status(msg, DPP_STATUS_OK);
        }
 skip_wrapped_data:
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -2608,6 +2709,9 @@ static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
        } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
                status = 255;
+       } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+               status = 254;
        } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
                r_nonce = NULL;
@@ -2998,7 +3102,7 @@ static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
                4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
-               attr_len += 4;
+               attr_len += 5;
 #endif /* CONFIG_TESTING_OPTIONS */
        msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
        if (!msg)
@@ -3013,14 +3117,17 @@ static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
                i_pubkey_hash = NULL;
 
 #ifdef CONFIG_TESTING_OPTIONS
-       if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF)
+       if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
                goto skip_status;
+       } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+               status = 254;
+       }
 #endif /* CONFIG_TESTING_OPTIONS */
 
        /* DPP Status */
-       wpabuf_put_le16(msg, DPP_ATTR_STATUS);
-       wpabuf_put_le16(msg, 1);
-       wpabuf_put_u8(msg, status);
+       dpp_build_attr_status(msg, status);
 
 #ifdef CONFIG_TESTING_OPTIONS
 skip_status:
@@ -3051,19 +3158,10 @@ skip_status:
 #endif /* CONFIG_TESTING_OPTIONS */
 
        /* Responder Bootstrapping Key Hash */
-       if (r_pubkey_hash) {
-               wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
-               wpabuf_put_le16(msg, SHA256_MAC_LEN);
-               wpabuf_put_data(msg, r_pubkey_hash, SHA256_MAC_LEN);
-       }
+       dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
 
-       if (i_pubkey_hash) {
-               /* 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, i_pubkey_hash, SHA256_MAC_LEN);
-       }
+       /* Initiator Bootstrapping Key Hash (mutual authentication) */
+       dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
 
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
@@ -3136,8 +3234,7 @@ skip_i_auth:
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
                wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
-               wpabuf_put_le16(msg, DPP_ATTR_TESTING);
-               wpabuf_put_le16(msg, 0);
+               dpp_build_attr_status(msg, DPP_STATUS_OK);
        }
 skip_wrapped_data:
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -3243,7 +3340,9 @@ dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
                } else {
                        wpa_printf(MSG_DEBUG,
                                   "DPP: Continue waiting for full DPP Authentication Response");
-                       wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_RESPONSE_PENDING);
+                       wpa_msg(auth->msg_ctx, MSG_INFO,
+                               DPP_EVENT_RESPONSE_PENDING "%s",
+                               auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
                }
        }
 fail:
@@ -3649,7 +3748,7 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
 
        if (auth->initiator) {
                dpp_auth_fail(auth, "Unexpected Authentication Confirm");
-               return NULL;
+               return -1;
        }
 
        auth->waiting_auth_conf = 0;
@@ -3809,6 +3908,7 @@ void dpp_auth_deinit(struct dpp_authentication *auth)
        os_free(auth->connector);
        wpabuf_free(auth->net_access_key);
        wpabuf_free(auth->c_sign_key);
+       dpp_bootstrap_info_free(auth->tmp_own_bi);
 #ifdef CONFIG_TESTING_OPTIONS
        os_free(auth->config_obj_override);
        os_free(auth->discovery_override);
@@ -4096,7 +4196,7 @@ dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap,
        if (!buf)
                return NULL;
 
-       wpabuf_put_str(buf, "\"cred\":{\"akm\":\"psk\",");
+       wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm));
        if (conf->passphrase) {
                char pass[63 * 6 + 1];
 
@@ -4149,7 +4249,7 @@ dpp_build_conf_obj(struct dpp_authentication *auth, int ap)
                return NULL;
        }
 
-       if (conf->dpp)
+       if (conf->akm == DPP_AKM_DPP)
                return dpp_build_conf_obj_dpp(auth, ap, conf);
        return dpp_build_conf_obj_legacy(auth, ap, conf);
 }
@@ -4182,7 +4282,7 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
        attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE;
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP)
-               attr_len += 4;
+               attr_len += 5;
 #endif /* CONFIG_TESTING_OPTIONS */
        msg = wpabuf_alloc(attr_len);
        if (!clear || !msg)
@@ -4239,9 +4339,7 @@ skip_config_obj:
 #endif /* CONFIG_TESTING_OPTIONS */
 
        /* DPP Status */
-       wpabuf_put_le16(msg, DPP_ATTR_STATUS);
-       wpabuf_put_le16(msg, 1);
-       wpabuf_put_u8(msg, status);
+       dpp_build_attr_status(msg, status);
 
 #ifdef CONFIG_TESTING_OPTIONS
 skip_status:
@@ -4266,8 +4364,7 @@ skip_status:
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
-               wpabuf_put_le16(msg, DPP_ATTR_TESTING);
-               wpabuf_put_le16(msg, 0);
+               dpp_build_attr_status(msg, DPP_STATUS_OK);
        }
 skip_wrapped_data:
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -4504,6 +4601,11 @@ static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
                os_strlcpy(auth->passphrase, pass->string,
                           sizeof(auth->passphrase));
        } else if (psk_hex && psk_hex->type == JSON_STRING) {
+               if (auth->akm == DPP_AKM_SAE) {
+                       wpa_printf(MSG_DEBUG,
+                                  "DPP: Unexpected psk_hex with akm=sae");
+                       return -1;
+               }
                if (os_strlen(psk_hex->string) != PMK_LEN * 2 ||
                    hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) {
                        wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding");
@@ -4517,6 +4619,12 @@ static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
                return -1;
        }
 
+       if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) &&
+           !auth->passphrase[0]) {
+               wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
+               return -1;
+       }
+
        return 0;
 }
 
@@ -5079,6 +5187,37 @@ fail:
 }
 
 
+const char * dpp_akm_str(enum dpp_akm akm)
+{
+       switch (akm) {
+       case DPP_AKM_DPP:
+               return "dpp";
+       case DPP_AKM_PSK:
+               return "psk";
+       case DPP_AKM_SAE:
+               return "sae";
+       case DPP_AKM_PSK_SAE:
+               return "psk+sae";
+       default:
+               return "??";
+       }
+}
+
+
+static enum dpp_akm dpp_akm_from_str(const char *akm)
+{
+       if (os_strcmp(akm, "psk") == 0)
+               return DPP_AKM_PSK;
+       if (os_strcmp(akm, "sae") == 0)
+               return DPP_AKM_SAE;
+       if (os_strcmp(akm, "psk+sae") == 0)
+               return DPP_AKM_PSK_SAE;
+       if (os_strcmp(akm, "dpp") == 0)
+               return DPP_AKM_DPP;
+       return DPP_AKM_UNKNOWN;
+}
+
+
 static int dpp_parse_conf_obj(struct dpp_authentication *auth,
                              const u8 *conf_obj, u16 conf_obj_len)
 {
@@ -5136,10 +5275,13 @@ static int dpp_parse_conf_obj(struct dpp_authentication *auth,
                dpp_auth_fail(auth, "No cred::akm string value found");
                goto fail;
        }
-       if (os_strcmp(token->string, "psk") == 0) {
+       auth->akm = dpp_akm_from_str(token->string);
+
+       if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE ||
+           auth->akm == DPP_AKM_PSK_SAE) {
                if (dpp_parse_cred_legacy(auth, cred) < 0)
                        goto fail;
-       } else if (os_strcmp(token->string, "dpp") == 0) {
+       } else if (auth->akm == DPP_AKM_DPP) {
                if (dpp_parse_cred_dpp(auth, cred) < 0)
                        goto fail;
        } else {
@@ -5327,7 +5469,7 @@ fail:
 
 
 int dpp_configurator_own_config(struct dpp_authentication *auth,
-                               const char *curve)
+                               const char *curve, int ap)
 {
        struct wpabuf *conf_obj;
        int ret = -1;
@@ -5358,7 +5500,7 @@ int dpp_configurator_own_config(struct dpp_authentication *auth,
        auth->peer_protocol_key = auth->own_protocol_key;
        dpp_copy_csign(auth, auth->conf->csign);
 
-       conf_obj = dpp_build_conf_obj(auth, 0);
+       conf_obj = dpp_build_conf_obj(auth, ap);
        if (!conf_obj)
                goto fail;
        ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj),
@@ -5813,6 +5955,7 @@ static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
                wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
                goto fail;
        }
+       dpp_debug_print_point("DPP: Qi", group, Qi);
 out:
        EC_KEY_free(Pi_ec);
        EVP_PKEY_free(Pi);
@@ -5894,6 +6037,7 @@ static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
                wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
                goto fail;
        }
+       dpp_debug_print_point("DPP: Qr", group, Qr);
 out:
        EC_KEY_free(Pr_ec);
        EVP_PKEY_free(Pr);
@@ -5998,7 +6142,21 @@ static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
                goto fail;
 
        /* Generate a random ephemeral keypair x/X */
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_pkex_ephemeral_key_override_len) {
+               const struct dpp_curve_params *tmp_curve;
+
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - override ephemeral key x/X");
+               pkex->x = dpp_set_keypair(&tmp_curve,
+                                         dpp_pkex_ephemeral_key_override,
+                                         dpp_pkex_ephemeral_key_override_len);
+       } else {
+               pkex->x = dpp_gen_keypair(curve);
+       }
+#else /* CONFIG_TESTING_OPTIONS */
        pkex->x = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
        if (!pkex->x)
                goto fail;
 
@@ -6009,6 +6167,7 @@ static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
        X_point = EC_KEY_get0_public_key(X_ec);
        if (!X_point)
                goto fail;
+       dpp_debug_print_point("DPP: X", group, X_point);
        M = EC_POINT_new(group);
        Mx = BN_new();
        My = BN_new();
@@ -6016,6 +6175,7 @@ static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
            EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
            EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
                goto fail;
+       dpp_debug_print_point("DPP: M", group, M);
 
        /* Initiator -> Responder: group, [identifier,] M */
        attr_len = 4 + 2;
@@ -6106,6 +6266,14 @@ struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
 {
        struct dpp_pkex *pkex;
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+                          MAC2STR(dpp_pkex_own_mac_override));
+               own_mac = dpp_pkex_own_mac_override;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        pkex = os_zalloc(sizeof(*pkex));
        if (!pkex)
                return NULL;
@@ -6162,9 +6330,7 @@ dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
 #endif /* CONFIG_TESTING_OPTIONS */
 
        /* DPP Status */
-       wpabuf_put_le16(msg, DPP_ATTR_STATUS);
-       wpabuf_put_le16(msg, 1);
-       wpabuf_put_u8(msg, status);
+       dpp_build_attr_status(msg, status);
 
 #ifdef CONFIG_TESTING_OPTIONS
 skip_status:
@@ -6312,6 +6478,19 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
                return NULL;
        }
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+                          MAC2STR(dpp_pkex_peer_mac_override));
+               peer_mac = dpp_pkex_peer_mac_override;
+       }
+       if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+                          MAC2STR(dpp_pkex_own_mac_override));
+               own_mac = dpp_pkex_own_mac_override;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
                               &attr_id_len);
        if (!attr_id && identifier) {
@@ -6387,6 +6566,8 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
                bi->pkex_t++;
                goto fail;
        }
+       dpp_debug_print_point("DPP: M", group, M);
+       dpp_debug_print_point("DPP: X'", group, X);
 
        pkex = os_zalloc(sizeof(*pkex));
        if (!pkex)
@@ -6423,7 +6604,21 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
                goto fail;
 
        /* Generate a random ephemeral keypair y/Y */
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_pkex_ephemeral_key_override_len) {
+               const struct dpp_curve_params *tmp_curve;
+
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - override ephemeral key y/Y");
+               pkex->y = dpp_set_keypair(&tmp_curve,
+                                         dpp_pkex_ephemeral_key_override,
+                                         dpp_pkex_ephemeral_key_override_len);
+       } else {
+               pkex->y = dpp_gen_keypair(curve);
+       }
+#else /* CONFIG_TESTING_OPTIONS */
        pkex->y = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
        if (!pkex->y)
                goto fail;
 
@@ -6434,6 +6629,7 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
        Y_point = EC_KEY_get0_public_key(Y_ec);
        if (!Y_point)
                goto fail;
+       dpp_debug_print_point("DPP: Y", group, Y_point);
        N = EC_POINT_new(group);
        Nx = BN_new();
        Ny = BN_new();
@@ -6441,6 +6637,7 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
            EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
            EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
                goto fail;
+       dpp_debug_print_point("DPP: N", group, N);
 
        pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
                                                           Nx, Ny);
@@ -6518,7 +6715,7 @@ dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
        attr_len = 4 + clear_len + AES_BLOCK_SIZE;
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
-               attr_len += 4;
+               attr_len += 5;
 #endif /* CONFIG_TESTING_OPTIONS */
        msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
        if (!clear || !msg)
@@ -6596,8 +6793,7 @@ skip_i_auth_tag:
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
                wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
-               wpabuf_put_le16(msg, DPP_ATTR_TESTING);
-               wpabuf_put_le16(msg, 0);
+               dpp_build_attr_status(msg, DPP_STATUS_OK);
        }
 skip_wrapped_data:
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -6614,6 +6810,7 @@ fail:
 
 
 struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+                                         const u8 *peer_mac,
                                          const u8 *buf, size_t buflen)
 {
        const u8 *attr_status, *attr_id, *attr_key, *attr_group;
@@ -6636,6 +6833,16 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
        if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
                return NULL;
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+                          MAC2STR(dpp_pkex_peer_mac_override));
+               peer_mac = dpp_pkex_peer_mac_override;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+
        attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
                                   &attr_status_len);
        if (!attr_status || attr_status_len != 1) {
@@ -6709,6 +6916,8 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
                pkex->t++;
                goto fail;
        }
+       dpp_debug_print_point("DPP: N", group, N);
+       dpp_debug_print_point("DPP: Y'", group, Y);
 
        pkex->exchange_done = 1;
 
@@ -6827,7 +7036,7 @@ dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
        attr_len = 4 + clear_len + AES_BLOCK_SIZE;
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
-               attr_len += 4;
+               attr_len += 5;
 #endif /* CONFIG_TESTING_OPTIONS */
        msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
        if (!clear || !msg)
@@ -6905,8 +7114,7 @@ skip_r_auth_tag:
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
-               wpabuf_put_le16(msg, DPP_ATTR_TESTING);
-               wpabuf_put_le16(msg, 0);
+               dpp_build_attr_status(msg, DPP_STATUS_OK);
        }
 skip_wrapped_data:
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -7251,3 +7459,56 @@ void dpp_pkex_free(struct dpp_pkex *pkex)
        wpabuf_free(pkex->exchange_resp);
        os_free(pkex);
 }
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+char * dpp_corrupt_connector_signature(const char *connector)
+{
+       char *tmp, *pos, *signed3 = NULL;
+       unsigned char *signature = NULL;
+       size_t signature_len = 0, signed3_len;
+
+       tmp = os_zalloc(os_strlen(connector) + 5);
+       if (!tmp)
+               goto fail;
+       os_memcpy(tmp, connector, os_strlen(connector));
+
+       pos = os_strchr(tmp, '.');
+       if (!pos)
+               goto fail;
+
+       pos = os_strchr(pos + 1, '.');
+       if (!pos)
+               goto fail;
+       pos++;
+
+       wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
+                  pos);
+       signature = base64_url_decode((const unsigned char *) pos,
+                                     os_strlen(pos), &signature_len);
+       if (!signature || signature_len == 0)
+               goto fail;
+       wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
+                   signature, signature_len);
+       signature[signature_len - 1] ^= 0x01;
+       wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
+                   signature, signature_len);
+       signed3 = (char *) base64_url_encode(signature, signature_len,
+                                            &signed3_len, 0);
+       if (!signed3)
+               goto fail;
+       os_memcpy(pos, signed3, signed3_len);
+       pos[signed3_len] = '\0';
+       wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
+                  pos);
+
+out:
+       os_free(signature);
+       os_free(signed3);
+       return tmp;
+fail:
+       os_free(tmp);
+       tmp = NULL;
+       goto out;
+}
+#endif /* CONFIG_TESTING_OPTIONS */