]> 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 e1a3583b29cb993cef23f17c1bf18fbeeca84d0d..74cdddac757d5cc0d7a0cacbc8ebdfdb001e9a26 100644 (file)
@@ -9,6 +9,8 @@
 #include "utils/includes.h"
 #include <openssl/opensslv.h>
 #include <openssl/err.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
 
 #include "utils/common.h"
 #include "utils/base64.h"
 #include "crypto/aes_siv.h"
 #include "crypto/sha384.h"
 #include "crypto/sha512.h"
+#include "drivers/driver.h"
 #include "dpp.h"
 
 
 #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);
 #endif /* CONFIG_TESTING_OPTIONS */
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
 /* Compatibility wrappers for older versions. */
 
 static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
@@ -276,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)
@@ -339,6 +382,20 @@ static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
 }
 
 
+static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
+{
+       int num_bytes, offset;
+
+       num_bytes = BN_num_bytes(bn);
+       if ((size_t) num_bytes > len)
+               return -1;
+       offset = len - num_bytes;
+       os_memset(pos, 0, offset);
+       BN_bn2bin(bn, pos + offset);
+       return 0;
+}
+
+
 static struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
 {
        int len, res;
@@ -422,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 ||
@@ -749,7 +807,7 @@ static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
        const unsigned char *pk;
        int ppklen;
        X509_ALGOR *pa;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
        ASN1_OBJECT *pa_oid;
 #else
        const ASN1_OBJECT *pa_oid;
@@ -957,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)
@@ -979,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);
@@ -1034,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");
@@ -1139,34 +1206,111 @@ static EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
 }
 
 
-int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+typedef struct {
+       /* AlgorithmIdentifier ecPublicKey with optional parameters present
+        * as an OID identifying the curve */
+       X509_ALGOR *alg;
+       /* Compressed format public key per ANSI X9.63 */
+       ASN1_BIT_STRING *pub_key;
+} DPP_BOOTSTRAPPING_KEY;
+
+ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
+       ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
+       ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
+
+IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
+
+
+static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
 {
        unsigned char *der = NULL;
        int der_len;
        EC_KEY *eckey;
-       int res;
+       struct wpabuf *ret = NULL;
        size_t len;
+       const EC_GROUP *group;
+       const EC_POINT *point;
+       BN_CTX *ctx;
+       DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
+       int nid;
 
-       /* Need to get the compressed form of the public key through EC_KEY, so
-        * cannot use the simpler i2d_PUBKEY() here. */
-       eckey = EVP_PKEY_get1_EC_KEY(bi->pubkey);
-       if (!eckey)
-               return -1;
-       EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
-       der_len = i2d_EC_PUBKEY(eckey, &der);
-       EC_KEY_free(eckey);
+       ctx = BN_CTX_new();
+       eckey = EVP_PKEY_get1_EC_KEY(key);
+       if (!ctx || !eckey)
+               goto fail;
+
+       group = EC_KEY_get0_group(eckey);
+       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();
+       if (!bootstrap ||
+           X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
+                           V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
+               goto fail;
+
+       len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+                                NULL, 0, ctx);
+       if (len == 0)
+               goto fail;
+
+       der = OPENSSL_malloc(len);
+       if (!der)
+               goto fail;
+       len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+                                der, len, ctx);
+
+       OPENSSL_free(bootstrap->pub_key->data);
+       bootstrap->pub_key->data = der;
+       der = NULL;
+       bootstrap->pub_key->length = len;
+       /* No unused bits */
+       bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+       bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+       der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
        if (der_len <= 0) {
                wpa_printf(MSG_ERROR,
                           "DDP: Failed to build DER encoded public key");
-               OPENSSL_free(der);
-               return -1;
+               goto fail;
        }
 
-       len = der_len;
-       res = sha256_vector(1, (const u8 **) &der, &len, bi->pubkey_hash);
+       ret = wpabuf_alloc_copy(der, der_len);
+fail:
+       DPP_BOOTSTRAPPING_KEY_free(bootstrap);
        OPENSSL_free(der);
+       EC_KEY_free(eckey);
+       BN_CTX_free(ctx);
+       return ret;
+}
+
+
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+{
+       struct wpabuf *der;
+       int res;
+       const u8 *addr[1];
+       size_t len[1];
+
+       der = dpp_bootstrap_key_der(bi->pubkey);
+       if (!der)
+               return -1;
+       wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+                       der);
+
+       addr[0] = wpabuf_head(der);
+       len[0] = wpabuf_len(der);
+       res = sha256_vector(1, addr, len, bi->pubkey_hash);
        if (res < 0)
                wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+       else
+               wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
+                           SHA256_MAC_LEN);
+       wpabuf_free(der);
        return res;
 }
 
@@ -1177,9 +1321,9 @@ char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
        unsigned char *base64 = NULL;
        char *pos, *end;
        size_t len;
-       unsigned char *der = NULL;
-       int der_len;
-       EC_KEY *eckey;
+       struct wpabuf *der = NULL;
+       const u8 *addr[1];
+       int res;
 
        if (!curve) {
                bi->curve = &dpp_curves[0];
@@ -1199,28 +1343,23 @@ char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
                goto fail;
        bi->own = 1;
 
-       /* Need to get the compressed form of the public key through EC_KEY, so
-        * cannot use the simpler i2d_PUBKEY() here. */
-       eckey = EVP_PKEY_get1_EC_KEY(bi->pubkey);
-       if (!eckey)
-               goto fail;
-       EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED);
-       der_len = i2d_EC_PUBKEY(eckey, &der);
-       EC_KEY_free(eckey);
-       if (der_len <= 0) {
-               wpa_printf(MSG_ERROR,
-                          "DDP: Failed to build DER encoded public key");
+       der = dpp_bootstrap_key_der(bi->pubkey);
+       if (!der)
                goto fail;
-       }
+       wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+                       der);
 
-       len = der_len;
-       if (sha256_vector(1, (const u8 **) &der, &len, bi->pubkey_hash) < 0) {
+       addr[0] = wpabuf_head(der);
+       len = wpabuf_len(der);
+       res = sha256_vector(1, addr, &len, bi->pubkey_hash);
+       if (res < 0)
                wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
-               goto fail;
-       }
+       else
+               wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash,
+                           SHA256_MAC_LEN);
 
-       base64 = base64_encode(der, der_len, &len);
-       OPENSSL_free(der);
+       base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
+       wpabuf_free(der);
        der = NULL;
        if (!base64)
                goto fail;
@@ -1235,7 +1374,7 @@ char * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
        return (char *) base64;
 fail:
        os_free(base64);
-       OPENSSL_free(der);
+       wpabuf_free(der);
        return NULL;
 }
 
@@ -1345,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,
@@ -1367,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)
@@ -1376,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) {
@@ -1430,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 */
@@ -1453,8 +1628,7 @@ skip_i_nonce:
        pos += 2;
        WPA_PUT_LE16(pos, 1);
        pos += 2;
-       auth->i_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
-               DPP_CAPAB_ENROLLEE;
+       auth->i_capab = auth->allowed_roles;
        *pos++ = auth->i_capab;
 #ifdef CONFIG_TESTING_OPTIONS
        if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
@@ -1494,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 */
@@ -1527,42 +1700,31 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
        size_t len[2], siv_len, attr_len;
        u8 *attr_start, *attr_end, *pos;
 
+       auth->waiting_auth_conf = 1;
+       auth->auth_resp_tries = 0;
+
        /* Build DPP Authentication Response frame attributes */
        attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
                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)
                return NULL;
-       wpabuf_free(auth->resp_msg);
 
        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) {
@@ -1631,8 +1793,12 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
        } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
                wpa_printf(MSG_INFO,
                           "DPP: TESTING - incompatible R-capabilities");
-               pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
-                       DPP_CAPAB_CONFIGURATOR;
+               if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
+                   (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
+                       pos[-1] = 0;
+               else
+                       pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
+                               DPP_CAPAB_CONFIGURATOR;
        }
 skip_r_capab:
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -1675,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 */
@@ -1687,30 +1852,237 @@ skip_wrapped_data:
 }
 
 
+static int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes,
+                              u16 num_modes, unsigned int freq)
+{
+       u16 m;
+       int c, flag;
+
+       if (!own_modes || !num_modes)
+               return 1;
+
+       for (m = 0; m < num_modes; m++) {
+               for (c = 0; c < own_modes[m].num_channels; c++) {
+                       if ((unsigned int) own_modes[m].channels[c].freq !=
+                           freq)
+                               continue;
+                       flag = own_modes[m].channels[c].flag;
+                       if (!(flag & (HOSTAPD_CHAN_DISABLED |
+                                     HOSTAPD_CHAN_NO_IR |
+                                     HOSTAPD_CHAN_RADAR)))
+                               return 1;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq);
+       return 0;
+}
+
+
+static int freq_included(const unsigned int freqs[], unsigned int num,
+                        unsigned int freq)
+{
+       while (num > 0) {
+               if (freqs[--num] == freq)
+                       return 1;
+       }
+       return 0;
+}
+
+
+static void freq_to_start(unsigned int freqs[], unsigned int num,
+                         unsigned int freq)
+{
+       unsigned int i;
+
+       for (i = 0; i < num; i++) {
+               if (freqs[i] == freq)
+                       break;
+       }
+       if (i == 0 || i >= num)
+               return;
+       os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0]));
+       freqs[0] = freq;
+}
+
+
+static int dpp_channel_intersect(struct dpp_authentication *auth,
+                                struct hostapd_hw_modes *own_modes,
+                                u16 num_modes)
+{
+       struct dpp_bootstrap_info *peer_bi = auth->peer_bi;
+       unsigned int i, freq;
+
+       for (i = 0; i < peer_bi->num_freq; i++) {
+               freq = peer_bi->freq[i];
+               if (freq_included(auth->freq, auth->num_freq, freq))
+                       continue;
+               if (dpp_channel_ok_init(own_modes, num_modes, freq))
+                       auth->freq[auth->num_freq++] = freq;
+       }
+       if (!auth->num_freq) {
+               wpa_printf(MSG_INFO,
+                          "DPP: No available channels for initiating DPP Authentication");
+               return -1;
+       }
+       auth->curr_freq = auth->freq[0];
+       return 0;
+}
+
+
+static int dpp_channel_local_list(struct dpp_authentication *auth,
+                                 struct hostapd_hw_modes *own_modes,
+                                 u16 num_modes)
+{
+       u16 m;
+       int c, flag;
+       unsigned int freq;
+
+       auth->num_freq = 0;
+
+       if (!own_modes || !num_modes) {
+               auth->freq[0] = 2412;
+               auth->freq[1] = 2437;
+               auth->freq[2] = 2462;
+               auth->num_freq = 3;
+               return 0;
+       }
+
+       for (m = 0; m < num_modes; m++) {
+               for (c = 0; c < own_modes[m].num_channels; c++) {
+                       freq = own_modes[m].channels[c].freq;
+                       flag = own_modes[m].channels[c].flag;
+                       if (flag & (HOSTAPD_CHAN_DISABLED |
+                                   HOSTAPD_CHAN_NO_IR |
+                                   HOSTAPD_CHAN_RADAR))
+                               continue;
+                       if (freq_included(auth->freq, auth->num_freq, freq))
+                               continue;
+                       auth->freq[auth->num_freq++] = freq;
+                       if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) {
+                               m = num_modes;
+                               break;
+                       }
+               }
+       }
+
+       return auth->num_freq == 0 ? -1 : 0;
+}
+
+
+static int dpp_prepare_channel_list(struct dpp_authentication *auth,
+                                   struct hostapd_hw_modes *own_modes,
+                                   u16 num_modes)
+{
+       int res;
+       char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end;
+       unsigned int i;
+
+       if (auth->peer_bi->num_freq > 0)
+               res = dpp_channel_intersect(auth, own_modes, num_modes);
+       else
+               res = dpp_channel_local_list(auth, own_modes, num_modes);
+       if (res < 0)
+               return res;
+
+       /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most
+        * likely channels first. */
+       freq_to_start(auth->freq, auth->num_freq, 2462);
+       freq_to_start(auth->freq, auth->num_freq, 2412);
+       freq_to_start(auth->freq, auth->num_freq, 2437);
+
+       auth->freq_idx = 0;
+       auth->curr_freq = auth->freq[0];
+
+       pos = freqs;
+       end = pos + sizeof(freqs);
+       for (i = 0; i < auth->num_freq; i++) {
+               res = os_snprintf(pos, end - pos, " %u", auth->freq[i]);
+               if (os_snprintf_error(end - pos, res))
+                       break;
+               pos += res;
+       }
+       *pos = '\0';
+       wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s",
+                  freqs);
+
+       return 0;
+}
+
+
+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,
-                                         int configurator,
-                                         unsigned int neg_freq)
+                                         u8 dpp_allowed_roles,
+                                         unsigned int neg_freq,
+                                         struct hostapd_hw_modes *own_modes,
+                                         u16 num_modes)
 {
        struct dpp_authentication *auth;
        size_t nonce_len;
        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];
+#endif /* CONFIG_TESTING_OPTIONS */
 
        auth = os_zalloc(sizeof(*auth));
        if (!auth)
                return NULL;
        auth->msg_ctx = msg_ctx;
        auth->initiator = 1;
-       auth->configurator = configurator;
+       auth->waiting_auth_resp = 1;
+       auth->allowed_roles = dpp_allowed_roles;
+       auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
        auth->peer_bi = peer_bi;
        auth->own_bi = own_bi;
        auth->curve = peer_bi->curve;
 
+       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;
        if (random_get_bytes(auth->i_nonce, nonce_len)) {
                wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
@@ -1751,25 +2123,37 @@ 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) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
                r_pubkey_hash = NULL;
+       } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - invalid R-Bootstrap Key Hash");
+               os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+               test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+               r_pubkey_hash = test_hash;
        } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
                i_pubkey_hash = NULL;
+       } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - invalid I-Bootstrap Key Hash");
+               os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+               test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+               i_pubkey_hash = test_hash;
        } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
                wpabuf_free(pi);
                pi = NULL;
+       } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
+               wpabuf_free(pi);
+               pi = wpabuf_alloc(2 * auth->curve->prime_len);
+               if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
+                       goto fail;
        }
 #endif /* CONFIG_TESTING_OPTIONS */
 
@@ -1815,22 +2199,45 @@ 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)
                goto fail;
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
+               goto skip_e_nonce;
+       }
+       if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+               goto skip_wrapped_data;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        /* E-nonce */
        wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
        wpabuf_put_le16(clear, nonce_len);
        wpabuf_put_data(clear, auth->e_nonce, nonce_len);
 
+#ifdef CONFIG_TESTING_OPTIONS
+skip_e_nonce:
+       if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib");
+               goto skip_conf_attr_obj;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        /* configAttrib */
        wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ);
        wpabuf_put_le16(clear, json_len);
        wpabuf_put_data(clear, json, json_len);
 
+#ifdef CONFIG_TESTING_OPTIONS
+skip_conf_attr_obj:
+#endif /* CONFIG_TESTING_OPTIONS */
+
        wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
        wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
        wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
@@ -1847,9 +2254,9 @@ struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
 #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 */
 
        wpa_hexdump_buf(MSG_DEBUG,
@@ -2050,7 +2457,6 @@ static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
        BIGNUM *lx, *sum, *q;
        const BIGNUM *bR_bn, *pR_bn;
        int ret = -1;
-       int num_bytes, offset;
 
        /* L = ((bR + pR) modulo q) * BI */
 
@@ -2090,16 +2496,8 @@ static int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
                goto fail;
        }
 
-       num_bytes = BN_num_bytes(lx);
-       if ((size_t) num_bytes > auth->secret_len)
+       if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
                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:
@@ -2125,7 +2523,6 @@ static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
        BIGNUM *lx;
        const BIGNUM *bI_bn;
        int ret = -1;
-       int num_bytes, offset;
 
        /* L = bI * (BR + PR) */
 
@@ -2160,16 +2557,8 @@ static int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
                goto fail;
        }
 
-       num_bytes = BN_num_bytes(lx);
-       if ((size_t) num_bytes > auth->secret_len)
+       if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
                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:
@@ -2195,8 +2584,13 @@ static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
        int ret = -1;
        const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
        enum dpp_status_error status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+       u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
 
        wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+       if (!auth->own_bi)
+               return -1;
 
        nonce_len = auth->curve->nonce_len;
        if (random_get_bytes(auth->r_nonce, nonce_len)) {
@@ -2278,13 +2672,36 @@ static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
        if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
                r_pubkey_hash = NULL;
+       } else if (dpp_test ==
+                  DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - invalid R-Bootstrap Key Hash");
+               os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+               test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+               r_pubkey_hash = test_hash;
        } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
                i_pubkey_hash = NULL;
+       } else if (dpp_test ==
+                  DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - invalid I-Bootstrap Key Hash");
+               if (i_pubkey_hash)
+                       os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+               else
+                       os_memset(test_hash, 0, SHA256_MAC_LEN);
+               test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+               i_pubkey_hash = test_hash;
        } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
                wpabuf_free(pr);
                pr = NULL;
+       } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
+               wpabuf_free(pr);
+               pr = wpabuf_alloc(2 * auth->curve->prime_len);
+               if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
+                       goto fail;
        } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
                w_r_auth = NULL;
@@ -2292,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;
@@ -2308,6 +2728,7 @@ static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
                                  auth->k2);
        if (!msg)
                goto fail;
+       wpabuf_free(auth->resp_msg);
        auth->resp_msg = msg;
        ret = 0;
 fail:
@@ -2321,7 +2742,12 @@ static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
 {
        struct wpabuf *msg;
        const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
+#ifdef CONFIG_TESTING_OPTIONS
+       u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
 
+       if (!auth->own_bi)
+               return -1;
        wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
 
        r_pubkey_hash = auth->own_bi->pubkey_hash;
@@ -2336,9 +2762,26 @@ static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
        if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
                r_pubkey_hash = NULL;
+       } else if (dpp_test ==
+                  DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - invalid R-Bootstrap Key Hash");
+               os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+               test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+               r_pubkey_hash = test_hash;
        } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
                i_pubkey_hash = NULL;
+       } else if (dpp_test ==
+                  DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - invalid I-Bootstrap Key Hash");
+               if (i_pubkey_hash)
+                       os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+               else
+                       os_memset(test_hash, 0, SHA256_MAC_LEN);
+               test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+               i_pubkey_hash = test_hash;
        } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
                wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
                status = -1;
@@ -2353,6 +2796,7 @@ static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
                                  NULL, i_nonce, NULL, 0, auth->k1);
        if (!msg)
                return -1;
+       wpabuf_free(auth->resp_msg);
        auth->resp_msg = msg;
        return 0;
 }
@@ -2535,6 +2979,19 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
                wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
                auth->configurator = 0;
                break;
+       case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
+               if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
+                       wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+                       auth->configurator = 0;
+               } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
+                       wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+                       auth->configurator = 1;
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "DPP: Local policy does not allow Configurator/Enrollee role");
+                       goto not_compatible;
+               }
+               break;
        default:
                wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
                wpa_msg(auth->msg_ctx, MSG_INFO,
@@ -2631,6 +3088,10 @@ static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
        u8 *wrapped_i_auth;
        u8 *wrapped_r_nonce;
        u8 *attr_start, *attr_end;
+       const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+       u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
 
        wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
 
@@ -2641,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)
@@ -2649,43 +3110,60 @@ static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
 
        attr_start = wpabuf_put(msg, 0);
 
+       r_pubkey_hash = auth->peer_bi->pubkey_hash;
+       if (auth->own_bi)
+               i_pubkey_hash = auth->own_bi->pubkey_hash;
+       else
+               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:
-       if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF)
-               goto skip_r_bootstrap_key;
+       if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+               r_pubkey_hash = NULL;
+       } else if (dpp_test ==
+                  DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - invalid R-Bootstrap Key Hash");
+               os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+               test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+               r_pubkey_hash = test_hash;
+       } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+               i_pubkey_hash = NULL;
+       } else if (dpp_test ==
+                  DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - invalid I-Bootstrap Key Hash");
+               if (i_pubkey_hash)
+                       os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+               else
+                       os_memset(test_hash, 0, SHA256_MAC_LEN);
+               test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+               i_pubkey_hash = test_hash;
+       }
 #endif /* CONFIG_TESTING_OPTIONS */
 
        /* 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);
-
-#ifdef CONFIG_TESTING_OPTIONS
-skip_r_bootstrap_key:
-       if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF)
-               goto skip_i_bootstrap_key;
-#endif /* CONFIG_TESTING_OPTIONS */
+       dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
 
-       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);
-       }
+       /* Initiator Bootstrapping Key Hash (mutual authentication) */
+       dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
 
 #ifdef CONFIG_TESTING_OPTIONS
-skip_i_bootstrap_key:
        if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
                goto skip_wrapped_data;
        if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
@@ -2756,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 */
@@ -2863,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:
@@ -2890,6 +3369,13 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
        u8 r_auth2[DPP_MAX_HASH_LEN];
        u8 role;
 
+       if (!auth->initiator) {
+               dpp_auth_fail(auth, "Unexpected Authentication Response");
+               return NULL;
+       }
+
+       auth->waiting_auth_resp = 0;
+
        wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
                                    &wrapped_data_len);
        if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
@@ -3076,8 +3562,16 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
        auth->r_capab = r_capab[0];
        wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
        role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
-       if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
-           (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+       if ((auth->allowed_roles ==
+            (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
+           (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
+               /* Peer selected its role, so move from "either role" to the
+                * role that is compatible with peer's selection. */
+               auth->configurator = role == DPP_CAPAB_ENROLLEE;
+               wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
+                          auth->configurator ? "Configurator" : "Enrollee");
+       } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+                  (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
                wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
                wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
                        "Unexpected role in R-capabilities 0x%02x",
@@ -3148,6 +3642,16 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
        bin_clear_free(unwrapped, unwrapped_len);
        bin_clear_free(unwrapped2, unwrapped2_len);
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - Authentication Response in place of Confirm");
+               if (dpp_auth_build_resp_ok(auth) < 0)
+                       return NULL;
+               return wpabuf_dup(auth->resp_msg);
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        return dpp_auth_build_conf(auth, DPP_STATUS_OK);
 
 fail:
@@ -3242,6 +3746,13 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
        size_t unwrapped_len = 0;
        u8 i_auth2[DPP_MAX_HASH_LEN];
 
+       if (auth->initiator) {
+               dpp_auth_fail(auth, "Unexpected Authentication Confirm");
+               return -1;
+       }
+
+       auth->waiting_auth_conf = 0;
+
        wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
                                    &wrapped_data_len);
        if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
@@ -3397,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);
@@ -3435,28 +3947,12 @@ dpp_build_conf_start(struct dpp_authentication *auth,
        json_escape_string(ssid, sizeof(ssid),
                           (const char *) conf->ssid, conf->ssid_len);
        wpabuf_put_str(buf, ssid);
-       wpabuf_put_str(buf, "\"");
-       /* TODO: optional channel information */
-       wpabuf_put_str(buf, "},");
+       wpabuf_put_str(buf, "\"},");
 
        return buf;
 }
 
 
-static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
-{
-       int num_bytes, offset;
-
-       num_bytes = BN_num_bytes(bn);
-       if ((size_t) num_bytes > len)
-               return -1;
-       offset = len - num_bytes;
-       os_memset(pos, 0, offset);
-       BN_bn2bin(bn, pos + offset);
-       return 0;
-}
-
-
 static int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
                         const char *kid, const struct dpp_curve_params *curve)
 {
@@ -3700,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];
 
@@ -3753,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);
 }
@@ -3786,29 +4282,68 @@ 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)
                goto fail;
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce");
+               goto skip_e_nonce;
+       }
+       if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch");
+               wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+               wpabuf_put_le16(clear, e_nonce_len);
+               wpabuf_put_data(clear, e_nonce, e_nonce_len - 1);
+               wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01);
+               goto skip_e_nonce;
+       }
+       if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+               goto skip_wrapped_data;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        /* E-nonce */
        wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
        wpabuf_put_le16(clear, e_nonce_len);
        wpabuf_put_data(clear, e_nonce, e_nonce_len);
 
+#ifdef CONFIG_TESTING_OPTIONS
+skip_e_nonce:
+       if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - Config Object");
+               goto skip_config_obj;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        if (conf) {
                wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ);
                wpabuf_put_le16(clear, wpabuf_len(conf));
                wpabuf_put_buf(clear, conf);
-               wpabuf_free(conf);
-               conf = NULL;
        }
 
+#ifdef CONFIG_TESTING_OPTIONS
+skip_config_obj:
+       if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - Status");
+               goto skip_status;
+       }
+       if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+               status = 255;
+       }
+#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:
+#endif /* CONFIG_TESTING_OPTIONS */
 
        addr[0] = wpabuf_head(msg);
        len[0] = wpabuf_len(msg);
@@ -3825,25 +4360,26 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
                goto fail;
        wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
                    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
-       wpabuf_free(clear);
-       clear = NULL;
 
 #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 */
 
        wpa_hexdump_buf(MSG_DEBUG,
                        "DPP: Configuration Response attributes", msg);
-       return msg;
-fail:
+out:
        wpabuf_free(conf);
        wpabuf_free(clear);
+
+       return msg;
+fail:
        wpabuf_free(msg);
-       return NULL;
+       msg = NULL;
+       goto out;
 }
 
 
@@ -3860,16 +4396,15 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
        int ap;
 
        if (dpp_check_attrs(attr_start, attr_len) < 0) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Invalid attribute in config request");
+               dpp_auth_fail(auth, "Invalid attribute in config request");
                return NULL;
        }
 
        wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
                                    &wrapped_data_len);
        if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid required Wrapped data attribute");
+               dpp_auth_fail(auth,
+                             "Missing or invalid required Wrapped Data attribute");
                return NULL;
        }
 
@@ -3882,15 +4417,14 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
        if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
                            wrapped_data, wrapped_data_len,
                            0, NULL, NULL, unwrapped) < 0) {
-               wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+               dpp_auth_fail(auth, "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");
+               dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
                goto fail;
        }
 
@@ -3898,8 +4432,8 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
                               DPP_ATTR_ENROLLEE_NONCE,
                               &e_nonce_len);
        if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid Enrollee Nonce attribute");
+               dpp_auth_fail(auth,
+                             "Missing or invalid Enrollee Nonce attribute");
                goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
@@ -3908,8 +4442,8 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
                                   DPP_ATTR_CONFIG_ATTR_OBJ,
                                   &config_attr_len);
        if (!config_attr) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid Config Attributes attribute");
+               dpp_auth_fail(auth,
+                             "Missing or invalid Config Attributes attribute");
                goto fail;
        }
        wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes",
@@ -3917,32 +4451,33 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
 
        root = json_parse((const char *) config_attr, config_attr_len);
        if (!root) {
-               wpa_printf(MSG_DEBUG, "DPP: Could not parse Config Attributes");
+               dpp_auth_fail(auth, "Could not parse Config Attributes");
                goto fail;
        }
 
        token = json_get_member(root, "name");
        if (!token || token->type != JSON_STRING) {
-               wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - name");
+               dpp_auth_fail(auth, "No Config Attributes - name");
                goto fail;
        }
        wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string);
 
        token = json_get_member(root, "wi-fi_tech");
        if (!token || token->type != JSON_STRING) {
-               wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - wi-fi_tech");
+               dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech");
                goto fail;
        }
        wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string);
        if (os_strcmp(token->string, "infra") != 0) {
                wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'",
                           token->string);
+               dpp_auth_fail(auth, "Unsupported wi-fi_tech");
                goto fail;
        }
 
        token = json_get_member(root, "netRole");
        if (!token || token->type != JSON_STRING) {
-               wpa_printf(MSG_DEBUG, "DPP: No Config Attributes - netRole");
+               dpp_auth_fail(auth, "No Config Attributes - netRole");
                goto fail;
        }
        wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string);
@@ -3953,6 +4488,7 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
        } else {
                wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'",
                           token->string);
+               dpp_auth_fail(auth, "Unsupported netRole");
                goto fail;
        }
 
@@ -4065,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");
@@ -4078,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;
 }
 
@@ -4640,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)
 {
@@ -4650,38 +5228,37 @@ static int dpp_parse_conf_obj(struct dpp_authentication *auth,
        if (!root)
                return -1;
        if (root->type != JSON_OBJECT) {
-               wpa_printf(MSG_DEBUG, "DPP: JSON root is not an object");
+               dpp_auth_fail(auth, "JSON root is not an object");
                goto fail;
        }
 
        token = json_get_member(root, "wi-fi_tech");
        if (!token || token->type != JSON_STRING) {
-               wpa_printf(MSG_DEBUG, "DPP: No wi-fi_tech string value found");
+               dpp_auth_fail(auth, "No wi-fi_tech string value found");
                goto fail;
        }
        if (os_strcmp(token->string, "infra") != 0) {
                wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'",
                           token->string);
+               dpp_auth_fail(auth, "Unsupported wi-fi_tech value");
                goto fail;
        }
 
        discovery = json_get_member(root, "discovery");
        if (!discovery || discovery->type != JSON_OBJECT) {
-               wpa_printf(MSG_DEBUG, "DPP: No discovery object in JSON");
+               dpp_auth_fail(auth, "No discovery object in JSON");
                goto fail;
        }
 
        token = json_get_member(discovery, "ssid");
        if (!token || token->type != JSON_STRING) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: No discovery::ssid string value found");
+               dpp_auth_fail(auth, "No discovery::ssid string value found");
                goto fail;
        }
        wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid",
                          token->string, os_strlen(token->string));
        if (os_strlen(token->string) > SSID_MAX_LEN) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Too long discovery::ssid string value");
+               dpp_auth_fail(auth, "Too long discovery::ssid string value");
                goto fail;
        }
        auth->ssid_len = os_strlen(token->string);
@@ -4689,25 +5266,28 @@ static int dpp_parse_conf_obj(struct dpp_authentication *auth,
 
        cred = json_get_member(root, "cred");
        if (!cred || cred->type != JSON_OBJECT) {
-               wpa_printf(MSG_DEBUG, "DPP: No cred object in JSON");
+               dpp_auth_fail(auth, "No cred object in JSON");
                goto fail;
        }
 
        token = json_get_member(cred, "akm");
        if (!token || token->type != JSON_STRING) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: No cred::akm string value found");
+               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 {
                wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s",
                           token->string);
+               dpp_auth_fail(auth, "Unsupported akm");
                goto fail;
        }
 
@@ -4731,8 +5311,7 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
        int ret = -1;
 
        if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Invalid attribute in config response");
+               dpp_auth_fail(auth, "Invalid attribute in config response");
                return -1;
        }
 
@@ -4740,8 +5319,8 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
                                    DPP_ATTR_WRAPPED_DATA,
                                    &wrapped_data_len);
        if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid required Wrapped data attribute");
+               dpp_auth_fail(auth,
+                             "Missing or invalid required Wrapped Data attribute");
                return -1;
        }
 
@@ -4759,15 +5338,14 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
        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");
+               dpp_auth_fail(auth, "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");
+               dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
                goto fail;
        }
 
@@ -4775,34 +5353,34 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
                               DPP_ATTR_ENROLLEE_NONCE,
                               &e_nonce_len);
        if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid Enrollee Nonce attribute");
+               dpp_auth_fail(auth,
+                             "Missing or invalid Enrollee Nonce attribute");
                goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
        if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
-               wpa_printf(MSG_DEBUG, "Enrollee Nonce mismatch");
+               dpp_auth_fail(auth, "Enrollee Nonce mismatch");
                goto fail;
        }
 
        status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp),
                              DPP_ATTR_STATUS, &status_len);
        if (!status || status_len < 1) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid required DPP Status attribute");
+               dpp_auth_fail(auth,
+                             "Missing or invalid required DPP Status attribute");
                goto fail;
        }
        wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
        if (status[0] != DPP_STATUS_OK) {
-               wpa_printf(MSG_DEBUG, "DPP: Configuration failed");
+               dpp_auth_fail(auth, "Configurator rejected configuration");
                goto fail;
        }
 
        conf_obj = dpp_get_attr(unwrapped, unwrapped_len,
                                DPP_ATTR_CONFIG_OBJ, &conf_obj_len);
        if (!conf_obj) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing required Configuration Object attribute");
+               dpp_auth_fail(auth,
+                             "Missing required Configuration Object attribute");
                goto fail;
        }
        wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON",
@@ -4891,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;
@@ -4922,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),
@@ -5374,9 +5952,10 @@ static EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
            EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
                goto fail;
        if (EC_POINT_is_at_infinity(group, Qi)) {
-               wpa_printf(MSG_INFO, "PDP: Qi is the point-at-infinity");
+               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);
@@ -5454,6 +6033,11 @@ static EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
        if (!hash_bn ||
            EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
                goto fail;
+       if (EC_POINT_is_at_infinity(group, Qr)) {
+               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);
@@ -5468,6 +6052,71 @@ fail:
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+static int dpp_test_gen_invalid_key(struct wpabuf *msg,
+                                   const struct dpp_curve_params *curve)
+{
+       BN_CTX *ctx;
+       BIGNUM *x, *y;
+       int ret = -1;
+       EC_GROUP *group;
+       EC_POINT *point;
+
+       group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+       if (!group)
+               return -1;
+
+       ctx = BN_CTX_new();
+       point = EC_POINT_new(group);
+       x = BN_new();
+       y = BN_new();
+       if (!ctx || !point || !x || !y)
+               goto fail;
+
+       if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
+               goto fail;
+
+       /* Generate a random y coordinate that results in a point that is not
+        * on the curve. */
+       for (;;) {
+               if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
+                       goto fail;
+
+               if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
+                                                       ctx) != 1) {
+#ifdef OPENSSL_IS_BORINGSSL
+               /* Unlike OpenSSL, BoringSSL returns an error from
+                * EC_POINT_set_affine_coordinates_GFp() is not on the curve. */
+                       break;
+#else /* OPENSSL_IS_BORINGSSL */
+                       goto fail;
+#endif /* OPENSSL_IS_BORINGSSL */
+               }
+
+               if (!EC_POINT_is_on_curve(group, point, ctx))
+                       break;
+       }
+
+       if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
+                          curve->prime_len) < 0 ||
+           dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
+                          curve->prime_len) < 0)
+               goto fail;
+
+       ret = 0;
+fail:
+       if (ret < 0)
+               wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
+       BN_free(x);
+       BN_free(y);
+       EC_POINT_free(point);
+       BN_CTX_free(ctx);
+
+       return ret;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
 static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
 {
        EC_KEY *X_ec = NULL;
@@ -5480,7 +6129,6 @@ static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
        struct wpabuf *msg = NULL;
        size_t attr_len;
        const struct dpp_curve_params *curve = pkex->own_bi->curve;
-       int num_bytes, offset;
 
        wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
 
@@ -5494,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;
 
@@ -5505,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();
@@ -5512,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;
@@ -5522,11 +6186,22 @@ static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
        if (!msg)
                goto fail;
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
+               goto skip_finite_cyclic_group;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        /* Finite Cyclic Group attribute */
        wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
        wpabuf_put_le16(msg, 2);
        wpabuf_put_le16(msg, curve->ike_group);
 
+#ifdef CONFIG_TESTING_OPTIONS
+skip_finite_cyclic_group:
+#endif /* CONFIG_TESTING_OPTIONS */
+
        /* Code Identifier attribute */
        if (pkex->identifier) {
                wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
@@ -5534,31 +6209,32 @@ static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
                wpabuf_put_str(msg, pkex->identifier);
        }
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+               goto out;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        /* M in Encrypted Key attribute */
        wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
        wpabuf_put_le16(msg, 2 * curve->prime_len);
 
-       num_bytes = BN_num_bytes(Mx);
-       if ((size_t) num_bytes > curve->prime_len)
-               goto fail;
-       if (curve->prime_len > (size_t) num_bytes)
-               offset = curve->prime_len - num_bytes;
-       else
-               offset = 0;
-       os_memset(wpabuf_put(msg, offset), 0, offset);
-       BN_bn2bin(Mx, wpabuf_put(msg, num_bytes));
-       os_memset(pkex->Mx, 0, offset);
-       BN_bn2bin(Mx, pkex->Mx + offset);
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+               if (dpp_test_gen_invalid_key(msg, curve) < 0)
+                       goto fail;
+               goto out;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
 
-       num_bytes = BN_num_bytes(My);
-       if ((size_t) num_bytes > curve->prime_len)
+       if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
+                          curve->prime_len) < 0 ||
+           dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
+           dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
+                          curve->prime_len) < 0)
                goto fail;
-       if (curve->prime_len > (size_t) num_bytes)
-               offset = curve->prime_len - num_bytes;
-       else
-               offset = 0;
-       os_memset(wpabuf_put(msg, offset), 0, offset);
-       BN_bn2bin(My, wpabuf_put(msg, num_bytes));
 
 out:
        wpabuf_free(M_buf);
@@ -5590,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;
@@ -5615,29 +6299,197 @@ fail:
 }
 
 
-struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
-                                          struct dpp_bootstrap_info *bi,
-                                          const u8 *own_mac,
-                                          const u8 *peer_mac,
-                                          const char *identifier,
-                                          const char *code,
-                                          const u8 *buf, size_t len)
+static struct wpabuf *
+dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
+                            enum dpp_status_error status,
+                            const BIGNUM *Nx, const BIGNUM *Ny)
 {
-       const u8 *attr_group, *attr_id, *attr_key;
-       u16 attr_group_len, attr_id_len, attr_key_len;
-       const struct dpp_curve_params *curve = bi->curve;
-       u16 ike_group;
-       struct dpp_pkex *pkex = NULL;
-       EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
+       struct wpabuf *msg = NULL;
+       size_t attr_len;
+       const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+       /* Initiator -> Responder: DPP Status, [identifier,] N */
+       attr_len = 4 + 1;
+       if (pkex->identifier)
+               attr_len += 4 + os_strlen(pkex->identifier);
+       attr_len += 4 + 2 * curve->prime_len;
+       msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
+       if (!msg)
+               goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+               goto skip_status;
+       }
+
+       if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+               status = 255;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       /* DPP Status */
+       dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       /* Code Identifier attribute */
+       if (pkex->identifier) {
+               wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+               wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+               wpabuf_put_str(msg, pkex->identifier);
+       }
+
+       if (status != DPP_STATUS_OK)
+               goto skip_encrypted_key;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+               goto skip_encrypted_key;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       /* N in Encrypted Key attribute */
+       wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+       wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+               if (dpp_test_gen_invalid_key(msg, curve) < 0)
+                       goto fail;
+               goto skip_encrypted_key;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
+                          curve->prime_len) < 0 ||
+           dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
+           dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
+                          curve->prime_len) < 0)
+               goto fail;
+
+skip_encrypted_key:
+       if (status == DPP_STATUS_BAD_GROUP) {
+               /* Finite Cyclic Group attribute */
+               wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+               wpabuf_put_le16(msg, 2);
+               wpabuf_put_le16(msg, curve->ike_group);
+       }
+
+       return msg;
+fail:
+       wpabuf_free(msg);
+       return NULL;
+}
+
+
+static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+                            const u8 *Mx, size_t Mx_len,
+                            const u8 *Nx, size_t Nx_len,
+                            const char *code,
+                            const u8 *Kx, size_t Kx_len,
+                            u8 *z, unsigned int hash_len)
+{
+       u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+       int res;
+       u8 *info, *pos;
+       size_t info_len;
+
+       /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+        */
+
+       /* HKDF-Extract(<>, IKM=K.x) */
+       os_memset(salt, 0, hash_len);
+       if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
+               return -1;
+       wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+                       prk, hash_len);
+       info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
+       info = os_malloc(info_len);
+       if (!info)
+               return -1;
+       pos = info;
+       os_memcpy(pos, mac_init, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, mac_resp, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, Mx, Mx_len);
+       pos += Mx_len;
+       os_memcpy(pos, Nx, Nx_len);
+       pos += Nx_len;
+       os_memcpy(pos, code, os_strlen(code));
+
+       /* HKDF-Expand(PRK, info, L) */
+       if (hash_len == 32)
+               res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
+                                     z, hash_len);
+       else if (hash_len == 48)
+               res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
+                                     z, hash_len);
+       else if (hash_len == 64)
+               res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
+                                     z, hash_len);
+       else
+               res = -1;
+       os_free(info);
+       os_memset(prk, 0, hash_len);
+       if (res < 0)
+               return -1;
+
+       wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
+                       z, hash_len);
+       return 0;
+}
+
+
+struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
+                                          struct dpp_bootstrap_info *bi,
+                                          const u8 *own_mac,
+                                          const u8 *peer_mac,
+                                          const char *identifier,
+                                          const char *code,
+                                          const u8 *buf, size_t len)
+{
+       const u8 *attr_group, *attr_id, *attr_key;
+       u16 attr_group_len, attr_id_len, attr_key_len;
+       const struct dpp_curve_params *curve = bi->curve;
+       u16 ike_group;
+       struct dpp_pkex *pkex = NULL;
+       EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
        BN_CTX *bnctx = NULL;
        const EC_GROUP *group;
        BIGNUM *Mx = NULL, *My = NULL;
        EC_KEY *Y_ec = NULL, *X_ec = NULL;;
        const EC_POINT *Y_point;
        BIGNUM *Nx = NULL, *Ny = NULL;
-       struct wpabuf *msg = NULL;
-       size_t attr_len;
-       int num_bytes, offset;
+       u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
+       size_t Kx_len;
+       int res;
+       EVP_PKEY_CTX *ctx = NULL;
+
+       if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
+               wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+                       "PKEX counter t limit reached - ignore message");
+               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);
@@ -5656,18 +6508,25 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
        attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
                                  &attr_group_len);
        if (!attr_group || attr_group_len != 2) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid Finite Cyclic Group attribute");
+               wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+                       "Missing or invalid Finite Cyclic Group attribute");
                return NULL;
        }
        ike_group = WPA_GET_LE16(attr_group);
        if (ike_group != curve->ike_group) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Mismatching PKEX curve: peer=%u own=%u",
-                          ike_group, curve->ike_group);
-               /* TODO: error response with suggested curve:
-                * DPP Status, group */
-               return NULL;
+               wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+                       "Mismatching PKEX curve: peer=%u own=%u",
+                       ike_group, curve->ike_group);
+               pkex = os_zalloc(sizeof(*pkex));
+               if (!pkex)
+                       goto fail;
+               pkex->own_bi = bi;
+               pkex->failed = 1;
+               pkex->exchange_resp = dpp_pkex_build_exchange_resp(
+                       pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
+               if (!pkex->exchange_resp)
+                       goto fail;
+               return pkex;
        }
 
        /* M in Encrypted Key attribute */
@@ -5675,7 +6534,8 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
                                &attr_key_len);
        if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
            attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
-               wpa_printf(MSG_DEBUG, "DPP: Missing Encrypted Key attribute");
+               wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+                       "Missing Encrypted Key attribute");
                return NULL;
        }
 
@@ -5700,12 +6560,19 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
            EC_POINT_invert(group, Qi, bnctx) != 1 ||
            EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
            EC_POINT_is_at_infinity(group, X) ||
-           !EC_POINT_is_on_curve(group, X, bnctx))
+           !EC_POINT_is_on_curve(group, X, bnctx)) {
+               wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+                       "Invalid Encrypted Key value");
+               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)
                goto fail;
+       pkex->t = bi->pkex_t;
        pkex->msg_ctx = msg_ctx;
        pkex->own_bi = bi;
        os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
@@ -5737,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;
 
@@ -5748,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();
@@ -5755,60 +6637,44 @@ 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);
 
-       /* Initiator -> Responder: DPP Status, [identifier,] N */
-       attr_len = 4 + 1;
-       if (identifier)
-               attr_len += 4 + os_strlen(identifier);
-       attr_len += 4 + 2 * curve->prime_len;
-       msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
-       if (!msg)
+       pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
+                                                          Nx, Ny);
+       if (!pkex->exchange_resp)
                goto fail;
 
-       /* DPP Status */
-       wpabuf_put_le16(msg, DPP_ATTR_STATUS);
-       wpabuf_put_le16(msg, 1);
-       wpabuf_put_u8(msg, DPP_STATUS_OK);
-
-       /* Code Identifier attribute */
-       if (pkex->identifier) {
-               wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
-               wpabuf_put_le16(msg, os_strlen(pkex->identifier));
-               wpabuf_put_str(msg, pkex->identifier);
+       /* K = y * X' */
+       ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
+       if (!ctx ||
+           EVP_PKEY_derive_init(ctx) != 1 ||
+           EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
+           EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
+           Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
+           EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
+               wpa_printf(MSG_ERROR,
+                          "DPP: Failed to derive ECDH shared secret: %s",
+                          ERR_error_string(ERR_get_error(), NULL));
+               goto fail;
        }
 
-       /* N in Encrypted Key attribute */
-       wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
-       wpabuf_put_le16(msg, 2 * curve->prime_len);
-
-       num_bytes = BN_num_bytes(Nx);
-       if ((size_t) num_bytes > curve->prime_len)
-               goto fail;
-       if (curve->prime_len > (size_t) num_bytes)
-               offset = curve->prime_len - num_bytes;
-       else
-               offset = 0;
-       os_memset(wpabuf_put(msg, offset), 0, offset);
-       BN_bn2bin(Nx, wpabuf_put(msg, num_bytes));
-       os_memset(pkex->Nx, 0, offset);
-       BN_bn2bin(Nx, pkex->Nx + offset);
+       wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+                       Kx, Kx_len);
 
-       num_bytes = BN_num_bytes(Ny);
-       if ((size_t) num_bytes > curve->prime_len)
+       /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+        */
+       res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
+                               pkex->Mx, curve->prime_len,
+                               pkex->Nx, curve->prime_len, pkex->code,
+                               Kx, Kx_len, pkex->z, curve->hash_len);
+       os_memset(Kx, 0, Kx_len);
+       if (res < 0)
                goto fail;
-       if (curve->prime_len > (size_t) num_bytes)
-               offset = curve->prime_len - num_bytes;
-       else
-               offset = 0;
-       os_memset(wpabuf_put(msg, offset), 0, offset);
-       BN_bn2bin(Ny, wpabuf_put(msg, num_bytes));
 
-       pkex->exchange_resp = msg;
-       msg = NULL;
        pkex->exchange_done = 1;
 
 out:
-       wpabuf_free(msg);
+       EVP_PKEY_CTX_free(ctx);
        BN_CTX_free(bnctx);
        EC_POINT_free(Qi);
        EC_POINT_free(Qr);
@@ -5823,82 +6689,134 @@ out:
        EC_KEY_free(Y_ec);
        return pkex;
 fail:
-       wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing faileed");
+       wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
        dpp_pkex_free(pkex);
        pkex = NULL;
        goto out;
 }
 
 
-static int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
-                            const u8 *Mx, size_t Mx_len,
-                            const u8 *Nx, size_t Nx_len,
-                            const char *code,
-                            const u8 *Kx, size_t Kx_len,
-                            u8 *z, unsigned int hash_len)
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
+                                const struct wpabuf *A_pub, const u8 *u)
 {
-       u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
-       int res;
-       u8 *info, *pos;
-       size_t info_len;
+       const struct dpp_curve_params *curve = pkex->own_bi->curve;
+       struct wpabuf *msg = NULL;
+       size_t clear_len, attr_len;
+       struct wpabuf *clear = NULL;
+       u8 *wrapped;
+       u8 octet;
+       const u8 *addr[2];
+       size_t len[2];
 
-       /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
-        */
+       /* {A, u, [bootstrapping info]}z */
+       clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+       clear = wpabuf_alloc(clear_len);
+       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 += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+       msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
+       if (!clear || !msg)
+               goto fail;
 
-       /* HKDF-Extract(<>, IKM=K.x) */
-       os_memset(salt, 0, hash_len);
-       if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
-               return -1;
-       wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
-                       prk, hash_len);
-       info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
-       info = os_malloc(info_len);
-       if (!info)
-               return -1;
-       pos = info;
-       os_memcpy(pos, mac_init, ETH_ALEN);
-       pos += ETH_ALEN;
-       os_memcpy(pos, mac_resp, ETH_ALEN);
-       pos += ETH_ALEN;
-       os_memcpy(pos, Mx, Mx_len);
-       pos += Mx_len;
-       os_memcpy(pos, Nx, Nx_len);
-       pos += Nx_len;
-       os_memcpy(pos, code, os_strlen(code));
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+               goto skip_bootstrap_key;
+       }
+       if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+               wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+               wpabuf_put_le16(clear, 2 * curve->prime_len);
+               if (dpp_test_gen_invalid_key(clear, curve) < 0)
+                       goto fail;
+               goto skip_bootstrap_key;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       /* A in Bootstrap Key attribute */
+       wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+       wpabuf_put_le16(clear, wpabuf_len(A_pub));
+       wpabuf_put_buf(clear, A_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+       if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
+               goto skip_i_auth_tag;
+       }
+       if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
+               wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+               wpabuf_put_le16(clear, curve->hash_len);
+               wpabuf_put_data(clear, u, curve->hash_len - 1);
+               wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
+               goto skip_i_auth_tag;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       /* u in I-Auth tag attribute */
+       wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+       wpabuf_put_le16(clear, curve->hash_len);
+       wpabuf_put_data(clear, u, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_auth_tag:
+       if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+               goto skip_wrapped_data;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       addr[0] = wpabuf_head_u8(msg) + 2;
+       len[0] = DPP_HDR_LEN;
+       octet = 0;
+       addr[1] = &octet;
+       len[1] = sizeof(octet);
+       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+       wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+       wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+       wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+       wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+       if (aes_siv_encrypt(pkex->z, curve->hash_len,
+                           wpabuf_head(clear), wpabuf_len(clear),
+                           2, addr, len, wrapped) < 0)
+               goto fail;
+       wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+                   wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#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");
+               dpp_build_attr_status(msg, DPP_STATUS_OK);
+       }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
 
-       /* HKDF-Expand(PRK, info, L) */
-       if (hash_len == 32)
-               res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
-                                     z, hash_len);
-       else if (hash_len == 48)
-               res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
-                                     z, hash_len);
-       else if (hash_len == 64)
-               res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
-                                     z, hash_len);
-       else
-               res = -1;
-       os_free(info);
-       os_memset(prk, 0, hash_len);
-       if (res < 0)
-               return -1;
+out:
+       wpabuf_free(clear);
+       return msg;
 
-       wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
-                       z, hash_len);
-       return 0;
+fail:
+       wpabuf_free(msg);
+       msg = NULL;
+       goto out;
 }
 
 
 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;
-       u16 attr_status_len, attr_id_len, attr_key_len;
+       const u8 *attr_status, *attr_id, *attr_key, *attr_group;
+       u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
        const EC_GROUP *group;
        BN_CTX *bnctx = NULL;
-       size_t clear_len, attr_len;
-       struct wpabuf *clear = NULL;
-       u8 *wrapped;
        struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
        const struct dpp_curve_params *curve = pkex->own_bi->curve;
        EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
@@ -5910,18 +6828,43 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
        const u8 *addr[4];
        size_t len[4];
        u8 u[DPP_MAX_HASH_LEN];
-       u8 octet;
        int res;
 
+       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) {
-               wpa_printf(MSG_DEBUG, "DPP: No DPP Status attribute");
+               dpp_pkex_fail(pkex, "No DPP Status attribute");
                return NULL;
        }
        wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
+
+       if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
+               attr_group = dpp_get_attr(buf, buflen,
+                                         DPP_ATTR_FINITE_CYCLIC_GROUP,
+                                         &attr_group_len);
+               if (attr_group && attr_group_len == 2) {
+                       wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+                               "Peer indicated mismatching PKEX group - proposed %u",
+                               WPA_GET_LE16(attr_group));
+                       return NULL;
+               }
+       }
+
        if (attr_status[0] != DPP_STATUS_OK) {
-               wpa_printf(MSG_DEBUG, "DPP: PKEX failed");
+               dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
                return NULL;
        }
 
@@ -5935,7 +6878,7 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
        if (attr_id && pkex->identifier &&
            (os_strlen(pkex->identifier) != attr_id_len ||
             os_memcmp(pkex->identifier, attr_id, attr_id_len) != 0)) {
-               wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+               dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
                return NULL;
        }
 
@@ -5943,7 +6886,7 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
        attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
                                &attr_key_len);
        if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
-               wpa_printf(MSG_DEBUG, "DPP: Missing Encrypted Key attribute");
+               dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
                return NULL;
        }
 
@@ -5968,8 +6911,13 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
            EC_POINT_invert(group, Qr, bnctx) != 1 ||
            EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
            EC_POINT_is_at_infinity(group, Y) ||
-           !EC_POINT_is_on_curve(group, Y, bnctx))
+           !EC_POINT_is_on_curve(group, Y, bnctx)) {
+               dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
+               pkex->t++;
                goto fail;
+       }
+       dpp_debug_print_point("DPP: N", group, N);
+       dpp_debug_print_point("DPP: Y'", group, Y);
 
        pkex->exchange_done = 1;
 
@@ -6046,31 +6994,106 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
        if (res < 0)
                goto fail;
 
-       /* {A, u, [bootstrapping info]}z */
+       msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
+       if (!msg)
+               goto fail;
+
+out:
+       wpabuf_free(A_pub);
+       wpabuf_free(X_pub);
+       wpabuf_free(Y_pub);
+       EC_POINT_free(Qr);
+       EC_POINT_free(Y);
+       EC_POINT_free(N);
+       BN_free(Nx);
+       BN_free(Ny);
+       EC_KEY_free(Y_ec);
+       EVP_PKEY_CTX_free(ctx);
+       BN_CTX_free(bnctx);
+       return msg;
+fail:
+       wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
+       goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
+                                 const struct wpabuf *B_pub, const u8 *v)
+{
+       const struct dpp_curve_params *curve = pkex->own_bi->curve;
+       struct wpabuf *msg = NULL;
+       const u8 *addr[2];
+       size_t len[2];
+       u8 octet;
+       u8 *wrapped;
+       struct wpabuf *clear = NULL;
+       size_t clear_len, attr_len;
+
+       /* {B, v [bootstrapping info]}z */
        clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
        clear = wpabuf_alloc(clear_len);
        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;
+       if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
+               attr_len += 5;
 #endif /* CONFIG_TESTING_OPTIONS */
-       msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
+       msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
        if (!clear || !msg)
                goto fail;
 
-       /* A in Bootstrap Key attribute */
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+               goto skip_bootstrap_key;
+       }
+       if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+               wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+               wpabuf_put_le16(clear, 2 * curve->prime_len);
+               if (dpp_test_gen_invalid_key(clear, curve) < 0)
+                       goto fail;
+               goto skip_bootstrap_key;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       /* B in Bootstrap Key attribute */
        wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
-       wpabuf_put_le16(clear, wpabuf_len(A_pub));
-       wpabuf_put_buf(clear, A_pub);
+       wpabuf_put_le16(clear, wpabuf_len(B_pub));
+       wpabuf_put_buf(clear, B_pub);
 
-       /* u in I-Auth tag attribute */
-       wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+       if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
+               goto skip_r_auth_tag;
+       }
+       if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
+               wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+               wpabuf_put_le16(clear, curve->hash_len);
+               wpabuf_put_data(clear, v, curve->hash_len - 1);
+               wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
+               goto skip_r_auth_tag;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       /* v in R-Auth tag attribute */
+       wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
        wpabuf_put_le16(clear, curve->hash_len);
-       wpabuf_put_data(clear, u, curve->hash_len);
+       wpabuf_put_data(clear, v, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_r_auth_tag:
+       if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+               goto skip_wrapped_data;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
 
        addr[0] = wpabuf_head_u8(msg) + 2;
        len[0] = DPP_HDR_LEN;
-       octet = 0;
+       octet = 1;
        addr[1] = &octet;
        len[1] = sizeof(octet);
        wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
@@ -6089,29 +7112,18 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
                    wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
 
 #ifdef CONFIG_TESTING_OPTIONS
-       if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
+       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 */
 
 out:
        wpabuf_free(clear);
-       wpabuf_free(A_pub);
-       wpabuf_free(X_pub);
-       wpabuf_free(Y_pub);
-       EC_POINT_free(Qr);
-       EC_POINT_free(Y);
-       EC_POINT_free(N);
-       BN_free(Nx);
-       BN_free(Ny);
-       EC_KEY_free(Y_ec);
-       EVP_PKEY_CTX_free(ctx);
-       BN_CTX_free(bnctx);
        return msg;
+
 fail:
-       wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing faileed");
        wpabuf_free(msg);
        msg = NULL;
        goto out;
@@ -6123,9 +7135,9 @@ struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
                                              const u8 *buf, size_t buflen)
 {
        const struct dpp_curve_params *curve = pkex->own_bi->curve;
-       EVP_PKEY_CTX *ctx;
-       size_t Jx_len, Kx_len, Lx_len;
-       u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+       EVP_PKEY_CTX *ctx = NULL;
+       size_t Jx_len, Lx_len;
+       u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
        u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
        const u8 *wrapped_data, *b_key, *peer_u;
        u16 wrapped_data_len, b_key_len, peer_u_len = 0;
@@ -6137,43 +7149,16 @@ struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
        struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
        struct wpabuf *B_pub = NULL;
        u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
-       size_t clear_len, attr_len;
-       struct wpabuf *clear = NULL;
-       u8 *wrapped;
-       int res;
-
-       /* K = y * X' */
-       ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
-       if (!ctx ||
-           EVP_PKEY_derive_init(ctx) != 1 ||
-           EVP_PKEY_derive_set_peer(ctx, pkex->x) != 1 ||
-           EVP_PKEY_derive(ctx, NULL, &Kx_len) != 1 ||
-           Kx_len > DPP_MAX_SHARED_SECRET_LEN ||
-           EVP_PKEY_derive(ctx, Kx, &Kx_len) != 1) {
-               wpa_printf(MSG_ERROR,
-                          "DPP: Failed to derive ECDH shared secret: %s",
-                          ERR_error_string(ERR_get_error(), NULL));
-               goto fail;
-       }
-
-       wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
-                       Kx, Kx_len);
 
-       /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
-        */
-       res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
-                               pkex->Mx, curve->prime_len,
-                               pkex->Nx, curve->prime_len, pkex->code,
-                               Kx, Kx_len, pkex->z, curve->hash_len);
-       os_memset(Kx, 0, Kx_len);
-       if (res < 0)
+       if (!pkex->exchange_done || pkex->failed ||
+           pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
                goto fail;
 
        wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
                                    &wrapped_data_len);
        if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid required Wrapped data attribute");
+               dpp_pkex_fail(pkex,
+                             "Missing or invalid required Wrapped Data attribute");
                goto fail;
        }
 
@@ -6197,33 +7182,34 @@ struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
                            2, addr, len, unwrapped) < 0) {
                dpp_pkex_fail(pkex,
                              "AES-SIV decryption failed - possible PKEX code mismatch");
+               pkex->failed = 1;
+               pkex->t++;
                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");
+               dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
                goto fail;
        }
 
        b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
                             &b_key_len);
        if (!b_key || b_key_len != 2 * curve->prime_len) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: No valid peer bootstrapping key found");
+               dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
                goto fail;
        }
        pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
                                                        b_key_len);
-       if (!pkex->peer_bootstrap_key)
+       if (!pkex->peer_bootstrap_key) {
+               dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
                goto fail;
+       }
        dpp_debug_print_key("DPP: Peer bootstrap public key",
                            pkex->peer_bootstrap_key);
 
        /* ECDH: J' = y * A' */
-       EVP_PKEY_CTX_free(ctx);
        ctx = EVP_PKEY_CTX_new(pkex->y, NULL);
        if (!ctx ||
            EVP_PKEY_derive_init(ctx) != 1 ||
@@ -6261,10 +7247,11 @@ struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
                              &peer_u_len);
        if (!peer_u || peer_u_len != curve->hash_len ||
            os_memcmp(peer_u, u, curve->hash_len) != 0) {
-               wpa_printf(MSG_DEBUG, "DPP: No valid u (I-Auth tag) found");
+               dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
                wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
                            u, curve->hash_len);
                wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
+               pkex->t++;
                goto fail;
        }
        wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
@@ -6303,55 +7290,10 @@ struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
                goto fail;
        wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
 
-       /* {B, v [bootstrapping info]}z */
-       clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
-       clear = wpabuf_alloc(clear_len);
-       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;
-#endif /* CONFIG_TESTING_OPTIONS */
-       msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
-       if (!clear || !msg)
-               goto fail;
-
-       /* A in Bootstrap Key attribute */
-       wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
-       wpabuf_put_le16(clear, wpabuf_len(B_pub));
-       wpabuf_put_buf(clear, B_pub);
-
-       /* v in R-Auth tag attribute */
-       wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
-       wpabuf_put_le16(clear, curve->hash_len);
-       wpabuf_put_data(clear, v, curve->hash_len);
-
-       addr[0] = wpabuf_head_u8(msg) + 2;
-       len[0] = DPP_HDR_LEN;
-       octet = 1;
-       addr[1] = &octet;
-       len[1] = sizeof(octet);
-       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
-       wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
-       wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-       wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
-
-       wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
-       if (aes_siv_encrypt(pkex->z, curve->hash_len,
-                           wpabuf_head(clear), wpabuf_len(clear),
-                           2, addr, len, wrapped) < 0)
+       msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
+       if (!msg)
                goto fail;
-       wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
-                   wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
 
-#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);
-       }
-#endif /* CONFIG_TESTING_OPTIONS */
 out:
        EVP_PKEY_CTX_free(ctx);
        os_free(unwrapped);
@@ -6359,11 +7301,10 @@ out:
        wpabuf_free(B_pub);
        wpabuf_free(X_pub);
        wpabuf_free(Y_pub);
-       wpabuf_free(clear);
        return msg;
 fail:
-       wpabuf_free(msg);
-       msg = NULL;
+       wpa_printf(MSG_DEBUG,
+                  "DPP: PKEX Commit-Reveal Request processing failed");
        goto out;
 }
 
@@ -6386,11 +7327,15 @@ int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
        EVP_PKEY_CTX *ctx = NULL;
        struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
 
+       if (!pkex->exchange_done || pkex->failed ||
+           pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+               goto fail;
+
        wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
                                    &wrapped_data_len);
        if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid required Wrapped data attribute");
+               dpp_pkex_fail(pkex,
+                             "Missing or invalid required Wrapped Data attribute");
                goto fail;
        }
 
@@ -6414,28 +7359,29 @@ int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
                            2, addr, len, unwrapped) < 0) {
                dpp_pkex_fail(pkex,
                              "AES-SIV decryption failed - possible PKEX code mismatch");
+               pkex->t++;
                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");
+               dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
                goto fail;
        }
 
        b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
                             &b_key_len);
        if (!b_key || b_key_len != 2 * curve->prime_len) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: No valid peer bootstrapping key found");
+               dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
                goto fail;
        }
        pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
                                                        b_key_len);
-       if (!pkex->peer_bootstrap_key)
+       if (!pkex->peer_bootstrap_key) {
+               dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
                goto fail;
+       }
        dpp_debug_print_key("DPP: Peer bootstrap public key",
                            pkex->peer_bootstrap_key);
 
@@ -6477,10 +7423,11 @@ int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
                              &peer_v_len);
        if (!peer_v || peer_v_len != curve->hash_len ||
            os_memcmp(peer_v, v, curve->hash_len) != 0) {
-               wpa_printf(MSG_DEBUG, "DPP: No valid v (R-Auth tag) found");
+               dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
                wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
                            v, curve->hash_len);
                wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
+               pkex->t++;
                goto fail;
        }
        wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
@@ -6512,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 */