]> 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 c1906791f3f9da136437208167c55c3091214655..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"
 
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#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 || defined(OPENSSL_IS_BORINGSSL)
 /* Compatibility wrappers for older versions. */
 
 static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
@@ -272,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)
@@ -335,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;
@@ -418,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 ||
@@ -479,6 +541,12 @@ static EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key,
 }
 
 
+static void dpp_auth_fail(struct dpp_authentication *auth, const char *txt)
+{
+       wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
 struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
                              size_t len)
 {
@@ -523,6 +591,7 @@ const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len)
 int dpp_check_attrs(const u8 *buf, size_t len)
 {
        const u8 *pos, *end;
+       int wrapped_data = 0;
 
        pos = buf;
        end = buf + len;
@@ -540,6 +609,13 @@ int dpp_check_attrs(const u8 *buf, size_t len)
                                   "DPP: Truncated message - not enough room for the attribute - dropped");
                        return -1;
                }
+               if (wrapped_data) {
+                       wpa_printf(MSG_DEBUG,
+                                  "DPP: An unexpected attribute included after the Wrapped Data attribute");
+                       return -1;
+               }
+               if (id == DPP_ATTR_WRAPPED_DATA)
+                       wrapped_data = 1;
                pos += alen;
        }
 
@@ -731,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;
@@ -939,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)
@@ -961,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);
@@ -1016,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");
@@ -1121,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;
 }
 
@@ -1159,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];
@@ -1181,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;
@@ -1217,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;
 }
 
@@ -1327,104 +1484,129 @@ static int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke,
 }
 
 
-struct dpp_authentication * dpp_auth_init(void *msg_ctx,
-                                         struct dpp_bootstrap_info *peer_bi,
-                                         struct dpp_bootstrap_info *own_bi,
-                                         int configurator)
+static void dpp_build_attr_status(struct wpabuf *msg,
+                                 enum dpp_status_error status)
 {
-       struct dpp_authentication *auth;
-       size_t nonce_len;
-       EVP_PKEY_CTX *ctx = NULL;
-       size_t secret_len;
-       struct wpabuf *msg, *pi = NULL;
-       u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
-       u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
-       u8 *pos;
-       const u8 *addr[2];
-       size_t len[2], siv_len, attr_len;
-       u8 *attr_start, *attr_end;
+       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);
+}
 
-       auth = os_zalloc(sizeof(*auth));
-       if (!auth)
-               return NULL;
-       auth->msg_ctx = msg_ctx;
-       auth->initiator = 1;
-       auth->configurator = configurator;
-       auth->peer_bi = peer_bi;
-       auth->own_bi = own_bi;
-       auth->curve = peer_bi->curve;
 
-       nonce_len = auth->curve->nonce_len;
-       if (random_get_bytes(auth->i_nonce, nonce_len)) {
-               wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
-               goto fail;
+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);
        }
-       wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
-
-       auth->own_protocol_key = dpp_gen_keypair(auth->curve);
-       if (!auth->own_protocol_key)
-               goto fail;
+}
 
-       pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
-       if (!pi)
-               goto fail;
 
-       /* ECDH: M = pI * BR */
-       ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
-       if (!ctx ||
-           EVP_PKEY_derive_init(ctx) != 1 ||
-           EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
-           EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
-           secret_len > DPP_MAX_SHARED_SECRET_LEN ||
-           EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
-               wpa_printf(MSG_ERROR,
-                          "DPP: Failed to derive ECDH shared secret: %s",
-                          ERR_error_string(ERR_get_error(), NULL));
-               goto fail;
+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);
        }
-       auth->secret_len = secret_len;
-       EVP_PKEY_CTX_free(ctx);
-       ctx = NULL;
+}
 
-       wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
-                       auth->Mx, auth->secret_len);
 
-       if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
-                         auth->curve->hash_len) < 0)
-               goto fail;
+static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
+                                         const struct wpabuf *pi,
+                                         size_t nonce_len,
+                                         const u8 *r_pubkey_hash,
+                                         const u8 *i_pubkey_hash,
+                                         unsigned int neg_freq)
+{
+       struct wpabuf *msg;
+       u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
+       u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
+       u8 *pos;
+       const u8 *addr[2];
+       size_t len[2], siv_len, attr_len;
+       u8 *attr_start, *attr_end;
 
        /* Build DPP Authentication Request frame attributes */
-       attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + wpabuf_len(pi) +
+       attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
                4 + sizeof(wrapped_data);
+       if (neg_freq > 0)
+               attr_len += 4 + 2;
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
+               attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
        msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
        if (!msg)
-               goto fail;
-       auth->req_msg = msg;
+               return NULL;
 
        attr_start = wpabuf_put(msg, 0);
 
        /* 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);
+       dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
 
        /* Initiator Bootstrapping Key Hash */
-       wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
-       wpabuf_put_le16(msg, SHA256_MAC_LEN);
-       if (auth->own_bi)
-               wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
-       else
-               os_memset(wpabuf_put(msg, SHA256_MAC_LEN), 0, SHA256_MAC_LEN);
+       dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
 
        /* Initiator Protocol Key */
-       wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
-       wpabuf_put_le16(msg, wpabuf_len(pi));
-       wpabuf_put_buf(msg, pi);
-       wpabuf_free(pi);
-       pi = NULL;
+       if (pi) {
+               wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+               wpabuf_put_le16(msg, wpabuf_len(pi));
+               wpabuf_put_buf(msg, pi);
+       }
+
+       /* Channel */
+       if (neg_freq > 0) {
+               u8 op_class, channel;
+
+               if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
+                                                 &channel) ==
+                   NUM_HOSTAPD_MODES) {
+                       wpa_printf(MSG_INFO,
+                                  "DPP: Unsupported negotiation frequency request: %d",
+                                  neg_freq);
+                       wpabuf_free(msg);
+                       return NULL;
+               }
+               wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
+               wpabuf_put_le16(msg, 2);
+               wpabuf_put_u8(msg, op_class);
+               wpabuf_put_u8(msg, channel);
+       }
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+               goto skip_wrapped_data;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
 
        /* Wrapped data ({I-nonce, I-capabilities}k1) */
        pos = clear;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
+               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 */
        WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
        pos += 2;
@@ -1432,14 +1614,29 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx,
        pos += 2;
        os_memcpy(pos, auth->i_nonce, nonce_len);
        pos += nonce_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_nonce:
+       if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
+               goto skip_i_capab;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        /* I-capabilities */
        WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
        pos += 2;
        WPA_PUT_LE16(pos, 1);
        pos += 2;
-       auth->i_capab = configurator ? DPP_CAPAB_CONFIGURATOR :
-               DPP_CAPAB_ENROLLEE;
+       auth->i_capab = auth->allowed_roles;
        *pos++ = auth->i_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
+               pos[-1] = 0;
+       }
+skip_i_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
 
        attr_end = wpabuf_put(msg, 0);
 
@@ -1456,8 +1653,10 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx,
        siv_len = pos - clear;
        wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
        if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
-                           2, addr, len, wrapped_data) < 0)
-               goto fail;
+                           2, addr, len, wrapped_data) < 0) {
+               wpabuf_free(msg);
+               return NULL;
+       }
        siv_len += AES_BLOCK_SIZE;
        wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
                    wrapped_data, siv_len);
@@ -1466,54 +1665,579 @@ struct dpp_authentication * dpp_auth_init(void *msg_ctx,
        wpabuf_put_le16(msg, siv_len);
        wpabuf_put_data(msg, wrapped_data, siv_len);
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_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 */
+
        wpa_hexdump_buf(MSG_DEBUG,
                        "DPP: Authentication Request frame attributes", msg);
 
-       return auth;
-fail:
-       wpabuf_free(pi);
-       EVP_PKEY_CTX_free(ctx);
-       dpp_auth_deinit(auth);
-       return NULL;
+       return msg;
 }
 
 
-struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
-                                  const char *json)
+static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
+                                          enum dpp_status_error status,
+                                          const struct wpabuf *pr,
+                                          size_t nonce_len,
+                                          const u8 *r_pubkey_hash,
+                                          const u8 *i_pubkey_hash,
+                                          const u8 *r_nonce, const u8 *i_nonce,
+                                          const u8 *wrapped_r_auth,
+                                          size_t wrapped_r_auth_len,
+                                          const u8 *siv_key)
 {
-       size_t nonce_len;
-       size_t json_len, clear_len;
-       struct wpabuf *clear = NULL, *msg = NULL;
-       u8 *wrapped;
-
-       wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
+       struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+               4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
+       u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+       u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+       const u8 *addr[2];
+       size_t len[2], siv_len, attr_len;
+       u8 *attr_start, *attr_end, *pos;
 
-       nonce_len = auth->curve->nonce_len;
-       if (random_get_bytes(auth->e_nonce, nonce_len)) {
-               wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
-               goto fail;
-       }
-       wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
-       json_len = os_strlen(json);
-       wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len);
+       auth->waiting_auth_conf = 1;
+       auth->auth_resp_tries = 0;
 
-       /* { E-nonce, configAttrib }ke */
-       clear_len = 4 + nonce_len + 4 + json_len;
-       clear = wpabuf_alloc(clear_len);
-       msg = wpabuf_alloc(4 + clear_len + AES_BLOCK_SIZE);
-       if (!clear || !msg)
-               goto fail;
+       /* 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 += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+       msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+       if (!msg)
+               return NULL;
+
+       attr_start = wpabuf_put(msg, 0);
+
+       /* DPP Status */
+       if (status != 255)
+               dpp_build_attr_status(msg, status);
+
+       /* Responder Bootstrapping Key Hash */
+       dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+       /* Initiator Bootstrapping Key Hash (mutual authentication) */
+       dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+       /* Responder Protocol Key */
+       if (pr) {
+               wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+               wpabuf_put_le16(msg, wpabuf_len(pr));
+               wpabuf_put_buf(msg, pr);
+       }
+
+       attr_end = wpabuf_put(msg, 0);
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+               goto skip_wrapped_data;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+       pos = clear;
+
+       if (r_nonce) {
+               /* R-nonce */
+               WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+               pos += 2;
+               WPA_PUT_LE16(pos, nonce_len);
+               pos += 2;
+               os_memcpy(pos, r_nonce, nonce_len);
+               pos += nonce_len;
+       }
+
+       if (i_nonce) {
+               /* I-nonce */
+               WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+               pos += 2;
+               WPA_PUT_LE16(pos, nonce_len);
+               pos += 2;
+               os_memcpy(pos, i_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+               if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
+                       wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
+                       pos[nonce_len / 2] ^= 0x01;
+               }
+#endif /* CONFIG_TESTING_OPTIONS */
+               pos += nonce_len;
+       }
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
+               goto skip_r_capab;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       /* R-capabilities */
+       WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+       pos += 2;
+       WPA_PUT_LE16(pos, 1);
+       pos += 2;
+       auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+               DPP_CAPAB_ENROLLEE;
+       *pos++ = auth->r_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
+               pos[-1] = 0;
+       } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
+               wpa_printf(MSG_INFO,
+                          "DPP: TESTING - incompatible R-capabilities");
+               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 */
+
+       if (wrapped_r_auth) {
+               /* {R-auth}ke */
+               WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+               pos += 2;
+               WPA_PUT_LE16(pos, wrapped_r_auth_len);
+               pos += 2;
+               os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+               pos += wrapped_r_auth_len;
+       }
+
+       /* OUI, OUI type, Crypto Suite, DPP frame type */
+       addr[0] = wpabuf_head_u8(msg) + 2;
+       len[0] = 3 + 1 + 1 + 1;
+       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+       /* Attributes before Wrapped Data */
+       addr[1] = attr_start;
+       len[1] = attr_end - attr_start;
+       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+       siv_len = pos - clear;
+       wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+       if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
+                           2, addr, len, wrapped_data) < 0) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+       siv_len += AES_BLOCK_SIZE;
+       wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+                   wrapped_data, siv_len);
+
+       wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+       wpabuf_put_le16(msg, siv_len);
+       wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
+               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 */
+
+       wpa_hexdump_buf(MSG_DEBUG,
+                       "DPP: Authentication Response frame attributes", msg);
+       return msg;
+}
+
+
+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,
+                                         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;
+       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->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");
+               goto fail;
+       }
+       wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+
+       auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+       if (!auth->own_protocol_key)
+               goto fail;
+
+       pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+       if (!pi)
+               goto fail;
+
+       /* ECDH: M = pI * BR */
+       ctx = EVP_PKEY_CTX_new(auth->own_protocol_key, NULL);
+       if (!ctx ||
+           EVP_PKEY_derive_init(ctx) != 1 ||
+           EVP_PKEY_derive_set_peer(ctx, auth->peer_bi->pubkey) != 1 ||
+           EVP_PKEY_derive(ctx, NULL, &secret_len) != 1 ||
+           secret_len > DPP_MAX_SHARED_SECRET_LEN ||
+           EVP_PKEY_derive(ctx, auth->Mx, &secret_len) != 1) {
+               wpa_printf(MSG_ERROR,
+                          "DPP: Failed to derive ECDH shared secret: %s",
+                          ERR_error_string(ERR_get_error(), NULL));
+               goto fail;
+       }
+       auth->secret_len = secret_len;
+       EVP_PKEY_CTX_free(ctx);
+       ctx = NULL;
+
+       wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+                       auth->Mx, auth->secret_len);
+
+       if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+                         auth->curve->hash_len) < 0)
+               goto fail;
+
+       r_pubkey_hash = auth->peer_bi->pubkey_hash;
+       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 */
+
+       auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
+                                          i_pubkey_hash, neg_freq);
+       if (!auth->req_msg)
+               goto fail;
+
+out:
+       wpabuf_free(pi);
+       EVP_PKEY_CTX_free(ctx);
+       return auth;
+fail:
+       dpp_auth_deinit(auth);
+       auth = NULL;
+       goto out;
+}
+
+
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+                                  const char *json)
+{
+       size_t nonce_len;
+       size_t json_len, clear_len;
+       struct wpabuf *clear = NULL, *msg = NULL;
+       u8 *wrapped;
+       size_t attr_len;
+
+       wpa_printf(MSG_DEBUG, "DPP: Build configuration request");
+
+       nonce_len = auth->curve->nonce_len;
+       if (random_get_bytes(auth->e_nonce, nonce_len)) {
+               wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
+               goto fail;
+       }
+       wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len);
+       json_len = os_strlen(json);
+       wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len);
+
+       /* { E-nonce, configAttrib }ke */
+       clear_len = 4 + nonce_len + 4 + json_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_CONF_REQ)
+               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);
@@ -1527,6 +2251,14 @@ struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
        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_CONF_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 */
+
        wpa_hexdump_buf(MSG_DEBUG,
                        "DPP: Configuration Request frame attributes", msg);
        wpabuf_free(clear);
@@ -1725,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 */
 
@@ -1765,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:
@@ -1800,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) */
 
@@ -1835,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:
@@ -1858,25 +2572,25 @@ fail:
 }
 
 
-static int dpp_auth_build_resp(struct dpp_authentication *auth)
+static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
 {
        size_t nonce_len;
        EVP_PKEY_CTX *ctx = NULL;
        size_t secret_len;
        struct wpabuf *msg, *pr = NULL;
        u8 r_auth[4 + DPP_MAX_HASH_LEN];
-       u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE];
-#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
-               4 + sizeof(wrapped_r_auth)
+       u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
        size_t wrapped_r_auth_len;
-       u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
-       u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
-       u8 *pos;
-       const u8 *addr[2];
-       size_t len[2], siv_len, attr_len;
-       u8 *attr_start, *attr_end;
+       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)) {
@@ -1928,215 +2642,163 @@ static int dpp_auth_build_resp(struct dpp_authentication *auth)
        /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
        WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
        WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
-       if (dpp_gen_r_auth(auth, r_auth + 4) < 0 ||
-           aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+       if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
+               goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
+               r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+       if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
                            r_auth, 4 + auth->curve->hash_len,
                            0, NULL, NULL, wrapped_r_auth) < 0)
                goto fail;
        wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
        wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
                    wrapped_r_auth, wrapped_r_auth_len);
+       w_r_auth = wrapped_r_auth;
 
-       /* Build DPP Authentication Response frame attributes */
-       attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
-               4 + wpabuf_len(pr) + 4 + sizeof(wrapped_data);
-       msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
-       if (!msg)
-               goto fail;
-       wpabuf_free(auth->resp_msg);
-       auth->resp_msg = msg;
-
-       attr_start = wpabuf_put(msg, 0);
-
-       /* DPP Status */
-       wpabuf_put_le16(msg, DPP_ATTR_STATUS);
-       wpabuf_put_le16(msg, 1);
-       wpabuf_put_u8(msg, DPP_STATUS_OK);
-
-       /* Responder Bootstrapping Key Hash */
-       wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
-       wpabuf_put_le16(msg, SHA256_MAC_LEN);
-       wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
-
-       if (auth->peer_bi) {
-               /* Mutual authentication */
-               /* Initiator Bootstrapping Key Hash */
-               wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
-               wpabuf_put_le16(msg, SHA256_MAC_LEN);
-               wpabuf_put_data(msg, auth->peer_bi->pubkey_hash,
-                               SHA256_MAC_LEN);
-       }
-
-       /* Responder Protocol Key */
-       wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
-       wpabuf_put_le16(msg, wpabuf_len(pr));
-       wpabuf_put_buf(msg, pr);
-       wpabuf_free(pr);
-       pr = NULL;
-
-       attr_end = wpabuf_put(msg, 0);
-
-       /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
-       pos = clear;
-       /* R-nonce */
-       WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
-       pos += 2;
-       WPA_PUT_LE16(pos, nonce_len);
-       pos += 2;
-       os_memcpy(pos, auth->r_nonce, nonce_len);
-       pos += nonce_len;
-       /* I-nonce */
-       WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
-       pos += 2;
-       WPA_PUT_LE16(pos, nonce_len);
-       pos += 2;
-       os_memcpy(pos, auth->i_nonce, nonce_len);
-       pos += nonce_len;
-       /* R-capabilities */
-       WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
-       pos += 2;
-       WPA_PUT_LE16(pos, 1);
-       pos += 2;
-       auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
-               DPP_CAPAB_ENROLLEE;
-       *pos++ = auth->r_capab;
-       /* {R-auth}ke */
-       WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
-       pos += 2;
-       WPA_PUT_LE16(pos, wrapped_r_auth_len);
-       pos += 2;
-       os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
-       pos += wrapped_r_auth_len;
-
-       /* OUI, OUI type, Crypto Suite, DPP frame type */
-       addr[0] = wpabuf_head_u8(msg) + 2;
-       len[0] = 3 + 1 + 1 + 1;
-       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
-
-       /* Attributes before Wrapped Data */
-       addr[1] = attr_start;
-       len[1] = attr_end - attr_start;
-       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
-
-       siv_len = pos - clear;
-       wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
-       if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, clear, siv_len,
-                           2, addr, len, wrapped_data) < 0)
-               goto fail;
-       siv_len += AES_BLOCK_SIZE;
-       wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
-                   wrapped_data, siv_len);
-
-       wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
-       wpabuf_put_le16(msg, siv_len);
-       wpabuf_put_data(msg, wrapped_data, siv_len);
-
-       wpa_hexdump_buf(MSG_DEBUG,
-                       "DPP: Authentication Response frame attributes", msg);
-
-       return 0;
-
-fail:
-       wpabuf_free(pr);
-       return -1;
-}
-
-
-static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
-                                     enum dpp_status_error status)
-{
-       size_t nonce_len;
-       struct wpabuf *msg;
-#define DPP_AUTH_RESP_CLEAR_LEN2 4 + DPP_MAX_NONCE_LEN + 4 + 1
-       u8 clear[DPP_AUTH_RESP_CLEAR_LEN2];
-       u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN2 + AES_BLOCK_SIZE];
-       u8 *pos;
-       const u8 *addr[2];
-       size_t len[2], siv_len, attr_len;
-       u8 *attr_start, *attr_end;
-
-       wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
-
-       /* Build DPP Authentication Response frame attributes */
-       attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + 4 + sizeof(wrapped_data);
-       msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
-       if (!msg)
-               goto fail;
-       wpabuf_free(auth->resp_msg);
-       auth->resp_msg = msg;
-
-       attr_start = wpabuf_put(msg, 0);
-
-       /* DPP Status */
-       wpabuf_put_le16(msg, DPP_ATTR_STATUS);
-       wpabuf_put_le16(msg, 1);
-       wpabuf_put_u8(msg, status);
-
-       /* Responder Bootstrapping Key Hash */
-       wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH);
-       wpabuf_put_le16(msg, SHA256_MAC_LEN);
-       wpabuf_put_data(msg, auth->own_bi->pubkey_hash, SHA256_MAC_LEN);
-
-       if (auth->peer_bi) {
-               /* Mutual authentication */
-               /* Initiator Bootstrapping Key Hash */
-               wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
-               wpabuf_put_le16(msg, SHA256_MAC_LEN);
-               wpabuf_put_data(msg, auth->peer_bi->pubkey_hash,
-                               SHA256_MAC_LEN);
-       }
-
-       attr_end = wpabuf_put(msg, 0);
-
-       /* Wrapped data ({I-nonce, R-capabilities}k1) */
-       pos = clear;
-       /* I-nonce */
-       nonce_len = auth->curve->nonce_len;
-       WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
-       pos += 2;
-       WPA_PUT_LE16(pos, nonce_len);
-       pos += 2;
-       os_memcpy(pos, auth->i_nonce, nonce_len);
-       pos += nonce_len;
-       /* R-capabilities */
-       WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
-       pos += 2;
-       WPA_PUT_LE16(pos, 1);
-       pos += 2;
-       auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
-               DPP_CAPAB_ENROLLEE;
-       *pos++ = auth->r_capab;
+       r_pubkey_hash = auth->own_bi->pubkey_hash;
+       if (auth->peer_bi)
+               i_pubkey_hash = auth->peer_bi->pubkey_hash;
+       else
+               i_pubkey_hash = NULL;
 
-       /* OUI, OUI type, Crypto Suite, DPP frame type */
-       addr[0] = wpabuf_head_u8(msg) + 2;
-       len[0] = 3 + 1 + 1 + 1;
-       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+       i_nonce = auth->i_nonce;
+       r_nonce = auth->r_nonce;
 
-       /* Attributes before Wrapped Data */
-       addr[1] = attr_start;
-       len[1] = attr_end - attr_start;
-       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+#ifdef CONFIG_TESTING_OPTIONS
+       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;
+               wrapped_r_auth_len = 0;
+       } 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;
+       } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+               i_nonce = NULL;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
 
-       siv_len = pos - clear;
-       wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
-       if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
-                           2, addr, len, wrapped_data) < 0)
+       msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
+                                 r_pubkey_hash, i_pubkey_hash,
+                                 r_nonce, i_nonce,
+                                 w_r_auth, wrapped_r_auth_len,
+                                 auth->k2);
+       if (!msg)
                goto fail;
-       siv_len += AES_BLOCK_SIZE;
-       wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
-                   wrapped_data, siv_len);
+       wpabuf_free(auth->resp_msg);
+       auth->resp_msg = msg;
+       ret = 0;
+fail:
+       wpabuf_free(pr);
+       return ret;
+}
 
-       wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
-       wpabuf_put_le16(msg, siv_len);
-       wpabuf_put_data(msg, wrapped_data, siv_len);
 
-       wpa_hexdump_buf(MSG_DEBUG,
-                       "DPP: Authentication Response frame attributes", msg);
+static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
+                                     enum dpp_status_error status)
+{
+       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 */
 
-       return 0;
+       if (!auth->own_bi)
+               return -1;
+       wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
 
-fail:
-       return -1;
+       r_pubkey_hash = auth->own_bi->pubkey_hash;
+       if (auth->peer_bi)
+               i_pubkey_hash = auth->peer_bi->pubkey_hash;
+       else
+               i_pubkey_hash = NULL;
+
+       i_nonce = auth->i_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       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;
+       } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+               wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+               i_nonce = NULL;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
+                                 r_pubkey_hash, i_pubkey_hash,
+                                 NULL, i_nonce, NULL, 0, auth->k1);
+       if (!msg)
+               return -1;
+       wpabuf_free(auth->resp_msg);
+       auth->resp_msg = msg;
+       return 0;
 }
 
 
@@ -2145,7 +2807,7 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
                struct dpp_bootstrap_info *peer_bi,
                struct dpp_bootstrap_info *own_bi,
                unsigned int freq, const u8 *hdr, const u8 *attr_start,
-               const u8 *wrapped_data, u16 wrapped_data_len)
+               size_t attr_len)
 {
        EVP_PKEY *pi = NULL;
        EVP_PKEY_CTX *ctx = NULL;
@@ -2154,14 +2816,21 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
        size_t len[2];
        u8 *unwrapped = NULL;
        size_t unwrapped_len = 0;
-       const u8 *i_proto, *i_nonce, *i_capab, *i_bootstrap;
-       u16 i_proto_len, i_nonce_len, i_capab_len, i_bootstrap_len;
+       const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
+               *channel;
+       u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
+               i_bootstrap_len, channel_len;
        struct dpp_authentication *auth = NULL;
-       size_t attr_len;
 
-       if (wrapped_data_len < AES_BLOCK_SIZE)
+       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_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+                       "Missing or invalid required Wrapped Data attribute");
                return NULL;
-
+       }
+       wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+                   wrapped_data, wrapped_data_len);
        attr_len = wrapped_data - 4 - attr_start;
 
        auth = os_zalloc(sizeof(*auth));
@@ -2173,11 +2842,39 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
        auth->curve = own_bi->curve;
        auth->curr_freq = freq;
 
+       channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
+                              &channel_len);
+       if (channel) {
+               int neg_freq;
+
+               if (channel_len < 2) {
+                       dpp_auth_fail(auth, "Too short Channel attribute");
+                       goto fail;
+               }
+
+               neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
+               wpa_printf(MSG_DEBUG,
+                          "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
+                          channel[0], channel[1], neg_freq);
+               if (neg_freq < 0) {
+                       dpp_auth_fail(auth,
+                                     "Unsupported Channel attribute value");
+                       goto fail;
+               }
+
+               if (auth->curr_freq != (unsigned int) neg_freq) {
+                       wpa_printf(MSG_DEBUG,
+                                  "DPP: Changing negotiation channel from %u MHz to %u MHz",
+                                  freq, neg_freq);
+                       auth->curr_freq = neg_freq;
+               }
+       }
+
        i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
                               &i_proto_len);
        if (!i_proto) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing required Initiator Protocol Key attribute");
+               dpp_auth_fail(auth,
+                             "Missing required Initiator Protocol Key attribute");
                goto fail;
        }
        wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
@@ -2186,7 +2883,7 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
        /* M = bR * PI */
        pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
        if (!pi) {
-               wpa_printf(MSG_DEBUG, "DPP: Invalid Initiator Protocol Key");
+               dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
                goto fail;
        }
        dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
@@ -2201,6 +2898,7 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
                wpa_printf(MSG_ERROR,
                           "DPP: Failed to derive ECDH shared secret: %s",
                           ERR_error_string(ERR_get_error(), NULL));
+               dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
                goto fail;
        }
        auth->secret_len = secret_len;
@@ -2229,22 +2927,21 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
        if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
                            wrapped_data, wrapped_data_len,
                            2, 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;
        }
 
        i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
                               &i_nonce_len);
        if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
-               wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+               dpp_auth_fail(auth, "Missing or invalid I-nonce");
                goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
@@ -2254,7 +2951,7 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
                               DPP_ATTR_I_CAPABILITIES,
                               &i_capab_len);
        if (!i_capab || i_capab_len < 1) {
-               wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-capabilities");
+               dpp_auth_fail(auth, "Missing or invalid I-capabilities");
                goto fail;
        }
        auth->i_capab = i_capab[0];
@@ -2282,9 +2979,25 @@ 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");
-               goto not_compatible;
+               wpa_msg(auth->msg_ctx, MSG_INFO,
+                       DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
+                       auth->i_capab & DPP_CAPAB_ROLE_MASK);
+               goto fail;
        }
 
        auth->peer_protocol_key = pi;
@@ -2314,7 +3027,7 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
                        "%s", hex);
                return auth;
        }
-       if (dpp_auth_build_resp(auth) < 0)
+       if (dpp_auth_build_resp_ok(auth) < 0)
                goto fail;
 
        return auth;
@@ -2355,52 +3068,107 @@ int dpp_notify_new_qr_code(struct dpp_authentication *auth,
                   MACSTR, MAC2STR(auth->peer_mac_addr));
        auth->peer_bi = peer_bi;
 
-       if (dpp_auth_build_resp(auth) < 0)
+       if (dpp_auth_build_resp_ok(auth) < 0)
                return -1;
 
        return 1;
 }
 
 
-static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth)
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
+                                          enum dpp_status_error status)
 {
        struct wpabuf *msg;
        u8 i_auth[4 + DPP_MAX_HASH_LEN];
        size_t i_auth_len;
+       u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
+       size_t r_nonce_len;
        const u8 *addr[2];
        size_t len[2], attr_len;
        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");
 
        i_auth_len = 4 + auth->curve->hash_len;
+       r_nonce_len = 4 + auth->curve->nonce_len;
        /* Build DPP Authentication Confirmation frame attributes */
        attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
-               4 + i_auth_len + AES_BLOCK_SIZE;
+               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 += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
        msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
        if (!msg)
                goto fail;
 
        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) {
+               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, DPP_STATUS_OK);
+       dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+       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);
+       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
+       if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
+               goto skip_wrapped_data;
+       if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+               i_auth_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
 
        attr_end = wpabuf_put(msg, 0);
 
@@ -2414,24 +3182,68 @@ static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth)
        len[1] = attr_end - attr_start;
        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, i_auth_len + AES_BLOCK_SIZE);
-       wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
-       /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
-       WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
-       WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
-       if (dpp_gen_i_auth(auth, i_auth + 4) < 0 ||
-           aes_siv_encrypt(auth->ke, auth->curve->hash_len,
-                           i_auth, i_auth_len,
-                           2, addr, len, wrapped_i_auth) < 0)
-               goto fail;
-       wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
-                   wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+       if (status == DPP_STATUS_OK) {
+               /* I-auth wrapped with ke */
+               wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+               wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+               wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+               if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+                       goto skip_i_auth;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+               /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
+                *            1) */
+               WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+               WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+               if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
+                       goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+               if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
+                       wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
+                       i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+               }
+skip_i_auth:
+#endif /* CONFIG_TESTING_OPTIONS */
+               if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+                                   i_auth, i_auth_len,
+                                   2, addr, len, wrapped_i_auth) < 0)
+                       goto fail;
+               wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+                           wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+       } else {
+               /* R-nonce wrapped with k2 */
+               wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+               wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
+               wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
+
+               WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
+               WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
+               os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
+
+               if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
+                                   r_nonce, r_nonce_len,
+                                   2, addr, len, wrapped_r_nonce) < 0)
+                       goto fail;
+               wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
+                           wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
+       }
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
+               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 */
 
        wpa_hexdump_buf(MSG_DEBUG,
                        "DPP: Authentication Confirmation frame attributes",
                        msg);
-       dpp_auth_success(auth);
+       if (status == DPP_STATUS_OK)
+               dpp_auth_success(auth);
 
        return msg;
 
@@ -2463,6 +3275,7 @@ dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
                wpa_printf(MSG_DEBUG,
                           "DPP: Responder reported failure (status %d)",
                           status);
+               dpp_auth_fail(auth, "Responder reported failure");
                return;
        }
 
@@ -2481,27 +3294,26 @@ dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
        if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
                            wrapped_data, wrapped_data_len,
                            2, 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;
        }
 
        i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
                               &i_nonce_len);
        if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
-               wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+               dpp_auth_fail(auth, "Missing or invalid I-nonce");
                goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
        if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
-               wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch");
+               dpp_auth_fail(auth, "I-nonce mismatch");
                goto fail;
        }
 
@@ -2509,7 +3321,7 @@ dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
                               DPP_ATTR_R_CAPABILITIES,
                               &r_capab_len);
        if (!r_capab || r_capab_len < 1) {
-               wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities");
+               dpp_auth_fail(auth, "Missing or invalid R-capabilities");
                goto fail;
        }
        auth->r_capab = r_capab[0];
@@ -2518,9 +3330,20 @@ dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
                wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
                        "r-capab=0x%02x", auth->r_capab);
        } else if (status == DPP_STATUS_RESPONSE_PENDING) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Continue waiting for full DPP Authentication Response");
-               wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_RESPONSE_PENDING);
+               u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+
+               if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+                   (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+                       wpa_msg(auth->msg_ctx, MSG_INFO,
+                               DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
+                               role);
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "DPP: Continue waiting for full DPP Authentication Response");
+                       wpa_msg(auth->msg_ctx, MSG_INFO,
+                               DPP_EVENT_RESPONSE_PENDING "%s",
+                               auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
+               }
        }
 fail:
        bin_clear_free(unwrapped, unwrapped_len);
@@ -2544,34 +3367,41 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
                r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
                wrapped2_len, r_auth_len;
        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) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing required Wrapped data attribute");
+       if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+               dpp_auth_fail(auth,
+                             "Missing or invalid required Wrapped Data attribute");
                return NULL;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
                    wrapped_data, wrapped_data_len);
 
-       if (wrapped_data_len < AES_BLOCK_SIZE)
-               return NULL;
-
        attr_len = wrapped_data - 4 - attr_start;
 
        r_bootstrap = dpp_get_attr(attr_start, attr_len,
                                   DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
                                   &r_bootstrap_len);
        if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+               dpp_auth_fail(auth,
+                             "Missing or invalid required Responder Bootstrapping Key Hash attribute");
                return NULL;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
                    r_bootstrap, r_bootstrap_len);
        if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
                      SHA256_MAC_LEN) != 0) {
+               dpp_auth_fail(auth,
+                             "Unexpected Responder Bootstrapping Key Hash value");
                wpa_hexdump(MSG_DEBUG,
                            "DPP: Expected Responder Bootstrapping Key Hash",
                            auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
@@ -2583,8 +3413,8 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
                                   &i_bootstrap_len);
        if (i_bootstrap) {
                if (i_bootstrap_len != SHA256_MAC_LEN) {
-                       wpa_printf(MSG_DEBUG,
-                                  "DPP: Invalid Initiator Bootstrapping Key Hash attribute");
+                       dpp_auth_fail(auth,
+                                     "Invalid Initiator Bootstrapping Key Hash attribute");
                        return NULL;
                }
                wpa_hexdump(MSG_MSGDUMP,
@@ -2593,17 +3423,22 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
                if (!auth->own_bi ||
                    os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
                              SHA256_MAC_LEN) != 0) {
-                       wpa_printf(MSG_DEBUG,
-                                  "DPP: Initiator Bootstrapping Key Hash attribute did not match");
+                       dpp_auth_fail(auth,
+                                     "Initiator Bootstrapping Key Hash attribute did not match");
                        return NULL;
                }
+       } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
+               /* PKEX bootstrapping mandates use of mutual authentication */
+               dpp_auth_fail(auth,
+                             "Missing Initiator Bootstrapping Key Hash attribute");
+               return NULL;
        }
 
        status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
                              &status_len);
        if (!status || status_len < 1) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid required DPP Status attribute");
+               dpp_auth_fail(auth,
+                             "Missing or invalid required DPP Status attribute");
                return NULL;
        }
        wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
@@ -2615,11 +3450,17 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
                return NULL;
        }
 
+       if (!i_bootstrap && auth->own_bi) {
+               wpa_printf(MSG_DEBUG,
+                          "DPP: Responder decided not to use mutual authentication");
+               auth->own_bi = NULL;
+       }
+
        r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
                               &r_proto_len);
        if (!r_proto) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing required Responder Protocol Key attribute");
+               dpp_auth_fail(auth,
+                             "Missing required Responder Protocol Key attribute");
                return NULL;
        }
        wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
@@ -2628,7 +3469,7 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
        /* N = pI * PR */
        pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
        if (!pr) {
-               wpa_printf(MSG_DEBUG, "DPP: Invalid Responder Protocol Key");
+               dpp_auth_fail(auth, "Invalid Responder Protocol Key");
                return NULL;
        }
        dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
@@ -2643,6 +3484,7 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
                wpa_printf(MSG_ERROR,
                           "DPP: Failed to derive ECDH shared secret: %s",
                           ERR_error_string(ERR_get_error(), NULL));
+               dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
                goto fail;
        }
        EVP_PKEY_CTX_free(ctx);
@@ -2672,22 +3514,21 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
        if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
                            wrapped_data, wrapped_data_len,
                            2, 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;
        }
 
        r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
                               &r_nonce_len);
        if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
-               wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-nonce");
+               dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
                goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
@@ -2696,12 +3537,12 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
        i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
                               &i_nonce_len);
        if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
-               wpa_printf(MSG_DEBUG, "DPP: Missing or invalid I-nonce");
+               dpp_auth_fail(auth, "Missing or invalid I-nonce");
                goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
        if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
-               wpa_printf(MSG_DEBUG, "DPP: I-nonce mismatch");
+               dpp_auth_fail(auth, "I-nonce mismatch");
                goto fail;
        }
 
@@ -2711,34 +3552,52 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
                        goto fail;
        }
 
-       if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
-               goto fail;
-
        r_capab = dpp_get_attr(unwrapped, unwrapped_len,
                               DPP_ATTR_R_CAPABILITIES,
                               &r_capab_len);
        if (!r_capab || r_capab_len < 1) {
-               wpa_printf(MSG_DEBUG, "DPP: Missing or invalid R-capabilities");
+               dpp_auth_fail(auth, "Missing or invalid R-capabilities");
                goto fail;
        }
        auth->r_capab = r_capab[0];
        wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
-       if ((auth->configurator && (auth->r_capab & DPP_CAPAB_CONFIGURATOR)) ||
-           (!auth->configurator && (auth->r_capab & DPP_CAPAB_ENROLLEE))) {
+       role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+       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");
-               goto fail;
+               wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+                       "Unexpected role in R-capabilities 0x%02x",
+                       role);
+               if (role != DPP_CAPAB_ENROLLEE &&
+                   role != DPP_CAPAB_CONFIGURATOR)
+                       goto fail;
+               bin_clear_free(unwrapped, unwrapped_len);
+               auth->remove_on_tx_status = 1;
+               return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
        }
 
        wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
                                DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
        if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid Secondary Wrapped Data");
+               dpp_auth_fail(auth,
+                             "Missing or invalid Secondary Wrapped Data");
                goto fail;
        }
 
        wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
                    wrapped2, wrapped2_len);
+
+       if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0)
+               goto fail;
+
        unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
        unwrapped2 = os_malloc(unwrapped2_len);
        if (!unwrapped2)
@@ -2746,23 +3605,23 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
        if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
                            wrapped2, wrapped2_len,
                            0, NULL, NULL, unwrapped2) < 0) {
-               wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+               dpp_auth_fail(auth, "AES-SIV decryption failed");
                goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
                    unwrapped2, unwrapped2_len);
 
        if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Invalid attribute in secondary unwrapped data");
+               dpp_auth_fail(auth,
+                             "Invalid attribute in secondary unwrapped data");
                goto fail;
        }
 
        r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
                               &r_auth_len);
        if (!r_auth || r_auth_len != auth->curve->hash_len) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid Responder Authenticating Tag");
+               dpp_auth_fail(auth,
+                             "Missing or invalid Responder Authenticating Tag");
                goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
@@ -2773,15 +3632,27 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
        wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
                    r_auth2, r_auth_len);
        if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Mismatching Responder Authenticating Tag");
-               goto fail;
+               dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
+               bin_clear_free(unwrapped, unwrapped_len);
+               bin_clear_free(unwrapped2, unwrapped2_len);
+               auth->remove_on_tx_status = 1;
+               return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
        }
 
        bin_clear_free(unwrapped, unwrapped_len);
        bin_clear_free(unwrapped2, unwrapped2_len);
 
-       return dpp_auth_build_conf(auth);
+#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:
        bin_clear_free(unwrapped, unwrapped_len);
@@ -2792,6 +3663,77 @@ fail:
 }
 
 
+static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
+                                   const u8 *hdr,
+                                   const u8 *attr_start, size_t attr_len,
+                                   const u8 *wrapped_data,
+                                   u16 wrapped_data_len,
+                                   enum dpp_status_error status)
+{
+       const u8 *addr[2];
+       size_t len[2];
+       u8 *unwrapped = NULL;
+       size_t unwrapped_len = 0;
+       const u8 *r_nonce;
+       u16 r_nonce_len;
+
+       /* Authentication Confirm failure cases are expected to include
+        * {R-nonce}k2 in the Wrapped Data attribute. */
+
+       addr[0] = hdr;
+       len[0] = DPP_HDR_LEN;
+       addr[1] = attr_start;
+       len[1] = attr_len;
+       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]);
+       wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+                   wrapped_data, wrapped_data_len);
+       unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+       unwrapped = os_malloc(unwrapped_len);
+       if (!unwrapped) {
+               dpp_auth_fail(auth, "Authentication failed");
+               goto fail;
+       }
+       if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+                           wrapped_data, wrapped_data_len,
+                           2, addr, len, unwrapped) < 0) {
+               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) {
+               dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+               goto fail;
+       }
+
+       r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+                              &r_nonce_len);
+       if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+               dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+               goto fail;
+       }
+       if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
+               wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
+                           r_nonce, r_nonce_len);
+               wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
+                           auth->r_nonce, r_nonce_len);
+               dpp_auth_fail(auth, "R-nonce mismatch");
+               goto fail;
+       }
+
+       if (status == DPP_STATUS_NOT_COMPATIBLE)
+               dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
+       else if (status == DPP_STATUS_AUTH_FAILURE)
+               dpp_auth_fail(auth, "Peer reported authentication failure)");
+
+fail:
+       bin_clear_free(unwrapped, unwrapped_len);
+       return -1;
+}
+
+
 int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
                     const u8 *attr_start, size_t attr_len)
 {
@@ -2804,28 +3746,31 @@ 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) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing required Wrapped data attribute");
+       if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+               dpp_auth_fail(auth,
+                             "Missing or invalid required Wrapped Data attribute");
                return -1;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
                    wrapped_data, wrapped_data_len);
 
-       if (wrapped_data_len < AES_BLOCK_SIZE)
-               return -1;
-
        attr_len = wrapped_data - 4 - attr_start;
 
        r_bootstrap = dpp_get_attr(attr_start, attr_len,
                                   DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
                                   &r_bootstrap_len);
-       if (!r_bootstrap || r_bootstrap > wrapped_data ||
-           r_bootstrap_len != SHA256_MAC_LEN) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+       if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+               dpp_auth_fail(auth,
+                             "Missing or invalid required Responder Bootstrapping Key Hash attribute");
                return -1;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
@@ -2835,6 +3780,8 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
                wpa_hexdump(MSG_DEBUG,
                            "DPP: Expected Responder Bootstrapping Key Hash",
                            auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+               dpp_auth_fail(auth,
+                             "Responder Bootstrapping Key Hash mismatch");
                return -1;
        }
 
@@ -2842,10 +3789,9 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
                                   DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
                                   &i_bootstrap_len);
        if (i_bootstrap) {
-               if (i_bootstrap > wrapped_data ||
-                   i_bootstrap_len != SHA256_MAC_LEN) {
-                       wpa_printf(MSG_DEBUG,
-                                  "DPP: Invalid Initiator Bootstrapping Key Hash attribute");
+               if (i_bootstrap_len != SHA256_MAC_LEN) {
+                       dpp_auth_fail(auth,
+                                     "Invalid Initiator Bootstrapping Key Hash attribute");
                        return -1;
                }
                wpa_hexdump(MSG_MSGDUMP,
@@ -2854,22 +3800,34 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
                if (!auth->peer_bi ||
                    os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
                              SHA256_MAC_LEN) != 0) {
-                       wpa_printf(MSG_DEBUG,
-                                  "DPP: Initiator Bootstrapping Key Hash attribute did not match");
+                       dpp_auth_fail(auth,
+                                     "Initiator Bootstrapping Key Hash mismatch");
                        return -1;
                }
+       } else if (auth->own_bi && auth->peer_bi) {
+               /* Mutual authentication and peer did not include its
+                * Bootstrapping Key Hash attribute. */
+               dpp_auth_fail(auth,
+                             "Missing Initiator Bootstrapping Key Hash attribute");
+               return -1;
        }
 
        status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
                              &status_len);
        if (!status || status_len < 1) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid required DPP Status attribute");
+               dpp_auth_fail(auth,
+                             "Missing or invalid required DPP Status attribute");
                return -1;
        }
        wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+       if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
+           status[0] == DPP_STATUS_AUTH_FAILURE)
+               return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
+                                               attr_len, wrapped_data,
+                                               wrapped_data_len, status[0]);
+
        if (status[0] != DPP_STATUS_OK) {
-               wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+               dpp_auth_fail(auth, "Authentication failed");
                return -1;
        }
 
@@ -2888,23 +3846,22 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
        if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
                            wrapped_data, wrapped_data_len,
                            2, 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;
        }
 
        i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
                              &i_auth_len);
        if (!i_auth || i_auth_len != auth->curve->hash_len) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Missing or invalid Initiator Authenticating Tag");
+               dpp_auth_fail(auth,
+                             "Missing or invalid Initiator Authenticating Tag");
                goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
@@ -2915,8 +3872,7 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
        wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
                    i_auth2, i_auth_len);
        if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
-               wpa_printf(MSG_DEBUG,
-                          "DPP: Mismatching Initiator Authenticating Tag");
+               dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
                goto fail;
        }
 
@@ -2952,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);
@@ -2985,30 +3942,14 @@ dpp_build_conf_start(struct dpp_authentication *auth,
                wpabuf_put_u8(buf, ',');
                return buf;
        }
-#endif /* CONFIG_TESTING_OPTIONS */
-       wpabuf_put_str(buf, "{\"ssid\":\"");
-       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, "},");
-
-       return buf;
-}
-
-
-static int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
-{
-       int num_bytes, offset;
+#endif /* CONFIG_TESTING_OPTIONS */
+       wpabuf_put_str(buf, "{\"ssid\":\"");
+       json_escape_string(ssid, sizeof(ssid),
+                          (const char *) conf->ssid, conf->ssid_len);
+       wpabuf_put_str(buf, ssid);
+       wpabuf_put_str(buf, "\"},");
 
-       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;
+       return buf;
 }
 
 
@@ -3255,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];
 
@@ -3308,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);
 }
@@ -3319,7 +4260,7 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
                    u16 e_nonce_len, int ap)
 {
        struct wpabuf *conf;
-       size_t clear_len;
+       size_t clear_len, attr_len;
        struct wpabuf *clear = NULL, *msg = NULL;
        u8 *wrapped;
        const u8 *addr[1];
@@ -3338,27 +4279,71 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
        if (conf)
                clear_len += 4 + wpabuf_len(conf);
        clear = wpabuf_alloc(clear_len);
-       msg = wpabuf_alloc(4 + 1 + 4 + clear_len + AES_BLOCK_SIZE);
+       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 += 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);
@@ -3375,17 +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");
+               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;
 }
 
 
@@ -3402,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;
        }
 
@@ -3424,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;
        }
 
@@ -3440,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);
@@ -3450,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",
@@ -3459,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);
@@ -3495,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;
        }
 
@@ -3607,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");
@@ -3620,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;
 }
 
@@ -3950,11 +4955,11 @@ struct dpp_signed_connector_info {
        size_t payload_len;
 };
 
-static int
+static enum dpp_status_error
 dpp_process_signed_connector(struct dpp_signed_connector_info *info,
                             EVP_PKEY *csign_pub, const char *connector)
 {
-       int ret = -1;
+       enum dpp_status_error ret = 255;
        const char *pos, *end, *signed_start, *signed_end;
        struct wpabuf *kid = NULL;
        unsigned char *prot_hdr = NULL, *signature = NULL;
@@ -3988,6 +4993,7 @@ dpp_process_signed_connector(struct dpp_signed_connector_info *info,
        end = os_strchr(pos, '.');
        if (!end) {
                wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
        }
        prot_hdr = base64_url_decode((const unsigned char *) pos,
@@ -3995,18 +5001,22 @@ dpp_process_signed_connector(struct dpp_signed_connector_info *info,
        if (!prot_hdr) {
                wpa_printf(MSG_DEBUG,
                           "DPP: Failed to base64url decode signedConnector JWS Protected Header");
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
        }
        wpa_hexdump_ascii(MSG_DEBUG,
                          "DPP: signedConnector - JWS Protected Header",
                          prot_hdr, prot_hdr_len);
        kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
-       if (!kid)
+       if (!kid) {
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
+       }
        if (wpabuf_len(kid) != SHA256_MAC_LEN) {
                wpa_printf(MSG_DEBUG,
                           "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
                           (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
        }
 
@@ -4015,6 +5025,7 @@ dpp_process_signed_connector(struct dpp_signed_connector_info *info,
        if (!end) {
                wpa_printf(MSG_DEBUG,
                           "DPP: Missing dot(2) in signedConnector");
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
        }
        signed_end = end - 1;
@@ -4023,6 +5034,7 @@ dpp_process_signed_connector(struct dpp_signed_connector_info *info,
        if (!info->payload) {
                wpa_printf(MSG_DEBUG,
                           "DPP: Failed to base64url decode signedConnector JWS Payload");
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
        }
        wpa_hexdump_ascii(MSG_DEBUG,
@@ -4034,18 +5046,22 @@ dpp_process_signed_connector(struct dpp_signed_connector_info *info,
        if (!signature) {
                wpa_printf(MSG_DEBUG,
                           "DPP: Failed to base64url decode signedConnector signature");
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
                }
        wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
                    signature, signature_len);
 
-       if (dpp_check_pubkey_match(csign_pub, kid) < 0)
+       if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
+               ret = DPP_STATUS_NO_MATCH;
                goto fail;
+       }
 
        if (signature_len & 0x01) {
                wpa_printf(MSG_DEBUG,
                           "DPP: Unexpected signedConnector signature length (%d)",
                           (int) signature_len);
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
        }
 
@@ -4086,10 +5102,11 @@ dpp_process_signed_connector(struct dpp_signed_connector_info *info,
                wpa_printf(MSG_DEBUG,
                           "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
                           res, ERR_error_string(ERR_get_error(), NULL));
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
        }
 
-       ret = 0;
+       ret = DPP_STATUS_OK;
 fail:
        EC_KEY_free(eckey);
        EVP_MD_CTX_destroy(md_ctx);
@@ -4148,7 +5165,7 @@ static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
        }
 
        if (dpp_process_signed_connector(&info, csign_pub,
-                                        signed_connector) < 0)
+                                        signed_connector) != DPP_STATUS_OK)
                goto fail;
 
        if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) {
@@ -4170,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)
 {
@@ -4180,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);
@@ -4219,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;
        }
 
@@ -4261,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;
        }
 
@@ -4270,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;
        }
 
@@ -4289,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;
        }
 
@@ -4305,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",
@@ -4421,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;
@@ -4452,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),
@@ -4615,15 +5663,16 @@ fail:
 }
 
 
-int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
-                  const u8 *net_access_key, size_t net_access_key_len,
-                  const u8 *csign_key, size_t csign_key_len,
-                  const u8 *peer_connector, size_t peer_connector_len,
-                  os_time_t *expiry)
+enum dpp_status_error
+dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
+              const u8 *net_access_key, size_t net_access_key_len,
+              const u8 *csign_key, size_t csign_key_len,
+              const u8 *peer_connector, size_t peer_connector_len,
+              os_time_t *expiry)
 {
        struct json_token *root = NULL, *netkey, *token;
        struct json_token *own_root = NULL;
-       int ret = -1;
+       enum dpp_status_error ret = 255, res;
        EVP_PKEY *own_key = NULL, *peer_key = NULL;
        struct wpabuf *own_key_pub = NULL;
        const struct dpp_curve_params *curve, *own_curve;
@@ -4691,18 +5740,23 @@ int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
        os_memcpy(signed_connector, peer_connector, peer_connector_len);
        signed_connector[peer_connector_len] = '\0';
 
-       if (dpp_process_signed_connector(&info, csign, signed_connector) < 0)
+       res = dpp_process_signed_connector(&info, csign, signed_connector);
+       if (res != DPP_STATUS_OK) {
+               ret = res;
                goto fail;
+       }
 
        root = json_parse((const char *) info.payload, info.payload_len);
        if (!root) {
                wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed");
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
        }
 
        if (!dpp_connector_match_groups(own_root, root)) {
                wpa_printf(MSG_DEBUG,
                           "DPP: Peer connector does not include compatible group netrole with own connector");
+               ret = DPP_STATUS_NO_MATCH;
                goto fail;
        }
 
@@ -4715,6 +5769,7 @@ int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
                if (dpp_key_expired(token->string, expiry)) {
                        wpa_printf(MSG_DEBUG,
                                   "DPP: Connector (netAccessKey) has expired");
+                       ret = DPP_STATUS_INVALID_CONNECTOR;
                        goto fail;
                }
        }
@@ -4722,18 +5777,22 @@ int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
        netkey = json_get_member(root, "netAccessKey");
        if (!netkey || netkey->type != JSON_OBJECT) {
                wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
        }
 
        peer_key = dpp_parse_jwk(netkey, &curve);
-       if (!peer_key)
+       if (!peer_key) {
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
+       }
        dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
 
        if (own_curve != curve) {
                wpa_printf(MSG_DEBUG,
                           "DPP: Mismatching netAccessKey curves (%s != %s)",
                           own_curve->name, curve->name);
+               ret = DPP_STATUS_INVALID_CONNECTOR;
                goto fail;
        }
 
@@ -4767,9 +5826,9 @@ int dpp_peer_intro(struct dpp_introduction *intro, const char *own_connector,
                goto fail;
        }
 
-       ret = 0;
+       ret = DPP_STATUS_OK;
 fail:
-       if (ret < 0)
+       if (ret != DPP_STATUS_OK)
                os_memset(intro, 0, sizeof(*intro));
        os_memset(Nx, 0, sizeof(Nx));
        EVP_PKEY_CTX_free(ctx);
@@ -4893,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);
@@ -4973,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);
@@ -4987,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;
@@ -4999,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");
 
@@ -5013,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;
 
@@ -5024,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();
@@ -5031,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;
@@ -5041,11 +6186,156 @@ 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);
+               wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+               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);
+
+#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 */
+
+       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;
+
+out:
+       wpabuf_free(M_buf);
+       EC_KEY_free(X_ec);
+       EC_POINT_free(M);
+       EC_POINT_free(Qi);
+       BN_clear_free(Mx);
+       BN_clear_free(My);
+       BN_CTX_free(bnctx);
+       return msg;
+fail:
+       wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
+       wpabuf_free(msg);
+       msg = NULL;
+       goto out;
+}
+
+
+static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
+{
+       wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
+struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
+                               const u8 *own_mac,
+                               const char *identifier,
+                               const char *code)
+{
+       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;
+       pkex->msg_ctx = msg_ctx;
+       pkex->initiator = 1;
+       pkex->own_bi = bi;
+       os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+       if (identifier) {
+               pkex->identifier = os_strdup(identifier);
+               if (!pkex->identifier)
+                       goto fail;
+       }
+       pkex->code = os_strdup(code);
+       if (!pkex->code)
+               goto fail;
+       pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
+       if (!pkex->exchange_req)
+               goto fail;
+       return pkex;
+fail:
+       dpp_pkex_free(pkex);
+       return NULL;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
+                            enum dpp_status_error status,
+                            const BIGNUM *Nx, const BIGNUM *Ny)
+{
+       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);
@@ -5053,81 +6343,112 @@ static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
                wpabuf_put_str(msg, pkex->identifier);
        }
 
-       /* M in Encrypted Key attribute */
+       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);
 
-       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_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 */
 
-       num_bytes = BN_num_bytes(My);
-       if ((size_t) num_bytes > curve->prime_len)
+       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;
-       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);
-       EC_KEY_free(X_ec);
-       EC_POINT_free(M);
-       EC_POINT_free(Qi);
-       BN_clear_free(Mx);
-       BN_clear_free(My);
-       BN_CTX_free(bnctx);
+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:
-       wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
        wpabuf_free(msg);
-       msg = NULL;
-       goto out;
+       return NULL;
 }
 
 
-struct dpp_pkex * dpp_pkex_init(struct dpp_bootstrap_info *bi,
-                               const u8 *own_mac,
-                               const char *identifier,
-                               const char *code)
+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)
 {
-       struct dpp_pkex *pkex;
+       u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+       int res;
+       u8 *info, *pos;
+       size_t info_len;
 
-       pkex = os_zalloc(sizeof(*pkex));
-       if (!pkex)
-               return NULL;
-       pkex->initiator = 1;
-       pkex->own_bi = bi;
-       os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
-       if (identifier) {
-               pkex->identifier = os_strdup(identifier);
-               if (!pkex->identifier)
-                       goto fail;
-       }
-       pkex->code = os_strdup(code);
-       if (!pkex->code)
-               goto fail;
-       pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
-       if (!pkex->exchange_req)
-               goto fail;
-       return pkex;
-fail:
-       dpp_pkex_free(pkex);
-       return NULL;
+       /* 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(struct dpp_bootstrap_info *bi,
+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,
@@ -5146,9 +6467,29 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
        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);
@@ -5167,18 +6508,25 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
        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 */
@@ -5186,7 +6534,8 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
                                &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;
        }
 
@@ -5211,12 +6560,20 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
            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);
        os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
@@ -5247,7 +6604,21 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
                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;
 
@@ -5258,6 +6629,7 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
        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();
@@ -5265,60 +6637,44 @@ struct dpp_pkex * dpp_pkex_rx_exchange_req(struct dpp_bootstrap_info *bi,
            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);
@@ -5333,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);
 
-       /* 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_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);
 
-       wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
-                       z, hash_len);
-       return 0;
+#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 */
+
+out:
+       wpabuf_free(clear);
+       return msg;
+
+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;
-       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;
@@ -5420,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;
        }
 
@@ -5445,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;
        }
 
@@ -5453,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;
        }
 
@@ -5478,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;
 
@@ -5556,28 +6994,110 @@ 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);
-       msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ,
-                           4 + clear_len + AES_BLOCK_SIZE);
+       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 += 5;
+#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 */
+#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);
 
-       octet = 0;
-       addr[0] = &octet;
-       len[0] = sizeof(octet);
-       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
+#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 = 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);
@@ -5586,27 +7106,24 @@ struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
        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),
-                           1, addr, len, wrapped) < 0)
+                           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_RESP) {
+               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 */
+
 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;
@@ -5614,12 +7131,13 @@ fail:
 
 
 struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+                                             const u8 *hdr,
                                              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;
@@ -5631,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;
-       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;
        }
 
@@ -5678,42 +7169,47 @@ struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
        if (!unwrapped)
                goto fail;
 
+       addr[0] = hdr;
+       len[0] = DPP_HDR_LEN;
        octet = 0;
-       addr[0] = &octet;
-       len[0] = sizeof(octet);
-       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[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]);
 
        if (aes_siv_decrypt(pkex->z, curve->hash_len,
                            wrapped_data, wrapped_data_len,
-                           1, addr, len, unwrapped) < 0) {
-               wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+                           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 ||
@@ -5751,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");
@@ -5793,40 +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);
-       msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP,
-                           4 + clear_len + AES_BLOCK_SIZE);
-       if (!clear || !msg)
+       msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
+       if (!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);
-
-       octet = 1;
-       addr[0] = &octet;
-       len[0] = sizeof(octet);
-       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]);
-
-       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),
-                           1, addr, len, wrapped) < 0)
-               goto fail;
-       wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
-                   wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
 out:
        EVP_PKEY_CTX_free(ctx);
        os_free(unwrapped);
@@ -5834,16 +7301,15 @@ 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;
 }
 
 
-int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex,
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
                                   const u8 *buf, size_t buflen)
 {
        const struct dpp_curve_params *curve = pkex->own_bi->curve;
@@ -5861,11 +7327,15 @@ int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex,
        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;
        }
 
@@ -5876,37 +7346,42 @@ int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex,
        if (!unwrapped)
                goto fail;
 
+       addr[0] = hdr;
+       len[0] = DPP_HDR_LEN;
        octet = 1;
-       addr[0] = &octet;
-       len[0] = sizeof(octet);
-       wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[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]);
 
        if (aes_siv_decrypt(pkex->z, curve->hash_len,
                            wrapped_data, wrapped_data_len,
-                           1, addr, len, unwrapped) < 0) {
-               wpa_printf(MSG_DEBUG, "DPP: AES-SIV decryption failed");
+                           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);
 
@@ -5948,10 +7423,11 @@ int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex,
                              &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");
@@ -5983,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 */