1/*
2 * Copyright (C) 2008 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
17package com.android.server.wifi;
18
19import android.os.Parcelable;
20import android.os.Parcel;
21
22import java.util.BitSet;
23
24import java.nio.ByteBuffer;
25
26import java.util.Date;
27
28/**
29 * Describes information about a detected access point. In addition
30 * to the attributes described here, the supplicant keeps track of
31 * {@code quality}, {@code noise}, and {@code maxbitrate} attributes,
32 * but does not currently report them to external clients.
33 */
34public class WifiParser {
35
36    public WifiParser() {}
37
38
39    /*
40     * {@hide}
41     */
42    class IE {
43        int id;
44        byte data[];
45    }
46
47    private static final int VENDOR_SPECIFIC_IE = 221;
48    private static final int IEEE_RSN_IE = 48; //IEEE 2012 8.4.2.27
49
50
51    private static final int WPA_IE_VENDOR_TYPE = 0x0050f201; //WFA WPA vendor IE OUI/type
52
53    /*
54     * parse beacon or probe response frame and build the capabilities
55     * {@hide}
56     *
57     * This function is called so as to build the capabilities string of the scan result, hence it is called
58     * by AutoJoin controller when handling scan results that are coming from WifiScanner.
59     *
60     * It parses the ieee beacon's capability field, WPA and RSNE IE as per spec, but build the
61     * ScanResult.capabilities String in a way that mirror the values returned by wpa_supplicant.
62     *
63     * Once the capabilities string is build, the ScanResult can be used be the system as if it was coming from supplicant.
64     */
65     /* @hide
66     * */
67    static public String parse_akm(IE full_IE[], BitSet ieee_cap) {
68        boolean privacy = false;
69        boolean error = false;
70        if (ieee_cap == null)
71            return null;
72
73        if (full_IE == null)
74            return null;
75
76        privacy = ieee_cap.get(4);
77
78        String capabilities = "";
79        boolean rsne_found = false;
80        boolean wpa_found = false;
81
82        for (IE ie : full_IE) {
83            String security = "";
84            if (ie.id == IEEE_RSN_IE) {
85                rsne_found = true;
86                //parsing WPA2 capabilities
87
88                ByteBuffer buf = ByteBuffer.wrap(ie.data);
89
90                int total_len = ie.data.length;
91                int offset = 2;
92
93                //version
94                if ((total_len - offset) < 2) {
95                    //not enough space for version field
96                    security = "";
97                    error = true;
98                    break;
99                }
100                int val = 0;
101                if (0x0100 != buf.getShort(offset)) {
102                    //incorrect version
103                    security = "";
104                    error = true;
105                    break;
106                }
107                offset += 2;
108
109
110                //group cipher
111                if ((total_len - offset) < 4) {
112                    security = ""; //parse error on group cipher suite
113                    error = true;
114                    break;
115                }
116                offset += 4; //skip the group cipher
117
118                security = "[WPA2"; //found the RSNE IE, hence start building the capability string
119
120                //pairwise cipher
121                if ((total_len - offset) < 2) {
122                    security = ""; //parse error no pairwise cipher
123                    error = true;
124                    break;
125                }
126                val = buf.getShort(offset);
127                if ((total_len - offset) < (2 + val * 4)) {
128                    security = ""; //parse error no pairwise cipher
129                    error = true;
130                    break;
131                }
132                offset += 2 + val * 4; //skip the pairwise ciphers
133
134                //AKM
135                if ((total_len - offset) < 2) {
136                    security = ""; //parse error no AKM
137                    error = true;
138                    break;
139                }
140                val = buf.getShort(offset);
141                if ((total_len - offset) < (2 + val * 4)) {
142                    security = ""; //parse error no pairwise cipher
143                    error = true;
144                    break;
145                }
146                offset += 2;
147                if (val == 0) {
148                    security += "-EAP"; //default AKM
149                }
150                for (int i = 0; i < val; i++) {
151                    int akm = buf.getInt(offset);
152                    boolean found = false;
153                    switch (akm) {
154                        case 0x01ac0f00:
155                            security += found ? "+" : "-" + "EAP";
156                            found = true;
157                            break;
158                        case 0x02ac0f00:
159                            security += found ? "+" : "-" + "PSK"; //PSK as 802.11-2012 11.6.1.2
160                            found = true;
161                            break;
162                        case 0x03ac0f00:
163                            security += found ? "+" : "-" + "FT/EAP";
164                            found = true;
165                            break;
166                        case 0x04ac0f00:
167                            security += found ? "+" : "-" + "FT/PSK";
168                            found = true;
169                            break;
170                        case 0x06ac0f00:
171                            security += found ? "+" : "-" + "PSK-SHA256";
172                            found = true;
173                            break;
174                        case 0x05ac0f00:
175                            security += found ? "+" : "-" + "EAP-SHA256";
176                            found = true;
177                            break;
178                    }
179                    offset += 4;
180                }
181                //we parsed what we want at this point
182                security += "]";
183                capabilities += security;
184
185            }
186
187            if (ie.id == VENDOR_SPECIFIC_IE) {
188                int total_len = ie.data.length;
189                int offset = 2;
190
191                //version
192                if ((total_len - offset) < 4) {
193                    //not enough space for OUI and type field
194                    security = "";
195                    error = true;
196                    break;
197                }
198
199                ByteBuffer buf = ByteBuffer.wrap(ie.data);
200
201                if (buf.getInt(offset) != 0x01F25000) {
202                    //look for HS2.0 and WPA IE
203                    security = "";
204                    continue;
205                }
206
207                security = "[WPA"; //prep the string for WPA
208
209                //version
210                if ((total_len - offset) < 2) {
211                    //not enough space for version field
212                    security = "";
213                    error = true;
214                    break;
215                }
216                int val = 0;
217                if (0x0100 != buf.getShort(offset)) {
218                    //incorrect version
219                    security = "";
220                    error = true;
221                    break;
222                }
223                offset += 2;
224
225
226                //group cipher
227                if ((total_len - offset) < 4) {
228                    security = ""; //parse error on group cipher suite
229                    error = true;
230                    break;
231                }
232                offset += 4; //skip the group cipher
233
234
235                //pairwise cipher
236                if ((total_len - offset) < 2) {
237                    security = ""; //parse error no pairwise cipher
238                    error = true;
239                    break;
240                }
241                val = buf.getShort(offset);
242                if ((total_len - offset) < (2 + val * 4)) {
243                    security = ""; //parse error no pairwise cipher
244                    error = true;
245                    break;
246                }
247                offset += 2 + val * 4; //skip the pairwise ciphers
248
249                //AKM
250                if ((total_len - offset) < 2) {
251                    security = ""; //parse error no AKM
252                    error = true;
253                    break;
254                }
255                val = buf.getShort(offset);
256                if ((total_len - offset) < (2 + val * 4)) {
257                    security = ""; //parse error no pairwise cipher
258                    error = true;
259                    break;
260                }
261                offset += 2;
262                if (val == 0) {
263                    security += "-EAP"; //default AKM
264                }
265                for (int i = 0; i < val; i++) {
266                    int akm = buf.getInt(offset);
267                    boolean found = false;
268                    switch (akm) {
269                        case 0x01f25000:
270                            security += found ? "+" : "-" + "EAP";
271                            found = true;
272                            break;
273                        case 0x02f25000:
274                            security += found ? "+" : "-" + "PSK"; //PSK as 802.11-2012 11.6.1.2
275                            found = true;
276                            break;
277
278                    }
279                    offset += 4;
280                }
281                //we parsed what we want at this point
282                security += "]";
283            }
284        }
285
286        if (rsne_found == false && wpa_found == false && privacy) {
287            //private Beacon without an RSNE or WPA IE, hence WEP0
288            capabilities += "[WEP]";
289        }
290
291        if (error)
292            return null;
293        else
294            return capabilities;
295    }
296
297
298}
299