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