1341618Scy/* 2341618Scy * DPP functionality shared between hostapd and wpa_supplicant 3341618Scy * Copyright (c) 2017, Qualcomm Atheros, Inc. 4346981Scy * Copyright (c) 2018-2019, The Linux Foundation 5341618Scy * 6341618Scy * This software may be distributed under the terms of the BSD license. 7341618Scy * See README for more details. 8341618Scy */ 9341618Scy 10341618Scy#include "utils/includes.h" 11351611Scy#include <fcntl.h> 12341618Scy#include <openssl/opensslv.h> 13341618Scy#include <openssl/err.h> 14341618Scy#include <openssl/asn1.h> 15341618Scy#include <openssl/asn1t.h> 16341618Scy 17341618Scy#include "utils/common.h" 18341618Scy#include "utils/base64.h" 19341618Scy#include "utils/json.h" 20351611Scy#include "utils/ip_addr.h" 21351611Scy#include "utils/eloop.h" 22341618Scy#include "common/ieee802_11_common.h" 23341618Scy#include "common/ieee802_11_defs.h" 24341618Scy#include "common/wpa_ctrl.h" 25346981Scy#include "common/gas.h" 26341618Scy#include "crypto/crypto.h" 27341618Scy#include "crypto/random.h" 28341618Scy#include "crypto/aes.h" 29341618Scy#include "crypto/aes_siv.h" 30341618Scy#include "crypto/sha384.h" 31341618Scy#include "crypto/sha512.h" 32341618Scy#include "drivers/driver.h" 33341618Scy#include "dpp.h" 34341618Scy 35341618Scy 36341618Scy#ifdef CONFIG_TESTING_OPTIONS 37341618Scyenum dpp_test_behavior dpp_test = DPP_TEST_DISABLED; 38341618Scyu8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; 39341618Scyu8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; 40341618Scyu8 dpp_pkex_ephemeral_key_override[600]; 41341618Scysize_t dpp_pkex_ephemeral_key_override_len = 0; 42341618Scyu8 dpp_protocol_key_override[600]; 43341618Scysize_t dpp_protocol_key_override_len = 0; 44341618Scyu8 dpp_nonce_override[DPP_MAX_NONCE_LEN]; 45341618Scysize_t dpp_nonce_override_len = 0; 46341618Scy 47341618Scystatic int dpp_test_gen_invalid_key(struct wpabuf *msg, 48341618Scy const struct dpp_curve_params *curve); 49341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 50341618Scy 51341618Scy#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ 52341618Scy (defined(LIBRESSL_VERSION_NUMBER) && \ 53341618Scy LIBRESSL_VERSION_NUMBER < 0x20700000L) 54341618Scy/* Compatibility wrappers for older versions. */ 55341618Scy 56341618Scystatic int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) 57341618Scy{ 58341618Scy sig->r = r; 59341618Scy sig->s = s; 60341618Scy return 1; 61341618Scy} 62341618Scy 63341618Scy 64341618Scystatic void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, 65341618Scy const BIGNUM **ps) 66341618Scy{ 67341618Scy if (pr) 68341618Scy *pr = sig->r; 69341618Scy if (ps) 70341618Scy *ps = sig->s; 71341618Scy} 72341618Scy 73341618Scy#endif 74341618Scy 75341618Scy 76351611Scystruct dpp_connection { 77351611Scy struct dl_list list; 78351611Scy struct dpp_controller *ctrl; 79351611Scy struct dpp_relay_controller *relay; 80351611Scy struct dpp_global *global; 81351611Scy struct dpp_authentication *auth; 82351611Scy int sock; 83351611Scy u8 mac_addr[ETH_ALEN]; 84351611Scy unsigned int freq; 85351611Scy u8 msg_len[4]; 86351611Scy size_t msg_len_octets; 87351611Scy struct wpabuf *msg; 88351611Scy struct wpabuf *msg_out; 89351611Scy size_t msg_out_pos; 90351611Scy unsigned int read_eloop:1; 91351611Scy unsigned int write_eloop:1; 92351611Scy unsigned int on_tcp_tx_complete_gas_done:1; 93351611Scy unsigned int on_tcp_tx_complete_remove:1; 94351611Scy unsigned int on_tcp_tx_complete_auth_ok:1; 95351611Scy}; 96351611Scy 97351611Scy/* Remote Controller */ 98351611Scystruct dpp_relay_controller { 99351611Scy struct dl_list list; 100351611Scy struct dpp_global *global; 101351611Scy u8 pkhash[SHA256_MAC_LEN]; 102351611Scy struct hostapd_ip_addr ipaddr; 103351611Scy void *cb_ctx; 104351611Scy void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg, 105351611Scy size_t len); 106351611Scy void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token, 107351611Scy int prot, struct wpabuf *buf); 108351611Scy struct dl_list conn; /* struct dpp_connection */ 109351611Scy}; 110351611Scy 111351611Scy/* Local Controller */ 112351611Scystruct dpp_controller { 113351611Scy struct dpp_global *global; 114351611Scy u8 allowed_roles; 115351611Scy int qr_mutual; 116351611Scy int sock; 117351611Scy struct dl_list conn; /* struct dpp_connection */ 118351611Scy char *configurator_params; 119351611Scy}; 120351611Scy 121346981Scystruct dpp_global { 122351611Scy void *msg_ctx; 123346981Scy struct dl_list bootstrap; /* struct dpp_bootstrap_info */ 124346981Scy struct dl_list configurator; /* struct dpp_configurator */ 125351611Scy#ifdef CONFIG_DPP2 126351611Scy struct dl_list controllers; /* struct dpp_relay_controller */ 127351611Scy struct dpp_controller *controller; 128351611Scy struct dl_list tcp_init; /* struct dpp_connection */ 129351611Scy void *cb_ctx; 130351611Scy int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth); 131351611Scy#endif /* CONFIG_DPP2 */ 132346981Scy}; 133346981Scy 134341618Scystatic const struct dpp_curve_params dpp_curves[] = { 135341618Scy /* The mandatory to support and the default NIST P-256 curve needs to 136341618Scy * be the first entry on this list. */ 137341618Scy { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" }, 138341618Scy { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" }, 139341618Scy { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" }, 140341618Scy { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" }, 141341618Scy { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" }, 142341618Scy { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" }, 143341618Scy { NULL, 0, 0, 0, 0, NULL, 0, NULL } 144341618Scy}; 145341618Scy 146341618Scy 147341618Scy/* Role-specific elements for PKEX */ 148341618Scy 149341618Scy/* NIST P-256 */ 150341618Scystatic const u8 pkex_init_x_p256[32] = { 151341618Scy 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b, 152341618Scy 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54, 153341618Scy 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07, 154341618Scy 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25 155341618Scy }; 156341618Scystatic const u8 pkex_init_y_p256[32] = { 157341618Scy 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b, 158341618Scy 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc, 159341618Scy 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45, 160341618Scy 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4 161341618Scy }; 162341618Scystatic const u8 pkex_resp_x_p256[32] = { 163341618Scy 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39, 164341618Scy 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f, 165341618Scy 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f, 166341618Scy 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76 167341618Scy}; 168341618Scystatic const u8 pkex_resp_y_p256[32] = { 169341618Scy 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19, 170341618Scy 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1, 171341618Scy 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a, 172341618Scy 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67 173341618Scy}; 174341618Scy 175341618Scy/* NIST P-384 */ 176341618Scystatic const u8 pkex_init_x_p384[48] = { 177341618Scy 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa, 178341618Scy 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68, 179341618Scy 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53, 180341618Scy 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac, 181341618Scy 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12, 182341618Scy 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3 183341618Scy}; 184341618Scystatic const u8 pkex_init_y_p384[48] = { 185341618Scy 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29, 186341618Scy 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56, 187341618Scy 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7, 188341618Scy 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6, 189341618Scy 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94, 190341618Scy 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18 191341618Scy}; 192341618Scystatic const u8 pkex_resp_x_p384[48] = { 193341618Scy 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98, 194341618Scy 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97, 195341618Scy 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92, 196341618Scy 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44, 197341618Scy 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf, 198341618Scy 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf 199341618Scy}; 200341618Scystatic const u8 pkex_resp_y_p384[48] = { 201341618Scy 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c, 202341618Scy 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c, 203341618Scy 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3, 204341618Scy 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1, 205341618Scy 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63, 206341618Scy 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06 207341618Scy}; 208341618Scy 209341618Scy/* NIST P-521 */ 210341618Scystatic const u8 pkex_init_x_p521[66] = { 211341618Scy 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23, 212341618Scy 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0, 213341618Scy 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76, 214341618Scy 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5, 215341618Scy 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38, 216341618Scy 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01, 217341618Scy 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e, 218341618Scy 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d, 219341618Scy 0x97, 0x76 220341618Scy}; 221341618Scystatic const u8 pkex_init_y_p521[66] = { 222341618Scy 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59, 223341618Scy 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99, 224341618Scy 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b, 225341618Scy 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd, 226341618Scy 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f, 227341618Scy 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf, 228341618Scy 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02, 229341618Scy 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d, 230341618Scy 0x03, 0xa8 231341618Scy}; 232341618Scystatic const u8 pkex_resp_x_p521[66] = { 233341618Scy 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a, 234341618Scy 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44, 235341618Scy 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f, 236341618Scy 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb, 237341618Scy 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48, 238341618Scy 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e, 239341618Scy 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a, 240341618Scy 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97, 241341618Scy 0x84, 0xb4 242341618Scy}; 243341618Scystatic const u8 pkex_resp_y_p521[66] = { 244341618Scy 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d, 245341618Scy 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20, 246341618Scy 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3, 247341618Scy 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84, 248341618Scy 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9, 249341618Scy 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2, 250341618Scy 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80, 251341618Scy 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53, 252341618Scy 0xce, 0xe1 253341618Scy}; 254341618Scy 255341618Scy/* Brainpool P-256r1 */ 256341618Scystatic const u8 pkex_init_x_bp_p256r1[32] = { 257341618Scy 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10, 258341618Scy 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca, 259341618Scy 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75, 260341618Scy 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8 261341618Scy}; 262341618Scystatic const u8 pkex_init_y_bp_p256r1[32] = { 263341618Scy 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd, 264341618Scy 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30, 265341618Scy 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe, 266341618Scy 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b 267341618Scy}; 268341618Scystatic const u8 pkex_resp_x_bp_p256r1[32] = { 269341618Scy 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f, 270341618Scy 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a, 271341618Scy 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a, 272341618Scy 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3 273341618Scy}; 274341618Scystatic const u8 pkex_resp_y_bp_p256r1[32] = { 275341618Scy 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd, 276341618Scy 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2, 277341618Scy 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e, 278341618Scy 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64 279341618Scy}; 280341618Scy 281341618Scy/* Brainpool P-384r1 */ 282341618Scystatic const u8 pkex_init_x_bp_p384r1[48] = { 283341618Scy 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd, 284341618Scy 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19, 285341618Scy 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06, 286341618Scy 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62, 287341618Scy 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30, 288341618Scy 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe 289341618Scy}; 290341618Scystatic const u8 pkex_init_y_bp_p384r1[48] = { 291341618Scy 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99, 292341618Scy 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86, 293341618Scy 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32, 294341618Scy 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9, 295341618Scy 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e, 296341618Scy 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52 297341618Scy}; 298341618Scystatic const u8 pkex_resp_x_bp_p384r1[48] = { 299341618Scy 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0, 300341618Scy 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25, 301341618Scy 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b, 302341618Scy 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71, 303341618Scy 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce, 304341618Scy 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c 305341618Scy}; 306341618Scystatic const u8 pkex_resp_y_bp_p384r1[48] = { 307341618Scy 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65, 308341618Scy 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04, 309341618Scy 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70, 310341618Scy 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c, 311341618Scy 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb, 312341618Scy 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1 313341618Scy}; 314341618Scy 315341618Scy/* Brainpool P-512r1 */ 316341618Scystatic const u8 pkex_init_x_bp_p512r1[64] = { 317341618Scy 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c, 318341618Scy 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51, 319341618Scy 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc, 320341618Scy 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95, 321341618Scy 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d, 322341618Scy 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff, 323341618Scy 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc, 324341618Scy 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f 325341618Scy}; 326341618Scystatic const u8 pkex_init_y_bp_p512r1[64] = { 327341618Scy 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94, 328341618Scy 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8, 329341618Scy 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3, 330341618Scy 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45, 331341618Scy 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e, 332341618Scy 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58, 333341618Scy 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71, 334341618Scy 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99 335341618Scy}; 336341618Scystatic const u8 pkex_resp_x_bp_p512r1[64] = { 337341618Scy 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72, 338341618Scy 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76, 339341618Scy 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19, 340341618Scy 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e, 341341618Scy 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9, 342341618Scy 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88, 343341618Scy 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29, 344341618Scy 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e 345341618Scy}; 346341618Scystatic const u8 pkex_resp_y_bp_p512r1[64] = { 347341618Scy 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81, 348341618Scy 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68, 349341618Scy 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa, 350341618Scy 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d, 351341618Scy 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c, 352341618Scy 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09, 353341618Scy 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56, 354341618Scy 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7 355341618Scy}; 356341618Scy 357341618Scy 358341618Scystatic void dpp_debug_print_point(const char *title, const EC_GROUP *group, 359341618Scy const EC_POINT *point) 360341618Scy{ 361341618Scy BIGNUM *x, *y; 362341618Scy BN_CTX *ctx; 363341618Scy char *x_str = NULL, *y_str = NULL; 364341618Scy 365341618Scy if (!wpa_debug_show_keys) 366341618Scy return; 367341618Scy 368341618Scy ctx = BN_CTX_new(); 369341618Scy x = BN_new(); 370341618Scy y = BN_new(); 371341618Scy if (!ctx || !x || !y || 372341618Scy EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1) 373341618Scy goto fail; 374341618Scy 375341618Scy x_str = BN_bn2hex(x); 376341618Scy y_str = BN_bn2hex(y); 377341618Scy if (!x_str || !y_str) 378341618Scy goto fail; 379341618Scy 380341618Scy wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str); 381341618Scy 382341618Scyfail: 383341618Scy OPENSSL_free(x_str); 384341618Scy OPENSSL_free(y_str); 385341618Scy BN_free(x); 386341618Scy BN_free(y); 387341618Scy BN_CTX_free(ctx); 388341618Scy} 389341618Scy 390341618Scy 391341618Scystatic int dpp_hash_vector(const struct dpp_curve_params *curve, 392341618Scy size_t num_elem, const u8 *addr[], const size_t *len, 393341618Scy u8 *mac) 394341618Scy{ 395341618Scy if (curve->hash_len == 32) 396341618Scy return sha256_vector(num_elem, addr, len, mac); 397341618Scy if (curve->hash_len == 48) 398341618Scy return sha384_vector(num_elem, addr, len, mac); 399341618Scy if (curve->hash_len == 64) 400341618Scy return sha512_vector(num_elem, addr, len, mac); 401341618Scy return -1; 402341618Scy} 403341618Scy 404341618Scy 405341618Scystatic int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len, 406341618Scy const char *label, u8 *out, size_t outlen) 407341618Scy{ 408341618Scy if (hash_len == 32) 409341618Scy return hmac_sha256_kdf(secret, secret_len, NULL, 410341618Scy (const u8 *) label, os_strlen(label), 411341618Scy out, outlen); 412341618Scy if (hash_len == 48) 413341618Scy return hmac_sha384_kdf(secret, secret_len, NULL, 414341618Scy (const u8 *) label, os_strlen(label), 415341618Scy out, outlen); 416341618Scy if (hash_len == 64) 417341618Scy return hmac_sha512_kdf(secret, secret_len, NULL, 418341618Scy (const u8 *) label, os_strlen(label), 419341618Scy out, outlen); 420341618Scy return -1; 421341618Scy} 422341618Scy 423341618Scy 424341618Scystatic int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len, 425341618Scy size_t num_elem, const u8 *addr[], 426341618Scy const size_t *len, u8 *mac) 427341618Scy{ 428341618Scy if (hash_len == 32) 429341618Scy return hmac_sha256_vector(key, key_len, num_elem, addr, len, 430341618Scy mac); 431341618Scy if (hash_len == 48) 432341618Scy return hmac_sha384_vector(key, key_len, num_elem, addr, len, 433341618Scy mac); 434341618Scy if (hash_len == 64) 435341618Scy return hmac_sha512_vector(key, key_len, num_elem, addr, len, 436341618Scy mac); 437341618Scy return -1; 438341618Scy} 439341618Scy 440341618Scy 441341618Scystatic int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len, 442341618Scy const u8 *data, size_t data_len, u8 *mac) 443341618Scy{ 444341618Scy if (hash_len == 32) 445341618Scy return hmac_sha256(key, key_len, data, data_len, mac); 446341618Scy if (hash_len == 48) 447341618Scy return hmac_sha384(key, key_len, data, data_len, mac); 448341618Scy if (hash_len == 64) 449341618Scy return hmac_sha512(key, key_len, data, data_len, mac); 450341618Scy return -1; 451341618Scy} 452341618Scy 453341618Scy 454341618Scystatic int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len) 455341618Scy{ 456341618Scy int num_bytes, offset; 457341618Scy 458341618Scy num_bytes = BN_num_bytes(bn); 459341618Scy if ((size_t) num_bytes > len) 460341618Scy return -1; 461341618Scy offset = len - num_bytes; 462341618Scy os_memset(pos, 0, offset); 463341618Scy BN_bn2bin(bn, pos + offset); 464341618Scy return 0; 465341618Scy} 466341618Scy 467341618Scy 468341618Scystatic struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix) 469341618Scy{ 470341618Scy int len, res; 471341618Scy EC_KEY *eckey; 472341618Scy struct wpabuf *buf; 473341618Scy unsigned char *pos; 474341618Scy 475341618Scy eckey = EVP_PKEY_get1_EC_KEY(pkey); 476341618Scy if (!eckey) 477341618Scy return NULL; 478341618Scy EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED); 479341618Scy len = i2o_ECPublicKey(eckey, NULL); 480341618Scy if (len <= 0) { 481341618Scy wpa_printf(MSG_ERROR, 482341618Scy "DDP: Failed to determine public key encoding length"); 483341618Scy EC_KEY_free(eckey); 484341618Scy return NULL; 485341618Scy } 486341618Scy 487341618Scy buf = wpabuf_alloc(len); 488341618Scy if (!buf) { 489341618Scy EC_KEY_free(eckey); 490341618Scy return NULL; 491341618Scy } 492341618Scy 493341618Scy pos = wpabuf_put(buf, len); 494341618Scy res = i2o_ECPublicKey(eckey, &pos); 495341618Scy EC_KEY_free(eckey); 496341618Scy if (res != len) { 497341618Scy wpa_printf(MSG_ERROR, 498341618Scy "DDP: Failed to encode public key (res=%d/%d)", 499341618Scy res, len); 500341618Scy wpabuf_free(buf); 501341618Scy return NULL; 502341618Scy } 503341618Scy 504341618Scy if (!prefix) { 505341618Scy /* Remove 0x04 prefix to match DPP definition */ 506341618Scy pos = wpabuf_mhead(buf); 507341618Scy os_memmove(pos, pos + 1, len - 1); 508341618Scy buf->used--; 509341618Scy } 510341618Scy 511341618Scy return buf; 512341618Scy} 513341618Scy 514341618Scy 515341618Scystatic EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group, 516341618Scy const u8 *buf_x, const u8 *buf_y, 517341618Scy size_t len) 518341618Scy{ 519341618Scy EC_KEY *eckey = NULL; 520341618Scy BN_CTX *ctx; 521341618Scy EC_POINT *point = NULL; 522341618Scy BIGNUM *x = NULL, *y = NULL; 523341618Scy EVP_PKEY *pkey = NULL; 524341618Scy 525341618Scy ctx = BN_CTX_new(); 526341618Scy if (!ctx) { 527341618Scy wpa_printf(MSG_ERROR, "DPP: Out of memory"); 528341618Scy return NULL; 529341618Scy } 530341618Scy 531341618Scy point = EC_POINT_new(group); 532341618Scy x = BN_bin2bn(buf_x, len, NULL); 533341618Scy y = BN_bin2bn(buf_y, len, NULL); 534341618Scy if (!point || !x || !y) { 535341618Scy wpa_printf(MSG_ERROR, "DPP: Out of memory"); 536341618Scy goto fail; 537341618Scy } 538341618Scy 539341618Scy if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) { 540341618Scy wpa_printf(MSG_ERROR, 541341618Scy "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s", 542341618Scy ERR_error_string(ERR_get_error(), NULL)); 543341618Scy goto fail; 544341618Scy } 545341618Scy 546341618Scy if (!EC_POINT_is_on_curve(group, point, ctx) || 547341618Scy EC_POINT_is_at_infinity(group, point)) { 548341618Scy wpa_printf(MSG_ERROR, "DPP: Invalid point"); 549341618Scy goto fail; 550341618Scy } 551341618Scy dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point); 552341618Scy 553341618Scy eckey = EC_KEY_new(); 554341618Scy if (!eckey || 555341618Scy EC_KEY_set_group(eckey, group) != 1 || 556341618Scy EC_KEY_set_public_key(eckey, point) != 1) { 557341618Scy wpa_printf(MSG_ERROR, 558341618Scy "DPP: Failed to set EC_KEY: %s", 559341618Scy ERR_error_string(ERR_get_error(), NULL)); 560341618Scy goto fail; 561341618Scy } 562341618Scy EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); 563341618Scy 564341618Scy pkey = EVP_PKEY_new(); 565341618Scy if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) { 566341618Scy wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY"); 567341618Scy goto fail; 568341618Scy } 569341618Scy 570341618Scyout: 571341618Scy BN_free(x); 572341618Scy BN_free(y); 573341618Scy EC_KEY_free(eckey); 574341618Scy EC_POINT_free(point); 575341618Scy BN_CTX_free(ctx); 576341618Scy return pkey; 577341618Scyfail: 578341618Scy EVP_PKEY_free(pkey); 579341618Scy pkey = NULL; 580341618Scy goto out; 581341618Scy} 582341618Scy 583341618Scy 584341618Scystatic EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, 585341618Scy const u8 *buf, size_t len) 586341618Scy{ 587341618Scy EC_KEY *eckey; 588341618Scy const EC_GROUP *group; 589341618Scy EVP_PKEY *pkey = NULL; 590341618Scy 591341618Scy if (len & 1) 592341618Scy return NULL; 593341618Scy 594341618Scy eckey = EVP_PKEY_get1_EC_KEY(group_key); 595341618Scy if (!eckey) { 596341618Scy wpa_printf(MSG_ERROR, 597341618Scy "DPP: Could not get EC_KEY from group_key"); 598341618Scy return NULL; 599341618Scy } 600341618Scy 601341618Scy group = EC_KEY_get0_group(eckey); 602341618Scy if (group) 603341618Scy pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2, 604341618Scy len / 2); 605341618Scy else 606341618Scy wpa_printf(MSG_ERROR, "DPP: Could not get EC group"); 607341618Scy 608341618Scy EC_KEY_free(eckey); 609341618Scy return pkey; 610341618Scy} 611341618Scy 612341618Scy 613351611Scystatic int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, 614351611Scy u8 *secret, size_t *secret_len) 615351611Scy{ 616351611Scy EVP_PKEY_CTX *ctx; 617351611Scy int ret = -1; 618351611Scy 619351611Scy ERR_clear_error(); 620351611Scy *secret_len = 0; 621351611Scy 622351611Scy ctx = EVP_PKEY_CTX_new(own, NULL); 623351611Scy if (!ctx) { 624351611Scy wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s", 625351611Scy ERR_error_string(ERR_get_error(), NULL)); 626351611Scy return -1; 627351611Scy } 628351611Scy 629351611Scy if (EVP_PKEY_derive_init(ctx) != 1) { 630351611Scy wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s", 631351611Scy ERR_error_string(ERR_get_error(), NULL)); 632351611Scy goto fail; 633351611Scy } 634351611Scy 635351611Scy if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) { 636351611Scy wpa_printf(MSG_ERROR, 637351611Scy "DPP: EVP_PKEY_derive_set_peet failed: %s", 638351611Scy ERR_error_string(ERR_get_error(), NULL)); 639351611Scy goto fail; 640351611Scy } 641351611Scy 642351611Scy if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) { 643351611Scy wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s", 644351611Scy ERR_error_string(ERR_get_error(), NULL)); 645351611Scy goto fail; 646351611Scy } 647351611Scy 648351611Scy if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) { 649351611Scy u8 buf[200]; 650351611Scy int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG; 651351611Scy 652351611Scy /* It looks like OpenSSL can return unexpectedly large buffer 653351611Scy * need for shared secret from EVP_PKEY_derive(NULL) in some 654351611Scy * cases. For example, group 19 has shown cases where secret_len 655351611Scy * is set to 72 even though the actual length ends up being 656351611Scy * updated to 32 when EVP_PKEY_derive() is called with a buffer 657351611Scy * for the value. Work around this by trying to fetch the value 658351611Scy * and continue if it is within supported range even when the 659351611Scy * initial buffer need is claimed to be larger. */ 660351611Scy wpa_printf(level, 661351611Scy "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()", 662351611Scy (int) *secret_len); 663351611Scy if (*secret_len > 200) 664351611Scy goto fail; 665351611Scy if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) { 666351611Scy wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s", 667351611Scy ERR_error_string(ERR_get_error(), NULL)); 668351611Scy goto fail; 669351611Scy } 670351611Scy if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) { 671351611Scy wpa_printf(MSG_ERROR, 672351611Scy "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()", 673351611Scy (int) *secret_len); 674351611Scy goto fail; 675351611Scy } 676351611Scy wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change", 677351611Scy buf, *secret_len); 678351611Scy os_memcpy(secret, buf, *secret_len); 679351611Scy forced_memzero(buf, sizeof(buf)); 680351611Scy goto done; 681351611Scy } 682351611Scy 683351611Scy if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) { 684351611Scy wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s", 685351611Scy ERR_error_string(ERR_get_error(), NULL)); 686351611Scy goto fail; 687351611Scy } 688351611Scy 689351611Scydone: 690351611Scy ret = 0; 691351611Scy 692351611Scyfail: 693351611Scy EVP_PKEY_CTX_free(ctx); 694351611Scy return ret; 695351611Scy} 696351611Scy 697351611Scy 698341618Scystatic void dpp_auth_fail(struct dpp_authentication *auth, const char *txt) 699341618Scy{ 700341618Scy wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); 701341618Scy} 702341618Scy 703341618Scy 704341618Scystruct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type, 705341618Scy size_t len) 706341618Scy{ 707341618Scy struct wpabuf *msg; 708341618Scy 709341618Scy msg = wpabuf_alloc(8 + len); 710341618Scy if (!msg) 711341618Scy return NULL; 712341618Scy wpabuf_put_u8(msg, WLAN_ACTION_PUBLIC); 713341618Scy wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); 714341618Scy wpabuf_put_be24(msg, OUI_WFA); 715341618Scy wpabuf_put_u8(msg, DPP_OUI_TYPE); 716341618Scy wpabuf_put_u8(msg, 1); /* Crypto Suite */ 717341618Scy wpabuf_put_u8(msg, type); 718341618Scy return msg; 719341618Scy} 720341618Scy 721341618Scy 722341618Scyconst u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len) 723341618Scy{ 724341618Scy u16 id, alen; 725341618Scy const u8 *pos = buf, *end = buf + len; 726341618Scy 727341618Scy while (end - pos >= 4) { 728341618Scy id = WPA_GET_LE16(pos); 729341618Scy pos += 2; 730341618Scy alen = WPA_GET_LE16(pos); 731341618Scy pos += 2; 732341618Scy if (alen > end - pos) 733341618Scy return NULL; 734341618Scy if (id == req_id) { 735341618Scy *ret_len = alen; 736341618Scy return pos; 737341618Scy } 738341618Scy pos += alen; 739341618Scy } 740341618Scy 741341618Scy return NULL; 742341618Scy} 743341618Scy 744341618Scy 745341618Scyint dpp_check_attrs(const u8 *buf, size_t len) 746341618Scy{ 747341618Scy const u8 *pos, *end; 748341618Scy int wrapped_data = 0; 749341618Scy 750341618Scy pos = buf; 751341618Scy end = buf + len; 752341618Scy while (end - pos >= 4) { 753341618Scy u16 id, alen; 754341618Scy 755341618Scy id = WPA_GET_LE16(pos); 756341618Scy pos += 2; 757341618Scy alen = WPA_GET_LE16(pos); 758341618Scy pos += 2; 759341618Scy wpa_printf(MSG_MSGDUMP, "DPP: Attribute ID %04x len %u", 760341618Scy id, alen); 761341618Scy if (alen > end - pos) { 762341618Scy wpa_printf(MSG_DEBUG, 763341618Scy "DPP: Truncated message - not enough room for the attribute - dropped"); 764341618Scy return -1; 765341618Scy } 766341618Scy if (wrapped_data) { 767341618Scy wpa_printf(MSG_DEBUG, 768341618Scy "DPP: An unexpected attribute included after the Wrapped Data attribute"); 769341618Scy return -1; 770341618Scy } 771341618Scy if (id == DPP_ATTR_WRAPPED_DATA) 772341618Scy wrapped_data = 1; 773341618Scy pos += alen; 774341618Scy } 775341618Scy 776341618Scy if (end != pos) { 777341618Scy wpa_printf(MSG_DEBUG, 778341618Scy "DPP: Unexpected octets (%d) after the last attribute", 779341618Scy (int) (end - pos)); 780341618Scy return -1; 781341618Scy } 782341618Scy 783341618Scy return 0; 784341618Scy} 785341618Scy 786341618Scy 787341618Scyvoid dpp_bootstrap_info_free(struct dpp_bootstrap_info *info) 788341618Scy{ 789341618Scy if (!info) 790341618Scy return; 791341618Scy os_free(info->uri); 792341618Scy os_free(info->info); 793341618Scy EVP_PKEY_free(info->pubkey); 794341618Scy os_free(info); 795341618Scy} 796341618Scy 797341618Scy 798341618Scyconst char * dpp_bootstrap_type_txt(enum dpp_bootstrap_type type) 799341618Scy{ 800341618Scy switch (type) { 801341618Scy case DPP_BOOTSTRAP_QR_CODE: 802341618Scy return "QRCODE"; 803341618Scy case DPP_BOOTSTRAP_PKEX: 804341618Scy return "PKEX"; 805341618Scy } 806341618Scy return "??"; 807341618Scy} 808341618Scy 809341618Scy 810341618Scystatic int dpp_uri_valid_info(const char *info) 811341618Scy{ 812341618Scy while (*info) { 813341618Scy unsigned char val = *info++; 814341618Scy 815341618Scy if (val < 0x20 || val > 0x7e || val == 0x3b) 816341618Scy return 0; 817341618Scy } 818341618Scy 819341618Scy return 1; 820341618Scy} 821341618Scy 822341618Scy 823341618Scystatic int dpp_clone_uri(struct dpp_bootstrap_info *bi, const char *uri) 824341618Scy{ 825341618Scy bi->uri = os_strdup(uri); 826341618Scy return bi->uri ? 0 : -1; 827341618Scy} 828341618Scy 829341618Scy 830341618Scyint dpp_parse_uri_chan_list(struct dpp_bootstrap_info *bi, 831341618Scy const char *chan_list) 832341618Scy{ 833351611Scy const char *pos = chan_list, *pos2; 834351611Scy int opclass = -1, channel, freq; 835341618Scy 836341618Scy while (pos && *pos && *pos != ';') { 837351611Scy pos2 = pos; 838351611Scy while (*pos2 >= '0' && *pos2 <= '9') 839351611Scy pos2++; 840351611Scy if (*pos2 == '/') { 841351611Scy opclass = atoi(pos); 842351611Scy pos = pos2 + 1; 843351611Scy } 844341618Scy if (opclass <= 0) 845341618Scy goto fail; 846341618Scy channel = atoi(pos); 847341618Scy if (channel <= 0) 848341618Scy goto fail; 849341618Scy while (*pos >= '0' && *pos <= '9') 850341618Scy pos++; 851341618Scy freq = ieee80211_chan_to_freq(NULL, opclass, channel); 852341618Scy wpa_printf(MSG_DEBUG, 853341618Scy "DPP: URI channel-list: opclass=%d channel=%d ==> freq=%d", 854341618Scy opclass, channel, freq); 855341618Scy if (freq < 0) { 856341618Scy wpa_printf(MSG_DEBUG, 857341618Scy "DPP: Ignore unknown URI channel-list channel (opclass=%d channel=%d)", 858341618Scy opclass, channel); 859341618Scy } else if (bi->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { 860341618Scy wpa_printf(MSG_DEBUG, 861341618Scy "DPP: Too many channels in URI channel-list - ignore list"); 862341618Scy bi->num_freq = 0; 863341618Scy break; 864341618Scy } else { 865341618Scy bi->freq[bi->num_freq++] = freq; 866341618Scy } 867341618Scy 868341618Scy if (*pos == ';' || *pos == '\0') 869341618Scy break; 870341618Scy if (*pos != ',') 871341618Scy goto fail; 872341618Scy pos++; 873341618Scy } 874341618Scy 875341618Scy return 0; 876341618Scyfail: 877341618Scy wpa_printf(MSG_DEBUG, "DPP: Invalid URI channel-list"); 878341618Scy return -1; 879341618Scy} 880341618Scy 881341618Scy 882341618Scyint dpp_parse_uri_mac(struct dpp_bootstrap_info *bi, const char *mac) 883341618Scy{ 884341618Scy if (!mac) 885341618Scy return 0; 886341618Scy 887341618Scy if (hwaddr_aton2(mac, bi->mac_addr) < 0) { 888341618Scy wpa_printf(MSG_DEBUG, "DPP: Invalid URI mac"); 889341618Scy return -1; 890341618Scy } 891341618Scy 892341618Scy wpa_printf(MSG_DEBUG, "DPP: URI mac: " MACSTR, MAC2STR(bi->mac_addr)); 893341618Scy 894341618Scy return 0; 895341618Scy} 896341618Scy 897341618Scy 898341618Scyint dpp_parse_uri_info(struct dpp_bootstrap_info *bi, const char *info) 899341618Scy{ 900341618Scy const char *end; 901341618Scy 902341618Scy if (!info) 903341618Scy return 0; 904341618Scy 905341618Scy end = os_strchr(info, ';'); 906341618Scy if (!end) 907341618Scy end = info + os_strlen(info); 908341618Scy bi->info = os_malloc(end - info + 1); 909341618Scy if (!bi->info) 910341618Scy return -1; 911341618Scy os_memcpy(bi->info, info, end - info); 912341618Scy bi->info[end - info] = '\0'; 913341618Scy wpa_printf(MSG_DEBUG, "DPP: URI(information): %s", bi->info); 914341618Scy if (!dpp_uri_valid_info(bi->info)) { 915341618Scy wpa_printf(MSG_DEBUG, "DPP: Invalid URI information payload"); 916341618Scy return -1; 917341618Scy } 918341618Scy 919341618Scy return 0; 920341618Scy} 921341618Scy 922341618Scy 923341618Scystatic const struct dpp_curve_params * 924341618Scydpp_get_curve_oid(const ASN1_OBJECT *poid) 925341618Scy{ 926341618Scy ASN1_OBJECT *oid; 927341618Scy int i; 928341618Scy 929341618Scy for (i = 0; dpp_curves[i].name; i++) { 930341618Scy oid = OBJ_txt2obj(dpp_curves[i].name, 0); 931341618Scy if (oid && OBJ_cmp(poid, oid) == 0) 932341618Scy return &dpp_curves[i]; 933341618Scy } 934341618Scy return NULL; 935341618Scy} 936341618Scy 937341618Scy 938341618Scystatic const struct dpp_curve_params * dpp_get_curve_nid(int nid) 939341618Scy{ 940341618Scy int i, tmp; 941341618Scy 942341618Scy if (!nid) 943341618Scy return NULL; 944341618Scy for (i = 0; dpp_curves[i].name; i++) { 945341618Scy tmp = OBJ_txt2nid(dpp_curves[i].name); 946341618Scy if (tmp == nid) 947341618Scy return &dpp_curves[i]; 948341618Scy } 949341618Scy return NULL; 950341618Scy} 951341618Scy 952341618Scy 953341618Scystatic int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info) 954341618Scy{ 955341618Scy const char *end; 956341618Scy u8 *data; 957341618Scy size_t data_len; 958341618Scy EVP_PKEY *pkey; 959341618Scy const unsigned char *p; 960341618Scy int res; 961341618Scy X509_PUBKEY *pub = NULL; 962341618Scy ASN1_OBJECT *ppkalg; 963341618Scy const unsigned char *pk; 964341618Scy int ppklen; 965341618Scy X509_ALGOR *pa; 966346981Scy#if OPENSSL_VERSION_NUMBER < 0x10100000L || \ 967346981Scy (defined(LIBRESSL_VERSION_NUMBER) && \ 968346981Scy LIBRESSL_VERSION_NUMBER < 0x20800000L) 969341618Scy ASN1_OBJECT *pa_oid; 970341618Scy#else 971341618Scy const ASN1_OBJECT *pa_oid; 972341618Scy#endif 973341618Scy const void *pval; 974341618Scy int ptype; 975341618Scy const ASN1_OBJECT *poid; 976341618Scy char buf[100]; 977341618Scy 978341618Scy end = os_strchr(info, ';'); 979341618Scy if (!end) 980341618Scy return -1; 981341618Scy 982341618Scy data = base64_decode((const unsigned char *) info, end - info, 983341618Scy &data_len); 984341618Scy if (!data) { 985341618Scy wpa_printf(MSG_DEBUG, 986341618Scy "DPP: Invalid base64 encoding on URI public-key"); 987341618Scy return -1; 988341618Scy } 989341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Base64 decoded URI public-key", 990341618Scy data, data_len); 991341618Scy 992341618Scy if (sha256_vector(1, (const u8 **) &data, &data_len, 993341618Scy bi->pubkey_hash) < 0) { 994341618Scy wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); 995341618Scy os_free(data); 996341618Scy return -1; 997341618Scy } 998341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", 999341618Scy bi->pubkey_hash, SHA256_MAC_LEN); 1000341618Scy 1001341618Scy /* DER encoded ASN.1 SubjectPublicKeyInfo 1002341618Scy * 1003341618Scy * SubjectPublicKeyInfo ::= SEQUENCE { 1004341618Scy * algorithm AlgorithmIdentifier, 1005341618Scy * subjectPublicKey BIT STRING } 1006341618Scy * 1007341618Scy * AlgorithmIdentifier ::= SEQUENCE { 1008341618Scy * algorithm OBJECT IDENTIFIER, 1009341618Scy * parameters ANY DEFINED BY algorithm OPTIONAL } 1010341618Scy * 1011341618Scy * subjectPublicKey = compressed format public key per ANSI X9.63 1012341618Scy * algorithm = ecPublicKey (1.2.840.10045.2.1) 1013341618Scy * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g., 1014341618Scy * prime256v1 (1.2.840.10045.3.1.7) 1015341618Scy */ 1016341618Scy 1017341618Scy p = data; 1018341618Scy pkey = d2i_PUBKEY(NULL, &p, data_len); 1019341618Scy os_free(data); 1020341618Scy 1021341618Scy if (!pkey) { 1022341618Scy wpa_printf(MSG_DEBUG, 1023341618Scy "DPP: Could not parse URI public-key SubjectPublicKeyInfo"); 1024341618Scy return -1; 1025341618Scy } 1026341618Scy 1027341618Scy if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) { 1028341618Scy wpa_printf(MSG_DEBUG, 1029341618Scy "DPP: SubjectPublicKeyInfo does not describe an EC key"); 1030341618Scy EVP_PKEY_free(pkey); 1031341618Scy return -1; 1032341618Scy } 1033341618Scy 1034341618Scy res = X509_PUBKEY_set(&pub, pkey); 1035341618Scy if (res != 1) { 1036341618Scy wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey"); 1037341618Scy goto fail; 1038341618Scy } 1039341618Scy 1040341618Scy res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub); 1041341618Scy if (res != 1) { 1042341618Scy wpa_printf(MSG_DEBUG, 1043341618Scy "DPP: Could not extract SubjectPublicKeyInfo parameters"); 1044341618Scy goto fail; 1045341618Scy } 1046341618Scy res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0); 1047341618Scy if (res < 0 || (size_t) res >= sizeof(buf)) { 1048341618Scy wpa_printf(MSG_DEBUG, 1049341618Scy "DPP: Could not extract SubjectPublicKeyInfo algorithm"); 1050341618Scy goto fail; 1051341618Scy } 1052341618Scy wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf); 1053341618Scy if (os_strcmp(buf, "id-ecPublicKey") != 0) { 1054341618Scy wpa_printf(MSG_DEBUG, 1055341618Scy "DPP: Unsupported SubjectPublicKeyInfo algorithm"); 1056341618Scy goto fail; 1057341618Scy } 1058341618Scy 1059341618Scy X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa); 1060341618Scy if (ptype != V_ASN1_OBJECT) { 1061341618Scy wpa_printf(MSG_DEBUG, 1062341618Scy "DPP: SubjectPublicKeyInfo parameters did not contain an OID"); 1063341618Scy goto fail; 1064341618Scy } 1065341618Scy poid = pval; 1066341618Scy res = OBJ_obj2txt(buf, sizeof(buf), poid, 0); 1067341618Scy if (res < 0 || (size_t) res >= sizeof(buf)) { 1068341618Scy wpa_printf(MSG_DEBUG, 1069341618Scy "DPP: Could not extract SubjectPublicKeyInfo parameters OID"); 1070341618Scy goto fail; 1071341618Scy } 1072341618Scy wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf); 1073341618Scy bi->curve = dpp_get_curve_oid(poid); 1074341618Scy if (!bi->curve) { 1075341618Scy wpa_printf(MSG_DEBUG, 1076341618Scy "DPP: Unsupported SubjectPublicKeyInfo curve: %s", 1077341618Scy buf); 1078341618Scy goto fail; 1079341618Scy } 1080341618Scy 1081341618Scy wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen); 1082341618Scy 1083341618Scy X509_PUBKEY_free(pub); 1084341618Scy bi->pubkey = pkey; 1085341618Scy return 0; 1086341618Scyfail: 1087341618Scy X509_PUBKEY_free(pub); 1088341618Scy EVP_PKEY_free(pkey); 1089341618Scy return -1; 1090341618Scy} 1091341618Scy 1092341618Scy 1093341618Scystatic struct dpp_bootstrap_info * dpp_parse_uri(const char *uri) 1094341618Scy{ 1095341618Scy const char *pos = uri; 1096341618Scy const char *end; 1097341618Scy const char *chan_list = NULL, *mac = NULL, *info = NULL, *pk = NULL; 1098341618Scy struct dpp_bootstrap_info *bi; 1099341618Scy 1100341618Scy wpa_hexdump_ascii(MSG_DEBUG, "DPP: URI", uri, os_strlen(uri)); 1101341618Scy 1102341618Scy if (os_strncmp(pos, "DPP:", 4) != 0) { 1103341618Scy wpa_printf(MSG_INFO, "DPP: Not a DPP URI"); 1104341618Scy return NULL; 1105341618Scy } 1106341618Scy pos += 4; 1107341618Scy 1108341618Scy for (;;) { 1109341618Scy end = os_strchr(pos, ';'); 1110341618Scy if (!end) 1111341618Scy break; 1112341618Scy 1113341618Scy if (end == pos) { 1114341618Scy /* Handle terminating ";;" and ignore unexpected ";" 1115341618Scy * for parsing robustness. */ 1116341618Scy pos++; 1117341618Scy continue; 1118341618Scy } 1119341618Scy 1120341618Scy if (pos[0] == 'C' && pos[1] == ':' && !chan_list) 1121341618Scy chan_list = pos + 2; 1122341618Scy else if (pos[0] == 'M' && pos[1] == ':' && !mac) 1123341618Scy mac = pos + 2; 1124341618Scy else if (pos[0] == 'I' && pos[1] == ':' && !info) 1125341618Scy info = pos + 2; 1126341618Scy else if (pos[0] == 'K' && pos[1] == ':' && !pk) 1127341618Scy pk = pos + 2; 1128341618Scy else 1129341618Scy wpa_hexdump_ascii(MSG_DEBUG, 1130341618Scy "DPP: Ignore unrecognized URI parameter", 1131341618Scy pos, end - pos); 1132341618Scy pos = end + 1; 1133341618Scy } 1134341618Scy 1135341618Scy if (!pk) { 1136341618Scy wpa_printf(MSG_INFO, "DPP: URI missing public-key"); 1137341618Scy return NULL; 1138341618Scy } 1139341618Scy 1140341618Scy bi = os_zalloc(sizeof(*bi)); 1141341618Scy if (!bi) 1142341618Scy return NULL; 1143341618Scy 1144341618Scy if (dpp_clone_uri(bi, uri) < 0 || 1145341618Scy dpp_parse_uri_chan_list(bi, chan_list) < 0 || 1146341618Scy dpp_parse_uri_mac(bi, mac) < 0 || 1147341618Scy dpp_parse_uri_info(bi, info) < 0 || 1148341618Scy dpp_parse_uri_pk(bi, pk) < 0) { 1149341618Scy dpp_bootstrap_info_free(bi); 1150341618Scy bi = NULL; 1151341618Scy } 1152341618Scy 1153341618Scy return bi; 1154341618Scy} 1155341618Scy 1156341618Scy 1157341618Scystruct dpp_bootstrap_info * dpp_parse_qr_code(const char *uri) 1158341618Scy{ 1159341618Scy struct dpp_bootstrap_info *bi; 1160341618Scy 1161341618Scy bi = dpp_parse_uri(uri); 1162341618Scy if (bi) 1163341618Scy bi->type = DPP_BOOTSTRAP_QR_CODE; 1164341618Scy return bi; 1165341618Scy} 1166341618Scy 1167341618Scy 1168341618Scystatic void dpp_debug_print_key(const char *title, EVP_PKEY *key) 1169341618Scy{ 1170341618Scy EC_KEY *eckey; 1171341618Scy BIO *out; 1172341618Scy size_t rlen; 1173341618Scy char *txt; 1174341618Scy int res; 1175341618Scy unsigned char *der = NULL; 1176341618Scy int der_len; 1177341618Scy const EC_GROUP *group; 1178341618Scy const EC_POINT *point; 1179341618Scy 1180341618Scy out = BIO_new(BIO_s_mem()); 1181341618Scy if (!out) 1182341618Scy return; 1183341618Scy 1184341618Scy EVP_PKEY_print_private(out, key, 0, NULL); 1185341618Scy rlen = BIO_ctrl_pending(out); 1186341618Scy txt = os_malloc(rlen + 1); 1187341618Scy if (txt) { 1188341618Scy res = BIO_read(out, txt, rlen); 1189341618Scy if (res > 0) { 1190341618Scy txt[res] = '\0'; 1191341618Scy wpa_printf(MSG_DEBUG, "%s: %s", title, txt); 1192341618Scy } 1193341618Scy os_free(txt); 1194341618Scy } 1195341618Scy BIO_free(out); 1196341618Scy 1197341618Scy eckey = EVP_PKEY_get1_EC_KEY(key); 1198341618Scy if (!eckey) 1199341618Scy return; 1200341618Scy 1201341618Scy group = EC_KEY_get0_group(eckey); 1202341618Scy point = EC_KEY_get0_public_key(eckey); 1203341618Scy if (group && point) 1204341618Scy dpp_debug_print_point(title, group, point); 1205341618Scy 1206341618Scy der_len = i2d_ECPrivateKey(eckey, &der); 1207341618Scy if (der_len > 0) 1208341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len); 1209341618Scy OPENSSL_free(der); 1210341618Scy if (der_len <= 0) { 1211341618Scy der = NULL; 1212341618Scy der_len = i2d_EC_PUBKEY(eckey, &der); 1213341618Scy if (der_len > 0) 1214341618Scy wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len); 1215341618Scy OPENSSL_free(der); 1216341618Scy } 1217341618Scy 1218341618Scy EC_KEY_free(eckey); 1219341618Scy} 1220341618Scy 1221341618Scy 1222341618Scystatic EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve) 1223341618Scy{ 1224341618Scy EVP_PKEY_CTX *kctx = NULL; 1225351611Scy EC_KEY *ec_params = NULL; 1226341618Scy EVP_PKEY *params = NULL, *key = NULL; 1227341618Scy int nid; 1228341618Scy 1229341618Scy wpa_printf(MSG_DEBUG, "DPP: Generating a keypair"); 1230341618Scy 1231341618Scy nid = OBJ_txt2nid(curve->name); 1232341618Scy if (nid == NID_undef) { 1233341618Scy wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name); 1234341618Scy return NULL; 1235341618Scy } 1236341618Scy 1237341618Scy ec_params = EC_KEY_new_by_curve_name(nid); 1238341618Scy if (!ec_params) { 1239341618Scy wpa_printf(MSG_ERROR, 1240341618Scy "DPP: Failed to generate EC_KEY parameters"); 1241341618Scy goto fail; 1242341618Scy } 1243341618Scy EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE); 1244341618Scy params = EVP_PKEY_new(); 1245341618Scy if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) { 1246341618Scy wpa_printf(MSG_ERROR, 1247341618Scy "DPP: Failed to generate EVP_PKEY parameters"); 1248341618Scy goto fail; 1249341618Scy } 1250341618Scy 1251341618Scy kctx = EVP_PKEY_CTX_new(params, NULL); 1252341618Scy if (!kctx || 1253341618Scy EVP_PKEY_keygen_init(kctx) != 1 || 1254341618Scy EVP_PKEY_keygen(kctx, &key) != 1) { 1255341618Scy wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key"); 1256351611Scy key = NULL; 1257341618Scy goto fail; 1258341618Scy } 1259341618Scy 1260341618Scy if (wpa_debug_show_keys) 1261341618Scy dpp_debug_print_key("Own generated key", key); 1262341618Scy 1263351611Scyfail: 1264351611Scy EC_KEY_free(ec_params); 1265341618Scy EVP_PKEY_free(params); 1266341618Scy EVP_PKEY_CTX_free(kctx); 1267341618Scy return key; 1268341618Scy} 1269341618Scy 1270341618Scy 1271341618Scystatic const struct dpp_curve_params * 1272341618Scydpp_get_curve_name(const char *name) 1273341618Scy{ 1274341618Scy int i; 1275341618Scy 1276341618Scy for (i = 0; dpp_curves[i].name; i++) { 1277341618Scy if (os_strcmp(name, dpp_curves[i].name) == 0 || 1278341618Scy (dpp_curves[i].jwk_crv && 1279341618Scy os_strcmp(name, dpp_curves[i].jwk_crv) == 0)) 1280341618Scy return &dpp_curves[i]; 1281341618Scy } 1282341618Scy return NULL; 1283341618Scy} 1284341618Scy 1285341618Scy 1286341618Scystatic const struct dpp_curve_params * 1287341618Scydpp_get_curve_jwk_crv(const char *name) 1288341618Scy{ 1289341618Scy int i; 1290341618Scy 1291341618Scy for (i = 0; dpp_curves[i].name; i++) { 1292341618Scy if (dpp_curves[i].jwk_crv && 1293341618Scy os_strcmp(name, dpp_curves[i].jwk_crv) == 0) 1294341618Scy return &dpp_curves[i]; 1295341618Scy } 1296341618Scy return NULL; 1297341618Scy} 1298341618Scy 1299341618Scy 1300341618Scystatic EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve, 1301341618Scy const u8 *privkey, size_t privkey_len) 1302341618Scy{ 1303341618Scy EVP_PKEY *pkey; 1304341618Scy EC_KEY *eckey; 1305341618Scy const EC_GROUP *group; 1306341618Scy int nid; 1307341618Scy 1308341618Scy pkey = EVP_PKEY_new(); 1309341618Scy if (!pkey) 1310341618Scy return NULL; 1311341618Scy eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len); 1312341618Scy if (!eckey) { 1313341618Scy wpa_printf(MSG_INFO, 1314341618Scy "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s", 1315341618Scy ERR_error_string(ERR_get_error(), NULL)); 1316341618Scy EVP_PKEY_free(pkey); 1317341618Scy return NULL; 1318341618Scy } 1319341618Scy group = EC_KEY_get0_group(eckey); 1320341618Scy if (!group) { 1321341618Scy EC_KEY_free(eckey); 1322341618Scy EVP_PKEY_free(pkey); 1323341618Scy return NULL; 1324341618Scy } 1325341618Scy nid = EC_GROUP_get_curve_name(group); 1326341618Scy *curve = dpp_get_curve_nid(nid); 1327341618Scy if (!*curve) { 1328341618Scy wpa_printf(MSG_INFO, 1329341618Scy "DPP: Unsupported curve (nid=%d) in pre-assigned key", 1330341618Scy nid); 1331341618Scy EC_KEY_free(eckey); 1332341618Scy EVP_PKEY_free(pkey); 1333341618Scy return NULL; 1334341618Scy } 1335341618Scy 1336341618Scy if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) { 1337341618Scy EC_KEY_free(eckey); 1338341618Scy EVP_PKEY_free(pkey); 1339341618Scy return NULL; 1340341618Scy } 1341341618Scy return pkey; 1342341618Scy} 1343341618Scy 1344341618Scy 1345341618Scytypedef struct { 1346341618Scy /* AlgorithmIdentifier ecPublicKey with optional parameters present 1347341618Scy * as an OID identifying the curve */ 1348341618Scy X509_ALGOR *alg; 1349341618Scy /* Compressed format public key per ANSI X9.63 */ 1350341618Scy ASN1_BIT_STRING *pub_key; 1351341618Scy} DPP_BOOTSTRAPPING_KEY; 1352341618Scy 1353341618ScyASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = { 1354341618Scy ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR), 1355341618Scy ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING) 1356341618Scy} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY); 1357341618Scy 1358341618ScyIMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY); 1359341618Scy 1360341618Scy 1361341618Scystatic struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key) 1362341618Scy{ 1363341618Scy unsigned char *der = NULL; 1364341618Scy int der_len; 1365341618Scy EC_KEY *eckey; 1366341618Scy struct wpabuf *ret = NULL; 1367341618Scy size_t len; 1368341618Scy const EC_GROUP *group; 1369341618Scy const EC_POINT *point; 1370341618Scy BN_CTX *ctx; 1371341618Scy DPP_BOOTSTRAPPING_KEY *bootstrap = NULL; 1372341618Scy int nid; 1373341618Scy 1374341618Scy ctx = BN_CTX_new(); 1375341618Scy eckey = EVP_PKEY_get1_EC_KEY(key); 1376341618Scy if (!ctx || !eckey) 1377341618Scy goto fail; 1378341618Scy 1379341618Scy group = EC_KEY_get0_group(eckey); 1380341618Scy point = EC_KEY_get0_public_key(eckey); 1381341618Scy if (!group || !point) 1382341618Scy goto fail; 1383341618Scy dpp_debug_print_point("DPP: bootstrap public key", group, point); 1384341618Scy nid = EC_GROUP_get_curve_name(group); 1385341618Scy 1386341618Scy bootstrap = DPP_BOOTSTRAPPING_KEY_new(); 1387341618Scy if (!bootstrap || 1388341618Scy X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC), 1389341618Scy V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1) 1390341618Scy goto fail; 1391341618Scy 1392341618Scy len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, 1393341618Scy NULL, 0, ctx); 1394341618Scy if (len == 0) 1395341618Scy goto fail; 1396341618Scy 1397341618Scy der = OPENSSL_malloc(len); 1398341618Scy if (!der) 1399341618Scy goto fail; 1400341618Scy len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED, 1401341618Scy der, len, ctx); 1402341618Scy 1403341618Scy OPENSSL_free(bootstrap->pub_key->data); 1404341618Scy bootstrap->pub_key->data = der; 1405341618Scy der = NULL; 1406341618Scy bootstrap->pub_key->length = len; 1407341618Scy /* No unused bits */ 1408341618Scy bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); 1409341618Scy bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT; 1410341618Scy 1411341618Scy der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der); 1412341618Scy if (der_len <= 0) { 1413341618Scy wpa_printf(MSG_ERROR, 1414341618Scy "DDP: Failed to build DER encoded public key"); 1415341618Scy goto fail; 1416341618Scy } 1417341618Scy 1418341618Scy ret = wpabuf_alloc_copy(der, der_len); 1419341618Scyfail: 1420341618Scy DPP_BOOTSTRAPPING_KEY_free(bootstrap); 1421341618Scy OPENSSL_free(der); 1422341618Scy EC_KEY_free(eckey); 1423341618Scy BN_CTX_free(ctx); 1424341618Scy return ret; 1425341618Scy} 1426341618Scy 1427341618Scy 1428341618Scyint dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi) 1429341618Scy{ 1430341618Scy struct wpabuf *der; 1431341618Scy int res; 1432341618Scy const u8 *addr[1]; 1433341618Scy size_t len[1]; 1434341618Scy 1435341618Scy der = dpp_bootstrap_key_der(bi->pubkey); 1436341618Scy if (!der) 1437341618Scy return -1; 1438341618Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)", 1439341618Scy der); 1440341618Scy 1441341618Scy addr[0] = wpabuf_head(der); 1442341618Scy len[0] = wpabuf_len(der); 1443341618Scy res = sha256_vector(1, addr, len, bi->pubkey_hash); 1444341618Scy if (res < 0) 1445341618Scy wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); 1446341618Scy else 1447341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash, 1448341618Scy SHA256_MAC_LEN); 1449341618Scy wpabuf_free(der); 1450341618Scy return res; 1451341618Scy} 1452341618Scy 1453341618Scy 1454341618Scychar * dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve, 1455341618Scy const u8 *privkey, size_t privkey_len) 1456341618Scy{ 1457341618Scy unsigned char *base64 = NULL; 1458341618Scy char *pos, *end; 1459341618Scy size_t len; 1460341618Scy struct wpabuf *der = NULL; 1461341618Scy const u8 *addr[1]; 1462341618Scy int res; 1463341618Scy 1464341618Scy if (!curve) { 1465341618Scy bi->curve = &dpp_curves[0]; 1466341618Scy } else { 1467341618Scy bi->curve = dpp_get_curve_name(curve); 1468341618Scy if (!bi->curve) { 1469341618Scy wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", 1470341618Scy curve); 1471341618Scy return NULL; 1472341618Scy } 1473341618Scy } 1474341618Scy if (privkey) 1475341618Scy bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len); 1476341618Scy else 1477341618Scy bi->pubkey = dpp_gen_keypair(bi->curve); 1478341618Scy if (!bi->pubkey) 1479341618Scy goto fail; 1480341618Scy bi->own = 1; 1481341618Scy 1482341618Scy der = dpp_bootstrap_key_der(bi->pubkey); 1483341618Scy if (!der) 1484341618Scy goto fail; 1485341618Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)", 1486341618Scy der); 1487341618Scy 1488341618Scy addr[0] = wpabuf_head(der); 1489341618Scy len = wpabuf_len(der); 1490341618Scy res = sha256_vector(1, addr, &len, bi->pubkey_hash); 1491341618Scy if (res < 0) { 1492341618Scy wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key"); 1493341618Scy goto fail; 1494341618Scy } 1495341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Public key hash", bi->pubkey_hash, 1496341618Scy SHA256_MAC_LEN); 1497341618Scy 1498341618Scy base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len); 1499341618Scy wpabuf_free(der); 1500341618Scy der = NULL; 1501341618Scy if (!base64) 1502341618Scy goto fail; 1503341618Scy pos = (char *) base64; 1504341618Scy end = pos + len; 1505341618Scy for (;;) { 1506341618Scy pos = os_strchr(pos, '\n'); 1507341618Scy if (!pos) 1508341618Scy break; 1509341618Scy os_memmove(pos, pos + 1, end - pos); 1510341618Scy } 1511341618Scy return (char *) base64; 1512341618Scyfail: 1513341618Scy os_free(base64); 1514341618Scy wpabuf_free(der); 1515341618Scy return NULL; 1516341618Scy} 1517341618Scy 1518341618Scy 1519341618Scystatic int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, 1520341618Scy unsigned int hash_len) 1521341618Scy{ 1522341618Scy u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; 1523341618Scy const char *info = "first intermediate key"; 1524341618Scy int res; 1525341618Scy 1526341618Scy /* k1 = HKDF(<>, "first intermediate key", M.x) */ 1527341618Scy 1528341618Scy /* HKDF-Extract(<>, M.x) */ 1529341618Scy os_memset(salt, 0, hash_len); 1530341618Scy if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0) 1531341618Scy return -1; 1532341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)", 1533341618Scy prk, hash_len); 1534341618Scy 1535341618Scy /* HKDF-Expand(PRK, info, L) */ 1536341618Scy res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len); 1537341618Scy os_memset(prk, 0, hash_len); 1538341618Scy if (res < 0) 1539341618Scy return -1; 1540341618Scy 1541341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)", 1542341618Scy k1, hash_len); 1543341618Scy return 0; 1544341618Scy} 1545341618Scy 1546341618Scy 1547341618Scystatic int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, 1548341618Scy unsigned int hash_len) 1549341618Scy{ 1550341618Scy u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; 1551341618Scy const char *info = "second intermediate key"; 1552341618Scy int res; 1553341618Scy 1554341618Scy /* k2 = HKDF(<>, "second intermediate key", N.x) */ 1555341618Scy 1556341618Scy /* HKDF-Extract(<>, N.x) */ 1557341618Scy os_memset(salt, 0, hash_len); 1558341618Scy res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk); 1559341618Scy if (res < 0) 1560341618Scy return -1; 1561341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)", 1562341618Scy prk, hash_len); 1563341618Scy 1564341618Scy /* HKDF-Expand(PRK, info, L) */ 1565341618Scy res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len); 1566341618Scy os_memset(prk, 0, hash_len); 1567341618Scy if (res < 0) 1568341618Scy return -1; 1569341618Scy 1570341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)", 1571341618Scy k2, hash_len); 1572341618Scy return 0; 1573341618Scy} 1574341618Scy 1575341618Scy 1576341618Scystatic int dpp_derive_ke(struct dpp_authentication *auth, u8 *ke, 1577341618Scy unsigned int hash_len) 1578341618Scy{ 1579341618Scy size_t nonce_len; 1580341618Scy u8 nonces[2 * DPP_MAX_NONCE_LEN]; 1581341618Scy const char *info_ke = "DPP Key"; 1582341618Scy u8 prk[DPP_MAX_HASH_LEN]; 1583341618Scy int res; 1584341618Scy const u8 *addr[3]; 1585341618Scy size_t len[3]; 1586341618Scy size_t num_elem = 0; 1587341618Scy 1588341618Scy if (!auth->Mx_len || !auth->Nx_len) { 1589341618Scy wpa_printf(MSG_DEBUG, 1590341618Scy "DPP: Mx/Nx not available - cannot derive ke"); 1591341618Scy return -1; 1592341618Scy } 1593341618Scy 1594341618Scy /* ke = HKDF(I-nonce | R-nonce, "DPP Key", M.x | N.x [| L.x]) */ 1595341618Scy 1596341618Scy /* HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */ 1597341618Scy nonce_len = auth->curve->nonce_len; 1598341618Scy os_memcpy(nonces, auth->i_nonce, nonce_len); 1599341618Scy os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len); 1600341618Scy addr[num_elem] = auth->Mx; 1601341618Scy len[num_elem] = auth->Mx_len; 1602341618Scy num_elem++; 1603341618Scy addr[num_elem] = auth->Nx; 1604341618Scy len[num_elem] = auth->Nx_len; 1605341618Scy num_elem++; 1606341618Scy if (auth->peer_bi && auth->own_bi) { 1607341618Scy if (!auth->Lx_len) { 1608341618Scy wpa_printf(MSG_DEBUG, 1609341618Scy "DPP: Lx not available - cannot derive ke"); 1610341618Scy return -1; 1611341618Scy } 1612341618Scy addr[num_elem] = auth->Lx; 1613341618Scy len[num_elem] = auth->secret_len; 1614341618Scy num_elem++; 1615341618Scy } 1616341618Scy res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len, 1617341618Scy num_elem, addr, len, prk); 1618341618Scy if (res < 0) 1619341618Scy return -1; 1620341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)", 1621341618Scy prk, hash_len); 1622341618Scy 1623341618Scy /* HKDF-Expand(PRK, info, L) */ 1624341618Scy res = dpp_hkdf_expand(hash_len, prk, hash_len, info_ke, ke, hash_len); 1625341618Scy os_memset(prk, 0, hash_len); 1626341618Scy if (res < 0) 1627341618Scy return -1; 1628341618Scy 1629341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ke = HKDF-Expand(PRK, info, L)", 1630341618Scy ke, hash_len); 1631341618Scy return 0; 1632341618Scy} 1633341618Scy 1634341618Scy 1635341618Scystatic void dpp_build_attr_status(struct wpabuf *msg, 1636341618Scy enum dpp_status_error status) 1637341618Scy{ 1638341618Scy wpa_printf(MSG_DEBUG, "DPP: Status %d", status); 1639341618Scy wpabuf_put_le16(msg, DPP_ATTR_STATUS); 1640341618Scy wpabuf_put_le16(msg, 1); 1641341618Scy wpabuf_put_u8(msg, status); 1642341618Scy} 1643341618Scy 1644341618Scy 1645341618Scystatic void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, 1646341618Scy const u8 *hash) 1647341618Scy{ 1648341618Scy if (hash) { 1649341618Scy wpa_printf(MSG_DEBUG, "DPP: R-Bootstrap Key Hash"); 1650341618Scy wpabuf_put_le16(msg, DPP_ATTR_R_BOOTSTRAP_KEY_HASH); 1651341618Scy wpabuf_put_le16(msg, SHA256_MAC_LEN); 1652341618Scy wpabuf_put_data(msg, hash, SHA256_MAC_LEN); 1653341618Scy } 1654341618Scy} 1655341618Scy 1656341618Scy 1657341618Scystatic void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg, 1658341618Scy const u8 *hash) 1659341618Scy{ 1660341618Scy if (hash) { 1661341618Scy wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash"); 1662341618Scy wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH); 1663341618Scy wpabuf_put_le16(msg, SHA256_MAC_LEN); 1664341618Scy wpabuf_put_data(msg, hash, SHA256_MAC_LEN); 1665341618Scy } 1666341618Scy} 1667341618Scy 1668341618Scy 1669341618Scystatic struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth, 1670341618Scy const struct wpabuf *pi, 1671341618Scy size_t nonce_len, 1672341618Scy const u8 *r_pubkey_hash, 1673341618Scy const u8 *i_pubkey_hash, 1674341618Scy unsigned int neg_freq) 1675341618Scy{ 1676341618Scy struct wpabuf *msg; 1677341618Scy u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1]; 1678341618Scy u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE]; 1679341618Scy u8 *pos; 1680341618Scy const u8 *addr[2]; 1681341618Scy size_t len[2], siv_len, attr_len; 1682341618Scy u8 *attr_start, *attr_end; 1683341618Scy 1684341618Scy /* Build DPP Authentication Request frame attributes */ 1685341618Scy attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) + 1686341618Scy 4 + sizeof(wrapped_data); 1687341618Scy if (neg_freq > 0) 1688341618Scy attr_len += 4 + 2; 1689346981Scy#ifdef CONFIG_DPP2 1690346981Scy attr_len += 5; 1691346981Scy#endif /* CONFIG_DPP2 */ 1692341618Scy#ifdef CONFIG_TESTING_OPTIONS 1693341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) 1694341618Scy attr_len += 5; 1695341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1696341618Scy msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len); 1697341618Scy if (!msg) 1698341618Scy return NULL; 1699341618Scy 1700341618Scy attr_start = wpabuf_put(msg, 0); 1701341618Scy 1702341618Scy /* Responder Bootstrapping Key Hash */ 1703341618Scy dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); 1704341618Scy 1705341618Scy /* Initiator Bootstrapping Key Hash */ 1706341618Scy dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); 1707341618Scy 1708341618Scy /* Initiator Protocol Key */ 1709341618Scy if (pi) { 1710341618Scy wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY); 1711341618Scy wpabuf_put_le16(msg, wpabuf_len(pi)); 1712341618Scy wpabuf_put_buf(msg, pi); 1713341618Scy } 1714341618Scy 1715341618Scy /* Channel */ 1716341618Scy if (neg_freq > 0) { 1717341618Scy u8 op_class, channel; 1718341618Scy 1719341618Scy if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class, 1720341618Scy &channel) == 1721341618Scy NUM_HOSTAPD_MODES) { 1722341618Scy wpa_printf(MSG_INFO, 1723341618Scy "DPP: Unsupported negotiation frequency request: %d", 1724341618Scy neg_freq); 1725341618Scy wpabuf_free(msg); 1726341618Scy return NULL; 1727341618Scy } 1728341618Scy wpabuf_put_le16(msg, DPP_ATTR_CHANNEL); 1729341618Scy wpabuf_put_le16(msg, 2); 1730341618Scy wpabuf_put_u8(msg, op_class); 1731341618Scy wpabuf_put_u8(msg, channel); 1732341618Scy } 1733341618Scy 1734346981Scy#ifdef CONFIG_DPP2 1735346981Scy /* Protocol Version */ 1736346981Scy wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); 1737346981Scy wpabuf_put_le16(msg, 1); 1738346981Scy wpabuf_put_u8(msg, 2); 1739346981Scy#endif /* CONFIG_DPP2 */ 1740346981Scy 1741341618Scy#ifdef CONFIG_TESTING_OPTIONS 1742341618Scy if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) { 1743341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); 1744341618Scy goto skip_wrapped_data; 1745341618Scy } 1746341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1747341618Scy 1748341618Scy /* Wrapped data ({I-nonce, I-capabilities}k1) */ 1749341618Scy pos = clear; 1750341618Scy 1751341618Scy#ifdef CONFIG_TESTING_OPTIONS 1752341618Scy if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) { 1753341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); 1754341618Scy goto skip_i_nonce; 1755341618Scy } 1756341618Scy if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) { 1757341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce"); 1758341618Scy WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); 1759341618Scy pos += 2; 1760341618Scy WPA_PUT_LE16(pos, nonce_len - 1); 1761341618Scy pos += 2; 1762341618Scy os_memcpy(pos, auth->i_nonce, nonce_len - 1); 1763341618Scy pos += nonce_len - 1; 1764341618Scy goto skip_i_nonce; 1765341618Scy } 1766341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1767341618Scy 1768341618Scy /* I-nonce */ 1769341618Scy WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); 1770341618Scy pos += 2; 1771341618Scy WPA_PUT_LE16(pos, nonce_len); 1772341618Scy pos += 2; 1773341618Scy os_memcpy(pos, auth->i_nonce, nonce_len); 1774341618Scy pos += nonce_len; 1775341618Scy 1776341618Scy#ifdef CONFIG_TESTING_OPTIONS 1777341618Scyskip_i_nonce: 1778341618Scy if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) { 1779341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab"); 1780341618Scy goto skip_i_capab; 1781341618Scy } 1782341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1783341618Scy 1784341618Scy /* I-capabilities */ 1785341618Scy WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES); 1786341618Scy pos += 2; 1787341618Scy WPA_PUT_LE16(pos, 1); 1788341618Scy pos += 2; 1789341618Scy auth->i_capab = auth->allowed_roles; 1790341618Scy *pos++ = auth->i_capab; 1791341618Scy#ifdef CONFIG_TESTING_OPTIONS 1792341618Scy if (dpp_test == DPP_TEST_ZERO_I_CAPAB) { 1793341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities"); 1794341618Scy pos[-1] = 0; 1795341618Scy } 1796341618Scyskip_i_capab: 1797341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1798341618Scy 1799341618Scy attr_end = wpabuf_put(msg, 0); 1800341618Scy 1801341618Scy /* OUI, OUI type, Crypto Suite, DPP frame type */ 1802341618Scy addr[0] = wpabuf_head_u8(msg) + 2; 1803341618Scy len[0] = 3 + 1 + 1 + 1; 1804341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 1805341618Scy 1806341618Scy /* Attributes before Wrapped Data */ 1807341618Scy addr[1] = attr_start; 1808341618Scy len[1] = attr_end - attr_start; 1809341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 1810341618Scy 1811341618Scy siv_len = pos - clear; 1812341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); 1813341618Scy if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len, 1814341618Scy 2, addr, len, wrapped_data) < 0) { 1815341618Scy wpabuf_free(msg); 1816341618Scy return NULL; 1817341618Scy } 1818341618Scy siv_len += AES_BLOCK_SIZE; 1819341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 1820341618Scy wrapped_data, siv_len); 1821341618Scy 1822341618Scy wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 1823341618Scy wpabuf_put_le16(msg, siv_len); 1824341618Scy wpabuf_put_data(msg, wrapped_data, siv_len); 1825341618Scy 1826341618Scy#ifdef CONFIG_TESTING_OPTIONS 1827341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) { 1828341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); 1829341618Scy dpp_build_attr_status(msg, DPP_STATUS_OK); 1830341618Scy } 1831341618Scyskip_wrapped_data: 1832341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1833341618Scy 1834341618Scy wpa_hexdump_buf(MSG_DEBUG, 1835341618Scy "DPP: Authentication Request frame attributes", msg); 1836341618Scy 1837341618Scy return msg; 1838341618Scy} 1839341618Scy 1840341618Scy 1841341618Scystatic struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth, 1842341618Scy enum dpp_status_error status, 1843341618Scy const struct wpabuf *pr, 1844341618Scy size_t nonce_len, 1845341618Scy const u8 *r_pubkey_hash, 1846341618Scy const u8 *i_pubkey_hash, 1847341618Scy const u8 *r_nonce, const u8 *i_nonce, 1848341618Scy const u8 *wrapped_r_auth, 1849341618Scy size_t wrapped_r_auth_len, 1850341618Scy const u8 *siv_key) 1851341618Scy{ 1852341618Scy struct wpabuf *msg; 1853341618Scy#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \ 1854341618Scy 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE 1855341618Scy u8 clear[DPP_AUTH_RESP_CLEAR_LEN]; 1856341618Scy u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE]; 1857341618Scy const u8 *addr[2]; 1858341618Scy size_t len[2], siv_len, attr_len; 1859341618Scy u8 *attr_start, *attr_end, *pos; 1860341618Scy 1861341618Scy auth->waiting_auth_conf = 1; 1862341618Scy auth->auth_resp_tries = 0; 1863341618Scy 1864341618Scy /* Build DPP Authentication Response frame attributes */ 1865341618Scy attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + 1866341618Scy 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data); 1867346981Scy#ifdef CONFIG_DPP2 1868346981Scy attr_len += 5; 1869346981Scy#endif /* CONFIG_DPP2 */ 1870341618Scy#ifdef CONFIG_TESTING_OPTIONS 1871341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) 1872341618Scy attr_len += 5; 1873341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1874341618Scy msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len); 1875341618Scy if (!msg) 1876341618Scy return NULL; 1877341618Scy 1878341618Scy attr_start = wpabuf_put(msg, 0); 1879341618Scy 1880341618Scy /* DPP Status */ 1881341618Scy if (status != 255) 1882341618Scy dpp_build_attr_status(msg, status); 1883341618Scy 1884341618Scy /* Responder Bootstrapping Key Hash */ 1885341618Scy dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); 1886341618Scy 1887341618Scy /* Initiator Bootstrapping Key Hash (mutual authentication) */ 1888341618Scy dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); 1889341618Scy 1890341618Scy /* Responder Protocol Key */ 1891341618Scy if (pr) { 1892341618Scy wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY); 1893341618Scy wpabuf_put_le16(msg, wpabuf_len(pr)); 1894341618Scy wpabuf_put_buf(msg, pr); 1895341618Scy } 1896341618Scy 1897346981Scy#ifdef CONFIG_DPP2 1898346981Scy /* Protocol Version */ 1899346981Scy wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION); 1900346981Scy wpabuf_put_le16(msg, 1); 1901346981Scy wpabuf_put_u8(msg, 2); 1902346981Scy#endif /* CONFIG_DPP2 */ 1903346981Scy 1904341618Scy attr_end = wpabuf_put(msg, 0); 1905341618Scy 1906341618Scy#ifdef CONFIG_TESTING_OPTIONS 1907341618Scy if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) { 1908341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); 1909341618Scy goto skip_wrapped_data; 1910341618Scy } 1911341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1912341618Scy 1913341618Scy /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */ 1914341618Scy pos = clear; 1915341618Scy 1916341618Scy if (r_nonce) { 1917341618Scy /* R-nonce */ 1918341618Scy WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE); 1919341618Scy pos += 2; 1920341618Scy WPA_PUT_LE16(pos, nonce_len); 1921341618Scy pos += 2; 1922341618Scy os_memcpy(pos, r_nonce, nonce_len); 1923341618Scy pos += nonce_len; 1924341618Scy } 1925341618Scy 1926341618Scy if (i_nonce) { 1927341618Scy /* I-nonce */ 1928341618Scy WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE); 1929341618Scy pos += 2; 1930341618Scy WPA_PUT_LE16(pos, nonce_len); 1931341618Scy pos += 2; 1932341618Scy os_memcpy(pos, i_nonce, nonce_len); 1933341618Scy#ifdef CONFIG_TESTING_OPTIONS 1934341618Scy if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) { 1935341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch"); 1936341618Scy pos[nonce_len / 2] ^= 0x01; 1937341618Scy } 1938341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1939341618Scy pos += nonce_len; 1940341618Scy } 1941341618Scy 1942341618Scy#ifdef CONFIG_TESTING_OPTIONS 1943341618Scy if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) { 1944341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab"); 1945341618Scy goto skip_r_capab; 1946341618Scy } 1947341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1948341618Scy 1949341618Scy /* R-capabilities */ 1950341618Scy WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES); 1951341618Scy pos += 2; 1952341618Scy WPA_PUT_LE16(pos, 1); 1953341618Scy pos += 2; 1954341618Scy auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR : 1955341618Scy DPP_CAPAB_ENROLLEE; 1956341618Scy *pos++ = auth->r_capab; 1957341618Scy#ifdef CONFIG_TESTING_OPTIONS 1958341618Scy if (dpp_test == DPP_TEST_ZERO_R_CAPAB) { 1959341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities"); 1960341618Scy pos[-1] = 0; 1961341618Scy } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) { 1962341618Scy wpa_printf(MSG_INFO, 1963341618Scy "DPP: TESTING - incompatible R-capabilities"); 1964341618Scy if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) == 1965341618Scy (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) 1966341618Scy pos[-1] = 0; 1967341618Scy else 1968341618Scy pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE : 1969341618Scy DPP_CAPAB_CONFIGURATOR; 1970341618Scy } 1971341618Scyskip_r_capab: 1972341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 1973341618Scy 1974341618Scy if (wrapped_r_auth) { 1975341618Scy /* {R-auth}ke */ 1976341618Scy WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA); 1977341618Scy pos += 2; 1978341618Scy WPA_PUT_LE16(pos, wrapped_r_auth_len); 1979341618Scy pos += 2; 1980341618Scy os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len); 1981341618Scy pos += wrapped_r_auth_len; 1982341618Scy } 1983341618Scy 1984341618Scy /* OUI, OUI type, Crypto Suite, DPP frame type */ 1985341618Scy addr[0] = wpabuf_head_u8(msg) + 2; 1986341618Scy len[0] = 3 + 1 + 1 + 1; 1987341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 1988341618Scy 1989341618Scy /* Attributes before Wrapped Data */ 1990341618Scy addr[1] = attr_start; 1991341618Scy len[1] = attr_end - attr_start; 1992341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 1993341618Scy 1994341618Scy siv_len = pos - clear; 1995341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len); 1996341618Scy if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len, 1997341618Scy 2, addr, len, wrapped_data) < 0) { 1998341618Scy wpabuf_free(msg); 1999341618Scy return NULL; 2000341618Scy } 2001341618Scy siv_len += AES_BLOCK_SIZE; 2002341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 2003341618Scy wrapped_data, siv_len); 2004341618Scy 2005341618Scy wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 2006341618Scy wpabuf_put_le16(msg, siv_len); 2007341618Scy wpabuf_put_data(msg, wrapped_data, siv_len); 2008341618Scy 2009341618Scy#ifdef CONFIG_TESTING_OPTIONS 2010341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) { 2011341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); 2012341618Scy dpp_build_attr_status(msg, DPP_STATUS_OK); 2013341618Scy } 2014341618Scyskip_wrapped_data: 2015341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2016341618Scy 2017341618Scy wpa_hexdump_buf(MSG_DEBUG, 2018341618Scy "DPP: Authentication Response frame attributes", msg); 2019341618Scy return msg; 2020341618Scy} 2021341618Scy 2022341618Scy 2023341618Scystatic int dpp_channel_ok_init(struct hostapd_hw_modes *own_modes, 2024341618Scy u16 num_modes, unsigned int freq) 2025341618Scy{ 2026341618Scy u16 m; 2027341618Scy int c, flag; 2028341618Scy 2029341618Scy if (!own_modes || !num_modes) 2030341618Scy return 1; 2031341618Scy 2032341618Scy for (m = 0; m < num_modes; m++) { 2033341618Scy for (c = 0; c < own_modes[m].num_channels; c++) { 2034341618Scy if ((unsigned int) own_modes[m].channels[c].freq != 2035341618Scy freq) 2036341618Scy continue; 2037341618Scy flag = own_modes[m].channels[c].flag; 2038341618Scy if (!(flag & (HOSTAPD_CHAN_DISABLED | 2039341618Scy HOSTAPD_CHAN_NO_IR | 2040341618Scy HOSTAPD_CHAN_RADAR))) 2041341618Scy return 1; 2042341618Scy } 2043341618Scy } 2044341618Scy 2045341618Scy wpa_printf(MSG_DEBUG, "DPP: Peer channel %u MHz not supported", freq); 2046341618Scy return 0; 2047341618Scy} 2048341618Scy 2049341618Scy 2050341618Scystatic int freq_included(const unsigned int freqs[], unsigned int num, 2051341618Scy unsigned int freq) 2052341618Scy{ 2053341618Scy while (num > 0) { 2054341618Scy if (freqs[--num] == freq) 2055341618Scy return 1; 2056341618Scy } 2057341618Scy return 0; 2058341618Scy} 2059341618Scy 2060341618Scy 2061341618Scystatic void freq_to_start(unsigned int freqs[], unsigned int num, 2062341618Scy unsigned int freq) 2063341618Scy{ 2064341618Scy unsigned int i; 2065341618Scy 2066341618Scy for (i = 0; i < num; i++) { 2067341618Scy if (freqs[i] == freq) 2068341618Scy break; 2069341618Scy } 2070341618Scy if (i == 0 || i >= num) 2071341618Scy return; 2072341618Scy os_memmove(&freqs[1], &freqs[0], i * sizeof(freqs[0])); 2073341618Scy freqs[0] = freq; 2074341618Scy} 2075341618Scy 2076341618Scy 2077341618Scystatic int dpp_channel_intersect(struct dpp_authentication *auth, 2078341618Scy struct hostapd_hw_modes *own_modes, 2079341618Scy u16 num_modes) 2080341618Scy{ 2081341618Scy struct dpp_bootstrap_info *peer_bi = auth->peer_bi; 2082341618Scy unsigned int i, freq; 2083341618Scy 2084341618Scy for (i = 0; i < peer_bi->num_freq; i++) { 2085341618Scy freq = peer_bi->freq[i]; 2086341618Scy if (freq_included(auth->freq, auth->num_freq, freq)) 2087341618Scy continue; 2088341618Scy if (dpp_channel_ok_init(own_modes, num_modes, freq)) 2089341618Scy auth->freq[auth->num_freq++] = freq; 2090341618Scy } 2091341618Scy if (!auth->num_freq) { 2092341618Scy wpa_printf(MSG_INFO, 2093341618Scy "DPP: No available channels for initiating DPP Authentication"); 2094341618Scy return -1; 2095341618Scy } 2096341618Scy auth->curr_freq = auth->freq[0]; 2097341618Scy return 0; 2098341618Scy} 2099341618Scy 2100341618Scy 2101341618Scystatic int dpp_channel_local_list(struct dpp_authentication *auth, 2102341618Scy struct hostapd_hw_modes *own_modes, 2103341618Scy u16 num_modes) 2104341618Scy{ 2105341618Scy u16 m; 2106341618Scy int c, flag; 2107341618Scy unsigned int freq; 2108341618Scy 2109341618Scy auth->num_freq = 0; 2110341618Scy 2111341618Scy if (!own_modes || !num_modes) { 2112341618Scy auth->freq[0] = 2412; 2113341618Scy auth->freq[1] = 2437; 2114341618Scy auth->freq[2] = 2462; 2115341618Scy auth->num_freq = 3; 2116341618Scy return 0; 2117341618Scy } 2118341618Scy 2119341618Scy for (m = 0; m < num_modes; m++) { 2120341618Scy for (c = 0; c < own_modes[m].num_channels; c++) { 2121341618Scy freq = own_modes[m].channels[c].freq; 2122341618Scy flag = own_modes[m].channels[c].flag; 2123341618Scy if (flag & (HOSTAPD_CHAN_DISABLED | 2124341618Scy HOSTAPD_CHAN_NO_IR | 2125341618Scy HOSTAPD_CHAN_RADAR)) 2126341618Scy continue; 2127341618Scy if (freq_included(auth->freq, auth->num_freq, freq)) 2128341618Scy continue; 2129341618Scy auth->freq[auth->num_freq++] = freq; 2130341618Scy if (auth->num_freq == DPP_BOOTSTRAP_MAX_FREQ) { 2131341618Scy m = num_modes; 2132341618Scy break; 2133341618Scy } 2134341618Scy } 2135341618Scy } 2136341618Scy 2137341618Scy return auth->num_freq == 0 ? -1 : 0; 2138341618Scy} 2139341618Scy 2140341618Scy 2141341618Scystatic int dpp_prepare_channel_list(struct dpp_authentication *auth, 2142341618Scy struct hostapd_hw_modes *own_modes, 2143341618Scy u16 num_modes) 2144341618Scy{ 2145341618Scy int res; 2146341618Scy char freqs[DPP_BOOTSTRAP_MAX_FREQ * 6 + 10], *pos, *end; 2147341618Scy unsigned int i; 2148341618Scy 2149341618Scy if (auth->peer_bi->num_freq > 0) 2150341618Scy res = dpp_channel_intersect(auth, own_modes, num_modes); 2151341618Scy else 2152341618Scy res = dpp_channel_local_list(auth, own_modes, num_modes); 2153341618Scy if (res < 0) 2154341618Scy return res; 2155341618Scy 2156341618Scy /* Prioritize 2.4 GHz channels 6, 1, 11 (in this order) to hit the most 2157341618Scy * likely channels first. */ 2158341618Scy freq_to_start(auth->freq, auth->num_freq, 2462); 2159341618Scy freq_to_start(auth->freq, auth->num_freq, 2412); 2160341618Scy freq_to_start(auth->freq, auth->num_freq, 2437); 2161341618Scy 2162341618Scy auth->freq_idx = 0; 2163341618Scy auth->curr_freq = auth->freq[0]; 2164341618Scy 2165341618Scy pos = freqs; 2166341618Scy end = pos + sizeof(freqs); 2167341618Scy for (i = 0; i < auth->num_freq; i++) { 2168341618Scy res = os_snprintf(pos, end - pos, " %u", auth->freq[i]); 2169341618Scy if (os_snprintf_error(end - pos, res)) 2170341618Scy break; 2171341618Scy pos += res; 2172341618Scy } 2173341618Scy *pos = '\0'; 2174341618Scy wpa_printf(MSG_DEBUG, "DPP: Possible frequencies for initiating:%s", 2175341618Scy freqs); 2176341618Scy 2177341618Scy return 0; 2178341618Scy} 2179341618Scy 2180341618Scy 2181341618Scystatic int dpp_autogen_bootstrap_key(struct dpp_authentication *auth) 2182341618Scy{ 2183341618Scy struct dpp_bootstrap_info *bi; 2184341618Scy char *pk = NULL; 2185341618Scy size_t len; 2186341618Scy 2187341618Scy if (auth->own_bi) 2188341618Scy return 0; /* already generated */ 2189341618Scy 2190341618Scy bi = os_zalloc(sizeof(*bi)); 2191341618Scy if (!bi) 2192341618Scy return -1; 2193341618Scy bi->type = DPP_BOOTSTRAP_QR_CODE; 2194341618Scy pk = dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0); 2195341618Scy if (!pk) 2196341618Scy goto fail; 2197341618Scy 2198341618Scy len = 4; /* "DPP:" */ 2199341618Scy len += 4 + os_strlen(pk); 2200341618Scy bi->uri = os_malloc(len + 1); 2201341618Scy if (!bi->uri) 2202341618Scy goto fail; 2203341618Scy os_snprintf(bi->uri, len + 1, "DPP:K:%s;;", pk); 2204341618Scy wpa_printf(MSG_DEBUG, 2205341618Scy "DPP: Auto-generated own bootstrapping key info: URI %s", 2206341618Scy bi->uri); 2207341618Scy 2208341618Scy auth->tmp_own_bi = auth->own_bi = bi; 2209341618Scy 2210341618Scy os_free(pk); 2211341618Scy 2212341618Scy return 0; 2213341618Scyfail: 2214341618Scy os_free(pk); 2215341618Scy dpp_bootstrap_info_free(bi); 2216341618Scy return -1; 2217341618Scy} 2218341618Scy 2219341618Scy 2220341618Scystruct dpp_authentication * dpp_auth_init(void *msg_ctx, 2221341618Scy struct dpp_bootstrap_info *peer_bi, 2222341618Scy struct dpp_bootstrap_info *own_bi, 2223341618Scy u8 dpp_allowed_roles, 2224341618Scy unsigned int neg_freq, 2225341618Scy struct hostapd_hw_modes *own_modes, 2226341618Scy u16 num_modes) 2227341618Scy{ 2228341618Scy struct dpp_authentication *auth; 2229341618Scy size_t nonce_len; 2230341618Scy size_t secret_len; 2231341618Scy struct wpabuf *pi = NULL; 2232341618Scy const u8 *r_pubkey_hash, *i_pubkey_hash; 2233341618Scy#ifdef CONFIG_TESTING_OPTIONS 2234341618Scy u8 test_hash[SHA256_MAC_LEN]; 2235341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2236341618Scy 2237341618Scy auth = os_zalloc(sizeof(*auth)); 2238341618Scy if (!auth) 2239341618Scy return NULL; 2240341618Scy auth->msg_ctx = msg_ctx; 2241341618Scy auth->initiator = 1; 2242341618Scy auth->waiting_auth_resp = 1; 2243341618Scy auth->allowed_roles = dpp_allowed_roles; 2244341618Scy auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR); 2245341618Scy auth->peer_bi = peer_bi; 2246341618Scy auth->own_bi = own_bi; 2247341618Scy auth->curve = peer_bi->curve; 2248341618Scy 2249341618Scy if (dpp_autogen_bootstrap_key(auth) < 0 || 2250341618Scy dpp_prepare_channel_list(auth, own_modes, num_modes) < 0) 2251341618Scy goto fail; 2252341618Scy 2253341618Scy#ifdef CONFIG_TESTING_OPTIONS 2254341618Scy if (dpp_nonce_override_len > 0) { 2255341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce"); 2256341618Scy nonce_len = dpp_nonce_override_len; 2257341618Scy os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len); 2258341618Scy } else { 2259341618Scy nonce_len = auth->curve->nonce_len; 2260341618Scy if (random_get_bytes(auth->i_nonce, nonce_len)) { 2261341618Scy wpa_printf(MSG_ERROR, 2262341618Scy "DPP: Failed to generate I-nonce"); 2263341618Scy goto fail; 2264341618Scy } 2265341618Scy } 2266341618Scy#else /* CONFIG_TESTING_OPTIONS */ 2267341618Scy nonce_len = auth->curve->nonce_len; 2268341618Scy if (random_get_bytes(auth->i_nonce, nonce_len)) { 2269341618Scy wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce"); 2270341618Scy goto fail; 2271341618Scy } 2272341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2273341618Scy wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len); 2274341618Scy 2275341618Scy#ifdef CONFIG_TESTING_OPTIONS 2276341618Scy if (dpp_protocol_key_override_len) { 2277341618Scy const struct dpp_curve_params *tmp_curve; 2278341618Scy 2279341618Scy wpa_printf(MSG_INFO, 2280341618Scy "DPP: TESTING - override protocol key"); 2281341618Scy auth->own_protocol_key = dpp_set_keypair( 2282341618Scy &tmp_curve, dpp_protocol_key_override, 2283341618Scy dpp_protocol_key_override_len); 2284341618Scy } else { 2285341618Scy auth->own_protocol_key = dpp_gen_keypair(auth->curve); 2286341618Scy } 2287341618Scy#else /* CONFIG_TESTING_OPTIONS */ 2288341618Scy auth->own_protocol_key = dpp_gen_keypair(auth->curve); 2289341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2290341618Scy if (!auth->own_protocol_key) 2291341618Scy goto fail; 2292341618Scy 2293341618Scy pi = dpp_get_pubkey_point(auth->own_protocol_key, 0); 2294341618Scy if (!pi) 2295341618Scy goto fail; 2296341618Scy 2297341618Scy /* ECDH: M = pI * BR */ 2298351611Scy if (dpp_ecdh(auth->own_protocol_key, auth->peer_bi->pubkey, 2299351611Scy auth->Mx, &secret_len) < 0) 2300341618Scy goto fail; 2301341618Scy auth->secret_len = secret_len; 2302341618Scy 2303341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", 2304341618Scy auth->Mx, auth->secret_len); 2305341618Scy auth->Mx_len = auth->secret_len; 2306341618Scy 2307341618Scy if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, 2308341618Scy auth->curve->hash_len) < 0) 2309341618Scy goto fail; 2310341618Scy 2311341618Scy r_pubkey_hash = auth->peer_bi->pubkey_hash; 2312341618Scy i_pubkey_hash = auth->own_bi->pubkey_hash; 2313341618Scy 2314341618Scy#ifdef CONFIG_TESTING_OPTIONS 2315341618Scy if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) { 2316341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); 2317341618Scy r_pubkey_hash = NULL; 2318341618Scy } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) { 2319341618Scy wpa_printf(MSG_INFO, 2320341618Scy "DPP: TESTING - invalid R-Bootstrap Key Hash"); 2321341618Scy os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); 2322341618Scy test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 2323341618Scy r_pubkey_hash = test_hash; 2324341618Scy } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) { 2325341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); 2326341618Scy i_pubkey_hash = NULL; 2327341618Scy } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) { 2328341618Scy wpa_printf(MSG_INFO, 2329341618Scy "DPP: TESTING - invalid I-Bootstrap Key Hash"); 2330341618Scy os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); 2331341618Scy test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 2332341618Scy i_pubkey_hash = test_hash; 2333341618Scy } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) { 2334341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key"); 2335341618Scy wpabuf_free(pi); 2336341618Scy pi = NULL; 2337341618Scy } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) { 2338341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key"); 2339341618Scy wpabuf_free(pi); 2340341618Scy pi = wpabuf_alloc(2 * auth->curve->prime_len); 2341341618Scy if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0) 2342341618Scy goto fail; 2343341618Scy } 2344341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2345341618Scy 2346341618Scy auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash, 2347341618Scy i_pubkey_hash, neg_freq); 2348341618Scy if (!auth->req_msg) 2349341618Scy goto fail; 2350341618Scy 2351341618Scyout: 2352341618Scy wpabuf_free(pi); 2353341618Scy return auth; 2354341618Scyfail: 2355341618Scy dpp_auth_deinit(auth); 2356341618Scy auth = NULL; 2357341618Scy goto out; 2358341618Scy} 2359341618Scy 2360341618Scy 2361346981Scystatic struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth, 2362346981Scy const char *json) 2363341618Scy{ 2364341618Scy size_t nonce_len; 2365341618Scy size_t json_len, clear_len; 2366341618Scy struct wpabuf *clear = NULL, *msg = NULL; 2367341618Scy u8 *wrapped; 2368341618Scy size_t attr_len; 2369341618Scy 2370341618Scy wpa_printf(MSG_DEBUG, "DPP: Build configuration request"); 2371341618Scy 2372341618Scy nonce_len = auth->curve->nonce_len; 2373341618Scy if (random_get_bytes(auth->e_nonce, nonce_len)) { 2374341618Scy wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce"); 2375341618Scy goto fail; 2376341618Scy } 2377341618Scy wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", auth->e_nonce, nonce_len); 2378341618Scy json_len = os_strlen(json); 2379341618Scy wpa_hexdump_ascii(MSG_DEBUG, "DPP: configAttr JSON", json, json_len); 2380341618Scy 2381341618Scy /* { E-nonce, configAttrib }ke */ 2382341618Scy clear_len = 4 + nonce_len + 4 + json_len; 2383341618Scy clear = wpabuf_alloc(clear_len); 2384341618Scy attr_len = 4 + clear_len + AES_BLOCK_SIZE; 2385341618Scy#ifdef CONFIG_TESTING_OPTIONS 2386341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) 2387341618Scy attr_len += 5; 2388341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2389341618Scy msg = wpabuf_alloc(attr_len); 2390341618Scy if (!clear || !msg) 2391341618Scy goto fail; 2392341618Scy 2393341618Scy#ifdef CONFIG_TESTING_OPTIONS 2394341618Scy if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_REQ) { 2395341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); 2396341618Scy goto skip_e_nonce; 2397341618Scy } 2398341618Scy if (dpp_test == DPP_TEST_INVALID_E_NONCE_CONF_REQ) { 2399341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid E-nonce"); 2400341618Scy wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 2401341618Scy wpabuf_put_le16(clear, nonce_len - 1); 2402341618Scy wpabuf_put_data(clear, auth->e_nonce, nonce_len - 1); 2403341618Scy goto skip_e_nonce; 2404341618Scy } 2405341618Scy if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_REQ) { 2406341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); 2407341618Scy goto skip_wrapped_data; 2408341618Scy } 2409341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2410341618Scy 2411341618Scy /* E-nonce */ 2412341618Scy wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 2413341618Scy wpabuf_put_le16(clear, nonce_len); 2414341618Scy wpabuf_put_data(clear, auth->e_nonce, nonce_len); 2415341618Scy 2416341618Scy#ifdef CONFIG_TESTING_OPTIONS 2417341618Scyskip_e_nonce: 2418341618Scy if (dpp_test == DPP_TEST_NO_CONFIG_ATTR_OBJ_CONF_REQ) { 2419341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no configAttrib"); 2420341618Scy goto skip_conf_attr_obj; 2421341618Scy } 2422341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2423341618Scy 2424341618Scy /* configAttrib */ 2425341618Scy wpabuf_put_le16(clear, DPP_ATTR_CONFIG_ATTR_OBJ); 2426341618Scy wpabuf_put_le16(clear, json_len); 2427341618Scy wpabuf_put_data(clear, json, json_len); 2428341618Scy 2429341618Scy#ifdef CONFIG_TESTING_OPTIONS 2430341618Scyskip_conf_attr_obj: 2431341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2432341618Scy 2433341618Scy wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 2434341618Scy wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 2435341618Scy wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 2436341618Scy 2437341618Scy /* No AES-SIV AD */ 2438341618Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); 2439341618Scy if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, 2440341618Scy wpabuf_head(clear), wpabuf_len(clear), 2441341618Scy 0, NULL, NULL, wrapped) < 0) 2442341618Scy goto fail; 2443341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 2444341618Scy wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); 2445341618Scy 2446341618Scy#ifdef CONFIG_TESTING_OPTIONS 2447341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_REQ) { 2448341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); 2449341618Scy dpp_build_attr_status(msg, DPP_STATUS_OK); 2450341618Scy } 2451341618Scyskip_wrapped_data: 2452341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2453341618Scy 2454341618Scy wpa_hexdump_buf(MSG_DEBUG, 2455341618Scy "DPP: Configuration Request frame attributes", msg); 2456341618Scy wpabuf_free(clear); 2457341618Scy return msg; 2458341618Scy 2459341618Scyfail: 2460341618Scy wpabuf_free(clear); 2461341618Scy wpabuf_free(msg); 2462341618Scy return NULL; 2463341618Scy} 2464341618Scy 2465341618Scy 2466346981Scystatic void dpp_write_adv_proto(struct wpabuf *buf) 2467346981Scy{ 2468346981Scy /* Advertisement Protocol IE */ 2469346981Scy wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO); 2470346981Scy wpabuf_put_u8(buf, 8); /* Length */ 2471346981Scy wpabuf_put_u8(buf, 0x7f); 2472346981Scy wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); 2473346981Scy wpabuf_put_u8(buf, 5); 2474346981Scy wpabuf_put_be24(buf, OUI_WFA); 2475346981Scy wpabuf_put_u8(buf, DPP_OUI_TYPE); 2476346981Scy wpabuf_put_u8(buf, 0x01); 2477346981Scy} 2478346981Scy 2479346981Scy 2480346981Scystatic void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query) 2481346981Scy{ 2482346981Scy /* GAS Query */ 2483346981Scy wpabuf_put_le16(buf, wpabuf_len(query)); 2484346981Scy wpabuf_put_buf(buf, query); 2485346981Scy} 2486346981Scy 2487346981Scy 2488346981Scystruct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth, 2489346981Scy const char *json) 2490346981Scy{ 2491346981Scy struct wpabuf *buf, *conf_req; 2492346981Scy 2493346981Scy conf_req = dpp_build_conf_req_attr(auth, json); 2494346981Scy if (!conf_req) { 2495346981Scy wpa_printf(MSG_DEBUG, 2496346981Scy "DPP: No configuration request data available"); 2497346981Scy return NULL; 2498346981Scy } 2499346981Scy 2500346981Scy buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req)); 2501346981Scy if (!buf) { 2502346981Scy wpabuf_free(conf_req); 2503346981Scy return NULL; 2504346981Scy } 2505346981Scy 2506346981Scy dpp_write_adv_proto(buf); 2507346981Scy dpp_write_gas_query(buf, conf_req); 2508346981Scy wpabuf_free(conf_req); 2509346981Scy wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf); 2510346981Scy 2511346981Scy return buf; 2512346981Scy} 2513346981Scy 2514346981Scy 2515341618Scystatic void dpp_auth_success(struct dpp_authentication *auth) 2516341618Scy{ 2517341618Scy wpa_printf(MSG_DEBUG, 2518341618Scy "DPP: Authentication success - clear temporary keys"); 2519341618Scy os_memset(auth->Mx, 0, sizeof(auth->Mx)); 2520341618Scy auth->Mx_len = 0; 2521341618Scy os_memset(auth->Nx, 0, sizeof(auth->Nx)); 2522341618Scy auth->Nx_len = 0; 2523341618Scy os_memset(auth->Lx, 0, sizeof(auth->Lx)); 2524341618Scy auth->Lx_len = 0; 2525341618Scy os_memset(auth->k1, 0, sizeof(auth->k1)); 2526341618Scy os_memset(auth->k2, 0, sizeof(auth->k2)); 2527341618Scy 2528341618Scy auth->auth_success = 1; 2529341618Scy} 2530341618Scy 2531341618Scy 2532341618Scystatic int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth) 2533341618Scy{ 2534341618Scy struct wpabuf *pix, *prx, *bix, *brx; 2535341618Scy const u8 *addr[7]; 2536341618Scy size_t len[7]; 2537341618Scy size_t i, num_elem = 0; 2538341618Scy size_t nonce_len; 2539341618Scy u8 zero = 0; 2540341618Scy int res = -1; 2541341618Scy 2542341618Scy /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ 2543341618Scy nonce_len = auth->curve->nonce_len; 2544341618Scy 2545341618Scy if (auth->initiator) { 2546341618Scy pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); 2547341618Scy prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); 2548341618Scy if (auth->own_bi) 2549341618Scy bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); 2550341618Scy else 2551341618Scy bix = NULL; 2552341618Scy brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); 2553341618Scy } else { 2554341618Scy pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); 2555341618Scy prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); 2556341618Scy if (auth->peer_bi) 2557341618Scy bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); 2558341618Scy else 2559341618Scy bix = NULL; 2560341618Scy brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); 2561341618Scy } 2562341618Scy if (!pix || !prx || !brx) 2563341618Scy goto fail; 2564341618Scy 2565341618Scy addr[num_elem] = auth->i_nonce; 2566341618Scy len[num_elem] = nonce_len; 2567341618Scy num_elem++; 2568341618Scy 2569341618Scy addr[num_elem] = auth->r_nonce; 2570341618Scy len[num_elem] = nonce_len; 2571341618Scy num_elem++; 2572341618Scy 2573341618Scy addr[num_elem] = wpabuf_head(pix); 2574341618Scy len[num_elem] = wpabuf_len(pix) / 2; 2575341618Scy num_elem++; 2576341618Scy 2577341618Scy addr[num_elem] = wpabuf_head(prx); 2578341618Scy len[num_elem] = wpabuf_len(prx) / 2; 2579341618Scy num_elem++; 2580341618Scy 2581341618Scy if (bix) { 2582341618Scy addr[num_elem] = wpabuf_head(bix); 2583341618Scy len[num_elem] = wpabuf_len(bix) / 2; 2584341618Scy num_elem++; 2585341618Scy } 2586341618Scy 2587341618Scy addr[num_elem] = wpabuf_head(brx); 2588341618Scy len[num_elem] = wpabuf_len(brx) / 2; 2589341618Scy num_elem++; 2590341618Scy 2591341618Scy addr[num_elem] = &zero; 2592341618Scy len[num_elem] = 1; 2593341618Scy num_elem++; 2594341618Scy 2595341618Scy wpa_printf(MSG_DEBUG, "DPP: R-auth hash components"); 2596341618Scy for (i = 0; i < num_elem; i++) 2597341618Scy wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); 2598341618Scy res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth); 2599341618Scy if (res == 0) 2600341618Scy wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth, 2601341618Scy auth->curve->hash_len); 2602341618Scyfail: 2603341618Scy wpabuf_free(pix); 2604341618Scy wpabuf_free(prx); 2605341618Scy wpabuf_free(bix); 2606341618Scy wpabuf_free(brx); 2607341618Scy return res; 2608341618Scy} 2609341618Scy 2610341618Scy 2611341618Scystatic int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth) 2612341618Scy{ 2613341618Scy struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL; 2614341618Scy const u8 *addr[7]; 2615341618Scy size_t len[7]; 2616341618Scy size_t i, num_elem = 0; 2617341618Scy size_t nonce_len; 2618341618Scy u8 one = 1; 2619341618Scy int res = -1; 2620341618Scy 2621341618Scy /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ 2622341618Scy nonce_len = auth->curve->nonce_len; 2623341618Scy 2624341618Scy if (auth->initiator) { 2625341618Scy pix = dpp_get_pubkey_point(auth->own_protocol_key, 0); 2626341618Scy prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0); 2627341618Scy if (auth->own_bi) 2628341618Scy bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); 2629341618Scy else 2630341618Scy bix = NULL; 2631341618Scy if (!auth->peer_bi) 2632341618Scy goto fail; 2633341618Scy brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); 2634341618Scy } else { 2635341618Scy pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0); 2636341618Scy prx = dpp_get_pubkey_point(auth->own_protocol_key, 0); 2637341618Scy if (auth->peer_bi) 2638341618Scy bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0); 2639341618Scy else 2640341618Scy bix = NULL; 2641341618Scy if (!auth->own_bi) 2642341618Scy goto fail; 2643341618Scy brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0); 2644341618Scy } 2645341618Scy if (!pix || !prx || !brx) 2646341618Scy goto fail; 2647341618Scy 2648341618Scy addr[num_elem] = auth->r_nonce; 2649341618Scy len[num_elem] = nonce_len; 2650341618Scy num_elem++; 2651341618Scy 2652341618Scy addr[num_elem] = auth->i_nonce; 2653341618Scy len[num_elem] = nonce_len; 2654341618Scy num_elem++; 2655341618Scy 2656341618Scy addr[num_elem] = wpabuf_head(prx); 2657341618Scy len[num_elem] = wpabuf_len(prx) / 2; 2658341618Scy num_elem++; 2659341618Scy 2660341618Scy addr[num_elem] = wpabuf_head(pix); 2661341618Scy len[num_elem] = wpabuf_len(pix) / 2; 2662341618Scy num_elem++; 2663341618Scy 2664341618Scy addr[num_elem] = wpabuf_head(brx); 2665341618Scy len[num_elem] = wpabuf_len(brx) / 2; 2666341618Scy num_elem++; 2667341618Scy 2668341618Scy if (bix) { 2669341618Scy addr[num_elem] = wpabuf_head(bix); 2670341618Scy len[num_elem] = wpabuf_len(bix) / 2; 2671341618Scy num_elem++; 2672341618Scy } 2673341618Scy 2674341618Scy addr[num_elem] = &one; 2675341618Scy len[num_elem] = 1; 2676341618Scy num_elem++; 2677341618Scy 2678341618Scy wpa_printf(MSG_DEBUG, "DPP: I-auth hash components"); 2679341618Scy for (i = 0; i < num_elem; i++) 2680341618Scy wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]); 2681341618Scy res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth); 2682341618Scy if (res == 0) 2683341618Scy wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth, 2684341618Scy auth->curve->hash_len); 2685341618Scyfail: 2686341618Scy wpabuf_free(pix); 2687341618Scy wpabuf_free(prx); 2688341618Scy wpabuf_free(bix); 2689341618Scy wpabuf_free(brx); 2690341618Scy return res; 2691341618Scy} 2692341618Scy 2693341618Scy 2694341618Scystatic int dpp_auth_derive_l_responder(struct dpp_authentication *auth) 2695341618Scy{ 2696341618Scy const EC_GROUP *group; 2697341618Scy EC_POINT *l = NULL; 2698341618Scy EC_KEY *BI = NULL, *bR = NULL, *pR = NULL; 2699341618Scy const EC_POINT *BI_point; 2700341618Scy BN_CTX *bnctx; 2701341618Scy BIGNUM *lx, *sum, *q; 2702341618Scy const BIGNUM *bR_bn, *pR_bn; 2703341618Scy int ret = -1; 2704341618Scy 2705341618Scy /* L = ((bR + pR) modulo q) * BI */ 2706341618Scy 2707341618Scy bnctx = BN_CTX_new(); 2708341618Scy sum = BN_new(); 2709341618Scy q = BN_new(); 2710341618Scy lx = BN_new(); 2711341618Scy if (!bnctx || !sum || !q || !lx) 2712341618Scy goto fail; 2713341618Scy BI = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); 2714341618Scy if (!BI) 2715341618Scy goto fail; 2716341618Scy BI_point = EC_KEY_get0_public_key(BI); 2717341618Scy group = EC_KEY_get0_group(BI); 2718341618Scy if (!group) 2719341618Scy goto fail; 2720341618Scy 2721341618Scy bR = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); 2722341618Scy pR = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key); 2723341618Scy if (!bR || !pR) 2724341618Scy goto fail; 2725341618Scy bR_bn = EC_KEY_get0_private_key(bR); 2726341618Scy pR_bn = EC_KEY_get0_private_key(pR); 2727341618Scy if (!bR_bn || !pR_bn) 2728341618Scy goto fail; 2729341618Scy if (EC_GROUP_get_order(group, q, bnctx) != 1 || 2730341618Scy BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1) 2731341618Scy goto fail; 2732341618Scy l = EC_POINT_new(group); 2733341618Scy if (!l || 2734341618Scy EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 || 2735341618Scy EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, 2736341618Scy bnctx) != 1) { 2737341618Scy wpa_printf(MSG_ERROR, 2738341618Scy "OpenSSL: failed: %s", 2739341618Scy ERR_error_string(ERR_get_error(), NULL)); 2740341618Scy goto fail; 2741341618Scy } 2742341618Scy 2743341618Scy if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0) 2744341618Scy goto fail; 2745341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); 2746341618Scy auth->Lx_len = auth->secret_len; 2747341618Scy ret = 0; 2748341618Scyfail: 2749341618Scy EC_POINT_clear_free(l); 2750341618Scy EC_KEY_free(BI); 2751341618Scy EC_KEY_free(bR); 2752341618Scy EC_KEY_free(pR); 2753341618Scy BN_clear_free(lx); 2754341618Scy BN_clear_free(sum); 2755341618Scy BN_free(q); 2756341618Scy BN_CTX_free(bnctx); 2757341618Scy return ret; 2758341618Scy} 2759341618Scy 2760341618Scy 2761341618Scystatic int dpp_auth_derive_l_initiator(struct dpp_authentication *auth) 2762341618Scy{ 2763341618Scy const EC_GROUP *group; 2764341618Scy EC_POINT *l = NULL, *sum = NULL; 2765341618Scy EC_KEY *bI = NULL, *BR = NULL, *PR = NULL; 2766341618Scy const EC_POINT *BR_point, *PR_point; 2767341618Scy BN_CTX *bnctx; 2768341618Scy BIGNUM *lx; 2769341618Scy const BIGNUM *bI_bn; 2770341618Scy int ret = -1; 2771341618Scy 2772341618Scy /* L = bI * (BR + PR) */ 2773341618Scy 2774341618Scy bnctx = BN_CTX_new(); 2775341618Scy lx = BN_new(); 2776341618Scy if (!bnctx || !lx) 2777341618Scy goto fail; 2778341618Scy BR = EVP_PKEY_get1_EC_KEY(auth->peer_bi->pubkey); 2779341618Scy PR = EVP_PKEY_get1_EC_KEY(auth->peer_protocol_key); 2780341618Scy if (!BR || !PR) 2781341618Scy goto fail; 2782341618Scy BR_point = EC_KEY_get0_public_key(BR); 2783341618Scy PR_point = EC_KEY_get0_public_key(PR); 2784341618Scy 2785341618Scy bI = EVP_PKEY_get1_EC_KEY(auth->own_bi->pubkey); 2786341618Scy if (!bI) 2787341618Scy goto fail; 2788341618Scy group = EC_KEY_get0_group(bI); 2789341618Scy bI_bn = EC_KEY_get0_private_key(bI); 2790341618Scy if (!group || !bI_bn) 2791341618Scy goto fail; 2792341618Scy sum = EC_POINT_new(group); 2793341618Scy l = EC_POINT_new(group); 2794341618Scy if (!sum || !l || 2795341618Scy EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 || 2796341618Scy EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 || 2797341618Scy EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL, 2798341618Scy bnctx) != 1) { 2799341618Scy wpa_printf(MSG_ERROR, 2800341618Scy "OpenSSL: failed: %s", 2801341618Scy ERR_error_string(ERR_get_error(), NULL)); 2802341618Scy goto fail; 2803341618Scy } 2804341618Scy 2805341618Scy if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0) 2806341618Scy goto fail; 2807341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len); 2808341618Scy auth->Lx_len = auth->secret_len; 2809341618Scy ret = 0; 2810341618Scyfail: 2811341618Scy EC_POINT_clear_free(l); 2812341618Scy EC_POINT_clear_free(sum); 2813341618Scy EC_KEY_free(bI); 2814341618Scy EC_KEY_free(BR); 2815341618Scy EC_KEY_free(PR); 2816341618Scy BN_clear_free(lx); 2817341618Scy BN_CTX_free(bnctx); 2818341618Scy return ret; 2819341618Scy} 2820341618Scy 2821341618Scy 2822341618Scystatic int dpp_auth_build_resp_ok(struct dpp_authentication *auth) 2823341618Scy{ 2824341618Scy size_t nonce_len; 2825341618Scy size_t secret_len; 2826341618Scy struct wpabuf *msg, *pr = NULL; 2827341618Scy u8 r_auth[4 + DPP_MAX_HASH_LEN]; 2828341618Scy u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth; 2829341618Scy size_t wrapped_r_auth_len; 2830341618Scy int ret = -1; 2831341618Scy const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce; 2832341618Scy enum dpp_status_error status = DPP_STATUS_OK; 2833341618Scy#ifdef CONFIG_TESTING_OPTIONS 2834341618Scy u8 test_hash[SHA256_MAC_LEN]; 2835341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2836341618Scy 2837341618Scy wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); 2838341618Scy if (!auth->own_bi) 2839341618Scy return -1; 2840341618Scy 2841341618Scy#ifdef CONFIG_TESTING_OPTIONS 2842341618Scy if (dpp_nonce_override_len > 0) { 2843341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce"); 2844341618Scy nonce_len = dpp_nonce_override_len; 2845341618Scy os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len); 2846341618Scy } else { 2847341618Scy nonce_len = auth->curve->nonce_len; 2848341618Scy if (random_get_bytes(auth->r_nonce, nonce_len)) { 2849341618Scy wpa_printf(MSG_ERROR, 2850341618Scy "DPP: Failed to generate R-nonce"); 2851341618Scy goto fail; 2852341618Scy } 2853341618Scy } 2854341618Scy#else /* CONFIG_TESTING_OPTIONS */ 2855341618Scy nonce_len = auth->curve->nonce_len; 2856341618Scy if (random_get_bytes(auth->r_nonce, nonce_len)) { 2857341618Scy wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce"); 2858341618Scy goto fail; 2859341618Scy } 2860341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2861341618Scy wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len); 2862341618Scy 2863351611Scy EVP_PKEY_free(auth->own_protocol_key); 2864341618Scy#ifdef CONFIG_TESTING_OPTIONS 2865341618Scy if (dpp_protocol_key_override_len) { 2866341618Scy const struct dpp_curve_params *tmp_curve; 2867341618Scy 2868341618Scy wpa_printf(MSG_INFO, 2869341618Scy "DPP: TESTING - override protocol key"); 2870341618Scy auth->own_protocol_key = dpp_set_keypair( 2871341618Scy &tmp_curve, dpp_protocol_key_override, 2872341618Scy dpp_protocol_key_override_len); 2873341618Scy } else { 2874341618Scy auth->own_protocol_key = dpp_gen_keypair(auth->curve); 2875341618Scy } 2876341618Scy#else /* CONFIG_TESTING_OPTIONS */ 2877341618Scy auth->own_protocol_key = dpp_gen_keypair(auth->curve); 2878341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2879341618Scy if (!auth->own_protocol_key) 2880341618Scy goto fail; 2881341618Scy 2882341618Scy pr = dpp_get_pubkey_point(auth->own_protocol_key, 0); 2883341618Scy if (!pr) 2884341618Scy goto fail; 2885341618Scy 2886341618Scy /* ECDH: N = pR * PI */ 2887351611Scy if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key, 2888351611Scy auth->Nx, &secret_len) < 0) 2889341618Scy goto fail; 2890341618Scy 2891341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", 2892341618Scy auth->Nx, auth->secret_len); 2893341618Scy auth->Nx_len = auth->secret_len; 2894341618Scy 2895341618Scy if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, 2896341618Scy auth->curve->hash_len) < 0) 2897341618Scy goto fail; 2898341618Scy 2899341618Scy if (auth->own_bi && auth->peer_bi) { 2900341618Scy /* Mutual authentication */ 2901341618Scy if (dpp_auth_derive_l_responder(auth) < 0) 2902341618Scy goto fail; 2903341618Scy } 2904341618Scy 2905341618Scy if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) 2906341618Scy goto fail; 2907341618Scy 2908341618Scy /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ 2909341618Scy WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG); 2910341618Scy WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len); 2911341618Scy if (dpp_gen_r_auth(auth, r_auth + 4) < 0) 2912341618Scy goto fail; 2913341618Scy#ifdef CONFIG_TESTING_OPTIONS 2914341618Scy if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) { 2915341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch"); 2916341618Scy r_auth[4 + auth->curve->hash_len / 2] ^= 0x01; 2917341618Scy } 2918341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2919341618Scy if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, 2920341618Scy r_auth, 4 + auth->curve->hash_len, 2921341618Scy 0, NULL, NULL, wrapped_r_auth) < 0) 2922341618Scy goto fail; 2923341618Scy wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE; 2924341618Scy wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke", 2925341618Scy wrapped_r_auth, wrapped_r_auth_len); 2926341618Scy w_r_auth = wrapped_r_auth; 2927341618Scy 2928341618Scy r_pubkey_hash = auth->own_bi->pubkey_hash; 2929341618Scy if (auth->peer_bi) 2930341618Scy i_pubkey_hash = auth->peer_bi->pubkey_hash; 2931341618Scy else 2932341618Scy i_pubkey_hash = NULL; 2933341618Scy 2934341618Scy i_nonce = auth->i_nonce; 2935341618Scy r_nonce = auth->r_nonce; 2936341618Scy 2937341618Scy#ifdef CONFIG_TESTING_OPTIONS 2938341618Scy if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { 2939341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); 2940341618Scy r_pubkey_hash = NULL; 2941341618Scy } else if (dpp_test == 2942341618Scy DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { 2943341618Scy wpa_printf(MSG_INFO, 2944341618Scy "DPP: TESTING - invalid R-Bootstrap Key Hash"); 2945341618Scy os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); 2946341618Scy test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 2947341618Scy r_pubkey_hash = test_hash; 2948341618Scy } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { 2949341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); 2950341618Scy i_pubkey_hash = NULL; 2951341618Scy } else if (dpp_test == 2952341618Scy DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { 2953341618Scy wpa_printf(MSG_INFO, 2954341618Scy "DPP: TESTING - invalid I-Bootstrap Key Hash"); 2955341618Scy if (i_pubkey_hash) 2956341618Scy os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); 2957341618Scy else 2958341618Scy os_memset(test_hash, 0, SHA256_MAC_LEN); 2959341618Scy test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 2960341618Scy i_pubkey_hash = test_hash; 2961341618Scy } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) { 2962341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key"); 2963341618Scy wpabuf_free(pr); 2964341618Scy pr = NULL; 2965341618Scy } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) { 2966341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key"); 2967341618Scy wpabuf_free(pr); 2968341618Scy pr = wpabuf_alloc(2 * auth->curve->prime_len); 2969341618Scy if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0) 2970341618Scy goto fail; 2971341618Scy } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) { 2972341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth"); 2973341618Scy w_r_auth = NULL; 2974341618Scy wrapped_r_auth_len = 0; 2975341618Scy } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) { 2976341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); 2977341618Scy status = 255; 2978341618Scy } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) { 2979341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); 2980341618Scy status = 254; 2981341618Scy } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) { 2982341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce"); 2983341618Scy r_nonce = NULL; 2984341618Scy } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) { 2985341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); 2986341618Scy i_nonce = NULL; 2987341618Scy } 2988341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 2989341618Scy 2990341618Scy msg = dpp_auth_build_resp(auth, status, pr, nonce_len, 2991341618Scy r_pubkey_hash, i_pubkey_hash, 2992341618Scy r_nonce, i_nonce, 2993341618Scy w_r_auth, wrapped_r_auth_len, 2994341618Scy auth->k2); 2995341618Scy if (!msg) 2996341618Scy goto fail; 2997341618Scy wpabuf_free(auth->resp_msg); 2998341618Scy auth->resp_msg = msg; 2999341618Scy ret = 0; 3000341618Scyfail: 3001341618Scy wpabuf_free(pr); 3002341618Scy return ret; 3003341618Scy} 3004341618Scy 3005341618Scy 3006341618Scystatic int dpp_auth_build_resp_status(struct dpp_authentication *auth, 3007341618Scy enum dpp_status_error status) 3008341618Scy{ 3009341618Scy struct wpabuf *msg; 3010341618Scy const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce; 3011341618Scy#ifdef CONFIG_TESTING_OPTIONS 3012341618Scy u8 test_hash[SHA256_MAC_LEN]; 3013341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3014341618Scy 3015341618Scy if (!auth->own_bi) 3016341618Scy return -1; 3017341618Scy wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response"); 3018341618Scy 3019341618Scy r_pubkey_hash = auth->own_bi->pubkey_hash; 3020341618Scy if (auth->peer_bi) 3021341618Scy i_pubkey_hash = auth->peer_bi->pubkey_hash; 3022341618Scy else 3023341618Scy i_pubkey_hash = NULL; 3024341618Scy 3025341618Scy i_nonce = auth->i_nonce; 3026341618Scy 3027341618Scy#ifdef CONFIG_TESTING_OPTIONS 3028341618Scy if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { 3029341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); 3030341618Scy r_pubkey_hash = NULL; 3031341618Scy } else if (dpp_test == 3032341618Scy DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) { 3033341618Scy wpa_printf(MSG_INFO, 3034341618Scy "DPP: TESTING - invalid R-Bootstrap Key Hash"); 3035341618Scy os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); 3036341618Scy test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 3037341618Scy r_pubkey_hash = test_hash; 3038341618Scy } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { 3039341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); 3040341618Scy i_pubkey_hash = NULL; 3041341618Scy } else if (dpp_test == 3042341618Scy DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) { 3043341618Scy wpa_printf(MSG_INFO, 3044341618Scy "DPP: TESTING - invalid I-Bootstrap Key Hash"); 3045341618Scy if (i_pubkey_hash) 3046341618Scy os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); 3047341618Scy else 3048341618Scy os_memset(test_hash, 0, SHA256_MAC_LEN); 3049341618Scy test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 3050341618Scy i_pubkey_hash = test_hash; 3051341618Scy } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) { 3052341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); 3053341618Scy status = 255; 3054341618Scy } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) { 3055341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce"); 3056341618Scy i_nonce = NULL; 3057341618Scy } 3058341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3059341618Scy 3060341618Scy msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len, 3061341618Scy r_pubkey_hash, i_pubkey_hash, 3062341618Scy NULL, i_nonce, NULL, 0, auth->k1); 3063341618Scy if (!msg) 3064341618Scy return -1; 3065341618Scy wpabuf_free(auth->resp_msg); 3066341618Scy auth->resp_msg = msg; 3067341618Scy return 0; 3068341618Scy} 3069341618Scy 3070341618Scy 3071341618Scystruct dpp_authentication * 3072341618Scydpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual, 3073341618Scy struct dpp_bootstrap_info *peer_bi, 3074341618Scy struct dpp_bootstrap_info *own_bi, 3075341618Scy unsigned int freq, const u8 *hdr, const u8 *attr_start, 3076341618Scy size_t attr_len) 3077341618Scy{ 3078341618Scy EVP_PKEY *pi = NULL; 3079341618Scy EVP_PKEY_CTX *ctx = NULL; 3080341618Scy size_t secret_len; 3081341618Scy const u8 *addr[2]; 3082341618Scy size_t len[2]; 3083341618Scy u8 *unwrapped = NULL; 3084341618Scy size_t unwrapped_len = 0; 3085341618Scy const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap, 3086341618Scy *channel; 3087341618Scy u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len, 3088341618Scy i_bootstrap_len, channel_len; 3089341618Scy struct dpp_authentication *auth = NULL; 3090346981Scy#ifdef CONFIG_DPP2 3091346981Scy const u8 *version; 3092346981Scy u16 version_len; 3093346981Scy#endif /* CONFIG_DPP2 */ 3094341618Scy 3095341618Scy#ifdef CONFIG_TESTING_OPTIONS 3096341618Scy if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) { 3097341618Scy wpa_printf(MSG_INFO, 3098341618Scy "DPP: TESTING - stop at Authentication Request"); 3099341618Scy return NULL; 3100341618Scy } 3101341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3102341618Scy 3103341618Scy wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, 3104341618Scy &wrapped_data_len); 3105341618Scy if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 3106341618Scy wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL 3107341618Scy "Missing or invalid required Wrapped Data attribute"); 3108341618Scy return NULL; 3109341618Scy } 3110341618Scy wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data", 3111341618Scy wrapped_data, wrapped_data_len); 3112341618Scy attr_len = wrapped_data - 4 - attr_start; 3113341618Scy 3114341618Scy auth = os_zalloc(sizeof(*auth)); 3115341618Scy if (!auth) 3116341618Scy goto fail; 3117341618Scy auth->msg_ctx = msg_ctx; 3118341618Scy auth->peer_bi = peer_bi; 3119341618Scy auth->own_bi = own_bi; 3120341618Scy auth->curve = own_bi->curve; 3121341618Scy auth->curr_freq = freq; 3122341618Scy 3123346981Scy auth->peer_version = 1; /* default to the first version */ 3124346981Scy#ifdef CONFIG_DPP2 3125346981Scy version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, 3126346981Scy &version_len); 3127346981Scy if (version) { 3128346981Scy if (version_len < 1 || version[0] == 0) { 3129346981Scy dpp_auth_fail(auth, 3130346981Scy "Invalid Protocol Version attribute"); 3131346981Scy goto fail; 3132346981Scy } 3133346981Scy auth->peer_version = version[0]; 3134346981Scy wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", 3135346981Scy auth->peer_version); 3136346981Scy } 3137346981Scy#endif /* CONFIG_DPP2 */ 3138346981Scy 3139341618Scy channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL, 3140341618Scy &channel_len); 3141341618Scy if (channel) { 3142341618Scy int neg_freq; 3143341618Scy 3144341618Scy if (channel_len < 2) { 3145341618Scy dpp_auth_fail(auth, "Too short Channel attribute"); 3146341618Scy goto fail; 3147341618Scy } 3148341618Scy 3149341618Scy neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]); 3150341618Scy wpa_printf(MSG_DEBUG, 3151341618Scy "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d", 3152341618Scy channel[0], channel[1], neg_freq); 3153341618Scy if (neg_freq < 0) { 3154341618Scy dpp_auth_fail(auth, 3155341618Scy "Unsupported Channel attribute value"); 3156341618Scy goto fail; 3157341618Scy } 3158341618Scy 3159341618Scy if (auth->curr_freq != (unsigned int) neg_freq) { 3160341618Scy wpa_printf(MSG_DEBUG, 3161341618Scy "DPP: Changing negotiation channel from %u MHz to %u MHz", 3162341618Scy freq, neg_freq); 3163341618Scy auth->curr_freq = neg_freq; 3164341618Scy } 3165341618Scy } 3166341618Scy 3167341618Scy i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY, 3168341618Scy &i_proto_len); 3169341618Scy if (!i_proto) { 3170341618Scy dpp_auth_fail(auth, 3171341618Scy "Missing required Initiator Protocol Key attribute"); 3172341618Scy goto fail; 3173341618Scy } 3174341618Scy wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key", 3175341618Scy i_proto, i_proto_len); 3176341618Scy 3177341618Scy /* M = bR * PI */ 3178341618Scy pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len); 3179341618Scy if (!pi) { 3180341618Scy dpp_auth_fail(auth, "Invalid Initiator Protocol Key"); 3181341618Scy goto fail; 3182341618Scy } 3183341618Scy dpp_debug_print_key("Peer (Initiator) Protocol Key", pi); 3184341618Scy 3185351611Scy if (dpp_ecdh(own_bi->pubkey, pi, auth->Mx, &secret_len) < 0) 3186341618Scy goto fail; 3187341618Scy auth->secret_len = secret_len; 3188341618Scy 3189341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)", 3190341618Scy auth->Mx, auth->secret_len); 3191341618Scy auth->Mx_len = auth->secret_len; 3192341618Scy 3193341618Scy if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1, 3194341618Scy auth->curve->hash_len) < 0) 3195341618Scy goto fail; 3196341618Scy 3197341618Scy addr[0] = hdr; 3198341618Scy len[0] = DPP_HDR_LEN; 3199341618Scy addr[1] = attr_start; 3200341618Scy len[1] = attr_len; 3201341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 3202341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 3203341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 3204341618Scy wrapped_data, wrapped_data_len); 3205341618Scy unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 3206341618Scy unwrapped = os_malloc(unwrapped_len); 3207341618Scy if (!unwrapped) 3208341618Scy goto fail; 3209341618Scy if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, 3210341618Scy wrapped_data, wrapped_data_len, 3211341618Scy 2, addr, len, unwrapped) < 0) { 3212341618Scy dpp_auth_fail(auth, "AES-SIV decryption failed"); 3213341618Scy goto fail; 3214341618Scy } 3215341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 3216341618Scy unwrapped, unwrapped_len); 3217341618Scy 3218341618Scy if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 3219341618Scy dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 3220341618Scy goto fail; 3221341618Scy } 3222341618Scy 3223341618Scy i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, 3224341618Scy &i_nonce_len); 3225341618Scy if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { 3226341618Scy dpp_auth_fail(auth, "Missing or invalid I-nonce"); 3227341618Scy goto fail; 3228341618Scy } 3229341618Scy wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); 3230341618Scy os_memcpy(auth->i_nonce, i_nonce, i_nonce_len); 3231341618Scy 3232341618Scy i_capab = dpp_get_attr(unwrapped, unwrapped_len, 3233341618Scy DPP_ATTR_I_CAPABILITIES, 3234341618Scy &i_capab_len); 3235341618Scy if (!i_capab || i_capab_len < 1) { 3236341618Scy dpp_auth_fail(auth, "Missing or invalid I-capabilities"); 3237341618Scy goto fail; 3238341618Scy } 3239341618Scy auth->i_capab = i_capab[0]; 3240341618Scy wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab); 3241341618Scy 3242341618Scy bin_clear_free(unwrapped, unwrapped_len); 3243341618Scy unwrapped = NULL; 3244341618Scy 3245341618Scy switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) { 3246341618Scy case DPP_CAPAB_ENROLLEE: 3247341618Scy if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) { 3248341618Scy wpa_printf(MSG_DEBUG, 3249341618Scy "DPP: Local policy does not allow Configurator role"); 3250341618Scy goto not_compatible; 3251341618Scy } 3252341618Scy wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); 3253341618Scy auth->configurator = 1; 3254341618Scy break; 3255341618Scy case DPP_CAPAB_CONFIGURATOR: 3256341618Scy if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) { 3257341618Scy wpa_printf(MSG_DEBUG, 3258341618Scy "DPP: Local policy does not allow Enrollee role"); 3259341618Scy goto not_compatible; 3260341618Scy } 3261341618Scy wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); 3262341618Scy auth->configurator = 0; 3263341618Scy break; 3264341618Scy case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE: 3265341618Scy if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) { 3266341618Scy wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee"); 3267341618Scy auth->configurator = 0; 3268341618Scy } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) { 3269341618Scy wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator"); 3270341618Scy auth->configurator = 1; 3271341618Scy } else { 3272341618Scy wpa_printf(MSG_DEBUG, 3273341618Scy "DPP: Local policy does not allow Configurator/Enrollee role"); 3274341618Scy goto not_compatible; 3275341618Scy } 3276341618Scy break; 3277341618Scy default: 3278341618Scy wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities"); 3279341618Scy wpa_msg(auth->msg_ctx, MSG_INFO, 3280341618Scy DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x", 3281341618Scy auth->i_capab & DPP_CAPAB_ROLE_MASK); 3282341618Scy goto fail; 3283341618Scy } 3284341618Scy 3285341618Scy auth->peer_protocol_key = pi; 3286341618Scy pi = NULL; 3287341618Scy if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) { 3288341618Scy char hex[SHA256_MAC_LEN * 2 + 1]; 3289341618Scy 3290341618Scy wpa_printf(MSG_DEBUG, 3291341618Scy "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time"); 3292341618Scy if (dpp_auth_build_resp_status(auth, 3293341618Scy DPP_STATUS_RESPONSE_PENDING) < 0) 3294341618Scy goto fail; 3295341618Scy i_bootstrap = dpp_get_attr(attr_start, attr_len, 3296341618Scy DPP_ATTR_I_BOOTSTRAP_KEY_HASH, 3297341618Scy &i_bootstrap_len); 3298341618Scy if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) { 3299341618Scy auth->response_pending = 1; 3300341618Scy os_memcpy(auth->waiting_pubkey_hash, 3301341618Scy i_bootstrap, i_bootstrap_len); 3302341618Scy wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap, 3303341618Scy i_bootstrap_len); 3304341618Scy } else { 3305341618Scy hex[0] = '\0'; 3306341618Scy } 3307341618Scy 3308341618Scy wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE 3309341618Scy "%s", hex); 3310341618Scy return auth; 3311341618Scy } 3312341618Scy if (dpp_auth_build_resp_ok(auth) < 0) 3313341618Scy goto fail; 3314341618Scy 3315341618Scy return auth; 3316341618Scy 3317341618Scynot_compatible: 3318341618Scy wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE 3319341618Scy "i-capab=0x%02x", auth->i_capab); 3320341618Scy if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) 3321341618Scy auth->configurator = 1; 3322341618Scy else 3323341618Scy auth->configurator = 0; 3324341618Scy auth->peer_protocol_key = pi; 3325341618Scy pi = NULL; 3326341618Scy if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0) 3327341618Scy goto fail; 3328341618Scy 3329341618Scy auth->remove_on_tx_status = 1; 3330341618Scy return auth; 3331341618Scyfail: 3332341618Scy bin_clear_free(unwrapped, unwrapped_len); 3333341618Scy EVP_PKEY_free(pi); 3334341618Scy EVP_PKEY_CTX_free(ctx); 3335341618Scy dpp_auth_deinit(auth); 3336341618Scy return NULL; 3337341618Scy} 3338341618Scy 3339341618Scy 3340341618Scyint dpp_notify_new_qr_code(struct dpp_authentication *auth, 3341341618Scy struct dpp_bootstrap_info *peer_bi) 3342341618Scy{ 3343341618Scy if (!auth || !auth->response_pending || 3344341618Scy os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash, 3345341618Scy SHA256_MAC_LEN) != 0) 3346341618Scy return 0; 3347341618Scy 3348341618Scy wpa_printf(MSG_DEBUG, 3349341618Scy "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with " 3350341618Scy MACSTR, MAC2STR(auth->peer_mac_addr)); 3351341618Scy auth->peer_bi = peer_bi; 3352341618Scy 3353341618Scy if (dpp_auth_build_resp_ok(auth) < 0) 3354341618Scy return -1; 3355341618Scy 3356341618Scy return 1; 3357341618Scy} 3358341618Scy 3359341618Scy 3360341618Scystatic struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth, 3361341618Scy enum dpp_status_error status) 3362341618Scy{ 3363341618Scy struct wpabuf *msg; 3364341618Scy u8 i_auth[4 + DPP_MAX_HASH_LEN]; 3365341618Scy size_t i_auth_len; 3366341618Scy u8 r_nonce[4 + DPP_MAX_NONCE_LEN]; 3367341618Scy size_t r_nonce_len; 3368341618Scy const u8 *addr[2]; 3369341618Scy size_t len[2], attr_len; 3370341618Scy u8 *wrapped_i_auth; 3371341618Scy u8 *wrapped_r_nonce; 3372341618Scy u8 *attr_start, *attr_end; 3373341618Scy const u8 *r_pubkey_hash, *i_pubkey_hash; 3374341618Scy#ifdef CONFIG_TESTING_OPTIONS 3375341618Scy u8 test_hash[SHA256_MAC_LEN]; 3376341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3377341618Scy 3378341618Scy wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation"); 3379341618Scy 3380341618Scy i_auth_len = 4 + auth->curve->hash_len; 3381341618Scy r_nonce_len = 4 + auth->curve->nonce_len; 3382341618Scy /* Build DPP Authentication Confirmation frame attributes */ 3383341618Scy attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) + 3384341618Scy 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE; 3385341618Scy#ifdef CONFIG_TESTING_OPTIONS 3386341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) 3387341618Scy attr_len += 5; 3388341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3389341618Scy msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len); 3390341618Scy if (!msg) 3391341618Scy goto fail; 3392341618Scy 3393341618Scy attr_start = wpabuf_put(msg, 0); 3394341618Scy 3395341618Scy r_pubkey_hash = auth->peer_bi->pubkey_hash; 3396341618Scy if (auth->own_bi) 3397341618Scy i_pubkey_hash = auth->own_bi->pubkey_hash; 3398341618Scy else 3399341618Scy i_pubkey_hash = NULL; 3400341618Scy 3401341618Scy#ifdef CONFIG_TESTING_OPTIONS 3402341618Scy if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) { 3403341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); 3404341618Scy goto skip_status; 3405341618Scy } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) { 3406341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); 3407341618Scy status = 254; 3408341618Scy } 3409341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3410341618Scy 3411341618Scy /* DPP Status */ 3412341618Scy dpp_build_attr_status(msg, status); 3413341618Scy 3414341618Scy#ifdef CONFIG_TESTING_OPTIONS 3415341618Scyskip_status: 3416341618Scy if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) { 3417341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash"); 3418341618Scy r_pubkey_hash = NULL; 3419341618Scy } else if (dpp_test == 3420341618Scy DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) { 3421341618Scy wpa_printf(MSG_INFO, 3422341618Scy "DPP: TESTING - invalid R-Bootstrap Key Hash"); 3423341618Scy os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN); 3424341618Scy test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 3425341618Scy r_pubkey_hash = test_hash; 3426341618Scy } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) { 3427341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash"); 3428341618Scy i_pubkey_hash = NULL; 3429341618Scy } else if (dpp_test == 3430341618Scy DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) { 3431341618Scy wpa_printf(MSG_INFO, 3432341618Scy "DPP: TESTING - invalid I-Bootstrap Key Hash"); 3433341618Scy if (i_pubkey_hash) 3434341618Scy os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN); 3435341618Scy else 3436341618Scy os_memset(test_hash, 0, SHA256_MAC_LEN); 3437341618Scy test_hash[SHA256_MAC_LEN - 1] ^= 0x01; 3438341618Scy i_pubkey_hash = test_hash; 3439341618Scy } 3440341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3441341618Scy 3442341618Scy /* Responder Bootstrapping Key Hash */ 3443341618Scy dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash); 3444341618Scy 3445341618Scy /* Initiator Bootstrapping Key Hash (mutual authentication) */ 3446341618Scy dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash); 3447341618Scy 3448341618Scy#ifdef CONFIG_TESTING_OPTIONS 3449341618Scy if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF) 3450341618Scy goto skip_wrapped_data; 3451341618Scy if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF) 3452341618Scy i_auth_len = 0; 3453341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3454341618Scy 3455341618Scy attr_end = wpabuf_put(msg, 0); 3456341618Scy 3457341618Scy /* OUI, OUI type, Crypto Suite, DPP frame type */ 3458341618Scy addr[0] = wpabuf_head_u8(msg) + 2; 3459341618Scy len[0] = 3 + 1 + 1 + 1; 3460341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 3461341618Scy 3462341618Scy /* Attributes before Wrapped Data */ 3463341618Scy addr[1] = attr_start; 3464341618Scy len[1] = attr_end - attr_start; 3465341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 3466341618Scy 3467341618Scy if (status == DPP_STATUS_OK) { 3468341618Scy /* I-auth wrapped with ke */ 3469341618Scy wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 3470341618Scy wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE); 3471341618Scy wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE); 3472341618Scy 3473341618Scy#ifdef CONFIG_TESTING_OPTIONS 3474341618Scy if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF) 3475341618Scy goto skip_i_auth; 3476341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3477341618Scy 3478341618Scy /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 3479341618Scy * 1) */ 3480341618Scy WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG); 3481341618Scy WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len); 3482341618Scy if (dpp_gen_i_auth(auth, i_auth + 4) < 0) 3483341618Scy goto fail; 3484341618Scy 3485341618Scy#ifdef CONFIG_TESTING_OPTIONS 3486341618Scy if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) { 3487341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch"); 3488341618Scy i_auth[4 + auth->curve->hash_len / 2] ^= 0x01; 3489341618Scy } 3490341618Scyskip_i_auth: 3491341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3492341618Scy if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, 3493341618Scy i_auth, i_auth_len, 3494341618Scy 2, addr, len, wrapped_i_auth) < 0) 3495341618Scy goto fail; 3496341618Scy wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke", 3497341618Scy wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE); 3498341618Scy } else { 3499341618Scy /* R-nonce wrapped with k2 */ 3500341618Scy wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 3501341618Scy wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE); 3502341618Scy wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE); 3503341618Scy 3504341618Scy WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE); 3505341618Scy WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len); 3506341618Scy os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len); 3507341618Scy 3508341618Scy if (aes_siv_encrypt(auth->k2, auth->curve->hash_len, 3509341618Scy r_nonce, r_nonce_len, 3510341618Scy 2, addr, len, wrapped_r_nonce) < 0) 3511341618Scy goto fail; 3512341618Scy wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2", 3513341618Scy wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE); 3514341618Scy } 3515341618Scy 3516341618Scy#ifdef CONFIG_TESTING_OPTIONS 3517341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) { 3518341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); 3519341618Scy dpp_build_attr_status(msg, DPP_STATUS_OK); 3520341618Scy } 3521341618Scyskip_wrapped_data: 3522341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3523341618Scy 3524341618Scy wpa_hexdump_buf(MSG_DEBUG, 3525341618Scy "DPP: Authentication Confirmation frame attributes", 3526341618Scy msg); 3527341618Scy if (status == DPP_STATUS_OK) 3528341618Scy dpp_auth_success(auth); 3529341618Scy 3530341618Scy return msg; 3531341618Scy 3532341618Scyfail: 3533341618Scy wpabuf_free(msg); 3534341618Scy return NULL; 3535341618Scy} 3536341618Scy 3537341618Scy 3538341618Scystatic void 3539341618Scydpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr, 3540341618Scy const u8 *attr_start, size_t attr_len, 3541341618Scy const u8 *wrapped_data, u16 wrapped_data_len, 3542341618Scy enum dpp_status_error status) 3543341618Scy{ 3544341618Scy const u8 *addr[2]; 3545341618Scy size_t len[2]; 3546341618Scy u8 *unwrapped = NULL; 3547341618Scy size_t unwrapped_len = 0; 3548341618Scy const u8 *i_nonce, *r_capab; 3549341618Scy u16 i_nonce_len, r_capab_len; 3550341618Scy 3551341618Scy if (status == DPP_STATUS_NOT_COMPATIBLE) { 3552341618Scy wpa_printf(MSG_DEBUG, 3553341618Scy "DPP: Responder reported incompatible roles"); 3554341618Scy } else if (status == DPP_STATUS_RESPONSE_PENDING) { 3555341618Scy wpa_printf(MSG_DEBUG, 3556341618Scy "DPP: Responder reported more time needed"); 3557341618Scy } else { 3558341618Scy wpa_printf(MSG_DEBUG, 3559341618Scy "DPP: Responder reported failure (status %d)", 3560341618Scy status); 3561341618Scy dpp_auth_fail(auth, "Responder reported failure"); 3562341618Scy return; 3563341618Scy } 3564341618Scy 3565341618Scy addr[0] = hdr; 3566341618Scy len[0] = DPP_HDR_LEN; 3567341618Scy addr[1] = attr_start; 3568341618Scy len[1] = attr_len; 3569341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 3570341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 3571341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 3572341618Scy wrapped_data, wrapped_data_len); 3573341618Scy unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 3574341618Scy unwrapped = os_malloc(unwrapped_len); 3575341618Scy if (!unwrapped) 3576341618Scy goto fail; 3577341618Scy if (aes_siv_decrypt(auth->k1, auth->curve->hash_len, 3578341618Scy wrapped_data, wrapped_data_len, 3579341618Scy 2, addr, len, unwrapped) < 0) { 3580341618Scy dpp_auth_fail(auth, "AES-SIV decryption failed"); 3581341618Scy goto fail; 3582341618Scy } 3583341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 3584341618Scy unwrapped, unwrapped_len); 3585341618Scy 3586341618Scy if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 3587341618Scy dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 3588341618Scy goto fail; 3589341618Scy } 3590341618Scy 3591341618Scy i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, 3592341618Scy &i_nonce_len); 3593341618Scy if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { 3594341618Scy dpp_auth_fail(auth, "Missing or invalid I-nonce"); 3595341618Scy goto fail; 3596341618Scy } 3597341618Scy wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); 3598341618Scy if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { 3599341618Scy dpp_auth_fail(auth, "I-nonce mismatch"); 3600341618Scy goto fail; 3601341618Scy } 3602341618Scy 3603341618Scy r_capab = dpp_get_attr(unwrapped, unwrapped_len, 3604341618Scy DPP_ATTR_R_CAPABILITIES, 3605341618Scy &r_capab_len); 3606341618Scy if (!r_capab || r_capab_len < 1) { 3607341618Scy dpp_auth_fail(auth, "Missing or invalid R-capabilities"); 3608341618Scy goto fail; 3609341618Scy } 3610341618Scy auth->r_capab = r_capab[0]; 3611341618Scy wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); 3612341618Scy if (status == DPP_STATUS_NOT_COMPATIBLE) { 3613341618Scy wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE 3614341618Scy "r-capab=0x%02x", auth->r_capab); 3615341618Scy } else if (status == DPP_STATUS_RESPONSE_PENDING) { 3616341618Scy u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK; 3617341618Scy 3618341618Scy if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) || 3619341618Scy (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) { 3620341618Scy wpa_msg(auth->msg_ctx, MSG_INFO, 3621341618Scy DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x", 3622341618Scy role); 3623341618Scy } else { 3624341618Scy wpa_printf(MSG_DEBUG, 3625341618Scy "DPP: Continue waiting for full DPP Authentication Response"); 3626341618Scy wpa_msg(auth->msg_ctx, MSG_INFO, 3627341618Scy DPP_EVENT_RESPONSE_PENDING "%s", 3628341618Scy auth->tmp_own_bi ? auth->tmp_own_bi->uri : ""); 3629341618Scy } 3630341618Scy } 3631341618Scyfail: 3632341618Scy bin_clear_free(unwrapped, unwrapped_len); 3633341618Scy} 3634341618Scy 3635341618Scy 3636341618Scystruct wpabuf * 3637341618Scydpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr, 3638341618Scy const u8 *attr_start, size_t attr_len) 3639341618Scy{ 3640341618Scy EVP_PKEY *pr; 3641341618Scy size_t secret_len; 3642341618Scy const u8 *addr[2]; 3643341618Scy size_t len[2]; 3644341618Scy u8 *unwrapped = NULL, *unwrapped2 = NULL; 3645341618Scy size_t unwrapped_len = 0, unwrapped2_len = 0; 3646341618Scy const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto, 3647341618Scy *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth; 3648341618Scy u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, 3649341618Scy r_proto_len, r_nonce_len, i_nonce_len, r_capab_len, 3650341618Scy wrapped2_len, r_auth_len; 3651341618Scy u8 r_auth2[DPP_MAX_HASH_LEN]; 3652341618Scy u8 role; 3653346981Scy#ifdef CONFIG_DPP2 3654346981Scy const u8 *version; 3655346981Scy u16 version_len; 3656346981Scy#endif /* CONFIG_DPP2 */ 3657341618Scy 3658341618Scy#ifdef CONFIG_TESTING_OPTIONS 3659341618Scy if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) { 3660341618Scy wpa_printf(MSG_INFO, 3661341618Scy "DPP: TESTING - stop at Authentication Response"); 3662341618Scy return NULL; 3663341618Scy } 3664341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3665341618Scy 3666341618Scy if (!auth->initiator || !auth->peer_bi) { 3667341618Scy dpp_auth_fail(auth, "Unexpected Authentication Response"); 3668341618Scy return NULL; 3669341618Scy } 3670341618Scy 3671341618Scy auth->waiting_auth_resp = 0; 3672341618Scy 3673341618Scy wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, 3674341618Scy &wrapped_data_len); 3675341618Scy if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 3676341618Scy dpp_auth_fail(auth, 3677341618Scy "Missing or invalid required Wrapped Data attribute"); 3678341618Scy return NULL; 3679341618Scy } 3680341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", 3681341618Scy wrapped_data, wrapped_data_len); 3682341618Scy 3683341618Scy attr_len = wrapped_data - 4 - attr_start; 3684341618Scy 3685341618Scy r_bootstrap = dpp_get_attr(attr_start, attr_len, 3686341618Scy DPP_ATTR_R_BOOTSTRAP_KEY_HASH, 3687341618Scy &r_bootstrap_len); 3688341618Scy if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { 3689341618Scy dpp_auth_fail(auth, 3690341618Scy "Missing or invalid required Responder Bootstrapping Key Hash attribute"); 3691341618Scy return NULL; 3692341618Scy } 3693341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", 3694341618Scy r_bootstrap, r_bootstrap_len); 3695341618Scy if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash, 3696341618Scy SHA256_MAC_LEN) != 0) { 3697341618Scy dpp_auth_fail(auth, 3698341618Scy "Unexpected Responder Bootstrapping Key Hash value"); 3699341618Scy wpa_hexdump(MSG_DEBUG, 3700341618Scy "DPP: Expected Responder Bootstrapping Key Hash", 3701341618Scy auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); 3702341618Scy return NULL; 3703341618Scy } 3704341618Scy 3705341618Scy i_bootstrap = dpp_get_attr(attr_start, attr_len, 3706341618Scy DPP_ATTR_I_BOOTSTRAP_KEY_HASH, 3707341618Scy &i_bootstrap_len); 3708341618Scy if (i_bootstrap) { 3709341618Scy if (i_bootstrap_len != SHA256_MAC_LEN) { 3710341618Scy dpp_auth_fail(auth, 3711341618Scy "Invalid Initiator Bootstrapping Key Hash attribute"); 3712341618Scy return NULL; 3713341618Scy } 3714341618Scy wpa_hexdump(MSG_MSGDUMP, 3715341618Scy "DPP: Initiator Bootstrapping Key Hash", 3716341618Scy i_bootstrap, i_bootstrap_len); 3717341618Scy if (!auth->own_bi || 3718341618Scy os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash, 3719341618Scy SHA256_MAC_LEN) != 0) { 3720341618Scy dpp_auth_fail(auth, 3721341618Scy "Initiator Bootstrapping Key Hash attribute did not match"); 3722341618Scy return NULL; 3723341618Scy } 3724341618Scy } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) { 3725341618Scy /* PKEX bootstrapping mandates use of mutual authentication */ 3726341618Scy dpp_auth_fail(auth, 3727341618Scy "Missing Initiator Bootstrapping Key Hash attribute"); 3728341618Scy return NULL; 3729341618Scy } 3730341618Scy 3731346981Scy auth->peer_version = 1; /* default to the first version */ 3732346981Scy#ifdef CONFIG_DPP2 3733346981Scy version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION, 3734346981Scy &version_len); 3735346981Scy if (version) { 3736346981Scy if (version_len < 1 || version[0] == 0) { 3737346981Scy dpp_auth_fail(auth, 3738346981Scy "Invalid Protocol Version attribute"); 3739346981Scy return NULL; 3740346981Scy } 3741346981Scy auth->peer_version = version[0]; 3742346981Scy wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u", 3743346981Scy auth->peer_version); 3744346981Scy } 3745346981Scy#endif /* CONFIG_DPP2 */ 3746346981Scy 3747341618Scy status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, 3748341618Scy &status_len); 3749341618Scy if (!status || status_len < 1) { 3750341618Scy dpp_auth_fail(auth, 3751341618Scy "Missing or invalid required DPP Status attribute"); 3752341618Scy return NULL; 3753341618Scy } 3754341618Scy wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); 3755341618Scy auth->auth_resp_status = status[0]; 3756341618Scy if (status[0] != DPP_STATUS_OK) { 3757341618Scy dpp_auth_resp_rx_status(auth, hdr, attr_start, 3758341618Scy attr_len, wrapped_data, 3759341618Scy wrapped_data_len, status[0]); 3760341618Scy return NULL; 3761341618Scy } 3762341618Scy 3763341618Scy if (!i_bootstrap && auth->own_bi) { 3764341618Scy wpa_printf(MSG_DEBUG, 3765341618Scy "DPP: Responder decided not to use mutual authentication"); 3766341618Scy auth->own_bi = NULL; 3767341618Scy } 3768341618Scy 3769341618Scy wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d", 3770341618Scy auth->own_bi != NULL); 3771341618Scy 3772341618Scy r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY, 3773341618Scy &r_proto_len); 3774341618Scy if (!r_proto) { 3775341618Scy dpp_auth_fail(auth, 3776341618Scy "Missing required Responder Protocol Key attribute"); 3777341618Scy return NULL; 3778341618Scy } 3779341618Scy wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key", 3780341618Scy r_proto, r_proto_len); 3781341618Scy 3782341618Scy /* N = pI * PR */ 3783341618Scy pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len); 3784341618Scy if (!pr) { 3785341618Scy dpp_auth_fail(auth, "Invalid Responder Protocol Key"); 3786341618Scy return NULL; 3787341618Scy } 3788341618Scy dpp_debug_print_key("Peer (Responder) Protocol Key", pr); 3789341618Scy 3790351611Scy if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) { 3791341618Scy dpp_auth_fail(auth, "Failed to derive ECDH shared secret"); 3792341618Scy goto fail; 3793341618Scy } 3794351611Scy EVP_PKEY_free(auth->peer_protocol_key); 3795341618Scy auth->peer_protocol_key = pr; 3796341618Scy pr = NULL; 3797341618Scy 3798341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", 3799341618Scy auth->Nx, auth->secret_len); 3800341618Scy auth->Nx_len = auth->secret_len; 3801341618Scy 3802341618Scy if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2, 3803341618Scy auth->curve->hash_len) < 0) 3804341618Scy goto fail; 3805341618Scy 3806341618Scy addr[0] = hdr; 3807341618Scy len[0] = DPP_HDR_LEN; 3808341618Scy addr[1] = attr_start; 3809341618Scy len[1] = attr_len; 3810341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 3811341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 3812341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 3813341618Scy wrapped_data, wrapped_data_len); 3814341618Scy unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 3815341618Scy unwrapped = os_malloc(unwrapped_len); 3816341618Scy if (!unwrapped) 3817341618Scy goto fail; 3818341618Scy if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, 3819341618Scy wrapped_data, wrapped_data_len, 3820341618Scy 2, addr, len, unwrapped) < 0) { 3821341618Scy dpp_auth_fail(auth, "AES-SIV decryption failed"); 3822341618Scy goto fail; 3823341618Scy } 3824341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 3825341618Scy unwrapped, unwrapped_len); 3826341618Scy 3827341618Scy if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 3828341618Scy dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 3829341618Scy goto fail; 3830341618Scy } 3831341618Scy 3832341618Scy r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, 3833341618Scy &r_nonce_len); 3834341618Scy if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { 3835341618Scy dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce"); 3836341618Scy goto fail; 3837341618Scy } 3838341618Scy wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len); 3839341618Scy os_memcpy(auth->r_nonce, r_nonce, r_nonce_len); 3840341618Scy 3841341618Scy i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE, 3842341618Scy &i_nonce_len); 3843341618Scy if (!i_nonce || i_nonce_len != auth->curve->nonce_len) { 3844341618Scy dpp_auth_fail(auth, "Missing or invalid I-nonce"); 3845341618Scy goto fail; 3846341618Scy } 3847341618Scy wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len); 3848341618Scy if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) { 3849341618Scy dpp_auth_fail(auth, "I-nonce mismatch"); 3850341618Scy goto fail; 3851341618Scy } 3852341618Scy 3853341618Scy if (auth->own_bi) { 3854341618Scy /* Mutual authentication */ 3855341618Scy if (dpp_auth_derive_l_initiator(auth) < 0) 3856341618Scy goto fail; 3857341618Scy } 3858341618Scy 3859341618Scy r_capab = dpp_get_attr(unwrapped, unwrapped_len, 3860341618Scy DPP_ATTR_R_CAPABILITIES, 3861341618Scy &r_capab_len); 3862341618Scy if (!r_capab || r_capab_len < 1) { 3863341618Scy dpp_auth_fail(auth, "Missing or invalid R-capabilities"); 3864341618Scy goto fail; 3865341618Scy } 3866341618Scy auth->r_capab = r_capab[0]; 3867341618Scy wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab); 3868341618Scy role = auth->r_capab & DPP_CAPAB_ROLE_MASK; 3869341618Scy if ((auth->allowed_roles == 3870341618Scy (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) && 3871341618Scy (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) { 3872341618Scy /* Peer selected its role, so move from "either role" to the 3873341618Scy * role that is compatible with peer's selection. */ 3874341618Scy auth->configurator = role == DPP_CAPAB_ENROLLEE; 3875341618Scy wpa_printf(MSG_DEBUG, "DPP: Acting as %s", 3876341618Scy auth->configurator ? "Configurator" : "Enrollee"); 3877341618Scy } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) || 3878341618Scy (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) { 3879341618Scy wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection"); 3880341618Scy wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL 3881341618Scy "Unexpected role in R-capabilities 0x%02x", 3882341618Scy role); 3883341618Scy if (role != DPP_CAPAB_ENROLLEE && 3884341618Scy role != DPP_CAPAB_CONFIGURATOR) 3885341618Scy goto fail; 3886341618Scy bin_clear_free(unwrapped, unwrapped_len); 3887341618Scy auth->remove_on_tx_status = 1; 3888341618Scy return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE); 3889341618Scy } 3890341618Scy 3891341618Scy wrapped2 = dpp_get_attr(unwrapped, unwrapped_len, 3892341618Scy DPP_ATTR_WRAPPED_DATA, &wrapped2_len); 3893341618Scy if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) { 3894341618Scy dpp_auth_fail(auth, 3895341618Scy "Missing or invalid Secondary Wrapped Data"); 3896341618Scy goto fail; 3897341618Scy } 3898341618Scy 3899341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 3900341618Scy wrapped2, wrapped2_len); 3901341618Scy 3902341618Scy if (dpp_derive_ke(auth, auth->ke, auth->curve->hash_len) < 0) 3903341618Scy goto fail; 3904341618Scy 3905341618Scy unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE; 3906341618Scy unwrapped2 = os_malloc(unwrapped2_len); 3907341618Scy if (!unwrapped2) 3908341618Scy goto fail; 3909341618Scy if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, 3910341618Scy wrapped2, wrapped2_len, 3911341618Scy 0, NULL, NULL, unwrapped2) < 0) { 3912341618Scy dpp_auth_fail(auth, "AES-SIV decryption failed"); 3913341618Scy goto fail; 3914341618Scy } 3915341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 3916341618Scy unwrapped2, unwrapped2_len); 3917341618Scy 3918341618Scy if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) { 3919341618Scy dpp_auth_fail(auth, 3920341618Scy "Invalid attribute in secondary unwrapped data"); 3921341618Scy goto fail; 3922341618Scy } 3923341618Scy 3924341618Scy r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG, 3925341618Scy &r_auth_len); 3926341618Scy if (!r_auth || r_auth_len != auth->curve->hash_len) { 3927341618Scy dpp_auth_fail(auth, 3928341618Scy "Missing or invalid Responder Authenticating Tag"); 3929341618Scy goto fail; 3930341618Scy } 3931341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag", 3932341618Scy r_auth, r_auth_len); 3933341618Scy /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */ 3934341618Scy if (dpp_gen_r_auth(auth, r_auth2) < 0) 3935341618Scy goto fail; 3936341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag", 3937341618Scy r_auth2, r_auth_len); 3938341618Scy if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) { 3939341618Scy dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag"); 3940341618Scy bin_clear_free(unwrapped, unwrapped_len); 3941341618Scy bin_clear_free(unwrapped2, unwrapped2_len); 3942341618Scy auth->remove_on_tx_status = 1; 3943341618Scy return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE); 3944341618Scy } 3945341618Scy 3946341618Scy bin_clear_free(unwrapped, unwrapped_len); 3947341618Scy bin_clear_free(unwrapped2, unwrapped2_len); 3948341618Scy 3949341618Scy#ifdef CONFIG_TESTING_OPTIONS 3950341618Scy if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) { 3951341618Scy wpa_printf(MSG_INFO, 3952341618Scy "DPP: TESTING - Authentication Response in place of Confirm"); 3953341618Scy if (dpp_auth_build_resp_ok(auth) < 0) 3954341618Scy return NULL; 3955341618Scy return wpabuf_dup(auth->resp_msg); 3956341618Scy } 3957341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 3958341618Scy 3959341618Scy return dpp_auth_build_conf(auth, DPP_STATUS_OK); 3960341618Scy 3961341618Scyfail: 3962341618Scy bin_clear_free(unwrapped, unwrapped_len); 3963341618Scy bin_clear_free(unwrapped2, unwrapped2_len); 3964341618Scy EVP_PKEY_free(pr); 3965341618Scy return NULL; 3966341618Scy} 3967341618Scy 3968341618Scy 3969341618Scystatic int dpp_auth_conf_rx_failure(struct dpp_authentication *auth, 3970341618Scy const u8 *hdr, 3971341618Scy const u8 *attr_start, size_t attr_len, 3972341618Scy const u8 *wrapped_data, 3973341618Scy u16 wrapped_data_len, 3974341618Scy enum dpp_status_error status) 3975341618Scy{ 3976341618Scy const u8 *addr[2]; 3977341618Scy size_t len[2]; 3978341618Scy u8 *unwrapped = NULL; 3979341618Scy size_t unwrapped_len = 0; 3980341618Scy const u8 *r_nonce; 3981341618Scy u16 r_nonce_len; 3982341618Scy 3983341618Scy /* Authentication Confirm failure cases are expected to include 3984341618Scy * {R-nonce}k2 in the Wrapped Data attribute. */ 3985341618Scy 3986341618Scy addr[0] = hdr; 3987341618Scy len[0] = DPP_HDR_LEN; 3988341618Scy addr[1] = attr_start; 3989341618Scy len[1] = attr_len; 3990341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 3991341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 3992341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 3993341618Scy wrapped_data, wrapped_data_len); 3994341618Scy unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 3995341618Scy unwrapped = os_malloc(unwrapped_len); 3996341618Scy if (!unwrapped) { 3997341618Scy dpp_auth_fail(auth, "Authentication failed"); 3998341618Scy goto fail; 3999341618Scy } 4000341618Scy if (aes_siv_decrypt(auth->k2, auth->curve->hash_len, 4001341618Scy wrapped_data, wrapped_data_len, 4002341618Scy 2, addr, len, unwrapped) < 0) { 4003341618Scy dpp_auth_fail(auth, "AES-SIV decryption failed"); 4004341618Scy goto fail; 4005341618Scy } 4006341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 4007341618Scy unwrapped, unwrapped_len); 4008341618Scy 4009341618Scy if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 4010341618Scy dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 4011341618Scy goto fail; 4012341618Scy } 4013341618Scy 4014341618Scy r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE, 4015341618Scy &r_nonce_len); 4016341618Scy if (!r_nonce || r_nonce_len != auth->curve->nonce_len) { 4017341618Scy dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce"); 4018341618Scy goto fail; 4019341618Scy } 4020341618Scy if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) { 4021341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce", 4022341618Scy r_nonce, r_nonce_len); 4023341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce", 4024341618Scy auth->r_nonce, r_nonce_len); 4025341618Scy dpp_auth_fail(auth, "R-nonce mismatch"); 4026341618Scy goto fail; 4027341618Scy } 4028341618Scy 4029341618Scy if (status == DPP_STATUS_NOT_COMPATIBLE) 4030341618Scy dpp_auth_fail(auth, "Peer reported incompatible R-capab role"); 4031341618Scy else if (status == DPP_STATUS_AUTH_FAILURE) 4032341618Scy dpp_auth_fail(auth, "Peer reported authentication failure)"); 4033341618Scy 4034341618Scyfail: 4035341618Scy bin_clear_free(unwrapped, unwrapped_len); 4036341618Scy return -1; 4037341618Scy} 4038341618Scy 4039341618Scy 4040341618Scyint dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr, 4041341618Scy const u8 *attr_start, size_t attr_len) 4042341618Scy{ 4043341618Scy const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth; 4044341618Scy u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len, 4045341618Scy i_auth_len; 4046341618Scy const u8 *addr[2]; 4047341618Scy size_t len[2]; 4048341618Scy u8 *unwrapped = NULL; 4049341618Scy size_t unwrapped_len = 0; 4050341618Scy u8 i_auth2[DPP_MAX_HASH_LEN]; 4051341618Scy 4052341618Scy#ifdef CONFIG_TESTING_OPTIONS 4053341618Scy if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { 4054341618Scy wpa_printf(MSG_INFO, 4055341618Scy "DPP: TESTING - stop at Authentication Confirm"); 4056341618Scy return -1; 4057341618Scy } 4058341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4059341618Scy 4060341618Scy if (auth->initiator || !auth->own_bi) { 4061341618Scy dpp_auth_fail(auth, "Unexpected Authentication Confirm"); 4062341618Scy return -1; 4063341618Scy } 4064341618Scy 4065341618Scy auth->waiting_auth_conf = 0; 4066341618Scy 4067341618Scy wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, 4068341618Scy &wrapped_data_len); 4069341618Scy if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 4070341618Scy dpp_auth_fail(auth, 4071341618Scy "Missing or invalid required Wrapped Data attribute"); 4072341618Scy return -1; 4073341618Scy } 4074341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", 4075341618Scy wrapped_data, wrapped_data_len); 4076341618Scy 4077341618Scy attr_len = wrapped_data - 4 - attr_start; 4078341618Scy 4079341618Scy r_bootstrap = dpp_get_attr(attr_start, attr_len, 4080341618Scy DPP_ATTR_R_BOOTSTRAP_KEY_HASH, 4081341618Scy &r_bootstrap_len); 4082341618Scy if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { 4083341618Scy dpp_auth_fail(auth, 4084341618Scy "Missing or invalid required Responder Bootstrapping Key Hash attribute"); 4085341618Scy return -1; 4086341618Scy } 4087341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash", 4088341618Scy r_bootstrap, r_bootstrap_len); 4089341618Scy if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash, 4090341618Scy SHA256_MAC_LEN) != 0) { 4091341618Scy wpa_hexdump(MSG_DEBUG, 4092341618Scy "DPP: Expected Responder Bootstrapping Key Hash", 4093341618Scy auth->peer_bi->pubkey_hash, SHA256_MAC_LEN); 4094341618Scy dpp_auth_fail(auth, 4095341618Scy "Responder Bootstrapping Key Hash mismatch"); 4096341618Scy return -1; 4097341618Scy } 4098341618Scy 4099341618Scy i_bootstrap = dpp_get_attr(attr_start, attr_len, 4100341618Scy DPP_ATTR_I_BOOTSTRAP_KEY_HASH, 4101341618Scy &i_bootstrap_len); 4102341618Scy if (i_bootstrap) { 4103341618Scy if (i_bootstrap_len != SHA256_MAC_LEN) { 4104341618Scy dpp_auth_fail(auth, 4105341618Scy "Invalid Initiator Bootstrapping Key Hash attribute"); 4106341618Scy return -1; 4107341618Scy } 4108341618Scy wpa_hexdump(MSG_MSGDUMP, 4109341618Scy "DPP: Initiator Bootstrapping Key Hash", 4110341618Scy i_bootstrap, i_bootstrap_len); 4111341618Scy if (!auth->peer_bi || 4112341618Scy os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash, 4113341618Scy SHA256_MAC_LEN) != 0) { 4114341618Scy dpp_auth_fail(auth, 4115341618Scy "Initiator Bootstrapping Key Hash mismatch"); 4116341618Scy return -1; 4117341618Scy } 4118341618Scy } else if (auth->peer_bi) { 4119341618Scy /* Mutual authentication and peer did not include its 4120341618Scy * Bootstrapping Key Hash attribute. */ 4121341618Scy dpp_auth_fail(auth, 4122341618Scy "Missing Initiator Bootstrapping Key Hash attribute"); 4123341618Scy return -1; 4124341618Scy } 4125341618Scy 4126341618Scy status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS, 4127341618Scy &status_len); 4128341618Scy if (!status || status_len < 1) { 4129341618Scy dpp_auth_fail(auth, 4130341618Scy "Missing or invalid required DPP Status attribute"); 4131341618Scy return -1; 4132341618Scy } 4133341618Scy wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); 4134341618Scy if (status[0] == DPP_STATUS_NOT_COMPATIBLE || 4135341618Scy status[0] == DPP_STATUS_AUTH_FAILURE) 4136341618Scy return dpp_auth_conf_rx_failure(auth, hdr, attr_start, 4137341618Scy attr_len, wrapped_data, 4138341618Scy wrapped_data_len, status[0]); 4139341618Scy 4140341618Scy if (status[0] != DPP_STATUS_OK) { 4141341618Scy dpp_auth_fail(auth, "Authentication failed"); 4142341618Scy return -1; 4143341618Scy } 4144341618Scy 4145341618Scy addr[0] = hdr; 4146341618Scy len[0] = DPP_HDR_LEN; 4147341618Scy addr[1] = attr_start; 4148341618Scy len[1] = attr_len; 4149341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 4150341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 4151341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 4152341618Scy wrapped_data, wrapped_data_len); 4153341618Scy unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 4154341618Scy unwrapped = os_malloc(unwrapped_len); 4155341618Scy if (!unwrapped) 4156341618Scy return -1; 4157341618Scy if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, 4158341618Scy wrapped_data, wrapped_data_len, 4159341618Scy 2, addr, len, unwrapped) < 0) { 4160341618Scy dpp_auth_fail(auth, "AES-SIV decryption failed"); 4161341618Scy goto fail; 4162341618Scy } 4163341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 4164341618Scy unwrapped, unwrapped_len); 4165341618Scy 4166341618Scy if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 4167341618Scy dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 4168341618Scy goto fail; 4169341618Scy } 4170341618Scy 4171341618Scy i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG, 4172341618Scy &i_auth_len); 4173341618Scy if (!i_auth || i_auth_len != auth->curve->hash_len) { 4174341618Scy dpp_auth_fail(auth, 4175341618Scy "Missing or invalid Initiator Authenticating Tag"); 4176341618Scy goto fail; 4177341618Scy } 4178341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag", 4179341618Scy i_auth, i_auth_len); 4180341618Scy /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */ 4181341618Scy if (dpp_gen_i_auth(auth, i_auth2) < 0) 4182341618Scy goto fail; 4183341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag", 4184341618Scy i_auth2, i_auth_len); 4185341618Scy if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) { 4186341618Scy dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag"); 4187341618Scy goto fail; 4188341618Scy } 4189341618Scy 4190341618Scy bin_clear_free(unwrapped, unwrapped_len); 4191341618Scy dpp_auth_success(auth); 4192341618Scy return 0; 4193341618Scyfail: 4194341618Scy bin_clear_free(unwrapped, unwrapped_len); 4195341618Scy return -1; 4196341618Scy} 4197341618Scy 4198341618Scy 4199346981Scystatic int bin_str_eq(const char *val, size_t len, const char *cmp) 4200346981Scy{ 4201346981Scy return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0; 4202346981Scy} 4203346981Scy 4204346981Scy 4205346981Scystruct dpp_configuration * dpp_configuration_alloc(const char *type) 4206346981Scy{ 4207346981Scy struct dpp_configuration *conf; 4208346981Scy const char *end; 4209346981Scy size_t len; 4210346981Scy 4211346981Scy conf = os_zalloc(sizeof(*conf)); 4212346981Scy if (!conf) 4213346981Scy goto fail; 4214346981Scy 4215346981Scy end = os_strchr(type, ' '); 4216346981Scy if (end) 4217346981Scy len = end - type; 4218346981Scy else 4219346981Scy len = os_strlen(type); 4220346981Scy 4221346981Scy if (bin_str_eq(type, len, "psk")) 4222346981Scy conf->akm = DPP_AKM_PSK; 4223346981Scy else if (bin_str_eq(type, len, "sae")) 4224346981Scy conf->akm = DPP_AKM_SAE; 4225346981Scy else if (bin_str_eq(type, len, "psk-sae") || 4226346981Scy bin_str_eq(type, len, "psk+sae")) 4227346981Scy conf->akm = DPP_AKM_PSK_SAE; 4228346981Scy else if (bin_str_eq(type, len, "sae-dpp") || 4229346981Scy bin_str_eq(type, len, "dpp+sae")) 4230346981Scy conf->akm = DPP_AKM_SAE_DPP; 4231346981Scy else if (bin_str_eq(type, len, "psk-sae-dpp") || 4232346981Scy bin_str_eq(type, len, "dpp+psk+sae")) 4233346981Scy conf->akm = DPP_AKM_PSK_SAE_DPP; 4234346981Scy else if (bin_str_eq(type, len, "dpp")) 4235346981Scy conf->akm = DPP_AKM_DPP; 4236346981Scy else 4237346981Scy goto fail; 4238346981Scy 4239346981Scy return conf; 4240346981Scyfail: 4241346981Scy dpp_configuration_free(conf); 4242346981Scy return NULL; 4243346981Scy} 4244346981Scy 4245346981Scy 4246346981Scyint dpp_akm_psk(enum dpp_akm akm) 4247346981Scy{ 4248346981Scy return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || 4249346981Scy akm == DPP_AKM_PSK_SAE_DPP; 4250346981Scy} 4251346981Scy 4252346981Scy 4253346981Scyint dpp_akm_sae(enum dpp_akm akm) 4254346981Scy{ 4255346981Scy return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE || 4256346981Scy akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; 4257346981Scy} 4258346981Scy 4259346981Scy 4260346981Scyint dpp_akm_legacy(enum dpp_akm akm) 4261346981Scy{ 4262346981Scy return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE || 4263346981Scy akm == DPP_AKM_SAE; 4264346981Scy} 4265346981Scy 4266346981Scy 4267346981Scyint dpp_akm_dpp(enum dpp_akm akm) 4268346981Scy{ 4269346981Scy return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP || 4270346981Scy akm == DPP_AKM_PSK_SAE_DPP; 4271346981Scy} 4272346981Scy 4273346981Scy 4274346981Scyint dpp_akm_ver2(enum dpp_akm akm) 4275346981Scy{ 4276346981Scy return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP; 4277346981Scy} 4278346981Scy 4279346981Scy 4280346981Scyint dpp_configuration_valid(const struct dpp_configuration *conf) 4281346981Scy{ 4282346981Scy if (conf->ssid_len == 0) 4283346981Scy return 0; 4284346981Scy if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set) 4285346981Scy return 0; 4286346981Scy if (dpp_akm_sae(conf->akm) && !conf->passphrase) 4287346981Scy return 0; 4288346981Scy return 1; 4289346981Scy} 4290346981Scy 4291346981Scy 4292341618Scyvoid dpp_configuration_free(struct dpp_configuration *conf) 4293341618Scy{ 4294341618Scy if (!conf) 4295341618Scy return; 4296341618Scy str_clear_free(conf->passphrase); 4297341618Scy os_free(conf->group_id); 4298341618Scy bin_clear_free(conf, sizeof(*conf)); 4299341618Scy} 4300341618Scy 4301341618Scy 4302346981Scystatic int dpp_configuration_parse(struct dpp_authentication *auth, 4303346981Scy const char *cmd) 4304346981Scy{ 4305346981Scy const char *pos, *end; 4306346981Scy struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL; 4307346981Scy struct dpp_configuration *conf = NULL; 4308346981Scy 4309346981Scy pos = os_strstr(cmd, " conf=sta-"); 4310346981Scy if (pos) { 4311346981Scy conf_sta = dpp_configuration_alloc(pos + 10); 4312346981Scy if (!conf_sta) 4313346981Scy goto fail; 4314346981Scy conf = conf_sta; 4315346981Scy } 4316346981Scy 4317346981Scy pos = os_strstr(cmd, " conf=ap-"); 4318346981Scy if (pos) { 4319346981Scy conf_ap = dpp_configuration_alloc(pos + 9); 4320346981Scy if (!conf_ap) 4321346981Scy goto fail; 4322346981Scy conf = conf_ap; 4323346981Scy } 4324346981Scy 4325346981Scy if (!conf) 4326346981Scy return 0; 4327346981Scy 4328346981Scy pos = os_strstr(cmd, " ssid="); 4329346981Scy if (pos) { 4330346981Scy pos += 6; 4331346981Scy end = os_strchr(pos, ' '); 4332346981Scy conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos); 4333346981Scy conf->ssid_len /= 2; 4334346981Scy if (conf->ssid_len > sizeof(conf->ssid) || 4335346981Scy hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0) 4336346981Scy goto fail; 4337346981Scy } else { 4338346981Scy#ifdef CONFIG_TESTING_OPTIONS 4339346981Scy /* use a default SSID for legacy testing reasons */ 4340346981Scy os_memcpy(conf->ssid, "test", 4); 4341346981Scy conf->ssid_len = 4; 4342346981Scy#else /* CONFIG_TESTING_OPTIONS */ 4343346981Scy goto fail; 4344346981Scy#endif /* CONFIG_TESTING_OPTIONS */ 4345346981Scy } 4346346981Scy 4347346981Scy pos = os_strstr(cmd, " pass="); 4348346981Scy if (pos) { 4349346981Scy size_t pass_len; 4350346981Scy 4351346981Scy pos += 6; 4352346981Scy end = os_strchr(pos, ' '); 4353346981Scy pass_len = end ? (size_t) (end - pos) : os_strlen(pos); 4354346981Scy pass_len /= 2; 4355346981Scy if (pass_len > 63 || pass_len < 8) 4356346981Scy goto fail; 4357346981Scy conf->passphrase = os_zalloc(pass_len + 1); 4358346981Scy if (!conf->passphrase || 4359346981Scy hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0) 4360346981Scy goto fail; 4361346981Scy } 4362346981Scy 4363346981Scy pos = os_strstr(cmd, " psk="); 4364346981Scy if (pos) { 4365346981Scy pos += 5; 4366346981Scy if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0) 4367346981Scy goto fail; 4368346981Scy conf->psk_set = 1; 4369346981Scy } 4370346981Scy 4371346981Scy pos = os_strstr(cmd, " group_id="); 4372346981Scy if (pos) { 4373346981Scy size_t group_id_len; 4374346981Scy 4375346981Scy pos += 10; 4376346981Scy end = os_strchr(pos, ' '); 4377346981Scy group_id_len = end ? (size_t) (end - pos) : os_strlen(pos); 4378346981Scy conf->group_id = os_malloc(group_id_len + 1); 4379346981Scy if (!conf->group_id) 4380346981Scy goto fail; 4381346981Scy os_memcpy(conf->group_id, pos, group_id_len); 4382346981Scy conf->group_id[group_id_len] = '\0'; 4383346981Scy } 4384346981Scy 4385346981Scy pos = os_strstr(cmd, " expiry="); 4386346981Scy if (pos) { 4387346981Scy long int val; 4388346981Scy 4389346981Scy pos += 8; 4390346981Scy val = strtol(pos, NULL, 0); 4391346981Scy if (val <= 0) 4392346981Scy goto fail; 4393346981Scy conf->netaccesskey_expiry = val; 4394346981Scy } 4395346981Scy 4396346981Scy if (!dpp_configuration_valid(conf)) 4397346981Scy goto fail; 4398346981Scy 4399346981Scy auth->conf_sta = conf_sta; 4400346981Scy auth->conf_ap = conf_ap; 4401346981Scy return 0; 4402346981Scy 4403346981Scyfail: 4404346981Scy dpp_configuration_free(conf_sta); 4405346981Scy dpp_configuration_free(conf_ap); 4406346981Scy return -1; 4407346981Scy} 4408346981Scy 4409346981Scy 4410346981Scystatic struct dpp_configurator * 4411346981Scydpp_configurator_get_id(struct dpp_global *dpp, unsigned int id) 4412346981Scy{ 4413346981Scy struct dpp_configurator *conf; 4414346981Scy 4415346981Scy if (!dpp) 4416346981Scy return NULL; 4417346981Scy 4418346981Scy dl_list_for_each(conf, &dpp->configurator, 4419346981Scy struct dpp_configurator, list) { 4420346981Scy if (conf->id == id) 4421346981Scy return conf; 4422346981Scy } 4423346981Scy return NULL; 4424346981Scy} 4425346981Scy 4426346981Scy 4427346981Scyint dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx, 4428346981Scy struct dpp_authentication *auth, 4429346981Scy const char *cmd) 4430346981Scy{ 4431346981Scy const char *pos; 4432346981Scy 4433346981Scy if (!cmd) 4434346981Scy return 0; 4435346981Scy 4436346981Scy wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd); 4437346981Scy 4438346981Scy pos = os_strstr(cmd, " configurator="); 4439346981Scy if (pos) { 4440346981Scy pos += 14; 4441346981Scy auth->conf = dpp_configurator_get_id(dpp, atoi(pos)); 4442346981Scy if (!auth->conf) { 4443346981Scy wpa_printf(MSG_INFO, 4444346981Scy "DPP: Could not find the specified configurator"); 4445346981Scy return -1; 4446346981Scy } 4447346981Scy } 4448346981Scy 4449346981Scy if (dpp_configuration_parse(auth, cmd) < 0) { 4450346981Scy wpa_msg(msg_ctx, MSG_INFO, 4451346981Scy "DPP: Failed to set configurator parameters"); 4452346981Scy return -1; 4453346981Scy } 4454346981Scy return 0; 4455346981Scy} 4456346981Scy 4457346981Scy 4458341618Scyvoid dpp_auth_deinit(struct dpp_authentication *auth) 4459341618Scy{ 4460341618Scy if (!auth) 4461341618Scy return; 4462341618Scy dpp_configuration_free(auth->conf_ap); 4463341618Scy dpp_configuration_free(auth->conf_sta); 4464341618Scy EVP_PKEY_free(auth->own_protocol_key); 4465341618Scy EVP_PKEY_free(auth->peer_protocol_key); 4466341618Scy wpabuf_free(auth->req_msg); 4467341618Scy wpabuf_free(auth->resp_msg); 4468341618Scy wpabuf_free(auth->conf_req); 4469341618Scy os_free(auth->connector); 4470341618Scy wpabuf_free(auth->net_access_key); 4471341618Scy wpabuf_free(auth->c_sign_key); 4472341618Scy dpp_bootstrap_info_free(auth->tmp_own_bi); 4473341618Scy#ifdef CONFIG_TESTING_OPTIONS 4474341618Scy os_free(auth->config_obj_override); 4475341618Scy os_free(auth->discovery_override); 4476341618Scy os_free(auth->groups_override); 4477341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4478341618Scy bin_clear_free(auth, sizeof(*auth)); 4479341618Scy} 4480341618Scy 4481341618Scy 4482341618Scystatic struct wpabuf * 4483341618Scydpp_build_conf_start(struct dpp_authentication *auth, 4484341618Scy struct dpp_configuration *conf, size_t tailroom) 4485341618Scy{ 4486341618Scy struct wpabuf *buf; 4487341618Scy char ssid[6 * sizeof(conf->ssid) + 1]; 4488341618Scy 4489341618Scy#ifdef CONFIG_TESTING_OPTIONS 4490341618Scy if (auth->discovery_override) 4491341618Scy tailroom += os_strlen(auth->discovery_override); 4492341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4493341618Scy 4494341618Scy buf = wpabuf_alloc(200 + tailroom); 4495341618Scy if (!buf) 4496341618Scy return NULL; 4497341618Scy wpabuf_put_str(buf, "{\"wi-fi_tech\":\"infra\",\"discovery\":"); 4498341618Scy#ifdef CONFIG_TESTING_OPTIONS 4499341618Scy if (auth->discovery_override) { 4500341618Scy wpa_printf(MSG_DEBUG, "DPP: TESTING - discovery override: '%s'", 4501341618Scy auth->discovery_override); 4502341618Scy wpabuf_put_str(buf, auth->discovery_override); 4503341618Scy wpabuf_put_u8(buf, ','); 4504341618Scy return buf; 4505341618Scy } 4506341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4507341618Scy wpabuf_put_str(buf, "{\"ssid\":\""); 4508341618Scy json_escape_string(ssid, sizeof(ssid), 4509341618Scy (const char *) conf->ssid, conf->ssid_len); 4510341618Scy wpabuf_put_str(buf, ssid); 4511341618Scy wpabuf_put_str(buf, "\"},"); 4512341618Scy 4513341618Scy return buf; 4514341618Scy} 4515341618Scy 4516341618Scy 4517341618Scystatic int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key, 4518341618Scy const char *kid, const struct dpp_curve_params *curve) 4519341618Scy{ 4520341618Scy struct wpabuf *pub; 4521341618Scy const u8 *pos; 4522341618Scy char *x = NULL, *y = NULL; 4523341618Scy int ret = -1; 4524341618Scy 4525341618Scy pub = dpp_get_pubkey_point(key, 0); 4526341618Scy if (!pub) 4527341618Scy goto fail; 4528341618Scy pos = wpabuf_head(pub); 4529341618Scy x = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0); 4530341618Scy pos += curve->prime_len; 4531341618Scy y = (char *) base64_url_encode(pos, curve->prime_len, NULL, 0); 4532341618Scy if (!x || !y) 4533341618Scy goto fail; 4534341618Scy 4535341618Scy wpabuf_put_str(buf, "\""); 4536341618Scy wpabuf_put_str(buf, name); 4537341618Scy wpabuf_put_str(buf, "\":{\"kty\":\"EC\",\"crv\":\""); 4538341618Scy wpabuf_put_str(buf, curve->jwk_crv); 4539341618Scy wpabuf_put_str(buf, "\",\"x\":\""); 4540341618Scy wpabuf_put_str(buf, x); 4541341618Scy wpabuf_put_str(buf, "\",\"y\":\""); 4542341618Scy wpabuf_put_str(buf, y); 4543341618Scy if (kid) { 4544341618Scy wpabuf_put_str(buf, "\",\"kid\":\""); 4545341618Scy wpabuf_put_str(buf, kid); 4546341618Scy } 4547341618Scy wpabuf_put_str(buf, "\"}"); 4548341618Scy ret = 0; 4549341618Scyfail: 4550341618Scy wpabuf_free(pub); 4551341618Scy os_free(x); 4552341618Scy os_free(y); 4553341618Scy return ret; 4554341618Scy} 4555341618Scy 4556341618Scy 4557346981Scystatic void dpp_build_legacy_cred_params(struct wpabuf *buf, 4558346981Scy struct dpp_configuration *conf) 4559346981Scy{ 4560346981Scy if (conf->passphrase && os_strlen(conf->passphrase) < 64) { 4561346981Scy char pass[63 * 6 + 1]; 4562346981Scy 4563346981Scy json_escape_string(pass, sizeof(pass), conf->passphrase, 4564346981Scy os_strlen(conf->passphrase)); 4565346981Scy wpabuf_put_str(buf, "\"pass\":\""); 4566346981Scy wpabuf_put_str(buf, pass); 4567346981Scy wpabuf_put_str(buf, "\""); 4568346981Scy os_memset(pass, 0, sizeof(pass)); 4569346981Scy } else if (conf->psk_set) { 4570346981Scy char psk[2 * sizeof(conf->psk) + 1]; 4571346981Scy 4572346981Scy wpa_snprintf_hex(psk, sizeof(psk), 4573346981Scy conf->psk, sizeof(conf->psk)); 4574346981Scy wpabuf_put_str(buf, "\"psk_hex\":\""); 4575346981Scy wpabuf_put_str(buf, psk); 4576346981Scy wpabuf_put_str(buf, "\""); 4577346981Scy os_memset(psk, 0, sizeof(psk)); 4578346981Scy } 4579346981Scy} 4580346981Scy 4581346981Scy 4582341618Scystatic struct wpabuf * 4583341618Scydpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap, 4584341618Scy struct dpp_configuration *conf) 4585341618Scy{ 4586341618Scy struct wpabuf *buf = NULL; 4587341618Scy char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL; 4588341618Scy size_t tailroom; 4589341618Scy const struct dpp_curve_params *curve; 4590341618Scy char jws_prot_hdr[100]; 4591341618Scy size_t signed1_len, signed2_len, signed3_len; 4592341618Scy struct wpabuf *dppcon = NULL; 4593341618Scy unsigned char *signature = NULL; 4594341618Scy const unsigned char *p; 4595341618Scy size_t signature_len; 4596341618Scy EVP_MD_CTX *md_ctx = NULL; 4597341618Scy ECDSA_SIG *sig = NULL; 4598341618Scy char *dot = "."; 4599341618Scy const EVP_MD *sign_md; 4600341618Scy const BIGNUM *r, *s; 4601341618Scy size_t extra_len = 1000; 4602346981Scy int incl_legacy; 4603346981Scy enum dpp_akm akm; 4604341618Scy 4605341618Scy if (!auth->conf) { 4606341618Scy wpa_printf(MSG_INFO, 4607341618Scy "DPP: No configurator specified - cannot generate DPP config object"); 4608341618Scy goto fail; 4609341618Scy } 4610341618Scy curve = auth->conf->curve; 4611341618Scy if (curve->hash_len == SHA256_MAC_LEN) { 4612341618Scy sign_md = EVP_sha256(); 4613341618Scy } else if (curve->hash_len == SHA384_MAC_LEN) { 4614341618Scy sign_md = EVP_sha384(); 4615341618Scy } else if (curve->hash_len == SHA512_MAC_LEN) { 4616341618Scy sign_md = EVP_sha512(); 4617341618Scy } else { 4618341618Scy wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm"); 4619341618Scy goto fail; 4620341618Scy } 4621341618Scy 4622346981Scy akm = conf->akm; 4623346981Scy if (dpp_akm_ver2(akm) && auth->peer_version < 2) { 4624346981Scy wpa_printf(MSG_DEBUG, 4625346981Scy "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2"); 4626346981Scy akm = DPP_AKM_DPP; 4627346981Scy } 4628346981Scy 4629341618Scy#ifdef CONFIG_TESTING_OPTIONS 4630341618Scy if (auth->groups_override) 4631341618Scy extra_len += os_strlen(auth->groups_override); 4632341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4633341618Scy 4634341618Scy if (conf->group_id) 4635341618Scy extra_len += os_strlen(conf->group_id); 4636341618Scy 4637341618Scy /* Connector (JSON dppCon object) */ 4638341618Scy dppcon = wpabuf_alloc(extra_len + 2 * auth->curve->prime_len * 4 / 3); 4639341618Scy if (!dppcon) 4640341618Scy goto fail; 4641341618Scy#ifdef CONFIG_TESTING_OPTIONS 4642341618Scy if (auth->groups_override) { 4643341618Scy wpabuf_put_u8(dppcon, '{'); 4644341618Scy if (auth->groups_override) { 4645341618Scy wpa_printf(MSG_DEBUG, 4646341618Scy "DPP: TESTING - groups override: '%s'", 4647341618Scy auth->groups_override); 4648341618Scy wpabuf_put_str(dppcon, "\"groups\":"); 4649341618Scy wpabuf_put_str(dppcon, auth->groups_override); 4650341618Scy wpabuf_put_u8(dppcon, ','); 4651341618Scy } 4652341618Scy goto skip_groups; 4653341618Scy } 4654341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4655341618Scy wpabuf_printf(dppcon, "{\"groups\":[{\"groupId\":\"%s\",", 4656341618Scy conf->group_id ? conf->group_id : "*"); 4657341618Scy wpabuf_printf(dppcon, "\"netRole\":\"%s\"}],", ap ? "ap" : "sta"); 4658341618Scy#ifdef CONFIG_TESTING_OPTIONS 4659341618Scyskip_groups: 4660341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4661341618Scy if (dpp_build_jwk(dppcon, "netAccessKey", auth->peer_protocol_key, NULL, 4662341618Scy auth->curve) < 0) { 4663341618Scy wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK"); 4664341618Scy goto fail; 4665341618Scy } 4666341618Scy if (conf->netaccesskey_expiry) { 4667341618Scy struct os_tm tm; 4668341618Scy 4669341618Scy if (os_gmtime(conf->netaccesskey_expiry, &tm) < 0) { 4670341618Scy wpa_printf(MSG_DEBUG, 4671341618Scy "DPP: Failed to generate expiry string"); 4672341618Scy goto fail; 4673341618Scy } 4674341618Scy wpabuf_printf(dppcon, 4675341618Scy ",\"expiry\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"", 4676341618Scy tm.year, tm.month, tm.day, 4677341618Scy tm.hour, tm.min, tm.sec); 4678341618Scy } 4679341618Scy wpabuf_put_u8(dppcon, '}'); 4680341618Scy wpa_printf(MSG_DEBUG, "DPP: dppCon: %s", 4681341618Scy (const char *) wpabuf_head(dppcon)); 4682341618Scy 4683341618Scy os_snprintf(jws_prot_hdr, sizeof(jws_prot_hdr), 4684341618Scy "{\"typ\":\"dppCon\",\"kid\":\"%s\",\"alg\":\"%s\"}", 4685341618Scy auth->conf->kid, curve->jws_alg); 4686341618Scy signed1 = (char *) base64_url_encode((unsigned char *) jws_prot_hdr, 4687341618Scy os_strlen(jws_prot_hdr), 4688341618Scy &signed1_len, 0); 4689341618Scy signed2 = (char *) base64_url_encode(wpabuf_head(dppcon), 4690341618Scy wpabuf_len(dppcon), 4691341618Scy &signed2_len, 0); 4692341618Scy if (!signed1 || !signed2) 4693341618Scy goto fail; 4694341618Scy 4695341618Scy md_ctx = EVP_MD_CTX_create(); 4696341618Scy if (!md_ctx) 4697341618Scy goto fail; 4698341618Scy 4699341618Scy ERR_clear_error(); 4700341618Scy if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL, 4701341618Scy auth->conf->csign) != 1) { 4702341618Scy wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s", 4703341618Scy ERR_error_string(ERR_get_error(), NULL)); 4704341618Scy goto fail; 4705341618Scy } 4706341618Scy if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 || 4707341618Scy EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 || 4708341618Scy EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) { 4709341618Scy wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s", 4710341618Scy ERR_error_string(ERR_get_error(), NULL)); 4711341618Scy goto fail; 4712341618Scy } 4713341618Scy if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) { 4714341618Scy wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s", 4715341618Scy ERR_error_string(ERR_get_error(), NULL)); 4716341618Scy goto fail; 4717341618Scy } 4718341618Scy signature = os_malloc(signature_len); 4719341618Scy if (!signature) 4720341618Scy goto fail; 4721341618Scy if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) { 4722341618Scy wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s", 4723341618Scy ERR_error_string(ERR_get_error(), NULL)); 4724341618Scy goto fail; 4725341618Scy } 4726341618Scy wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)", 4727341618Scy signature, signature_len); 4728341618Scy /* Convert to raw coordinates r,s */ 4729341618Scy p = signature; 4730341618Scy sig = d2i_ECDSA_SIG(NULL, &p, signature_len); 4731341618Scy if (!sig) 4732341618Scy goto fail; 4733341618Scy ECDSA_SIG_get0(sig, &r, &s); 4734341618Scy if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 || 4735341618Scy dpp_bn2bin_pad(s, signature + curve->prime_len, 4736341618Scy curve->prime_len) < 0) 4737341618Scy goto fail; 4738341618Scy signature_len = 2 * curve->prime_len; 4739341618Scy wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)", 4740341618Scy signature, signature_len); 4741341618Scy signed3 = (char *) base64_url_encode(signature, signature_len, 4742341618Scy &signed3_len, 0); 4743341618Scy if (!signed3) 4744341618Scy goto fail; 4745341618Scy 4746346981Scy incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm); 4747341618Scy tailroom = 1000; 4748341618Scy tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid); 4749341618Scy tailroom += signed1_len + signed2_len + signed3_len; 4750346981Scy if (incl_legacy) 4751346981Scy tailroom += 1000; 4752341618Scy buf = dpp_build_conf_start(auth, conf, tailroom); 4753341618Scy if (!buf) 4754341618Scy goto fail; 4755341618Scy 4756346981Scy wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(akm)); 4757346981Scy if (incl_legacy) { 4758346981Scy dpp_build_legacy_cred_params(buf, conf); 4759346981Scy wpabuf_put_str(buf, ","); 4760346981Scy } 4761346981Scy wpabuf_put_str(buf, "\"signedConnector\":\""); 4762341618Scy wpabuf_put_str(buf, signed1); 4763341618Scy wpabuf_put_u8(buf, '.'); 4764341618Scy wpabuf_put_str(buf, signed2); 4765341618Scy wpabuf_put_u8(buf, '.'); 4766341618Scy wpabuf_put_str(buf, signed3); 4767341618Scy wpabuf_put_str(buf, "\","); 4768341618Scy if (dpp_build_jwk(buf, "csign", auth->conf->csign, auth->conf->kid, 4769341618Scy curve) < 0) { 4770341618Scy wpa_printf(MSG_DEBUG, "DPP: Failed to build csign JWK"); 4771341618Scy goto fail; 4772341618Scy } 4773341618Scy 4774341618Scy wpabuf_put_str(buf, "}}"); 4775341618Scy 4776341618Scy wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object", 4777341618Scy wpabuf_head(buf), wpabuf_len(buf)); 4778341618Scy 4779341618Scyout: 4780341618Scy EVP_MD_CTX_destroy(md_ctx); 4781341618Scy ECDSA_SIG_free(sig); 4782341618Scy os_free(signed1); 4783341618Scy os_free(signed2); 4784341618Scy os_free(signed3); 4785341618Scy os_free(signature); 4786341618Scy wpabuf_free(dppcon); 4787341618Scy return buf; 4788341618Scyfail: 4789341618Scy wpa_printf(MSG_DEBUG, "DPP: Failed to build configuration object"); 4790341618Scy wpabuf_free(buf); 4791341618Scy buf = NULL; 4792341618Scy goto out; 4793341618Scy} 4794341618Scy 4795341618Scy 4796341618Scystatic struct wpabuf * 4797341618Scydpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap, 4798341618Scy struct dpp_configuration *conf) 4799341618Scy{ 4800341618Scy struct wpabuf *buf; 4801341618Scy 4802341618Scy buf = dpp_build_conf_start(auth, conf, 1000); 4803341618Scy if (!buf) 4804341618Scy return NULL; 4805341618Scy 4806341618Scy wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm)); 4807346981Scy dpp_build_legacy_cred_params(buf, conf); 4808341618Scy wpabuf_put_str(buf, "}}"); 4809341618Scy 4810341618Scy wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)", 4811341618Scy wpabuf_head(buf), wpabuf_len(buf)); 4812341618Scy 4813341618Scy return buf; 4814341618Scy} 4815341618Scy 4816341618Scy 4817341618Scystatic struct wpabuf * 4818341618Scydpp_build_conf_obj(struct dpp_authentication *auth, int ap) 4819341618Scy{ 4820341618Scy struct dpp_configuration *conf; 4821341618Scy 4822341618Scy#ifdef CONFIG_TESTING_OPTIONS 4823341618Scy if (auth->config_obj_override) { 4824341618Scy wpa_printf(MSG_DEBUG, "DPP: Testing - Config Object override"); 4825341618Scy return wpabuf_alloc_copy(auth->config_obj_override, 4826341618Scy os_strlen(auth->config_obj_override)); 4827341618Scy } 4828341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4829341618Scy 4830341618Scy conf = ap ? auth->conf_ap : auth->conf_sta; 4831341618Scy if (!conf) { 4832341618Scy wpa_printf(MSG_DEBUG, 4833341618Scy "DPP: No configuration available for Enrollee(%s) - reject configuration request", 4834341618Scy ap ? "ap" : "sta"); 4835341618Scy return NULL; 4836341618Scy } 4837341618Scy 4838346981Scy if (dpp_akm_dpp(conf->akm)) 4839341618Scy return dpp_build_conf_obj_dpp(auth, ap, conf); 4840341618Scy return dpp_build_conf_obj_legacy(auth, ap, conf); 4841341618Scy} 4842341618Scy 4843341618Scy 4844341618Scystatic struct wpabuf * 4845341618Scydpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce, 4846341618Scy u16 e_nonce_len, int ap) 4847341618Scy{ 4848341618Scy struct wpabuf *conf; 4849341618Scy size_t clear_len, attr_len; 4850341618Scy struct wpabuf *clear = NULL, *msg = NULL; 4851341618Scy u8 *wrapped; 4852341618Scy const u8 *addr[1]; 4853341618Scy size_t len[1]; 4854341618Scy enum dpp_status_error status; 4855341618Scy 4856341618Scy conf = dpp_build_conf_obj(auth, ap); 4857341618Scy if (conf) { 4858341618Scy wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", 4859341618Scy wpabuf_head(conf), wpabuf_len(conf)); 4860341618Scy } 4861341618Scy status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE; 4862346981Scy auth->conf_resp_status = status; 4863341618Scy 4864341618Scy /* { E-nonce, configurationObject}ke */ 4865341618Scy clear_len = 4 + e_nonce_len; 4866341618Scy if (conf) 4867341618Scy clear_len += 4 + wpabuf_len(conf); 4868341618Scy clear = wpabuf_alloc(clear_len); 4869341618Scy attr_len = 4 + 1 + 4 + clear_len + AES_BLOCK_SIZE; 4870341618Scy#ifdef CONFIG_TESTING_OPTIONS 4871341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) 4872341618Scy attr_len += 5; 4873341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4874341618Scy msg = wpabuf_alloc(attr_len); 4875341618Scy if (!clear || !msg) 4876341618Scy goto fail; 4877341618Scy 4878341618Scy#ifdef CONFIG_TESTING_OPTIONS 4879341618Scy if (dpp_test == DPP_TEST_NO_E_NONCE_CONF_RESP) { 4880341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no E-nonce"); 4881341618Scy goto skip_e_nonce; 4882341618Scy } 4883341618Scy if (dpp_test == DPP_TEST_E_NONCE_MISMATCH_CONF_RESP) { 4884341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - E-nonce mismatch"); 4885341618Scy wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 4886341618Scy wpabuf_put_le16(clear, e_nonce_len); 4887341618Scy wpabuf_put_data(clear, e_nonce, e_nonce_len - 1); 4888341618Scy wpabuf_put_u8(clear, e_nonce[e_nonce_len - 1] ^ 0x01); 4889341618Scy goto skip_e_nonce; 4890341618Scy } 4891341618Scy if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_CONF_RESP) { 4892341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); 4893341618Scy goto skip_wrapped_data; 4894341618Scy } 4895341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4896341618Scy 4897341618Scy /* E-nonce */ 4898341618Scy wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 4899341618Scy wpabuf_put_le16(clear, e_nonce_len); 4900341618Scy wpabuf_put_data(clear, e_nonce, e_nonce_len); 4901341618Scy 4902341618Scy#ifdef CONFIG_TESTING_OPTIONS 4903341618Scyskip_e_nonce: 4904341618Scy if (dpp_test == DPP_TEST_NO_CONFIG_OBJ_CONF_RESP) { 4905341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - Config Object"); 4906341618Scy goto skip_config_obj; 4907341618Scy } 4908341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4909341618Scy 4910341618Scy if (conf) { 4911341618Scy wpabuf_put_le16(clear, DPP_ATTR_CONFIG_OBJ); 4912341618Scy wpabuf_put_le16(clear, wpabuf_len(conf)); 4913341618Scy wpabuf_put_buf(clear, conf); 4914341618Scy } 4915341618Scy 4916341618Scy#ifdef CONFIG_TESTING_OPTIONS 4917341618Scyskip_config_obj: 4918341618Scy if (dpp_test == DPP_TEST_NO_STATUS_CONF_RESP) { 4919341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - Status"); 4920341618Scy goto skip_status; 4921341618Scy } 4922341618Scy if (dpp_test == DPP_TEST_INVALID_STATUS_CONF_RESP) { 4923341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); 4924341618Scy status = 255; 4925341618Scy } 4926341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4927341618Scy 4928341618Scy /* DPP Status */ 4929341618Scy dpp_build_attr_status(msg, status); 4930341618Scy 4931341618Scy#ifdef CONFIG_TESTING_OPTIONS 4932341618Scyskip_status: 4933341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4934341618Scy 4935341618Scy addr[0] = wpabuf_head(msg); 4936341618Scy len[0] = wpabuf_len(msg); 4937341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); 4938341618Scy 4939341618Scy wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 4940341618Scy wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 4941341618Scy wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 4942341618Scy 4943341618Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); 4944341618Scy if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, 4945341618Scy wpabuf_head(clear), wpabuf_len(clear), 4946341618Scy 1, addr, len, wrapped) < 0) 4947341618Scy goto fail; 4948341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 4949341618Scy wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); 4950341618Scy 4951341618Scy#ifdef CONFIG_TESTING_OPTIONS 4952341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_CONF_RESP) { 4953341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); 4954341618Scy dpp_build_attr_status(msg, DPP_STATUS_OK); 4955341618Scy } 4956341618Scyskip_wrapped_data: 4957341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4958341618Scy 4959341618Scy wpa_hexdump_buf(MSG_DEBUG, 4960341618Scy "DPP: Configuration Response attributes", msg); 4961341618Scyout: 4962341618Scy wpabuf_free(conf); 4963341618Scy wpabuf_free(clear); 4964341618Scy 4965341618Scy return msg; 4966341618Scyfail: 4967341618Scy wpabuf_free(msg); 4968341618Scy msg = NULL; 4969341618Scy goto out; 4970341618Scy} 4971341618Scy 4972341618Scy 4973341618Scystruct wpabuf * 4974341618Scydpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start, 4975341618Scy size_t attr_len) 4976341618Scy{ 4977341618Scy const u8 *wrapped_data, *e_nonce, *config_attr; 4978341618Scy u16 wrapped_data_len, e_nonce_len, config_attr_len; 4979341618Scy u8 *unwrapped = NULL; 4980341618Scy size_t unwrapped_len = 0; 4981341618Scy struct wpabuf *resp = NULL; 4982341618Scy struct json_token *root = NULL, *token; 4983341618Scy int ap; 4984341618Scy 4985341618Scy#ifdef CONFIG_TESTING_OPTIONS 4986341618Scy if (dpp_test == DPP_TEST_STOP_AT_CONF_REQ) { 4987341618Scy wpa_printf(MSG_INFO, 4988341618Scy "DPP: TESTING - stop at Config Request"); 4989341618Scy return NULL; 4990341618Scy } 4991341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 4992341618Scy 4993341618Scy if (dpp_check_attrs(attr_start, attr_len) < 0) { 4994341618Scy dpp_auth_fail(auth, "Invalid attribute in config request"); 4995341618Scy return NULL; 4996341618Scy } 4997341618Scy 4998341618Scy wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, 4999341618Scy &wrapped_data_len); 5000341618Scy if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 5001341618Scy dpp_auth_fail(auth, 5002341618Scy "Missing or invalid required Wrapped Data attribute"); 5003341618Scy return NULL; 5004341618Scy } 5005341618Scy 5006341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 5007341618Scy wrapped_data, wrapped_data_len); 5008341618Scy unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 5009341618Scy unwrapped = os_malloc(unwrapped_len); 5010341618Scy if (!unwrapped) 5011341618Scy return NULL; 5012341618Scy if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, 5013341618Scy wrapped_data, wrapped_data_len, 5014341618Scy 0, NULL, NULL, unwrapped) < 0) { 5015341618Scy dpp_auth_fail(auth, "AES-SIV decryption failed"); 5016341618Scy goto fail; 5017341618Scy } 5018341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 5019341618Scy unwrapped, unwrapped_len); 5020341618Scy 5021341618Scy if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 5022341618Scy dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 5023341618Scy goto fail; 5024341618Scy } 5025341618Scy 5026341618Scy e_nonce = dpp_get_attr(unwrapped, unwrapped_len, 5027341618Scy DPP_ATTR_ENROLLEE_NONCE, 5028341618Scy &e_nonce_len); 5029341618Scy if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { 5030341618Scy dpp_auth_fail(auth, 5031341618Scy "Missing or invalid Enrollee Nonce attribute"); 5032341618Scy goto fail; 5033341618Scy } 5034341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); 5035346981Scy os_memcpy(auth->e_nonce, e_nonce, e_nonce_len); 5036341618Scy 5037341618Scy config_attr = dpp_get_attr(unwrapped, unwrapped_len, 5038341618Scy DPP_ATTR_CONFIG_ATTR_OBJ, 5039341618Scy &config_attr_len); 5040341618Scy if (!config_attr) { 5041341618Scy dpp_auth_fail(auth, 5042341618Scy "Missing or invalid Config Attributes attribute"); 5043341618Scy goto fail; 5044341618Scy } 5045341618Scy wpa_hexdump_ascii(MSG_DEBUG, "DPP: Config Attributes", 5046341618Scy config_attr, config_attr_len); 5047341618Scy 5048341618Scy root = json_parse((const char *) config_attr, config_attr_len); 5049341618Scy if (!root) { 5050341618Scy dpp_auth_fail(auth, "Could not parse Config Attributes"); 5051341618Scy goto fail; 5052341618Scy } 5053341618Scy 5054341618Scy token = json_get_member(root, "name"); 5055341618Scy if (!token || token->type != JSON_STRING) { 5056341618Scy dpp_auth_fail(auth, "No Config Attributes - name"); 5057341618Scy goto fail; 5058341618Scy } 5059341618Scy wpa_printf(MSG_DEBUG, "DPP: Enrollee name = '%s'", token->string); 5060341618Scy 5061341618Scy token = json_get_member(root, "wi-fi_tech"); 5062341618Scy if (!token || token->type != JSON_STRING) { 5063341618Scy dpp_auth_fail(auth, "No Config Attributes - wi-fi_tech"); 5064341618Scy goto fail; 5065341618Scy } 5066341618Scy wpa_printf(MSG_DEBUG, "DPP: wi-fi_tech = '%s'", token->string); 5067341618Scy if (os_strcmp(token->string, "infra") != 0) { 5068341618Scy wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech '%s'", 5069341618Scy token->string); 5070341618Scy dpp_auth_fail(auth, "Unsupported wi-fi_tech"); 5071341618Scy goto fail; 5072341618Scy } 5073341618Scy 5074341618Scy token = json_get_member(root, "netRole"); 5075341618Scy if (!token || token->type != JSON_STRING) { 5076341618Scy dpp_auth_fail(auth, "No Config Attributes - netRole"); 5077341618Scy goto fail; 5078341618Scy } 5079341618Scy wpa_printf(MSG_DEBUG, "DPP: netRole = '%s'", token->string); 5080341618Scy if (os_strcmp(token->string, "sta") == 0) { 5081341618Scy ap = 0; 5082341618Scy } else if (os_strcmp(token->string, "ap") == 0) { 5083341618Scy ap = 1; 5084341618Scy } else { 5085341618Scy wpa_printf(MSG_DEBUG, "DPP: Unsupported netRole '%s'", 5086341618Scy token->string); 5087341618Scy dpp_auth_fail(auth, "Unsupported netRole"); 5088341618Scy goto fail; 5089341618Scy } 5090341618Scy 5091341618Scy resp = dpp_build_conf_resp(auth, e_nonce, e_nonce_len, ap); 5092341618Scy 5093341618Scyfail: 5094341618Scy json_free(root); 5095341618Scy os_free(unwrapped); 5096341618Scy return resp; 5097341618Scy} 5098341618Scy 5099341618Scy 5100341618Scystatic struct wpabuf * 5101341618Scydpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve, 5102341618Scy const u8 *prot_hdr, u16 prot_hdr_len, 5103341618Scy const EVP_MD **ret_md) 5104341618Scy{ 5105341618Scy struct json_token *root, *token; 5106341618Scy struct wpabuf *kid = NULL; 5107341618Scy 5108341618Scy root = json_parse((const char *) prot_hdr, prot_hdr_len); 5109341618Scy if (!root) { 5110341618Scy wpa_printf(MSG_DEBUG, 5111341618Scy "DPP: JSON parsing failed for JWS Protected Header"); 5112341618Scy goto fail; 5113341618Scy } 5114341618Scy 5115341618Scy if (root->type != JSON_OBJECT) { 5116341618Scy wpa_printf(MSG_DEBUG, 5117341618Scy "DPP: JWS Protected Header root is not an object"); 5118341618Scy goto fail; 5119341618Scy } 5120341618Scy 5121341618Scy token = json_get_member(root, "typ"); 5122341618Scy if (!token || token->type != JSON_STRING) { 5123341618Scy wpa_printf(MSG_DEBUG, "DPP: No typ string value found"); 5124341618Scy goto fail; 5125341618Scy } 5126341618Scy wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s", 5127341618Scy token->string); 5128341618Scy if (os_strcmp(token->string, "dppCon") != 0) { 5129341618Scy wpa_printf(MSG_DEBUG, 5130341618Scy "DPP: Unsupported JWS Protected Header typ=%s", 5131341618Scy token->string); 5132341618Scy goto fail; 5133341618Scy } 5134341618Scy 5135341618Scy token = json_get_member(root, "alg"); 5136341618Scy if (!token || token->type != JSON_STRING) { 5137341618Scy wpa_printf(MSG_DEBUG, "DPP: No alg string value found"); 5138341618Scy goto fail; 5139341618Scy } 5140341618Scy wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s", 5141341618Scy token->string); 5142341618Scy if (os_strcmp(token->string, curve->jws_alg) != 0) { 5143341618Scy wpa_printf(MSG_DEBUG, 5144341618Scy "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)", 5145341618Scy token->string, curve->jws_alg); 5146341618Scy goto fail; 5147341618Scy } 5148341618Scy if (os_strcmp(token->string, "ES256") == 0 || 5149341618Scy os_strcmp(token->string, "BS256") == 0) 5150341618Scy *ret_md = EVP_sha256(); 5151341618Scy else if (os_strcmp(token->string, "ES384") == 0 || 5152341618Scy os_strcmp(token->string, "BS384") == 0) 5153341618Scy *ret_md = EVP_sha384(); 5154341618Scy else if (os_strcmp(token->string, "ES512") == 0 || 5155341618Scy os_strcmp(token->string, "BS512") == 0) 5156341618Scy *ret_md = EVP_sha512(); 5157341618Scy else 5158341618Scy *ret_md = NULL; 5159341618Scy if (!*ret_md) { 5160341618Scy wpa_printf(MSG_DEBUG, 5161341618Scy "DPP: Unsupported JWS Protected Header alg=%s", 5162341618Scy token->string); 5163341618Scy goto fail; 5164341618Scy } 5165341618Scy 5166341618Scy kid = json_get_member_base64url(root, "kid"); 5167341618Scy if (!kid) { 5168341618Scy wpa_printf(MSG_DEBUG, "DPP: No kid string value found"); 5169341618Scy goto fail; 5170341618Scy } 5171341618Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)", 5172341618Scy kid); 5173341618Scy 5174341618Scyfail: 5175341618Scy json_free(root); 5176341618Scy return kid; 5177341618Scy} 5178341618Scy 5179341618Scy 5180341618Scystatic int dpp_parse_cred_legacy(struct dpp_authentication *auth, 5181341618Scy struct json_token *cred) 5182341618Scy{ 5183341618Scy struct json_token *pass, *psk_hex; 5184341618Scy 5185341618Scy wpa_printf(MSG_DEBUG, "DPP: Legacy akm=psk credential"); 5186341618Scy 5187341618Scy pass = json_get_member(cred, "pass"); 5188341618Scy psk_hex = json_get_member(cred, "psk_hex"); 5189341618Scy 5190341618Scy if (pass && pass->type == JSON_STRING) { 5191341618Scy size_t len = os_strlen(pass->string); 5192341618Scy 5193341618Scy wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Legacy passphrase", 5194341618Scy pass->string, len); 5195341618Scy if (len < 8 || len > 63) 5196341618Scy return -1; 5197341618Scy os_strlcpy(auth->passphrase, pass->string, 5198341618Scy sizeof(auth->passphrase)); 5199341618Scy } else if (psk_hex && psk_hex->type == JSON_STRING) { 5200346981Scy if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) { 5201341618Scy wpa_printf(MSG_DEBUG, 5202341618Scy "DPP: Unexpected psk_hex with akm=sae"); 5203341618Scy return -1; 5204341618Scy } 5205341618Scy if (os_strlen(psk_hex->string) != PMK_LEN * 2 || 5206341618Scy hexstr2bin(psk_hex->string, auth->psk, PMK_LEN) < 0) { 5207341618Scy wpa_printf(MSG_DEBUG, "DPP: Invalid psk_hex encoding"); 5208341618Scy return -1; 5209341618Scy } 5210341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: Legacy PSK", 5211341618Scy auth->psk, PMK_LEN); 5212341618Scy auth->psk_set = 1; 5213341618Scy } else { 5214341618Scy wpa_printf(MSG_DEBUG, "DPP: No pass or psk_hex strings found"); 5215341618Scy return -1; 5216341618Scy } 5217341618Scy 5218346981Scy if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) { 5219341618Scy wpa_printf(MSG_DEBUG, "DPP: No pass for sae found"); 5220341618Scy return -1; 5221341618Scy } 5222341618Scy 5223341618Scy return 0; 5224341618Scy} 5225341618Scy 5226341618Scy 5227341618Scystatic EVP_PKEY * dpp_parse_jwk(struct json_token *jwk, 5228341618Scy const struct dpp_curve_params **key_curve) 5229341618Scy{ 5230341618Scy struct json_token *token; 5231341618Scy const struct dpp_curve_params *curve; 5232341618Scy struct wpabuf *x = NULL, *y = NULL; 5233341618Scy EC_GROUP *group; 5234341618Scy EVP_PKEY *pkey = NULL; 5235341618Scy 5236341618Scy token = json_get_member(jwk, "kty"); 5237341618Scy if (!token || token->type != JSON_STRING) { 5238341618Scy wpa_printf(MSG_DEBUG, "DPP: No kty in JWK"); 5239341618Scy goto fail; 5240341618Scy } 5241341618Scy if (os_strcmp(token->string, "EC") != 0) { 5242341618Scy wpa_printf(MSG_DEBUG, "DPP: Unexpected JWK kty '%s'", 5243341618Scy token->string); 5244341618Scy goto fail; 5245341618Scy } 5246341618Scy 5247341618Scy token = json_get_member(jwk, "crv"); 5248341618Scy if (!token || token->type != JSON_STRING) { 5249341618Scy wpa_printf(MSG_DEBUG, "DPP: No crv in JWK"); 5250341618Scy goto fail; 5251341618Scy } 5252341618Scy curve = dpp_get_curve_jwk_crv(token->string); 5253341618Scy if (!curve) { 5254341618Scy wpa_printf(MSG_DEBUG, "DPP: Unsupported JWK crv '%s'", 5255341618Scy token->string); 5256341618Scy goto fail; 5257341618Scy } 5258341618Scy 5259341618Scy x = json_get_member_base64url(jwk, "x"); 5260341618Scy if (!x) { 5261341618Scy wpa_printf(MSG_DEBUG, "DPP: No x in JWK"); 5262341618Scy goto fail; 5263341618Scy } 5264341618Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK x", x); 5265341618Scy if (wpabuf_len(x) != curve->prime_len) { 5266341618Scy wpa_printf(MSG_DEBUG, 5267341618Scy "DPP: Unexpected JWK x length %u (expected %u for curve %s)", 5268341618Scy (unsigned int) wpabuf_len(x), 5269341618Scy (unsigned int) curve->prime_len, curve->name); 5270341618Scy goto fail; 5271341618Scy } 5272341618Scy 5273341618Scy y = json_get_member_base64url(jwk, "y"); 5274341618Scy if (!y) { 5275341618Scy wpa_printf(MSG_DEBUG, "DPP: No y in JWK"); 5276341618Scy goto fail; 5277341618Scy } 5278341618Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: JWK y", y); 5279341618Scy if (wpabuf_len(y) != curve->prime_len) { 5280341618Scy wpa_printf(MSG_DEBUG, 5281341618Scy "DPP: Unexpected JWK y length %u (expected %u for curve %s)", 5282341618Scy (unsigned int) wpabuf_len(y), 5283341618Scy (unsigned int) curve->prime_len, curve->name); 5284341618Scy goto fail; 5285341618Scy } 5286341618Scy 5287341618Scy group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); 5288341618Scy if (!group) { 5289341618Scy wpa_printf(MSG_DEBUG, "DPP: Could not prepare group for JWK"); 5290341618Scy goto fail; 5291341618Scy } 5292341618Scy 5293341618Scy pkey = dpp_set_pubkey_point_group(group, wpabuf_head(x), wpabuf_head(y), 5294341618Scy wpabuf_len(x)); 5295351611Scy EC_GROUP_free(group); 5296341618Scy *key_curve = curve; 5297341618Scy 5298341618Scyfail: 5299341618Scy wpabuf_free(x); 5300341618Scy wpabuf_free(y); 5301341618Scy 5302341618Scy return pkey; 5303341618Scy} 5304341618Scy 5305341618Scy 5306341618Scyint dpp_key_expired(const char *timestamp, os_time_t *expiry) 5307341618Scy{ 5308341618Scy struct os_time now; 5309341618Scy unsigned int year, month, day, hour, min, sec; 5310341618Scy os_time_t utime; 5311341618Scy const char *pos; 5312341618Scy 5313341618Scy /* ISO 8601 date and time: 5314341618Scy * <date>T<time> 5315341618Scy * YYYY-MM-DDTHH:MM:SSZ 5316341618Scy * YYYY-MM-DDTHH:MM:SS+03:00 5317341618Scy */ 5318341618Scy if (os_strlen(timestamp) < 19) { 5319341618Scy wpa_printf(MSG_DEBUG, 5320341618Scy "DPP: Too short timestamp - assume expired key"); 5321341618Scy return 1; 5322341618Scy } 5323341618Scy if (sscanf(timestamp, "%04u-%02u-%02uT%02u:%02u:%02u", 5324341618Scy &year, &month, &day, &hour, &min, &sec) != 6) { 5325341618Scy wpa_printf(MSG_DEBUG, 5326341618Scy "DPP: Failed to parse expiration day - assume expired key"); 5327341618Scy return 1; 5328341618Scy } 5329341618Scy 5330341618Scy if (os_mktime(year, month, day, hour, min, sec, &utime) < 0) { 5331341618Scy wpa_printf(MSG_DEBUG, 5332341618Scy "DPP: Invalid date/time information - assume expired key"); 5333341618Scy return 1; 5334341618Scy } 5335341618Scy 5336341618Scy pos = timestamp + 19; 5337341618Scy if (*pos == 'Z' || *pos == '\0') { 5338341618Scy /* In UTC - no need to adjust */ 5339341618Scy } else if (*pos == '-' || *pos == '+') { 5340341618Scy int items; 5341341618Scy 5342341618Scy /* Adjust local time to UTC */ 5343341618Scy items = sscanf(pos + 1, "%02u:%02u", &hour, &min); 5344341618Scy if (items < 1) { 5345341618Scy wpa_printf(MSG_DEBUG, 5346341618Scy "DPP: Invalid time zone designator (%s) - assume expired key", 5347341618Scy pos); 5348341618Scy return 1; 5349341618Scy } 5350341618Scy if (*pos == '-') 5351341618Scy utime += 3600 * hour; 5352341618Scy if (*pos == '+') 5353341618Scy utime -= 3600 * hour; 5354341618Scy if (items > 1) { 5355341618Scy if (*pos == '-') 5356341618Scy utime += 60 * min; 5357341618Scy if (*pos == '+') 5358341618Scy utime -= 60 * min; 5359341618Scy } 5360341618Scy } else { 5361341618Scy wpa_printf(MSG_DEBUG, 5362341618Scy "DPP: Invalid time zone designator (%s) - assume expired key", 5363341618Scy pos); 5364341618Scy return 1; 5365341618Scy } 5366341618Scy if (expiry) 5367341618Scy *expiry = utime; 5368341618Scy 5369341618Scy if (os_get_time(&now) < 0) { 5370341618Scy wpa_printf(MSG_DEBUG, 5371341618Scy "DPP: Cannot get current time - assume expired key"); 5372341618Scy return 1; 5373341618Scy } 5374341618Scy 5375341618Scy if (now.sec > utime) { 5376341618Scy wpa_printf(MSG_DEBUG, "DPP: Key has expired (%lu < %lu)", 5377341618Scy utime, now.sec); 5378341618Scy return 1; 5379341618Scy } 5380341618Scy 5381341618Scy return 0; 5382341618Scy} 5383341618Scy 5384341618Scy 5385341618Scystatic int dpp_parse_connector(struct dpp_authentication *auth, 5386341618Scy const unsigned char *payload, 5387341618Scy u16 payload_len) 5388341618Scy{ 5389341618Scy struct json_token *root, *groups, *netkey, *token; 5390341618Scy int ret = -1; 5391341618Scy EVP_PKEY *key = NULL; 5392341618Scy const struct dpp_curve_params *curve; 5393341618Scy unsigned int rules = 0; 5394341618Scy 5395341618Scy root = json_parse((const char *) payload, payload_len); 5396341618Scy if (!root) { 5397341618Scy wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed"); 5398341618Scy goto fail; 5399341618Scy } 5400341618Scy 5401341618Scy groups = json_get_member(root, "groups"); 5402341618Scy if (!groups || groups->type != JSON_ARRAY) { 5403341618Scy wpa_printf(MSG_DEBUG, "DPP: No groups array found"); 5404341618Scy goto skip_groups; 5405341618Scy } 5406341618Scy for (token = groups->child; token; token = token->sibling) { 5407341618Scy struct json_token *id, *role; 5408341618Scy 5409341618Scy id = json_get_member(token, "groupId"); 5410341618Scy if (!id || id->type != JSON_STRING) { 5411341618Scy wpa_printf(MSG_DEBUG, "DPP: Missing groupId string"); 5412341618Scy goto fail; 5413341618Scy } 5414341618Scy 5415341618Scy role = json_get_member(token, "netRole"); 5416341618Scy if (!role || role->type != JSON_STRING) { 5417341618Scy wpa_printf(MSG_DEBUG, "DPP: Missing netRole string"); 5418341618Scy goto fail; 5419341618Scy } 5420341618Scy wpa_printf(MSG_DEBUG, 5421341618Scy "DPP: connector group: groupId='%s' netRole='%s'", 5422341618Scy id->string, role->string); 5423341618Scy rules++; 5424341618Scy } 5425341618Scyskip_groups: 5426341618Scy 5427341618Scy if (!rules) { 5428341618Scy wpa_printf(MSG_DEBUG, 5429341618Scy "DPP: Connector includes no groups"); 5430341618Scy goto fail; 5431341618Scy } 5432341618Scy 5433341618Scy token = json_get_member(root, "expiry"); 5434341618Scy if (!token || token->type != JSON_STRING) { 5435341618Scy wpa_printf(MSG_DEBUG, 5436341618Scy "DPP: No expiry string found - connector does not expire"); 5437341618Scy } else { 5438341618Scy wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string); 5439341618Scy if (dpp_key_expired(token->string, 5440341618Scy &auth->net_access_key_expiry)) { 5441341618Scy wpa_printf(MSG_DEBUG, 5442341618Scy "DPP: Connector (netAccessKey) has expired"); 5443341618Scy goto fail; 5444341618Scy } 5445341618Scy } 5446341618Scy 5447341618Scy netkey = json_get_member(root, "netAccessKey"); 5448341618Scy if (!netkey || netkey->type != JSON_OBJECT) { 5449341618Scy wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); 5450341618Scy goto fail; 5451341618Scy } 5452341618Scy 5453341618Scy key = dpp_parse_jwk(netkey, &curve); 5454341618Scy if (!key) 5455341618Scy goto fail; 5456341618Scy dpp_debug_print_key("DPP: Received netAccessKey", key); 5457341618Scy 5458341618Scy if (EVP_PKEY_cmp(key, auth->own_protocol_key) != 1) { 5459341618Scy wpa_printf(MSG_DEBUG, 5460341618Scy "DPP: netAccessKey in connector does not match own protocol key"); 5461341618Scy#ifdef CONFIG_TESTING_OPTIONS 5462341618Scy if (auth->ignore_netaccesskey_mismatch) { 5463341618Scy wpa_printf(MSG_DEBUG, 5464341618Scy "DPP: TESTING - skip netAccessKey mismatch"); 5465341618Scy } else { 5466341618Scy goto fail; 5467341618Scy } 5468341618Scy#else /* CONFIG_TESTING_OPTIONS */ 5469341618Scy goto fail; 5470341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 5471341618Scy } 5472341618Scy 5473341618Scy ret = 0; 5474341618Scyfail: 5475341618Scy EVP_PKEY_free(key); 5476341618Scy json_free(root); 5477341618Scy return ret; 5478341618Scy} 5479341618Scy 5480341618Scy 5481341618Scystatic int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash) 5482341618Scy{ 5483341618Scy struct wpabuf *uncomp; 5484341618Scy int res; 5485341618Scy u8 hash[SHA256_MAC_LEN]; 5486341618Scy const u8 *addr[1]; 5487341618Scy size_t len[1]; 5488341618Scy 5489341618Scy if (wpabuf_len(r_hash) != SHA256_MAC_LEN) 5490341618Scy return -1; 5491341618Scy uncomp = dpp_get_pubkey_point(pub, 1); 5492341618Scy if (!uncomp) 5493341618Scy return -1; 5494341618Scy addr[0] = wpabuf_head(uncomp); 5495341618Scy len[0] = wpabuf_len(uncomp); 5496341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key", 5497341618Scy addr[0], len[0]); 5498341618Scy res = sha256_vector(1, addr, len, hash); 5499341618Scy wpabuf_free(uncomp); 5500341618Scy if (res < 0) 5501341618Scy return -1; 5502341618Scy if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) { 5503341618Scy wpa_printf(MSG_DEBUG, 5504341618Scy "DPP: Received hash value does not match calculated public key hash value"); 5505341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash", 5506341618Scy hash, SHA256_MAC_LEN); 5507341618Scy return -1; 5508341618Scy } 5509341618Scy return 0; 5510341618Scy} 5511341618Scy 5512341618Scy 5513341618Scystatic void dpp_copy_csign(struct dpp_authentication *auth, EVP_PKEY *csign) 5514341618Scy{ 5515341618Scy unsigned char *der = NULL; 5516341618Scy int der_len; 5517341618Scy 5518341618Scy der_len = i2d_PUBKEY(csign, &der); 5519341618Scy if (der_len <= 0) 5520341618Scy return; 5521341618Scy wpabuf_free(auth->c_sign_key); 5522341618Scy auth->c_sign_key = wpabuf_alloc_copy(der, der_len); 5523341618Scy OPENSSL_free(der); 5524341618Scy} 5525341618Scy 5526341618Scy 5527341618Scystatic void dpp_copy_netaccesskey(struct dpp_authentication *auth) 5528341618Scy{ 5529341618Scy unsigned char *der = NULL; 5530341618Scy int der_len; 5531341618Scy EC_KEY *eckey; 5532341618Scy 5533341618Scy eckey = EVP_PKEY_get1_EC_KEY(auth->own_protocol_key); 5534341618Scy if (!eckey) 5535341618Scy return; 5536341618Scy 5537341618Scy der_len = i2d_ECPrivateKey(eckey, &der); 5538341618Scy if (der_len <= 0) { 5539341618Scy EC_KEY_free(eckey); 5540341618Scy return; 5541341618Scy } 5542341618Scy wpabuf_free(auth->net_access_key); 5543341618Scy auth->net_access_key = wpabuf_alloc_copy(der, der_len); 5544341618Scy OPENSSL_free(der); 5545341618Scy EC_KEY_free(eckey); 5546341618Scy} 5547341618Scy 5548341618Scy 5549341618Scystruct dpp_signed_connector_info { 5550341618Scy unsigned char *payload; 5551341618Scy size_t payload_len; 5552341618Scy}; 5553341618Scy 5554341618Scystatic enum dpp_status_error 5555341618Scydpp_process_signed_connector(struct dpp_signed_connector_info *info, 5556341618Scy EVP_PKEY *csign_pub, const char *connector) 5557341618Scy{ 5558341618Scy enum dpp_status_error ret = 255; 5559341618Scy const char *pos, *end, *signed_start, *signed_end; 5560341618Scy struct wpabuf *kid = NULL; 5561341618Scy unsigned char *prot_hdr = NULL, *signature = NULL; 5562341618Scy size_t prot_hdr_len = 0, signature_len = 0; 5563341618Scy const EVP_MD *sign_md = NULL; 5564341618Scy unsigned char *der = NULL; 5565341618Scy int der_len; 5566341618Scy int res; 5567341618Scy EVP_MD_CTX *md_ctx = NULL; 5568341618Scy ECDSA_SIG *sig = NULL; 5569341618Scy BIGNUM *r = NULL, *s = NULL; 5570341618Scy const struct dpp_curve_params *curve; 5571341618Scy EC_KEY *eckey; 5572341618Scy const EC_GROUP *group; 5573341618Scy int nid; 5574341618Scy 5575341618Scy eckey = EVP_PKEY_get1_EC_KEY(csign_pub); 5576341618Scy if (!eckey) 5577341618Scy goto fail; 5578341618Scy group = EC_KEY_get0_group(eckey); 5579341618Scy if (!group) 5580341618Scy goto fail; 5581341618Scy nid = EC_GROUP_get_curve_name(group); 5582341618Scy curve = dpp_get_curve_nid(nid); 5583341618Scy if (!curve) 5584341618Scy goto fail; 5585341618Scy wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv); 5586341618Scy os_memset(info, 0, sizeof(*info)); 5587341618Scy 5588341618Scy signed_start = pos = connector; 5589341618Scy end = os_strchr(pos, '.'); 5590341618Scy if (!end) { 5591341618Scy wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector"); 5592341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 5593341618Scy goto fail; 5594341618Scy } 5595341618Scy prot_hdr = base64_url_decode((const unsigned char *) pos, 5596341618Scy end - pos, &prot_hdr_len); 5597341618Scy if (!prot_hdr) { 5598341618Scy wpa_printf(MSG_DEBUG, 5599341618Scy "DPP: Failed to base64url decode signedConnector JWS Protected Header"); 5600341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 5601341618Scy goto fail; 5602341618Scy } 5603341618Scy wpa_hexdump_ascii(MSG_DEBUG, 5604341618Scy "DPP: signedConnector - JWS Protected Header", 5605341618Scy prot_hdr, prot_hdr_len); 5606341618Scy kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md); 5607341618Scy if (!kid) { 5608341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 5609341618Scy goto fail; 5610341618Scy } 5611341618Scy if (wpabuf_len(kid) != SHA256_MAC_LEN) { 5612341618Scy wpa_printf(MSG_DEBUG, 5613341618Scy "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)", 5614341618Scy (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN); 5615341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 5616341618Scy goto fail; 5617341618Scy } 5618341618Scy 5619341618Scy pos = end + 1; 5620341618Scy end = os_strchr(pos, '.'); 5621341618Scy if (!end) { 5622341618Scy wpa_printf(MSG_DEBUG, 5623341618Scy "DPP: Missing dot(2) in signedConnector"); 5624341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 5625341618Scy goto fail; 5626341618Scy } 5627341618Scy signed_end = end - 1; 5628341618Scy info->payload = base64_url_decode((const unsigned char *) pos, 5629341618Scy end - pos, &info->payload_len); 5630341618Scy if (!info->payload) { 5631341618Scy wpa_printf(MSG_DEBUG, 5632341618Scy "DPP: Failed to base64url decode signedConnector JWS Payload"); 5633341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 5634341618Scy goto fail; 5635341618Scy } 5636341618Scy wpa_hexdump_ascii(MSG_DEBUG, 5637341618Scy "DPP: signedConnector - JWS Payload", 5638341618Scy info->payload, info->payload_len); 5639341618Scy pos = end + 1; 5640341618Scy signature = base64_url_decode((const unsigned char *) pos, 5641341618Scy os_strlen(pos), &signature_len); 5642341618Scy if (!signature) { 5643341618Scy wpa_printf(MSG_DEBUG, 5644341618Scy "DPP: Failed to base64url decode signedConnector signature"); 5645341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 5646341618Scy goto fail; 5647341618Scy } 5648341618Scy wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature", 5649341618Scy signature, signature_len); 5650341618Scy 5651341618Scy if (dpp_check_pubkey_match(csign_pub, kid) < 0) { 5652341618Scy ret = DPP_STATUS_NO_MATCH; 5653341618Scy goto fail; 5654341618Scy } 5655341618Scy 5656341618Scy if (signature_len & 0x01) { 5657341618Scy wpa_printf(MSG_DEBUG, 5658341618Scy "DPP: Unexpected signedConnector signature length (%d)", 5659341618Scy (int) signature_len); 5660341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 5661341618Scy goto fail; 5662341618Scy } 5663341618Scy 5664341618Scy /* JWS Signature encodes the signature (r,s) as two octet strings. Need 5665341618Scy * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */ 5666341618Scy r = BN_bin2bn(signature, signature_len / 2, NULL); 5667341618Scy s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL); 5668341618Scy sig = ECDSA_SIG_new(); 5669341618Scy if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1) 5670341618Scy goto fail; 5671341618Scy r = NULL; 5672341618Scy s = NULL; 5673341618Scy 5674341618Scy der_len = i2d_ECDSA_SIG(sig, &der); 5675341618Scy if (der_len <= 0) { 5676341618Scy wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature"); 5677341618Scy goto fail; 5678341618Scy } 5679341618Scy wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len); 5680341618Scy md_ctx = EVP_MD_CTX_create(); 5681341618Scy if (!md_ctx) 5682341618Scy goto fail; 5683341618Scy 5684341618Scy ERR_clear_error(); 5685341618Scy if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) { 5686341618Scy wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s", 5687341618Scy ERR_error_string(ERR_get_error(), NULL)); 5688341618Scy goto fail; 5689341618Scy } 5690341618Scy if (EVP_DigestVerifyUpdate(md_ctx, signed_start, 5691341618Scy signed_end - signed_start + 1) != 1) { 5692341618Scy wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s", 5693341618Scy ERR_error_string(ERR_get_error(), NULL)); 5694341618Scy goto fail; 5695341618Scy } 5696341618Scy res = EVP_DigestVerifyFinal(md_ctx, der, der_len); 5697341618Scy if (res != 1) { 5698341618Scy wpa_printf(MSG_DEBUG, 5699341618Scy "DPP: EVP_DigestVerifyFinal failed (res=%d): %s", 5700341618Scy res, ERR_error_string(ERR_get_error(), NULL)); 5701341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 5702341618Scy goto fail; 5703341618Scy } 5704341618Scy 5705341618Scy ret = DPP_STATUS_OK; 5706341618Scyfail: 5707341618Scy EC_KEY_free(eckey); 5708341618Scy EVP_MD_CTX_destroy(md_ctx); 5709341618Scy os_free(prot_hdr); 5710341618Scy wpabuf_free(kid); 5711341618Scy os_free(signature); 5712341618Scy ECDSA_SIG_free(sig); 5713341618Scy BN_free(r); 5714341618Scy BN_free(s); 5715341618Scy OPENSSL_free(der); 5716341618Scy return ret; 5717341618Scy} 5718341618Scy 5719341618Scy 5720341618Scystatic int dpp_parse_cred_dpp(struct dpp_authentication *auth, 5721341618Scy struct json_token *cred) 5722341618Scy{ 5723341618Scy struct dpp_signed_connector_info info; 5724341618Scy struct json_token *token, *csign; 5725341618Scy int ret = -1; 5726341618Scy EVP_PKEY *csign_pub = NULL; 5727341618Scy const struct dpp_curve_params *key_curve = NULL; 5728341618Scy const char *signed_connector; 5729341618Scy 5730341618Scy os_memset(&info, 0, sizeof(info)); 5731341618Scy 5732346981Scy if (dpp_akm_psk(auth->akm) || dpp_akm_sae(auth->akm)) { 5733346981Scy wpa_printf(MSG_DEBUG, 5734346981Scy "DPP: Legacy credential included in Connector credential"); 5735346981Scy if (dpp_parse_cred_legacy(auth, cred) < 0) 5736346981Scy return -1; 5737346981Scy } 5738346981Scy 5739341618Scy wpa_printf(MSG_DEBUG, "DPP: Connector credential"); 5740341618Scy 5741341618Scy csign = json_get_member(cred, "csign"); 5742341618Scy if (!csign || csign->type != JSON_OBJECT) { 5743341618Scy wpa_printf(MSG_DEBUG, "DPP: No csign JWK in JSON"); 5744341618Scy goto fail; 5745341618Scy } 5746341618Scy 5747341618Scy csign_pub = dpp_parse_jwk(csign, &key_curve); 5748341618Scy if (!csign_pub) { 5749341618Scy wpa_printf(MSG_DEBUG, "DPP: Failed to parse csign JWK"); 5750341618Scy goto fail; 5751341618Scy } 5752341618Scy dpp_debug_print_key("DPP: Received C-sign-key", csign_pub); 5753341618Scy 5754341618Scy token = json_get_member(cred, "signedConnector"); 5755341618Scy if (!token || token->type != JSON_STRING) { 5756341618Scy wpa_printf(MSG_DEBUG, "DPP: No signedConnector string found"); 5757341618Scy goto fail; 5758341618Scy } 5759341618Scy wpa_hexdump_ascii(MSG_DEBUG, "DPP: signedConnector", 5760341618Scy token->string, os_strlen(token->string)); 5761341618Scy signed_connector = token->string; 5762341618Scy 5763341618Scy if (os_strchr(signed_connector, '"') || 5764341618Scy os_strchr(signed_connector, '\n')) { 5765341618Scy wpa_printf(MSG_DEBUG, 5766341618Scy "DPP: Unexpected character in signedConnector"); 5767341618Scy goto fail; 5768341618Scy } 5769341618Scy 5770341618Scy if (dpp_process_signed_connector(&info, csign_pub, 5771341618Scy signed_connector) != DPP_STATUS_OK) 5772341618Scy goto fail; 5773341618Scy 5774341618Scy if (dpp_parse_connector(auth, info.payload, info.payload_len) < 0) { 5775341618Scy wpa_printf(MSG_DEBUG, "DPP: Failed to parse connector"); 5776341618Scy goto fail; 5777341618Scy } 5778341618Scy 5779341618Scy os_free(auth->connector); 5780341618Scy auth->connector = os_strdup(signed_connector); 5781341618Scy 5782341618Scy dpp_copy_csign(auth, csign_pub); 5783341618Scy dpp_copy_netaccesskey(auth); 5784341618Scy 5785341618Scy ret = 0; 5786341618Scyfail: 5787341618Scy EVP_PKEY_free(csign_pub); 5788341618Scy os_free(info.payload); 5789341618Scy return ret; 5790341618Scy} 5791341618Scy 5792341618Scy 5793341618Scyconst char * dpp_akm_str(enum dpp_akm akm) 5794341618Scy{ 5795341618Scy switch (akm) { 5796341618Scy case DPP_AKM_DPP: 5797341618Scy return "dpp"; 5798341618Scy case DPP_AKM_PSK: 5799341618Scy return "psk"; 5800341618Scy case DPP_AKM_SAE: 5801341618Scy return "sae"; 5802341618Scy case DPP_AKM_PSK_SAE: 5803341618Scy return "psk+sae"; 5804346981Scy case DPP_AKM_SAE_DPP: 5805346981Scy return "dpp+sae"; 5806346981Scy case DPP_AKM_PSK_SAE_DPP: 5807346981Scy return "dpp+psk+sae"; 5808341618Scy default: 5809341618Scy return "??"; 5810341618Scy } 5811341618Scy} 5812341618Scy 5813341618Scy 5814341618Scystatic enum dpp_akm dpp_akm_from_str(const char *akm) 5815341618Scy{ 5816341618Scy if (os_strcmp(akm, "psk") == 0) 5817341618Scy return DPP_AKM_PSK; 5818341618Scy if (os_strcmp(akm, "sae") == 0) 5819341618Scy return DPP_AKM_SAE; 5820341618Scy if (os_strcmp(akm, "psk+sae") == 0) 5821341618Scy return DPP_AKM_PSK_SAE; 5822341618Scy if (os_strcmp(akm, "dpp") == 0) 5823341618Scy return DPP_AKM_DPP; 5824346981Scy if (os_strcmp(akm, "dpp+sae") == 0) 5825346981Scy return DPP_AKM_SAE_DPP; 5826346981Scy if (os_strcmp(akm, "dpp+psk+sae") == 0) 5827346981Scy return DPP_AKM_PSK_SAE_DPP; 5828341618Scy return DPP_AKM_UNKNOWN; 5829341618Scy} 5830341618Scy 5831341618Scy 5832341618Scystatic int dpp_parse_conf_obj(struct dpp_authentication *auth, 5833341618Scy const u8 *conf_obj, u16 conf_obj_len) 5834341618Scy{ 5835341618Scy int ret = -1; 5836341618Scy struct json_token *root, *token, *discovery, *cred; 5837341618Scy 5838341618Scy root = json_parse((const char *) conf_obj, conf_obj_len); 5839341618Scy if (!root) 5840341618Scy return -1; 5841341618Scy if (root->type != JSON_OBJECT) { 5842341618Scy dpp_auth_fail(auth, "JSON root is not an object"); 5843341618Scy goto fail; 5844341618Scy } 5845341618Scy 5846341618Scy token = json_get_member(root, "wi-fi_tech"); 5847341618Scy if (!token || token->type != JSON_STRING) { 5848341618Scy dpp_auth_fail(auth, "No wi-fi_tech string value found"); 5849341618Scy goto fail; 5850341618Scy } 5851341618Scy if (os_strcmp(token->string, "infra") != 0) { 5852341618Scy wpa_printf(MSG_DEBUG, "DPP: Unsupported wi-fi_tech value: '%s'", 5853341618Scy token->string); 5854341618Scy dpp_auth_fail(auth, "Unsupported wi-fi_tech value"); 5855341618Scy goto fail; 5856341618Scy } 5857341618Scy 5858341618Scy discovery = json_get_member(root, "discovery"); 5859341618Scy if (!discovery || discovery->type != JSON_OBJECT) { 5860341618Scy dpp_auth_fail(auth, "No discovery object in JSON"); 5861341618Scy goto fail; 5862341618Scy } 5863341618Scy 5864341618Scy token = json_get_member(discovery, "ssid"); 5865341618Scy if (!token || token->type != JSON_STRING) { 5866341618Scy dpp_auth_fail(auth, "No discovery::ssid string value found"); 5867341618Scy goto fail; 5868341618Scy } 5869341618Scy wpa_hexdump_ascii(MSG_DEBUG, "DPP: discovery::ssid", 5870341618Scy token->string, os_strlen(token->string)); 5871341618Scy if (os_strlen(token->string) > SSID_MAX_LEN) { 5872341618Scy dpp_auth_fail(auth, "Too long discovery::ssid string value"); 5873341618Scy goto fail; 5874341618Scy } 5875341618Scy auth->ssid_len = os_strlen(token->string); 5876341618Scy os_memcpy(auth->ssid, token->string, auth->ssid_len); 5877341618Scy 5878341618Scy cred = json_get_member(root, "cred"); 5879341618Scy if (!cred || cred->type != JSON_OBJECT) { 5880341618Scy dpp_auth_fail(auth, "No cred object in JSON"); 5881341618Scy goto fail; 5882341618Scy } 5883341618Scy 5884341618Scy token = json_get_member(cred, "akm"); 5885341618Scy if (!token || token->type != JSON_STRING) { 5886341618Scy dpp_auth_fail(auth, "No cred::akm string value found"); 5887341618Scy goto fail; 5888341618Scy } 5889341618Scy auth->akm = dpp_akm_from_str(token->string); 5890341618Scy 5891346981Scy if (dpp_akm_legacy(auth->akm)) { 5892341618Scy if (dpp_parse_cred_legacy(auth, cred) < 0) 5893341618Scy goto fail; 5894346981Scy } else if (dpp_akm_dpp(auth->akm)) { 5895341618Scy if (dpp_parse_cred_dpp(auth, cred) < 0) 5896341618Scy goto fail; 5897341618Scy } else { 5898341618Scy wpa_printf(MSG_DEBUG, "DPP: Unsupported akm: %s", 5899341618Scy token->string); 5900341618Scy dpp_auth_fail(auth, "Unsupported akm"); 5901341618Scy goto fail; 5902341618Scy } 5903341618Scy 5904341618Scy wpa_printf(MSG_DEBUG, "DPP: JSON parsing completed successfully"); 5905341618Scy ret = 0; 5906341618Scyfail: 5907341618Scy json_free(root); 5908341618Scy return ret; 5909341618Scy} 5910341618Scy 5911341618Scy 5912341618Scyint dpp_conf_resp_rx(struct dpp_authentication *auth, 5913341618Scy const struct wpabuf *resp) 5914341618Scy{ 5915341618Scy const u8 *wrapped_data, *e_nonce, *status, *conf_obj; 5916341618Scy u16 wrapped_data_len, e_nonce_len, status_len, conf_obj_len; 5917341618Scy const u8 *addr[1]; 5918341618Scy size_t len[1]; 5919341618Scy u8 *unwrapped = NULL; 5920341618Scy size_t unwrapped_len = 0; 5921341618Scy int ret = -1; 5922341618Scy 5923346981Scy auth->conf_resp_status = 255; 5924346981Scy 5925341618Scy if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) { 5926341618Scy dpp_auth_fail(auth, "Invalid attribute in config response"); 5927341618Scy return -1; 5928341618Scy } 5929341618Scy 5930341618Scy wrapped_data = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp), 5931341618Scy DPP_ATTR_WRAPPED_DATA, 5932341618Scy &wrapped_data_len); 5933341618Scy if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 5934341618Scy dpp_auth_fail(auth, 5935341618Scy "Missing or invalid required Wrapped Data attribute"); 5936341618Scy return -1; 5937341618Scy } 5938341618Scy 5939341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 5940341618Scy wrapped_data, wrapped_data_len); 5941341618Scy unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 5942341618Scy unwrapped = os_malloc(unwrapped_len); 5943341618Scy if (!unwrapped) 5944341618Scy return -1; 5945341618Scy 5946341618Scy addr[0] = wpabuf_head(resp); 5947341618Scy len[0] = wrapped_data - 4 - (const u8 *) wpabuf_head(resp); 5948341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD", addr[0], len[0]); 5949341618Scy 5950341618Scy if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, 5951341618Scy wrapped_data, wrapped_data_len, 5952341618Scy 1, addr, len, unwrapped) < 0) { 5953341618Scy dpp_auth_fail(auth, "AES-SIV decryption failed"); 5954341618Scy goto fail; 5955341618Scy } 5956341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 5957341618Scy unwrapped, unwrapped_len); 5958341618Scy 5959341618Scy if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 5960341618Scy dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 5961341618Scy goto fail; 5962341618Scy } 5963341618Scy 5964341618Scy e_nonce = dpp_get_attr(unwrapped, unwrapped_len, 5965341618Scy DPP_ATTR_ENROLLEE_NONCE, 5966341618Scy &e_nonce_len); 5967341618Scy if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { 5968341618Scy dpp_auth_fail(auth, 5969341618Scy "Missing or invalid Enrollee Nonce attribute"); 5970341618Scy goto fail; 5971341618Scy } 5972341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); 5973341618Scy if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { 5974341618Scy dpp_auth_fail(auth, "Enrollee Nonce mismatch"); 5975341618Scy goto fail; 5976341618Scy } 5977341618Scy 5978341618Scy status = dpp_get_attr(wpabuf_head(resp), wpabuf_len(resp), 5979341618Scy DPP_ATTR_STATUS, &status_len); 5980341618Scy if (!status || status_len < 1) { 5981341618Scy dpp_auth_fail(auth, 5982341618Scy "Missing or invalid required DPP Status attribute"); 5983341618Scy goto fail; 5984341618Scy } 5985346981Scy auth->conf_resp_status = status[0]; 5986341618Scy wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); 5987341618Scy if (status[0] != DPP_STATUS_OK) { 5988341618Scy dpp_auth_fail(auth, "Configurator rejected configuration"); 5989341618Scy goto fail; 5990341618Scy } 5991341618Scy 5992341618Scy conf_obj = dpp_get_attr(unwrapped, unwrapped_len, 5993341618Scy DPP_ATTR_CONFIG_OBJ, &conf_obj_len); 5994341618Scy if (!conf_obj) { 5995341618Scy dpp_auth_fail(auth, 5996341618Scy "Missing required Configuration Object attribute"); 5997341618Scy goto fail; 5998341618Scy } 5999341618Scy wpa_hexdump_ascii(MSG_DEBUG, "DPP: configurationObject JSON", 6000341618Scy conf_obj, conf_obj_len); 6001341618Scy if (dpp_parse_conf_obj(auth, conf_obj, conf_obj_len) < 0) 6002341618Scy goto fail; 6003341618Scy 6004341618Scy ret = 0; 6005341618Scy 6006341618Scyfail: 6007341618Scy os_free(unwrapped); 6008341618Scy return ret; 6009341618Scy} 6010341618Scy 6011341618Scy 6012346981Scy#ifdef CONFIG_DPP2 6013346981Scyenum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth, 6014346981Scy const u8 *hdr, 6015346981Scy const u8 *attr_start, size_t attr_len) 6016346981Scy{ 6017346981Scy const u8 *wrapped_data, *status, *e_nonce; 6018346981Scy u16 wrapped_data_len, status_len, e_nonce_len; 6019346981Scy const u8 *addr[2]; 6020346981Scy size_t len[2]; 6021346981Scy u8 *unwrapped = NULL; 6022346981Scy size_t unwrapped_len = 0; 6023346981Scy enum dpp_status_error ret = 256; 6024346981Scy 6025346981Scy wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA, 6026346981Scy &wrapped_data_len); 6027346981Scy if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 6028346981Scy dpp_auth_fail(auth, 6029346981Scy "Missing or invalid required Wrapped Data attribute"); 6030346981Scy goto fail; 6031346981Scy } 6032346981Scy wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data", 6033346981Scy wrapped_data, wrapped_data_len); 6034346981Scy 6035346981Scy attr_len = wrapped_data - 4 - attr_start; 6036346981Scy 6037346981Scy addr[0] = hdr; 6038346981Scy len[0] = DPP_HDR_LEN; 6039346981Scy addr[1] = attr_start; 6040346981Scy len[1] = attr_len; 6041346981Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 6042346981Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 6043346981Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 6044346981Scy wrapped_data, wrapped_data_len); 6045346981Scy unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 6046346981Scy unwrapped = os_malloc(unwrapped_len); 6047346981Scy if (!unwrapped) 6048346981Scy goto fail; 6049346981Scy if (aes_siv_decrypt(auth->ke, auth->curve->hash_len, 6050346981Scy wrapped_data, wrapped_data_len, 6051346981Scy 2, addr, len, unwrapped) < 0) { 6052346981Scy dpp_auth_fail(auth, "AES-SIV decryption failed"); 6053346981Scy goto fail; 6054346981Scy } 6055346981Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 6056346981Scy unwrapped, unwrapped_len); 6057346981Scy 6058346981Scy if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 6059346981Scy dpp_auth_fail(auth, "Invalid attribute in unwrapped data"); 6060346981Scy goto fail; 6061346981Scy } 6062346981Scy 6063346981Scy e_nonce = dpp_get_attr(unwrapped, unwrapped_len, 6064346981Scy DPP_ATTR_ENROLLEE_NONCE, 6065346981Scy &e_nonce_len); 6066346981Scy if (!e_nonce || e_nonce_len != auth->curve->nonce_len) { 6067346981Scy dpp_auth_fail(auth, 6068346981Scy "Missing or invalid Enrollee Nonce attribute"); 6069346981Scy goto fail; 6070346981Scy } 6071346981Scy wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len); 6072346981Scy if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) { 6073346981Scy dpp_auth_fail(auth, "Enrollee Nonce mismatch"); 6074346981Scy wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce", 6075346981Scy auth->e_nonce, e_nonce_len); 6076346981Scy goto fail; 6077346981Scy } 6078346981Scy 6079346981Scy status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS, 6080346981Scy &status_len); 6081346981Scy if (!status || status_len < 1) { 6082346981Scy dpp_auth_fail(auth, 6083346981Scy "Missing or invalid required DPP Status attribute"); 6084346981Scy goto fail; 6085346981Scy } 6086346981Scy wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]); 6087346981Scy ret = status[0]; 6088346981Scy 6089346981Scyfail: 6090346981Scy bin_clear_free(unwrapped, unwrapped_len); 6091346981Scy return ret; 6092346981Scy} 6093346981Scy#endif /* CONFIG_DPP2 */ 6094346981Scy 6095346981Scy 6096346981Scystruct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth, 6097346981Scy enum dpp_status_error status) 6098346981Scy{ 6099346981Scy struct wpabuf *msg, *clear; 6100346981Scy size_t nonce_len, clear_len, attr_len; 6101346981Scy const u8 *addr[2]; 6102346981Scy size_t len[2]; 6103346981Scy u8 *wrapped; 6104346981Scy 6105346981Scy nonce_len = auth->curve->nonce_len; 6106346981Scy clear_len = 5 + 4 + nonce_len; 6107346981Scy attr_len = 4 + clear_len + AES_BLOCK_SIZE; 6108346981Scy clear = wpabuf_alloc(clear_len); 6109346981Scy msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len); 6110346981Scy if (!clear || !msg) 6111346981Scy return NULL; 6112346981Scy 6113346981Scy /* DPP Status */ 6114346981Scy dpp_build_attr_status(clear, status); 6115346981Scy 6116346981Scy /* E-nonce */ 6117346981Scy wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE); 6118346981Scy wpabuf_put_le16(clear, nonce_len); 6119346981Scy wpabuf_put_data(clear, auth->e_nonce, nonce_len); 6120346981Scy 6121346981Scy /* OUI, OUI type, Crypto Suite, DPP frame type */ 6122346981Scy addr[0] = wpabuf_head_u8(msg) + 2; 6123346981Scy len[0] = 3 + 1 + 1 + 1; 6124346981Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 6125346981Scy 6126346981Scy /* Attributes before Wrapped Data (none) */ 6127346981Scy addr[1] = wpabuf_put(msg, 0); 6128346981Scy len[1] = 0; 6129346981Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 6130346981Scy 6131346981Scy /* Wrapped Data */ 6132346981Scy wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 6133346981Scy wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 6134346981Scy wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 6135346981Scy 6136346981Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); 6137346981Scy if (aes_siv_encrypt(auth->ke, auth->curve->hash_len, 6138346981Scy wpabuf_head(clear), wpabuf_len(clear), 6139346981Scy 2, addr, len, wrapped) < 0) 6140346981Scy goto fail; 6141346981Scy 6142346981Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Result attributes", msg); 6143346981Scy wpabuf_free(clear); 6144346981Scy return msg; 6145346981Scyfail: 6146346981Scy wpabuf_free(clear); 6147346981Scy wpabuf_free(msg); 6148346981Scy return NULL; 6149346981Scy} 6150346981Scy 6151346981Scy 6152341618Scyvoid dpp_configurator_free(struct dpp_configurator *conf) 6153341618Scy{ 6154341618Scy if (!conf) 6155341618Scy return; 6156341618Scy EVP_PKEY_free(conf->csign); 6157341618Scy os_free(conf->kid); 6158341618Scy os_free(conf); 6159341618Scy} 6160341618Scy 6161341618Scy 6162341618Scyint dpp_configurator_get_key(const struct dpp_configurator *conf, char *buf, 6163341618Scy size_t buflen) 6164341618Scy{ 6165341618Scy EC_KEY *eckey; 6166341618Scy int key_len, ret = -1; 6167341618Scy unsigned char *key = NULL; 6168341618Scy 6169341618Scy if (!conf->csign) 6170341618Scy return -1; 6171341618Scy 6172341618Scy eckey = EVP_PKEY_get1_EC_KEY(conf->csign); 6173341618Scy if (!eckey) 6174341618Scy return -1; 6175341618Scy 6176341618Scy key_len = i2d_ECPrivateKey(eckey, &key); 6177341618Scy if (key_len > 0) 6178341618Scy ret = wpa_snprintf_hex(buf, buflen, key, key_len); 6179341618Scy 6180341618Scy EC_KEY_free(eckey); 6181341618Scy OPENSSL_free(key); 6182341618Scy return ret; 6183341618Scy} 6184341618Scy 6185341618Scy 6186341618Scystruct dpp_configurator * 6187341618Scydpp_keygen_configurator(const char *curve, const u8 *privkey, 6188341618Scy size_t privkey_len) 6189341618Scy{ 6190341618Scy struct dpp_configurator *conf; 6191341618Scy struct wpabuf *csign_pub = NULL; 6192341618Scy u8 kid_hash[SHA256_MAC_LEN]; 6193341618Scy const u8 *addr[1]; 6194341618Scy size_t len[1]; 6195341618Scy 6196341618Scy conf = os_zalloc(sizeof(*conf)); 6197341618Scy if (!conf) 6198341618Scy return NULL; 6199341618Scy 6200341618Scy if (!curve) { 6201341618Scy conf->curve = &dpp_curves[0]; 6202341618Scy } else { 6203341618Scy conf->curve = dpp_get_curve_name(curve); 6204341618Scy if (!conf->curve) { 6205341618Scy wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", 6206341618Scy curve); 6207341618Scy os_free(conf); 6208341618Scy return NULL; 6209341618Scy } 6210341618Scy } 6211341618Scy if (privkey) 6212341618Scy conf->csign = dpp_set_keypair(&conf->curve, privkey, 6213341618Scy privkey_len); 6214341618Scy else 6215341618Scy conf->csign = dpp_gen_keypair(conf->curve); 6216341618Scy if (!conf->csign) 6217341618Scy goto fail; 6218341618Scy conf->own = 1; 6219341618Scy 6220341618Scy csign_pub = dpp_get_pubkey_point(conf->csign, 1); 6221341618Scy if (!csign_pub) { 6222341618Scy wpa_printf(MSG_INFO, "DPP: Failed to extract C-sign-key"); 6223341618Scy goto fail; 6224341618Scy } 6225341618Scy 6226341618Scy /* kid = SHA256(ANSI X9.63 uncompressed C-sign-key) */ 6227341618Scy addr[0] = wpabuf_head(csign_pub); 6228341618Scy len[0] = wpabuf_len(csign_pub); 6229341618Scy if (sha256_vector(1, addr, len, kid_hash) < 0) { 6230341618Scy wpa_printf(MSG_DEBUG, 6231341618Scy "DPP: Failed to derive kid for C-sign-key"); 6232341618Scy goto fail; 6233341618Scy } 6234341618Scy 6235341618Scy conf->kid = (char *) base64_url_encode(kid_hash, sizeof(kid_hash), 6236341618Scy NULL, 0); 6237341618Scy if (!conf->kid) 6238341618Scy goto fail; 6239341618Scyout: 6240341618Scy wpabuf_free(csign_pub); 6241341618Scy return conf; 6242341618Scyfail: 6243341618Scy dpp_configurator_free(conf); 6244341618Scy conf = NULL; 6245341618Scy goto out; 6246341618Scy} 6247341618Scy 6248341618Scy 6249341618Scyint dpp_configurator_own_config(struct dpp_authentication *auth, 6250341618Scy const char *curve, int ap) 6251341618Scy{ 6252341618Scy struct wpabuf *conf_obj; 6253341618Scy int ret = -1; 6254341618Scy 6255341618Scy if (!auth->conf) { 6256341618Scy wpa_printf(MSG_DEBUG, "DPP: No configurator specified"); 6257341618Scy return -1; 6258341618Scy } 6259341618Scy 6260341618Scy if (!curve) { 6261341618Scy auth->curve = &dpp_curves[0]; 6262341618Scy } else { 6263341618Scy auth->curve = dpp_get_curve_name(curve); 6264341618Scy if (!auth->curve) { 6265341618Scy wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", 6266341618Scy curve); 6267341618Scy return -1; 6268341618Scy } 6269341618Scy } 6270341618Scy wpa_printf(MSG_DEBUG, 6271341618Scy "DPP: Building own configuration/connector with curve %s", 6272341618Scy auth->curve->name); 6273341618Scy 6274341618Scy auth->own_protocol_key = dpp_gen_keypair(auth->curve); 6275341618Scy if (!auth->own_protocol_key) 6276341618Scy return -1; 6277341618Scy dpp_copy_netaccesskey(auth); 6278341618Scy auth->peer_protocol_key = auth->own_protocol_key; 6279341618Scy dpp_copy_csign(auth, auth->conf->csign); 6280341618Scy 6281341618Scy conf_obj = dpp_build_conf_obj(auth, ap); 6282341618Scy if (!conf_obj) 6283341618Scy goto fail; 6284341618Scy ret = dpp_parse_conf_obj(auth, wpabuf_head(conf_obj), 6285341618Scy wpabuf_len(conf_obj)); 6286341618Scyfail: 6287341618Scy wpabuf_free(conf_obj); 6288341618Scy auth->peer_protocol_key = NULL; 6289341618Scy return ret; 6290341618Scy} 6291341618Scy 6292341618Scy 6293341618Scystatic int dpp_compatible_netrole(const char *role1, const char *role2) 6294341618Scy{ 6295341618Scy return (os_strcmp(role1, "sta") == 0 && os_strcmp(role2, "ap") == 0) || 6296341618Scy (os_strcmp(role1, "ap") == 0 && os_strcmp(role2, "sta") == 0); 6297341618Scy} 6298341618Scy 6299341618Scy 6300341618Scystatic int dpp_connector_compatible_group(struct json_token *root, 6301341618Scy const char *group_id, 6302341618Scy const char *net_role) 6303341618Scy{ 6304341618Scy struct json_token *groups, *token; 6305341618Scy 6306341618Scy groups = json_get_member(root, "groups"); 6307341618Scy if (!groups || groups->type != JSON_ARRAY) 6308341618Scy return 0; 6309341618Scy 6310341618Scy for (token = groups->child; token; token = token->sibling) { 6311341618Scy struct json_token *id, *role; 6312341618Scy 6313341618Scy id = json_get_member(token, "groupId"); 6314341618Scy if (!id || id->type != JSON_STRING) 6315341618Scy continue; 6316341618Scy 6317341618Scy role = json_get_member(token, "netRole"); 6318341618Scy if (!role || role->type != JSON_STRING) 6319341618Scy continue; 6320341618Scy 6321341618Scy if (os_strcmp(id->string, "*") != 0 && 6322341618Scy os_strcmp(group_id, "*") != 0 && 6323341618Scy os_strcmp(id->string, group_id) != 0) 6324341618Scy continue; 6325341618Scy 6326341618Scy if (dpp_compatible_netrole(role->string, net_role)) 6327341618Scy return 1; 6328341618Scy } 6329341618Scy 6330341618Scy return 0; 6331341618Scy} 6332341618Scy 6333341618Scy 6334341618Scystatic int dpp_connector_match_groups(struct json_token *own_root, 6335341618Scy struct json_token *peer_root) 6336341618Scy{ 6337341618Scy struct json_token *groups, *token; 6338341618Scy 6339341618Scy groups = json_get_member(peer_root, "groups"); 6340341618Scy if (!groups || groups->type != JSON_ARRAY) { 6341341618Scy wpa_printf(MSG_DEBUG, "DPP: No peer groups array found"); 6342341618Scy return 0; 6343341618Scy } 6344341618Scy 6345341618Scy for (token = groups->child; token; token = token->sibling) { 6346341618Scy struct json_token *id, *role; 6347341618Scy 6348341618Scy id = json_get_member(token, "groupId"); 6349341618Scy if (!id || id->type != JSON_STRING) { 6350341618Scy wpa_printf(MSG_DEBUG, 6351341618Scy "DPP: Missing peer groupId string"); 6352341618Scy continue; 6353341618Scy } 6354341618Scy 6355341618Scy role = json_get_member(token, "netRole"); 6356341618Scy if (!role || role->type != JSON_STRING) { 6357341618Scy wpa_printf(MSG_DEBUG, 6358341618Scy "DPP: Missing peer groups::netRole string"); 6359341618Scy continue; 6360341618Scy } 6361341618Scy wpa_printf(MSG_DEBUG, 6362341618Scy "DPP: peer connector group: groupId='%s' netRole='%s'", 6363341618Scy id->string, role->string); 6364341618Scy if (dpp_connector_compatible_group(own_root, id->string, 6365341618Scy role->string)) { 6366341618Scy wpa_printf(MSG_DEBUG, 6367341618Scy "DPP: Compatible group/netRole in own connector"); 6368341618Scy return 1; 6369341618Scy } 6370341618Scy } 6371341618Scy 6372341618Scy return 0; 6373341618Scy} 6374341618Scy 6375341618Scy 6376341618Scystatic int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, 6377341618Scy unsigned int hash_len) 6378341618Scy{ 6379341618Scy u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; 6380341618Scy const char *info = "DPP PMK"; 6381341618Scy int res; 6382341618Scy 6383341618Scy /* PMK = HKDF(<>, "DPP PMK", N.x) */ 6384341618Scy 6385341618Scy /* HKDF-Extract(<>, N.x) */ 6386341618Scy os_memset(salt, 0, hash_len); 6387341618Scy if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0) 6388341618Scy return -1; 6389341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)", 6390341618Scy prk, hash_len); 6391341618Scy 6392341618Scy /* HKDF-Expand(PRK, info, L) */ 6393341618Scy res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len); 6394341618Scy os_memset(prk, 0, hash_len); 6395341618Scy if (res < 0) 6396341618Scy return -1; 6397341618Scy 6398341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)", 6399341618Scy pmk, hash_len); 6400341618Scy return 0; 6401341618Scy} 6402341618Scy 6403341618Scy 6404341618Scystatic int dpp_derive_pmkid(const struct dpp_curve_params *curve, 6405341618Scy EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid) 6406341618Scy{ 6407341618Scy struct wpabuf *nkx, *pkx; 6408341618Scy int ret = -1, res; 6409341618Scy const u8 *addr[2]; 6410341618Scy size_t len[2]; 6411341618Scy u8 hash[SHA256_MAC_LEN]; 6412341618Scy 6413341618Scy /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */ 6414341618Scy nkx = dpp_get_pubkey_point(own_key, 0); 6415341618Scy pkx = dpp_get_pubkey_point(peer_key, 0); 6416341618Scy if (!nkx || !pkx) 6417341618Scy goto fail; 6418341618Scy addr[0] = wpabuf_head(nkx); 6419341618Scy len[0] = wpabuf_len(nkx) / 2; 6420341618Scy addr[1] = wpabuf_head(pkx); 6421341618Scy len[1] = wpabuf_len(pkx) / 2; 6422341618Scy if (len[0] != len[1]) 6423341618Scy goto fail; 6424341618Scy if (os_memcmp(addr[0], addr[1], len[0]) > 0) { 6425341618Scy addr[0] = wpabuf_head(pkx); 6426341618Scy addr[1] = wpabuf_head(nkx); 6427341618Scy } 6428341618Scy wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]); 6429341618Scy wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]); 6430341618Scy res = sha256_vector(2, addr, len, hash); 6431341618Scy if (res < 0) 6432341618Scy goto fail; 6433341618Scy wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN); 6434341618Scy os_memcpy(pmkid, hash, PMKID_LEN); 6435341618Scy wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN); 6436341618Scy ret = 0; 6437341618Scyfail: 6438341618Scy wpabuf_free(nkx); 6439341618Scy wpabuf_free(pkx); 6440341618Scy return ret; 6441341618Scy} 6442341618Scy 6443341618Scy 6444341618Scyenum dpp_status_error 6445341618Scydpp_peer_intro(struct dpp_introduction *intro, const char *own_connector, 6446341618Scy const u8 *net_access_key, size_t net_access_key_len, 6447341618Scy const u8 *csign_key, size_t csign_key_len, 6448341618Scy const u8 *peer_connector, size_t peer_connector_len, 6449341618Scy os_time_t *expiry) 6450341618Scy{ 6451341618Scy struct json_token *root = NULL, *netkey, *token; 6452341618Scy struct json_token *own_root = NULL; 6453341618Scy enum dpp_status_error ret = 255, res; 6454341618Scy EVP_PKEY *own_key = NULL, *peer_key = NULL; 6455341618Scy struct wpabuf *own_key_pub = NULL; 6456341618Scy const struct dpp_curve_params *curve, *own_curve; 6457341618Scy struct dpp_signed_connector_info info; 6458341618Scy const unsigned char *p; 6459341618Scy EVP_PKEY *csign = NULL; 6460341618Scy char *signed_connector = NULL; 6461341618Scy const char *pos, *end; 6462341618Scy unsigned char *own_conn = NULL; 6463341618Scy size_t own_conn_len; 6464341618Scy size_t Nx_len; 6465341618Scy u8 Nx[DPP_MAX_SHARED_SECRET_LEN]; 6466341618Scy 6467341618Scy os_memset(intro, 0, sizeof(*intro)); 6468341618Scy os_memset(&info, 0, sizeof(info)); 6469341618Scy if (expiry) 6470341618Scy *expiry = 0; 6471341618Scy 6472341618Scy p = csign_key; 6473341618Scy csign = d2i_PUBKEY(NULL, &p, csign_key_len); 6474341618Scy if (!csign) { 6475341618Scy wpa_printf(MSG_ERROR, 6476341618Scy "DPP: Failed to parse local C-sign-key information"); 6477341618Scy goto fail; 6478341618Scy } 6479341618Scy 6480341618Scy own_key = dpp_set_keypair(&own_curve, net_access_key, 6481341618Scy net_access_key_len); 6482341618Scy if (!own_key) { 6483341618Scy wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); 6484341618Scy goto fail; 6485341618Scy } 6486341618Scy 6487341618Scy pos = os_strchr(own_connector, '.'); 6488341618Scy if (!pos) { 6489341618Scy wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the first dot (.)"); 6490341618Scy goto fail; 6491341618Scy } 6492341618Scy pos++; 6493341618Scy end = os_strchr(pos, '.'); 6494341618Scy if (!end) { 6495341618Scy wpa_printf(MSG_DEBUG, "DPP: Own connector is missing the second dot (.)"); 6496341618Scy goto fail; 6497341618Scy } 6498341618Scy own_conn = base64_url_decode((const unsigned char *) pos, 6499341618Scy end - pos, &own_conn_len); 6500341618Scy if (!own_conn) { 6501341618Scy wpa_printf(MSG_DEBUG, 6502341618Scy "DPP: Failed to base64url decode own signedConnector JWS Payload"); 6503341618Scy goto fail; 6504341618Scy } 6505341618Scy 6506341618Scy own_root = json_parse((const char *) own_conn, own_conn_len); 6507341618Scy if (!own_root) { 6508341618Scy wpa_printf(MSG_DEBUG, "DPP: Failed to parse local connector"); 6509341618Scy goto fail; 6510341618Scy } 6511341618Scy 6512341618Scy wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector", 6513341618Scy peer_connector, peer_connector_len); 6514341618Scy signed_connector = os_malloc(peer_connector_len + 1); 6515341618Scy if (!signed_connector) 6516341618Scy goto fail; 6517341618Scy os_memcpy(signed_connector, peer_connector, peer_connector_len); 6518341618Scy signed_connector[peer_connector_len] = '\0'; 6519341618Scy 6520341618Scy res = dpp_process_signed_connector(&info, csign, signed_connector); 6521341618Scy if (res != DPP_STATUS_OK) { 6522341618Scy ret = res; 6523341618Scy goto fail; 6524341618Scy } 6525341618Scy 6526341618Scy root = json_parse((const char *) info.payload, info.payload_len); 6527341618Scy if (!root) { 6528341618Scy wpa_printf(MSG_DEBUG, "DPP: JSON parsing of connector failed"); 6529341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 6530341618Scy goto fail; 6531341618Scy } 6532341618Scy 6533341618Scy if (!dpp_connector_match_groups(own_root, root)) { 6534341618Scy wpa_printf(MSG_DEBUG, 6535341618Scy "DPP: Peer connector does not include compatible group netrole with own connector"); 6536341618Scy ret = DPP_STATUS_NO_MATCH; 6537341618Scy goto fail; 6538341618Scy } 6539341618Scy 6540341618Scy token = json_get_member(root, "expiry"); 6541341618Scy if (!token || token->type != JSON_STRING) { 6542341618Scy wpa_printf(MSG_DEBUG, 6543341618Scy "DPP: No expiry string found - connector does not expire"); 6544341618Scy } else { 6545341618Scy wpa_printf(MSG_DEBUG, "DPP: expiry = %s", token->string); 6546341618Scy if (dpp_key_expired(token->string, expiry)) { 6547341618Scy wpa_printf(MSG_DEBUG, 6548341618Scy "DPP: Connector (netAccessKey) has expired"); 6549341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 6550341618Scy goto fail; 6551341618Scy } 6552341618Scy } 6553341618Scy 6554341618Scy netkey = json_get_member(root, "netAccessKey"); 6555341618Scy if (!netkey || netkey->type != JSON_OBJECT) { 6556341618Scy wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found"); 6557341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 6558341618Scy goto fail; 6559341618Scy } 6560341618Scy 6561341618Scy peer_key = dpp_parse_jwk(netkey, &curve); 6562341618Scy if (!peer_key) { 6563341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 6564341618Scy goto fail; 6565341618Scy } 6566341618Scy dpp_debug_print_key("DPP: Received netAccessKey", peer_key); 6567341618Scy 6568341618Scy if (own_curve != curve) { 6569341618Scy wpa_printf(MSG_DEBUG, 6570341618Scy "DPP: Mismatching netAccessKey curves (%s != %s)", 6571341618Scy own_curve->name, curve->name); 6572341618Scy ret = DPP_STATUS_INVALID_CONNECTOR; 6573341618Scy goto fail; 6574341618Scy } 6575341618Scy 6576341618Scy /* ECDH: N = nk * PK */ 6577351611Scy if (dpp_ecdh(own_key, peer_key, Nx, &Nx_len) < 0) 6578341618Scy goto fail; 6579341618Scy 6580341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)", 6581341618Scy Nx, Nx_len); 6582341618Scy 6583341618Scy /* PMK = HKDF(<>, "DPP PMK", N.x) */ 6584341618Scy if (dpp_derive_pmk(Nx, Nx_len, intro->pmk, curve->hash_len) < 0) { 6585341618Scy wpa_printf(MSG_ERROR, "DPP: Failed to derive PMK"); 6586341618Scy goto fail; 6587341618Scy } 6588341618Scy intro->pmk_len = curve->hash_len; 6589341618Scy 6590341618Scy /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */ 6591341618Scy if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) { 6592341618Scy wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID"); 6593341618Scy goto fail; 6594341618Scy } 6595341618Scy 6596341618Scy ret = DPP_STATUS_OK; 6597341618Scyfail: 6598341618Scy if (ret != DPP_STATUS_OK) 6599341618Scy os_memset(intro, 0, sizeof(*intro)); 6600341618Scy os_memset(Nx, 0, sizeof(Nx)); 6601341618Scy os_free(own_conn); 6602341618Scy os_free(signed_connector); 6603341618Scy os_free(info.payload); 6604341618Scy EVP_PKEY_free(own_key); 6605341618Scy wpabuf_free(own_key_pub); 6606341618Scy EVP_PKEY_free(peer_key); 6607341618Scy EVP_PKEY_free(csign); 6608341618Scy json_free(root); 6609341618Scy json_free(own_root); 6610341618Scy return ret; 6611341618Scy} 6612341618Scy 6613341618Scy 6614341618Scystatic EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve, 6615341618Scy int init) 6616341618Scy{ 6617341618Scy EC_GROUP *group; 6618341618Scy size_t len = curve->prime_len; 6619341618Scy const u8 *x, *y; 6620351611Scy EVP_PKEY *res; 6621341618Scy 6622341618Scy switch (curve->ike_group) { 6623341618Scy case 19: 6624341618Scy x = init ? pkex_init_x_p256 : pkex_resp_x_p256; 6625341618Scy y = init ? pkex_init_y_p256 : pkex_resp_y_p256; 6626341618Scy break; 6627341618Scy case 20: 6628341618Scy x = init ? pkex_init_x_p384 : pkex_resp_x_p384; 6629341618Scy y = init ? pkex_init_y_p384 : pkex_resp_y_p384; 6630341618Scy break; 6631341618Scy case 21: 6632341618Scy x = init ? pkex_init_x_p521 : pkex_resp_x_p521; 6633341618Scy y = init ? pkex_init_y_p521 : pkex_resp_y_p521; 6634341618Scy break; 6635341618Scy case 28: 6636341618Scy x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1; 6637341618Scy y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1; 6638341618Scy break; 6639341618Scy case 29: 6640341618Scy x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1; 6641341618Scy y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1; 6642341618Scy break; 6643341618Scy case 30: 6644341618Scy x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1; 6645341618Scy y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1; 6646341618Scy break; 6647341618Scy default: 6648341618Scy return NULL; 6649341618Scy } 6650341618Scy 6651341618Scy group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); 6652341618Scy if (!group) 6653341618Scy return NULL; 6654351611Scy res = dpp_set_pubkey_point_group(group, x, y, len); 6655351611Scy EC_GROUP_free(group); 6656351611Scy return res; 6657341618Scy} 6658341618Scy 6659341618Scy 6660341618Scystatic EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, 6661341618Scy const u8 *mac_init, const char *code, 6662341618Scy const char *identifier, BN_CTX *bnctx, 6663351611Scy EC_GROUP **ret_group) 6664341618Scy{ 6665341618Scy u8 hash[DPP_MAX_HASH_LEN]; 6666341618Scy const u8 *addr[3]; 6667341618Scy size_t len[3]; 6668341618Scy unsigned int num_elem = 0; 6669341618Scy EC_POINT *Qi = NULL; 6670341618Scy EVP_PKEY *Pi = NULL; 6671341618Scy EC_KEY *Pi_ec = NULL; 6672341618Scy const EC_POINT *Pi_point; 6673341618Scy BIGNUM *hash_bn = NULL; 6674341618Scy const EC_GROUP *group = NULL; 6675341618Scy EC_GROUP *group2 = NULL; 6676341618Scy 6677341618Scy /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */ 6678341618Scy 6679341618Scy wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init)); 6680341618Scy addr[num_elem] = mac_init; 6681341618Scy len[num_elem] = ETH_ALEN; 6682341618Scy num_elem++; 6683341618Scy if (identifier) { 6684341618Scy wpa_printf(MSG_DEBUG, "DPP: code identifier: %s", 6685341618Scy identifier); 6686341618Scy addr[num_elem] = (const u8 *) identifier; 6687341618Scy len[num_elem] = os_strlen(identifier); 6688341618Scy num_elem++; 6689341618Scy } 6690341618Scy wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code)); 6691341618Scy addr[num_elem] = (const u8 *) code; 6692341618Scy len[num_elem] = os_strlen(code); 6693341618Scy num_elem++; 6694341618Scy if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0) 6695341618Scy goto fail; 6696341618Scy wpa_hexdump_key(MSG_DEBUG, 6697341618Scy "DPP: H(MAC-Initiator | [identifier |] code)", 6698341618Scy hash, curve->hash_len); 6699341618Scy Pi = dpp_pkex_get_role_elem(curve, 1); 6700341618Scy if (!Pi) 6701341618Scy goto fail; 6702341618Scy dpp_debug_print_key("DPP: Pi", Pi); 6703341618Scy Pi_ec = EVP_PKEY_get1_EC_KEY(Pi); 6704341618Scy if (!Pi_ec) 6705341618Scy goto fail; 6706341618Scy Pi_point = EC_KEY_get0_public_key(Pi_ec); 6707341618Scy 6708341618Scy group = EC_KEY_get0_group(Pi_ec); 6709341618Scy if (!group) 6710341618Scy goto fail; 6711341618Scy group2 = EC_GROUP_dup(group); 6712341618Scy if (!group2) 6713341618Scy goto fail; 6714341618Scy Qi = EC_POINT_new(group2); 6715341618Scy if (!Qi) { 6716341618Scy EC_GROUP_free(group2); 6717341618Scy goto fail; 6718341618Scy } 6719341618Scy hash_bn = BN_bin2bn(hash, curve->hash_len, NULL); 6720341618Scy if (!hash_bn || 6721341618Scy EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1) 6722341618Scy goto fail; 6723341618Scy if (EC_POINT_is_at_infinity(group, Qi)) { 6724341618Scy wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity"); 6725341618Scy goto fail; 6726341618Scy } 6727341618Scy dpp_debug_print_point("DPP: Qi", group, Qi); 6728341618Scyout: 6729341618Scy EC_KEY_free(Pi_ec); 6730341618Scy EVP_PKEY_free(Pi); 6731341618Scy BN_clear_free(hash_bn); 6732351611Scy if (ret_group && Qi) 6733341618Scy *ret_group = group2; 6734351611Scy else 6735351611Scy EC_GROUP_free(group2); 6736341618Scy return Qi; 6737341618Scyfail: 6738341618Scy EC_POINT_free(Qi); 6739341618Scy Qi = NULL; 6740341618Scy goto out; 6741341618Scy} 6742341618Scy 6743341618Scy 6744341618Scystatic EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, 6745341618Scy const u8 *mac_resp, const char *code, 6746341618Scy const char *identifier, BN_CTX *bnctx, 6747351611Scy EC_GROUP **ret_group) 6748341618Scy{ 6749341618Scy u8 hash[DPP_MAX_HASH_LEN]; 6750341618Scy const u8 *addr[3]; 6751341618Scy size_t len[3]; 6752341618Scy unsigned int num_elem = 0; 6753341618Scy EC_POINT *Qr = NULL; 6754341618Scy EVP_PKEY *Pr = NULL; 6755341618Scy EC_KEY *Pr_ec = NULL; 6756341618Scy const EC_POINT *Pr_point; 6757341618Scy BIGNUM *hash_bn = NULL; 6758341618Scy const EC_GROUP *group = NULL; 6759341618Scy EC_GROUP *group2 = NULL; 6760341618Scy 6761341618Scy /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */ 6762341618Scy 6763341618Scy wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp)); 6764341618Scy addr[num_elem] = mac_resp; 6765341618Scy len[num_elem] = ETH_ALEN; 6766341618Scy num_elem++; 6767341618Scy if (identifier) { 6768341618Scy wpa_printf(MSG_DEBUG, "DPP: code identifier: %s", 6769341618Scy identifier); 6770341618Scy addr[num_elem] = (const u8 *) identifier; 6771341618Scy len[num_elem] = os_strlen(identifier); 6772341618Scy num_elem++; 6773341618Scy } 6774341618Scy wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code)); 6775341618Scy addr[num_elem] = (const u8 *) code; 6776341618Scy len[num_elem] = os_strlen(code); 6777341618Scy num_elem++; 6778341618Scy if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0) 6779341618Scy goto fail; 6780341618Scy wpa_hexdump_key(MSG_DEBUG, 6781341618Scy "DPP: H(MAC-Responder | [identifier |] code)", 6782341618Scy hash, curve->hash_len); 6783341618Scy Pr = dpp_pkex_get_role_elem(curve, 0); 6784341618Scy if (!Pr) 6785341618Scy goto fail; 6786341618Scy dpp_debug_print_key("DPP: Pr", Pr); 6787341618Scy Pr_ec = EVP_PKEY_get1_EC_KEY(Pr); 6788341618Scy if (!Pr_ec) 6789341618Scy goto fail; 6790341618Scy Pr_point = EC_KEY_get0_public_key(Pr_ec); 6791341618Scy 6792341618Scy group = EC_KEY_get0_group(Pr_ec); 6793341618Scy if (!group) 6794341618Scy goto fail; 6795341618Scy group2 = EC_GROUP_dup(group); 6796341618Scy if (!group2) 6797341618Scy goto fail; 6798341618Scy Qr = EC_POINT_new(group2); 6799341618Scy if (!Qr) { 6800341618Scy EC_GROUP_free(group2); 6801341618Scy goto fail; 6802341618Scy } 6803341618Scy hash_bn = BN_bin2bn(hash, curve->hash_len, NULL); 6804341618Scy if (!hash_bn || 6805341618Scy EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1) 6806341618Scy goto fail; 6807341618Scy if (EC_POINT_is_at_infinity(group, Qr)) { 6808341618Scy wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity"); 6809341618Scy goto fail; 6810341618Scy } 6811341618Scy dpp_debug_print_point("DPP: Qr", group, Qr); 6812341618Scyout: 6813341618Scy EC_KEY_free(Pr_ec); 6814341618Scy EVP_PKEY_free(Pr); 6815341618Scy BN_clear_free(hash_bn); 6816351611Scy if (ret_group && Qr) 6817341618Scy *ret_group = group2; 6818351611Scy else 6819351611Scy EC_GROUP_free(group2); 6820341618Scy return Qr; 6821341618Scyfail: 6822341618Scy EC_POINT_free(Qr); 6823341618Scy Qr = NULL; 6824341618Scy goto out; 6825341618Scy} 6826341618Scy 6827341618Scy 6828341618Scy#ifdef CONFIG_TESTING_OPTIONS 6829341618Scystatic int dpp_test_gen_invalid_key(struct wpabuf *msg, 6830341618Scy const struct dpp_curve_params *curve) 6831341618Scy{ 6832341618Scy BN_CTX *ctx; 6833341618Scy BIGNUM *x, *y; 6834341618Scy int ret = -1; 6835341618Scy EC_GROUP *group; 6836341618Scy EC_POINT *point; 6837341618Scy 6838341618Scy group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name)); 6839341618Scy if (!group) 6840341618Scy return -1; 6841341618Scy 6842341618Scy ctx = BN_CTX_new(); 6843341618Scy point = EC_POINT_new(group); 6844341618Scy x = BN_new(); 6845341618Scy y = BN_new(); 6846341618Scy if (!ctx || !point || !x || !y) 6847341618Scy goto fail; 6848341618Scy 6849341618Scy if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1) 6850341618Scy goto fail; 6851341618Scy 6852341618Scy /* Generate a random y coordinate that results in a point that is not 6853341618Scy * on the curve. */ 6854341618Scy for (;;) { 6855341618Scy if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1) 6856341618Scy goto fail; 6857341618Scy 6858341618Scy if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y, 6859341618Scy ctx) != 1) { 6860341618Scy#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL) 6861341618Scy /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL 6862341618Scy * return an error from EC_POINT_set_affine_coordinates_GFp() 6863341618Scy * when the point is not on the curve. */ 6864341618Scy break; 6865341618Scy#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */ 6866341618Scy goto fail; 6867341618Scy#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */ 6868341618Scy } 6869341618Scy 6870341618Scy if (!EC_POINT_is_on_curve(group, point, ctx)) 6871341618Scy break; 6872341618Scy } 6873341618Scy 6874341618Scy if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len), 6875341618Scy curve->prime_len) < 0 || 6876341618Scy dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len), 6877341618Scy curve->prime_len) < 0) 6878341618Scy goto fail; 6879341618Scy 6880341618Scy ret = 0; 6881341618Scyfail: 6882341618Scy if (ret < 0) 6883341618Scy wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key"); 6884341618Scy BN_free(x); 6885341618Scy BN_free(y); 6886341618Scy EC_POINT_free(point); 6887341618Scy BN_CTX_free(ctx); 6888351611Scy EC_GROUP_free(group); 6889341618Scy 6890341618Scy return ret; 6891341618Scy} 6892341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 6893341618Scy 6894341618Scy 6895341618Scystatic struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex) 6896341618Scy{ 6897341618Scy EC_KEY *X_ec = NULL; 6898341618Scy const EC_POINT *X_point; 6899341618Scy BN_CTX *bnctx = NULL; 6900351611Scy EC_GROUP *group = NULL; 6901341618Scy EC_POINT *Qi = NULL, *M = NULL; 6902341618Scy struct wpabuf *M_buf = NULL; 6903341618Scy BIGNUM *Mx = NULL, *My = NULL; 6904341618Scy struct wpabuf *msg = NULL; 6905341618Scy size_t attr_len; 6906341618Scy const struct dpp_curve_params *curve = pkex->own_bi->curve; 6907341618Scy 6908341618Scy wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request"); 6909341618Scy 6910341618Scy /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */ 6911341618Scy bnctx = BN_CTX_new(); 6912341618Scy if (!bnctx) 6913341618Scy goto fail; 6914341618Scy Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code, 6915341618Scy pkex->identifier, bnctx, &group); 6916341618Scy if (!Qi) 6917341618Scy goto fail; 6918341618Scy 6919341618Scy /* Generate a random ephemeral keypair x/X */ 6920341618Scy#ifdef CONFIG_TESTING_OPTIONS 6921341618Scy if (dpp_pkex_ephemeral_key_override_len) { 6922341618Scy const struct dpp_curve_params *tmp_curve; 6923341618Scy 6924341618Scy wpa_printf(MSG_INFO, 6925341618Scy "DPP: TESTING - override ephemeral key x/X"); 6926341618Scy pkex->x = dpp_set_keypair(&tmp_curve, 6927341618Scy dpp_pkex_ephemeral_key_override, 6928341618Scy dpp_pkex_ephemeral_key_override_len); 6929341618Scy } else { 6930341618Scy pkex->x = dpp_gen_keypair(curve); 6931341618Scy } 6932341618Scy#else /* CONFIG_TESTING_OPTIONS */ 6933341618Scy pkex->x = dpp_gen_keypair(curve); 6934341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 6935341618Scy if (!pkex->x) 6936341618Scy goto fail; 6937341618Scy 6938341618Scy /* M = X + Qi */ 6939341618Scy X_ec = EVP_PKEY_get1_EC_KEY(pkex->x); 6940341618Scy if (!X_ec) 6941341618Scy goto fail; 6942341618Scy X_point = EC_KEY_get0_public_key(X_ec); 6943341618Scy if (!X_point) 6944341618Scy goto fail; 6945341618Scy dpp_debug_print_point("DPP: X", group, X_point); 6946341618Scy M = EC_POINT_new(group); 6947341618Scy Mx = BN_new(); 6948341618Scy My = BN_new(); 6949341618Scy if (!M || !Mx || !My || 6950341618Scy EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 || 6951341618Scy EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1) 6952341618Scy goto fail; 6953341618Scy dpp_debug_print_point("DPP: M", group, M); 6954341618Scy 6955341618Scy /* Initiator -> Responder: group, [identifier,] M */ 6956341618Scy attr_len = 4 + 2; 6957341618Scy if (pkex->identifier) 6958341618Scy attr_len += 4 + os_strlen(pkex->identifier); 6959341618Scy attr_len += 4 + 2 * curve->prime_len; 6960341618Scy msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len); 6961341618Scy if (!msg) 6962341618Scy goto fail; 6963341618Scy 6964341618Scy#ifdef CONFIG_TESTING_OPTIONS 6965341618Scy if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) { 6966341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group"); 6967341618Scy goto skip_finite_cyclic_group; 6968341618Scy } 6969341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 6970341618Scy 6971341618Scy /* Finite Cyclic Group attribute */ 6972341618Scy wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP); 6973341618Scy wpabuf_put_le16(msg, 2); 6974341618Scy wpabuf_put_le16(msg, curve->ike_group); 6975341618Scy 6976341618Scy#ifdef CONFIG_TESTING_OPTIONS 6977341618Scyskip_finite_cyclic_group: 6978341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 6979341618Scy 6980341618Scy /* Code Identifier attribute */ 6981341618Scy if (pkex->identifier) { 6982341618Scy wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER); 6983341618Scy wpabuf_put_le16(msg, os_strlen(pkex->identifier)); 6984341618Scy wpabuf_put_str(msg, pkex->identifier); 6985341618Scy } 6986341618Scy 6987341618Scy#ifdef CONFIG_TESTING_OPTIONS 6988341618Scy if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) { 6989341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key"); 6990341618Scy goto out; 6991341618Scy } 6992341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 6993341618Scy 6994341618Scy /* M in Encrypted Key attribute */ 6995341618Scy wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY); 6996341618Scy wpabuf_put_le16(msg, 2 * curve->prime_len); 6997341618Scy 6998341618Scy#ifdef CONFIG_TESTING_OPTIONS 6999341618Scy if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) { 7000341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key"); 7001341618Scy if (dpp_test_gen_invalid_key(msg, curve) < 0) 7002341618Scy goto fail; 7003341618Scy goto out; 7004341618Scy } 7005341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7006341618Scy 7007341618Scy if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len), 7008341618Scy curve->prime_len) < 0 || 7009341618Scy dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 || 7010341618Scy dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len), 7011341618Scy curve->prime_len) < 0) 7012341618Scy goto fail; 7013341618Scy 7014341618Scyout: 7015341618Scy wpabuf_free(M_buf); 7016341618Scy EC_KEY_free(X_ec); 7017341618Scy EC_POINT_free(M); 7018341618Scy EC_POINT_free(Qi); 7019341618Scy BN_clear_free(Mx); 7020341618Scy BN_clear_free(My); 7021341618Scy BN_CTX_free(bnctx); 7022351611Scy EC_GROUP_free(group); 7023341618Scy return msg; 7024341618Scyfail: 7025341618Scy wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request"); 7026341618Scy wpabuf_free(msg); 7027341618Scy msg = NULL; 7028341618Scy goto out; 7029341618Scy} 7030341618Scy 7031341618Scy 7032341618Scystatic void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt) 7033341618Scy{ 7034341618Scy wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt); 7035341618Scy} 7036341618Scy 7037341618Scy 7038341618Scystruct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi, 7039341618Scy const u8 *own_mac, 7040341618Scy const char *identifier, 7041341618Scy const char *code) 7042341618Scy{ 7043341618Scy struct dpp_pkex *pkex; 7044341618Scy 7045341618Scy#ifdef CONFIG_TESTING_OPTIONS 7046341618Scy if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) { 7047341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR, 7048341618Scy MAC2STR(dpp_pkex_own_mac_override)); 7049341618Scy own_mac = dpp_pkex_own_mac_override; 7050341618Scy } 7051341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7052341618Scy 7053341618Scy pkex = os_zalloc(sizeof(*pkex)); 7054341618Scy if (!pkex) 7055341618Scy return NULL; 7056341618Scy pkex->msg_ctx = msg_ctx; 7057341618Scy pkex->initiator = 1; 7058341618Scy pkex->own_bi = bi; 7059341618Scy os_memcpy(pkex->own_mac, own_mac, ETH_ALEN); 7060341618Scy if (identifier) { 7061341618Scy pkex->identifier = os_strdup(identifier); 7062341618Scy if (!pkex->identifier) 7063341618Scy goto fail; 7064341618Scy } 7065341618Scy pkex->code = os_strdup(code); 7066341618Scy if (!pkex->code) 7067341618Scy goto fail; 7068341618Scy pkex->exchange_req = dpp_pkex_build_exchange_req(pkex); 7069341618Scy if (!pkex->exchange_req) 7070341618Scy goto fail; 7071341618Scy return pkex; 7072341618Scyfail: 7073341618Scy dpp_pkex_free(pkex); 7074341618Scy return NULL; 7075341618Scy} 7076341618Scy 7077341618Scy 7078341618Scystatic struct wpabuf * 7079341618Scydpp_pkex_build_exchange_resp(struct dpp_pkex *pkex, 7080341618Scy enum dpp_status_error status, 7081341618Scy const BIGNUM *Nx, const BIGNUM *Ny) 7082341618Scy{ 7083341618Scy struct wpabuf *msg = NULL; 7084341618Scy size_t attr_len; 7085341618Scy const struct dpp_curve_params *curve = pkex->own_bi->curve; 7086341618Scy 7087341618Scy /* Initiator -> Responder: DPP Status, [identifier,] N */ 7088341618Scy attr_len = 4 + 1; 7089341618Scy if (pkex->identifier) 7090341618Scy attr_len += 4 + os_strlen(pkex->identifier); 7091341618Scy attr_len += 4 + 2 * curve->prime_len; 7092341618Scy msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len); 7093341618Scy if (!msg) 7094341618Scy goto fail; 7095341618Scy 7096341618Scy#ifdef CONFIG_TESTING_OPTIONS 7097341618Scy if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) { 7098341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Status"); 7099341618Scy goto skip_status; 7100341618Scy } 7101341618Scy 7102341618Scy if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) { 7103341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status"); 7104341618Scy status = 255; 7105341618Scy } 7106341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7107341618Scy 7108341618Scy /* DPP Status */ 7109341618Scy dpp_build_attr_status(msg, status); 7110341618Scy 7111341618Scy#ifdef CONFIG_TESTING_OPTIONS 7112341618Scyskip_status: 7113341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7114341618Scy 7115341618Scy /* Code Identifier attribute */ 7116341618Scy if (pkex->identifier) { 7117341618Scy wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER); 7118341618Scy wpabuf_put_le16(msg, os_strlen(pkex->identifier)); 7119341618Scy wpabuf_put_str(msg, pkex->identifier); 7120341618Scy } 7121341618Scy 7122341618Scy if (status != DPP_STATUS_OK) 7123341618Scy goto skip_encrypted_key; 7124341618Scy 7125341618Scy#ifdef CONFIG_TESTING_OPTIONS 7126341618Scy if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) { 7127341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key"); 7128341618Scy goto skip_encrypted_key; 7129341618Scy } 7130341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7131341618Scy 7132341618Scy /* N in Encrypted Key attribute */ 7133341618Scy wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY); 7134341618Scy wpabuf_put_le16(msg, 2 * curve->prime_len); 7135341618Scy 7136341618Scy#ifdef CONFIG_TESTING_OPTIONS 7137341618Scy if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) { 7138341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key"); 7139341618Scy if (dpp_test_gen_invalid_key(msg, curve) < 0) 7140341618Scy goto fail; 7141341618Scy goto skip_encrypted_key; 7142341618Scy } 7143341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7144341618Scy 7145341618Scy if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len), 7146341618Scy curve->prime_len) < 0 || 7147341618Scy dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 || 7148341618Scy dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len), 7149341618Scy curve->prime_len) < 0) 7150341618Scy goto fail; 7151341618Scy 7152341618Scyskip_encrypted_key: 7153341618Scy if (status == DPP_STATUS_BAD_GROUP) { 7154341618Scy /* Finite Cyclic Group attribute */ 7155341618Scy wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP); 7156341618Scy wpabuf_put_le16(msg, 2); 7157341618Scy wpabuf_put_le16(msg, curve->ike_group); 7158341618Scy } 7159341618Scy 7160341618Scy return msg; 7161341618Scyfail: 7162341618Scy wpabuf_free(msg); 7163341618Scy return NULL; 7164341618Scy} 7165341618Scy 7166341618Scy 7167341618Scystatic int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp, 7168341618Scy const u8 *Mx, size_t Mx_len, 7169341618Scy const u8 *Nx, size_t Nx_len, 7170341618Scy const char *code, 7171341618Scy const u8 *Kx, size_t Kx_len, 7172341618Scy u8 *z, unsigned int hash_len) 7173341618Scy{ 7174341618Scy u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN]; 7175341618Scy int res; 7176341618Scy u8 *info, *pos; 7177341618Scy size_t info_len; 7178341618Scy 7179341618Scy /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x) 7180341618Scy */ 7181341618Scy 7182341618Scy /* HKDF-Extract(<>, IKM=K.x) */ 7183341618Scy os_memset(salt, 0, hash_len); 7184341618Scy if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0) 7185341618Scy return -1; 7186341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)", 7187341618Scy prk, hash_len); 7188341618Scy info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code); 7189341618Scy info = os_malloc(info_len); 7190341618Scy if (!info) 7191341618Scy return -1; 7192341618Scy pos = info; 7193341618Scy os_memcpy(pos, mac_init, ETH_ALEN); 7194341618Scy pos += ETH_ALEN; 7195341618Scy os_memcpy(pos, mac_resp, ETH_ALEN); 7196341618Scy pos += ETH_ALEN; 7197341618Scy os_memcpy(pos, Mx, Mx_len); 7198341618Scy pos += Mx_len; 7199341618Scy os_memcpy(pos, Nx, Nx_len); 7200341618Scy pos += Nx_len; 7201341618Scy os_memcpy(pos, code, os_strlen(code)); 7202341618Scy 7203341618Scy /* HKDF-Expand(PRK, info, L) */ 7204341618Scy if (hash_len == 32) 7205341618Scy res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len, 7206341618Scy z, hash_len); 7207341618Scy else if (hash_len == 48) 7208341618Scy res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len, 7209341618Scy z, hash_len); 7210341618Scy else if (hash_len == 64) 7211341618Scy res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len, 7212341618Scy z, hash_len); 7213341618Scy else 7214341618Scy res = -1; 7215341618Scy os_free(info); 7216341618Scy os_memset(prk, 0, hash_len); 7217341618Scy if (res < 0) 7218341618Scy return -1; 7219341618Scy 7220341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)", 7221341618Scy z, hash_len); 7222341618Scy return 0; 7223341618Scy} 7224341618Scy 7225341618Scy 7226341618Scystatic int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len, 7227341618Scy const char *identifier) 7228341618Scy{ 7229341618Scy if (!attr_id && identifier) { 7230341618Scy wpa_printf(MSG_DEBUG, 7231341618Scy "DPP: No PKEX code identifier received, but expected one"); 7232341618Scy return 0; 7233341618Scy } 7234341618Scy 7235341618Scy if (attr_id && !identifier) { 7236341618Scy wpa_printf(MSG_DEBUG, 7237341618Scy "DPP: PKEX code identifier received, but not expecting one"); 7238341618Scy return 0; 7239341618Scy } 7240341618Scy 7241341618Scy if (attr_id && identifier && 7242341618Scy (os_strlen(identifier) != attr_id_len || 7243341618Scy os_memcmp(identifier, attr_id, attr_id_len) != 0)) { 7244341618Scy wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch"); 7245341618Scy return 0; 7246341618Scy } 7247341618Scy 7248341618Scy return 1; 7249341618Scy} 7250341618Scy 7251341618Scy 7252341618Scystruct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx, 7253341618Scy struct dpp_bootstrap_info *bi, 7254341618Scy const u8 *own_mac, 7255341618Scy const u8 *peer_mac, 7256341618Scy const char *identifier, 7257341618Scy const char *code, 7258341618Scy const u8 *buf, size_t len) 7259341618Scy{ 7260341618Scy const u8 *attr_group, *attr_id, *attr_key; 7261341618Scy u16 attr_group_len, attr_id_len, attr_key_len; 7262341618Scy const struct dpp_curve_params *curve = bi->curve; 7263341618Scy u16 ike_group; 7264341618Scy struct dpp_pkex *pkex = NULL; 7265341618Scy EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL; 7266341618Scy BN_CTX *bnctx = NULL; 7267351611Scy EC_GROUP *group = NULL; 7268341618Scy BIGNUM *Mx = NULL, *My = NULL; 7269341618Scy EC_KEY *Y_ec = NULL, *X_ec = NULL;; 7270341618Scy const EC_POINT *Y_point; 7271341618Scy BIGNUM *Nx = NULL, *Ny = NULL; 7272341618Scy u8 Kx[DPP_MAX_SHARED_SECRET_LEN]; 7273341618Scy size_t Kx_len; 7274341618Scy int res; 7275341618Scy 7276341618Scy if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) { 7277341618Scy wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL 7278341618Scy "PKEX counter t limit reached - ignore message"); 7279341618Scy return NULL; 7280341618Scy } 7281341618Scy 7282341618Scy#ifdef CONFIG_TESTING_OPTIONS 7283341618Scy if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) { 7284341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR, 7285341618Scy MAC2STR(dpp_pkex_peer_mac_override)); 7286341618Scy peer_mac = dpp_pkex_peer_mac_override; 7287341618Scy } 7288341618Scy if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) { 7289341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR, 7290341618Scy MAC2STR(dpp_pkex_own_mac_override)); 7291341618Scy own_mac = dpp_pkex_own_mac_override; 7292341618Scy } 7293341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7294341618Scy 7295341618Scy attr_id_len = 0; 7296341618Scy attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER, 7297341618Scy &attr_id_len); 7298341618Scy if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier)) 7299341618Scy return NULL; 7300341618Scy 7301341618Scy attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP, 7302341618Scy &attr_group_len); 7303341618Scy if (!attr_group || attr_group_len != 2) { 7304341618Scy wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL 7305341618Scy "Missing or invalid Finite Cyclic Group attribute"); 7306341618Scy return NULL; 7307341618Scy } 7308341618Scy ike_group = WPA_GET_LE16(attr_group); 7309341618Scy if (ike_group != curve->ike_group) { 7310341618Scy wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL 7311341618Scy "Mismatching PKEX curve: peer=%u own=%u", 7312341618Scy ike_group, curve->ike_group); 7313341618Scy pkex = os_zalloc(sizeof(*pkex)); 7314341618Scy if (!pkex) 7315341618Scy goto fail; 7316341618Scy pkex->own_bi = bi; 7317341618Scy pkex->failed = 1; 7318341618Scy pkex->exchange_resp = dpp_pkex_build_exchange_resp( 7319341618Scy pkex, DPP_STATUS_BAD_GROUP, NULL, NULL); 7320341618Scy if (!pkex->exchange_resp) 7321341618Scy goto fail; 7322341618Scy return pkex; 7323341618Scy } 7324341618Scy 7325341618Scy /* M in Encrypted Key attribute */ 7326341618Scy attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY, 7327341618Scy &attr_key_len); 7328341618Scy if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 || 7329341618Scy attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) { 7330341618Scy wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL 7331341618Scy "Missing Encrypted Key attribute"); 7332341618Scy return NULL; 7333341618Scy } 7334341618Scy 7335341618Scy /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */ 7336341618Scy bnctx = BN_CTX_new(); 7337341618Scy if (!bnctx) 7338341618Scy goto fail; 7339341618Scy Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx, 7340341618Scy &group); 7341341618Scy if (!Qi) 7342341618Scy goto fail; 7343341618Scy 7344341618Scy /* X' = M - Qi */ 7345341618Scy X = EC_POINT_new(group); 7346341618Scy M = EC_POINT_new(group); 7347341618Scy Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL); 7348341618Scy My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL); 7349341618Scy if (!X || !M || !Mx || !My || 7350341618Scy EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 || 7351341618Scy EC_POINT_is_at_infinity(group, M) || 7352341618Scy !EC_POINT_is_on_curve(group, M, bnctx) || 7353341618Scy EC_POINT_invert(group, Qi, bnctx) != 1 || 7354341618Scy EC_POINT_add(group, X, M, Qi, bnctx) != 1 || 7355341618Scy EC_POINT_is_at_infinity(group, X) || 7356341618Scy !EC_POINT_is_on_curve(group, X, bnctx)) { 7357341618Scy wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL 7358341618Scy "Invalid Encrypted Key value"); 7359341618Scy bi->pkex_t++; 7360341618Scy goto fail; 7361341618Scy } 7362341618Scy dpp_debug_print_point("DPP: M", group, M); 7363341618Scy dpp_debug_print_point("DPP: X'", group, X); 7364341618Scy 7365341618Scy pkex = os_zalloc(sizeof(*pkex)); 7366341618Scy if (!pkex) 7367341618Scy goto fail; 7368341618Scy pkex->t = bi->pkex_t; 7369341618Scy pkex->msg_ctx = msg_ctx; 7370341618Scy pkex->own_bi = bi; 7371341618Scy os_memcpy(pkex->own_mac, own_mac, ETH_ALEN); 7372341618Scy os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN); 7373341618Scy if (identifier) { 7374341618Scy pkex->identifier = os_strdup(identifier); 7375341618Scy if (!pkex->identifier) 7376341618Scy goto fail; 7377341618Scy } 7378341618Scy pkex->code = os_strdup(code); 7379341618Scy if (!pkex->code) 7380341618Scy goto fail; 7381341618Scy 7382341618Scy os_memcpy(pkex->Mx, attr_key, attr_key_len / 2); 7383341618Scy 7384341618Scy X_ec = EC_KEY_new(); 7385341618Scy if (!X_ec || 7386341618Scy EC_KEY_set_group(X_ec, group) != 1 || 7387341618Scy EC_KEY_set_public_key(X_ec, X) != 1) 7388341618Scy goto fail; 7389341618Scy pkex->x = EVP_PKEY_new(); 7390341618Scy if (!pkex->x || 7391341618Scy EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1) 7392341618Scy goto fail; 7393341618Scy 7394341618Scy /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */ 7395341618Scy Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL); 7396341618Scy if (!Qr) 7397341618Scy goto fail; 7398341618Scy 7399341618Scy /* Generate a random ephemeral keypair y/Y */ 7400341618Scy#ifdef CONFIG_TESTING_OPTIONS 7401341618Scy if (dpp_pkex_ephemeral_key_override_len) { 7402341618Scy const struct dpp_curve_params *tmp_curve; 7403341618Scy 7404341618Scy wpa_printf(MSG_INFO, 7405341618Scy "DPP: TESTING - override ephemeral key y/Y"); 7406341618Scy pkex->y = dpp_set_keypair(&tmp_curve, 7407341618Scy dpp_pkex_ephemeral_key_override, 7408341618Scy dpp_pkex_ephemeral_key_override_len); 7409341618Scy } else { 7410341618Scy pkex->y = dpp_gen_keypair(curve); 7411341618Scy } 7412341618Scy#else /* CONFIG_TESTING_OPTIONS */ 7413341618Scy pkex->y = dpp_gen_keypair(curve); 7414341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7415341618Scy if (!pkex->y) 7416341618Scy goto fail; 7417341618Scy 7418341618Scy /* N = Y + Qr */ 7419341618Scy Y_ec = EVP_PKEY_get1_EC_KEY(pkex->y); 7420341618Scy if (!Y_ec) 7421341618Scy goto fail; 7422341618Scy Y_point = EC_KEY_get0_public_key(Y_ec); 7423341618Scy if (!Y_point) 7424341618Scy goto fail; 7425341618Scy dpp_debug_print_point("DPP: Y", group, Y_point); 7426341618Scy N = EC_POINT_new(group); 7427341618Scy Nx = BN_new(); 7428341618Scy Ny = BN_new(); 7429341618Scy if (!N || !Nx || !Ny || 7430341618Scy EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 || 7431341618Scy EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1) 7432341618Scy goto fail; 7433341618Scy dpp_debug_print_point("DPP: N", group, N); 7434341618Scy 7435341618Scy pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK, 7436341618Scy Nx, Ny); 7437341618Scy if (!pkex->exchange_resp) 7438341618Scy goto fail; 7439341618Scy 7440341618Scy /* K = y * X' */ 7441351611Scy if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0) 7442341618Scy goto fail; 7443341618Scy 7444341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)", 7445341618Scy Kx, Kx_len); 7446341618Scy 7447341618Scy /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x) 7448341618Scy */ 7449341618Scy res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac, 7450341618Scy pkex->Mx, curve->prime_len, 7451341618Scy pkex->Nx, curve->prime_len, pkex->code, 7452341618Scy Kx, Kx_len, pkex->z, curve->hash_len); 7453341618Scy os_memset(Kx, 0, Kx_len); 7454341618Scy if (res < 0) 7455341618Scy goto fail; 7456341618Scy 7457341618Scy pkex->exchange_done = 1; 7458341618Scy 7459341618Scyout: 7460341618Scy BN_CTX_free(bnctx); 7461341618Scy EC_POINT_free(Qi); 7462341618Scy EC_POINT_free(Qr); 7463341618Scy BN_free(Mx); 7464341618Scy BN_free(My); 7465341618Scy BN_free(Nx); 7466341618Scy BN_free(Ny); 7467341618Scy EC_POINT_free(M); 7468341618Scy EC_POINT_free(N); 7469341618Scy EC_POINT_free(X); 7470341618Scy EC_KEY_free(X_ec); 7471341618Scy EC_KEY_free(Y_ec); 7472351611Scy EC_GROUP_free(group); 7473341618Scy return pkex; 7474341618Scyfail: 7475341618Scy wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed"); 7476341618Scy dpp_pkex_free(pkex); 7477341618Scy pkex = NULL; 7478341618Scy goto out; 7479341618Scy} 7480341618Scy 7481341618Scy 7482341618Scystatic struct wpabuf * 7483341618Scydpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex, 7484341618Scy const struct wpabuf *A_pub, const u8 *u) 7485341618Scy{ 7486341618Scy const struct dpp_curve_params *curve = pkex->own_bi->curve; 7487341618Scy struct wpabuf *msg = NULL; 7488341618Scy size_t clear_len, attr_len; 7489341618Scy struct wpabuf *clear = NULL; 7490341618Scy u8 *wrapped; 7491341618Scy u8 octet; 7492341618Scy const u8 *addr[2]; 7493341618Scy size_t len[2]; 7494341618Scy 7495341618Scy /* {A, u, [bootstrapping info]}z */ 7496341618Scy clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len; 7497341618Scy clear = wpabuf_alloc(clear_len); 7498341618Scy attr_len = 4 + clear_len + AES_BLOCK_SIZE; 7499341618Scy#ifdef CONFIG_TESTING_OPTIONS 7500341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) 7501341618Scy attr_len += 5; 7502341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7503341618Scy msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len); 7504341618Scy if (!clear || !msg) 7505341618Scy goto fail; 7506341618Scy 7507341618Scy#ifdef CONFIG_TESTING_OPTIONS 7508341618Scy if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) { 7509341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key"); 7510341618Scy goto skip_bootstrap_key; 7511341618Scy } 7512341618Scy if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) { 7513341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key"); 7514341618Scy wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); 7515341618Scy wpabuf_put_le16(clear, 2 * curve->prime_len); 7516341618Scy if (dpp_test_gen_invalid_key(clear, curve) < 0) 7517341618Scy goto fail; 7518341618Scy goto skip_bootstrap_key; 7519341618Scy } 7520341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7521341618Scy 7522341618Scy /* A in Bootstrap Key attribute */ 7523341618Scy wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); 7524341618Scy wpabuf_put_le16(clear, wpabuf_len(A_pub)); 7525341618Scy wpabuf_put_buf(clear, A_pub); 7526341618Scy 7527341618Scy#ifdef CONFIG_TESTING_OPTIONS 7528341618Scyskip_bootstrap_key: 7529341618Scy if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) { 7530341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag"); 7531341618Scy goto skip_i_auth_tag; 7532341618Scy } 7533341618Scy if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) { 7534341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch"); 7535341618Scy wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG); 7536341618Scy wpabuf_put_le16(clear, curve->hash_len); 7537341618Scy wpabuf_put_data(clear, u, curve->hash_len - 1); 7538341618Scy wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01); 7539341618Scy goto skip_i_auth_tag; 7540341618Scy } 7541341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7542341618Scy 7543341618Scy /* u in I-Auth tag attribute */ 7544341618Scy wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG); 7545341618Scy wpabuf_put_le16(clear, curve->hash_len); 7546341618Scy wpabuf_put_data(clear, u, curve->hash_len); 7547341618Scy 7548341618Scy#ifdef CONFIG_TESTING_OPTIONS 7549341618Scyskip_i_auth_tag: 7550341618Scy if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) { 7551341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); 7552341618Scy goto skip_wrapped_data; 7553341618Scy } 7554341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7555341618Scy 7556341618Scy addr[0] = wpabuf_head_u8(msg) + 2; 7557341618Scy len[0] = DPP_HDR_LEN; 7558341618Scy octet = 0; 7559341618Scy addr[1] = &octet; 7560341618Scy len[1] = sizeof(octet); 7561341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 7562341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 7563341618Scy 7564341618Scy wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 7565341618Scy wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 7566341618Scy wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 7567341618Scy 7568341618Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); 7569341618Scy if (aes_siv_encrypt(pkex->z, curve->hash_len, 7570341618Scy wpabuf_head(clear), wpabuf_len(clear), 7571341618Scy 2, addr, len, wrapped) < 0) 7572341618Scy goto fail; 7573341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 7574341618Scy wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); 7575341618Scy 7576341618Scy#ifdef CONFIG_TESTING_OPTIONS 7577341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) { 7578341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); 7579341618Scy dpp_build_attr_status(msg, DPP_STATUS_OK); 7580341618Scy } 7581341618Scyskip_wrapped_data: 7582341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7583341618Scy 7584341618Scyout: 7585341618Scy wpabuf_free(clear); 7586341618Scy return msg; 7587341618Scy 7588341618Scyfail: 7589341618Scy wpabuf_free(msg); 7590341618Scy msg = NULL; 7591341618Scy goto out; 7592341618Scy} 7593341618Scy 7594341618Scy 7595341618Scystruct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex, 7596341618Scy const u8 *peer_mac, 7597341618Scy const u8 *buf, size_t buflen) 7598341618Scy{ 7599341618Scy const u8 *attr_status, *attr_id, *attr_key, *attr_group; 7600341618Scy u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len; 7601351611Scy EC_GROUP *group = NULL; 7602341618Scy BN_CTX *bnctx = NULL; 7603341618Scy struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL; 7604341618Scy const struct dpp_curve_params *curve = pkex->own_bi->curve; 7605341618Scy EC_POINT *Qr = NULL, *Y = NULL, *N = NULL; 7606341618Scy BIGNUM *Nx = NULL, *Ny = NULL; 7607341618Scy EC_KEY *Y_ec = NULL; 7608341618Scy size_t Jx_len, Kx_len; 7609341618Scy u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN]; 7610341618Scy const u8 *addr[4]; 7611341618Scy size_t len[4]; 7612341618Scy u8 u[DPP_MAX_HASH_LEN]; 7613341618Scy int res; 7614341618Scy 7615341618Scy if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator) 7616341618Scy return NULL; 7617341618Scy 7618341618Scy#ifdef CONFIG_TESTING_OPTIONS 7619341618Scy if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) { 7620341618Scy wpa_printf(MSG_INFO, 7621341618Scy "DPP: TESTING - stop at PKEX Exchange Response"); 7622341618Scy pkex->failed = 1; 7623341618Scy return NULL; 7624341618Scy } 7625341618Scy 7626341618Scy if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) { 7627341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR, 7628341618Scy MAC2STR(dpp_pkex_peer_mac_override)); 7629341618Scy peer_mac = dpp_pkex_peer_mac_override; 7630341618Scy } 7631341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7632341618Scy 7633341618Scy os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN); 7634341618Scy 7635341618Scy attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS, 7636341618Scy &attr_status_len); 7637341618Scy if (!attr_status || attr_status_len != 1) { 7638341618Scy dpp_pkex_fail(pkex, "No DPP Status attribute"); 7639341618Scy return NULL; 7640341618Scy } 7641341618Scy wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]); 7642341618Scy 7643341618Scy if (attr_status[0] == DPP_STATUS_BAD_GROUP) { 7644341618Scy attr_group = dpp_get_attr(buf, buflen, 7645341618Scy DPP_ATTR_FINITE_CYCLIC_GROUP, 7646341618Scy &attr_group_len); 7647341618Scy if (attr_group && attr_group_len == 2) { 7648341618Scy wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL 7649341618Scy "Peer indicated mismatching PKEX group - proposed %u", 7650341618Scy WPA_GET_LE16(attr_group)); 7651341618Scy return NULL; 7652341618Scy } 7653341618Scy } 7654341618Scy 7655341618Scy if (attr_status[0] != DPP_STATUS_OK) { 7656341618Scy dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)"); 7657341618Scy return NULL; 7658341618Scy } 7659341618Scy 7660341618Scy attr_id_len = 0; 7661341618Scy attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER, 7662341618Scy &attr_id_len); 7663341618Scy if (!dpp_pkex_identifier_match(attr_id, attr_id_len, 7664341618Scy pkex->identifier)) { 7665341618Scy dpp_pkex_fail(pkex, "PKEX code identifier mismatch"); 7666341618Scy return NULL; 7667341618Scy } 7668341618Scy 7669341618Scy /* N in Encrypted Key attribute */ 7670341618Scy attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY, 7671341618Scy &attr_key_len); 7672341618Scy if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) { 7673341618Scy dpp_pkex_fail(pkex, "Missing Encrypted Key attribute"); 7674341618Scy return NULL; 7675341618Scy } 7676341618Scy 7677341618Scy /* Qr = H(MAC-Responder | [identifier |] code) * Pr */ 7678341618Scy bnctx = BN_CTX_new(); 7679341618Scy if (!bnctx) 7680341618Scy goto fail; 7681341618Scy Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code, 7682341618Scy pkex->identifier, bnctx, &group); 7683341618Scy if (!Qr) 7684341618Scy goto fail; 7685341618Scy 7686341618Scy /* Y' = N - Qr */ 7687341618Scy Y = EC_POINT_new(group); 7688341618Scy N = EC_POINT_new(group); 7689341618Scy Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL); 7690341618Scy Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL); 7691341618Scy if (!Y || !N || !Nx || !Ny || 7692341618Scy EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 || 7693341618Scy EC_POINT_is_at_infinity(group, N) || 7694341618Scy !EC_POINT_is_on_curve(group, N, bnctx) || 7695341618Scy EC_POINT_invert(group, Qr, bnctx) != 1 || 7696341618Scy EC_POINT_add(group, Y, N, Qr, bnctx) != 1 || 7697341618Scy EC_POINT_is_at_infinity(group, Y) || 7698341618Scy !EC_POINT_is_on_curve(group, Y, bnctx)) { 7699341618Scy dpp_pkex_fail(pkex, "Invalid Encrypted Key value"); 7700341618Scy pkex->t++; 7701341618Scy goto fail; 7702341618Scy } 7703341618Scy dpp_debug_print_point("DPP: N", group, N); 7704341618Scy dpp_debug_print_point("DPP: Y'", group, Y); 7705341618Scy 7706341618Scy pkex->exchange_done = 1; 7707341618Scy 7708341618Scy /* ECDH: J = a * Y��� */ 7709341618Scy Y_ec = EC_KEY_new(); 7710341618Scy if (!Y_ec || 7711341618Scy EC_KEY_set_group(Y_ec, group) != 1 || 7712341618Scy EC_KEY_set_public_key(Y_ec, Y) != 1) 7713341618Scy goto fail; 7714341618Scy pkex->y = EVP_PKEY_new(); 7715341618Scy if (!pkex->y || 7716341618Scy EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1) 7717341618Scy goto fail; 7718351611Scy if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0) 7719341618Scy goto fail; 7720341618Scy 7721341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)", 7722341618Scy Jx, Jx_len); 7723341618Scy 7724341618Scy /* u = HMAC(J.x, MAC-Initiator | A.x | Y���.x | X.x ) */ 7725341618Scy A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0); 7726341618Scy Y_pub = dpp_get_pubkey_point(pkex->y, 0); 7727341618Scy X_pub = dpp_get_pubkey_point(pkex->x, 0); 7728341618Scy if (!A_pub || !Y_pub || !X_pub) 7729341618Scy goto fail; 7730341618Scy addr[0] = pkex->own_mac; 7731341618Scy len[0] = ETH_ALEN; 7732341618Scy addr[1] = wpabuf_head(A_pub); 7733341618Scy len[1] = wpabuf_len(A_pub) / 2; 7734341618Scy addr[2] = wpabuf_head(Y_pub); 7735341618Scy len[2] = wpabuf_len(Y_pub) / 2; 7736341618Scy addr[3] = wpabuf_head(X_pub); 7737341618Scy len[3] = wpabuf_len(X_pub) / 2; 7738341618Scy if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0) 7739341618Scy goto fail; 7740341618Scy wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len); 7741341618Scy 7742341618Scy /* K = x * Y��� */ 7743351611Scy if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0) 7744341618Scy goto fail; 7745341618Scy 7746341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)", 7747341618Scy Kx, Kx_len); 7748341618Scy 7749341618Scy /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x) 7750341618Scy */ 7751341618Scy res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac, 7752341618Scy pkex->Mx, curve->prime_len, 7753341618Scy attr_key /* N.x */, attr_key_len / 2, 7754341618Scy pkex->code, Kx, Kx_len, 7755341618Scy pkex->z, curve->hash_len); 7756341618Scy os_memset(Kx, 0, Kx_len); 7757341618Scy if (res < 0) 7758341618Scy goto fail; 7759341618Scy 7760341618Scy msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u); 7761341618Scy if (!msg) 7762341618Scy goto fail; 7763341618Scy 7764341618Scyout: 7765341618Scy wpabuf_free(A_pub); 7766341618Scy wpabuf_free(X_pub); 7767341618Scy wpabuf_free(Y_pub); 7768341618Scy EC_POINT_free(Qr); 7769341618Scy EC_POINT_free(Y); 7770341618Scy EC_POINT_free(N); 7771341618Scy BN_free(Nx); 7772341618Scy BN_free(Ny); 7773341618Scy EC_KEY_free(Y_ec); 7774341618Scy BN_CTX_free(bnctx); 7775351611Scy EC_GROUP_free(group); 7776341618Scy return msg; 7777341618Scyfail: 7778341618Scy wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed"); 7779341618Scy goto out; 7780341618Scy} 7781341618Scy 7782341618Scy 7783341618Scystatic struct wpabuf * 7784341618Scydpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex, 7785341618Scy const struct wpabuf *B_pub, const u8 *v) 7786341618Scy{ 7787341618Scy const struct dpp_curve_params *curve = pkex->own_bi->curve; 7788341618Scy struct wpabuf *msg = NULL; 7789341618Scy const u8 *addr[2]; 7790341618Scy size_t len[2]; 7791341618Scy u8 octet; 7792341618Scy u8 *wrapped; 7793341618Scy struct wpabuf *clear = NULL; 7794341618Scy size_t clear_len, attr_len; 7795341618Scy 7796341618Scy /* {B, v [bootstrapping info]}z */ 7797341618Scy clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len; 7798341618Scy clear = wpabuf_alloc(clear_len); 7799341618Scy attr_len = 4 + clear_len + AES_BLOCK_SIZE; 7800341618Scy#ifdef CONFIG_TESTING_OPTIONS 7801341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) 7802341618Scy attr_len += 5; 7803341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7804341618Scy msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len); 7805341618Scy if (!clear || !msg) 7806341618Scy goto fail; 7807341618Scy 7808341618Scy#ifdef CONFIG_TESTING_OPTIONS 7809341618Scy if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) { 7810341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key"); 7811341618Scy goto skip_bootstrap_key; 7812341618Scy } 7813341618Scy if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) { 7814341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key"); 7815341618Scy wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); 7816341618Scy wpabuf_put_le16(clear, 2 * curve->prime_len); 7817341618Scy if (dpp_test_gen_invalid_key(clear, curve) < 0) 7818341618Scy goto fail; 7819341618Scy goto skip_bootstrap_key; 7820341618Scy } 7821341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7822341618Scy 7823341618Scy /* B in Bootstrap Key attribute */ 7824341618Scy wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY); 7825341618Scy wpabuf_put_le16(clear, wpabuf_len(B_pub)); 7826341618Scy wpabuf_put_buf(clear, B_pub); 7827341618Scy 7828341618Scy#ifdef CONFIG_TESTING_OPTIONS 7829341618Scyskip_bootstrap_key: 7830341618Scy if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) { 7831341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag"); 7832341618Scy goto skip_r_auth_tag; 7833341618Scy } 7834341618Scy if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) { 7835341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch"); 7836341618Scy wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG); 7837341618Scy wpabuf_put_le16(clear, curve->hash_len); 7838341618Scy wpabuf_put_data(clear, v, curve->hash_len - 1); 7839341618Scy wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01); 7840341618Scy goto skip_r_auth_tag; 7841341618Scy } 7842341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7843341618Scy 7844341618Scy /* v in R-Auth tag attribute */ 7845341618Scy wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG); 7846341618Scy wpabuf_put_le16(clear, curve->hash_len); 7847341618Scy wpabuf_put_data(clear, v, curve->hash_len); 7848341618Scy 7849341618Scy#ifdef CONFIG_TESTING_OPTIONS 7850341618Scyskip_r_auth_tag: 7851341618Scy if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) { 7852341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data"); 7853341618Scy goto skip_wrapped_data; 7854341618Scy } 7855341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7856341618Scy 7857341618Scy addr[0] = wpabuf_head_u8(msg) + 2; 7858341618Scy len[0] = DPP_HDR_LEN; 7859341618Scy octet = 1; 7860341618Scy addr[1] = &octet; 7861341618Scy len[1] = sizeof(octet); 7862341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 7863341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 7864341618Scy 7865341618Scy wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA); 7866341618Scy wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 7867341618Scy wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE); 7868341618Scy 7869341618Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear); 7870341618Scy if (aes_siv_encrypt(pkex->z, curve->hash_len, 7871341618Scy wpabuf_head(clear), wpabuf_len(clear), 7872341618Scy 2, addr, len, wrapped) < 0) 7873341618Scy goto fail; 7874341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 7875341618Scy wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE); 7876341618Scy 7877341618Scy#ifdef CONFIG_TESTING_OPTIONS 7878341618Scy if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) { 7879341618Scy wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data"); 7880341618Scy dpp_build_attr_status(msg, DPP_STATUS_OK); 7881341618Scy } 7882341618Scyskip_wrapped_data: 7883341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7884341618Scy 7885341618Scyout: 7886341618Scy wpabuf_free(clear); 7887341618Scy return msg; 7888341618Scy 7889341618Scyfail: 7890341618Scy wpabuf_free(msg); 7891341618Scy msg = NULL; 7892341618Scy goto out; 7893341618Scy} 7894341618Scy 7895341618Scy 7896341618Scystruct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex, 7897341618Scy const u8 *hdr, 7898341618Scy const u8 *buf, size_t buflen) 7899341618Scy{ 7900341618Scy const struct dpp_curve_params *curve = pkex->own_bi->curve; 7901341618Scy size_t Jx_len, Lx_len; 7902341618Scy u8 Jx[DPP_MAX_SHARED_SECRET_LEN]; 7903341618Scy u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; 7904341618Scy const u8 *wrapped_data, *b_key, *peer_u; 7905341618Scy u16 wrapped_data_len, b_key_len, peer_u_len = 0; 7906341618Scy const u8 *addr[4]; 7907341618Scy size_t len[4]; 7908341618Scy u8 octet; 7909341618Scy u8 *unwrapped = NULL; 7910341618Scy size_t unwrapped_len = 0; 7911341618Scy struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL; 7912341618Scy struct wpabuf *B_pub = NULL; 7913341618Scy u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN]; 7914341618Scy 7915341618Scy#ifdef CONFIG_TESTING_OPTIONS 7916341618Scy if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) { 7917341618Scy wpa_printf(MSG_INFO, 7918341618Scy "DPP: TESTING - stop at PKEX CR Request"); 7919341618Scy pkex->failed = 1; 7920341618Scy return NULL; 7921341618Scy } 7922341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 7923341618Scy 7924341618Scy if (!pkex->exchange_done || pkex->failed || 7925341618Scy pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator) 7926341618Scy goto fail; 7927341618Scy 7928341618Scy wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA, 7929341618Scy &wrapped_data_len); 7930341618Scy if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 7931341618Scy dpp_pkex_fail(pkex, 7932341618Scy "Missing or invalid required Wrapped Data attribute"); 7933341618Scy goto fail; 7934341618Scy } 7935341618Scy 7936341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 7937341618Scy wrapped_data, wrapped_data_len); 7938341618Scy unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 7939341618Scy unwrapped = os_malloc(unwrapped_len); 7940341618Scy if (!unwrapped) 7941341618Scy goto fail; 7942341618Scy 7943341618Scy addr[0] = hdr; 7944341618Scy len[0] = DPP_HDR_LEN; 7945341618Scy octet = 0; 7946341618Scy addr[1] = &octet; 7947341618Scy len[1] = sizeof(octet); 7948341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 7949341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 7950341618Scy 7951341618Scy if (aes_siv_decrypt(pkex->z, curve->hash_len, 7952341618Scy wrapped_data, wrapped_data_len, 7953341618Scy 2, addr, len, unwrapped) < 0) { 7954341618Scy dpp_pkex_fail(pkex, 7955341618Scy "AES-SIV decryption failed - possible PKEX code mismatch"); 7956341618Scy pkex->failed = 1; 7957341618Scy pkex->t++; 7958341618Scy goto fail; 7959341618Scy } 7960341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 7961341618Scy unwrapped, unwrapped_len); 7962341618Scy 7963341618Scy if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 7964341618Scy dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data"); 7965341618Scy goto fail; 7966341618Scy } 7967341618Scy 7968341618Scy b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY, 7969341618Scy &b_key_len); 7970341618Scy if (!b_key || b_key_len != 2 * curve->prime_len) { 7971341618Scy dpp_pkex_fail(pkex, "No valid peer bootstrapping key found"); 7972341618Scy goto fail; 7973341618Scy } 7974341618Scy pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key, 7975341618Scy b_key_len); 7976341618Scy if (!pkex->peer_bootstrap_key) { 7977341618Scy dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid"); 7978341618Scy goto fail; 7979341618Scy } 7980341618Scy dpp_debug_print_key("DPP: Peer bootstrap public key", 7981341618Scy pkex->peer_bootstrap_key); 7982341618Scy 7983341618Scy /* ECDH: J' = y * A' */ 7984351611Scy if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0) 7985341618Scy goto fail; 7986341618Scy 7987341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)", 7988341618Scy Jx, Jx_len); 7989341618Scy 7990341618Scy /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */ 7991341618Scy A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0); 7992341618Scy Y_pub = dpp_get_pubkey_point(pkex->y, 0); 7993341618Scy X_pub = dpp_get_pubkey_point(pkex->x, 0); 7994341618Scy if (!A_pub || !Y_pub || !X_pub) 7995341618Scy goto fail; 7996341618Scy addr[0] = pkex->peer_mac; 7997341618Scy len[0] = ETH_ALEN; 7998341618Scy addr[1] = wpabuf_head(A_pub); 7999341618Scy len[1] = wpabuf_len(A_pub) / 2; 8000341618Scy addr[2] = wpabuf_head(Y_pub); 8001341618Scy len[2] = wpabuf_len(Y_pub) / 2; 8002341618Scy addr[3] = wpabuf_head(X_pub); 8003341618Scy len[3] = wpabuf_len(X_pub) / 2; 8004341618Scy if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0) 8005341618Scy goto fail; 8006341618Scy 8007341618Scy peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG, 8008341618Scy &peer_u_len); 8009341618Scy if (!peer_u || peer_u_len != curve->hash_len || 8010341618Scy os_memcmp(peer_u, u, curve->hash_len) != 0) { 8011341618Scy dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found"); 8012341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'", 8013341618Scy u, curve->hash_len); 8014341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len); 8015341618Scy pkex->t++; 8016341618Scy goto fail; 8017341618Scy } 8018341618Scy wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received"); 8019341618Scy 8020341618Scy /* ECDH: L = b * X' */ 8021351611Scy if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0) 8022341618Scy goto fail; 8023341618Scy 8024341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)", 8025341618Scy Lx, Lx_len); 8026341618Scy 8027341618Scy /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */ 8028341618Scy B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0); 8029341618Scy if (!B_pub) 8030341618Scy goto fail; 8031341618Scy addr[0] = pkex->own_mac; 8032341618Scy len[0] = ETH_ALEN; 8033341618Scy addr[1] = wpabuf_head(B_pub); 8034341618Scy len[1] = wpabuf_len(B_pub) / 2; 8035341618Scy addr[2] = wpabuf_head(X_pub); 8036341618Scy len[2] = wpabuf_len(X_pub) / 2; 8037341618Scy addr[3] = wpabuf_head(Y_pub); 8038341618Scy len[3] = wpabuf_len(Y_pub) / 2; 8039341618Scy if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0) 8040341618Scy goto fail; 8041341618Scy wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len); 8042341618Scy 8043341618Scy msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v); 8044341618Scy if (!msg) 8045341618Scy goto fail; 8046341618Scy 8047341618Scyout: 8048341618Scy os_free(unwrapped); 8049341618Scy wpabuf_free(A_pub); 8050341618Scy wpabuf_free(B_pub); 8051341618Scy wpabuf_free(X_pub); 8052341618Scy wpabuf_free(Y_pub); 8053341618Scy return msg; 8054341618Scyfail: 8055341618Scy wpa_printf(MSG_DEBUG, 8056341618Scy "DPP: PKEX Commit-Reveal Request processing failed"); 8057341618Scy goto out; 8058341618Scy} 8059341618Scy 8060341618Scy 8061341618Scyint dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr, 8062341618Scy const u8 *buf, size_t buflen) 8063341618Scy{ 8064341618Scy const struct dpp_curve_params *curve = pkex->own_bi->curve; 8065341618Scy const u8 *wrapped_data, *b_key, *peer_v; 8066341618Scy u16 wrapped_data_len, b_key_len, peer_v_len = 0; 8067341618Scy const u8 *addr[4]; 8068341618Scy size_t len[4]; 8069341618Scy u8 octet; 8070341618Scy u8 *unwrapped = NULL; 8071341618Scy size_t unwrapped_len = 0; 8072341618Scy int ret = -1; 8073341618Scy u8 v[DPP_MAX_HASH_LEN]; 8074341618Scy size_t Lx_len; 8075341618Scy u8 Lx[DPP_MAX_SHARED_SECRET_LEN]; 8076341618Scy struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL; 8077341618Scy 8078341618Scy#ifdef CONFIG_TESTING_OPTIONS 8079341618Scy if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) { 8080341618Scy wpa_printf(MSG_INFO, 8081341618Scy "DPP: TESTING - stop at PKEX CR Response"); 8082341618Scy pkex->failed = 1; 8083341618Scy goto fail; 8084341618Scy } 8085341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 8086341618Scy 8087341618Scy if (!pkex->exchange_done || pkex->failed || 8088341618Scy pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator) 8089341618Scy goto fail; 8090341618Scy 8091341618Scy wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA, 8092341618Scy &wrapped_data_len); 8093341618Scy if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) { 8094341618Scy dpp_pkex_fail(pkex, 8095341618Scy "Missing or invalid required Wrapped Data attribute"); 8096341618Scy goto fail; 8097341618Scy } 8098341618Scy 8099341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext", 8100341618Scy wrapped_data, wrapped_data_len); 8101341618Scy unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE; 8102341618Scy unwrapped = os_malloc(unwrapped_len); 8103341618Scy if (!unwrapped) 8104341618Scy goto fail; 8105341618Scy 8106341618Scy addr[0] = hdr; 8107341618Scy len[0] = DPP_HDR_LEN; 8108341618Scy octet = 1; 8109341618Scy addr[1] = &octet; 8110341618Scy len[1] = sizeof(octet); 8111341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]); 8112341618Scy wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]); 8113341618Scy 8114341618Scy if (aes_siv_decrypt(pkex->z, curve->hash_len, 8115341618Scy wrapped_data, wrapped_data_len, 8116341618Scy 2, addr, len, unwrapped) < 0) { 8117341618Scy dpp_pkex_fail(pkex, 8118341618Scy "AES-SIV decryption failed - possible PKEX code mismatch"); 8119341618Scy pkex->t++; 8120341618Scy goto fail; 8121341618Scy } 8122341618Scy wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", 8123341618Scy unwrapped, unwrapped_len); 8124341618Scy 8125341618Scy if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) { 8126341618Scy dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data"); 8127341618Scy goto fail; 8128341618Scy } 8129341618Scy 8130341618Scy b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY, 8131341618Scy &b_key_len); 8132341618Scy if (!b_key || b_key_len != 2 * curve->prime_len) { 8133341618Scy dpp_pkex_fail(pkex, "No valid peer bootstrapping key found"); 8134341618Scy goto fail; 8135341618Scy } 8136341618Scy pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key, 8137341618Scy b_key_len); 8138341618Scy if (!pkex->peer_bootstrap_key) { 8139341618Scy dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid"); 8140341618Scy goto fail; 8141341618Scy } 8142341618Scy dpp_debug_print_key("DPP: Peer bootstrap public key", 8143341618Scy pkex->peer_bootstrap_key); 8144341618Scy 8145341618Scy /* ECDH: L' = x * B' */ 8146351611Scy if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0) 8147341618Scy goto fail; 8148341618Scy 8149341618Scy wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)", 8150341618Scy Lx, Lx_len); 8151341618Scy 8152341618Scy /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */ 8153341618Scy B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0); 8154341618Scy X_pub = dpp_get_pubkey_point(pkex->x, 0); 8155341618Scy Y_pub = dpp_get_pubkey_point(pkex->y, 0); 8156341618Scy if (!B_pub || !X_pub || !Y_pub) 8157341618Scy goto fail; 8158341618Scy addr[0] = pkex->peer_mac; 8159341618Scy len[0] = ETH_ALEN; 8160341618Scy addr[1] = wpabuf_head(B_pub); 8161341618Scy len[1] = wpabuf_len(B_pub) / 2; 8162341618Scy addr[2] = wpabuf_head(X_pub); 8163341618Scy len[2] = wpabuf_len(X_pub) / 2; 8164341618Scy addr[3] = wpabuf_head(Y_pub); 8165341618Scy len[3] = wpabuf_len(Y_pub) / 2; 8166341618Scy if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0) 8167341618Scy goto fail; 8168341618Scy 8169341618Scy peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG, 8170341618Scy &peer_v_len); 8171341618Scy if (!peer_v || peer_v_len != curve->hash_len || 8172341618Scy os_memcmp(peer_v, v, curve->hash_len) != 0) { 8173341618Scy dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found"); 8174341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'", 8175341618Scy v, curve->hash_len); 8176341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len); 8177341618Scy pkex->t++; 8178341618Scy goto fail; 8179341618Scy } 8180341618Scy wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received"); 8181341618Scy 8182341618Scy ret = 0; 8183341618Scyout: 8184341618Scy wpabuf_free(B_pub); 8185341618Scy wpabuf_free(X_pub); 8186341618Scy wpabuf_free(Y_pub); 8187341618Scy os_free(unwrapped); 8188341618Scy return ret; 8189341618Scyfail: 8190341618Scy goto out; 8191341618Scy} 8192341618Scy 8193341618Scy 8194341618Scyvoid dpp_pkex_free(struct dpp_pkex *pkex) 8195341618Scy{ 8196341618Scy if (!pkex) 8197341618Scy return; 8198341618Scy 8199341618Scy os_free(pkex->identifier); 8200341618Scy os_free(pkex->code); 8201341618Scy EVP_PKEY_free(pkex->x); 8202341618Scy EVP_PKEY_free(pkex->y); 8203341618Scy EVP_PKEY_free(pkex->peer_bootstrap_key); 8204341618Scy wpabuf_free(pkex->exchange_req); 8205341618Scy wpabuf_free(pkex->exchange_resp); 8206341618Scy os_free(pkex); 8207341618Scy} 8208341618Scy 8209341618Scy 8210341618Scy#ifdef CONFIG_TESTING_OPTIONS 8211341618Scychar * dpp_corrupt_connector_signature(const char *connector) 8212341618Scy{ 8213341618Scy char *tmp, *pos, *signed3 = NULL; 8214341618Scy unsigned char *signature = NULL; 8215341618Scy size_t signature_len = 0, signed3_len; 8216341618Scy 8217341618Scy tmp = os_zalloc(os_strlen(connector) + 5); 8218341618Scy if (!tmp) 8219341618Scy goto fail; 8220341618Scy os_memcpy(tmp, connector, os_strlen(connector)); 8221341618Scy 8222341618Scy pos = os_strchr(tmp, '.'); 8223341618Scy if (!pos) 8224341618Scy goto fail; 8225341618Scy 8226341618Scy pos = os_strchr(pos + 1, '.'); 8227341618Scy if (!pos) 8228341618Scy goto fail; 8229341618Scy pos++; 8230341618Scy 8231341618Scy wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s", 8232341618Scy pos); 8233341618Scy signature = base64_url_decode((const unsigned char *) pos, 8234341618Scy os_strlen(pos), &signature_len); 8235341618Scy if (!signature || signature_len == 0) 8236341618Scy goto fail; 8237341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature", 8238341618Scy signature, signature_len); 8239341618Scy signature[signature_len - 1] ^= 0x01; 8240341618Scy wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature", 8241341618Scy signature, signature_len); 8242341618Scy signed3 = (char *) base64_url_encode(signature, signature_len, 8243341618Scy &signed3_len, 0); 8244341618Scy if (!signed3) 8245341618Scy goto fail; 8246341618Scy os_memcpy(pos, signed3, signed3_len); 8247341618Scy pos[signed3_len] = '\0'; 8248341618Scy wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s", 8249341618Scy pos); 8250341618Scy 8251341618Scyout: 8252341618Scy os_free(signature); 8253341618Scy os_free(signed3); 8254341618Scy return tmp; 8255341618Scyfail: 8256341618Scy os_free(tmp); 8257341618Scy tmp = NULL; 8258341618Scy goto out; 8259341618Scy} 8260341618Scy#endif /* CONFIG_TESTING_OPTIONS */ 8261346981Scy 8262346981Scy 8263346981Scy#ifdef CONFIG_DPP2 8264346981Scy 8265346981Scystruct dpp_pfs * dpp_pfs_init(const u8 *net_access_key, 8266346981Scy size_t net_access_key_len) 8267346981Scy{ 8268346981Scy struct wpabuf *pub = NULL; 8269346981Scy EVP_PKEY *own_key; 8270346981Scy struct dpp_pfs *pfs; 8271346981Scy 8272346981Scy pfs = os_zalloc(sizeof(*pfs)); 8273346981Scy if (!pfs) 8274346981Scy return NULL; 8275346981Scy 8276346981Scy own_key = dpp_set_keypair(&pfs->curve, net_access_key, 8277346981Scy net_access_key_len); 8278346981Scy if (!own_key) { 8279346981Scy wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey"); 8280346981Scy goto fail; 8281346981Scy } 8282346981Scy EVP_PKEY_free(own_key); 8283346981Scy 8284346981Scy pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group); 8285346981Scy if (!pfs->ecdh) 8286346981Scy goto fail; 8287346981Scy 8288346981Scy pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0); 8289346981Scy pub = wpabuf_zeropad(pub, pfs->curve->prime_len); 8290346981Scy if (!pub) 8291346981Scy goto fail; 8292346981Scy 8293346981Scy pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub)); 8294346981Scy if (!pfs->ie) 8295346981Scy goto fail; 8296346981Scy wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION); 8297346981Scy wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub)); 8298346981Scy wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM); 8299346981Scy wpabuf_put_le16(pfs->ie, pfs->curve->ike_group); 8300346981Scy wpabuf_put_buf(pfs->ie, pub); 8301346981Scy wpabuf_free(pub); 8302346981Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element", 8303346981Scy pfs->ie); 8304346981Scy 8305346981Scy return pfs; 8306346981Scyfail: 8307346981Scy wpabuf_free(pub); 8308346981Scy dpp_pfs_free(pfs); 8309346981Scy return NULL; 8310346981Scy} 8311346981Scy 8312346981Scy 8313346981Scyint dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len) 8314346981Scy{ 8315346981Scy if (peer_ie_len < 2) 8316346981Scy return -1; 8317346981Scy if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) { 8318346981Scy wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS"); 8319346981Scy return -1; 8320346981Scy } 8321346981Scy 8322346981Scy pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2, 8323346981Scy peer_ie_len - 2); 8324346981Scy pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len); 8325346981Scy if (!pfs->secret) { 8326346981Scy wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key"); 8327346981Scy return -1; 8328346981Scy } 8329346981Scy wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret); 8330346981Scy return 0; 8331346981Scy} 8332346981Scy 8333346981Scy 8334346981Scyvoid dpp_pfs_free(struct dpp_pfs *pfs) 8335346981Scy{ 8336346981Scy if (!pfs) 8337346981Scy return; 8338346981Scy crypto_ecdh_deinit(pfs->ecdh); 8339346981Scy wpabuf_free(pfs->ie); 8340346981Scy wpabuf_clear_free(pfs->secret); 8341346981Scy os_free(pfs); 8342346981Scy} 8343346981Scy 8344346981Scy#endif /* CONFIG_DPP2 */ 8345346981Scy 8346346981Scy 8347346981Scystatic unsigned int dpp_next_id(struct dpp_global *dpp) 8348346981Scy{ 8349346981Scy struct dpp_bootstrap_info *bi; 8350346981Scy unsigned int max_id = 0; 8351346981Scy 8352346981Scy dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { 8353346981Scy if (bi->id > max_id) 8354346981Scy max_id = bi->id; 8355346981Scy } 8356346981Scy return max_id + 1; 8357346981Scy} 8358346981Scy 8359346981Scy 8360346981Scystatic int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id) 8361346981Scy{ 8362346981Scy struct dpp_bootstrap_info *bi, *tmp; 8363346981Scy int found = 0; 8364346981Scy 8365346981Scy if (!dpp) 8366346981Scy return -1; 8367346981Scy 8368346981Scy dl_list_for_each_safe(bi, tmp, &dpp->bootstrap, 8369346981Scy struct dpp_bootstrap_info, list) { 8370346981Scy if (id && bi->id != id) 8371346981Scy continue; 8372346981Scy found = 1; 8373346981Scy dl_list_del(&bi->list); 8374346981Scy dpp_bootstrap_info_free(bi); 8375346981Scy } 8376346981Scy 8377346981Scy if (id == 0) 8378346981Scy return 0; /* flush succeeds regardless of entries found */ 8379346981Scy return found ? 0 : -1; 8380346981Scy} 8381346981Scy 8382346981Scy 8383346981Scystruct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp, 8384346981Scy const char *uri) 8385346981Scy{ 8386346981Scy struct dpp_bootstrap_info *bi; 8387346981Scy 8388346981Scy if (!dpp) 8389346981Scy return NULL; 8390346981Scy 8391346981Scy bi = dpp_parse_qr_code(uri); 8392346981Scy if (!bi) 8393346981Scy return NULL; 8394346981Scy 8395346981Scy bi->id = dpp_next_id(dpp); 8396346981Scy dl_list_add(&dpp->bootstrap, &bi->list); 8397346981Scy return bi; 8398346981Scy} 8399346981Scy 8400346981Scy 8401346981Scyint dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd) 8402346981Scy{ 8403346981Scy char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL; 8404346981Scy char *key = NULL; 8405346981Scy u8 *privkey = NULL; 8406346981Scy size_t privkey_len = 0; 8407346981Scy size_t len; 8408346981Scy int ret = -1; 8409346981Scy struct dpp_bootstrap_info *bi; 8410346981Scy 8411346981Scy if (!dpp) 8412346981Scy return -1; 8413346981Scy 8414346981Scy bi = os_zalloc(sizeof(*bi)); 8415346981Scy if (!bi) 8416346981Scy goto fail; 8417346981Scy 8418346981Scy if (os_strstr(cmd, "type=qrcode")) 8419346981Scy bi->type = DPP_BOOTSTRAP_QR_CODE; 8420346981Scy else if (os_strstr(cmd, "type=pkex")) 8421346981Scy bi->type = DPP_BOOTSTRAP_PKEX; 8422346981Scy else 8423346981Scy goto fail; 8424346981Scy 8425346981Scy chan = get_param(cmd, " chan="); 8426346981Scy mac = get_param(cmd, " mac="); 8427346981Scy info = get_param(cmd, " info="); 8428346981Scy curve = get_param(cmd, " curve="); 8429346981Scy key = get_param(cmd, " key="); 8430346981Scy 8431346981Scy if (key) { 8432346981Scy privkey_len = os_strlen(key) / 2; 8433346981Scy privkey = os_malloc(privkey_len); 8434346981Scy if (!privkey || 8435346981Scy hexstr2bin(key, privkey, privkey_len) < 0) 8436346981Scy goto fail; 8437346981Scy } 8438346981Scy 8439346981Scy pk = dpp_keygen(bi, curve, privkey, privkey_len); 8440346981Scy if (!pk) 8441346981Scy goto fail; 8442346981Scy 8443346981Scy len = 4; /* "DPP:" */ 8444346981Scy if (chan) { 8445346981Scy if (dpp_parse_uri_chan_list(bi, chan) < 0) 8446346981Scy goto fail; 8447346981Scy len += 3 + os_strlen(chan); /* C:...; */ 8448346981Scy } 8449346981Scy if (mac) { 8450346981Scy if (dpp_parse_uri_mac(bi, mac) < 0) 8451346981Scy goto fail; 8452346981Scy len += 3 + os_strlen(mac); /* M:...; */ 8453346981Scy } 8454346981Scy if (info) { 8455346981Scy if (dpp_parse_uri_info(bi, info) < 0) 8456346981Scy goto fail; 8457346981Scy len += 3 + os_strlen(info); /* I:...; */ 8458346981Scy } 8459346981Scy len += 4 + os_strlen(pk); 8460346981Scy bi->uri = os_malloc(len + 1); 8461346981Scy if (!bi->uri) 8462346981Scy goto fail; 8463346981Scy os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;", 8464346981Scy chan ? "C:" : "", chan ? chan : "", chan ? ";" : "", 8465346981Scy mac ? "M:" : "", mac ? mac : "", mac ? ";" : "", 8466346981Scy info ? "I:" : "", info ? info : "", info ? ";" : "", 8467346981Scy pk); 8468346981Scy bi->id = dpp_next_id(dpp); 8469346981Scy dl_list_add(&dpp->bootstrap, &bi->list); 8470346981Scy ret = bi->id; 8471346981Scy bi = NULL; 8472346981Scyfail: 8473346981Scy os_free(curve); 8474346981Scy os_free(pk); 8475346981Scy os_free(chan); 8476346981Scy os_free(mac); 8477346981Scy os_free(info); 8478346981Scy str_clear_free(key); 8479346981Scy bin_clear_free(privkey, privkey_len); 8480346981Scy dpp_bootstrap_info_free(bi); 8481346981Scy return ret; 8482346981Scy} 8483346981Scy 8484346981Scy 8485346981Scystruct dpp_bootstrap_info * 8486346981Scydpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id) 8487346981Scy{ 8488346981Scy struct dpp_bootstrap_info *bi; 8489346981Scy 8490346981Scy if (!dpp) 8491346981Scy return NULL; 8492346981Scy 8493346981Scy dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { 8494346981Scy if (bi->id == id) 8495346981Scy return bi; 8496346981Scy } 8497346981Scy return NULL; 8498346981Scy} 8499346981Scy 8500346981Scy 8501346981Scyint dpp_bootstrap_remove(struct dpp_global *dpp, const char *id) 8502346981Scy{ 8503346981Scy unsigned int id_val; 8504346981Scy 8505346981Scy if (os_strcmp(id, "*") == 0) { 8506346981Scy id_val = 0; 8507346981Scy } else { 8508346981Scy id_val = atoi(id); 8509346981Scy if (id_val == 0) 8510346981Scy return -1; 8511346981Scy } 8512346981Scy 8513346981Scy return dpp_bootstrap_del(dpp, id_val); 8514346981Scy} 8515346981Scy 8516346981Scy 8517346981Scystruct dpp_bootstrap_info * 8518346981Scydpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer, 8519346981Scy unsigned int freq) 8520346981Scy{ 8521346981Scy struct dpp_bootstrap_info *bi; 8522346981Scy 8523346981Scy bi = os_zalloc(sizeof(*bi)); 8524346981Scy if (!bi) 8525346981Scy return NULL; 8526346981Scy bi->id = dpp_next_id(dpp); 8527346981Scy bi->type = DPP_BOOTSTRAP_PKEX; 8528346981Scy os_memcpy(bi->mac_addr, peer, ETH_ALEN); 8529346981Scy bi->num_freq = 1; 8530346981Scy bi->freq[0] = freq; 8531346981Scy bi->curve = pkex->own_bi->curve; 8532346981Scy bi->pubkey = pkex->peer_bootstrap_key; 8533346981Scy pkex->peer_bootstrap_key = NULL; 8534346981Scy if (dpp_bootstrap_key_hash(bi) < 0) { 8535346981Scy dpp_bootstrap_info_free(bi); 8536346981Scy return NULL; 8537346981Scy } 8538346981Scy dpp_pkex_free(pkex); 8539346981Scy dl_list_add(&dpp->bootstrap, &bi->list); 8540346981Scy return bi; 8541346981Scy} 8542346981Scy 8543346981Scy 8544346981Scyconst char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id) 8545346981Scy{ 8546346981Scy struct dpp_bootstrap_info *bi; 8547346981Scy 8548346981Scy bi = dpp_bootstrap_get_id(dpp, id); 8549346981Scy if (!bi) 8550346981Scy return NULL; 8551346981Scy return bi->uri; 8552346981Scy} 8553346981Scy 8554346981Scy 8555346981Scyint dpp_bootstrap_info(struct dpp_global *dpp, int id, 8556346981Scy char *reply, int reply_size) 8557346981Scy{ 8558346981Scy struct dpp_bootstrap_info *bi; 8559351611Scy char pkhash[2 * SHA256_MAC_LEN + 1]; 8560346981Scy 8561346981Scy bi = dpp_bootstrap_get_id(dpp, id); 8562346981Scy if (!bi) 8563346981Scy return -1; 8564351611Scy wpa_snprintf_hex(pkhash, sizeof(pkhash), bi->pubkey_hash, 8565351611Scy SHA256_MAC_LEN); 8566346981Scy return os_snprintf(reply, reply_size, "type=%s\n" 8567346981Scy "mac_addr=" MACSTR "\n" 8568346981Scy "info=%s\n" 8569346981Scy "num_freq=%u\n" 8570351611Scy "curve=%s\n" 8571351611Scy "pkhash=%s\n", 8572346981Scy dpp_bootstrap_type_txt(bi->type), 8573346981Scy MAC2STR(bi->mac_addr), 8574346981Scy bi->info ? bi->info : "", 8575346981Scy bi->num_freq, 8576351611Scy bi->curve->name, 8577351611Scy pkhash); 8578346981Scy} 8579346981Scy 8580346981Scy 8581346981Scyvoid dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap, 8582346981Scy const u8 *r_bootstrap, 8583346981Scy struct dpp_bootstrap_info **own_bi, 8584346981Scy struct dpp_bootstrap_info **peer_bi) 8585346981Scy{ 8586346981Scy struct dpp_bootstrap_info *bi; 8587346981Scy 8588346981Scy *own_bi = NULL; 8589346981Scy *peer_bi = NULL; 8590346981Scy if (!dpp) 8591346981Scy return; 8592346981Scy 8593346981Scy dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) { 8594346981Scy if (!*own_bi && bi->own && 8595346981Scy os_memcmp(bi->pubkey_hash, r_bootstrap, 8596346981Scy SHA256_MAC_LEN) == 0) { 8597346981Scy wpa_printf(MSG_DEBUG, 8598346981Scy "DPP: Found matching own bootstrapping information"); 8599346981Scy *own_bi = bi; 8600346981Scy } 8601346981Scy 8602346981Scy if (!*peer_bi && !bi->own && 8603346981Scy os_memcmp(bi->pubkey_hash, i_bootstrap, 8604346981Scy SHA256_MAC_LEN) == 0) { 8605346981Scy wpa_printf(MSG_DEBUG, 8606346981Scy "DPP: Found matching peer bootstrapping information"); 8607346981Scy *peer_bi = bi; 8608346981Scy } 8609346981Scy 8610346981Scy if (*own_bi && *peer_bi) 8611346981Scy break; 8612346981Scy } 8613346981Scy 8614346981Scy} 8615346981Scy 8616346981Scy 8617346981Scystatic unsigned int dpp_next_configurator_id(struct dpp_global *dpp) 8618346981Scy{ 8619346981Scy struct dpp_configurator *conf; 8620346981Scy unsigned int max_id = 0; 8621346981Scy 8622346981Scy dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator, 8623346981Scy list) { 8624346981Scy if (conf->id > max_id) 8625346981Scy max_id = conf->id; 8626346981Scy } 8627346981Scy return max_id + 1; 8628346981Scy} 8629346981Scy 8630346981Scy 8631346981Scyint dpp_configurator_add(struct dpp_global *dpp, const char *cmd) 8632346981Scy{ 8633346981Scy char *curve = NULL; 8634346981Scy char *key = NULL; 8635346981Scy u8 *privkey = NULL; 8636346981Scy size_t privkey_len = 0; 8637346981Scy int ret = -1; 8638346981Scy struct dpp_configurator *conf = NULL; 8639346981Scy 8640346981Scy curve = get_param(cmd, " curve="); 8641346981Scy key = get_param(cmd, " key="); 8642346981Scy 8643346981Scy if (key) { 8644346981Scy privkey_len = os_strlen(key) / 2; 8645346981Scy privkey = os_malloc(privkey_len); 8646346981Scy if (!privkey || 8647346981Scy hexstr2bin(key, privkey, privkey_len) < 0) 8648346981Scy goto fail; 8649346981Scy } 8650346981Scy 8651346981Scy conf = dpp_keygen_configurator(curve, privkey, privkey_len); 8652346981Scy if (!conf) 8653346981Scy goto fail; 8654346981Scy 8655346981Scy conf->id = dpp_next_configurator_id(dpp); 8656346981Scy dl_list_add(&dpp->configurator, &conf->list); 8657346981Scy ret = conf->id; 8658346981Scy conf = NULL; 8659346981Scyfail: 8660346981Scy os_free(curve); 8661346981Scy str_clear_free(key); 8662346981Scy bin_clear_free(privkey, privkey_len); 8663346981Scy dpp_configurator_free(conf); 8664346981Scy return ret; 8665346981Scy} 8666346981Scy 8667346981Scy 8668346981Scystatic int dpp_configurator_del(struct dpp_global *dpp, unsigned int id) 8669346981Scy{ 8670346981Scy struct dpp_configurator *conf, *tmp; 8671346981Scy int found = 0; 8672346981Scy 8673346981Scy if (!dpp) 8674346981Scy return -1; 8675346981Scy 8676346981Scy dl_list_for_each_safe(conf, tmp, &dpp->configurator, 8677346981Scy struct dpp_configurator, list) { 8678346981Scy if (id && conf->id != id) 8679346981Scy continue; 8680346981Scy found = 1; 8681346981Scy dl_list_del(&conf->list); 8682346981Scy dpp_configurator_free(conf); 8683346981Scy } 8684346981Scy 8685346981Scy if (id == 0) 8686346981Scy return 0; /* flush succeeds regardless of entries found */ 8687346981Scy return found ? 0 : -1; 8688346981Scy} 8689346981Scy 8690346981Scy 8691346981Scyint dpp_configurator_remove(struct dpp_global *dpp, const char *id) 8692346981Scy{ 8693346981Scy unsigned int id_val; 8694346981Scy 8695346981Scy if (os_strcmp(id, "*") == 0) { 8696346981Scy id_val = 0; 8697346981Scy } else { 8698346981Scy id_val = atoi(id); 8699346981Scy if (id_val == 0) 8700346981Scy return -1; 8701346981Scy } 8702346981Scy 8703346981Scy return dpp_configurator_del(dpp, id_val); 8704346981Scy} 8705346981Scy 8706346981Scy 8707346981Scyint dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id, 8708346981Scy char *buf, size_t buflen) 8709346981Scy{ 8710346981Scy struct dpp_configurator *conf; 8711346981Scy 8712346981Scy conf = dpp_configurator_get_id(dpp, id); 8713346981Scy if (!conf) 8714346981Scy return -1; 8715346981Scy 8716346981Scy return dpp_configurator_get_key(conf, buf, buflen); 8717346981Scy} 8718346981Scy 8719346981Scy 8720351611Scy#ifdef CONFIG_DPP2 8721351611Scy 8722351611Scystatic void dpp_connection_free(struct dpp_connection *conn) 8723346981Scy{ 8724351611Scy if (conn->sock >= 0) { 8725351611Scy wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d", 8726351611Scy conn->sock); 8727351611Scy eloop_unregister_sock(conn->sock, EVENT_TYPE_READ); 8728351611Scy eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE); 8729351611Scy close(conn->sock); 8730351611Scy } 8731351611Scy wpabuf_free(conn->msg); 8732351611Scy wpabuf_free(conn->msg_out); 8733351611Scy dpp_auth_deinit(conn->auth); 8734351611Scy os_free(conn); 8735351611Scy} 8736351611Scy 8737351611Scy 8738351611Scystatic void dpp_connection_remove(struct dpp_connection *conn) 8739351611Scy{ 8740351611Scy dl_list_del(&conn->list); 8741351611Scy dpp_connection_free(conn); 8742351611Scy} 8743351611Scy 8744351611Scy 8745351611Scystatic void dpp_tcp_init_flush(struct dpp_global *dpp) 8746351611Scy{ 8747351611Scy struct dpp_connection *conn, *tmp; 8748351611Scy 8749351611Scy dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection, 8750351611Scy list) 8751351611Scy dpp_connection_remove(conn); 8752351611Scy} 8753351611Scy 8754351611Scy 8755351611Scystatic void dpp_relay_controller_free(struct dpp_relay_controller *ctrl) 8756351611Scy{ 8757351611Scy struct dpp_connection *conn, *tmp; 8758351611Scy 8759351611Scy dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection, 8760351611Scy list) 8761351611Scy dpp_connection_remove(conn); 8762351611Scy os_free(ctrl); 8763351611Scy} 8764351611Scy 8765351611Scy 8766351611Scystatic void dpp_relay_flush_controllers(struct dpp_global *dpp) 8767351611Scy{ 8768351611Scy struct dpp_relay_controller *ctrl, *tmp; 8769351611Scy 8770351611Scy if (!dpp) 8771351611Scy return; 8772351611Scy 8773351611Scy dl_list_for_each_safe(ctrl, tmp, &dpp->controllers, 8774351611Scy struct dpp_relay_controller, list) { 8775351611Scy dl_list_del(&ctrl->list); 8776351611Scy dpp_relay_controller_free(ctrl); 8777351611Scy } 8778351611Scy} 8779351611Scy 8780351611Scy#endif /* CONFIG_DPP2 */ 8781351611Scy 8782351611Scy 8783351611Scystruct dpp_global * dpp_global_init(struct dpp_global_config *config) 8784351611Scy{ 8785346981Scy struct dpp_global *dpp; 8786346981Scy 8787346981Scy dpp = os_zalloc(sizeof(*dpp)); 8788346981Scy if (!dpp) 8789346981Scy return NULL; 8790351611Scy dpp->msg_ctx = config->msg_ctx; 8791351611Scy#ifdef CONFIG_DPP2 8792351611Scy dpp->cb_ctx = config->cb_ctx; 8793351611Scy dpp->process_conf_obj = config->process_conf_obj; 8794351611Scy#endif /* CONFIG_DPP2 */ 8795346981Scy 8796346981Scy dl_list_init(&dpp->bootstrap); 8797346981Scy dl_list_init(&dpp->configurator); 8798351611Scy#ifdef CONFIG_DPP2 8799351611Scy dl_list_init(&dpp->controllers); 8800351611Scy dl_list_init(&dpp->tcp_init); 8801351611Scy#endif /* CONFIG_DPP2 */ 8802346981Scy 8803346981Scy return dpp; 8804346981Scy} 8805346981Scy 8806346981Scy 8807346981Scyvoid dpp_global_clear(struct dpp_global *dpp) 8808346981Scy{ 8809346981Scy if (!dpp) 8810346981Scy return; 8811346981Scy 8812346981Scy dpp_bootstrap_del(dpp, 0); 8813346981Scy dpp_configurator_del(dpp, 0); 8814351611Scy#ifdef CONFIG_DPP2 8815351611Scy dpp_tcp_init_flush(dpp); 8816351611Scy dpp_relay_flush_controllers(dpp); 8817351611Scy dpp_controller_stop(dpp); 8818351611Scy#endif /* CONFIG_DPP2 */ 8819346981Scy} 8820346981Scy 8821346981Scy 8822346981Scyvoid dpp_global_deinit(struct dpp_global *dpp) 8823346981Scy{ 8824346981Scy dpp_global_clear(dpp); 8825346981Scy os_free(dpp); 8826346981Scy} 8827351611Scy 8828351611Scy 8829351611Scy#ifdef CONFIG_DPP2 8830351611Scy 8831351611Scystatic void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx); 8832351611Scystatic void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx); 8833351611Scystatic void dpp_controller_auth_success(struct dpp_connection *conn, 8834351611Scy int initiator); 8835351611Scy 8836351611Scy 8837351611Scyint dpp_relay_add_controller(struct dpp_global *dpp, 8838351611Scy struct dpp_relay_config *config) 8839351611Scy{ 8840351611Scy struct dpp_relay_controller *ctrl; 8841351611Scy 8842351611Scy if (!dpp) 8843351611Scy return -1; 8844351611Scy 8845351611Scy ctrl = os_zalloc(sizeof(*ctrl)); 8846351611Scy if (!ctrl) 8847351611Scy return -1; 8848351611Scy dl_list_init(&ctrl->conn); 8849351611Scy ctrl->global = dpp; 8850351611Scy os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr)); 8851351611Scy os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN); 8852351611Scy ctrl->cb_ctx = config->cb_ctx; 8853351611Scy ctrl->tx = config->tx; 8854351611Scy ctrl->gas_resp_tx = config->gas_resp_tx; 8855351611Scy dl_list_add(&dpp->controllers, &ctrl->list); 8856351611Scy return 0; 8857351611Scy} 8858351611Scy 8859351611Scy 8860351611Scystatic struct dpp_relay_controller * 8861351611Scydpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash) 8862351611Scy{ 8863351611Scy struct dpp_relay_controller *ctrl; 8864351611Scy 8865351611Scy if (!dpp) 8866351611Scy return NULL; 8867351611Scy 8868351611Scy dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller, 8869351611Scy list) { 8870351611Scy if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0) 8871351611Scy return ctrl; 8872351611Scy } 8873351611Scy 8874351611Scy return NULL; 8875351611Scy} 8876351611Scy 8877351611Scy 8878351611Scystatic void dpp_controller_gas_done(struct dpp_connection *conn) 8879351611Scy{ 8880351611Scy struct dpp_authentication *auth = conn->auth; 8881351611Scy 8882351611Scy if (auth->peer_version >= 2 && 8883351611Scy auth->conf_resp_status == DPP_STATUS_OK) { 8884351611Scy wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result"); 8885351611Scy auth->waiting_conf_result = 1; 8886351611Scy return; 8887351611Scy } 8888351611Scy 8889351611Scy wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT); 8890351611Scy dpp_connection_remove(conn); 8891351611Scy} 8892351611Scy 8893351611Scy 8894351611Scystatic int dpp_tcp_send(struct dpp_connection *conn) 8895351611Scy{ 8896351611Scy int res; 8897351611Scy 8898351611Scy if (!conn->msg_out) { 8899351611Scy eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE); 8900351611Scy conn->write_eloop = 0; 8901351611Scy return -1; 8902351611Scy } 8903351611Scy res = send(conn->sock, 8904351611Scy wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos, 8905351611Scy wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0); 8906351611Scy if (res < 0) { 8907351611Scy wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s", 8908351611Scy strerror(errno)); 8909351611Scy dpp_connection_remove(conn); 8910351611Scy return -1; 8911351611Scy } 8912351611Scy 8913351611Scy conn->msg_out_pos += res; 8914351611Scy if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) { 8915351611Scy wpa_printf(MSG_DEBUG, 8916351611Scy "DPP: %u/%u bytes of message sent to Controller", 8917351611Scy (unsigned int) conn->msg_out_pos, 8918351611Scy (unsigned int) wpabuf_len(conn->msg_out)); 8919351611Scy if (!conn->write_eloop && 8920351611Scy eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, 8921351611Scy dpp_conn_tx_ready, conn, NULL) == 0) 8922351611Scy conn->write_eloop = 1; 8923351611Scy return 1; 8924351611Scy } 8925351611Scy 8926351611Scy wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP"); 8927351611Scy wpabuf_free(conn->msg_out); 8928351611Scy conn->msg_out = NULL; 8929351611Scy conn->msg_out_pos = 0; 8930351611Scy eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE); 8931351611Scy conn->write_eloop = 0; 8932351611Scy if (!conn->read_eloop && 8933351611Scy eloop_register_sock(conn->sock, EVENT_TYPE_READ, 8934351611Scy dpp_controller_rx, conn, NULL) == 0) 8935351611Scy conn->read_eloop = 1; 8936351611Scy if (conn->on_tcp_tx_complete_remove) { 8937351611Scy dpp_connection_remove(conn); 8938351611Scy } else if (conn->ctrl && conn->on_tcp_tx_complete_gas_done && 8939351611Scy conn->auth) { 8940351611Scy dpp_controller_gas_done(conn); 8941351611Scy } else if (conn->on_tcp_tx_complete_auth_ok) { 8942351611Scy conn->on_tcp_tx_complete_auth_ok = 0; 8943351611Scy dpp_controller_auth_success(conn, 1); 8944351611Scy } 8945351611Scy 8946351611Scy return 0; 8947351611Scy} 8948351611Scy 8949351611Scy 8950351611Scystatic void dpp_controller_start_gas_client(struct dpp_connection *conn) 8951351611Scy{ 8952351611Scy struct dpp_authentication *auth = conn->auth; 8953351611Scy struct wpabuf *buf; 8954351611Scy char json[100]; 8955351611Scy int netrole_ap = 0; /* TODO: make this configurable */ 8956351611Scy 8957351611Scy os_snprintf(json, sizeof(json), 8958351611Scy "{\"name\":\"Test\"," 8959351611Scy "\"wi-fi_tech\":\"infra\"," 8960351611Scy "\"netRole\":\"%s\"}", 8961351611Scy netrole_ap ? "ap" : "sta"); 8962351611Scy#ifdef CONFIG_TESTING_OPTIONS 8963351611Scy if (dpp_test == DPP_TEST_INVALID_CONFIG_ATTR_OBJ_CONF_REQ) { 8964351611Scy wpa_printf(MSG_INFO, "DPP: TESTING - invalid Config Attr"); 8965351611Scy json[29] = 'k'; /* replace "infra" with "knfra" */ 8966351611Scy } 8967351611Scy#endif /* CONFIG_TESTING_OPTIONS */ 8968351611Scy wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json); 8969351611Scy 8970351611Scy buf = dpp_build_conf_req(auth, json); 8971351611Scy if (!buf) { 8972351611Scy wpa_printf(MSG_DEBUG, 8973351611Scy "DPP: No configuration request data available"); 8974351611Scy return; 8975351611Scy } 8976351611Scy 8977351611Scy wpabuf_free(conn->msg_out); 8978351611Scy conn->msg_out_pos = 0; 8979351611Scy conn->msg_out = wpabuf_alloc(4 + wpabuf_len(buf) - 1); 8980351611Scy if (!conn->msg_out) { 8981351611Scy wpabuf_free(buf); 8982351611Scy return; 8983351611Scy } 8984351611Scy wpabuf_put_be32(conn->msg_out, wpabuf_len(buf) - 1); 8985351611Scy wpabuf_put_data(conn->msg_out, wpabuf_head_u8(buf) + 1, 8986351611Scy wpabuf_len(buf) - 1); 8987351611Scy wpabuf_free(buf); 8988351611Scy 8989351611Scy if (dpp_tcp_send(conn) == 1) { 8990351611Scy if (!conn->write_eloop) { 8991351611Scy if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, 8992351611Scy dpp_conn_tx_ready, 8993351611Scy conn, NULL) < 0) 8994351611Scy return; 8995351611Scy conn->write_eloop = 1; 8996351611Scy } 8997351611Scy } 8998351611Scy} 8999351611Scy 9000351611Scy 9001351611Scystatic void dpp_controller_auth_success(struct dpp_connection *conn, 9002351611Scy int initiator) 9003351611Scy{ 9004351611Scy struct dpp_authentication *auth = conn->auth; 9005351611Scy 9006351611Scy if (!auth) 9007351611Scy return; 9008351611Scy 9009351611Scy wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded"); 9010351611Scy wpa_msg(conn->global->msg_ctx, MSG_INFO, 9011351611Scy DPP_EVENT_AUTH_SUCCESS "init=%d", initiator); 9012351611Scy#ifdef CONFIG_TESTING_OPTIONS 9013351611Scy if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) { 9014351611Scy wpa_printf(MSG_INFO, 9015351611Scy "DPP: TESTING - stop at Authentication Confirm"); 9016351611Scy if (auth->configurator) { 9017351611Scy /* Prevent GAS response */ 9018351611Scy auth->auth_success = 0; 9019351611Scy } 9020351611Scy return; 9021351611Scy } 9022351611Scy#endif /* CONFIG_TESTING_OPTIONS */ 9023351611Scy 9024351611Scy if (!auth->configurator) 9025351611Scy dpp_controller_start_gas_client(conn); 9026351611Scy} 9027351611Scy 9028351611Scy 9029351611Scystatic void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) 9030351611Scy{ 9031351611Scy struct dpp_connection *conn = eloop_ctx; 9032351611Scy 9033351611Scy wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock); 9034351611Scy dpp_tcp_send(conn); 9035351611Scy} 9036351611Scy 9037351611Scy 9038351611Scystatic int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen, 9039351611Scy const struct hostapd_ip_addr *ipaddr, 9040351611Scy int port) 9041351611Scy{ 9042351611Scy struct sockaddr_in *dst; 9043351611Scy#ifdef CONFIG_IPV6 9044351611Scy struct sockaddr_in6 *dst6; 9045351611Scy#endif /* CONFIG_IPV6 */ 9046351611Scy 9047351611Scy switch (ipaddr->af) { 9048351611Scy case AF_INET: 9049351611Scy dst = (struct sockaddr_in *) addr; 9050351611Scy os_memset(dst, 0, sizeof(*dst)); 9051351611Scy dst->sin_family = AF_INET; 9052351611Scy dst->sin_addr.s_addr = ipaddr->u.v4.s_addr; 9053351611Scy dst->sin_port = htons(port); 9054351611Scy *addrlen = sizeof(*dst); 9055351611Scy break; 9056351611Scy#ifdef CONFIG_IPV6 9057351611Scy case AF_INET6: 9058351611Scy dst6 = (struct sockaddr_in6 *) addr; 9059351611Scy os_memset(dst6, 0, sizeof(*dst6)); 9060351611Scy dst6->sin6_family = AF_INET6; 9061351611Scy os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6, 9062351611Scy sizeof(struct in6_addr)); 9063351611Scy dst6->sin6_port = htons(port); 9064351611Scy *addrlen = sizeof(*dst6); 9065351611Scy break; 9066351611Scy#endif /* CONFIG_IPV6 */ 9067351611Scy default: 9068351611Scy return -1; 9069351611Scy } 9070351611Scy 9071351611Scy return 0; 9072351611Scy} 9073351611Scy 9074351611Scy 9075351611Scystatic struct dpp_connection * 9076351611Scydpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src, 9077351611Scy unsigned int freq) 9078351611Scy{ 9079351611Scy struct dpp_connection *conn; 9080351611Scy struct sockaddr_storage addr; 9081351611Scy socklen_t addrlen; 9082351611Scy char txt[100]; 9083351611Scy 9084351611Scy if (dl_list_len(&ctrl->conn) >= 15) { 9085351611Scy wpa_printf(MSG_DEBUG, 9086351611Scy "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one"); 9087351611Scy return NULL; 9088351611Scy } 9089351611Scy 9090351611Scy if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen, 9091351611Scy &ctrl->ipaddr, DPP_TCP_PORT) < 0) 9092351611Scy return NULL; 9093351611Scy 9094351611Scy conn = os_zalloc(sizeof(*conn)); 9095351611Scy if (!conn) 9096351611Scy return NULL; 9097351611Scy 9098351611Scy conn->global = ctrl->global; 9099351611Scy conn->relay = ctrl; 9100351611Scy os_memcpy(conn->mac_addr, src, ETH_ALEN); 9101351611Scy conn->freq = freq; 9102351611Scy 9103351611Scy conn->sock = socket(AF_INET, SOCK_STREAM, 0); 9104351611Scy if (conn->sock < 0) 9105351611Scy goto fail; 9106351611Scy wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s", 9107351611Scy conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt))); 9108351611Scy 9109351611Scy if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { 9110351611Scy wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", 9111351611Scy strerror(errno)); 9112351611Scy goto fail; 9113351611Scy } 9114351611Scy 9115351611Scy if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) { 9116351611Scy if (errno != EINPROGRESS) { 9117351611Scy wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s", 9118351611Scy strerror(errno)); 9119351611Scy goto fail; 9120351611Scy } 9121351611Scy 9122351611Scy /* 9123351611Scy * Continue connecting in the background; eloop will call us 9124351611Scy * once the connection is ready (or failed). 9125351611Scy */ 9126351611Scy } 9127351611Scy 9128351611Scy if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, 9129351611Scy dpp_conn_tx_ready, conn, NULL) < 0) 9130351611Scy goto fail; 9131351611Scy conn->write_eloop = 1; 9132351611Scy 9133351611Scy /* TODO: eloop timeout to clear a connection if it does not complete 9134351611Scy * properly */ 9135351611Scy 9136351611Scy dl_list_add(&ctrl->conn, &conn->list); 9137351611Scy return conn; 9138351611Scyfail: 9139351611Scy dpp_connection_free(conn); 9140351611Scy return NULL; 9141351611Scy} 9142351611Scy 9143351611Scy 9144351611Scystatic struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len) 9145351611Scy{ 9146351611Scy struct wpabuf *msg; 9147351611Scy 9148351611Scy msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len); 9149351611Scy if (!msg) 9150351611Scy return NULL; 9151351611Scy wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len); 9152351611Scy wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC); 9153351611Scy wpabuf_put_data(msg, hdr, DPP_HDR_LEN); 9154351611Scy wpabuf_put_data(msg, buf, len); 9155351611Scy wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg); 9156351611Scy return msg; 9157351611Scy} 9158351611Scy 9159351611Scy 9160351611Scystatic int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr, 9161351611Scy const u8 *buf, size_t len) 9162351611Scy{ 9163351611Scy u8 type = hdr[DPP_HDR_LEN - 1]; 9164351611Scy 9165351611Scy wpa_printf(MSG_DEBUG, 9166351611Scy "DPP: Continue already established Relay/Controller connection for this session"); 9167351611Scy wpabuf_free(conn->msg_out); 9168351611Scy conn->msg_out_pos = 0; 9169351611Scy conn->msg_out = dpp_tcp_encaps(hdr, buf, len); 9170351611Scy if (!conn->msg_out) { 9171351611Scy dpp_connection_remove(conn); 9172351611Scy return -1; 9173351611Scy } 9174351611Scy 9175351611Scy /* TODO: for proto ver 1, need to do remove connection based on GAS Resp 9176351611Scy * TX status */ 9177351611Scy if (type == DPP_PA_CONFIGURATION_RESULT) 9178351611Scy conn->on_tcp_tx_complete_remove = 1; 9179351611Scy dpp_tcp_send(conn); 9180351611Scy return 0; 9181351611Scy} 9182351611Scy 9183351611Scy 9184351611Scyint dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr, 9185351611Scy const u8 *buf, size_t len, unsigned int freq, 9186351611Scy const u8 *i_bootstrap, const u8 *r_bootstrap) 9187351611Scy{ 9188351611Scy struct dpp_relay_controller *ctrl; 9189351611Scy struct dpp_connection *conn; 9190351611Scy u8 type = hdr[DPP_HDR_LEN - 1]; 9191351611Scy 9192351611Scy /* Check if there is an already started session for this peer and if so, 9193351611Scy * continue that session (send this over TCP) and return 0. 9194351611Scy */ 9195351611Scy if (type != DPP_PA_PEER_DISCOVERY_REQ && 9196351611Scy type != DPP_PA_PEER_DISCOVERY_RESP) { 9197351611Scy dl_list_for_each(ctrl, &dpp->controllers, 9198351611Scy struct dpp_relay_controller, list) { 9199351611Scy dl_list_for_each(conn, &ctrl->conn, 9200351611Scy struct dpp_connection, list) { 9201351611Scy if (os_memcmp(src, conn->mac_addr, 9202351611Scy ETH_ALEN) == 0) 9203351611Scy return dpp_relay_tx(conn, hdr, buf, len); 9204351611Scy } 9205351611Scy } 9206351611Scy } 9207351611Scy 9208351611Scy if (!r_bootstrap) 9209351611Scy return -1; 9210351611Scy 9211351611Scy ctrl = dpp_relay_controller_get(dpp, r_bootstrap); 9212351611Scy if (!ctrl) 9213351611Scy return -1; 9214351611Scy 9215351611Scy wpa_printf(MSG_DEBUG, 9216351611Scy "DPP: Authentication Request for a configured Controller"); 9217351611Scy conn = dpp_relay_new_conn(ctrl, src, freq); 9218351611Scy if (!conn) 9219351611Scy return -1; 9220351611Scy 9221351611Scy conn->msg_out = dpp_tcp_encaps(hdr, buf, len); 9222351611Scy if (!conn->msg_out) { 9223351611Scy dpp_connection_remove(conn); 9224351611Scy return -1; 9225351611Scy } 9226351611Scy /* Message will be sent in dpp_conn_tx_ready() */ 9227351611Scy 9228351611Scy return 0; 9229351611Scy} 9230351611Scy 9231351611Scy 9232351611Scyint dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data, 9233351611Scy size_t data_len) 9234351611Scy{ 9235351611Scy struct dpp_relay_controller *ctrl; 9236351611Scy struct dpp_connection *conn, *found = NULL; 9237351611Scy struct wpabuf *msg; 9238351611Scy 9239351611Scy /* Check if there is a successfully completed authentication for this 9240351611Scy * and if so, continue that session (send this over TCP) and return 0. 9241351611Scy */ 9242351611Scy dl_list_for_each(ctrl, &dpp->controllers, 9243351611Scy struct dpp_relay_controller, list) { 9244351611Scy if (found) 9245351611Scy break; 9246351611Scy dl_list_for_each(conn, &ctrl->conn, 9247351611Scy struct dpp_connection, list) { 9248351611Scy if (os_memcmp(src, conn->mac_addr, 9249351611Scy ETH_ALEN) == 0) { 9250351611Scy found = conn; 9251351611Scy break; 9252351611Scy } 9253351611Scy } 9254351611Scy } 9255351611Scy 9256351611Scy if (!found) 9257351611Scy return -1; 9258351611Scy 9259351611Scy msg = wpabuf_alloc(4 + 1 + data_len); 9260351611Scy if (!msg) 9261351611Scy return -1; 9262351611Scy wpabuf_put_be32(msg, 1 + data_len); 9263351611Scy wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ); 9264351611Scy wpabuf_put_data(msg, data, data_len); 9265351611Scy wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg); 9266351611Scy 9267351611Scy wpabuf_free(conn->msg_out); 9268351611Scy conn->msg_out_pos = 0; 9269351611Scy conn->msg_out = msg; 9270351611Scy dpp_tcp_send(conn); 9271351611Scy return 0; 9272351611Scy} 9273351611Scy 9274351611Scy 9275351611Scystatic void dpp_controller_free(struct dpp_controller *ctrl) 9276351611Scy{ 9277351611Scy struct dpp_connection *conn, *tmp; 9278351611Scy 9279351611Scy if (!ctrl) 9280351611Scy return; 9281351611Scy 9282351611Scy dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection, 9283351611Scy list) 9284351611Scy dpp_connection_remove(conn); 9285351611Scy 9286351611Scy if (ctrl->sock >= 0) { 9287351611Scy close(ctrl->sock); 9288351611Scy eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ); 9289351611Scy } 9290351611Scy os_free(ctrl->configurator_params); 9291351611Scy os_free(ctrl); 9292351611Scy} 9293351611Scy 9294351611Scy 9295351611Scystatic int dpp_controller_rx_auth_req(struct dpp_connection *conn, 9296351611Scy const u8 *hdr, const u8 *buf, size_t len) 9297351611Scy{ 9298351611Scy const u8 *r_bootstrap, *i_bootstrap; 9299351611Scy u16 r_bootstrap_len, i_bootstrap_len; 9300351611Scy struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL; 9301351611Scy 9302351611Scy if (!conn->ctrl) 9303351611Scy return 0; 9304351611Scy 9305351611Scy wpa_printf(MSG_DEBUG, "DPP: Authentication Request"); 9306351611Scy 9307351611Scy r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH, 9308351611Scy &r_bootstrap_len); 9309351611Scy if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) { 9310351611Scy wpa_printf(MSG_INFO, 9311351611Scy "Missing or invalid required Responder Bootstrapping Key Hash attribute"); 9312351611Scy return -1; 9313351611Scy } 9314351611Scy wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash", 9315351611Scy r_bootstrap, r_bootstrap_len); 9316351611Scy 9317351611Scy i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH, 9318351611Scy &i_bootstrap_len); 9319351611Scy if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) { 9320351611Scy wpa_printf(MSG_INFO, 9321351611Scy "Missing or invalid required Initiator Bootstrapping Key Hash attribute"); 9322351611Scy return -1; 9323351611Scy } 9324351611Scy wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash", 9325351611Scy i_bootstrap, i_bootstrap_len); 9326351611Scy 9327351611Scy /* Try to find own and peer bootstrapping key matches based on the 9328351611Scy * received hash values */ 9329351611Scy dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap, 9330351611Scy &own_bi, &peer_bi); 9331351611Scy if (!own_bi) { 9332351611Scy wpa_printf(MSG_INFO, 9333351611Scy "No matching own bootstrapping key found - ignore message"); 9334351611Scy return -1; 9335351611Scy } 9336351611Scy 9337351611Scy if (conn->auth) { 9338351611Scy wpa_printf(MSG_INFO, 9339351611Scy "Already in DPP authentication exchange - ignore new one"); 9340351611Scy return 0; 9341351611Scy } 9342351611Scy 9343351611Scy conn->auth = dpp_auth_req_rx(conn->ctrl->global->msg_ctx, 9344351611Scy conn->ctrl->allowed_roles, 9345351611Scy conn->ctrl->qr_mutual, 9346351611Scy peer_bi, own_bi, -1, hdr, buf, len); 9347351611Scy if (!conn->auth) { 9348351611Scy wpa_printf(MSG_DEBUG, "DPP: No response generated"); 9349351611Scy return -1; 9350351611Scy } 9351351611Scy 9352351611Scy if (dpp_set_configurator(conn->ctrl->global, conn->ctrl->global->msg_ctx, 9353351611Scy conn->auth, 9354351611Scy conn->ctrl->configurator_params) < 0) { 9355351611Scy dpp_connection_remove(conn); 9356351611Scy return -1; 9357351611Scy } 9358351611Scy 9359351611Scy wpabuf_free(conn->msg_out); 9360351611Scy conn->msg_out_pos = 0; 9361351611Scy conn->msg_out = wpabuf_alloc(4 + wpabuf_len(conn->auth->resp_msg) - 1); 9362351611Scy if (!conn->msg_out) 9363351611Scy return -1; 9364351611Scy wpabuf_put_be32(conn->msg_out, wpabuf_len(conn->auth->resp_msg) - 1); 9365351611Scy wpabuf_put_data(conn->msg_out, wpabuf_head_u8(conn->auth->resp_msg) + 1, 9366351611Scy wpabuf_len(conn->auth->resp_msg) - 1); 9367351611Scy 9368351611Scy if (dpp_tcp_send(conn) == 1) { 9369351611Scy if (!conn->write_eloop) { 9370351611Scy if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, 9371351611Scy dpp_conn_tx_ready, 9372351611Scy conn, NULL) < 0) 9373351611Scy return -1; 9374351611Scy conn->write_eloop = 1; 9375351611Scy } 9376351611Scy } 9377351611Scy 9378351611Scy return 0; 9379351611Scy} 9380351611Scy 9381351611Scy 9382351611Scystatic int dpp_controller_rx_auth_resp(struct dpp_connection *conn, 9383351611Scy const u8 *hdr, const u8 *buf, size_t len) 9384351611Scy{ 9385351611Scy struct dpp_authentication *auth = conn->auth; 9386351611Scy struct wpabuf *msg; 9387351611Scy 9388351611Scy if (!auth) 9389351611Scy return -1; 9390351611Scy 9391351611Scy wpa_printf(MSG_DEBUG, "DPP: Authentication Response"); 9392351611Scy 9393351611Scy msg = dpp_auth_resp_rx(auth, hdr, buf, len); 9394351611Scy if (!msg) { 9395351611Scy if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) { 9396351611Scy wpa_printf(MSG_DEBUG, 9397351611Scy "DPP: Start wait for full response"); 9398351611Scy return -1; 9399351611Scy } 9400351611Scy wpa_printf(MSG_DEBUG, "DPP: No confirm generated"); 9401351611Scy dpp_connection_remove(conn); 9402351611Scy return -1; 9403351611Scy } 9404351611Scy 9405351611Scy wpabuf_free(conn->msg_out); 9406351611Scy conn->msg_out_pos = 0; 9407351611Scy conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1); 9408351611Scy if (!conn->msg_out) { 9409351611Scy wpabuf_free(msg); 9410351611Scy return -1; 9411351611Scy } 9412351611Scy wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1); 9413351611Scy wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1, 9414351611Scy wpabuf_len(msg) - 1); 9415351611Scy wpabuf_free(msg); 9416351611Scy 9417351611Scy conn->on_tcp_tx_complete_auth_ok = 1; 9418351611Scy if (dpp_tcp_send(conn) == 1) { 9419351611Scy if (!conn->write_eloop) { 9420351611Scy if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, 9421351611Scy dpp_conn_tx_ready, 9422351611Scy conn, NULL) < 0) 9423351611Scy return -1; 9424351611Scy conn->write_eloop = 1; 9425351611Scy } 9426351611Scy } 9427351611Scy 9428351611Scy return 0; 9429351611Scy} 9430351611Scy 9431351611Scy 9432351611Scystatic int dpp_controller_rx_auth_conf(struct dpp_connection *conn, 9433351611Scy const u8 *hdr, const u8 *buf, size_t len) 9434351611Scy{ 9435351611Scy struct dpp_authentication *auth = conn->auth; 9436351611Scy 9437351611Scy wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation"); 9438351611Scy 9439351611Scy if (!auth) { 9440351611Scy wpa_printf(MSG_DEBUG, 9441351611Scy "DPP: No DPP Authentication in progress - drop"); 9442351611Scy return -1; 9443351611Scy } 9444351611Scy 9445351611Scy if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) { 9446351611Scy wpa_printf(MSG_DEBUG, "DPP: Authentication failed"); 9447351611Scy return -1; 9448351611Scy } 9449351611Scy 9450351611Scy dpp_controller_auth_success(conn, 0); 9451351611Scy return 0; 9452351611Scy} 9453351611Scy 9454351611Scy 9455351611Scystatic int dpp_controller_rx_conf_result(struct dpp_connection *conn, 9456351611Scy const u8 *hdr, const u8 *buf, 9457351611Scy size_t len) 9458351611Scy{ 9459351611Scy struct dpp_authentication *auth = conn->auth; 9460351611Scy enum dpp_status_error status; 9461351611Scy 9462351611Scy if (!conn->ctrl) 9463351611Scy return 0; 9464351611Scy 9465351611Scy wpa_printf(MSG_DEBUG, "DPP: Configuration Result"); 9466351611Scy 9467351611Scy if (!auth || !auth->waiting_conf_result) { 9468351611Scy wpa_printf(MSG_DEBUG, 9469351611Scy "DPP: No DPP Configuration waiting for result - drop"); 9470351611Scy return -1; 9471351611Scy } 9472351611Scy 9473351611Scy status = dpp_conf_result_rx(auth, hdr, buf, len); 9474351611Scy if (status == DPP_STATUS_OK) 9475351611Scy wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, 9476351611Scy DPP_EVENT_CONF_SENT); 9477351611Scy else 9478351611Scy wpa_msg(conn->ctrl->global->msg_ctx, MSG_INFO, 9479351611Scy DPP_EVENT_CONF_FAILED); 9480351611Scy return -1; /* to remove the completed connection */ 9481351611Scy} 9482351611Scy 9483351611Scy 9484351611Scystatic int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg, 9485351611Scy size_t len) 9486351611Scy{ 9487351611Scy const u8 *pos, *end; 9488351611Scy u8 type; 9489351611Scy 9490351611Scy wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP"); 9491351611Scy pos = msg; 9492351611Scy end = msg + len; 9493351611Scy 9494351611Scy if (end - pos < DPP_HDR_LEN || 9495351611Scy WPA_GET_BE24(pos) != OUI_WFA || 9496351611Scy pos[3] != DPP_OUI_TYPE) { 9497351611Scy wpa_printf(MSG_DEBUG, "DPP: Unrecognized header"); 9498351611Scy return -1; 9499351611Scy } 9500351611Scy 9501351611Scy if (pos[4] != 1) { 9502351611Scy wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u", 9503351611Scy pos[4]); 9504351611Scy return -1; 9505351611Scy } 9506351611Scy type = pos[5]; 9507351611Scy wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type); 9508351611Scy pos += DPP_HDR_LEN; 9509351611Scy 9510351611Scy wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes", 9511351611Scy pos, end - pos); 9512351611Scy if (dpp_check_attrs(pos, end - pos) < 0) 9513351611Scy return -1; 9514351611Scy 9515351611Scy if (conn->relay) { 9516351611Scy wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN"); 9517351611Scy conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr, 9518351611Scy conn->freq, msg, len); 9519351611Scy return 0; 9520351611Scy } 9521351611Scy 9522351611Scy switch (type) { 9523351611Scy case DPP_PA_AUTHENTICATION_REQ: 9524351611Scy return dpp_controller_rx_auth_req(conn, msg, pos, end - pos); 9525351611Scy case DPP_PA_AUTHENTICATION_RESP: 9526351611Scy return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos); 9527351611Scy case DPP_PA_AUTHENTICATION_CONF: 9528351611Scy return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos); 9529351611Scy case DPP_PA_CONFIGURATION_RESULT: 9530351611Scy return dpp_controller_rx_conf_result(conn, msg, pos, end - pos); 9531351611Scy default: 9532351611Scy /* TODO: missing messages types */ 9533351611Scy wpa_printf(MSG_DEBUG, 9534351611Scy "DPP: Unsupported frame subtype %d", type); 9535351611Scy return -1; 9536351611Scy } 9537351611Scy} 9538351611Scy 9539351611Scy 9540351611Scystatic int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg, 9541351611Scy size_t len) 9542351611Scy{ 9543351611Scy const u8 *pos, *end, *next; 9544351611Scy u8 dialog_token; 9545351611Scy const u8 *adv_proto; 9546351611Scy u16 slen; 9547351611Scy struct wpabuf *resp, *buf; 9548351611Scy struct dpp_authentication *auth = conn->auth; 9549351611Scy 9550351611Scy if (len < 1 + 2) 9551351611Scy return -1; 9552351611Scy 9553351611Scy wpa_printf(MSG_DEBUG, 9554351611Scy "DPP: Received DPP Configuration Request over TCP"); 9555351611Scy 9556351611Scy if (!conn->ctrl || !auth || !auth->auth_success) { 9557351611Scy wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); 9558351611Scy return -1; 9559351611Scy } 9560351611Scy 9561351611Scy pos = msg; 9562351611Scy end = msg + len; 9563351611Scy 9564351611Scy dialog_token = *pos++; 9565351611Scy adv_proto = pos++; 9566351611Scy slen = *pos++; 9567351611Scy if (*adv_proto != WLAN_EID_ADV_PROTO || 9568351611Scy slen > end - pos || slen < 2) 9569351611Scy return -1; 9570351611Scy 9571351611Scy next = pos + slen; 9572351611Scy pos++; /* skip QueryRespLenLimit and PAME-BI */ 9573351611Scy 9574351611Scy if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC || 9575351611Scy pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA || 9576351611Scy pos[5] != DPP_OUI_TYPE || pos[6] != 0x01) 9577351611Scy return -1; 9578351611Scy 9579351611Scy pos = next; 9580351611Scy /* Query Request */ 9581351611Scy if (end - pos < 2) 9582351611Scy return -1; 9583351611Scy slen = WPA_GET_LE16(pos); 9584351611Scy pos += 2; 9585351611Scy if (slen > end - pos) 9586351611Scy return -1; 9587351611Scy 9588351611Scy resp = dpp_conf_req_rx(auth, pos, slen); 9589351611Scy if (!resp) 9590351611Scy return -1; 9591351611Scy 9592351611Scy buf = wpabuf_alloc(4 + 18 + wpabuf_len(resp)); 9593351611Scy if (!buf) { 9594351611Scy wpabuf_free(resp); 9595351611Scy return -1; 9596351611Scy } 9597351611Scy 9598351611Scy wpabuf_put_be32(buf, 18 + wpabuf_len(resp)); 9599351611Scy 9600351611Scy wpabuf_put_u8(buf, WLAN_PA_GAS_INITIAL_RESP); 9601351611Scy wpabuf_put_u8(buf, dialog_token); 9602351611Scy wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); 9603351611Scy wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */ 9604351611Scy 9605351611Scy dpp_write_adv_proto(buf); 9606351611Scy dpp_write_gas_query(buf, resp); 9607351611Scy wpabuf_free(resp); 9608351611Scy 9609351611Scy /* Send Config Response over TCP; GAS fragmentation is taken care of by 9610351611Scy * the Relay */ 9611351611Scy wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf); 9612351611Scy wpabuf_free(conn->msg_out); 9613351611Scy conn->msg_out_pos = 0; 9614351611Scy conn->msg_out = buf; 9615351611Scy conn->on_tcp_tx_complete_gas_done = 1; 9616351611Scy dpp_tcp_send(conn); 9617351611Scy return 0; 9618351611Scy} 9619351611Scy 9620351611Scy 9621351611Scystatic int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp) 9622351611Scy{ 9623351611Scy struct dpp_authentication *auth = conn->auth; 9624351611Scy int res; 9625351611Scy struct wpabuf *msg, *encaps; 9626351611Scy enum dpp_status_error status; 9627351611Scy 9628351611Scy wpa_printf(MSG_DEBUG, 9629351611Scy "DPP: Configuration Response for local stack from TCP"); 9630351611Scy 9631351611Scy res = dpp_conf_resp_rx(auth, resp); 9632351611Scy wpabuf_free(resp); 9633351611Scy if (res < 0) { 9634351611Scy wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed"); 9635351611Scy return -1; 9636351611Scy } 9637351611Scy 9638351611Scy if (conn->global->process_conf_obj) 9639351611Scy res = conn->global->process_conf_obj(conn->global->cb_ctx, 9640351611Scy auth); 9641351611Scy else 9642351611Scy res = 0; 9643351611Scy 9644351611Scy if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK) 9645351611Scy return -1; 9646351611Scy 9647351611Scy wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result"); 9648351611Scy status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK; 9649351611Scy msg = dpp_build_conf_result(auth, status); 9650351611Scy if (!msg) 9651351611Scy return -1; 9652351611Scy 9653351611Scy encaps = wpabuf_alloc(4 + wpabuf_len(msg) - 1); 9654351611Scy if (!encaps) { 9655351611Scy wpabuf_free(msg); 9656351611Scy return -1; 9657351611Scy } 9658351611Scy wpabuf_put_be32(encaps, wpabuf_len(msg) - 1); 9659351611Scy wpabuf_put_data(encaps, wpabuf_head_u8(msg) + 1, wpabuf_len(msg) - 1); 9660351611Scy wpabuf_free(msg); 9661351611Scy wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", encaps); 9662351611Scy 9663351611Scy wpabuf_free(conn->msg_out); 9664351611Scy conn->msg_out_pos = 0; 9665351611Scy conn->msg_out = encaps; 9666351611Scy conn->on_tcp_tx_complete_remove = 1; 9667351611Scy dpp_tcp_send(conn); 9668351611Scy 9669351611Scy /* This exchange will be terminated in the TX status handler */ 9670351611Scy 9671351611Scy return 0; 9672351611Scy} 9673351611Scy 9674351611Scy 9675351611Scystatic int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg, 9676351611Scy size_t len) 9677351611Scy{ 9678351611Scy struct wpabuf *buf; 9679351611Scy u8 dialog_token; 9680351611Scy const u8 *pos, *end, *next, *adv_proto; 9681351611Scy u16 status, slen; 9682351611Scy 9683351611Scy if (len < 5 + 2) 9684351611Scy return -1; 9685351611Scy 9686351611Scy wpa_printf(MSG_DEBUG, 9687351611Scy "DPP: Received DPP Configuration Response over TCP"); 9688351611Scy 9689351611Scy pos = msg; 9690351611Scy end = msg + len; 9691351611Scy 9692351611Scy dialog_token = *pos++; 9693351611Scy status = WPA_GET_LE16(pos); 9694351611Scy if (status != WLAN_STATUS_SUCCESS) { 9695351611Scy wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status); 9696351611Scy return -1; 9697351611Scy } 9698351611Scy pos += 2; 9699351611Scy pos += 2; /* ignore GAS Comeback Delay */ 9700351611Scy 9701351611Scy adv_proto = pos++; 9702351611Scy slen = *pos++; 9703351611Scy if (*adv_proto != WLAN_EID_ADV_PROTO || 9704351611Scy slen > end - pos || slen < 2) 9705351611Scy return -1; 9706351611Scy 9707351611Scy next = pos + slen; 9708351611Scy pos++; /* skip QueryRespLenLimit and PAME-BI */ 9709351611Scy 9710351611Scy if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC || 9711351611Scy pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA || 9712351611Scy pos[5] != DPP_OUI_TYPE || pos[6] != 0x01) 9713351611Scy return -1; 9714351611Scy 9715351611Scy pos = next; 9716351611Scy /* Query Response */ 9717351611Scy if (end - pos < 2) 9718351611Scy return -1; 9719351611Scy slen = WPA_GET_LE16(pos); 9720351611Scy pos += 2; 9721351611Scy if (slen > end - pos) 9722351611Scy return -1; 9723351611Scy 9724351611Scy buf = wpabuf_alloc(slen); 9725351611Scy if (!buf) 9726351611Scy return -1; 9727351611Scy wpabuf_put_data(buf, pos, slen); 9728351611Scy 9729351611Scy if (!conn->relay && !conn->ctrl) 9730351611Scy return dpp_tcp_rx_gas_resp(conn, buf); 9731351611Scy 9732351611Scy if (!conn->relay) { 9733351611Scy wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress"); 9734351611Scy wpabuf_free(buf); 9735351611Scy return -1; 9736351611Scy } 9737351611Scy wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN"); 9738351611Scy conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr, 9739351611Scy dialog_token, 0, buf); 9740351611Scy 9741351611Scy return 0; 9742351611Scy} 9743351611Scy 9744351611Scy 9745351611Scystatic void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx) 9746351611Scy{ 9747351611Scy struct dpp_connection *conn = eloop_ctx; 9748351611Scy int res; 9749351611Scy const u8 *pos; 9750351611Scy 9751351611Scy wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)", 9752351611Scy sd); 9753351611Scy 9754351611Scy if (conn->msg_len_octets < 4) { 9755351611Scy u32 msglen; 9756351611Scy 9757351611Scy res = recv(sd, &conn->msg_len[conn->msg_len_octets], 9758351611Scy 4 - conn->msg_len_octets, 0); 9759351611Scy if (res < 0) { 9760351611Scy wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", 9761351611Scy strerror(errno)); 9762351611Scy dpp_connection_remove(conn); 9763351611Scy return; 9764351611Scy } 9765351611Scy if (res == 0) { 9766351611Scy wpa_printf(MSG_DEBUG, 9767351611Scy "DPP: No more data available over TCP"); 9768351611Scy dpp_connection_remove(conn); 9769351611Scy return; 9770351611Scy } 9771351611Scy wpa_printf(MSG_DEBUG, 9772351611Scy "DPP: Received %d/%d octet(s) of message length field", 9773351611Scy res, (int) (4 - conn->msg_len_octets)); 9774351611Scy conn->msg_len_octets += res; 9775351611Scy 9776351611Scy if (conn->msg_len_octets < 4) { 9777351611Scy wpa_printf(MSG_DEBUG, 9778351611Scy "DPP: Need %d more octets of message length field", 9779351611Scy (int) (4 - conn->msg_len_octets)); 9780351611Scy return; 9781351611Scy } 9782351611Scy 9783351611Scy msglen = WPA_GET_BE32(conn->msg_len); 9784351611Scy wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen); 9785351611Scy if (msglen > 65535) { 9786351611Scy wpa_printf(MSG_INFO, "DPP: Unexpectedly long message"); 9787351611Scy dpp_connection_remove(conn); 9788351611Scy return; 9789351611Scy } 9790351611Scy 9791351611Scy wpabuf_free(conn->msg); 9792351611Scy conn->msg = wpabuf_alloc(msglen); 9793351611Scy } 9794351611Scy 9795351611Scy if (!conn->msg) { 9796351611Scy wpa_printf(MSG_DEBUG, 9797351611Scy "DPP: No buffer available for receiving the message"); 9798351611Scy dpp_connection_remove(conn); 9799351611Scy return; 9800351611Scy } 9801351611Scy 9802351611Scy wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload", 9803351611Scy (unsigned int) wpabuf_tailroom(conn->msg)); 9804351611Scy 9805351611Scy res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0); 9806351611Scy if (res < 0) { 9807351611Scy wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno)); 9808351611Scy dpp_connection_remove(conn); 9809351611Scy return; 9810351611Scy } 9811351611Scy if (res == 0) { 9812351611Scy wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP"); 9813351611Scy dpp_connection_remove(conn); 9814351611Scy return; 9815351611Scy } 9816351611Scy wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res); 9817351611Scy wpabuf_put(conn->msg, res); 9818351611Scy 9819351611Scy if (wpabuf_tailroom(conn->msg) > 0) { 9820351611Scy wpa_printf(MSG_DEBUG, 9821351611Scy "DPP: Need %u more octets of message payload", 9822351611Scy (unsigned int) wpabuf_tailroom(conn->msg)); 9823351611Scy return; 9824351611Scy } 9825351611Scy 9826351611Scy conn->msg_len_octets = 0; 9827351611Scy wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg); 9828351611Scy if (wpabuf_len(conn->msg) < 1) { 9829351611Scy dpp_connection_remove(conn); 9830351611Scy return; 9831351611Scy } 9832351611Scy 9833351611Scy pos = wpabuf_head(conn->msg); 9834351611Scy switch (*pos) { 9835351611Scy case WLAN_PA_VENDOR_SPECIFIC: 9836351611Scy if (dpp_controller_rx_action(conn, pos + 1, 9837351611Scy wpabuf_len(conn->msg) - 1) < 0) 9838351611Scy dpp_connection_remove(conn); 9839351611Scy break; 9840351611Scy case WLAN_PA_GAS_INITIAL_REQ: 9841351611Scy if (dpp_controller_rx_gas_req(conn, pos + 1, 9842351611Scy wpabuf_len(conn->msg) - 1) < 0) 9843351611Scy dpp_connection_remove(conn); 9844351611Scy break; 9845351611Scy case WLAN_PA_GAS_INITIAL_RESP: 9846351611Scy if (dpp_rx_gas_resp(conn, pos + 1, 9847351611Scy wpabuf_len(conn->msg) - 1) < 0) 9848351611Scy dpp_connection_remove(conn); 9849351611Scy break; 9850351611Scy default: 9851351611Scy wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u", 9852351611Scy *pos); 9853351611Scy break; 9854351611Scy } 9855351611Scy} 9856351611Scy 9857351611Scy 9858351611Scystatic void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx) 9859351611Scy{ 9860351611Scy struct dpp_controller *ctrl = eloop_ctx; 9861351611Scy struct sockaddr_in addr; 9862351611Scy socklen_t addr_len = sizeof(addr); 9863351611Scy int fd; 9864351611Scy struct dpp_connection *conn; 9865351611Scy 9866351611Scy wpa_printf(MSG_DEBUG, "DPP: New TCP connection"); 9867351611Scy 9868351611Scy fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len); 9869351611Scy if (fd < 0) { 9870351611Scy wpa_printf(MSG_DEBUG, 9871351611Scy "DPP: Failed to accept new connection: %s", 9872351611Scy strerror(errno)); 9873351611Scy return; 9874351611Scy } 9875351611Scy wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d", 9876351611Scy inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); 9877351611Scy 9878351611Scy conn = os_zalloc(sizeof(*conn)); 9879351611Scy if (!conn) 9880351611Scy goto fail; 9881351611Scy 9882351611Scy conn->global = ctrl->global; 9883351611Scy conn->ctrl = ctrl; 9884351611Scy conn->sock = fd; 9885351611Scy 9886351611Scy if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { 9887351611Scy wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", 9888351611Scy strerror(errno)); 9889351611Scy goto fail; 9890351611Scy } 9891351611Scy 9892351611Scy if (eloop_register_sock(conn->sock, EVENT_TYPE_READ, 9893351611Scy dpp_controller_rx, conn, NULL) < 0) 9894351611Scy goto fail; 9895351611Scy conn->read_eloop = 1; 9896351611Scy 9897351611Scy /* TODO: eloop timeout to expire connections that do not complete in 9898351611Scy * reasonable time */ 9899351611Scy dl_list_add(&ctrl->conn, &conn->list); 9900351611Scy return; 9901351611Scy 9902351611Scyfail: 9903351611Scy close(fd); 9904351611Scy os_free(conn); 9905351611Scy} 9906351611Scy 9907351611Scy 9908351611Scyint dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth, 9909351611Scy const struct hostapd_ip_addr *addr, int port) 9910351611Scy{ 9911351611Scy struct dpp_connection *conn; 9912351611Scy struct sockaddr_storage saddr; 9913351611Scy socklen_t addrlen; 9914351611Scy const u8 *hdr, *pos, *end; 9915351611Scy char txt[100]; 9916351611Scy 9917351611Scy wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d", 9918351611Scy hostapd_ip_txt(addr, txt, sizeof(txt)), port); 9919351611Scy if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen, 9920351611Scy addr, port) < 0) { 9921351611Scy dpp_auth_deinit(auth); 9922351611Scy return -1; 9923351611Scy } 9924351611Scy 9925351611Scy conn = os_zalloc(sizeof(*conn)); 9926351611Scy if (!conn) { 9927351611Scy dpp_auth_deinit(auth); 9928351611Scy return -1; 9929351611Scy } 9930351611Scy 9931351611Scy conn->global = dpp; 9932351611Scy conn->auth = auth; 9933351611Scy conn->sock = socket(AF_INET, SOCK_STREAM, 0); 9934351611Scy if (conn->sock < 0) 9935351611Scy goto fail; 9936351611Scy 9937351611Scy if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) { 9938351611Scy wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s", 9939351611Scy strerror(errno)); 9940351611Scy goto fail; 9941351611Scy } 9942351611Scy 9943351611Scy if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) { 9944351611Scy if (errno != EINPROGRESS) { 9945351611Scy wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s", 9946351611Scy strerror(errno)); 9947351611Scy goto fail; 9948351611Scy } 9949351611Scy 9950351611Scy /* 9951351611Scy * Continue connecting in the background; eloop will call us 9952351611Scy * once the connection is ready (or failed). 9953351611Scy */ 9954351611Scy } 9955351611Scy 9956351611Scy if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE, 9957351611Scy dpp_conn_tx_ready, conn, NULL) < 0) 9958351611Scy goto fail; 9959351611Scy conn->write_eloop = 1; 9960351611Scy 9961351611Scy hdr = wpabuf_head(auth->req_msg); 9962351611Scy end = hdr + wpabuf_len(auth->req_msg); 9963351611Scy hdr += 2; /* skip Category and Actiom */ 9964351611Scy pos = hdr + DPP_HDR_LEN; 9965351611Scy conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos); 9966351611Scy if (!conn->msg_out) 9967351611Scy goto fail; 9968351611Scy /* Message will be sent in dpp_conn_tx_ready() */ 9969351611Scy 9970351611Scy /* TODO: eloop timeout to clear a connection if it does not complete 9971351611Scy * properly */ 9972351611Scy dl_list_add(&dpp->tcp_init, &conn->list); 9973351611Scy return 0; 9974351611Scyfail: 9975351611Scy dpp_connection_free(conn); 9976351611Scy return -1; 9977351611Scy} 9978351611Scy 9979351611Scy 9980351611Scyint dpp_controller_start(struct dpp_global *dpp, 9981351611Scy struct dpp_controller_config *config) 9982351611Scy{ 9983351611Scy struct dpp_controller *ctrl; 9984351611Scy int on = 1; 9985351611Scy struct sockaddr_in sin; 9986351611Scy int port; 9987351611Scy 9988351611Scy if (!dpp || dpp->controller) 9989351611Scy return -1; 9990351611Scy 9991351611Scy ctrl = os_zalloc(sizeof(*ctrl)); 9992351611Scy if (!ctrl) 9993351611Scy return -1; 9994351611Scy ctrl->global = dpp; 9995351611Scy if (config->configurator_params) 9996351611Scy ctrl->configurator_params = 9997351611Scy os_strdup(config->configurator_params); 9998351611Scy dl_list_init(&ctrl->conn); 9999351611Scy /* TODO: configure these somehow */ 10000351611Scy ctrl->allowed_roles = DPP_CAPAB_ENROLLEE | DPP_CAPAB_CONFIGURATOR; 10001351611Scy ctrl->qr_mutual = 0; 10002351611Scy 10003351611Scy ctrl->sock = socket(AF_INET, SOCK_STREAM, 0); 10004351611Scy if (ctrl->sock < 0) 10005351611Scy goto fail; 10006351611Scy 10007351611Scy if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR, 10008351611Scy &on, sizeof(on)) < 0) { 10009351611Scy wpa_printf(MSG_DEBUG, 10010351611Scy "DPP: setsockopt(SO_REUSEADDR) failed: %s", 10011351611Scy strerror(errno)); 10012351611Scy /* try to continue anyway */ 10013351611Scy } 10014351611Scy 10015351611Scy if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) { 10016351611Scy wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s", 10017351611Scy strerror(errno)); 10018351611Scy goto fail; 10019351611Scy } 10020351611Scy 10021351611Scy /* TODO: IPv6 */ 10022351611Scy os_memset(&sin, 0, sizeof(sin)); 10023351611Scy sin.sin_family = AF_INET; 10024351611Scy sin.sin_addr.s_addr = INADDR_ANY; 10025351611Scy port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT; 10026351611Scy sin.sin_port = htons(port); 10027351611Scy if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) { 10028351611Scy wpa_printf(MSG_INFO, 10029351611Scy "DPP: Failed to bind Controller TCP port: %s", 10030351611Scy strerror(errno)); 10031351611Scy goto fail; 10032351611Scy } 10033351611Scy if (listen(ctrl->sock, 10 /* max backlog */) < 0 || 10034351611Scy fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 || 10035351611Scy eloop_register_sock(ctrl->sock, EVENT_TYPE_READ, 10036351611Scy dpp_controller_tcp_cb, ctrl, NULL)) 10037351611Scy goto fail; 10038351611Scy 10039351611Scy dpp->controller = ctrl; 10040351611Scy wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port); 10041351611Scy return 0; 10042351611Scyfail: 10043351611Scy dpp_controller_free(ctrl); 10044351611Scy return -1; 10045351611Scy} 10046351611Scy 10047351611Scy 10048351611Scyvoid dpp_controller_stop(struct dpp_global *dpp) 10049351611Scy{ 10050351611Scy if (dpp) { 10051351611Scy dpp_controller_free(dpp->controller); 10052351611Scy dpp->controller = NULL; 10053351611Scy } 10054351611Scy} 10055351611Scy 10056351611Scy#endif /* CONFIG_DPP2 */ 10057