| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.devicepolicy; |
| |
| import android.content.Context; |
| import android.content.pm.VerifierDeviceIdentity; |
| import android.net.wifi.WifiManager; |
| import android.os.Build; |
| import android.security.identity.Util; |
| import android.telephony.TelephonyManager; |
| import android.text.TextUtils; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.util.Preconditions; |
| |
| import java.nio.ByteBuffer; |
| |
| class EnterpriseSpecificIdCalculator { |
| private static final int PADDED_HW_ID_LENGTH = 16; |
| private static final int PADDED_PROFILE_OWNER_LENGTH = 64; |
| private static final int PADDED_ENTERPRISE_ID_LENGTH = 64; |
| private static final int ESID_LENGTH = 16; |
| |
| private final String mImei; |
| private final String mMeid; |
| private final String mSerialNumber; |
| private final String mMacAddress; |
| |
| @VisibleForTesting |
| EnterpriseSpecificIdCalculator(String imei, String meid, String serialNumber, |
| String macAddress) { |
| mImei = imei; |
| mMeid = meid; |
| mSerialNumber = serialNumber; |
| mMacAddress = macAddress; |
| } |
| |
| EnterpriseSpecificIdCalculator(Context context) { |
| TelephonyManager telephonyService = context.getSystemService(TelephonyManager.class); |
| Preconditions.checkState(telephonyService != null, "Unable to access telephony service"); |
| mImei = telephonyService.getImei(0); |
| mMeid = telephonyService.getMeid(0); |
| mSerialNumber = Build.getSerial(); |
| WifiManager wifiManager = context.getSystemService(WifiManager.class); |
| Preconditions.checkState(wifiManager != null, "Unable to access WiFi service"); |
| final String[] macAddresses = wifiManager.getFactoryMacAddresses(); |
| if (macAddresses == null || macAddresses.length == 0) { |
| mMacAddress = ""; |
| } else { |
| mMacAddress = macAddresses[0]; |
| } |
| } |
| |
| private static String getPaddedTruncatedString(String input, int maxLength) { |
| final String paddedValue = String.format("%" + maxLength + "s", input); |
| return paddedValue.substring(0, maxLength); |
| } |
| |
| private static String getPaddedHardwareIdentifier(String hardwareIdentifier) { |
| if (hardwareIdentifier == null) { |
| hardwareIdentifier = ""; |
| } |
| return getPaddedTruncatedString(hardwareIdentifier, PADDED_HW_ID_LENGTH); |
| } |
| |
| String getPaddedImei() { |
| return getPaddedHardwareIdentifier(mImei); |
| } |
| |
| String getPaddedMeid() { |
| return getPaddedHardwareIdentifier(mMeid); |
| } |
| |
| String getPaddedSerialNumber() { |
| return getPaddedHardwareIdentifier(mSerialNumber); |
| } |
| |
| String getPaddedProfileOwnerName(String profileOwnerPackage) { |
| return getPaddedTruncatedString(profileOwnerPackage, PADDED_PROFILE_OWNER_LENGTH); |
| } |
| |
| String getPaddedEnterpriseId(String enterpriseId) { |
| return getPaddedTruncatedString(enterpriseId, PADDED_ENTERPRISE_ID_LENGTH); |
| } |
| |
| /** |
| * Calculates the ESID. |
| * @param profileOwnerPackage Package of the Device Policy Client that manages the device/ |
| * profile. May not be null. |
| * @param enterpriseIdString The identifier for the enterprise in which the device/profile is |
| * being enrolled. This parameter may not be empty, but may be null. |
| * If called with {@code null}, will calculate an ESID with empty |
| * Enterprise ID. |
| */ |
| public String calculateEnterpriseId(String profileOwnerPackage, String enterpriseIdString) { |
| Preconditions.checkArgument(!TextUtils.isEmpty(profileOwnerPackage), |
| "owner package must be specified."); |
| |
| Preconditions.checkArgument(enterpriseIdString == null || !enterpriseIdString.isEmpty(), |
| "enterprise ID must either be null or non-empty."); |
| |
| if (enterpriseIdString == null) { |
| enterpriseIdString = ""; |
| } |
| |
| final byte[] serialNumber = getPaddedSerialNumber().getBytes(); |
| final byte[] imei = getPaddedImei().getBytes(); |
| final byte[] meid = getPaddedMeid().getBytes(); |
| final byte[] macAddress = mMacAddress.getBytes(); |
| final int totalIdentifiersLength = serialNumber.length + imei.length + meid.length |
| + macAddress.length; |
| final ByteBuffer fixedIdentifiers = ByteBuffer.allocate(totalIdentifiersLength); |
| fixedIdentifiers.put(serialNumber); |
| fixedIdentifiers.put(imei); |
| fixedIdentifiers.put(meid); |
| fixedIdentifiers.put(macAddress); |
| |
| final byte[] dpcPackage = getPaddedProfileOwnerName(profileOwnerPackage).getBytes(); |
| final byte[] enterpriseId = getPaddedEnterpriseId(enterpriseIdString).getBytes(); |
| final ByteBuffer info = ByteBuffer.allocate(dpcPackage.length + enterpriseId.length); |
| info.put(dpcPackage); |
| info.put(enterpriseId); |
| final byte[] esidBytes = Util.computeHkdf("HMACSHA256", fixedIdentifiers.array(), null, |
| info.array(), ESID_LENGTH); |
| ByteBuffer esidByteBuffer = ByteBuffer.wrap(esidBytes); |
| |
| VerifierDeviceIdentity firstId = new VerifierDeviceIdentity(esidByteBuffer.getLong()); |
| VerifierDeviceIdentity secondId = new VerifierDeviceIdentity(esidByteBuffer.getLong()); |
| return firstId.toString() + secondId.toString(); |
| } |
| } |