InformationElementUtilTest.java revision 9b5773d2805e8c6141ca75de272921a84941546b
1/* 2 * Copyright (C) 2016 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.util; 18 19import static org.junit.Assert.assertArrayEquals; 20import static org.junit.Assert.assertEquals; 21 22import android.net.wifi.ScanResult.InformationElement; 23import android.test.suitebuilder.annotation.SmallTest; 24 25import org.junit.Test; 26 27import java.io.ByteArrayOutputStream; 28import java.io.IOException; 29import java.util.Arrays; 30import java.util.BitSet; 31 32/** 33 * Unit tests for {@link com.android.server.wifi.util.InformationElementUtil}. 34 */ 35@SmallTest 36public class InformationElementUtilTest { 37 38 // SSID Information Element tags 39 private static final byte[] TEST_SSID_BYTES_TAG = new byte[] { (byte) 0x00, (byte) 0x0B }; 40 // SSID Information Element entry used for testing. 41 private static final byte[] TEST_SSID_BYTES = "GoogleGuest".getBytes(); 42 // Valid zero length tag. 43 private static final byte[] TEST_VALID_ZERO_LENGTH_TAG = 44 new byte[] { (byte) 0x0B, (byte) 0x00 }; 45 // BSS_LOAD Information Element entry used for testing. 46 private static final byte[] TEST_BSS_LOAD_BYTES_IE = 47 new byte[] { (byte) 0x0B, (byte) 0x01, (byte) 0x08 }; 48 49 /* 50 * Function to provide SSID Information Element (SSID = "GoogleGuest"). 51 * 52 * @return byte[] Byte array representing the test SSID 53 */ 54 private byte[] getTestSsidIEBytes() throws IOException { 55 return concatenateByteArrays(TEST_SSID_BYTES_TAG, TEST_SSID_BYTES); 56 } 57 58 /* 59 * Function used to set byte arrays used for testing. 60 * 61 * @param byteArrays variable number of byte arrays to concatenate 62 * @return byte[] Byte array resulting from concatenating the arrays passed to the function 63 */ 64 private static byte[] concatenateByteArrays(byte[]... byteArrays) throws IOException { 65 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 66 for (byte[] b : byteArrays) { 67 baos.write(b); 68 } 69 baos.flush(); 70 return baos.toByteArray(); 71 } 72 73 /** 74 * Test parseInformationElements with an empty byte array. 75 * Expect parseInformationElement to return an empty InformationElement array. 76 */ 77 @Test 78 public void parseInformationElements_withEmptyByteArray() { 79 byte[] emptyBytes = new byte[0]; 80 InformationElement[] results = 81 InformationElementUtil.parseInformationElements(emptyBytes); 82 assertEquals("parsed results should be empty", 0, results.length); 83 } 84 85 /** 86 * Test parseInformationElements called with a null parameter. 87 * Expect parseInfomrationElement to return an empty InformationElement array. 88 */ 89 @Test 90 public void parseInformationElements_withNullBytes() { 91 byte[] nullBytes = null; 92 InformationElement[] results = 93 InformationElementUtil.parseInformationElements(nullBytes); 94 assertEquals("parsed results should be empty", 0, results.length); 95 } 96 97 /* 98 * Test parseInformationElements with a single element represented in the byte array. 99 * Expect a single element to be returned in the InformationElements array. The 100 * length of this array should be 1 and the contents should be valid. 101 * 102 * @throws java.io.IOException 103 */ 104 @Test 105 public void parseInformationElements_withSingleElement() throws IOException { 106 byte[] ssidBytes = getTestSsidIEBytes(); 107 108 InformationElement[] results = 109 InformationElementUtil.parseInformationElements(ssidBytes); 110 assertEquals("Parsed results should have 1 IE", 1, results.length); 111 assertEquals("Parsed result should be a ssid", InformationElement.EID_SSID, results[0].id); 112 assertArrayEquals("parsed SSID does not match input", 113 TEST_SSID_BYTES, results[0].bytes); 114 } 115 116 /* 117 * Test parseInformationElement with extra padding in the data to parse. 118 * Expect the function to return the SSID information element. 119 * 120 * Note: Experience shows that APs often pad messages with 0x00. This happens to be the tag for 121 * EID_SSID. This test checks if padding will be properly discarded. 122 * 123 * @throws java.io.IOException 124 */ 125 @Test 126 public void parseInformationElements_withExtraPadding() throws IOException { 127 byte[] paddingBytes = new byte[10]; 128 Arrays.fill(paddingBytes, (byte) 0x00); 129 byte[] ssidBytesWithPadding = concatenateByteArrays(getTestSsidIEBytes(), paddingBytes); 130 131 InformationElement[] results = 132 InformationElementUtil.parseInformationElements(ssidBytesWithPadding); 133 assertEquals("Parsed results should have 1 IE", 1, results.length); 134 assertEquals("Parsed result should be a ssid", InformationElement.EID_SSID, results[0].id); 135 assertArrayEquals("parsed SSID does not match input", 136 TEST_SSID_BYTES, results[0].bytes); 137 } 138 139 /* 140 * Test parseInformationElement with two elements where the second element has an invalid 141 * length. 142 * Expect the function to return the first valid entry and skip the remaining information. 143 * 144 * Note: This test partially exposes issues with blindly parsing the data. A higher level 145 * function to validate the parsed data may be added. 146 * 147 * @throws java.io.IOException 148 * */ 149 @Test 150 public void parseInformationElements_secondElementInvalidLength() throws IOException { 151 byte[] invalidTag = new byte[] { (byte) 0x01, (byte) 0x08, (byte) 0x08 }; 152 byte[] twoTagsSecondInvalidBytes = concatenateByteArrays(getTestSsidIEBytes(), invalidTag); 153 154 InformationElement[] results = 155 InformationElementUtil.parseInformationElements(twoTagsSecondInvalidBytes); 156 assertEquals("Parsed results should have 1 IE", 1, results.length); 157 assertEquals("Parsed result should be a ssid.", InformationElement.EID_SSID, results[0].id); 158 assertArrayEquals("parsed SSID does not match input", 159 TEST_SSID_BYTES, results[0].bytes); 160 } 161 162 /* 163 * Test parseInformationElements with two valid Information Element entries. 164 * Expect the function to return an InformationElement array with two entries containing valid 165 * data. 166 * 167 * @throws java.io.IOException 168 */ 169 @Test 170 public void parseInformationElements_twoElements() throws IOException { 171 byte[] twoValidTagsBytes = 172 concatenateByteArrays(getTestSsidIEBytes(), TEST_BSS_LOAD_BYTES_IE); 173 174 InformationElement[] results = 175 InformationElementUtil.parseInformationElements(twoValidTagsBytes); 176 assertEquals("parsed results should have 2 elements", 2, results.length); 177 assertEquals("First parsed element should be a ssid", 178 InformationElement.EID_SSID, results[0].id); 179 assertArrayEquals("parsed SSID does not match input", 180 TEST_SSID_BYTES, results[0].bytes); 181 assertEquals("second element should be a BSS_LOAD tag", 182 InformationElement.EID_BSS_LOAD, results[1].id); 183 assertEquals("second element should have data of length 1", 1, results[1].bytes.length); 184 assertEquals("second element data was not parsed correctly.", 185 (byte) 0x08, results[1].bytes[0]); 186 } 187 188 /* 189 * Test parseInformationElements with two elements where the first information element has a 190 * length of zero. 191 * Expect the function to return an InformationElement array with two entries containing valid 192 * data. 193 * 194 * @throws java.io.IOException 195 */ 196 @Test 197 public void parseInformationElements_firstElementZeroLength() throws IOException { 198 byte[] zeroLengthTagWithSSIDBytes = 199 concatenateByteArrays(TEST_VALID_ZERO_LENGTH_TAG, getTestSsidIEBytes()); 200 201 InformationElement[] results = 202 InformationElementUtil.parseInformationElements(zeroLengthTagWithSSIDBytes); 203 assertEquals("Parsed results should have 2 elements.", 2, results.length); 204 assertEquals("First element tag should be EID_BSS_LOAD", 205 InformationElement.EID_BSS_LOAD, results[0].id); 206 assertEquals("First element should be length 0", 0, results[0].bytes.length); 207 208 assertEquals("Second element should be a ssid", InformationElement.EID_SSID, results[1].id); 209 assertArrayEquals("parsed SSID does not match input", 210 TEST_SSID_BYTES, results[1].bytes); 211 } 212 213 /* 214 * Test parseInformationElements with two elements where the first element has an invalid 215 * length. The invalid length in the first element causes us to miss the start of the second 216 * Infomation Element. This results in a single element in the returned array. 217 * Expect the function to return a single entry in an InformationElement array. This returned 218 * entry is not validated at this time and does not contain valid data (since the incorrect 219 * length was used). 220 * TODO: attempt to validate the data and recover as much as possible. When the follow-on CL 221 * is in development, this test will be updated to reflect the change. 222 * 223 * @throws java.io.IOException 224 */ 225 @Test 226 public void parseInformationElements_firstElementWrongLength() throws IOException { 227 byte[] invalidLengthTag = new byte[] {(byte) 0x0B, (byte) 0x01 }; 228 byte[] invalidLengthTagWithSSIDBytes = 229 concatenateByteArrays(invalidLengthTag, getTestSsidIEBytes()); 230 231 InformationElement[] results = 232 InformationElementUtil.parseInformationElements(invalidLengthTagWithSSIDBytes); 233 assertEquals("Parsed results should have 1 element", 1, results.length); 234 assertEquals("First result should be a EID_BSS_LOAD tag.", 235 InformationElement.EID_BSS_LOAD, results[0].id); 236 assertEquals("First result should have data of 1 byte", 1, results[0].bytes.length); 237 assertEquals("First result should have data set to 0x00", 238 invalidLengthTagWithSSIDBytes[2], results[0].bytes[0]); 239 } 240 241 /** 242 * Test Capabilities.buildCapabilities() with a RSN IE. 243 * Expect the function to return a string with the proper security information. 244 */ 245 @Test 246 public void buildCapabilities_rsnElement() { 247 InformationElement ie = new InformationElement(); 248 ie.id = InformationElement.EID_RSN; 249 ie.bytes = new byte[] { (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x0F, 250 (byte) 0xAC, (byte) 0x02, (byte) 0x02, (byte) 0x00, 251 (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x04, 252 (byte) 0x00, (byte) 0x0F, (byte) 0xAC, (byte) 0x02, 253 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x0F, 254 (byte) 0xAC, (byte) 0x02, (byte) 0x00, (byte) 0x00 }; 255 256 InformationElement[] ies = new InformationElement[] { ie }; 257 258 BitSet beaconCap = new BitSet(16); 259 beaconCap.set(4); 260 261 String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap); 262 263 assertEquals("[WPA2-PSK]", result); 264 } 265 266 /** 267 * Test Capabilities.buildCapabilities() with a WPA type 1 IE. 268 * Expect the function to return a string with the proper security information. 269 */ 270 @Test 271 public void buildCapabilities_wpa1Element() { 272 InformationElement ie = new InformationElement(); 273 ie.id = InformationElement.EID_VSA; 274 ie.bytes = new byte[] { (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x01, 275 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x50, 276 (byte) 0xF2, (byte) 0x02, (byte) 0x02, (byte) 0x00, 277 (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x04, 278 (byte) 0x00, (byte) 0x50, (byte) 0xF2, (byte) 0x02, 279 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x50, 280 (byte) 0xF2, (byte) 0x02, (byte) 0x00, (byte) 0x00 }; 281 282 InformationElement[] ies = new InformationElement[] { ie }; 283 284 BitSet beaconCap = new BitSet(16); 285 beaconCap.set(4); 286 287 String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap); 288 289 assertEquals("[WPA-PSK]", result); 290 } 291 292 /** 293 * Test Capabilities.buildCapabilities() with a vendor specific element which 294 * is not WPA type 1. Beacon Capability Information field has the Privacy 295 * bit set. 296 * 297 * Expect the function to return a string with the proper security information. 298 */ 299 @Test 300 public void buildCapabilities_nonRsnWpa1Element_privacySet() { 301 InformationElement ie = new InformationElement(); 302 ie.id = InformationElement.EID_VSA; 303 ie.bytes = new byte[] { (byte) 0x00, (byte) 0x04, (byte) 0x0E, (byte) 0x01, 304 (byte) 0x01, (byte) 0x02, (byte) 0x01, (byte) 0x00, 305 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 306 307 InformationElement[] ies = new InformationElement[] { ie }; 308 309 BitSet beaconCap = new BitSet(16); 310 beaconCap.set(4); 311 312 String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap); 313 314 assertEquals("[WEP]", result); 315 } 316 317 /** 318 * Test Capabilities.buildCapabilities() with a vendor specific element which 319 * is not WPA type 1. Beacon Capability Information field doesn't have the 320 * Privacy bit set. 321 * 322 * Expect the function to return an empty string. 323 */ 324 @Test 325 public void buildCapabilities_nonRsnWpa1Element_privacyClear() { 326 InformationElement ie = new InformationElement(); 327 ie.id = InformationElement.EID_VSA; 328 ie.bytes = new byte[] { (byte) 0x00, (byte) 0x04, (byte) 0x0E, (byte) 0x01, 329 (byte) 0x01, (byte) 0x02, (byte) 0x01, (byte) 0x00, 330 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 331 332 InformationElement[] ies = new InformationElement[] { ie }; 333 334 BitSet beaconCap = new BitSet(16); 335 beaconCap.clear(4); 336 337 String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap); 338 339 assertEquals("", result); 340 } 341 342 /** 343 * Test Capabilities.buildCapabilities() with a vendor specific element which 344 * is not WPA type 1. Beacon Capability Information field has the ESS bit set. 345 * 346 * Expect the function to return a string with [ESS] there. 347 */ 348 @Test 349 public void buildCapabilities_nonRsnWpa1Element_essSet() { 350 InformationElement ie = new InformationElement(); 351 ie.id = InformationElement.EID_VSA; 352 ie.bytes = new byte[] { (byte) 0x00, (byte) 0x04, (byte) 0x0E, (byte) 0x01, 353 (byte) 0x01, (byte) 0x02, (byte) 0x01, (byte) 0x00, 354 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 355 356 InformationElement[] ies = new InformationElement[] { ie }; 357 358 BitSet beaconCap = new BitSet(16); 359 beaconCap.set(0); 360 361 String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap); 362 363 assertEquals("[ESS]", result); 364 } 365 366 /** 367 * Test Capabilities.buildCapabilities() with a vendor specific element which 368 * is not WPA type 1. Beacon Capability Information field doesn't have the 369 * ESS bit set. 370 * 371 * Expect the function to return an empty string. 372 */ 373 @Test 374 public void buildCapabilities_nonRsnWpa1Element_essClear() { 375 InformationElement ie = new InformationElement(); 376 ie.id = InformationElement.EID_VSA; 377 ie.bytes = new byte[] { (byte) 0x00, (byte) 0x04, (byte) 0x0E, (byte) 0x01, 378 (byte) 0x01, (byte) 0x02, (byte) 0x01, (byte) 0x00, 379 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; 380 381 InformationElement[] ies = new InformationElement[] { ie }; 382 383 BitSet beaconCap = new BitSet(16); 384 beaconCap.clear(0); 385 386 String result = InformationElementUtil.Capabilities.buildCapabilities(ies, beaconCap); 387 388 assertEquals("", result); 389 } 390 391 /** 392 * Test a that a correctly formed TIM Information Element is decoded into a valid TIM element, 393 * and the values are captured 394 */ 395 @Test 396 public void parseTrafficIndicationMapInformationElementValid() { 397 InformationElement ie = new InformationElement(); 398 ie.id = InformationElement.EID_TIM; 399 ie.bytes = new byte[] { (byte) 0x03, (byte) 0x05, (byte) 0x00, (byte) 0x00}; 400 InformationElementUtil.TrafficIndicationMap trafficIndicationMap = 401 new InformationElementUtil.TrafficIndicationMap(); 402 trafficIndicationMap.from(ie); 403 assertEquals(trafficIndicationMap.mLength, 4); 404 assertEquals(trafficIndicationMap.mDtimCount, 3); 405 assertEquals(trafficIndicationMap.mDtimPeriod, 5); 406 assertEquals(trafficIndicationMap.mBitmapControl, 0); 407 assertEquals(trafficIndicationMap.isValid(), true); 408 } 409 410 /** 411 * Test that a short invalid Information Element is marked as being an invalid TIM element when 412 * parsed as Traffic Indication Map. 413 */ 414 @Test 415 public void parseTrafficIndicationMapInformationElementInvalidTooShort() { 416 InformationElement ie = new InformationElement(); 417 ie.id = InformationElement.EID_TIM; 418 ie.bytes = new byte[] { (byte) 0x01, (byte) 0x07 }; 419 InformationElementUtil.TrafficIndicationMap trafficIndicationMap = 420 new InformationElementUtil.TrafficIndicationMap(); 421 trafficIndicationMap.from(ie); 422 assertEquals(trafficIndicationMap.isValid(), false); 423 } 424 425 /** 426 * Test that a too-large invalid Information Element is marked as an invalid TIM element when 427 * parsed as Traffic Indication Map. 428 */ 429 @Test 430 public void parseTrafficIndicationMapInformationElementInvalidTooLong() { 431 InformationElement ie = new InformationElement(); 432 ie.id = InformationElement.EID_TIM; 433 ie.bytes = new byte[255]; // bytes length of upto 254 is valid for TIM 434 Arrays.fill(ie.bytes, (byte) 7); 435 InformationElementUtil.TrafficIndicationMap trafficIndicationMap = 436 new InformationElementUtil.TrafficIndicationMap(); 437 trafficIndicationMap.from(ie); 438 assertEquals(trafficIndicationMap.isValid(), false); 439 } 440} 441