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 mImei = telephonyService.getImei(0); 56 mMeid = telephonyService.getMeid(0); 57 mSerialNumber = Build.getSerial(); 58 WifiManager wifiManager = context.getSystemService(WifiManager.class); 59 Preconditions.checkState(wifiManager != null, "Unable to access WiFi service"); 60 final String[] macAddresses = wifiManager.getFactoryMacAddresses(); 61 if (macAddresses == null || macAddresses.length == 0) { 62 mMacAddress = ""; 63 } else { 64 mMacAddress = macAddresses[0]; 65 } 66 } 67 getPaddedTruncatedString(String input, int maxLength)68 private static String getPaddedTruncatedString(String input, int maxLength) { 69 final String paddedValue = String.format("%" + maxLength + "s", input); 70 return paddedValue.substring(0, maxLength); 71 } 72 getPaddedHardwareIdentifier(String hardwareIdentifier)73 private static String getPaddedHardwareIdentifier(String hardwareIdentifier) { 74 if (hardwareIdentifier == null) { 75 hardwareIdentifier = ""; 76 } 77 return getPaddedTruncatedString(hardwareIdentifier, PADDED_HW_ID_LENGTH); 78 } 79 getPaddedImei()80 String getPaddedImei() { 81 return getPaddedHardwareIdentifier(mImei); 82 } 83 getPaddedMeid()84 String getPaddedMeid() { 85 return getPaddedHardwareIdentifier(mMeid); 86 } 87 getPaddedSerialNumber()88 String getPaddedSerialNumber() { 89 return getPaddedHardwareIdentifier(mSerialNumber); 90 } 91 getPaddedProfileOwnerName(String profileOwnerPackage)92 String getPaddedProfileOwnerName(String profileOwnerPackage) { 93 return getPaddedTruncatedString(profileOwnerPackage, PADDED_PROFILE_OWNER_LENGTH); 94 } 95 getPaddedEnterpriseId(String enterpriseId)96 String getPaddedEnterpriseId(String enterpriseId) { 97 return getPaddedTruncatedString(enterpriseId, PADDED_ENTERPRISE_ID_LENGTH); 98 } 99 100 /** 101 * Calculates the ESID. 102 * @param profileOwnerPackage Package of the Device Policy Client that manages the device/ 103 * profile. May not be null. 104 * @param enterpriseIdString The identifier for the enterprise in which the device/profile is 105 * being enrolled. This parameter may not be empty, but may be null. 106 * If called with {@code null}, will calculate an ESID with empty 107 * Enterprise ID. 108 */ calculateEnterpriseId(String profileOwnerPackage, String enterpriseIdString)109 public String calculateEnterpriseId(String profileOwnerPackage, String enterpriseIdString) { 110 Preconditions.checkArgument(!TextUtils.isEmpty(profileOwnerPackage), 111 "owner package must be specified."); 112 113 Preconditions.checkArgument(enterpriseIdString == null || !enterpriseIdString.isEmpty(), 114 "enterprise ID must either be null or non-empty."); 115 116 if (enterpriseIdString == null) { 117 enterpriseIdString = ""; 118 } 119 120 final byte[] serialNumber = getPaddedSerialNumber().getBytes(); 121 final byte[] imei = getPaddedImei().getBytes(); 122 final byte[] meid = getPaddedMeid().getBytes(); 123 final byte[] macAddress = mMacAddress.getBytes(); 124 final int totalIdentifiersLength = serialNumber.length + imei.length + meid.length 125 + macAddress.length; 126 final ByteBuffer fixedIdentifiers = ByteBuffer.allocate(totalIdentifiersLength); 127 fixedIdentifiers.put(serialNumber); 128 fixedIdentifiers.put(imei); 129 fixedIdentifiers.put(meid); 130 fixedIdentifiers.put(macAddress); 131 132 final byte[] dpcPackage = getPaddedProfileOwnerName(profileOwnerPackage).getBytes(); 133 final byte[] enterpriseId = getPaddedEnterpriseId(enterpriseIdString).getBytes(); 134 final ByteBuffer info = ByteBuffer.allocate(dpcPackage.length + enterpriseId.length); 135 info.put(dpcPackage); 136 info.put(enterpriseId); 137 final byte[] esidBytes = Util.computeHkdf("HMACSHA256", fixedIdentifiers.array(), null, 138 info.array(), ESID_LENGTH); 139 ByteBuffer esidByteBuffer = ByteBuffer.wrap(esidBytes); 140 141 VerifierDeviceIdentity firstId = new VerifierDeviceIdentity(esidByteBuffer.getLong()); 142 VerifierDeviceIdentity secondId = new VerifierDeviceIdentity(esidByteBuffer.getLong()); 143 return firstId.toString() + secondId.toString(); 144 } 145 } 146