[wpa_supplicant] Cumulative patch from commit dd2daf084

Bug: 156933657
Test: Confirm random dialog token usage from logs
Test: Verify Passpoint ANQP functionality and Passpoint association
Test: act.py -c ../WifiDppConfig.json -tc WifiDppTest
Test: Connect to Passpoint, Open, WPA2, WPA3 networks and run traffic
Test: Regression test passed (Bug: 156969218)

dd2daf084 HE: Process HE 6 GHz band capab from associating HE STA
db603634a HE: Add 6 GHz Band Capabilities element in Beacon and response frames
88911a0aa HE: Add HE 6 GHz Band Capabilities into ieee802_11_parse_elems()
b2c0b83c6 HE: Remove VHT Operation Information from HE Operation element
e297a5bfd HE: Define 6 GHz band capability elements
39f29f250 defconfig: Enable TDLS
025ab330b ACS: Channel selection based freqlist
4ae3f3972 Add a helper function for recognizing BIP enum wpa_alg values
d3cab56c0 Rename WPA_ALG_IGTK to use the correct cipher name for BIP
bd1aebbd0 hostapd: Extend RESET_PN for BIGTK
df49c53f4 Fix a typo in a comment
4294d221d D-Bus: Increase introspection buffer size
79488da57 wolfssl: Do not hardcode include directory in wpa_supplicant build
eb595b3e3 wolfssl: Fix crypto_bignum_rand() implementation
6a28c4dbc wolfssl: Fix compiler warnings on size_t printf format use
f2dbaa8ac SAE: Fix a typo in a comment
038899290 wpa_gui: Fix build with Inkscape 1.0
3e1a13010 nl80211: Change AKM suite limit from warning to debug print
5a04a76aa Ignore Management frames while AP interface is not fully enabled
c82535edd Move deauthentication at AP start to be after beacon configuration
094c8a621 Remove unnecessary key clearing at AP start with nl80211
04030e8c0 nl80211: Remove AP mode interface from bridge for STA-mode-scan
7adea21d2 dpp-nfc: Enable hostapd beaconing for listen state
134ad50b0 dpp-nfc: Clean up debug prints when handover select is received
5d49c1bf7 dpp-nfc: Do not indicate a single channel 1 by default
d0e2d8091 dpp-nfc: Make handover request collision detection more robust
8791e7461 dpp-nfc: Write debug info to summary log
1e0bc897a dpp-nfc: Collision detection for handover request
9ad3fe934 dpp-nfc: Start handover server regardless of init-on-touch setting
24efcdf74 dpp-nfc: Own MAC address fetching from hostapd
8f96f2c3b dpp-nfc: Be more graceful when wpa_supplicant is not available
0b04d3c57 dpp-nfc: Allow wpa_supplicant control interface directory to be set
69dfbe6a9 dpp-nfc: Use Configurator/Enrollee parameters with tag reading
f85fb349f dpp-nfc: More robust determination of the script directory
98e4b3840 DPP2: Chirping in hostapd Enrollee
95471fc3e Handle hostapd_for_each_interface() at the process termination
99809c7a4 nl80211: Disable offchannel-ok in AP mode only if beaconing
0f58c88fc DPP2: Fix CONFIG_DPP2=y build with OpenSSL 1.0.2
44f786678 Clean up GET_CAPABILITY handling of 'strict' argument
3790f3a6e Use per-interface type driver key_mgmt capabilities when possible
8d7502809 Allow per interface type AKM capabilities to be fetched
b67bedf2e nl80211: Fetch information on supported AKMs from the driver
6fffb320f nl80211: Remove QCA vendor specific AKM capability handling
db59827a3 DPP2: Extend TCP encapsulation case to support Configurator as Initiator
0086c1452 DPP: Extend NFC bootstrapping script for more control by caller
890ae336c DPP2: Clean up CONFIG_DPP2 use with configurator connectivity IE
670e15337 DPP2: Fix DPP_CHIRP listen parameter value validation
7f20a3ebd DPP2: Reconfiguration support in Controller
6dcb8aaf1 DPP2: Reconfig Announcement relaying from AP to Controller
3b4f7dfaa DPP2: Fix Presence Announcement processing in Controller
5e2d877cc DPP: Mark internal-to-file functions static
3aaf269f6 DPP: Move TCP encapsulation into a separate source code file
21c612017 DPP: Move configurator backup into a separate source code file
fdbbb7406 DPP: Move authentication functionality into a separate source code file
182f6ae90 DPP2: Remove reconfigured network
3e48c5d4b DPP2: Reconfig Authentication Confirm processing
24b01c706 DPP2: Reconfig Authentication Response processing and Confirm generation
65e94351d DPP2: Reconfig Authentication Request processing and Response generation
3774b6bd0 DPP2: Reconfig Authentication Request generation and transmission
66ac616cd DPP2: Process received Reconfig Announcement frame
0c043d9de DPP2: Reconfig Announcement transmission
92492dd3a DPP2: Extend connector matching for reconfiguration
961435097 DPP2: Move connStatus object building into a helper function
94f73f90e DPP: Move signed connector checking into a helper function
94a28a494 DPP: Move parsing of own connector into a helper function
d4ae12355 DPP: Move PKEX functionality into a separate source code file
87b657261 DPP: Move crypto routines into a separate source code file
16626dff9 DPP2: Derive bk ("base key")
76029c6e1 DPP: Use EVP_PKEY_get0_EC_KEY() when a const reference is sufficient
0a488ef35 DPP: Track ending time for remain-on-channel operations
481fdfc46 DPP2: Fix URI version parser
7dd768c3c DPP2: Version information in bootstrapping info URI
cbafc8ef4 Fix truncated control interface command detection
5a0718a19 DPP2: Report MUD URL and bandSupport in control interface events
769139c49 DPP2: Do not include Protocol Version in Auth Req when testing v1
fad64b416 DPP: Move dppCon signing to a set of helper functions
12c8eacf7 DPP: Allow version number to be overridden for testing purposes
c3c38bc8b DPP2: Detect PFS downgrade attack while processing EAPOL-Key msg 3/4
9561925b4 DPP2: Detect PFS downgrade attack while processing EAPOL-Key msg 2/4
68422fedb DPP2: Parse DPP KDE in EAPOL-Key Key Data field
143e3d8bc DPP2: Add DPP KDE into EAPOL-Key msg 2/4 when using DPP AKM
b11a12401 DPP2: Add DPP KDE into EAPOL-Key msg 3/4 when using DPP AKM
85d545699 DPP2: Indicate if PFS was used in control interface STATUS
1f5f00008 DPP2: Try to negotiate PFS only if AP supports version 2 or newer
f6c22dcde Use a local pointer to simply current_ssid accesses in sme_associate()
42acf1292 DPP2: Add Protocol Version attribute to network introduction messages
96b6dd21a Increase wpa_supplicant control interface buffer size
a7d6098fb Add PRINTF_FORMAT for printf wrapper functions

Change-Id: I26a15ac578ce81d1e35f426085661e5c7b43b6ef
diff --git a/src/common/dpp_backup.c b/src/common/dpp_backup.c
new file mode 100644
index 0000000..c675c42
--- /dev/null
+++ b/src/common/dpp_backup.c
@@ -0,0 +1,1227 @@
+/*
+ * DPP configurator backup
+ * Copyright (c) 2019-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "tls/asn1.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+#ifdef CONFIG_DPP2
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+	(defined(LIBRESSL_VERSION_NUMBER) && \
+	 LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+	if (pkey->type != EVP_PKEY_EC)
+		return NULL;
+	return pkey->pkey.ec;
+}
+
+#endif
+
+
+void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key)
+{
+	while (key) {
+		struct dpp_asymmetric_key *next = key->next;
+
+		EVP_PKEY_free(key->csign);
+		str_clear_free(key->config_template);
+		str_clear_free(key->connector_template);
+		os_free(key);
+		key = next;
+	}
+}
+
+
+static struct wpabuf * dpp_build_conf_params(void)
+{
+	struct wpabuf *buf;
+	size_t len;
+	/* TODO: proper template values */
+	const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
+	const char *connector_template = NULL;
+
+	len = 100 + os_strlen(conf_template);
+	if (connector_template)
+		len += os_strlen(connector_template);
+	buf = wpabuf_alloc(len);
+	if (!buf)
+		return NULL;
+
+	/*
+	 * DPPConfigurationParameters ::= SEQUENCE {
+	 *    configurationTemplate	UTF8String,
+	 *    connectorTemplate		UTF8String OPTIONAL}
+	 */
+
+	asn1_put_utf8string(buf, conf_template);
+	if (connector_template)
+		asn1_put_utf8string(buf, connector_template);
+	return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_attribute(void)
+{
+	struct wpabuf *conf_params, *attr;
+
+	/*
+	 * aa-DPPConfigurationParameters ATTRIBUTE ::=
+	 * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
+	 *
+	 * Attribute ::= SEQUENCE {
+	 *    type OBJECT IDENTIFIER,
+	 *    values SET SIZE(1..MAX) OF Type
+	 */
+	conf_params = dpp_build_conf_params();
+	conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
+				  ASN1_TAG_SET);
+	if (!conf_params)
+		return NULL;
+
+	attr = wpabuf_alloc(100 + wpabuf_len(conf_params));
+	if (!attr) {
+		wpabuf_clear_free(conf_params);
+		return NULL;
+	}
+
+	asn1_put_oid(attr, &asn1_dpp_config_params_oid);
+	wpabuf_put_buf(attr, conf_params);
+	wpabuf_clear_free(conf_params);
+
+	return asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_key_alg(const struct dpp_curve_params *curve)
+{
+	const struct asn1_oid *oid;
+	struct wpabuf *params, *res;
+
+	switch (curve->ike_group) {
+	case 19:
+		oid = &asn1_prime256v1_oid;
+		break;
+	case 20:
+		oid = &asn1_secp384r1_oid;
+		break;
+	case 21:
+		oid = &asn1_secp521r1_oid;
+		break;
+	case 28:
+		oid = &asn1_brainpoolP256r1_oid;
+		break;
+	case 29:
+		oid = &asn1_brainpoolP384r1_oid;
+		break;
+	case 30:
+		oid = &asn1_brainpoolP512r1_oid;
+		break;
+	default:
+		return NULL;
+	}
+
+	params = wpabuf_alloc(20);
+	if (!params)
+		return NULL;
+	asn1_put_oid(params, oid); /* namedCurve */
+
+	res = asn1_build_alg_id(&asn1_ec_public_key_oid, params);
+	wpabuf_free(params);
+	return res;
+}
+
+
+static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth)
+{
+	struct wpabuf *key = NULL, *attr, *alg, *priv_key = NULL;
+	EC_KEY *eckey;
+	unsigned char *der = NULL;
+	int der_len;
+
+	eckey = EVP_PKEY_get0_EC_KEY(auth->conf->csign);
+	if (!eckey)
+		return NULL;
+
+	EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
+	der_len = i2d_ECPrivateKey(eckey, &der);
+	if (der_len > 0)
+		priv_key = wpabuf_alloc_copy(der, der_len);
+	OPENSSL_free(der);
+
+	alg = dpp_build_key_alg(auth->conf->curve);
+
+	/* Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } } */
+	attr = dpp_build_attribute();
+	attr = asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SET);
+	if (!priv_key || !attr || !alg)
+		goto fail;
+
+	/*
+	 * OneAsymmetricKey ::= SEQUENCE {
+	 *    version			Version,
+	 *    privateKeyAlgorithm	PrivateKeyAlgorithmIdentifier,
+	 *    privateKey		PrivateKey,
+	 *    attributes		[0] Attributes OPTIONAL,
+	 *    ...,
+	 *    [[2: publicKey		[1] BIT STRING OPTIONAL ]],
+	 *    ...
+	 * }
+	 */
+
+	key = wpabuf_alloc(100 + wpabuf_len(alg) + wpabuf_len(priv_key) +
+			   wpabuf_len(attr));
+	if (!key)
+		goto fail;
+
+	asn1_put_integer(key, 1); /* version = v2(1) */
+
+	/* PrivateKeyAlgorithmIdentifier */
+	wpabuf_put_buf(key, alg);
+
+	/* PrivateKey ::= OCTET STRING */
+	asn1_put_octet_string(key, priv_key);
+
+	/* [0] Attributes OPTIONAL */
+	asn1_put_hdr(key, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0, wpabuf_len(attr));
+	wpabuf_put_buf(key, attr);
+
+fail:
+	wpabuf_clear_free(attr);
+	wpabuf_clear_free(priv_key);
+	wpabuf_free(alg);
+
+	/*
+	 * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
+	 *
+	 * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
+	 *
+	 * OneAsymmetricKey ::= SEQUENCE
+	 */
+	return asn1_encaps(asn1_encaps(key,
+				       ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE),
+			   ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_pbkdf2_alg_id(const struct wpabuf *salt,
+					       size_t hash_len)
+{
+	struct wpabuf *params = NULL, *buf = NULL, *prf = NULL;
+	const struct asn1_oid *oid;
+
+	/*
+	 * PBKDF2-params ::= SEQUENCE {
+	 *    salt CHOICE {
+	 *       specified OCTET STRING,
+	 *       otherSource AlgorithmIdentifier}
+	 *    iterationCount INTEGER (1..MAX),
+	 *    keyLength INTEGER (1..MAX),
+	 *    prf AlgorithmIdentifier}
+	 *
+	 * salt is an 64 octet value, iterationCount is 1000, keyLength is based
+	 * on Configurator signing key length, prf is
+	 * id-hmacWithSHA{256,384,512} based on Configurator signing key.
+	 */
+
+	if (hash_len == 32)
+		oid = &asn1_pbkdf2_hmac_sha256_oid;
+	else if (hash_len == 48)
+		oid = &asn1_pbkdf2_hmac_sha384_oid;
+	else if (hash_len == 64)
+		oid = &asn1_pbkdf2_hmac_sha512_oid;
+	else
+		goto fail;
+	prf = asn1_build_alg_id(oid, NULL);
+	if (!prf)
+		goto fail;
+	params = wpabuf_alloc(100 + wpabuf_len(salt) + wpabuf_len(prf));
+	if (!params)
+		goto fail;
+	asn1_put_octet_string(params, salt); /* salt.specified */
+	asn1_put_integer(params, 1000); /* iterationCount */
+	asn1_put_integer(params, hash_len); /* keyLength */
+	wpabuf_put_buf(params, prf);
+	params = asn1_encaps(params, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+	if (!params)
+		goto fail;
+	buf = asn1_build_alg_id(&asn1_pbkdf2_oid, params);
+fail:
+	wpabuf_free(params);
+	wpabuf_free(prf);
+	return buf;
+}
+
+
+static struct wpabuf *
+dpp_build_pw_recipient_info(struct dpp_authentication *auth, size_t hash_len,
+			    const struct wpabuf *cont_enc_key)
+{
+	struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
+		*key_enc_alg = NULL, *salt;
+	u8 kek[DPP_MAX_HASH_LEN];
+	const u8 *key;
+	size_t key_len;
+
+	salt = wpabuf_alloc(64);
+	if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
+		goto fail;
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
+
+	/* TODO: For initial testing, use ke as the key. Replace this with a
+	 * new key once that has been defined. */
+	key = auth->ke;
+	key_len = auth->curve->hash_len;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
+
+	if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
+		       kek, hash_len)) {
+		wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
+			kek, hash_len);
+
+	enc_key = wpabuf_alloc(hash_len + AES_BLOCK_SIZE);
+	if (!enc_key ||
+	    aes_siv_encrypt(kek, hash_len, wpabuf_head(cont_enc_key),
+			    wpabuf_len(cont_enc_key), 0, NULL, NULL,
+			    wpabuf_put(enc_key, hash_len + AES_BLOCK_SIZE)) < 0)
+		goto fail;
+	wpa_hexdump_buf(MSG_DEBUG, "DPP: encryptedKey", enc_key);
+
+	/*
+	 * PasswordRecipientInfo ::= SEQUENCE {
+	 *    version			CMSVersion,
+	 *    keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
+	 *    keyEncryptionAlgorithm	KeyEncryptionAlgorithmIdentifier,
+	 *    encryptedKey		EncryptedKey}
+	 *
+	 * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
+	 * parameters contains PBKDF2-params SEQUENCE.
+	 */
+
+	key_der_alg = dpp_build_pbkdf2_alg_id(salt, hash_len);
+	key_enc_alg = asn1_build_alg_id(&asn1_aes_siv_cmac_aead_256_oid, NULL);
+	if (!key_der_alg || !key_enc_alg)
+		goto fail;
+	pwri = wpabuf_alloc(100 + wpabuf_len(key_der_alg) +
+			    wpabuf_len(key_enc_alg) + wpabuf_len(enc_key));
+	if (!pwri)
+		goto fail;
+
+	/* version = 0 */
+	asn1_put_integer(pwri, 0);
+
+	/* [0] KeyDerivationAlgorithmIdentifier */
+	asn1_put_hdr(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0,
+		     wpabuf_len(key_der_alg));
+	wpabuf_put_buf(pwri, key_der_alg);
+
+	/* KeyEncryptionAlgorithmIdentifier */
+	wpabuf_put_buf(pwri, key_enc_alg);
+
+	/* EncryptedKey ::= OCTET STRING */
+	asn1_put_octet_string(pwri, enc_key);
+
+fail:
+	wpabuf_clear_free(key_der_alg);
+	wpabuf_free(key_enc_alg);
+	wpabuf_free(enc_key);
+	wpabuf_free(salt);
+	forced_memzero(kek, sizeof(kek));
+	return asn1_encaps(pwri, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf *
+dpp_build_recipient_info(struct dpp_authentication *auth, size_t hash_len,
+			 const struct wpabuf *cont_enc_key)
+{
+	struct wpabuf *pwri;
+
+	/*
+	 * RecipientInfo ::= CHOICE {
+	 *    ktri		KeyTransRecipientInfo,
+	 *    kari	[1]	KeyAgreeRecipientInfo,
+	 *    kekri	[2]	KEKRecipientInfo,
+	 *    pwri	[3]	PasswordRecipientInfo,
+	 *    ori	[4]	OtherRecipientInfo}
+	 *
+	 * Shall always use the pwri CHOICE.
+	 */
+
+	pwri = dpp_build_pw_recipient_info(auth, hash_len, cont_enc_key);
+	return asn1_encaps(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
+}
+
+
+static struct wpabuf *
+dpp_build_enc_cont_info(struct dpp_authentication *auth, size_t hash_len,
+			const struct wpabuf *cont_enc_key)
+{
+	struct wpabuf *key_pkg, *enc_cont_info = NULL, *enc_cont = NULL,
+		*enc_alg;
+	const struct asn1_oid *oid;
+	size_t enc_cont_len;
+
+	/*
+	 * EncryptedContentInfo ::= SEQUENCE {
+	 *    contentType			ContentType,
+	 *    contentEncryptionAlgorithm  ContentEncryptionAlgorithmIdentifier,
+	 *    encryptedContent	[0] IMPLICIT	EncryptedContent OPTIONAL}
+	 */
+
+	if (hash_len == 32)
+		oid = &asn1_aes_siv_cmac_aead_256_oid;
+	else if (hash_len == 48)
+		oid = &asn1_aes_siv_cmac_aead_384_oid;
+	else if (hash_len == 64)
+		oid = &asn1_aes_siv_cmac_aead_512_oid;
+	else
+		return NULL;
+
+	key_pkg = dpp_build_key_pkg(auth);
+	enc_alg = asn1_build_alg_id(oid, NULL);
+	if (!key_pkg || !enc_alg)
+		goto fail;
+
+	wpa_hexdump_buf_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
+			    key_pkg);
+
+	enc_cont_len = wpabuf_len(key_pkg) + AES_BLOCK_SIZE;
+	enc_cont = wpabuf_alloc(enc_cont_len);
+	if (!enc_cont ||
+	    aes_siv_encrypt(wpabuf_head(cont_enc_key), wpabuf_len(cont_enc_key),
+			    wpabuf_head(key_pkg), wpabuf_len(key_pkg),
+			    0, NULL, NULL,
+			    wpabuf_put(enc_cont, enc_cont_len)) < 0)
+		goto fail;
+
+	enc_cont_info = wpabuf_alloc(100 + wpabuf_len(enc_alg) +
+				     wpabuf_len(enc_cont));
+	if (!enc_cont_info)
+		goto fail;
+
+	/* ContentType ::= OBJECT IDENTIFIER */
+	asn1_put_oid(enc_cont_info, &asn1_dpp_asymmetric_key_package_oid);
+
+	/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+	wpabuf_put_buf(enc_cont_info, enc_alg);
+
+	/* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+	 * EncryptedContent ::= OCTET STRING */
+	asn1_put_hdr(enc_cont_info, ASN1_CLASS_CONTEXT_SPECIFIC, 0, 0,
+		     wpabuf_len(enc_cont));
+	wpabuf_put_buf(enc_cont_info, enc_cont);
+
+fail:
+	wpabuf_clear_free(key_pkg);
+	wpabuf_free(enc_cont);
+	wpabuf_free(enc_alg);
+	return enc_cont_info;
+}
+
+
+static struct wpabuf * dpp_gen_random(size_t len)
+{
+	struct wpabuf *key;
+
+	key = wpabuf_alloc(len);
+	if (!key || os_get_random(wpabuf_put(key, len), len) < 0) {
+		wpabuf_free(key);
+		key = NULL;
+	}
+	wpa_hexdump_buf_key(MSG_DEBUG, "DPP: content-encryption key", key);
+	return key;
+}
+
+
+struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth)
+{
+	struct wpabuf *env = NULL;
+	struct wpabuf *recipient_info = NULL, *enc_cont_info = NULL;
+	struct wpabuf *cont_enc_key = NULL;
+	size_t hash_len;
+
+	if (!auth->conf) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: No Configurator instance selected for the session - cannot build DPPEnvelopedData");
+		return NULL;
+	}
+
+	if (!auth->provision_configurator) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Configurator provisioning not allowed");
+		return NULL;
+	}
+
+	wpa_printf(MSG_DEBUG, "DPP: Building DPPEnvelopedData");
+
+	hash_len = auth->conf->curve->hash_len;
+	cont_enc_key = dpp_gen_random(hash_len);
+	if (!cont_enc_key)
+		goto fail;
+	recipient_info = dpp_build_recipient_info(auth, hash_len, cont_enc_key);
+	enc_cont_info = dpp_build_enc_cont_info(auth, hash_len, cont_enc_key);
+	if (!recipient_info || !enc_cont_info)
+		goto fail;
+
+	env = wpabuf_alloc(wpabuf_len(recipient_info) +
+			   wpabuf_len(enc_cont_info) +
+			   100);
+	if (!env)
+		goto fail;
+
+	/*
+	 * DPPEnvelopedData ::= EnvelopedData
+	 *
+	 * EnvelopedData ::= SEQUENCE {
+	 *    version			CMSVersion,
+	 *    originatorInfo	[0]	IMPLICIT OriginatorInfo OPTIONAL,
+	 *    recipientInfos		RecipientInfos,
+	 *    encryptedContentInfo	EncryptedContentInfo,
+	 *    unprotectedAttrs  [1] IMPLICIT	UnprotectedAttributes OPTIONAL}
+	 *
+	 * For DPP, version is 3, both originatorInfo and
+	 * unprotectedAttrs are omitted, and recipientInfos contains a single
+	 * RecipientInfo.
+	 */
+
+	/* EnvelopedData.version = 3 */
+	asn1_put_integer(env, 3);
+
+	/* RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo */
+	asn1_put_set(env, recipient_info);
+
+	/* EncryptedContentInfo ::= SEQUENCE */
+	asn1_put_sequence(env, enc_cont_info);
+
+	env = asn1_encaps(env, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+	wpa_hexdump_buf(MSG_MSGDUMP, "DPP: DPPEnvelopedData", env);
+out:
+	wpabuf_clear_free(cont_enc_key);
+	wpabuf_clear_free(recipient_info);
+	wpabuf_free(enc_cont_info);
+	return env;
+fail:
+	wpabuf_free(env);
+	env = NULL;
+	goto out;
+}
+
+
+struct dpp_enveloped_data {
+	const u8 *enc_cont;
+	size_t enc_cont_len;
+	const u8 *enc_key;
+	size_t enc_key_len;
+	const u8 *salt;
+	size_t pbkdf2_key_len;
+	size_t prf_hash_len;
+};
+
+
+static int dpp_parse_recipient_infos(const u8 *pos, size_t len,
+				     struct dpp_enveloped_data *data)
+{
+	struct asn1_hdr hdr;
+	const u8 *end = pos + len;
+	const u8 *next, *e_end;
+	struct asn1_oid oid;
+	int val;
+	const u8 *params;
+	size_t params_len;
+
+	wpa_hexdump(MSG_MSGDUMP, "DPP: RecipientInfos", pos, len);
+
+	/*
+	 * RecipientInfo ::= CHOICE {
+	 *    ktri		KeyTransRecipientInfo,
+	 *    kari	[1]	KeyAgreeRecipientInfo,
+	 *    kekri	[2]	KEKRecipientInfo,
+	 *    pwri	[3]	PasswordRecipientInfo,
+	 *    ori	[4]	OtherRecipientInfo}
+	 *
+	 * Shall always use the pwri CHOICE.
+	 */
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 3) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected CHOICE [3] (pwri) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: PasswordRecipientInfo",
+		    hdr.payload, hdr.length);
+	pos = hdr.payload;
+	end = pos + hdr.length;
+
+	/*
+	 * PasswordRecipientInfo ::= SEQUENCE {
+	 *    version			CMSVersion,
+	 *    keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
+	 *    keyEncryptionAlgorithm	KeyEncryptionAlgorithmIdentifier,
+	 *    encryptedKey		EncryptedKey}
+	 *
+	 * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
+	 * parameters contains PBKDF2-params SEQUENCE.
+	 */
+
+	if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
+		return -1;
+	pos = hdr.payload;
+
+	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+		return -1;
+	if (val != 0) {
+		wpa_printf(MSG_DEBUG, "DPP: pwri.version != 0");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_MSGDUMP, "DPP: Remaining PasswordRecipientInfo after version",
+		    pos, end - pos);
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected keyDerivationAlgorithm [0] - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	pos = hdr.payload;
+	e_end = pos + hdr.length;
+
+	/* KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier */
+	if (asn1_get_alg_id(pos, e_end - pos, &oid, &params, &params_len,
+			    &next) < 0)
+		return -1;
+	if (!asn1_oid_equal(&oid, &asn1_pbkdf2_oid)) {
+		char buf[80];
+
+		asn1_oid_to_str(&oid, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected KeyDerivationAlgorithmIdentifier %s",
+			   buf);
+		return -1;
+	}
+
+	/*
+	 * PBKDF2-params ::= SEQUENCE {
+	 *    salt CHOICE {
+	 *       specified OCTET STRING,
+	 *       otherSource AlgorithmIdentifier}
+	 *    iterationCount INTEGER (1..MAX),
+	 *    keyLength INTEGER (1..MAX),
+	 *    prf AlgorithmIdentifier}
+	 *
+	 * salt is an 64 octet value, iterationCount is 1000, keyLength is based
+	 * on Configurator signing key length, prf is
+	 * id-hmacWithSHA{256,384,512} based on Configurator signing key.
+	 */
+	if (!params ||
+	    asn1_get_sequence(params, params_len, &hdr, &e_end) < 0)
+		return -1;
+	pos = hdr.payload;
+
+	if (asn1_get_next(pos, e_end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected OCTETSTRING (salt.specified) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: salt.specified",
+		    hdr.payload, hdr.length);
+	if (hdr.length != 64) {
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected salt length %u",
+			   hdr.length);
+		return -1;
+	}
+	data->salt = hdr.payload;
+	pos = hdr.payload + hdr.length;
+
+	if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
+		return -1;
+	if (val != 1000) {
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected iterationCount %d", val);
+		return -1;
+	}
+
+	if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
+		return -1;
+	if (val != 32 && val != 48 && val != 64) {
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected keyLength %d", val);
+		return -1;
+	}
+	data->pbkdf2_key_len = val;
+
+	if (asn1_get_sequence(pos, e_end - pos, &hdr, NULL) < 0 ||
+	    asn1_get_oid(hdr.payload, hdr.length, &oid, &pos) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not parse prf");
+		return -1;
+	}
+	if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha256_oid)) {
+		data->prf_hash_len = 32;
+	} else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha384_oid)) {
+		data->prf_hash_len = 48;
+	} else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha512_oid)) {
+		data->prf_hash_len = 64;
+	} else {
+		char buf[80];
+
+		asn1_oid_to_str(&oid, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected PBKDF2-params.prf %s",
+			   buf);
+		return -1;
+	}
+
+	pos = next;
+
+	/* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier
+	 *
+	 * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+	 *
+	 * id-alg-AES-SIV-CMAC-aed-256, id-alg-AES-SIV-CMAC-aed-384, or
+	 * id-alg-AES-SIV-CMAC-aed-512. */
+	if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
+		return -1;
+	if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
+	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
+	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
+		char buf[80];
+
+		asn1_oid_to_str(&oid, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected KeyEncryptionAlgorithmIdentifier %s",
+			   buf);
+		return -1;
+	}
+
+	/*
+	 * encryptedKey EncryptedKey
+	 *
+	 * EncryptedKey ::= OCTET STRING
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected OCTETSTRING (pwri.encryptedKey) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: pwri.encryptedKey",
+		    hdr.payload, hdr.length);
+	data->enc_key = hdr.payload;
+	data->enc_key_len = hdr.length;
+
+	return 0;
+}
+
+
+static int dpp_parse_encrypted_content_info(const u8 *pos, const u8 *end,
+					    struct dpp_enveloped_data *data)
+{
+	struct asn1_hdr hdr;
+	struct asn1_oid oid;
+
+	/*
+	 * EncryptedContentInfo ::= SEQUENCE {
+	 *    contentType			ContentType,
+	 *    contentEncryptionAlgorithm  ContentEncryptionAlgorithmIdentifier,
+	 *    encryptedContent	[0] IMPLICIT	EncryptedContent OPTIONAL}
+	 */
+	if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+		return -1;
+	wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContentInfo",
+		    hdr.payload, hdr.length);
+	if (pos < end) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Unexpected extra data after EncryptedContentInfo",
+			    pos, end - pos);
+		return -1;
+	}
+
+	end = pos;
+	pos = hdr.payload;
+
+	/* ContentType ::= OBJECT IDENTIFIER */
+	if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+		wpa_printf(MSG_DEBUG, "DPP: Could not parse ContentType");
+		return -1;
+	}
+	if (!asn1_oid_equal(&oid, &asn1_dpp_asymmetric_key_package_oid)) {
+		char buf[80];
+
+		asn1_oid_to_str(&oid, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG, "DPP: Unexpected ContentType %s", buf);
+		return -1;
+	}
+
+	/* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+	if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
+		return -1;
+	if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
+	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
+	    !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
+		char buf[80];
+
+		asn1_oid_to_str(&oid, buf, sizeof(buf));
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected ContentEncryptionAlgorithmIdentifier %s",
+			   buf);
+		return -1;
+	}
+	/* ignore optional parameters */
+
+	/* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+	 * EncryptedContent ::= OCTET STRING */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected [0] IMPLICIT (EncryptedContent) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContent",
+		    hdr.payload, hdr.length);
+	data->enc_cont = hdr.payload;
+	data->enc_cont_len = hdr.length;
+	return 0;
+}
+
+
+static int dpp_parse_enveloped_data(const u8 *env_data, size_t env_data_len,
+				    struct dpp_enveloped_data *data)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos, *end;
+	int val;
+
+	os_memset(data, 0, sizeof(*data));
+
+	/*
+	 * DPPEnvelopedData ::= EnvelopedData
+	 *
+	 * EnvelopedData ::= SEQUENCE {
+	 *    version			CMSVersion,
+	 *    originatorInfo	[0]	IMPLICIT OriginatorInfo OPTIONAL,
+	 *    recipientInfos		RecipientInfos,
+	 *    encryptedContentInfo	EncryptedContentInfo,
+	 *    unprotectedAttrs  [1] IMPLICIT	UnprotectedAttributes OPTIONAL}
+	 *
+	 * CMSVersion ::= INTEGER
+	 *
+	 * RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo
+	 *
+	 * For DPP, version is 3, both originatorInfo and
+	 * unprotectedAttrs are omitted, and recipientInfos contains a single
+	 * RecipientInfo.
+	 */
+	if (asn1_get_sequence(env_data, env_data_len, &hdr, &end) < 0)
+		return -1;
+	pos = hdr.payload;
+	if (end < env_data + env_data_len) {
+		wpa_hexdump(MSG_DEBUG,
+			    "DPP: Unexpected extra data after DPPEnvelopedData",
+			    end, env_data + env_data_len - end);
+		return -1;
+	}
+
+	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+		return -1;
+	if (val != 3) {
+		wpa_printf(MSG_DEBUG, "DPP: EnvelopedData.version != 3");
+		return -1;
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected SET (RecipientInfos) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		return -1;
+	}
+
+	if (dpp_parse_recipient_infos(hdr.payload, hdr.length, data) < 0)
+		return -1;
+	return dpp_parse_encrypted_content_info(hdr.payload + hdr.length, end,
+						data);
+}
+
+
+static struct dpp_asymmetric_key *
+dpp_parse_one_asymmetric_key(const u8 *buf, size_t len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos = buf, *end = buf + len, *next;
+	int val;
+	const u8 *params;
+	size_t params_len;
+	struct asn1_oid oid;
+	char txt[80];
+	struct dpp_asymmetric_key *key;
+	EC_KEY *eckey;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: OneAsymmetricKey", buf, len);
+
+	key = os_zalloc(sizeof(*key));
+	if (!key)
+		return NULL;
+
+	/*
+	 * OneAsymmetricKey ::= SEQUENCE {
+	 *    version			Version,
+	 *    privateKeyAlgorithm	PrivateKeyAlgorithmIdentifier,
+	 *    privateKey		PrivateKey,
+	 *    attributes		[0] Attributes OPTIONAL,
+	 *    ...,
+	 *    [[2: publicKey		[1] BIT STRING OPTIONAL ]],
+	 *    ...
+	 * }
+	 */
+	if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
+		goto fail;
+	pos = hdr.payload;
+
+	/* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
+	if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+		goto fail;
+	if (val != 1) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported DPPAsymmetricKeyPackage version %d",
+			   val);
+		goto fail;
+	}
+
+	/* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier */
+	if (asn1_get_alg_id(pos, end - pos, &oid, &params, &params_len,
+			    &pos) < 0)
+		goto fail;
+	if (!asn1_oid_equal(&oid, &asn1_ec_public_key_oid)) {
+		asn1_oid_to_str(&oid, txt, sizeof(txt));
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unsupported PrivateKeyAlgorithmIdentifier %s",
+			   txt);
+		goto fail;
+	}
+	wpa_hexdump(MSG_MSGDUMP, "DPP: PrivateKeyAlgorithmIdentifier params",
+		    params, params_len);
+	/*
+	 * ECParameters ::= CHOICE {
+	 *    namedCurve	OBJECT IDENTIFIER
+	 *    -- implicitCurve	NULL
+	 *    -- specifiedCurve	SpecifiedECDomain}
+	 */
+	if (!params || asn1_get_oid(params, params_len, &oid, &next) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Could not parse ECParameters.namedCurve");
+		goto fail;
+	}
+	asn1_oid_to_str(&oid, txt, sizeof(txt));
+	wpa_printf(MSG_MSGDUMP, "DPP: namedCurve %s", txt);
+	/* Assume the curve is identified within ECPrivateKey, so that this
+	 * separate indication is not really needed. */
+
+	/*
+	 * PrivateKey ::= OCTET STRING
+	 *    (Contains DER encoding of ECPrivateKey)
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_OCTETSTRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected OCTETSTRING (PrivateKey) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: PrivateKey",
+			hdr.payload, hdr.length);
+	pos = hdr.payload + hdr.length;
+	eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
+	if (!eckey) {
+		wpa_printf(MSG_INFO,
+			   "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+			   ERR_error_string(ERR_get_error(), NULL));
+		goto fail;
+	}
+	key->csign = EVP_PKEY_new();
+	if (!key->csign || EVP_PKEY_assign_EC_KEY(key->csign, eckey) != 1) {
+		EC_KEY_free(eckey);
+		goto fail;
+	}
+	if (wpa_debug_show_keys)
+		dpp_debug_print_key("DPP: Received c-sign-key", key->csign);
+
+	/*
+	 * Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
+	 *
+	 * Exactly one instance of type Attribute in OneAsymmetricKey.
+	 */
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC || hdr.tag != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected [0] Attributes - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: Attributes",
+			hdr.payload, hdr.length);
+	if (hdr.payload + hdr.length < end) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"DPP: Ignore additional data at the end of OneAsymmetricKey",
+				hdr.payload + hdr.length,
+				end - (hdr.payload + hdr.length));
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected SET (Attributes) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	if (hdr.payload + hdr.length < end) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"DPP: Ignore additional data at the end of OneAsymmetricKey (after SET)",
+				hdr.payload + hdr.length,
+				end - (hdr.payload + hdr.length));
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/*
+	 * OneAsymmetricKeyAttributes ATTRIBUTE ::= {
+	 *    aa-DPPConfigurationParameters,
+	 *    ... -- For local profiles
+	 * }
+	 *
+	 * aa-DPPConfigurationParameters ATTRIBUTE ::=
+	 * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
+	 *
+	 * Attribute ::= SEQUENCE {
+	 *    type OBJECT IDENTIFIER,
+	 *    values SET SIZE(1..MAX) OF Type
+	 *
+	 * Exactly one instance of ATTRIBUTE in attrValues.
+	 */
+	if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+		goto fail;
+	if (pos < end) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"DPP: Ignore additional data at the end of ATTRIBUTE",
+				pos, end - pos);
+	}
+	end = pos;
+	pos = hdr.payload;
+
+	if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0)
+		goto fail;
+	if (!asn1_oid_equal(&oid, &asn1_dpp_config_params_oid)) {
+		asn1_oid_to_str(&oid, txt, sizeof(txt));
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Unexpected Attribute identifier %s", txt);
+		goto fail;
+	}
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected SET (Attribute) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	pos = hdr.payload;
+	end = hdr.payload + hdr.length;
+
+	/*
+	 * DPPConfigurationParameters ::= SEQUENCE {
+	 *    configurationTemplate	UTF8String,
+	 *    connectorTemplate		UTF8String OPTIONAL}
+	 */
+
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPConfigurationParameters",
+			pos, end - pos);
+	if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+		goto fail;
+	if (pos < end) {
+		wpa_hexdump_key(MSG_MSGDUMP,
+				"DPP: Ignore additional data after DPPConfigurationParameters",
+				pos, end - pos);
+	}
+	end = pos;
+	pos = hdr.payload;
+
+	if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+	    hdr.class != ASN1_CLASS_UNIVERSAL ||
+	    hdr.tag != ASN1_TAG_UTF8STRING) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Expected UTF8STRING (configurationTemplate) - found class %d tag 0x%x",
+			   hdr.class, hdr.tag);
+		goto fail;
+	}
+	wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: configurationTemplate",
+			      hdr.payload, hdr.length);
+	key->config_template = os_zalloc(hdr.length + 1);
+	if (!key->config_template)
+		goto fail;
+	os_memcpy(key->config_template, hdr.payload, hdr.length);
+
+	pos = hdr.payload + hdr.length;
+
+	if (pos < end) {
+		if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+		    hdr.class != ASN1_CLASS_UNIVERSAL ||
+		    hdr.tag != ASN1_TAG_UTF8STRING) {
+			wpa_printf(MSG_DEBUG,
+				   "DPP: Expected UTF8STRING (connectorTemplate) - found class %d tag 0x%x",
+				   hdr.class, hdr.tag);
+			goto fail;
+		}
+		wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: connectorTemplate",
+				      hdr.payload, hdr.length);
+		key->connector_template = os_zalloc(hdr.length + 1);
+		if (!key->connector_template)
+			goto fail;
+		os_memcpy(key->connector_template, hdr.payload, hdr.length);
+	}
+
+	return key;
+fail:
+	wpa_printf(MSG_DEBUG, "DPP: Failed to parse OneAsymmetricKey");
+	dpp_free_asymmetric_key(key);
+	return NULL;
+}
+
+
+static struct dpp_asymmetric_key *
+dpp_parse_dpp_asymmetric_key_package(const u8 *key_pkg, size_t key_pkg_len)
+{
+	struct asn1_hdr hdr;
+	const u8 *pos = key_pkg, *end = key_pkg + key_pkg_len;
+	struct dpp_asymmetric_key *first = NULL, *last = NULL, *key;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
+			key_pkg, key_pkg_len);
+
+	/*
+	 * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
+	 *
+	 * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
+	 */
+	while (pos < end) {
+		if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0 ||
+		    !(key = dpp_parse_one_asymmetric_key(hdr.payload,
+							 hdr.length))) {
+			dpp_free_asymmetric_key(first);
+			return NULL;
+		}
+		if (!last) {
+			first = last = key;
+		} else {
+			last->next = key;
+			last = key;
+		}
+	}
+
+	return first;
+}
+
+
+int dpp_conf_resp_env_data(struct dpp_authentication *auth,
+			   const u8 *env_data, size_t env_data_len)
+{
+	const u8 *key;
+	size_t key_len;
+	u8 kek[DPP_MAX_HASH_LEN];
+	u8 cont_encr_key[DPP_MAX_HASH_LEN];
+	size_t cont_encr_key_len;
+	int res;
+	u8 *key_pkg;
+	size_t key_pkg_len;
+	struct dpp_enveloped_data data;
+	struct dpp_asymmetric_key *keys;
+
+	wpa_hexdump(MSG_DEBUG, "DPP: DPPEnvelopedData", env_data, env_data_len);
+
+	if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
+		return -1;
+
+	/* TODO: For initial testing, use ke as the key. Replace this with a
+	 * new key once that has been defined. */
+	key = auth->ke;
+	key_len = auth->curve->hash_len;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
+
+	if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
+		       kek, data.pbkdf2_key_len)) {
+		wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
+		return -1;
+	}
+	wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
+			kek, data.pbkdf2_key_len);
+
+	if (data.enc_key_len < AES_BLOCK_SIZE ||
+	    data.enc_key_len > sizeof(cont_encr_key) + AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG, "DPP: Invalid encryptedKey length");
+		return -1;
+	}
+	res = aes_siv_decrypt(kek, data.pbkdf2_key_len,
+			      data.enc_key, data.enc_key_len,
+			      0, NULL, NULL, cont_encr_key);
+	forced_memzero(kek, data.pbkdf2_key_len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: AES-SIV decryption of encryptedKey failed");
+		return -1;
+	}
+	cont_encr_key_len = data.enc_key_len - AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "DPP: content-encryption key",
+			cont_encr_key, cont_encr_key_len);
+
+	if (data.enc_cont_len < AES_BLOCK_SIZE)
+		return -1;
+	key_pkg_len = data.enc_cont_len - AES_BLOCK_SIZE;
+	key_pkg = os_malloc(key_pkg_len);
+	if (!key_pkg)
+		return -1;
+	res = aes_siv_decrypt(cont_encr_key, cont_encr_key_len,
+			      data.enc_cont, data.enc_cont_len,
+			      0, NULL, NULL, key_pkg);
+	forced_memzero(cont_encr_key, cont_encr_key_len);
+	if (res < 0) {
+		bin_clear_free(key_pkg, key_pkg_len);
+		wpa_printf(MSG_DEBUG,
+			   "DPP: AES-SIV decryption of encryptedContent failed");
+		return -1;
+	}
+
+	keys = dpp_parse_dpp_asymmetric_key_package(key_pkg, key_pkg_len);
+	bin_clear_free(key_pkg, key_pkg_len);
+	dpp_free_asymmetric_key(auth->conf_key_pkg);
+	auth->conf_key_pkg = keys;
+
+	return keys != NULL;
+}
+
+#endif /* CONFIG_DPP2 */