wps_common.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  1. /*
  2. * Wi-Fi Protected Setup - common functionality
  3. * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  4. *
  5. * This software may be distributed under the terms of the BSD license.
  6. * See README for more details.
  7. */
  8. #include "includes.h"
  9. #include "common.h"
  10. #include "crypto/aes_wrap.h"
  11. #include "crypto/crypto.h"
  12. #include "crypto/dh_group5.h"
  13. #include "crypto/sha1.h"
  14. #include "crypto/sha256.h"
  15. #include "crypto/random.h"
  16. #include "wps_i.h"
  17. void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
  18. const char *label, u8 *res, size_t res_len)
  19. {
  20. u8 i_buf[4], key_bits[4];
  21. const u8 *addr[4];
  22. size_t len[4];
  23. int i, iter;
  24. u8 hash[SHA256_MAC_LEN], *opos;
  25. size_t left;
  26. WPA_PUT_BE32(key_bits, res_len * 8);
  27. addr[0] = i_buf;
  28. len[0] = sizeof(i_buf);
  29. addr[1] = label_prefix;
  30. len[1] = label_prefix_len;
  31. addr[2] = (const u8 *) label;
  32. len[2] = os_strlen(label);
  33. addr[3] = key_bits;
  34. len[3] = sizeof(key_bits);
  35. iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
  36. opos = res;
  37. left = res_len;
  38. for (i = 1; i <= iter; i++) {
  39. WPA_PUT_BE32(i_buf, i);
  40. hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, len, hash);
  41. if (i < iter) {
  42. os_memcpy(opos, hash, SHA256_MAC_LEN);
  43. opos += SHA256_MAC_LEN;
  44. left -= SHA256_MAC_LEN;
  45. } else
  46. os_memcpy(opos, hash, left);
  47. }
  48. }
  49. int wps_derive_keys(struct wps_data *wps)
  50. {
  51. struct wpabuf *pubkey, *dh_shared;
  52. u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
  53. const u8 *addr[3];
  54. size_t len[3];
  55. u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
  56. if (wps->dh_privkey == NULL) {
  57. wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");
  58. return -1;
  59. }
  60. pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
  61. if (pubkey == NULL) {
  62. wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available");
  63. return -1;
  64. }
  65. wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
  66. wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
  67. dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
  68. dh5_free(wps->dh_ctx);
  69. wps->dh_ctx = NULL;
  70. dh_shared = wpabuf_zeropad(dh_shared, 192);
  71. if (dh_shared == NULL) {
  72. wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
  73. return -1;
  74. }
  75. /* Own DH private key is not needed anymore */
  76. wpabuf_free(wps->dh_privkey);
  77. wps->dh_privkey = NULL;
  78. wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
  79. /* DHKey = SHA-256(g^AB mod p) */
  80. addr[0] = wpabuf_head(dh_shared);
  81. len[0] = wpabuf_len(dh_shared);
  82. sha256_vector(1, addr, len, dhkey);
  83. wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
  84. wpabuf_free(dh_shared);
  85. /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
  86. addr[0] = wps->nonce_e;
  87. len[0] = WPS_NONCE_LEN;
  88. addr[1] = wps->mac_addr_e;
  89. len[1] = ETH_ALEN;
  90. addr[2] = wps->nonce_r;
  91. len[2] = WPS_NONCE_LEN;
  92. hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk);
  93. wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
  94. wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation",
  95. keys, sizeof(keys));
  96. os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
  97. os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
  98. os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
  99. WPS_EMSK_LEN);
  100. wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
  101. wps->authkey, WPS_AUTHKEY_LEN);
  102. wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
  103. wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
  104. wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
  105. return 0;
  106. }
  107. void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
  108. size_t dev_passwd_len)
  109. {
  110. u8 hash[SHA256_MAC_LEN];
  111. hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
  112. (dev_passwd_len + 1) / 2, hash);
  113. os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
  114. hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
  115. dev_passwd + (dev_passwd_len + 1) / 2,
  116. dev_passwd_len / 2, hash);
  117. os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
  118. wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
  119. dev_passwd, dev_passwd_len);
  120. wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
  121. wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
  122. }
  123. struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
  124. size_t encr_len)
  125. {
  126. struct wpabuf *decrypted;
  127. const size_t block_size = 16;
  128. size_t i;
  129. u8 pad;
  130. const u8 *pos;
  131. /* AES-128-CBC */
  132. if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
  133. {
  134. wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received");
  135. return NULL;
  136. }
  137. decrypted = wpabuf_alloc(encr_len - block_size);
  138. if (decrypted == NULL)
  139. return NULL;
  140. wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
  141. wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
  142. if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
  143. wpabuf_len(decrypted))) {
  144. wpabuf_free(decrypted);
  145. return NULL;
  146. }
  147. wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
  148. decrypted);
  149. pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
  150. pad = *pos;
  151. if (pad > wpabuf_len(decrypted)) {
  152. wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
  153. wpabuf_free(decrypted);
  154. return NULL;
  155. }
  156. for (i = 0; i < pad; i++) {
  157. if (*pos-- != pad) {
  158. wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
  159. "string");
  160. wpabuf_free(decrypted);
  161. return NULL;
  162. }
  163. }
  164. decrypted->used -= pad;
  165. return decrypted;
  166. }
  167. /**
  168. * wps_pin_checksum - Compute PIN checksum
  169. * @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit)
  170. * Returns: Checksum digit
  171. */
  172. unsigned int wps_pin_checksum(unsigned int pin)
  173. {
  174. unsigned int accum = 0;
  175. while (pin) {
  176. accum += 3 * (pin % 10);
  177. pin /= 10;
  178. accum += pin % 10;
  179. pin /= 10;
  180. }
  181. return (10 - accum % 10) % 10;
  182. }
  183. /**
  184. * wps_pin_valid - Check whether a PIN has a valid checksum
  185. * @pin: Eight digit PIN (i.e., including the checksum digit)
  186. * Returns: 1 if checksum digit is valid, or 0 if not
  187. */
  188. unsigned int wps_pin_valid(unsigned int pin)
  189. {
  190. return wps_pin_checksum(pin / 10) == (pin % 10);
  191. }
  192. /**
  193. * wps_generate_pin - Generate a random PIN
  194. * Returns: Eight digit PIN (i.e., including the checksum digit)
  195. */
  196. unsigned int wps_generate_pin(void)
  197. {
  198. unsigned int val;
  199. /* Generate seven random digits for the PIN */
  200. if (random_get_bytes((unsigned char *) &val, sizeof(val)) < 0) {
  201. struct os_time now;
  202. os_get_time(&now);
  203. val = os_random() ^ now.sec ^ now.usec;
  204. }
  205. val %= 10000000;
  206. /* Append checksum digit */
  207. return val * 10 + wps_pin_checksum(val);
  208. }
  209. int wps_pin_str_valid(const char *pin)
  210. {
  211. const char *p;
  212. size_t len;
  213. p = pin;
  214. while (*p >= '0' && *p <= '9')
  215. p++;
  216. if (*p != '\0')
  217. return 0;
  218. len = p - pin;
  219. return len == 4 || len == 8;
  220. }
  221. void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
  222. u16 config_error, u16 error_indication)
  223. {
  224. union wps_event_data data;
  225. if (wps->event_cb == NULL)
  226. return;
  227. os_memset(&data, 0, sizeof(data));
  228. data.fail.msg = msg;
  229. data.fail.config_error = config_error;
  230. data.fail.error_indication = error_indication;
  231. wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
  232. }
  233. void wps_success_event(struct wps_context *wps)
  234. {
  235. if (wps->event_cb == NULL)
  236. return;
  237. wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL);
  238. }
  239. void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
  240. {
  241. union wps_event_data data;
  242. if (wps->event_cb == NULL)
  243. return;
  244. os_memset(&data, 0, sizeof(data));
  245. data.pwd_auth_fail.enrollee = enrollee;
  246. data.pwd_auth_fail.part = part;
  247. wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data);
  248. }
  249. void wps_pbc_overlap_event(struct wps_context *wps)
  250. {
  251. if (wps->event_cb == NULL)
  252. return;
  253. wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL);
  254. }
  255. void wps_pbc_timeout_event(struct wps_context *wps)
  256. {
  257. if (wps->event_cb == NULL)
  258. return;
  259. wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL);
  260. }
  261. #ifdef CONFIG_WPS_OOB
  262. struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
  263. {
  264. struct wps_data data;
  265. struct wpabuf *plain;
  266. plain = wpabuf_alloc(500);
  267. if (plain == NULL) {
  268. wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
  269. "credential");
  270. return NULL;
  271. }
  272. os_memset(&data, 0, sizeof(data));
  273. data.wps = wps;
  274. data.auth_type = wps->auth_types;
  275. data.encr_type = wps->encr_types;
  276. if (wps_build_version(plain) ||
  277. wps_build_cred(&data, plain) ||
  278. wps_build_wfa_ext(plain, 0, NULL, 0)) {
  279. wpabuf_free(plain);
  280. return NULL;
  281. }
  282. return plain;
  283. }
  284. struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
  285. const struct wpabuf *pubkey,
  286. const struct wpabuf *dev_pw)
  287. {
  288. struct wpabuf *data;
  289. data = wpabuf_alloc(200);
  290. if (data == NULL)
  291. return NULL;
  292. if (wps_build_version(data) ||
  293. wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
  294. wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
  295. wps_build_wfa_ext(data, 0, NULL, 0)) {
  296. wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
  297. "token");
  298. wpabuf_free(data);
  299. return NULL;
  300. }
  301. return data;
  302. }
  303. int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr)
  304. {
  305. struct wpabuf msg;
  306. size_t i;
  307. for (i = 0; i < attr->num_cred; i++) {
  308. struct wps_credential local_cred;
  309. struct wps_parse_attr cattr;
  310. os_memset(&local_cred, 0, sizeof(local_cred));
  311. wpabuf_set(&msg, attr->cred[i], attr->cred_len[i]);
  312. if (wps_parse_msg(&msg, &cattr) < 0 ||
  313. wps_process_cred(&cattr, &local_cred)) {
  314. wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
  315. "credential");
  316. return -1;
  317. }
  318. wps->cred_cb(wps->cb_ctx, &local_cred);
  319. }
  320. return 0;
  321. }
  322. #endif /* CONFIG_WPS_OOB */
  323. int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN])
  324. {
  325. const char *pos;
  326. /* <categ>-<OUI>-<subcateg> */
  327. WPA_PUT_BE16(dev_type, atoi(str));
  328. pos = os_strchr(str, '-');
  329. if (pos == NULL)
  330. return -1;
  331. pos++;
  332. if (hexstr2bin(pos, &dev_type[2], 4))
  333. return -1;
  334. pos = os_strchr(pos, '-');
  335. if (pos == NULL)
  336. return -1;
  337. pos++;
  338. WPA_PUT_BE16(&dev_type[6], atoi(pos));
  339. return 0;
  340. }
  341. char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
  342. size_t buf_len)
  343. {
  344. int ret;
  345. ret = os_snprintf(buf, buf_len, "%u-%08X-%u",
  346. WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
  347. WPA_GET_BE16(&dev_type[6]));
  348. if (ret < 0 || (unsigned int) ret >= buf_len)
  349. return NULL;
  350. return buf;
  351. }
  352. void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid)
  353. {
  354. const u8 *addr[2];
  355. size_t len[2];
  356. u8 hash[SHA1_MAC_LEN];
  357. u8 nsid[16] = {
  358. 0x52, 0x64, 0x80, 0xf8,
  359. 0xc9, 0x9b,
  360. 0x4b, 0xe5,
  361. 0xa6, 0x55,
  362. 0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84
  363. };
  364. addr[0] = nsid;
  365. len[0] = sizeof(nsid);
  366. addr[1] = mac_addr;
  367. len[1] = 6;
  368. sha1_vector(2, addr, len, hash);
  369. os_memcpy(uuid, hash, 16);
  370. /* Version: 5 = named-based version using SHA-1 */
  371. uuid[6] = (5 << 4) | (uuid[6] & 0x0f);
  372. /* Variant specified in RFC 4122 */
  373. uuid[8] = 0x80 | (uuid[8] & 0x3f);
  374. }
  375. u16 wps_config_methods_str2bin(const char *str)
  376. {
  377. u16 methods = 0;
  378. if (str == NULL) {
  379. /* Default to enabling methods based on build configuration */
  380. methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
  381. #ifdef CONFIG_WPS2
  382. methods |= WPS_CONFIG_VIRT_DISPLAY;
  383. #endif /* CONFIG_WPS2 */
  384. #ifdef CONFIG_WPS_NFC
  385. methods |= WPS_CONFIG_NFC_INTERFACE;
  386. #endif /* CONFIG_WPS_NFC */
  387. } else {
  388. if (os_strstr(str, "ethernet"))
  389. methods |= WPS_CONFIG_ETHERNET;
  390. if (os_strstr(str, "label"))
  391. methods |= WPS_CONFIG_LABEL;
  392. if (os_strstr(str, "display"))
  393. methods |= WPS_CONFIG_DISPLAY;
  394. if (os_strstr(str, "ext_nfc_token"))
  395. methods |= WPS_CONFIG_EXT_NFC_TOKEN;
  396. if (os_strstr(str, "int_nfc_token"))
  397. methods |= WPS_CONFIG_INT_NFC_TOKEN;
  398. if (os_strstr(str, "nfc_interface"))
  399. methods |= WPS_CONFIG_NFC_INTERFACE;
  400. if (os_strstr(str, "push_button"))
  401. methods |= WPS_CONFIG_PUSHBUTTON;
  402. if (os_strstr(str, "keypad"))
  403. methods |= WPS_CONFIG_KEYPAD;
  404. #ifdef CONFIG_WPS2
  405. if (os_strstr(str, "virtual_display"))
  406. methods |= WPS_CONFIG_VIRT_DISPLAY;
  407. if (os_strstr(str, "physical_display"))
  408. methods |= WPS_CONFIG_PHY_DISPLAY;
  409. if (os_strstr(str, "virtual_push_button"))
  410. methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
  411. if (os_strstr(str, "physical_push_button"))
  412. methods |= WPS_CONFIG_PHY_PUSHBUTTON;
  413. #endif /* CONFIG_WPS2 */
  414. }
  415. return methods;
  416. }
  417. struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
  418. {
  419. struct wpabuf *msg;
  420. wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
  421. msg = wpabuf_alloc(1000);
  422. if (msg == NULL)
  423. return NULL;
  424. if (wps_build_version(msg) ||
  425. wps_build_msg_type(msg, WPS_WSC_ACK) ||
  426. wps_build_enrollee_nonce(wps, msg) ||
  427. wps_build_registrar_nonce(wps, msg) ||
  428. wps_build_wfa_ext(msg, 0, NULL, 0)) {
  429. wpabuf_free(msg);
  430. return NULL;
  431. }
  432. return msg;
  433. }
  434. struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
  435. {
  436. struct wpabuf *msg;
  437. wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
  438. msg = wpabuf_alloc(1000);
  439. if (msg == NULL)
  440. return NULL;
  441. if (wps_build_version(msg) ||
  442. wps_build_msg_type(msg, WPS_WSC_NACK) ||
  443. wps_build_enrollee_nonce(wps, msg) ||
  444. wps_build_registrar_nonce(wps, msg) ||
  445. wps_build_config_error(msg, wps->config_error) ||
  446. wps_build_wfa_ext(msg, 0, NULL, 0)) {
  447. wpabuf_free(msg);
  448. return NULL;
  449. }
  450. return msg;
  451. }
  452. #ifdef CONFIG_WPS_NFC
  453. struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
  454. struct wpabuf *dev_pw)
  455. {
  456. struct wpabuf *ret;
  457. if (pubkey == NULL || dev_pw == NULL)
  458. return NULL;
  459. ret = wps_build_nfc_pw_token(id, pubkey, dev_pw);
  460. if (ndef && ret) {
  461. struct wpabuf *tmp;
  462. tmp = ndef_build_wifi(ret);
  463. wpabuf_free(ret);
  464. if (tmp == NULL)
  465. return NULL;
  466. ret = tmp;
  467. }
  468. return ret;
  469. }
  470. struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
  471. struct wpabuf **privkey,
  472. struct wpabuf **dev_pw)
  473. {
  474. struct wpabuf *priv = NULL, *pub = NULL, *pw;
  475. void *dh_ctx;
  476. u16 val;
  477. pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
  478. if (pw == NULL)
  479. return NULL;
  480. if (random_get_bytes(wpabuf_put(pw, WPS_OOB_DEVICE_PASSWORD_LEN),
  481. WPS_OOB_DEVICE_PASSWORD_LEN) ||
  482. random_get_bytes((u8 *) &val, sizeof(val))) {
  483. wpabuf_free(pw);
  484. return NULL;
  485. }
  486. dh_ctx = dh5_init(&priv, &pub);
  487. if (dh_ctx == NULL) {
  488. wpabuf_free(pw);
  489. return NULL;
  490. }
  491. dh5_free(dh_ctx);
  492. *id = 0x10 + val % 0xfff0;
  493. wpabuf_free(*pubkey);
  494. *pubkey = pub;
  495. wpabuf_free(*privkey);
  496. *privkey = priv;
  497. wpabuf_free(*dev_pw);
  498. *dev_pw = pw;
  499. return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw);
  500. }
  501. #endif /* CONFIG_WPS_NFC */