1 /* 2 * Copyright (C) 2020 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.devicepolicy; 18 19 import android.content.Context; 20 import android.content.pm.VerifierDeviceIdentity; 21 import android.net.wifi.WifiManager; 22 import android.os.Build; 23 import android.security.identity.Util; 24 import android.telephony.TelephonyManager; 25 import android.text.TextUtils; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.internal.util.Preconditions; 29 30 import java.nio.ByteBuffer; 31 32 class EnterpriseSpecificIdCalculator { 33 private static final int PADDED_HW_ID_LENGTH = 16; 34 private static final int PADDED_PROFILE_OWNER_LENGTH = 64; 35 private static final int PADDED_ENTERPRISE_ID_LENGTH = 64; 36 private static final int ESID_LENGTH = 16; 37 38 private final String mImei; 39 private final String mMeid; 40 private final String mSerialNumber; 41 private final String mMacAddress; 42 43 @VisibleForTesting EnterpriseSpecificIdCalculator(String imei, String meid, String serialNumber, String macAddress)44 EnterpriseSpecificIdCalculator(String imei, String meid, String serialNumber, 45 String macAddress) { 46 mImei = imei; 47 mMeid = meid; 48 mSerialNumber = serialNumber; 49 mMacAddress = macAddress; 50 } 51 EnterpriseSpecificIdCalculator(Context context)52 EnterpriseSpecificIdCalculator(Context context) { 53 TelephonyManager telephonyService = context.getSystemService(TelephonyManager.class); 54 Preconditions.checkState(telephonyService != null, "Unable to access telephony service"); 55 56 String imei; 57 try { 58 imei = telephonyService.getImei(0); 59 } catch (UnsupportedOperationException doesNotSupportGms) { 60 // Instead of catching the exception, we could check for FEATURE_TELEPHONY_GSM. 61 // However that runs the risk of changing a device's existing ESID if on these devices 62 // telephonyService.getImei() actually returns non-null even when the device does not 63 // declare FEATURE_TELEPHONY_GSM. 64 imei = null; 65 } 66 mImei = imei; 67 String meid; 68 try { 69 meid = telephonyService.getMeid(0); 70 } catch (UnsupportedOperationException doesNotSupportCdma) { 71 // Instead of catching the exception, we could check for FEATURE_TELEPHONY_CDMA. 72 // However that runs the risk of changing a device's existing ESID if on these devices 73 // telephonyService.getMeid() actually returns non-null even when the device does not 74 // declare FEATURE_TELEPHONY_CDMA. 75 meid = null; 76 } 77 mMeid = meid; 78 mSerialNumber = Build.getSerial(); 79 WifiManager wifiManager = context.getSystemService(WifiManager.class); 80 Preconditions.checkState(wifiManager != null, "Unable to access WiFi service"); 81 final String[] macAddresses = wifiManager.getFactoryMacAddresses(); 82 if (macAddresses == null || macAddresses.length == 0) { 83 mMacAddress = ""; 84 } else { 85 mMacAddress = macAddresses[0]; 86 } 87 } 88 getPaddedTruncatedString(String input, int maxLength)89 private static String getPaddedTruncatedString(String input, int maxLength) { 90 final String paddedValue = String.format("%" + maxLength + "s", input); 91 return paddedValue.substring(0, maxLength); 92 } 93 getPaddedHardwareIdentifier(String hardwareIdentifier)94 private static String getPaddedHardwareIdentifier(String hardwareIdentifier) { 95 if (hardwareIdentifier == null) { 96 hardwareIdentifier = ""; 97 } 98 return getPaddedTruncatedString(hardwareIdentifier, PADDED_HW_ID_LENGTH); 99 } 100 getPaddedImei()101 String getPaddedImei() { 102 return getPaddedHardwareIdentifier(mImei); 103 } 104 getPaddedMeid()105 String getPaddedMeid() { 106 return getPaddedHardwareIdentifier(mMeid); 107 } 108 getPaddedSerialNumber()109 String getPaddedSerialNumber() { 110 return getPaddedHardwareIdentifier(mSerialNumber); 111 } 112 getPaddedProfileOwnerName(String profileOwnerPackage)113 String getPaddedProfileOwnerName(String profileOwnerPackage) { 114 return getPaddedTruncatedString(profileOwnerPackage, PADDED_PROFILE_OWNER_LENGTH); 115 } 116 getPaddedEnterpriseId(String enterpriseId)117 String getPaddedEnterpriseId(String enterpriseId) { 118 return getPaddedTruncatedString(enterpriseId, PADDED_ENTERPRISE_ID_LENGTH); 119 } 120 121 /** 122 * Calculates the ESID. 123 * @param profileOwnerPackage Package of the Device Policy Client that manages the device/ 124 * profile. May not be null. 125 * @param enterpriseIdString The identifier for the enterprise in which the device/profile is 126 * being enrolled. This parameter may not be empty, but may be null. 127 * If called with {@code null}, will calculate an ESID with empty 128 * Enterprise ID. 129 */ calculateEnterpriseId(String profileOwnerPackage, String enterpriseIdString)130 public String calculateEnterpriseId(String profileOwnerPackage, String enterpriseIdString) { 131 Preconditions.checkArgument(!TextUtils.isEmpty(profileOwnerPackage), 132 "owner package must be specified."); 133 134 Preconditions.checkArgument(enterpriseIdString == null || !enterpriseIdString.isEmpty(), 135 "enterprise ID must either be null or non-empty."); 136 137 if (enterpriseIdString == null) { 138 enterpriseIdString = ""; 139 } 140 141 final byte[] serialNumber = getPaddedSerialNumber().getBytes(); 142 final byte[] imei = getPaddedImei().getBytes(); 143 final byte[] meid = getPaddedMeid().getBytes(); 144 final byte[] macAddress = mMacAddress.getBytes(); 145 final int totalIdentifiersLength = serialNumber.length + imei.length + meid.length 146 + macAddress.length; 147 final ByteBuffer fixedIdentifiers = ByteBuffer.allocate(totalIdentifiersLength); 148 fixedIdentifiers.put(serialNumber); 149 fixedIdentifiers.put(imei); 150 fixedIdentifiers.put(meid); 151 fixedIdentifiers.put(macAddress); 152 153 final byte[] dpcPackage = getPaddedProfileOwnerName(profileOwnerPackage).getBytes(); 154 final byte[] enterpriseId = getPaddedEnterpriseId(enterpriseIdString).getBytes(); 155 final ByteBuffer info = ByteBuffer.allocate(dpcPackage.length + enterpriseId.length); 156 info.put(dpcPackage); 157 info.put(enterpriseId); 158 final byte[] esidBytes = Util.computeHkdf("HMACSHA256", fixedIdentifiers.array(), null, 159 info.array(), ESID_LENGTH); 160 ByteBuffer esidByteBuffer = ByteBuffer.wrap(esidBytes); 161 162 VerifierDeviceIdentity firstId = new VerifierDeviceIdentity(esidByteBuffer.getLong()); 163 VerifierDeviceIdentity secondId = new VerifierDeviceIdentity(esidByteBuffer.getLong()); 164 return firstId.toString() + secondId.toString(); 165 } 166 } 167