1 /* 2 * Copyright (C) 2019 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.wifi; 18 19 import android.annotation.NonNull; 20 import android.net.MacAddress; 21 import android.util.Log; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 import com.android.server.wifi.util.KeystoreWrapper; 25 26 import java.nio.ByteBuffer; 27 import java.nio.charset.StandardCharsets; 28 import java.security.ProviderException; 29 import java.util.Arrays; 30 31 import javax.crypto.Mac; 32 33 /** 34 * Contains helper methods to support MAC randomization. 35 */ 36 public class MacAddressUtil { 37 private static final String TAG = "MacAddressUtil"; 38 @VisibleForTesting 39 public static final String MAC_RANDOMIZATION_ALIAS = "MacRandSecret"; 40 @VisibleForTesting 41 public static final String MAC_RANDOMIZATION_SAP_ALIAS = "MacRandSapSecret"; 42 private static final long MAC_ADDRESS_VALID_LONG_MASK = (1L << 48) - 1; 43 private static final long MAC_ADDRESS_LOCALLY_ASSIGNED_MASK = 1L << 41; 44 private static final long MAC_ADDRESS_MULTICAST_MASK = 1L << 40; 45 private static final int ETHER_ADDR_LEN = 6; 46 private final KeystoreWrapper mKeystoreWrapper; 47 private Mac mMacForSta = null; 48 private Mac mMacForSap = null; 49 MacAddressUtil(KeystoreWrapper keystoreWrapper)50 public MacAddressUtil(KeystoreWrapper keystoreWrapper) { 51 mKeystoreWrapper = keystoreWrapper; 52 } 53 54 /** 55 * Computes the persistent randomized MAC using the given key and hash function. 56 * @param key the key to compute MAC address for 57 * @param hashFunction the hash function that will perform the MAC address computation. 58 * @return The persistent randomized MAC address or null if inputs are invalid. 59 */ calculatePersistentMacInternal(String key, Mac hashFunction)60 private MacAddress calculatePersistentMacInternal(String key, Mac hashFunction) { 61 if (key == null || hashFunction == null) { 62 return null; 63 } 64 byte[] hashedBytes; 65 try { 66 hashedBytes = hashFunction.doFinal(key.getBytes(StandardCharsets.UTF_8)); 67 } catch (ProviderException | IllegalStateException e) { 68 Log.e(TAG, "Failure in calculatePersistentMac", e); 69 return null; 70 } 71 ByteBuffer bf = ByteBuffer.wrap(hashedBytes); 72 long longFromSsid = bf.getLong(); 73 /** 74 * Masks the generated long so that it represents a valid randomized MAC address. 75 * Specifically, this sets the locally assigned bit to 1, multicast bit to 0 76 */ 77 longFromSsid &= MAC_ADDRESS_VALID_LONG_MASK; 78 longFromSsid |= MAC_ADDRESS_LOCALLY_ASSIGNED_MASK; 79 longFromSsid &= ~MAC_ADDRESS_MULTICAST_MASK; 80 bf.clear(); 81 bf.putLong(0, longFromSsid); 82 83 // MacAddress.fromBytes requires input of length 6, which is obtained from the 84 // last 6 bytes from the generated long. 85 MacAddress macAddress = MacAddress.fromBytes(Arrays.copyOfRange(bf.array(), 2, 8)); 86 return macAddress; 87 } 88 calculatePersistentMacWithCachedHash(@onNull String key, int uid, @NonNull String alias)89 private MacAddress calculatePersistentMacWithCachedHash(@NonNull String key, int uid, 90 @NonNull String alias) { 91 Mac hashFunction = getCachedHashFunction(alias); 92 if (hashFunction != null) { 93 // first try calculating the MacAddress using the cached hash function 94 MacAddress macAddress = calculatePersistentMacInternal(key, hashFunction); 95 if (macAddress != null) { 96 return macAddress; 97 } 98 // intentional fallthrough if calculating MacAddress with the cached hash function fails 99 } 100 hashFunction = mKeystoreWrapper.getHmacSHA256ForUid(uid, alias); 101 cacheHashFunction(alias, hashFunction); 102 return calculatePersistentMacInternal(key, hashFunction); 103 } 104 105 /** 106 * calculate the persistent randomized MAC for STA 107 */ calculatePersistentMacForSta(String key, int uid)108 public MacAddress calculatePersistentMacForSta(String key, int uid) { 109 if (key == null) { 110 return null; 111 } 112 return calculatePersistentMacWithCachedHash(key, uid, MAC_RANDOMIZATION_ALIAS); 113 } 114 115 /** 116 * calculate the persistent randomized MAC for SoftAp 117 */ calculatePersistentMacForSap(String key, int uid)118 public synchronized MacAddress calculatePersistentMacForSap(String key, int uid) { 119 if (key == null) { 120 return null; 121 } 122 return calculatePersistentMacWithCachedHash(key, uid, MAC_RANDOMIZATION_SAP_ALIAS); 123 } 124 getCachedHashFunction(@onNull String alias)125 private Mac getCachedHashFunction(@NonNull String alias) { 126 if (MAC_RANDOMIZATION_ALIAS.equals(alias)) { 127 return mMacForSta; 128 } else if (MAC_RANDOMIZATION_SAP_ALIAS.equals(alias)) { 129 return mMacForSap; 130 } 131 return null; 132 } 133 cacheHashFunction(@onNull String alias, Mac mac)134 private void cacheHashFunction(@NonNull String alias, Mac mac) { 135 if (MAC_RANDOMIZATION_ALIAS.equals(alias)) { 136 mMacForSta = mac; 137 } else if (MAC_RANDOMIZATION_SAP_ALIAS.equals(alias)) { 138 mMacForSap = mac; 139 } 140 } 141 142 /** 143 * Returns the next Mac address to the given Mac address. 144 */ nextMacAddress(MacAddress mac)145 public static MacAddress nextMacAddress(MacAddress mac) { 146 byte[] bytes = mac.toByteArray(); 147 bytes[MacAddressUtil.ETHER_ADDR_LEN - 1] = 148 (byte) ((bytes[MacAddressUtil.ETHER_ADDR_LEN - 1] + 1) & 0xff); 149 return MacAddress.fromBytes(bytes); 150 } 151 } 152