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

2

15

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.

Beore answered 21/4, 2016 at 1:54 Comment(0)
B
10

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.

Beore answered 3/8, 2016 at 11:53 Comment(1)
Thanks for the answer and your code. It's not nearly as hacky as I'd have expected. :DTakakotakakura
T
7

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.

Takakotakakura answered 2/8, 2016 at 19:29 Comment(2)
First of all, thank you for tracking down wps_supplicant.c. Second, while I haven't updated the original question in some time, I was able to create the Wifi record! I just wasn't very happy with the solution (and not overly proud as it is fairly hacky). I indeed acquired a copy of the "WiFi Simple Configuration Technical Specification" (version 2.0.0) and was able to follow the specification - the most important part was the Handover Record which I didn't even think of. I'll post my code as an answer.Beore
@Henry I had quite some trouble reading and understanding the specification so far. Since I'm currently trying to do this as well, I'm looking forward to see how you did it!Takakotakakura

© 2022 - 2024 — McMap. All rights reserved.