WPS: Fix CONFIG_WPS_OOB build
[libeap.git] / src / wps / wps_common.c
index 1a4ae03..82e6311 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - common functionality
- * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
 #include "includes.h"
 
 #include "common.h"
-#include "dh_groups.h"
-#include "sha256.h"
-#include "aes_wrap.h"
-#include "crypto.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
 #include "wps_i.h"
+#include "wps_dev_attr.h"
 
 
-static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
-                       const u8 *pos, u16 len)
-{
-       switch (type) {
-       case ATTR_VERSION:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
-                                  len);
-                       return -1;
-               }
-               attr->version = pos;
-               break;
-       case ATTR_MSG_TYPE:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->msg_type = pos;
-               break;
-       case ATTR_ENROLLEE_NONCE:
-               if (len != WPS_NONCE_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->enrollee_nonce = pos;
-               break;
-       case ATTR_REGISTRAR_NONCE:
-               if (len != WPS_NONCE_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->registrar_nonce = pos;
-               break;
-       case ATTR_UUID_E:
-               if (len != WPS_UUID_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
-                                  len);
-                       return -1;
-               }
-               attr->uuid_e = pos;
-               break;
-       case ATTR_UUID_R:
-               if (len != WPS_UUID_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
-                                  len);
-                       return -1;
-               }
-               attr->uuid_r = pos;
-               break;
-       case ATTR_AUTH_TYPE_FLAGS:
-               if (len != 2) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
-                                  "Type Flags length %u", len);
-                       return -1;
-               }
-               attr->auth_type_flags = pos;
-               break;
-       case ATTR_ENCR_TYPE_FLAGS:
-               if (len != 2) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
-                                  "Flags length %u", len);
-                       return -1;
-               }
-               attr->encr_type_flags = pos;
-               break;
-       case ATTR_CONN_TYPE_FLAGS:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
-                                  "Flags length %u", len);
-                       return -1;
-               }
-               attr->conn_type_flags = pos;
-               break;
-       case ATTR_CONFIG_METHODS:
-               if (len != 2) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->config_methods = pos;
-               break;
-       case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
-               if (len != 2) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
-                                  "Registrar Config Methods length %u", len);
-                       return -1;
-               }
-               attr->sel_reg_config_methods = pos;
-               break;
-       case ATTR_PRIMARY_DEV_TYPE:
-               if (len != sizeof(struct wps_dev_type)) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
-                                  "Type length %u", len);
-                       return -1;
-               }
-               attr->primary_dev_type = pos;
-               break;
-       case ATTR_RF_BANDS:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
-                                  "%u", len);
-                       return -1;
-               }
-               attr->rf_bands = pos;
-               break;
-       case ATTR_ASSOC_STATE:
-               if (len != 2) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->assoc_state = pos;
-               break;
-       case ATTR_CONFIG_ERROR:
-               if (len != 2) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
-                                  "Error length %u", len);
-                       return -1;
-               }
-               attr->config_error = pos;
-               break;
-       case ATTR_DEV_PASSWORD_ID:
-               if (len != 2) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
-                                  "ID length %u", len);
-                       return -1;
-               }
-               attr->dev_password_id = pos;
-               break;
-       case ATTR_OS_VERSION:
-               if (len != 4) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
-                                  "%u", len);
-                       return -1;
-               }
-               attr->os_version = pos;
-               break;
-       case ATTR_WPS_STATE:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
-                                  "Setup State length %u", len);
-                       return -1;
-               }
-               attr->wps_state = pos;
-               break;
-       case ATTR_AUTHENTICATOR:
-               if (len != WPS_AUTHENTICATOR_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->authenticator = pos;
-               break;
-       case ATTR_R_HASH1:
-               if (len != WPS_HASH_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
-                                  len);
-                       return -1;
-               }
-               attr->r_hash1 = pos;
-               break;
-       case ATTR_R_HASH2:
-               if (len != WPS_HASH_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
-                                  len);
-                       return -1;
-               }
-               attr->r_hash2 = pos;
-               break;
-       case ATTR_E_HASH1:
-               if (len != WPS_HASH_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
-                                  len);
-                       return -1;
-               }
-               attr->e_hash1 = pos;
-               break;
-       case ATTR_E_HASH2:
-               if (len != WPS_HASH_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
-                                  len);
-                       return -1;
-               }
-               attr->e_hash2 = pos;
-               break;
-       case ATTR_R_SNONCE1:
-               if (len != WPS_SECRET_NONCE_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
-                                  "%u", len);
-                       return -1;
-               }
-               attr->r_snonce1 = pos;
-               break;
-       case ATTR_R_SNONCE2:
-               if (len != WPS_SECRET_NONCE_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
-                                  "%u", len);
-                       return -1;
-               }
-               attr->r_snonce2 = pos;
-               break;
-       case ATTR_E_SNONCE1:
-               if (len != WPS_SECRET_NONCE_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
-                                  "%u", len);
-                       return -1;
-               }
-               attr->e_snonce1 = pos;
-               break;
-       case ATTR_E_SNONCE2:
-               if (len != WPS_SECRET_NONCE_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
-                                  "%u", len);
-                       return -1;
-               }
-               attr->e_snonce2 = pos;
-               break;
-       case ATTR_KEY_WRAP_AUTH:
-               if (len != WPS_KWA_LEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
-                                  "Authenticator length %u", len);
-                       return -1;
-               }
-               attr->key_wrap_auth = pos;
-               break;
-       case ATTR_AUTH_TYPE:
-               if (len != 2) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
-                                  "Type length %u", len);
-                       return -1;
-               }
-               attr->auth_type = pos;
-               break;
-       case ATTR_ENCR_TYPE:
-               if (len != 2) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
-                                  "Type length %u", len);
-                       return -1;
-               }
-               attr->encr_type = pos;
-               break;
-       case ATTR_NETWORK_INDEX:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->network_idx = pos;
-               break;
-       case ATTR_NETWORK_KEY_INDEX:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->network_key_idx = pos;
-               break;
-       case ATTR_MAC_ADDR:
-               if (len != ETH_ALEN) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->mac_addr = pos;
-               break;
-       case ATTR_KEY_PROVIDED_AUTO:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
-                                  "Automatically length %u", len);
-                       return -1;
-               }
-               attr->key_prov_auto = pos;
-               break;
-       case ATTR_802_1X_ENABLED:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->dot1x_enabled = pos;
-               break;
-       case ATTR_SELECTED_REGISTRAR:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
-                                  " length %u", len);
-                       return -1;
-               }
-               attr->selected_registrar = pos;
-               break;
-       case ATTR_REQUEST_TYPE:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->request_type = pos;
-               break;
-       case ATTR_RESPONSE_TYPE:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->request_type = pos;
-               break;
-       case ATTR_MANUFACTURER:
-               attr->manufacturer = pos;
-               attr->manufacturer_len = len;
-               break;
-       case ATTR_MODEL_NAME:
-               attr->model_name = pos;
-               attr->model_name_len = len;
-               break;
-       case ATTR_MODEL_NUMBER:
-               attr->model_number = pos;
-               attr->model_number_len = len;
-               break;
-       case ATTR_SERIAL_NUMBER:
-               attr->serial_number = pos;
-               attr->serial_number_len = len;
-               break;
-       case ATTR_DEV_NAME:
-               attr->dev_name = pos;
-               attr->dev_name_len = len;
-               break;
-       case ATTR_PUBLIC_KEY:
-               attr->public_key = pos;
-               attr->public_key_len = len;
-               break;
-       case ATTR_ENCR_SETTINGS:
-               attr->encr_settings = pos;
-               attr->encr_settings_len = len;
-               break;
-       case ATTR_CRED:
-               if (attr->num_cred >= MAX_CRED_COUNT) {
-                       wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
-                                  "attribute (max %d credentials)",
-                                  MAX_CRED_COUNT);
-                       break;
-               }
-               attr->cred[attr->num_cred] = pos;
-               attr->cred_len[attr->num_cred] = len;
-               attr->num_cred++;
-               break;
-       case ATTR_SSID:
-               attr->ssid = pos;
-               attr->ssid_len = len;
-               break;
-       case ATTR_NETWORK_KEY:
-               attr->network_key = pos;
-               attr->network_key_len = len;
-               break;
-       case ATTR_EAP_TYPE:
-               attr->eap_type = pos;
-               attr->eap_type_len = len;
-               break;
-       case ATTR_EAP_IDENTITY:
-               attr->eap_identity = pos;
-               attr->eap_identity_len = len;
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
-                          "len=%u", type, len);
-               break;
-       }
-
-       return 0;
-}
-
-
-int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
-{
-       const u8 *pos, *end;
-       u16 type, len;
-
-       os_memset(attr, 0, sizeof(*attr));
-       pos = wpabuf_head(msg);
-       end = pos + wpabuf_len(msg);
-
-       while (pos < end) {
-               if (end - pos < 4) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
-                                  "%lu bytes remaining",
-                                  (unsigned long) (end - pos));
-                       return -1;
-               }
-
-               type = WPA_GET_BE16(pos);
-               pos += 2;
-               len = WPA_GET_BE16(pos);
-               pos += 2;
-               wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u",
-                          type, len);
-               if (len > end - pos) {
-                       wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
-                       return -1;
-               }
-
-               if (wps_set_attr(attr, type, pos, len) < 0)
-                       return -1;
-
-               pos += len;
-       }
-
-       return 0;
-}
-
-
-void wps_kdf(const u8 *key, const char *label, u8 *res, size_t res_len)
+void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
+            const char *label, u8 *res, size_t res_len)
 {
        u8 i_buf[4], key_bits[4];
-       const u8 *addr[3];
-       size_t len[3];
+       const u8 *addr[4];
+       size_t len[4];
        int i, iter;
        u8 hash[SHA256_MAC_LEN], *opos;
        size_t left;
@@ -446,10 +38,12 @@ void wps_kdf(const u8 *key, const char *label, u8 *res, size_t res_len)
 
        addr[0] = i_buf;
        len[0] = sizeof(i_buf);
-       addr[1] = (const u8 *) label;
-       len[1] = os_strlen(label);
-       addr[2] = key_bits;
-       len[2] = sizeof(key_bits);
+       addr[1] = label_prefix;
+       len[1] = label_prefix_len;
+       addr[2] = (const u8 *) label;
+       len[2] = os_strlen(label);
+       addr[3] = key_bits;
+       len[3] = sizeof(key_bits);
 
        iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
        opos = res;
@@ -457,7 +51,7 @@ void wps_kdf(const u8 *key, const char *label, u8 *res, size_t res_len)
 
        for (i = 1; i <= iter; i++) {
                WPA_PUT_BE32(i_buf, i);
-               hmac_sha256_vector(key, SHA256_MAC_LEN, 3, addr, len, hash);
+               hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, len, hash);
                if (i < iter) {
                        os_memcpy(opos, hash, SHA256_MAC_LEN);
                        opos += SHA256_MAC_LEN;
@@ -468,34 +62,6 @@ void wps_kdf(const u8 *key, const char *label, u8 *res, size_t res_len)
 }
 
 
-int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
-{
-       struct wpabuf *pubkey;
-
-       wpa_printf(MSG_DEBUG, "WPS:  * Public Key");
-       pubkey = dh_init(dh_groups_get(WPS_DH_GROUP), &wps->dh_privkey);
-       if (pubkey == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
-                          "Diffie-Hellman handshake");
-               return -1;
-       }
-
-       wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
-       wpabuf_put_be16(msg, wpabuf_len(pubkey));
-       wpabuf_put_buf(msg, pubkey);
-
-       if (wps->registrar) {
-               wpabuf_free(wps->dh_pubkey_r);
-               wps->dh_pubkey_r = pubkey;
-       } else {
-               wpabuf_free(wps->dh_pubkey_e);
-               wps->dh_pubkey_e = pubkey;
-       }
-
-       return 0;
-}
-
-
 int wps_derive_keys(struct wps_data *wps)
 {
        struct wpabuf *pubkey, *dh_shared;
@@ -515,8 +81,12 @@ int wps_derive_keys(struct wps_data *wps)
                return -1;
        }
 
-       dh_shared = dh_derive_shared(pubkey, wps->dh_privkey,
-                                    dh_groups_get(WPS_DH_GROUP));
+       wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH Private Key", wps->dh_privkey);
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: DH peer Public Key", pubkey);
+       dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
+       dh5_free(wps->dh_ctx);
+       wps->dh_ctx = NULL;
+       dh_shared = wpabuf_zeropad(dh_shared, 192);
        if (dh_shared == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
                return -1;
@@ -545,7 +115,7 @@ int wps_derive_keys(struct wps_data *wps)
        hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk);
        wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
 
-       wps_kdf(kdk, "Wi-Fi Easy and Secure Key Derivation",
+       wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation",
                keys, sizeof(keys));
        os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
        os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
@@ -562,73 +132,6 @@ int wps_derive_keys(struct wps_data *wps)
 }
 
 
-int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
-{
-       u8 hash[SHA256_MAC_LEN];
-       const u8 *addr[2];
-       size_t len[2];
-
-       if (wps->last_msg == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
-                          "building authenticator");
-               return -1;
-       }
-
-       /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
-        * (M_curr* is M_curr without the Authenticator attribute)
-        */
-       addr[0] = wpabuf_head(wps->last_msg);
-       len[0] = wpabuf_len(wps->last_msg);
-       addr[1] = wpabuf_head(msg);
-       len[1] = wpabuf_len(msg);
-       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
-
-       wpa_printf(MSG_DEBUG, "WPS:  * Authenticator");
-       wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
-       wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
-       wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
-
-       return 0;
-}
-
-
-int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
-                             const struct wpabuf *msg)
-{
-       u8 hash[SHA256_MAC_LEN];
-       const u8 *addr[2];
-       size_t len[2];
-
-       if (authenticator == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: No Authenticator attribute "
-                          "included");
-               return -1;
-       }
-
-       if (wps->last_msg == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
-                          "validating authenticator");
-               return -1;
-       }
-
-       /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
-        * (M_curr* is M_curr without the Authenticator attribute)
-        */
-       addr[0] = wpabuf_head(wps->last_msg);
-       len[0] = wpabuf_len(wps->last_msg);
-       addr[1] = wpabuf_head(msg);
-       len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
-       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
-
-       if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
-               wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
-               return -1;
-       }
-
-       return 0;
-}
-
-
 void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
                    size_t dev_passwd_len)
 {
@@ -701,371 +204,450 @@ struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
 }
 
 
-int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
-                             const u8 *key_wrap_auth)
-{
-       u8 hash[SHA256_MAC_LEN];
-       const u8 *head;
-       size_t len;
-
-       if (key_wrap_auth == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: No KWA in decrypted attribute");
-               return -1;
-       }
-
-       head = wpabuf_head(msg);
-       len = wpabuf_len(msg) - 4 - WPS_KWA_LEN;
-       if (head + len != key_wrap_auth - 4) {
-               wpa_printf(MSG_DEBUG, "WPS: KWA not in the end of the "
-                          "decrypted attribute");
-               return -1;
-       }
-
-       hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
-       if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
-               wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
-               return -1;
+/**
+ * wps_pin_checksum - Compute PIN checksum
+ * @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit)
+ * Returns: Checksum digit
+ */
+unsigned int wps_pin_checksum(unsigned int pin)
+{
+       unsigned int accum = 0;
+       while (pin) {
+               accum += 3 * (pin % 10);
+               pin /= 10;
+               accum += pin % 10;
+               pin /= 10;
        }
 
-       return 0;
+       return (10 - accum % 10) % 10;
 }
 
 
-int wps_build_version(struct wpabuf *msg)
+/**
+ * wps_pin_valid - Check whether a PIN has a valid checksum
+ * @pin: Eight digit PIN (i.e., including the checksum digit)
+ * Returns: 1 if checksum digit is valid, or 0 if not
+ */
+unsigned int wps_pin_valid(unsigned int pin)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * Version");
-       wpabuf_put_be16(msg, ATTR_VERSION);
-       wpabuf_put_be16(msg, 1);
-       wpabuf_put_u8(msg, WPS_VERSION);
-       return 0;
+       return wps_pin_checksum(pin / 10) == (pin % 10);
 }
 
 
-int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
+/**
+ * wps_generate_pin - Generate a random PIN
+ * Returns: Eight digit PIN (i.e., including the checksum digit)
+ */
+unsigned int wps_generate_pin(void)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * Message Type (%d)", msg_type);
-       wpabuf_put_be16(msg, ATTR_MSG_TYPE);
-       wpabuf_put_be16(msg, 1);
-       wpabuf_put_u8(msg, msg_type);
-       return 0;
-}
+       unsigned int val;
 
+       /* Generate seven random digits for the PIN */
+       if (os_get_random((unsigned char *) &val, sizeof(val)) < 0) {
+               struct os_time now;
+               os_get_time(&now);
+               val = os_random() ^ now.sec ^ now.usec;
+       }
+       val %= 10000000;
 
-int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
-{
-       wpa_printf(MSG_DEBUG, "WPS:  * Enrollee Nonce");
-       wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
-       wpabuf_put_be16(msg, WPS_NONCE_LEN);
-       wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
-       return 0;
+       /* Append checksum digit */
+       return val * 10 + wps_pin_checksum(val);
 }
 
 
-int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * Registrar Nonce");
-       wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
-       wpabuf_put_be16(msg, WPS_NONCE_LEN);
-       wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
-       return 0;
-}
+       union wps_event_data data;
 
+       if (wps->event_cb == NULL)
+               return;
 
-int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
-{
-       wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags");
-       wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
-       wpabuf_put_be16(msg, 2);
-       wpabuf_put_be16(msg, WPS_AUTH_TYPES);
-       return 0;
+       os_memset(&data, 0, sizeof(data));
+       data.fail.msg = msg;
+       wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
 }
 
 
-int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
+void wps_success_event(struct wps_context *wps)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags");
-       wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
-       wpabuf_put_be16(msg, 2);
-       wpabuf_put_be16(msg, WPS_ENCR_TYPES);
-       return 0;
+       if (wps->event_cb == NULL)
+               return;
+
+       wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL);
 }
 
 
-int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * Connection Type Flags");
-       wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
-       wpabuf_put_be16(msg, 1);
-       wpabuf_put_u8(msg, WPS_CONN_ESS);
-       return 0;
-}
+       union wps_event_data data;
 
+       if (wps->event_cb == NULL)
+               return;
 
-int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
-{
-       wpa_printf(MSG_DEBUG, "WPS:  * Association State");
-       wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
-       wpabuf_put_be16(msg, 2);
-       wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
-       return 0;
+       os_memset(&data, 0, sizeof(data));
+       data.pwd_auth_fail.enrollee = enrollee;
+       data.pwd_auth_fail.part = part;
+       wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data);
 }
 
 
-int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
+void wps_pbc_overlap_event(struct wps_context *wps)
 {
-       u8 hash[SHA256_MAC_LEN];
+       if (wps->event_cb == NULL)
+               return;
 
-       wpa_printf(MSG_DEBUG, "WPS:  * Key Wrap Authenticator");
-       hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
-                   wpabuf_len(msg), hash);
-
-       wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
-       wpabuf_put_be16(msg, WPS_KWA_LEN);
-       wpabuf_put_data(msg, hash, WPS_KWA_LEN);
-       return 0;
+       wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL);
 }
 
 
-int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
-                           struct wpabuf *plain)
+void wps_pbc_timeout_event(struct wps_context *wps)
 {
-       size_t pad_len;
-       const size_t block_size = 16;
-       u8 *iv, *data;
+       if (wps->event_cb == NULL)
+               return;
 
-       wpa_printf(MSG_DEBUG, "WPS:  * Encrypted Settings");
+       wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL);
+}
 
-       /* PKCS#5 v2.0 pad */
-       pad_len = block_size - wpabuf_len(plain) % block_size;
-       os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
 
-       wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
-       wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
+#ifdef CONFIG_WPS_OOB
 
-       iv = wpabuf_put(msg, block_size);
-       if (os_get_random(iv, block_size) < 0)
-               return -1;
+static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+{
+       struct wps_data data;
+       struct wpabuf *plain;
 
-       data = wpabuf_put(msg, 0);
-       wpabuf_put_buf(msg, plain);
-       if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
-               return -1;
+       plain = wpabuf_alloc(500);
+       if (plain == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+                          "credential");
+               return NULL;
+       }
 
-       return 0;
+       os_memset(&data, 0, sizeof(data));
+       data.wps = wps;
+       data.auth_type = wps->auth_types;
+       data.encr_type = wps->encr_types;
+       if (wps_build_version(plain) ||
+           wps_build_cred(&data, plain) ||
+           wps_build_wfa_ext(plain, 0, NULL, 0)) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       return plain;
 }
 
 
-static int wps_process_cred_network_idx(struct wps_credential *cred,
-                                       const u8 *idx)
+static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
 {
-       if (idx == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
-                          "Network Index");
-               return -1;
+       struct wpabuf *data;
+
+       data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+       if (data == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+                          "device password attribute");
+               return NULL;
        }
 
-       wpa_printf(MSG_DEBUG, "WPS: Network Index: %d", *idx);
+       wpabuf_free(wps->oob_conf.dev_password);
+       wps->oob_conf.dev_password =
+               wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+       if (wps->oob_conf.dev_password == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+                          "device password");
+               wpabuf_free(data);
+               return NULL;
+       }
 
-       return 0;
+       if (wps_build_version(data) ||
+           wps_build_oob_dev_password(data, wps) ||
+           wps_build_wfa_ext(data, 0, NULL, 0)) {
+               wpa_printf(MSG_ERROR, "WPS: Build OOB device password "
+                          "attribute error");
+               wpabuf_free(data);
+               return NULL;
+       }
+
+       return data;
 }
 
 
-static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid,
-                                size_t ssid_len)
+static int wps_parse_oob_dev_pwd(struct wps_context *wps,
+                                struct wpabuf *data)
 {
-       if (ssid == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: Credential did not include SSID");
+       struct oob_conf_data *oob_conf = &wps->oob_conf;
+       struct wps_parse_attr attr;
+       const u8 *pos;
+
+       if (wps_parse_msg(data, &attr) < 0 ||
+           attr.oob_dev_password == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: OOB device password not found");
                return -1;
        }
 
-       /* Remove zero-padding since some Registrar implementations seem to use
-        * hardcoded 32-octet length for this attribute */
-       while (ssid_len > 0 && ssid[ssid_len - 1] == 0)
-               ssid_len--;
+       pos = attr.oob_dev_password;
 
-       wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len);
-       if (ssid_len <= sizeof(cred->ssid)) {
-               os_memcpy(cred->ssid, ssid, ssid_len);
-               cred->ssid_len = ssid_len;
+       oob_conf->pubkey_hash =
+               wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN);
+       if (oob_conf->pubkey_hash == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+                          "public key hash");
+               return -1;
        }
+       pos += WPS_OOB_PUBKEY_HASH_LEN;
 
-       return 0;
-}
+       wps->oob_dev_pw_id = WPA_GET_BE16(pos);
+       pos += sizeof(wps->oob_dev_pw_id);
 
-
-static int wps_process_cred_auth_type(struct wps_credential *cred,
-                                     const u8 *auth_type)
-{
-       if (auth_type == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
-                          "Authentication Type");
+       oob_conf->dev_password =
+               wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+       if (oob_conf->dev_password == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+                          "device password");
                return -1;
        }
-
-       cred->auth_type = WPA_GET_BE16(auth_type);
-       wpa_printf(MSG_DEBUG, "WPS: Authentication Type: 0x%x",
-                  cred->auth_type);
+       wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password,
+                                  wpabuf_size(oob_conf->dev_password)),
+                                  wpabuf_size(oob_conf->dev_password), pos,
+                                  WPS_OOB_DEVICE_PASSWORD_LEN);
 
        return 0;
 }
 
 
-static int wps_process_cred_encr_type(struct wps_credential *cred,
-                                     const u8 *encr_type)
+static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
 {
-       if (encr_type == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
-                          "Encryption Type");
+       struct wpabuf msg;
+       struct wps_parse_attr attr;
+       size_t i;
+
+       if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
+               wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
                return -1;
        }
 
-       cred->encr_type = WPA_GET_BE16(encr_type);
-       wpa_printf(MSG_DEBUG, "WPS: Encryption Type: 0x%x",
-                  cred->encr_type);
+       for (i = 0; i < attr.num_cred; i++) {
+               struct wps_credential local_cred;
+               struct wps_parse_attr cattr;
+
+               os_memset(&local_cred, 0, sizeof(local_cred));
+               wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]);
+               if (wps_parse_msg(&msg, &cattr) < 0 ||
+                   wps_process_cred(&cattr, &local_cred)) {
+                       wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
+                                  "credential");
+                       return -1;
+               }
+               wps->cred_cb(wps->cb_ctx, &local_cred);
+       }
 
        return 0;
 }
 
 
-static int wps_process_cred_network_key_idx(struct wps_credential *cred,
-                                           const u8 *key_idx)
+int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
+                   int registrar)
 {
-       if (key_idx == NULL)
-               return 0; /* optional attribute */
-
-       wpa_printf(MSG_DEBUG, "WPS: Network Key Index: %d", *key_idx);
-       cred->key_idx = *key_idx;
+       struct wpabuf *data;
+       int ret, write_f, oob_method = wps->oob_conf.oob_method;
+       void *oob_priv;
 
-       return 0;
-}
+       write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar;
 
-
-static int wps_process_cred_network_key(struct wps_credential *cred,
-                                       const u8 *key, size_t key_len)
-{
-       if (key == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
-                          "Network Key");
+       oob_priv = oob_dev->init_func(wps, oob_dev, registrar);
+       if (oob_priv == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device");
                return -1;
        }
 
-       wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len);
-       if (key_len <= sizeof(cred->key)) {
-               os_memcpy(cred->key, key, key_len);
-               cred->key_len = key_len;
-       }
-
-       return 0;
-}
+       if (write_f) {
+               if (oob_method == OOB_METHOD_CRED)
+                       data = wps_get_oob_cred(wps);
+               else
+                       data = wps_get_oob_dev_pwd(wps);
 
+               ret = 0;
+               if (data == NULL || oob_dev->write_func(oob_priv, data) < 0)
+                       ret = -1;
+       } else {
+               data = oob_dev->read_func(oob_priv);
+               if (data == NULL)
+                       ret = -1;
+               else {
+                       if (oob_method == OOB_METHOD_CRED)
+                               ret = wps_parse_oob_cred(wps, data);
+                       else
+                               ret = wps_parse_oob_dev_pwd(wps, data);
+               }
+       }
+       wpabuf_free(data);
+       oob_dev->deinit_func(oob_priv);
 
-static int wps_process_cred_mac_addr(struct wps_credential *cred,
-                                    const u8 *mac_addr)
-{
-       if (mac_addr == NULL) {
-               wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
-                          "MAC Address");
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data");
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(mac_addr));
-       os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN);
-
        return 0;
 }
 
 
-static int wps_process_cred_eap_type(struct wps_credential *cred,
-                                    const u8 *eap_type, size_t eap_type_len)
+struct oob_device_data * wps_get_oob_device(char *device_type)
 {
-       if (eap_type == NULL)
-               return 0; /* optional attribute */
+#ifdef CONFIG_WPS_UFD
+       if (os_strstr(device_type, "ufd") != NULL)
+               return &oob_ufd_device_data;
+#endif /* CONFIG_WPS_UFD */
+#ifdef CONFIG_WPS_NFC
+       if (os_strstr(device_type, "nfc") != NULL)
+               return &oob_nfc_device_data;
+#endif /* CONFIG_WPS_NFC */
 
-       wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
-
-       return 0;
+       return NULL;
 }
 
 
-static int wps_process_cred_eap_identity(struct wps_credential *cred,
-                                        const u8 *identity,
-                                        size_t identity_len)
+#ifdef CONFIG_WPS_NFC
+struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name)
 {
-       if (identity == NULL)
-               return 0; /* optional attribute */
-
-       wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
-                         identity, identity_len);
+       if (device_name == NULL)
+               return NULL;
+#ifdef CONFIG_WPS_NFC_PN531
+       if (os_strstr(device_name, "pn531") != NULL)
+               return &oob_nfc_pn531_device_data;
+#endif /* CONFIG_WPS_NFC_PN531 */
 
-       return 0;
+       return NULL;
 }
+#endif /* CONFIG_WPS_NFC */
 
 
-static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
-                                         const u8 *key_prov_auto)
+int wps_get_oob_method(char *method)
 {
-       if (key_prov_auto == NULL)
-               return 0; /* optional attribute */
-
-       wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
-                  *key_prov_auto);
-
-       return 0;
+       if (os_strstr(method, "pin-e") != NULL)
+               return OOB_METHOD_DEV_PWD_E;
+       if (os_strstr(method, "pin-r") != NULL)
+               return OOB_METHOD_DEV_PWD_R;
+       if (os_strstr(method, "cred") != NULL)
+               return OOB_METHOD_CRED;
+       return OOB_METHOD_UNKNOWN;
 }
 
+#endif /* CONFIG_WPS_OOB */
 
-static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
-                                          const u8 *dot1x_enabled)
+
+int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN])
 {
-       if (dot1x_enabled == NULL)
-               return 0; /* optional attribute */
+       const char *pos;
+
+       /* <categ>-<OUI>-<subcateg> */
+       WPA_PUT_BE16(dev_type, atoi(str));
+       pos = os_strchr(str, '-');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       if (hexstr2bin(pos, &dev_type[2], 4))
+               return -1;
+       pos = os_strchr(pos, '-');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       WPA_PUT_BE16(&dev_type[6], atoi(pos));
 
-       wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
 
        return 0;
 }
 
 
-int wps_process_cred(struct wps_parse_attr *attr,
-                    struct wps_credential *cred)
+char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
+                           size_t buf_len)
 {
-       wpa_printf(MSG_DEBUG, "WPS: Process Credential");
-
-       /* TODO: support multiple Network Keys */
-       if (wps_process_cred_network_idx(cred, attr->network_idx) ||
-           wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
-           wps_process_cred_auth_type(cred, attr->auth_type) ||
-           wps_process_cred_encr_type(cred, attr->encr_type) ||
-           wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
-           wps_process_cred_network_key(cred, attr->network_key,
-                                        attr->network_key_len) ||
-           wps_process_cred_mac_addr(cred, attr->mac_addr) ||
-           wps_process_cred_eap_type(cred, attr->eap_type,
-                                     attr->eap_type_len) ||
-           wps_process_cred_eap_identity(cred, attr->eap_identity,
-                                         attr->eap_identity_len) ||
-           wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
-           wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled))
-               return -1;
+       int ret;
 
-       return 0;
+       ret = os_snprintf(buf, buf_len, "%u-%08X-%u",
+                         WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
+                         WPA_GET_BE16(&dev_type[6]));
+       if (ret < 0 || (unsigned int) ret >= buf_len)
+               return NULL;
+
+       return buf;
 }
 
 
-int wps_process_ap_settings(struct wps_parse_attr *attr,
-                           struct wps_credential *cred)
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid)
 {
-       wpa_printf(MSG_DEBUG, "WPS: Processing AP Settings");
-       os_memset(cred, 0, sizeof(*cred));
-       /* TODO: optional attributes New Password and Device Password ID */
-       if (wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
-           wps_process_cred_auth_type(cred, attr->auth_type) ||
-           wps_process_cred_encr_type(cred, attr->encr_type) ||
-           wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
-           wps_process_cred_network_key(cred, attr->network_key,
-                                        attr->network_key_len) ||
-           wps_process_cred_mac_addr(cred, attr->mac_addr))
-               return -1;
+       const u8 *addr[2];
+       size_t len[2];
+       u8 hash[SHA1_MAC_LEN];
+       u8 nsid[16] = {
+               0x52, 0x64, 0x80, 0xf8,
+               0xc9, 0x9b,
+               0x4b, 0xe5,
+               0xa6, 0x55,
+               0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84
+       };
+
+       addr[0] = nsid;
+       len[0] = sizeof(nsid);
+       addr[1] = mac_addr;
+       len[1] = 6;
+       sha1_vector(2, addr, len, hash);
+       os_memcpy(uuid, hash, 16);
+
+       /* Version: 5 = named-based version using SHA-1 */
+       uuid[6] = (5 << 4) | (uuid[6] & 0x0f);
+
+       /* Variant specified in RFC 4122 */
+       uuid[8] = 0x80 | (uuid[8] & 0x3f);
+}
+
+
+u16 wps_config_methods_str2bin(const char *str)
+{
+       u16 methods = 0;
+
+       if (str == NULL) {
+               /* Default to enabling methods based on build configuration */
+               methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+               methods |= WPS_CONFIG_VIRT_DISPLAY;
+#endif /* CONFIG_WPS2 */
+#ifdef CONFIG_WPS_UFD
+               methods |= WPS_CONFIG_USBA;
+#endif /* CONFIG_WPS_UFD */
+#ifdef CONFIG_WPS_NFC
+               methods |= WPS_CONFIG_NFC_INTERFACE;
+#endif /* CONFIG_WPS_NFC */
+       } else {
+               if (os_strstr(str, "usba"))
+                       methods |= WPS_CONFIG_USBA;
+               if (os_strstr(str, "ethernet"))
+                       methods |= WPS_CONFIG_ETHERNET;
+               if (os_strstr(str, "label"))
+                       methods |= WPS_CONFIG_LABEL;
+               if (os_strstr(str, "display"))
+                       methods |= WPS_CONFIG_DISPLAY;
+               if (os_strstr(str, "ext_nfc_token"))
+                       methods |= WPS_CONFIG_EXT_NFC_TOKEN;
+               if (os_strstr(str, "int_nfc_token"))
+                       methods |= WPS_CONFIG_INT_NFC_TOKEN;
+               if (os_strstr(str, "nfc_interface"))
+                       methods |= WPS_CONFIG_NFC_INTERFACE;
+               if (os_strstr(str, "push_button"))
+                       methods |= WPS_CONFIG_PUSHBUTTON;
+               if (os_strstr(str, "keypad"))
+                       methods |= WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS2
+               if (os_strstr(str, "virtual_display"))
+                       methods |= WPS_CONFIG_VIRT_DISPLAY;
+               if (os_strstr(str, "physical_display"))
+                       methods |= WPS_CONFIG_PHY_DISPLAY;
+               if (os_strstr(str, "virtual_push_button"))
+                       methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
+               if (os_strstr(str, "physical_push_button"))
+                       methods |= WPS_CONFIG_PHY_PUSHBUTTON;
+#endif /* CONFIG_WPS2 */
+       }
 
-       return 0;
+       return methods;
 }