1 /* 2 * Copyright (C) 2022 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 17 package com.android.server.nearby.presence; 18 19 import static com.android.server.nearby.presence.PresenceConstants.PRESENCE_UUID_BYTES; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import android.nearby.BroadcastRequest; 24 import android.nearby.DataElement; 25 import android.nearby.PresenceBroadcastRequest; 26 import android.nearby.PresenceCredential; 27 import android.nearby.PrivateCredential; 28 import android.nearby.PublicCredential; 29 30 import com.android.server.nearby.util.ArrayUtils; 31 import com.android.server.nearby.util.encryption.CryptorMicImp; 32 33 import org.junit.Before; 34 import org.junit.Test; 35 36 import java.nio.ByteBuffer; 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.Collections; 40 import java.util.List; 41 42 public class ExtendedAdvertisementTest { 43 private static final int EXTENDED_ADVERTISEMENT_BYTE_LENGTH = 67; 44 private static final int IDENTITY_TYPE = PresenceCredential.IDENTITY_TYPE_PRIVATE; 45 private static final int DATA_TYPE_ACTION = 6; 46 private static final int DATA_TYPE_MODEL_ID = 7; 47 private static final int DATA_TYPE_BLE_ADDRESS = 101; 48 private static final int DATA_TYPE_PUBLIC_IDENTITY = 3; 49 private static final byte[] MODE_ID_DATA = 50 new byte[]{2, 1, 30, 2, 10, -16, 6, 22, 44, -2, -86, -69, -52}; 51 private static final byte[] BLE_ADDRESS = new byte[]{124, 4, 56, 60, 120, -29, -90}; 52 private static final DataElement MODE_ID_ADDRESS_ELEMENT = 53 new DataElement(DATA_TYPE_MODEL_ID, MODE_ID_DATA); 54 private static final DataElement BLE_ADDRESS_ELEMENT = 55 new DataElement(DATA_TYPE_BLE_ADDRESS, BLE_ADDRESS); 56 57 private static final byte[] METADATA_ENCRYPTION_KEY = 58 new byte[]{-39, -55, 115, 78, -57, 40, 115, 0, -112, 86, -86, 7, -42, 68, 11, 12}; 59 private static final int MEDIUM_TYPE_BLE = 0; 60 private static final byte[] SALT = {2, 3}; 61 62 private static final int PRESENCE_ACTION_1 = 1; 63 private static final int PRESENCE_ACTION_2 = 2; 64 private static final DataElement PRESENCE_ACTION_DE_1 = 65 new DataElement(DATA_TYPE_ACTION, new byte[]{PRESENCE_ACTION_1}); 66 private static final DataElement PRESENCE_ACTION_DE_2 = 67 new DataElement(DATA_TYPE_ACTION, new byte[]{PRESENCE_ACTION_2}); 68 69 private static final byte[] SECRET_ID = new byte[]{1, 2, 3, 4}; 70 private static final byte[] AUTHENTICITY_KEY = 71 new byte[]{-97, 10, 107, -86, 25, 65, -54, -95, -72, 59, 54, 93, 9, 3, -24, -88}; 72 private static final byte[] PUBLIC_KEY = 73 new byte[]{ 74 48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, 6, 8, 42, -122, 72, -50, 61, 75 66, 0, 4, -56, -39, -92, 69, 0, 52, 23, 67, 83, -14, 75, 52, -14, -5, -41, 48, 76 -83, 31, 42, -39, 102, -13, 22, -73, -73, 86, 30, -96, -84, -13, 4, 122, 104, 77 -65, 64, 91, -109, -45, -35, -56, 55, -79, 47, -85, 27, -96, -119, -82, -80, 78 123, 41, -119, -25, 1, -112, 112 79 }; 80 private static final byte[] ENCRYPTED_METADATA_BYTES = 81 new byte[]{ 82 -44, -25, -95, -124, -7, 90, 116, -8, 7, -120, -23, -22, -106, -44, -19, 61, 83 -18, 39, 29, 78, 108, -11, -39, 85, -30, 64, -99, 102, 65, 37, -42, 114, -37, 84 88, -112, 8, -75, -53, 23, -16, -104, 67, 49, 48, -53, 73, -109, 44, -23, -11, 85 -118, -61, -37, -104, 60, 105, 115, 1, 56, -89, -107, -45, -116, -1, -25, 84, 86 -19, -128, 81, 11, 92, 77, -58, 82, 122, 123, 31, -87, -57, 70, 23, -81, 7, 2, 87 -114, -83, 74, 124, -68, -98, 47, 91, 9, 48, -67, 41, -7, -97, 78, 66, -65, 58, 88 -4, -46, -30, -85, -50, 100, 46, -66, -128, 7, 66, 9, 88, 95, 12, -13, 81, -91, 89 }; 90 private static final byte[] METADATA_ENCRYPTION_KEY_TAG = 91 new byte[]{-100, 102, -35, -99, 66, -85, -55, -58, -52, 11, -74, 102, 109, -89, 1, -34, 92 45, 43, 107, -60, 99, -21, 28, 34, 31, -100, -96, 108, 108, -18, 107, 5}; 93 94 private static final String ENCODED_ADVERTISEMENT_ENCRYPTION_INFO = 95 "2091911000DE2A89ED98474AF3E41E48487E8AEBDE90014C18BCB9F9AAC5C11A1BE00A10A5DCD2C49A74BE" 96 + "BAF0FE72FD5053B9DF8B9976C80BE0DCE8FEE83F1BFA9A89EB176CA48EE4ED5D15C6CDAD6B9E" 97 + "41187AA6316D7BFD8E454A53971AC00836F7AB0771FF0534050037D49C6AEB18CF9F8590E5CD" 98 + "EE2FBC330FCDC640C63F0735B7E3F02FE61A0496EF976A158AD3455D"; 99 private static final byte[] METADATA_ENCRYPTION_KEY_TAG_2 = 100 new byte[]{-54, -39, 41, 16, 61, 79, -116, 14, 94, 0, 84, 45, 26, -108, 66, -48, 124, 101 -81, 61, 56, -98, -47, 14, -19, 116, 106, -27, 123, -81, 49, 83, -42}; 102 103 private static final String DEVICE_NAME = "test_device"; 104 105 private static final byte[] SALT_16 = 106 ArrayUtils.stringToBytes("DE2A89ED98474AF3E41E48487E8AEBDE"); 107 private static final byte[] AUTHENTICITY_KEY_2 = ArrayUtils.stringToBytes( 108 "959D2F3CAB8EE4A2DEB0255C03762CF5D39EB919300420E75A089050FB025E20"); 109 private static final byte[] METADATA_ENCRYPTION_KEY_2 = ArrayUtils.stringToBytes( 110 "EF5E9A0867560E52AE1F05FCA7E48D29"); 111 112 private static final DataElement DE1 = new DataElement(571, ArrayUtils.stringToBytes( 113 "537F96FD94E13BE589F0141145CFC0EEC4F86FBDB2")); 114 private static final DataElement DE2 = new DataElement(541, ArrayUtils.stringToBytes( 115 "D301FFB24B5B")); 116 private static final DataElement DE3 = new DataElement(51, ArrayUtils.stringToBytes( 117 "EA95F07C25B75C04E1B2B8731F6A55BA379FB141")); 118 private static final DataElement DE4 = new DataElement(729, ArrayUtils.stringToBytes( 119 "2EFD3101E2311BBB108F0A7503907EAF0C2EAAA60CDA8D33A294C4CEACE0")); 120 private static final DataElement DE5 = new DataElement(411, ArrayUtils.stringToBytes("B0")); 121 122 private PresenceBroadcastRequest.Builder mBuilder; 123 private PresenceBroadcastRequest.Builder mBuilderCredentialInfo; 124 private PrivateCredential mPrivateCredential; 125 private PrivateCredential mPrivateCredential2; 126 127 private PublicCredential mPublicCredential; 128 private PublicCredential mPublicCredential2; 129 130 @Before setUp()131 public void setUp() { 132 mPrivateCredential = 133 new PrivateCredential.Builder( 134 SECRET_ID, AUTHENTICITY_KEY, METADATA_ENCRYPTION_KEY, DEVICE_NAME) 135 .setIdentityType(PresenceCredential.IDENTITY_TYPE_PRIVATE) 136 .build(); 137 mPrivateCredential2 = 138 new PrivateCredential.Builder( 139 SECRET_ID, AUTHENTICITY_KEY_2, METADATA_ENCRYPTION_KEY_2, DEVICE_NAME) 140 .setIdentityType(PresenceCredential.IDENTITY_TYPE_PRIVATE) 141 .build(); 142 mPublicCredential = 143 new PublicCredential.Builder(SECRET_ID, AUTHENTICITY_KEY, PUBLIC_KEY, 144 ENCRYPTED_METADATA_BYTES, METADATA_ENCRYPTION_KEY_TAG) 145 .build(); 146 mPublicCredential2 = 147 new PublicCredential.Builder(SECRET_ID, AUTHENTICITY_KEY_2, PUBLIC_KEY, 148 ENCRYPTED_METADATA_BYTES, METADATA_ENCRYPTION_KEY_TAG_2) 149 .build(); 150 mBuilder = 151 new PresenceBroadcastRequest.Builder(Collections.singletonList(MEDIUM_TYPE_BLE), 152 SALT, mPrivateCredential) 153 .setVersion(BroadcastRequest.PRESENCE_VERSION_V1) 154 .addAction(PRESENCE_ACTION_1) 155 .addAction(PRESENCE_ACTION_2) 156 .addExtendedProperty(new DataElement(DATA_TYPE_BLE_ADDRESS, BLE_ADDRESS)) 157 .addExtendedProperty(new DataElement(DATA_TYPE_MODEL_ID, MODE_ID_DATA)); 158 159 mBuilderCredentialInfo = 160 new PresenceBroadcastRequest.Builder(Collections.singletonList(MEDIUM_TYPE_BLE), 161 SALT_16, mPrivateCredential2) 162 .setVersion(BroadcastRequest.PRESENCE_VERSION_V1) 163 .addExtendedProperty(DE1) 164 .addExtendedProperty(DE2) 165 .addExtendedProperty(DE3) 166 .addExtendedProperty(DE4) 167 .addExtendedProperty(DE5); 168 } 169 170 @Test test_createFromRequest()171 public void test_createFromRequest() { 172 ExtendedAdvertisement originalAdvertisement = ExtendedAdvertisement.createFromRequest( 173 mBuilder.build()); 174 175 assertThat(originalAdvertisement.getActions()) 176 .containsExactly(PRESENCE_ACTION_1, PRESENCE_ACTION_2); 177 assertThat(originalAdvertisement.getIdentity()).isEqualTo(METADATA_ENCRYPTION_KEY); 178 assertThat(originalAdvertisement.getIdentityType()).isEqualTo(IDENTITY_TYPE); 179 assertThat(originalAdvertisement.getVersion()).isEqualTo( 180 BroadcastRequest.PRESENCE_VERSION_V1); 181 assertThat(originalAdvertisement.getSalt()).isEqualTo(SALT); 182 assertThat(originalAdvertisement.getDataElements()) 183 .containsExactly(PRESENCE_ACTION_DE_1, PRESENCE_ACTION_DE_2, 184 MODE_ID_ADDRESS_ELEMENT, BLE_ADDRESS_ELEMENT); 185 assertThat(originalAdvertisement.getLength()).isEqualTo(EXTENDED_ADVERTISEMENT_BYTE_LENGTH); 186 } 187 188 @Test test_createFromRequest_credentialInfo()189 public void test_createFromRequest_credentialInfo() { 190 ExtendedAdvertisement originalAdvertisement = ExtendedAdvertisement.createFromRequest( 191 mBuilderCredentialInfo.build()); 192 193 assertThat(originalAdvertisement.getIdentity()).isEqualTo(METADATA_ENCRYPTION_KEY_2); 194 assertThat(originalAdvertisement.getIdentityType()).isEqualTo(IDENTITY_TYPE); 195 assertThat(originalAdvertisement.getVersion()).isEqualTo( 196 BroadcastRequest.PRESENCE_VERSION_V1); 197 assertThat(originalAdvertisement.getSalt()).isEqualTo(SALT_16); 198 assertThat(originalAdvertisement.getDataElements()) 199 .containsExactly(DE1, DE2, DE3, DE4, DE5); 200 } 201 202 @Test test_createFromRequest_encodeAndDecode()203 public void test_createFromRequest_encodeAndDecode() { 204 ExtendedAdvertisement originalAdvertisement = ExtendedAdvertisement.createFromRequest( 205 mBuilder.build()); 206 byte[] generatedBytes = originalAdvertisement.toBytes(); 207 ExtendedAdvertisement newAdvertisement = 208 ExtendedAdvertisement.fromBytes(generatedBytes, mPublicCredential); 209 210 assertThat(newAdvertisement.getActions()) 211 .containsExactly(PRESENCE_ACTION_1, PRESENCE_ACTION_2); 212 assertThat(newAdvertisement.getIdentity()).isEqualTo(METADATA_ENCRYPTION_KEY); 213 assertThat(newAdvertisement.getIdentityType()).isEqualTo(IDENTITY_TYPE); 214 assertThat(newAdvertisement.getLength()).isEqualTo(EXTENDED_ADVERTISEMENT_BYTE_LENGTH); 215 assertThat(newAdvertisement.getVersion()).isEqualTo( 216 BroadcastRequest.PRESENCE_VERSION_V1); 217 assertThat(newAdvertisement.getSalt()).isEqualTo(SALT); 218 assertThat(newAdvertisement.getDataElements()) 219 .containsExactly(MODE_ID_ADDRESS_ELEMENT, BLE_ADDRESS_ELEMENT, 220 PRESENCE_ACTION_DE_1, PRESENCE_ACTION_DE_2); 221 } 222 223 @Test test_createFromRequest_invalidParameter()224 public void test_createFromRequest_invalidParameter() { 225 // invalid version 226 mBuilder.setVersion(BroadcastRequest.PRESENCE_VERSION_V0); 227 assertThat(ExtendedAdvertisement.createFromRequest(mBuilder.build())).isNull(); 228 229 // invalid salt 230 PresenceBroadcastRequest.Builder builder = 231 new PresenceBroadcastRequest.Builder(Collections.singletonList(MEDIUM_TYPE_BLE), 232 new byte[]{1, 2, 3}, mPrivateCredential) 233 .setVersion(BroadcastRequest.PRESENCE_VERSION_V1) 234 .addAction(PRESENCE_ACTION_1); 235 assertThat(ExtendedAdvertisement.createFromRequest(builder.build())).isNull(); 236 237 // invalid identity 238 PrivateCredential privateCredential = 239 new PrivateCredential.Builder(SECRET_ID, 240 AUTHENTICITY_KEY, new byte[]{1, 2, 3, 4}, DEVICE_NAME) 241 .setIdentityType(PresenceCredential.IDENTITY_TYPE_PRIVATE) 242 .build(); 243 PresenceBroadcastRequest.Builder builder2 = 244 new PresenceBroadcastRequest.Builder(Collections.singletonList(MEDIUM_TYPE_BLE), 245 new byte[]{1, 2, 3}, privateCredential) 246 .setVersion(BroadcastRequest.PRESENCE_VERSION_V1) 247 .addAction(PRESENCE_ACTION_1); 248 assertThat(ExtendedAdvertisement.createFromRequest(builder2.build())).isNull(); 249 } 250 251 @Test test_toBytesSalt()252 public void test_toBytesSalt() throws Exception { 253 ExtendedAdvertisement adv = ExtendedAdvertisement.createFromRequest(mBuilder.build()); 254 assertThat(adv.toBytes()).isEqualTo(getExtendedAdvertisementByteArray()); 255 } 256 257 @Test test_fromBytesSalt()258 public void test_fromBytesSalt() throws Exception { 259 byte[] originalBytes = getExtendedAdvertisementByteArray(); 260 ExtendedAdvertisement adv = 261 ExtendedAdvertisement.fromBytes(originalBytes, mPublicCredential); 262 263 assertThat(adv.getActions()) 264 .containsExactly(PRESENCE_ACTION_1, PRESENCE_ACTION_2); 265 assertThat(adv.getIdentity()).isEqualTo(METADATA_ENCRYPTION_KEY); 266 assertThat(adv.getIdentityType()).isEqualTo(IDENTITY_TYPE); 267 assertThat(adv.getLength()).isEqualTo(EXTENDED_ADVERTISEMENT_BYTE_LENGTH); 268 assertThat(adv.getVersion()).isEqualTo( 269 BroadcastRequest.PRESENCE_VERSION_V1); 270 assertThat(adv.getSalt()).isEqualTo(SALT); 271 assertThat(adv.getDataElements()) 272 .containsExactly(MODE_ID_ADDRESS_ELEMENT, BLE_ADDRESS_ELEMENT, 273 PRESENCE_ACTION_DE_1, PRESENCE_ACTION_DE_2); 274 } 275 276 @Test test_toBytesCredentialElement()277 public void test_toBytesCredentialElement() { 278 ExtendedAdvertisement adv = 279 ExtendedAdvertisement.createFromRequest(mBuilderCredentialInfo.build()); 280 assertThat(ArrayUtils.bytesToStringUppercase(adv.toBytes())).isEqualTo( 281 ENCODED_ADVERTISEMENT_ENCRYPTION_INFO); 282 } 283 284 @Test test_fromBytesCredentialElement()285 public void test_fromBytesCredentialElement() { 286 ExtendedAdvertisement adv = 287 ExtendedAdvertisement.fromBytes( 288 ArrayUtils.stringToBytes(ENCODED_ADVERTISEMENT_ENCRYPTION_INFO), 289 mPublicCredential2); 290 assertThat(adv.getIdentity()).isEqualTo(METADATA_ENCRYPTION_KEY_2); 291 assertThat(adv.getIdentityType()).isEqualTo(IDENTITY_TYPE); 292 assertThat(adv.getVersion()).isEqualTo(BroadcastRequest.PRESENCE_VERSION_V1); 293 assertThat(adv.getSalt()).isEqualTo(SALT_16); 294 assertThat(adv.getDataElements()).containsExactly(DE1, DE2, DE3, DE4, DE5); 295 } 296 297 @Test test_fromBytes_metadataTagNotMatched_fail()298 public void test_fromBytes_metadataTagNotMatched_fail() throws Exception { 299 byte[] originalBytes = getExtendedAdvertisementByteArray(); 300 PublicCredential credential = 301 new PublicCredential.Builder(SECRET_ID, AUTHENTICITY_KEY, PUBLIC_KEY, 302 ENCRYPTED_METADATA_BYTES, 303 new byte[]{113, 90, -55, 73, 25, -9, 55, -44, 102, 44, 81, -68, 101, 21, 32, 304 92, -107, 3, 108, 90, 28, -73, 16, 49, -95, -121, 8, -45, -27, 16, 305 6, 108}) 306 .build(); 307 ExtendedAdvertisement adv = 308 ExtendedAdvertisement.fromBytes(originalBytes, credential); 309 assertThat(adv).isNull(); 310 } 311 312 @Test test_toString()313 public void test_toString() { 314 ExtendedAdvertisement adv = ExtendedAdvertisement.createFromRequest(mBuilder.build()); 315 assertThat(adv.toString()).isEqualTo("ExtendedAdvertisement:" 316 + "<VERSION: 1, length: " + EXTENDED_ADVERTISEMENT_BYTE_LENGTH 317 + ", dataElementCount: 4, identityType: 1, " 318 + "identity: " + Arrays.toString(METADATA_ENCRYPTION_KEY) 319 + ", salt: [2, 3]," 320 + " actions: [1, 2]>"); 321 } 322 323 @Test test_getDataElements_accordingToType()324 public void test_getDataElements_accordingToType() { 325 ExtendedAdvertisement adv = ExtendedAdvertisement.createFromRequest(mBuilder.build()); 326 List<DataElement> dataElements = new ArrayList<>(); 327 328 dataElements.add(BLE_ADDRESS_ELEMENT); 329 assertThat(adv.getDataElements(DATA_TYPE_BLE_ADDRESS)).isEqualTo(dataElements); 330 assertThat(adv.getDataElements(DATA_TYPE_PUBLIC_IDENTITY)).isEmpty(); 331 } 332 getExtendedAdvertisementByteArray()333 private static byte[] getExtendedAdvertisementByteArray() throws Exception { 334 CryptorMicImp cryptor = CryptorMicImp.getInstance(); 335 ByteBuffer buffer = ByteBuffer.allocate(EXTENDED_ADVERTISEMENT_BYTE_LENGTH); 336 buffer.put((byte) 0b00100000); // Header V1 337 buffer.put( 338 (byte) (EXTENDED_ADVERTISEMENT_BYTE_LENGTH - 2)); // Section header (section length) 339 340 // Salt data 341 // Salt header: length 2, type 0 342 byte[] saltBytes = ArrayUtils.concatByteArrays(new byte[]{(byte) 0b00100000}, SALT); 343 buffer.put(saltBytes); 344 // Identity header: length 16, type 1 (private identity) 345 byte[] identityHeader = new byte[]{(byte) 0b10010000, (byte) 0b00000001}; 346 buffer.put(identityHeader); 347 348 ByteBuffer deBuffer = ByteBuffer.allocate(28); 349 // Ble address header: length 7, type 102 350 deBuffer.put(new byte[]{(byte) 0b10000111, (byte) 0b01100101}); 351 // Ble address data 352 deBuffer.put(BLE_ADDRESS); 353 // model id header: length 13, type 7 354 deBuffer.put(new byte[]{(byte) 0b10001101, (byte) 0b00000111}); 355 // model id data 356 deBuffer.put(MODE_ID_DATA); 357 // Action1 header: length 1, type 6 358 deBuffer.put(new byte[]{(byte) 0b00010110}); 359 // Action1 data 360 deBuffer.put((byte) PRESENCE_ACTION_1); 361 // Action2 header: length 1, type 6 362 deBuffer.put(new byte[]{(byte) 0b00010110}); 363 // Action2 data 364 deBuffer.put((byte) PRESENCE_ACTION_2); 365 byte[] deBytes = deBuffer.array(); 366 byte[] nonce = CryptorMicImp.generateAdvNonce(SALT); 367 byte[] ciphertext = 368 cryptor.encrypt( 369 ArrayUtils.concatByteArrays(METADATA_ENCRYPTION_KEY, deBytes), 370 nonce, AUTHENTICITY_KEY); 371 buffer.put(ciphertext); 372 373 byte[] dataToSign = ArrayUtils.concatByteArrays( 374 PRESENCE_UUID_BYTES, /* UUID */ 375 new byte[]{(byte) 0b00100000}, /* header */ 376 new byte[]{(byte) (EXTENDED_ADVERTISEMENT_BYTE_LENGTH - 2)} /* sectionHeader */, 377 saltBytes, /* salt */ 378 nonce, identityHeader, ciphertext); 379 byte[] mic = cryptor.sign(dataToSign, AUTHENTICITY_KEY); 380 buffer.put(mic); 381 382 return buffer.array(); 383 } 384 } 385