Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an NDEF WiFi record using application/vnd.wfa.wsc in Android

As of Android 5.0.0 you can long tap on a WiFi connection and write that connection to a tag ("Write to NFC tag"). You can find the source for that operation here: WriteWifiConfigToNfcDialog.java. The relevant line that takes a WiFi connection and creates an NDEF payload appears to be here:

String wpsNfcConfigurationToken = mWifiManager.getWpsNfcConfigurationToken(mAccessPoint.networkId);

mWifiManager is an instance of WifiManager, however getWpsNfcConfigurationToken is not part of the API. By tracking down this method, we can find its commit here: Add calls for NFC WSC token creation which is unfortunately no help. This is where my investigation has run out. Edit: I've found out the following call stack:

WifiServiceImpl.java calls mWifiStateMachine.syncGetWpsNfcConfigurationToken(netId);

WifiStateMachine.java calls mWifiNative.getNfcWpsConfigurationToken(netId);

WifiNative.java finally has the method

public String getNfcWpsConfigurationToken(int netId) { return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId); }

which then calls

String result = doStringCommandNative(mInterfacePrefix + command);

where doStringCommandNative makes a system call (can't find the code for this anywhere).

Which is now where the investigation ends.

Hoping someone can step in and show me a method that creates an NdefRecord that is of the type application/vnd.wfa.wsc given an SSID, Password, Encryption/Auth type.

I've of course inspected the bytes of an actual application/vnd.wfa.wsc record created by Android but manually recreating this process with the bytes seems potentially very unreliable and is incredibly tedious.

like image 379
henrycjc Avatar asked Apr 21 '16 01:04

henrycjc


2 Answers

The answer lies in the Wi-Fi Alliance "Wi-Fi Simple Configuration Technical Specification v2.0.5" (available for download here). Android makes use of this standard format for configuring WiFi networks, I wrongly assumed it was proprietary.

Firstly, I created an NFC helper class (aptly named NFCHelper.java) which has all the byte constants needed to construct the record. Then, I created a hacky method for creating one of the two records required. The spec is actually fairly useless here, what I did was examined a number of payloads of tags that had been successfully configured via the Android OS. Finally, you need to have a mechanism to prepend a "Handover Select Record (NFC WKT Hs)" (see page 90 of WiFi spec). I believe this record "tells" Android to register the network in the following token.

How to create the handover record:

ndefRecords = new NdefRecord[2];
byte[] version = new byte[] { (0x1 << 4) | (0x2)};
ndefRecords[0] = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_HANDOVER_REQUEST, new byte[0], version);
// and then obviously add the record you create with the method below.

Method for creating the configuration token:

private NdefRecord createWifiRecord(String[] data) {
    String ssid = data[0];
    String password = data[1];
    String auth = data[2];
    String crypt = data[3];
    byte[] authByte = getAuthBytes(auth);
    byte[] cryptByte = getCryptBytes(crypt);
    byte[] ssidByte = ssid.getBytes();
    byte[] passwordByte = password.getBytes();
    byte[] ssidLength = {(byte)((int)Math.floor(ssid.length()/256)), (byte)(ssid.length()%256)};
    byte[] passwordLength = {(byte)((int)Math.floor(password.length()/256)), (byte)(password.length()%256)};
    byte[] cred = {0x00, 0x36};
    byte[] idx = {0x00, 0x01, 0x01};
    byte[] mac = {0x00, 0x06};
    byte[] keypad = {0x00, 0x0B};

    byte[] payload = concat(NFCHelper.CREDENTIAL, cred,
            NFCHelper.NETWORK_IDX, idx,
            NFCHelper.NETWORK_NAME, ssidLength, ssidByte,
            NFCHelper.AUTH_TYPE, NFCHelper.AUTH_WPA_PERSONAL, authByte,
            NFCHelper.CRYPT_TYPE, NFCHelper.CRYPT_WEP, NFCHelper.CRYPT_AES_TKIP,
            NFCHelper.NETWORK_KEY, passwordLength, passwordByte);
           // NFCHelper.MAC_ADDRESS, mac);
    return NdefRecord.createMime(NFC_TOKEN_MIME_TYPE, payload);
} 

License and gist here. You can find an implementation of the concat method anywhere on the net, or just write your own.

Note: this is a fairly hacky implementation (as you may notice). I am including the AES and AES/TKIP bytes as I found in testing it worked for a variety of networks using different encryption/auth methods under Android 5.* Please feel free to change the function prototype, the String array just worked nicely with what I was doing.

Using the two records created in the first snippet above, you should then pass that into an NdefMessage and write it to your tag.

One day soon I'm going to do a write up and a far better/robust soln with graphics and stuff too, so I'll update this answer then.

like image 101
henrycjc Avatar answered Nov 20 '22 14:11

henrycjc


The call of doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId) in the end is handled by the wpa_supplicant module. This feature is described here. I think the actual implementation of this can be found in wps_supplicant.c.

What you are actually trying to do isn't something Android specific actually. It's defined in the "WiFi Simple Configuration Technical Specification", which you can download by filling this form. The relevant part should be 10.1.2 Configuration Token.

NfcUtils.java has a working implementation for this! There are a few FIXMEs and TODOs, but in total it works and should give you a pretty good idea of what you need to do.

In case you want to parse such NdefRecords yourself and do something with the SSID and key, NfcWifiProtectedSetup.java shows how to do that.

like image 7
noone Avatar answered Nov 20 '22 13:11

noone