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