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