1189251Ssam/* 2189251Ssam * Wi-Fi Protected Setup - attribute building 3189251Ssam * Copyright (c) 2008, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12214734Srpaulo#include "crypto/aes_wrap.h" 13214734Srpaulo#include "crypto/crypto.h" 14214734Srpaulo#include "crypto/dh_group5.h" 15214734Srpaulo#include "crypto/sha256.h" 16252726Srpaulo#include "crypto/random.h" 17252726Srpaulo#include "common/ieee802_11_defs.h" 18189251Ssam#include "wps_i.h" 19189251Ssam 20189251Ssam 21189251Ssamint wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) 22189251Ssam{ 23189251Ssam struct wpabuf *pubkey; 24189251Ssam 25189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Public Key"); 26214734Srpaulo wpabuf_free(wps->dh_privkey); 27214734Srpaulo if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) { 28214734Srpaulo wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys"); 29214734Srpaulo wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey); 30214734Srpaulo wps->dh_ctx = wps->wps->dh_ctx; 31214734Srpaulo wps->wps->dh_ctx = NULL; 32214734Srpaulo pubkey = wpabuf_dup(wps->wps->dh_pubkey); 33252726Srpaulo#ifdef CONFIG_WPS_NFC 34252726Srpaulo } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap && 35252726Srpaulo wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) { 36252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys"); 37252726Srpaulo wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey); 38252726Srpaulo pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey); 39252726Srpaulo wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey); 40252726Srpaulo#endif /* CONFIG_WPS_NFC */ 41214734Srpaulo } else { 42214734Srpaulo wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys"); 43214734Srpaulo wps->dh_privkey = NULL; 44214734Srpaulo dh5_free(wps->dh_ctx); 45214734Srpaulo wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey); 46214734Srpaulo pubkey = wpabuf_zeropad(pubkey, 192); 47214734Srpaulo } 48214734Srpaulo if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) { 49189251Ssam wpa_printf(MSG_DEBUG, "WPS: Failed to initialize " 50189251Ssam "Diffie-Hellman handshake"); 51214734Srpaulo wpabuf_free(pubkey); 52189251Ssam return -1; 53189251Ssam } 54252726Srpaulo wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey); 55252726Srpaulo wpa_hexdump_buf(MSG_DEBUG, "WPS: DH own Public Key", pubkey); 56189251Ssam 57189251Ssam wpabuf_put_be16(msg, ATTR_PUBLIC_KEY); 58189251Ssam wpabuf_put_be16(msg, wpabuf_len(pubkey)); 59189251Ssam wpabuf_put_buf(msg, pubkey); 60189251Ssam 61189251Ssam if (wps->registrar) { 62189251Ssam wpabuf_free(wps->dh_pubkey_r); 63189251Ssam wps->dh_pubkey_r = pubkey; 64189251Ssam } else { 65189251Ssam wpabuf_free(wps->dh_pubkey_e); 66189251Ssam wps->dh_pubkey_e = pubkey; 67189251Ssam } 68189251Ssam 69189251Ssam return 0; 70189251Ssam} 71189251Ssam 72189251Ssam 73189251Ssamint wps_build_req_type(struct wpabuf *msg, enum wps_request_type type) 74189251Ssam{ 75189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Request Type"); 76189251Ssam wpabuf_put_be16(msg, ATTR_REQUEST_TYPE); 77189251Ssam wpabuf_put_be16(msg, 1); 78189251Ssam wpabuf_put_u8(msg, type); 79189251Ssam return 0; 80189251Ssam} 81189251Ssam 82189251Ssam 83214734Srpauloint wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type) 84214734Srpaulo{ 85214734Srpaulo wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", type); 86214734Srpaulo wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE); 87214734Srpaulo wpabuf_put_be16(msg, 1); 88214734Srpaulo wpabuf_put_u8(msg, type); 89214734Srpaulo return 0; 90214734Srpaulo} 91214734Srpaulo 92214734Srpaulo 93189251Ssamint wps_build_config_methods(struct wpabuf *msg, u16 methods) 94189251Ssam{ 95189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); 96189251Ssam wpabuf_put_be16(msg, ATTR_CONFIG_METHODS); 97189251Ssam wpabuf_put_be16(msg, 2); 98189251Ssam wpabuf_put_be16(msg, methods); 99189251Ssam return 0; 100189251Ssam} 101189251Ssam 102189251Ssam 103189251Ssamint wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid) 104189251Ssam{ 105189251Ssam wpa_printf(MSG_DEBUG, "WPS: * UUID-E"); 106189251Ssam wpabuf_put_be16(msg, ATTR_UUID_E); 107189251Ssam wpabuf_put_be16(msg, WPS_UUID_LEN); 108189251Ssam wpabuf_put_data(msg, uuid, WPS_UUID_LEN); 109189251Ssam return 0; 110189251Ssam} 111189251Ssam 112189251Ssam 113189251Ssamint wps_build_dev_password_id(struct wpabuf *msg, u16 id) 114189251Ssam{ 115189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Device Password ID (%d)", id); 116189251Ssam wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID); 117189251Ssam wpabuf_put_be16(msg, 2); 118189251Ssam wpabuf_put_be16(msg, id); 119189251Ssam return 0; 120189251Ssam} 121189251Ssam 122189251Ssam 123189251Ssamint wps_build_config_error(struct wpabuf *msg, u16 err) 124189251Ssam{ 125189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Configuration Error (%d)", err); 126189251Ssam wpabuf_put_be16(msg, ATTR_CONFIG_ERROR); 127189251Ssam wpabuf_put_be16(msg, 2); 128189251Ssam wpabuf_put_be16(msg, err); 129189251Ssam return 0; 130189251Ssam} 131189251Ssam 132189251Ssam 133189251Ssamint wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg) 134189251Ssam{ 135189251Ssam u8 hash[SHA256_MAC_LEN]; 136189251Ssam const u8 *addr[2]; 137189251Ssam size_t len[2]; 138189251Ssam 139189251Ssam if (wps->last_msg == NULL) { 140189251Ssam wpa_printf(MSG_DEBUG, "WPS: Last message not available for " 141189251Ssam "building authenticator"); 142189251Ssam return -1; 143189251Ssam } 144189251Ssam 145189251Ssam /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*) 146189251Ssam * (M_curr* is M_curr without the Authenticator attribute) 147189251Ssam */ 148189251Ssam addr[0] = wpabuf_head(wps->last_msg); 149189251Ssam len[0] = wpabuf_len(wps->last_msg); 150189251Ssam addr[1] = wpabuf_head(msg); 151189251Ssam len[1] = wpabuf_len(msg); 152189251Ssam hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash); 153189251Ssam 154189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Authenticator"); 155189251Ssam wpabuf_put_be16(msg, ATTR_AUTHENTICATOR); 156189251Ssam wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN); 157189251Ssam wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN); 158189251Ssam 159189251Ssam return 0; 160189251Ssam} 161189251Ssam 162189251Ssam 163189251Ssamint wps_build_version(struct wpabuf *msg) 164189251Ssam{ 165252726Srpaulo /* 166252726Srpaulo * Note: This attribute is deprecated and set to hardcoded 0x10 for 167252726Srpaulo * backwards compatibility reasons. The real version negotiation is 168252726Srpaulo * done with Version2. 169252726Srpaulo */ 170252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: * Version (hardcoded 0x10)"); 171189251Ssam wpabuf_put_be16(msg, ATTR_VERSION); 172189251Ssam wpabuf_put_be16(msg, 1); 173252726Srpaulo wpabuf_put_u8(msg, 0x10); 174252726Srpaulo return 0; 175252726Srpaulo} 176252726Srpaulo 177252726Srpaulo 178252726Srpauloint wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll, 179252726Srpaulo const u8 *auth_macs, size_t auth_macs_count) 180252726Srpaulo{ 181252726Srpaulo#ifdef CONFIG_WPS2 182252726Srpaulo u8 *len; 183252726Srpaulo 184252726Srpaulo wpabuf_put_be16(msg, ATTR_VENDOR_EXT); 185252726Srpaulo len = wpabuf_put(msg, 2); /* to be filled */ 186252726Srpaulo wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA); 187252726Srpaulo 188252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: * Version2 (0x%x)", WPS_VERSION); 189252726Srpaulo wpabuf_put_u8(msg, WFA_ELEM_VERSION2); 190252726Srpaulo wpabuf_put_u8(msg, 1); 191189251Ssam wpabuf_put_u8(msg, WPS_VERSION); 192252726Srpaulo 193252726Srpaulo if (req_to_enroll) { 194252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: * Request to Enroll (1)"); 195252726Srpaulo wpabuf_put_u8(msg, WFA_ELEM_REQUEST_TO_ENROLL); 196252726Srpaulo wpabuf_put_u8(msg, 1); 197252726Srpaulo wpabuf_put_u8(msg, 1); 198252726Srpaulo } 199252726Srpaulo 200252726Srpaulo if (auth_macs && auth_macs_count) { 201252726Srpaulo size_t i; 202252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)", 203252726Srpaulo (int) auth_macs_count); 204252726Srpaulo wpabuf_put_u8(msg, WFA_ELEM_AUTHORIZEDMACS); 205252726Srpaulo wpabuf_put_u8(msg, auth_macs_count * ETH_ALEN); 206252726Srpaulo wpabuf_put_data(msg, auth_macs, auth_macs_count * ETH_ALEN); 207252726Srpaulo for (i = 0; i < auth_macs_count; i++) 208252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: AuthorizedMAC: " MACSTR, 209252726Srpaulo MAC2STR(&auth_macs[i * ETH_ALEN])); 210252726Srpaulo } 211252726Srpaulo 212252726Srpaulo WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2); 213252726Srpaulo#endif /* CONFIG_WPS2 */ 214252726Srpaulo 215252726Srpaulo#ifdef CONFIG_WPS_TESTING 216252726Srpaulo if (WPS_VERSION > 0x20) { 217252726Srpaulo wpa_printf(MSG_DEBUG, "WPS: * Extensibility Testing - extra " 218252726Srpaulo "attribute"); 219252726Srpaulo wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST); 220252726Srpaulo wpabuf_put_be16(msg, 1); 221252726Srpaulo wpabuf_put_u8(msg, 42); 222252726Srpaulo } 223252726Srpaulo#endif /* CONFIG_WPS_TESTING */ 224189251Ssam return 0; 225189251Ssam} 226189251Ssam 227189251Ssam 228189251Ssamint wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type) 229189251Ssam{ 230189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Message Type (%d)", msg_type); 231189251Ssam wpabuf_put_be16(msg, ATTR_MSG_TYPE); 232189251Ssam wpabuf_put_be16(msg, 1); 233189251Ssam wpabuf_put_u8(msg, msg_type); 234189251Ssam return 0; 235189251Ssam} 236189251Ssam 237189251Ssam 238189251Ssamint wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg) 239189251Ssam{ 240189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Enrollee Nonce"); 241189251Ssam wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE); 242189251Ssam wpabuf_put_be16(msg, WPS_NONCE_LEN); 243189251Ssam wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN); 244189251Ssam return 0; 245189251Ssam} 246189251Ssam 247189251Ssam 248189251Ssamint wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg) 249189251Ssam{ 250189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Registrar Nonce"); 251189251Ssam wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE); 252189251Ssam wpabuf_put_be16(msg, WPS_NONCE_LEN); 253189251Ssam wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN); 254189251Ssam return 0; 255189251Ssam} 256189251Ssam 257189251Ssam 258189251Ssamint wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg) 259189251Ssam{ 260252726Srpaulo u16 auth_types = WPS_AUTH_TYPES; 261252726Srpaulo#ifdef CONFIG_WPS2 262252726Srpaulo auth_types &= ~WPS_AUTH_SHARED; 263252726Srpaulo#endif /* CONFIG_WPS2 */ 264189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Authentication Type Flags"); 265189251Ssam wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS); 266189251Ssam wpabuf_put_be16(msg, 2); 267252726Srpaulo wpabuf_put_be16(msg, auth_types); 268189251Ssam return 0; 269189251Ssam} 270189251Ssam 271189251Ssam 272189251Ssamint wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg) 273189251Ssam{ 274252726Srpaulo u16 encr_types = WPS_ENCR_TYPES; 275252726Srpaulo#ifdef CONFIG_WPS2 276252726Srpaulo encr_types &= ~WPS_ENCR_WEP; 277252726Srpaulo#endif /* CONFIG_WPS2 */ 278189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Encryption Type Flags"); 279189251Ssam wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS); 280189251Ssam wpabuf_put_be16(msg, 2); 281252726Srpaulo wpabuf_put_be16(msg, encr_types); 282189251Ssam return 0; 283189251Ssam} 284189251Ssam 285189251Ssam 286189251Ssamint wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg) 287189251Ssam{ 288189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Connection Type Flags"); 289189251Ssam wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS); 290189251Ssam wpabuf_put_be16(msg, 1); 291189251Ssam wpabuf_put_u8(msg, WPS_CONN_ESS); 292189251Ssam return 0; 293189251Ssam} 294189251Ssam 295189251Ssam 296189251Ssamint wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg) 297189251Ssam{ 298189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Association State"); 299189251Ssam wpabuf_put_be16(msg, ATTR_ASSOC_STATE); 300189251Ssam wpabuf_put_be16(msg, 2); 301189251Ssam wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC); 302189251Ssam return 0; 303189251Ssam} 304189251Ssam 305189251Ssam 306189251Ssamint wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg) 307189251Ssam{ 308189251Ssam u8 hash[SHA256_MAC_LEN]; 309189251Ssam 310189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Key Wrap Authenticator"); 311189251Ssam hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg), 312189251Ssam wpabuf_len(msg), hash); 313189251Ssam 314189251Ssam wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH); 315189251Ssam wpabuf_put_be16(msg, WPS_KWA_LEN); 316189251Ssam wpabuf_put_data(msg, hash, WPS_KWA_LEN); 317189251Ssam return 0; 318189251Ssam} 319189251Ssam 320189251Ssam 321189251Ssamint wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, 322189251Ssam struct wpabuf *plain) 323189251Ssam{ 324189251Ssam size_t pad_len; 325189251Ssam const size_t block_size = 16; 326189251Ssam u8 *iv, *data; 327189251Ssam 328189251Ssam wpa_printf(MSG_DEBUG, "WPS: * Encrypted Settings"); 329189251Ssam 330189251Ssam /* PKCS#5 v2.0 pad */ 331189251Ssam pad_len = block_size - wpabuf_len(plain) % block_size; 332189251Ssam os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len); 333189251Ssam 334189251Ssam wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS); 335189251Ssam wpabuf_put_be16(msg, block_size + wpabuf_len(plain)); 336189251Ssam 337189251Ssam iv = wpabuf_put(msg, block_size); 338252726Srpaulo if (random_get_bytes(iv, block_size) < 0) 339189251Ssam return -1; 340189251Ssam 341189251Ssam data = wpabuf_put(msg, 0); 342189251Ssam wpabuf_put_buf(msg, plain); 343189251Ssam if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain))) 344189251Ssam return -1; 345189251Ssam 346189251Ssam return 0; 347189251Ssam} 348214734Srpaulo 349214734Srpaulo 350214734Srpaulo#ifdef CONFIG_WPS_OOB 351252726Srpauloint wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id, 352252726Srpaulo const struct wpabuf *pubkey, const u8 *dev_pw, 353252726Srpaulo size_t dev_pw_len) 354214734Srpaulo{ 355214734Srpaulo size_t hash_len; 356214734Srpaulo const u8 *addr[1]; 357214734Srpaulo u8 pubkey_hash[WPS_HASH_LEN]; 358214734Srpaulo 359252726Srpaulo addr[0] = wpabuf_head(pubkey); 360252726Srpaulo hash_len = wpabuf_len(pubkey); 361214734Srpaulo sha256_vector(1, addr, &hash_len, pubkey_hash); 362214734Srpaulo 363252726Srpaulo wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); 364252726Srpaulo wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len); 365252726Srpaulo wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); 366252726Srpaulo wpabuf_put_be16(msg, dev_pw_id); 367252726Srpaulo wpabuf_put_data(msg, dev_pw, dev_pw_len); 368252726Srpaulo 369252726Srpaulo return 0; 370252726Srpaulo} 371252726Srpaulo#endif /* CONFIG_WPS_OOB */ 372252726Srpaulo 373252726Srpaulo 374252726Srpaulo/* Encapsulate WPS IE data with one (or more, if needed) IE headers */ 375252726Srpaulostruct wpabuf * wps_ie_encapsulate(struct wpabuf *data) 376252726Srpaulo{ 377252726Srpaulo struct wpabuf *ie; 378252726Srpaulo const u8 *pos, *end; 379252726Srpaulo 380252726Srpaulo ie = wpabuf_alloc(wpabuf_len(data) + 100); 381252726Srpaulo if (ie == NULL) { 382252726Srpaulo wpabuf_free(data); 383252726Srpaulo return NULL; 384214734Srpaulo } 385214734Srpaulo 386252726Srpaulo pos = wpabuf_head(data); 387252726Srpaulo end = pos + wpabuf_len(data); 388252726Srpaulo 389252726Srpaulo while (end > pos) { 390252726Srpaulo size_t frag_len = end - pos; 391252726Srpaulo if (frag_len > 251) 392252726Srpaulo frag_len = 251; 393252726Srpaulo wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); 394252726Srpaulo wpabuf_put_u8(ie, 4 + frag_len); 395252726Srpaulo wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); 396252726Srpaulo wpabuf_put_data(ie, pos, frag_len); 397252726Srpaulo pos += frag_len; 398214734Srpaulo } 399214734Srpaulo 400252726Srpaulo wpabuf_free(data); 401214734Srpaulo 402252726Srpaulo return ie; 403214734Srpaulo} 404