From af01489951624094fc71433db9a5a4fda626be72 Mon Sep 17 00:00:00 2001 From: Devin Moore Date: Fri, 2 Dec 2022 23:50:55 +0000 Subject: [PATCH] locksettings: Add AIDL weaver HAL support Test: atest FrameworksServicesTests:com.android.server.locksettings.LockSettingsServiceTests Test: Set PIN, enter incorrect PIN, enter correct PIN Test: Remove HIDL HAL service and flash new image Test: enter incorrect PIN, enter correct PIN. Test: Set a new PIN and retest all without HIDL service Bug: 182976659 Change-Id: Icf720b5ab4e93675cf72ab441fd4af86a46f90e9 --- services/core/Android.bp | 1 + .../SyntheticPasswordManager.java | 187 ++++++++++-------- .../locksettings/WeaverHidlWrapper.java | 143 ++++++++++++++ .../MockSyntheticPasswordManager.java | 2 +- 4 files changed, 248 insertions(+), 85 deletions(-) create mode 100644 services/core/java/com/android/server/locksettings/WeaverHidlWrapper.java diff --git a/services/core/Android.bp b/services/core/Android.bp index 1e1d61050a5de..5c00452396dc5 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -161,6 +161,7 @@ java_library_static { "android.hardware.tv.cec-V1-java", "android.hardware.tv.hdmi-V1-java", "android.hardware.weaver-V1.0-java", + "android.hardware.weaver-V2-java", "android.hardware.biometrics.face-V1.0-java", "android.hardware.biometrics.fingerprint-V2.3-java", "android.hardware.oemlock-V1.0-java", diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index acd7cc1f0ddba..66ce429c2df1f 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -24,13 +24,14 @@ import android.annotation.Nullable; import android.app.admin.PasswordMetrics; import android.content.Context; import android.content.pm.UserInfo; -import android.hardware.weaver.V1_0.IWeaver; -import android.hardware.weaver.V1_0.WeaverConfig; -import android.hardware.weaver.V1_0.WeaverReadResponse; -import android.hardware.weaver.V1_0.WeaverReadStatus; -import android.hardware.weaver.V1_0.WeaverStatus; +import android.hardware.weaver.IWeaver; +import android.hardware.weaver.WeaverConfig; +import android.hardware.weaver.WeaverReadResponse; +import android.hardware.weaver.WeaverReadStatus; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.ServiceSpecificException; import android.os.UserManager; import android.provider.Settings; import android.security.GateKeeper; @@ -60,7 +61,6 @@ import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -464,37 +464,67 @@ public class SyntheticPasswordManager { } @VisibleForTesting - protected IWeaver getWeaverService() throws RemoteException { + protected android.hardware.weaver.V1_0.IWeaver getWeaverHidlService() throws RemoteException { try { - return IWeaver.getService(/* retry */ true); + return android.hardware.weaver.V1_0.IWeaver.getService(/* retry */ true); } catch (NoSuchElementException e) { - Slog.i(TAG, "Device does not support weaver"); return null; } } + private IWeaver getWeaverService() { + // Try to get the AIDL service first + try { + IWeaver aidlWeaver = IWeaver.Stub.asInterface( + ServiceManager.waitForDeclaredService(IWeaver.DESCRIPTOR + "/default")); + if (aidlWeaver != null) { + Slog.i(TAG, "Using AIDL weaver service"); + return aidlWeaver; + } + } catch (SecurityException e) { + Slog.w(TAG, "Does not have permissions to get AIDL weaver service"); + } + + // If the AIDL service can't be found, look for the HIDL service + try { + android.hardware.weaver.V1_0.IWeaver hidlWeaver = getWeaverHidlService(); + if (hidlWeaver != null) { + Slog.i(TAG, "Using HIDL weaver service"); + return new WeaverHidlWrapper(hidlWeaver); + } + } catch (RemoteException e) { + Slog.w(TAG, "Failed to get HIDL weaver service.", e); + } + Slog.w(TAG, "Device does not support weaver"); + return null; + } + public synchronized void initWeaverService() { if (mWeaver != null) { return; } - try { - mWeaverConfig = null; - mWeaver = getWeaverService(); - if (mWeaver != null) { - mWeaver.getConfig((int status, WeaverConfig config) -> { - if (status == WeaverStatus.OK && config.slots > 0) { - mWeaverConfig = config; - } else { - Slog.e(TAG, "Failed to get weaver config, status " + status - + " slots: " + config.slots); - mWeaver = null; - } - }); - mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots()); - } - } catch (RemoteException e) { - Slog.e(TAG, "Failed to get weaver service", e); + + IWeaver weaver = getWeaverService(); + if (weaver == null) { + return; } + + // Get the config + WeaverConfig weaverConfig = null; + try { + weaverConfig = weaver.getConfig(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get weaver config", e); + } + if (weaverConfig == null || weaverConfig.slots <= 0) { + Slog.e(TAG, "Failed to initialize weaver config"); + return; + } + + mWeaver = weaver; + mWeaverConfig = weaverConfig; + mPasswordSlotManager.refreshActiveSlots(getUsedWeaverSlots()); + Slog.i(TAG, "Weaver service initialized"); } private synchronized boolean isWeaverAvailable() { @@ -525,18 +555,30 @@ public class SyntheticPasswordManager { value = secureRandom(mWeaverConfig.valueSize); } try { - int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value)); - if (writeStatus != WeaverStatus.OK) { - Slog.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus); - return null; - } + mWeaver.write(slot, key, value); } catch (RemoteException e) { - Slog.e(TAG, "weaver write failed", e); + Slog.e(TAG, "weaver write binder call failed, slot: " + slot, e); + return null; + } catch (ServiceSpecificException e) { + Slog.e(TAG, "weaver write failed, slot: " + slot, e); return null; } return value; } + /** + * Create a VerifyCredentialResponse from a timeout base on the WeaverReadResponse. + * This checks the received timeout(long) to make sure it sure it fits in an int before + * using it. If it doesn't fit, we use Integer.MAX_VALUE. + */ + private static VerifyCredentialResponse responseFromTimeout(WeaverReadResponse response) { + int timeout = + response.timeout > Integer.MAX_VALUE || response.timeout < 0 + ? Integer.MAX_VALUE + : (int) response.timeout; + return VerifyCredentialResponse.fromTimeout(timeout); + } + /** * Verify the supplied key against a weaver slot, returning a response indicating whether * the verification is successful, throttled or failed. If successful, the bound secret @@ -551,46 +593,39 @@ public class SyntheticPasswordManager { } else if (key.length != mWeaverConfig.keySize) { throw new IllegalArgumentException("Invalid key size for weaver"); } - final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1]; + final WeaverReadResponse readResponse; try { - mWeaver.read(slot, toByteArrayList(key), - (int status, WeaverReadResponse readResponse) -> { - switch (status) { - case WeaverReadStatus.OK: - response[0] = new VerifyCredentialResponse.Builder().setGatekeeperHAT( - fromByteArrayList(readResponse.value)).build(); - break; - case WeaverReadStatus.THROTTLE: - response[0] = VerifyCredentialResponse - .fromTimeout(readResponse.timeout); - Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot); - break; - case WeaverReadStatus.INCORRECT_KEY: - if (readResponse.timeout == 0) { - response[0] = VerifyCredentialResponse.ERROR; - Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot); - } else { - response[0] = VerifyCredentialResponse - .fromTimeout(readResponse.timeout); - Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " - + slot); - } - break; - case WeaverReadStatus.FAILED: - response[0] = VerifyCredentialResponse.ERROR; - Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot); - break; - default: - response[0] = VerifyCredentialResponse.ERROR; - Slog.e(TAG, "weaver read unknown status " + status + ", slot: " + slot); - break; - } - }); + readResponse = mWeaver.read(slot, key); } catch (RemoteException e) { - response[0] = VerifyCredentialResponse.ERROR; Slog.e(TAG, "weaver read failed, slot: " + slot, e); + return VerifyCredentialResponse.ERROR; + } + + switch (readResponse.status) { + case WeaverReadStatus.OK: + return new VerifyCredentialResponse.Builder() + .setGatekeeperHAT(readResponse.value) + .build(); + case WeaverReadStatus.THROTTLE: + Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot); + return responseFromTimeout(readResponse); + case WeaverReadStatus.INCORRECT_KEY: + if (readResponse.timeout == 0) { + Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot); + return VerifyCredentialResponse.ERROR; + } else { + Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot); + return responseFromTimeout(readResponse); + } + case WeaverReadStatus.FAILED: + Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot); + return VerifyCredentialResponse.ERROR; + default: + Slog.e(TAG, + "weaver read unknown status " + readResponse.status + + ", slot: " + slot); + return VerifyCredentialResponse.ERROR; } - return response[0]; } public void removeUser(IGateKeeperService gatekeeper, int userId) { @@ -1660,22 +1695,6 @@ public class SyntheticPasswordManager { native long nativeSidFromPasswordHandle(byte[] handle); - protected static ArrayList toByteArrayList(byte[] data) { - ArrayList result = new ArrayList(data.length); - for (int i = 0; i < data.length; i++) { - result.add(data[i]); - } - return result; - } - - protected static byte[] fromByteArrayList(ArrayList data) { - byte[] result = new byte[data.size()]; - for (int i = 0; i < data.size(); i++) { - result[i] = data.get(i); - } - return result; - } - @VisibleForTesting static byte[] bytesToHex(byte[] bytes) { return HexEncoding.encodeToString(bytes).getBytes(); diff --git a/services/core/java/com/android/server/locksettings/WeaverHidlWrapper.java b/services/core/java/com/android/server/locksettings/WeaverHidlWrapper.java new file mode 100644 index 0000000000000..9d93c3de2f1d6 --- /dev/null +++ b/services/core/java/com/android/server/locksettings/WeaverHidlWrapper.java @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2022 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.locksettings; + +import android.hardware.weaver.V1_0.IWeaver; +import android.hardware.weaver.V1_0.WeaverConfig; +import android.hardware.weaver.V1_0.WeaverReadResponse; +import android.hardware.weaver.V1_0.WeaverReadStatus; +import android.hardware.weaver.V1_0.WeaverStatus; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.Slog; + +import java.util.ArrayList; + +/** + * Implement the AIDL IWeaver interface wrapping the HIDL implementation + */ +class WeaverHidlWrapper implements android.hardware.weaver.IWeaver { + private static final String TAG = "WeaverHidlWrapper"; + private final IWeaver mImpl; + + WeaverHidlWrapper(IWeaver impl) { + mImpl = impl; + } + + private static ArrayList toByteArrayList(byte[] data) { + ArrayList result = new ArrayList(data.length); + for (int i = 0; i < data.length; i++) { + result.add(data[i]); + } + return result; + } + + private static byte[] fromByteArrayList(ArrayList data) { + byte[] result = new byte[data.size()]; + for (int i = 0; i < data.size(); i++) { + result[i] = data.get(i); + } + return result; + } + + @Override + public String getInterfaceHash() { + // We do not require the interface hash as the client. + throw new UnsupportedOperationException( + "WeaverHidlWrapper does not support getInterfaceHash"); + } + @Override + public int getInterfaceVersion() { + // Supports only V2 which is at feature parity. + return 2; + } + @Override + public android.os.IBinder asBinder() { + // There is no IHwBinder to IBinder. Not required as the client. + throw new UnsupportedOperationException("WeaverHidlWrapper does not support asBinder"); + } + + @Override + public android.hardware.weaver.WeaverConfig getConfig() throws RemoteException { + final WeaverConfig[] res = new WeaverConfig[1]; + mImpl.getConfig((int status, WeaverConfig config) -> { + if (status == WeaverStatus.OK && config.slots > 0) { + res[0] = config; + } else { + res[0] = null; + Slog.e(TAG, + "Failed to get HIDL weaver config. status: " + status + + ", slots: " + config.slots); + } + }); + + if (res[0] == null) { + return null; + } + android.hardware.weaver.WeaverConfig config = new android.hardware.weaver.WeaverConfig(); + config.slots = res[0].slots; + config.keySize = res[0].keySize; + config.valueSize = res[0].valueSize; + return config; + } + + @Override + public android.hardware.weaver.WeaverReadResponse read(int slotId, byte[] key) + throws RemoteException { + final WeaverReadResponse[] res = new WeaverReadResponse[1]; + final int[] status = new int[1]; + mImpl.read( + slotId, toByteArrayList(key), (int inStatus, WeaverReadResponse readResponse) -> { + status[0] = inStatus; + res[0] = readResponse; + }); + + android.hardware.weaver.WeaverReadResponse aidlRes = + new android.hardware.weaver.WeaverReadResponse(); + switch (status[0]) { + case WeaverReadStatus.OK: + aidlRes.status = android.hardware.weaver.WeaverReadStatus.OK; + break; + case WeaverReadStatus.THROTTLE: + aidlRes.status = android.hardware.weaver.WeaverReadStatus.THROTTLE; + break; + case WeaverReadStatus.INCORRECT_KEY: + aidlRes.status = android.hardware.weaver.WeaverReadStatus.INCORRECT_KEY; + break; + case WeaverReadStatus.FAILED: + aidlRes.status = android.hardware.weaver.WeaverReadStatus.FAILED; + break; + default: + aidlRes.status = android.hardware.weaver.WeaverReadStatus.FAILED; + break; + } + if (res[0] != null) { + aidlRes.timeout = res[0].timeout; + aidlRes.value = fromByteArrayList(res[0].value); + } + return aidlRes; + } + + @Override + public void write(int slotId, byte[] key, byte[] value) throws RemoteException { + int writeStatus = mImpl.write(slotId, toByteArrayList(key), toByteArrayList(value)); + if (writeStatus != WeaverStatus.OK) { + throw new ServiceSpecificException( + android.hardware.weaver.IWeaver.STATUS_FAILED, "Failed IWeaver.write call"); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java index 8cb18a88c840a..a40cb0625e1cc 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java @@ -113,7 +113,7 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager { } @Override - protected IWeaver getWeaverService() throws RemoteException { + protected IWeaver getWeaverHidlService() throws RemoteException { return mWeaverService; }