summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dmitry Dementyev <dementyev@google.com> 2017-12-11 11:33:12 -0800
committer Dmitry Dementyev <dementyev@google.com> 2017-12-18 16:04:15 -0800
commit1aa96132bdcbaf89f7a69d6c3664790f08f87cc9 (patch)
tree9a22a29ab8c9207a0819360113080183c72eac9e
parent93592a9b390e2f44a476068e1d732d3d6b13e82e (diff)
Add RecoverableKeyStoreLoader implementation in LockSettingsService.
1) Updates to ILockSettings.aidl Since we can't pass arbitrary exception using IPC, Serrvice converts them to ServiceSpecificException with an error code. 2) Added RecoverableKeyStoreManager class which is used as interface between RecoverableKeyStoreLoader implementation and LockSettingsService. Test: none Bug: 66499222 Change-Id: I03b695bc0ced1a91ea7ca5de179e121053dfe416
-rw-r--r--core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl2
-rw-r--r--core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java289
-rw-r--r--core/java/com/android/internal/widget/ILockSettings.aidl22
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java83
-rw-r--r--services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java211
5 files changed, 523 insertions, 84 deletions
diff --git a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl
index 1058463aa561..1674e51f77e1 100644
--- a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl
+++ b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.security.keystore.recoverablekeystore;
+package android.security.recoverablekeystore;
/* @hide */
parcelable KeyEntryRecoveryData;
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
index 0510320d3e11..f2f225df7f93 100644
--- a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
+++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
@@ -19,37 +19,40 @@ package android.security.recoverablekeystore;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+import android.security.KeyStore;
+import android.util.AndroidException;
import com.android.internal.widget.ILockSettings;
import java.util.List;
/**
- * A wrapper around KeyStore which lets key be exported to
- * trusted hardware on server side and recovered later.
+ * A wrapper around KeyStore which lets key be exported to trusted hardware on server side and
+ * recovered later.
*
* @hide
*/
-public class RecoverableKeyStoreLoader {
+public class RecoverableKeyStoreLoader {
- private final ILockSettings mBinder;
-
- // Exception codes, should be in sync with {@code KeyStoreException}.
- public static final int SYSTEM_ERROR = 4;
+ public static final String PERMISSION_RECOVER_KEYSTORE = "android.permission.RECOVER_KEYSTORE";
+ public static final int NO_ERROR = KeyStore.NO_ERROR;
+ public static final int SYSTEM_ERROR = KeyStore.SYSTEM_ERROR;
public static final int UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20;
-
// Too many updates to recovery public key or server parameters.
public static final int RATE_LIMIT_EXCEEDED = 21;
+ private final ILockSettings mBinder;
+
private RecoverableKeyStoreLoader(ILockSettings binder) {
mBinder = binder;
}
- /**
- * @hide
- */
+ /** @hide */
public static RecoverableKeyStoreLoader getInstance() {
ILockSettings lockSettings =
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
@@ -57,29 +60,69 @@ public class RecoverableKeyStoreLoader {
}
/**
+ * Exceptions returned by {@link RecoverableKeyStoreLoader}.
+ *
* @hide
*/
- public static class RecoverableKeyStoreLoaderException extends Exception {
- private final int mErrorCode;
+ public static class RecoverableKeyStoreLoaderException extends AndroidException {
+ private int mErrorCode;
+
+ /**
+ * Creates new {@link #RecoverableKeyStoreLoaderException} instance from the error code.
+ *
+ * @param errorCode
+ * @hide
+ */
+ public static RecoverableKeyStoreLoaderException fromErrorCode(int errorCode) {
+ return new RecoverableKeyStoreLoaderException(
+ errorCode, getMessageFromErrorCode(errorCode));
+ }
+
+ /**
+ * Creates new {@link #RecoverableKeyStoreLoaderException} from {@link
+ * ServiceSpecificException}.
+ *
+ * @param e exception thrown on service side.
+ * @hide
+ */
+ static RecoverableKeyStoreLoaderException fromServiceSpecificException(
+ ServiceSpecificException e) throws RecoverableKeyStoreLoaderException {
+ throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode);
+ }
- public RecoverableKeyStoreLoaderException(int errorCode, String message) {
+ private RecoverableKeyStoreLoaderException(int errorCode, String message) {
super(message);
- mErrorCode = errorCode;
}
+ /** Returns errorCode. */
public int getErrorCode() {
return mErrorCode;
}
+
+ /** @hide */
+ private static String getMessageFromErrorCode(int errorCode) {
+ switch (errorCode) {
+ case NO_ERROR:
+ return "OK";
+ case SYSTEM_ERROR:
+ return "System error";
+ case UNINITIALIZED_RECOVERY_PUBLIC_KEY:
+ return "Recovery service is not initialized";
+ case RATE_LIMIT_EXCEEDED:
+ return "Rate limit exceeded";
+ default:
+ return String.valueOf("Unknown error code " + errorCode);
+ }
+ }
}
/**
* Initializes key recovery service for the calling application. RecoverableKeyStoreLoader
- * randomly chooses one of the keys from the list
- * and keeps it to use for future key export operations. Collection of all keys
- * in the list must be signed by the provided {@code rootCertificateAlias}, which must also be
- * present in the list of root certificates preinstalled on the device. The random selection
- * allows RecoverableKeyStoreLoader to select which of a set of remote recovery service
- * devices will be used.
+ * randomly chooses one of the keys from the list and keeps it to use for future key export
+ * operations. Collection of all keys in the list must be signed by the provided {@code
+ * rootCertificateAlias}, which must also be present in the list of root certificates
+ * preinstalled on the device. The random selection allows RecoverableKeyStoreLoader to select
+ * which of a set of remote recovery service devices will be used.
*
* <p>In addition, RecoverableKeyStoreLoader enforces a delay of three months between
* consecutive initialization attempts, to limit the ability of an attacker to often switch
@@ -88,127 +131,200 @@ public class RecoverableKeyStoreLoader {
* @param rootCertificateAlias alias of a root certificate preinstalled on the device
* @param signedPublicKeyList binary blob a list of X509 certificates and signature
* @throws RecoverableKeyStoreLoaderException if signature is invalid, or key rotation was rate
- * limited.
+ * limited.
* @hide
*/
- public void initRecoveryService(@NonNull String rootCertificateAlias,
- @NonNull byte[] signedPublicKeyList)
+ public void initRecoveryService(
+ @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
throws RecoverableKeyStoreLoaderException {
- throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
- // TODO: extend widget/ILockSettings.aidl
- /* try {
- mBinder.initRecoveryService(rootCertificate, publicKeyList);
- } catch (RemoteException e) {
+ try {
+ mBinder.initRecoveryService(
+ rootCertificateAlias, signedPublicKeyList, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } */
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
}
/**
- * Returns data necessary to store all recoverable keys for given account.
- * Key material is encrypted with user secret and recovery public key.
+ * Returns data necessary to store all recoverable keys for given account. Key material is
+ * encrypted with user secret and recovery public key.
+ *
+ * @param account specific to Recovery agent.
+ * @return Data necessary to recover keystore.
+ * @hide
*/
public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
throws RecoverableKeyStoreLoaderException {
- throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+ try {
+ KeyStoreRecoveryData recoveryData =
+ mBinder.getRecoveryData(account, UserHandle.getCallingUserId());
+ return recoveryData;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
}
/**
* Server parameters used to generate new recovery key blobs. This value will be included in
- * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}.
- * The same value must be included in vaultParams {@link startRecoverySession}
+ * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}. The same value must be included
+ * in vaultParams {@link startRecoverySession}
*
+ * @param serverParameters included in recovery key blob.
* @see #getRecoveryData
* @throws RecoverableKeyStoreLoaderException If parameters rotation is rate limited.
+ * @hide
*/
- public void updateServerParameters(long serverParameters)
+ public void setServerParameters(long serverParameters)
throws RecoverableKeyStoreLoaderException {
- throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+ try {
+ mBinder.setServerParameters(serverParameters, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
}
/**
- * Updates recovery status for given keys.
- * It is used to notify keystore that key was successfully stored on the server or
- * there were an error. Returned as a part of KeyInfo data structure.
+ * Updates recovery status for given keys. It is used to notify keystore that key was
+ * successfully stored on the server or there were an error. Returned as a part of KeyInfo data
+ * structure.
*
* @param packageName Application whose recoverable keys' statuses are to be updated.
* @param aliases List of application-specific key aliases. If the array is empty, updates the
- * status for all existing recoverable keys.
+ * status for all existing recoverable keys.
* @param status Status specific to recovery agent.
*/
- public void setRecoveryStatus(@NonNull String packageName, @Nullable String[] aliases,
- int status) throws NameNotFoundException, RecoverableKeyStoreLoaderException {
- throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+ public void setRecoveryStatus(
+ @NonNull String packageName, @Nullable String[] aliases, int status)
+ throws NameNotFoundException, RecoverableKeyStoreLoaderException {
+ try {
+ mBinder.setRecoveryStatus(packageName, aliases, status, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
}
/**
- * Specifies a set of secret types used for end-to-end keystore encryption.
- * Knowing all of them is necessary to recover data.
+ * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
+ * is necessary to recover data.
*
- * @param secretTypes {@link KeyStoreRecoveryMetadata#TYPE_LOCKSCREEN} or
- * {@link KeyStoreRecoveryMetadata#TYPE_CUSTOM_PASSWORD}
+ * @param secretTypes {@link KeyStoreRecoveryMetadata#TYPE_LOCKSCREEN} or {@link
+ * KeyStoreRecoveryMetadata#TYPE_CUSTOM_PASSWORD}
*/
- public void setRecoverySecretTypes(@NonNull @KeyStoreRecoveryMetadata.UserSecretType
- int[] secretTypes) throws RecoverableKeyStoreLoaderException {
- throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+ public void setRecoverySecretTypes(
+ @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes)
+ throws RecoverableKeyStoreLoaderException {
+ try {
+ mBinder.setRecoverySecretTypes(secretTypes, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
}
/**
- * Defines a set of secret types used for end-to-end keystore encryption.
- * Knowing all of them is necessary to generate KeyStoreRecoveryData.
+ * Defines a set of secret types used for end-to-end keystore encryption. Knowing all of them is
+ * necessary to generate KeyStoreRecoveryData.
+ *
+ * @return list of recovery secret types
* @see KeyStoreRecoveryData
*/
public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getRecoverySecretTypes()
throws RecoverableKeyStoreLoaderException {
- throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+ try {
+ return mBinder.getRecoverySecretTypes(UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
}
/**
* Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
- * When user enters a secret of a pending type
- * {@link #recoverySecretAvailable} should be called.
+ * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
+ * called.
+ *
+ * @return list of recovery secret types
*/
public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes()
throws RecoverableKeyStoreLoaderException {
- throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+ try {
+ return mBinder.getPendingRecoverySecretTypes(UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
}
/**
- * Method notifies KeyStore that a user-generated secret is available.
- * This method generates a symmetric session key which a trusted remote device can use
- * to return a recovery key.
- * Caller should use {@link KeyStoreRecoveryMetadata#clearSecret} to override the secret value
- * in memory.
+ * Method notifies KeyStore that a user-generated secret is available. This method generates a
+ * symmetric session key which a trusted remote device can use to return a recovery key. Caller
+ * should use {@link KeyStoreRecoveryMetadata#clearSecret} to override the secret value in
+ * memory.
*
- * @param recoverySecret user generated secret together with parameters necessary to
- * regenerate it on a new device.
+ * @param recoverySecret user generated secret together with parameters necessary to regenerate
+ * it on a new device.
*/
public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret)
throws RecoverableKeyStoreLoaderException {
- throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+ try {
+ mBinder.recoverySecretAvailable(recoverySecret, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
}
/**
* Initializes recovery session and returns a blob with proof of recovery secrets possession.
- * The method generates symmetric key for a session, which trusted remote device can use
- * to return recovery key.
+ * The method generates symmetric key for a session, which trusted remote device can use to
+ * return recovery key.
*
* @param sessionId ID for recovery session.
- * @param verifierPublicKey Certificate with Public key used to create the recovery blob on
- * the source device. Keystore will verify the certificate using root of trust.
+ * @param verifierPublicKey Certificate with Public key used to create the recovery blob on the
+ * source device. Keystore will verify the certificate using root of trust.
* @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
- * Used to limit number of guesses.
+ * Used to limit number of guesses.
* @param vaultChallenge Data passed from server for this recovery session and used to prevent
- * replay attacks
+ * replay attacks
* @param secrets Secrets provided by user, the method only uses type and secret fields.
- * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and
- * contains a proof of user secrets, session symmetric key and parameters necessary to identify
- * the counter with the number of failed recovery attempts.
+ * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and contains
+ * a proof of user secrets, session symmetric key and parameters necessary to identify the
+ * counter with the number of failed recovery attempts.
*/
- public @NonNull byte[] startRecoverySession(@NonNull String sessionId,
- @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
- @NonNull byte[] vaultChallenge, @NonNull List<KeyStoreRecoveryMetadata> secrets)
+ public @NonNull byte[] startRecoverySession(
+ @NonNull String sessionId,
+ @NonNull byte[] verifierPublicKey,
+ @NonNull byte[] vaultParams,
+ @NonNull byte[] vaultChallenge,
+ @NonNull List<KeyStoreRecoveryMetadata> secrets)
throws RecoverableKeyStoreLoaderException {
- throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+ try {
+ byte[] recoveryClaim =
+ mBinder.startRecoverySession(
+ sessionId,
+ verifierPublicKey,
+ vaultParams,
+ vaultChallenge,
+ secrets,
+ UserHandle.getCallingUserId());
+ return recoveryClaim;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
}
/**
@@ -217,12 +333,21 @@ public class RecoverableKeyStoreLoader {
* @param sessionId Id for recovery session, same as in = {@link startRecoverySession}.
* @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
* @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
- * and session. KeyStore only uses package names from the application info in
- * {@link KeyEntryRecoveryData}. Caller is responsibility to perform certificates check.
+ * and session. KeyStore only uses package names from the application info in {@link
+ * KeyEntryRecoveryData}. Caller is responsibility to perform certificates check.
*/
- public void recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob,
+ public void recoverKeys(
+ @NonNull String sessionId,
+ @NonNull byte[] recoveryKeyBlob,
@NonNull List<KeyEntryRecoveryData> applicationKeys)
throws RecoverableKeyStoreLoaderException {
- throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+ try {
+ mBinder.recoverKeys(
+ sessionId, recoveryKeyBlob, applicationKeys, UserHandle.getCallingUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (ServiceSpecificException e) {
+ throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
+ }
}
}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index ee16ab609a2a..164a74509ed7 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -17,6 +17,10 @@
package com.android.internal.widget;
import android.app.trust.IStrongAuthTracker;
+import android.os.Bundle;
+import android.security.recoverablekeystore.KeyEntryRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.VerifyCredentialResponse;
@@ -52,4 +56,22 @@ interface ILockSettings {
boolean setLockCredentialWithToken(String credential, int type, long tokenHandle,
in byte[] token, int requestedQuality, int userId);
void unlockUserWithToken(long tokenHandle, in byte[] token, int userId);
+
+ // RecoverableKeyStoreLoader methods.
+ // {@code ServiceSpecificException} may be thrown to signal an error, which caller can
+ // convert to {@code RecoverableKeyStoreLoader}.
+ void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList,
+ int userId);
+ KeyStoreRecoveryData getRecoveryData(in byte[] account, int userId);
+ void setServerParameters(long serverParameters, int userId);
+ void setRecoveryStatus(in String packageName, in String[] aliases, int status, int userId);
+ void setRecoverySecretTypes(in int[] secretTypes, int userId);
+ int[] getRecoverySecretTypes(int userId);
+ int[] getPendingRecoverySecretTypes(int userId);
+ void recoverySecretAvailable(in KeyStoreRecoveryMetadata recoverySecret, int userId);
+ byte[] startRecoverySession(in String sessionId,
+ in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
+ in List<KeyStoreRecoveryMetadata> secrets, int userId);
+ void recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
+ in List<KeyEntryRecoveryData> applicationKeys, int userId);
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 60f451ab437e..aa55930697ba 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -28,6 +28,8 @@ import static com.android.internal.widget.LockPatternUtils.USER_FRP;
import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.IActivityManager;
@@ -60,6 +62,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.StrictMode;
import android.os.SystemProperties;
@@ -75,6 +78,10 @@ import android.security.keystore.AndroidKeyStoreProvider;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore.UserNotAuthenticatedException;
+import android.security.recoverablekeystore.KeyEntryRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
+import android.security.recoverablekeystore.RecoverableKeyStoreLoader.RecoverableKeyStoreLoaderException;
import android.service.gatekeeper.GateKeeperResponse;
import android.service.gatekeeper.IGateKeeperService;
import android.text.TextUtils;
@@ -95,9 +102,10 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
import com.android.server.SystemService;
import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
+import com.android.server.locksettings.LockSettingsStorage.PersistentData;
+import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
-import com.android.server.locksettings.LockSettingsStorage.PersistentData;
import libcore.util.HexEncoding;
@@ -169,6 +177,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private final KeyStore mKeyStore;
+ private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
+
private boolean mFirstCallToVold;
protected IGateKeeperService mGateKeeperService;
@@ -367,6 +377,10 @@ public class LockSettingsService extends ILockSettings.Stub {
return KeyStore.getInstance();
}
+ public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
+ return RecoverableKeyStoreManager.getInstance(mContext);
+ }
+
public IStorageManager getStorageManager() {
final IBinder service = ServiceManager.getService("mount");
if (service != null) {
@@ -393,6 +407,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mInjector = injector;
mContext = injector.getContext();
mKeyStore = injector.getKeyStore();
+ mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
mHandler = injector.getHandler();
mStrongAuth = injector.getStrongAuth();
mActivityManager = injector.getActivityManager();
@@ -1914,6 +1929,72 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
+ @Override
+ public void initRecoveryService(@NonNull String rootCertificateAlias,
+ @NonNull byte[] signedPublicKeyList, int userId)
+ throws RemoteException {
+ mRecoverableKeyStoreManager.initRecoveryService(rootCertificateAlias,
+ signedPublicKeyList, userId);
+ }
+
+ @Override
+ public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account, int userId)
+ throws RemoteException {
+ return mRecoverableKeyStoreManager.getRecoveryData(account, userId);
+ }
+
+ @Override
+ public void setServerParameters(long serverParameters, int userId) throws RemoteException {
+ mRecoverableKeyStoreManager.setServerParameters(serverParameters, userId);
+ }
+
+ @Override
+ public void setRecoveryStatus(@NonNull String packageName, @Nullable String[] aliases,
+ int status, int userId) throws RemoteException {
+ mRecoverableKeyStoreManager.setRecoveryStatus(packageName, aliases, status, userId);
+ }
+
+ @Override
+ public void setRecoverySecretTypes(@NonNull @KeyStoreRecoveryMetadata.UserSecretType
+ int[] secretTypes, int userId) throws RemoteException {
+ mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes, userId);
+ }
+
+ @Override
+ public int[] getRecoverySecretTypes(int userId) throws RemoteException {
+ return mRecoverableKeyStoreManager.getRecoverySecretTypes(userId);
+
+ }
+
+ @Override
+ public int[] getPendingRecoverySecretTypes(int userId) throws RemoteException {
+ throw new SecurityException("Not implemented");
+ }
+
+ @Override
+ public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret,
+ int userId)
+ throws RemoteException {
+ mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret, userId);
+ }
+
+ @Override
+ public byte[] startRecoverySession(@NonNull String sessionId,
+ @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
+ @NonNull byte[] vaultChallenge, @NonNull List<KeyStoreRecoveryMetadata> secrets,
+ int userId) throws RemoteException {
+ return mRecoverableKeyStoreManager.startRecoverySession(sessionId, verifierPublicKey,
+ vaultParams, vaultChallenge, secrets, userId);
+ }
+
+ @Override
+ public void recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob,
+ @NonNull List<KeyEntryRecoveryData> applicationKeys, int userId)
+ throws RemoteException {
+ mRecoverableKeyStoreManager.recoverKeys(sessionId, recoveryKeyBlob, applicationKeys,
+ userId);
+ }
+
private static final String[] VALID_SETTINGS = new String[] {
LockPatternUtils.LOCKOUT_PERMANENT_KEY,
LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
new file mode 100644
index 000000000000..e459f288b4c0
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2017 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.recoverablekeystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.os.UserHandle;
+
+import android.security.recoverablekeystore.KeyEntryRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryData;
+import android.security.recoverablekeystore.KeyStoreRecoveryMetadata;
+import android.security.recoverablekeystore.RecoverableKeyStoreLoader;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class with {@link RecoverableKeyStoreLoader} API implementation and internal methods to interact
+ * with {@code LockSettingsService}.
+ *
+ * @hide
+ */
+public class RecoverableKeyStoreManager {
+ private static final String TAG = "RecoverableKeyStoreManager";
+
+ private static RecoverableKeyStoreManager mInstance;
+ private Context mContext;
+
+ /**
+ * Returns a new or existing instance.
+ *
+ * @hide
+ */
+ public static synchronized RecoverableKeyStoreManager getInstance(Context mContext) {
+ if (mInstance == null) {
+ mInstance = new RecoverableKeyStoreManager(mContext);
+ }
+ return mInstance;
+ }
+
+ @VisibleForTesting
+ RecoverableKeyStoreManager(Context context) {
+ mContext = context;
+ }
+
+ public int initRecoveryService(
+ @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList, int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ // TODO open /system/etc/security/... cert file
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Gets all data necessary to recover application keys on new device.
+ *
+ * @return recovery data
+ * @hide
+ */
+ public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account, int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ final int callingUid = Binder.getCallingUid(); // Recovery agent uid.
+ final int callingUserId = UserHandle.getCallingUserId();
+ final long callingIdentiy = Binder.clearCallingIdentity();
+ try {
+ // TODO: Return the latest snapshot for the calling recovery agent.
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentiy);
+ }
+
+ // KeyStoreRecoveryData without application keys and empty recovery blob.
+ KeyStoreRecoveryData recoveryData =
+ new KeyStoreRecoveryData(
+ /*snapshotVersion=*/ 1,
+ new ArrayList<KeyStoreRecoveryMetadata>(),
+ new ArrayList<KeyEntryRecoveryData>(),
+ /*encryptedRecoveryKeyBlob=*/ new byte[] {});
+ throw new ServiceSpecificException(
+ RecoverableKeyStoreLoader.UNINITIALIZED_RECOVERY_PUBLIC_KEY);
+ }
+
+ public void setServerParameters(long serverParameters, int userId) throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ public void setRecoveryStatus(
+ @NonNull String packageName, @Nullable String[] aliases, int status, int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Sets recovery secrets list used by all recovery agents for given {@code userId}
+ *
+ * @hide
+ */
+ public void setRecoverySecretTypes(
+ @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] secretTypes, int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Gets secret types necessary to create Recovery Data.
+ *
+ * @return secret types
+ * @hide
+ */
+ public int[] getRecoverySecretTypes(int userId) throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Gets secret types RecoverableKeyStoreLoaders is waiting for to create new Recovery Data.
+ *
+ * @return secret types
+ * @hide
+ */
+ public int[] getPendingRecoverySecretTypes(int userId) throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ public void recoverySecretAvailable(
+ @NonNull KeyStoreRecoveryMetadata recoverySecret, int userId) throws RemoteException {
+ final int callingUid = Binder.getCallingUid(); // Recovery agent uid.
+ if (recoverySecret.getLockScreenUiFormat() == KeyStoreRecoveryMetadata.TYPE_LOCKSCREEN) {
+ throw new SecurityException(
+ "Caller " + callingUid + "is not allowed to set lock screen secret");
+ }
+ checkRecoverKeyStorePermission();
+ // TODO: add hook from LockSettingsService to set lock screen secret.
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Initializes recovery session.
+ *
+ * @return recovery claim
+ * @hide
+ */
+ public byte[] startRecoverySession(
+ @NonNull String sessionId,
+ @NonNull byte[] verifierPublicKey,
+ @NonNull byte[] vaultParams,
+ @NonNull byte[] vaultChallenge,
+ @NonNull List<KeyStoreRecoveryMetadata> secrets,
+ int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ public void recoverKeys(
+ @NonNull String sessionId,
+ @NonNull byte[] recoveryKeyBlob,
+ @NonNull List<KeyEntryRecoveryData> applicationKeys,
+ int userId)
+ throws RemoteException {
+ checkRecoverKeyStorePermission();
+ throw new UnsupportedOperationException();
+ }
+
+ /** This function can only be used inside LockSettingsService. */
+ public void lockScreenSecretAvailable(
+ @KeyStoreRecoveryMetadata.LockScreenUiFormat int type,
+ String unencryptedPassword,
+ int userId) {
+ // TODO: compute SHA256 or Argon2id depending on secret type.
+ throw new UnsupportedOperationException();
+ }
+
+ /** This function can only be used inside LockSettingsService. */
+ public void lockScreenSecretChanged(
+ @KeyStoreRecoveryMetadata.LockScreenUiFormat int type,
+ @Nullable String unencryptedPassword,
+ int userId) {
+ throw new UnsupportedOperationException();
+ }
+
+ private void checkRecoverKeyStorePermission() {
+ mContext.enforceCallingOrSelfPermission(
+ RecoverableKeyStoreLoader.PERMISSION_RECOVER_KEYSTORE,
+ "Caller " + Binder.getCallingUid() + " doesn't have RecoverKeyStore permission.");
+ }
+}