• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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