diff --git a/hostapd/Android.mk b/hostapd/Android.mk index eaa566e60..5cda74885 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -538,6 +538,7 @@ ifdef CONFIG_DPP L_CFLAGS += -DCONFIG_DPP OBJS += src/common/dpp.c OBJS += src/common/dpp_auth.c +OBJS += src/common/dpp_backup.c OBJS += src/common/dpp_crypto.c OBJS += src/common/dpp_pkex.c OBJS += src/common/dpp_reconfig.c diff --git a/hostapd/Makefile b/hostapd/Makefile index c6725b006..b2d782aac 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -569,6 +569,7 @@ ifdef CONFIG_DPP CFLAGS += -DCONFIG_DPP OBJS += ../src/common/dpp.o OBJS += ../src/common/dpp_auth.o +OBJS += ../src/common/dpp_backup.o OBJS += ../src/common/dpp_crypto.o OBJS += ../src/common/dpp_pkex.o OBJS += ../src/common/dpp_reconfig.o diff --git a/src/common/dpp.c b/src/common/dpp.c index e9f1a35cb..870364a29 100644 --- a/src/common/dpp.c +++ b/src/common/dpp.c @@ -24,7 +24,6 @@ #include "crypto/random.h" #include "crypto/aes.h" #include "crypto/aes_siv.h" -#include "tls/asn1.h" #include "drivers/driver.h" #include "dpp.h" #include "dpp_i.h" @@ -1274,20 +1273,6 @@ fail: } -static 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; - } -} - - void dpp_auth_deinit(struct dpp_authentication *auth) { unsigned int i; @@ -1312,7 +1297,9 @@ void dpp_auth_deinit(struct dpp_authentication *auth) os_free(conf->connector); wpabuf_free(conf->c_sign_key); } +#ifdef CONFIG_DPP2 dpp_free_asymmetric_key(auth->conf_key_pkg); +#endif /* CONFIG_DPP2 */ wpabuf_free(auth->net_access_key); dpp_bootstrap_info_free(auth->tmp_own_bi); #ifdef CONFIG_TESTING_OPTIONS @@ -1655,488 +1642,6 @@ dpp_build_conf_obj(struct dpp_authentication *auth, enum dpp_netrole netrole, } -#ifdef CONFIG_DPP2 - -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; -} - - -static 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; -} - -#endif /* CONFIG_DPP2 */ - - static struct wpabuf * dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, u16 e_nonce_len, enum dpp_netrole netrole) @@ -3123,709 +2628,6 @@ fail: } -#ifdef CONFIG_DPP2 - -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, ¶ms, ¶ms_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, ¶ms, ¶ms_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; -} - - -static 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 */ - - int dpp_conf_resp_rx(struct dpp_authentication *auth, const struct wpabuf *resp) { diff --git a/src/common/dpp_backup.c b/src/common/dpp_backup.c new file mode 100644 index 000000000..4bd5ba616 --- /dev/null +++ b/src/common/dpp_backup.c @@ -0,0 +1,1211 @@ +/* + * 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 + +#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 + +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, ¶ms, ¶ms_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, ¶ms, ¶ms_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 */ diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h index 490e5bc93..c81411a9b 100644 --- a/src/common/dpp_i.h +++ b/src/common/dpp_i.h @@ -47,6 +47,13 @@ int dpp_prepare_channel_list(struct dpp_authentication *auth, void dpp_auth_fail(struct dpp_authentication *auth, const char *txt); int dpp_gen_uri(struct dpp_bootstrap_info *bi); +/* dpp_backup.c */ + +void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key); +struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth); +int dpp_conf_resp_env_data(struct dpp_authentication *auth, + const u8 *env_data, size_t env_data_len); + /* dpp_crypto.c */ struct dpp_signed_connector_info { diff --git a/tests/fuzzing/dpp-uri/Makefile b/tests/fuzzing/dpp-uri/Makefile index 3b49fa4f5..a5c45fd90 100644 --- a/tests/fuzzing/dpp-uri/Makefile +++ b/tests/fuzzing/dpp-uri/Makefile @@ -22,6 +22,7 @@ OBJS += $(SRC)/crypto/sha512-kdf.o OBJS += $(SRC)/tls/asn1.o OBJS += $(SRC)/common/dpp.o OBJS += $(SRC)/common/dpp_auth.o +OBJS += $(SRC)/common/dpp_backup.o OBJS += $(SRC)/common/dpp_crypto.o OBJS += $(SRC)/common/dpp_pkex.o OBJS += $(SRC)/common/dpp_reconfig.o diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index c85e7f957..eea5875e0 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -248,6 +248,7 @@ ifdef CONFIG_DPP L_CFLAGS += -DCONFIG_DPP OBJS += src/common/dpp.c OBJS += src/common/dpp_auth.c +OBJS += src/common/dpp_backup.c OBJS += src/common/dpp_crypto.c OBJS += src/common/dpp_pkex.c OBJS += src/common/dpp_reconfig.c diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 34ac16472..66ed8352a 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -280,6 +280,7 @@ ifdef CONFIG_DPP CFLAGS += -DCONFIG_DPP OBJS += ../src/common/dpp.o OBJS += ../src/common/dpp_auth.o +OBJS += ../src/common/dpp_backup.o OBJS += ../src/common/dpp_crypto.o OBJS += ../src/common/dpp_pkex.o OBJS += ../src/common/dpp_reconfig.o