• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.android.nfc;
17 
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.net.wifi.WifiConfiguration;
22 import android.nfc.NdefMessage;
23 import android.nfc.NdefRecord;
24 import android.nfc.tech.Ndef;
25 import android.os.UserHandle;
26 import android.os.UserManager;
27 import android.util.Log;
28 
29 import java.nio.BufferUnderflowException;
30 import java.nio.ByteBuffer;
31 import java.util.Arrays;
32 import java.util.BitSet;
33 
34 public final class NfcWifiProtectedSetup {
35 
36     public static final String NFC_TOKEN_MIME_TYPE = "application/vnd.wfa.wsc";
37 
38     public static final String EXTRA_WIFI_CONFIG = "com.android.nfc.WIFI_CONFIG_EXTRA";
39 
40     /*
41      * ID into configuration record for SSID and Network Key in hex.
42      * Obtained from WFA Wifi Simple Configuration Technical Specification v2.0.2.1.
43      */
44     private static final short CREDENTIAL_FIELD_ID = 0x100E;
45     private static final short SSID_FIELD_ID = 0x1045;
46     private static final short NETWORK_KEY_FIELD_ID = 0x1027;
47     private static final short AUTH_TYPE_FIELD_ID = 0x1003;
48 
49     private static final short AUTH_TYPE_EXPECTED_SIZE = 2;
50 
51     private static final short AUTH_TYPE_OPEN = 0;
52     private static final short AUTH_TYPE_WPA_PSK = 0x0002;
53     private static final short AUTH_TYPE_WPA_EAP =  0x0008;
54     private static final short AUTH_TYPE_WPA2_EAP = 0x0010;
55     private static final short AUTH_TYPE_WPA2_PSK = 0x0020;
56 
57     private static final int MAX_NETWORK_KEY_SIZE_BYTES = 64;
58 
NfcWifiProtectedSetup()59     private NfcWifiProtectedSetup() {}
60 
tryNfcWifiSetup(Ndef ndef, Context context)61     public static boolean tryNfcWifiSetup(Ndef ndef, Context context) {
62 
63         if (ndef == null || context == null) {
64             return false;
65         }
66 
67         NdefMessage cachedNdefMessage = ndef.getCachedNdefMessage();
68         if (cachedNdefMessage == null) {
69             return false;
70         }
71 
72         final WifiConfiguration wifiConfiguration;
73         try {
74             wifiConfiguration = parse(cachedNdefMessage);
75         } catch (BufferUnderflowException e) {
76             // malformed payload
77             return false;
78         }
79 
80         if (wifiConfiguration != null &&!UserManager.get(context).hasUserRestriction(
81                 UserManager.DISALLOW_CONFIG_WIFI, UserHandle.CURRENT)) {
82             Intent configureNetworkIntent = new Intent()
83                     .putExtra(EXTRA_WIFI_CONFIG, wifiConfiguration)
84                     .setClass(context, ConfirmConnectToWifiNetworkActivity.class)
85                     .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
86 
87             context.startActivityAsUser(configureNetworkIntent, UserHandle.CURRENT);
88             return true;
89         }
90 
91         return false;
92     }
93 
parse(NdefMessage message)94     private static WifiConfiguration parse(NdefMessage message) {
95         NdefRecord[] records = message.getRecords();
96 
97         for (NdefRecord record : records) {
98             if (new String(record.getType()).equals(NFC_TOKEN_MIME_TYPE)) {
99                 ByteBuffer payload = ByteBuffer.wrap(record.getPayload());
100                 while (payload.hasRemaining()) {
101                     short fieldId = payload.getShort();
102                     short fieldSize = payload.getShort();
103                     if (fieldId == CREDENTIAL_FIELD_ID) {
104                         return parseCredential(payload, fieldSize);
105                     }
106                 }
107             }
108         }
109         return null;
110     }
111 
parseCredential(ByteBuffer payload, short size)112     private static WifiConfiguration parseCredential(ByteBuffer payload, short size) {
113         int startPosition = payload.position();
114         WifiConfiguration result = new WifiConfiguration();
115         while (payload.position() < startPosition + size) {
116             short fieldId = payload.getShort();
117             short fieldSize = payload.getShort();
118 
119             // sanity check
120             if (payload.position() + fieldSize > startPosition + size) {
121                 return null;
122             }
123 
124             switch (fieldId) {
125                 case SSID_FIELD_ID:
126                     byte[] ssid = new byte[fieldSize];
127                     payload.get(ssid);
128                     result.SSID = "\"" + new String(ssid) + "\"";
129                     break;
130                 case NETWORK_KEY_FIELD_ID:
131                     if (fieldSize > MAX_NETWORK_KEY_SIZE_BYTES) {
132                         return null;
133                     }
134                     byte[] networkKey = new byte[fieldSize];
135                     payload.get(networkKey);
136                     result.preSharedKey = "\"" + new String(networkKey) + "\"";
137                     break;
138                 case AUTH_TYPE_FIELD_ID:
139                     if (fieldSize != AUTH_TYPE_EXPECTED_SIZE) {
140                         // corrupt data
141                         return null;
142                     }
143 
144                     short authType = payload.getShort();
145                     populateAllowedKeyManagement(result.allowedKeyManagement, authType);
146                     break;
147                 default:
148                     // unknown / unparsed tag
149                     payload.position(payload.position() + fieldSize);
150                     break;
151             }
152         }
153 
154         if (result.preSharedKey != null && result.SSID != null) {
155             return result;
156         }
157 
158         return null;
159     }
160 
populateAllowedKeyManagement(BitSet allowedKeyManagement, short authType)161     private static void populateAllowedKeyManagement(BitSet allowedKeyManagement, short authType) {
162         if (authType == AUTH_TYPE_WPA_PSK || authType == AUTH_TYPE_WPA2_PSK) {
163             allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
164         } else if (authType == AUTH_TYPE_WPA_EAP || authType == AUTH_TYPE_WPA2_EAP) {
165             allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
166         } else if (authType == AUTH_TYPE_OPEN) {
167             allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
168         }
169     }
170 }
171