import java.util.Map;
/**
- * A wrapper around KeyStore which lets key be exported to trusted hardware on server side and
- * recovered later.
+ * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by
+ * other Android devices belonging to the user. The exported keychain is protected by the user's
+ * lock screen.
+ *
+ * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible
+ * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force
+ * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10).
+ * After that number of incorrect guesses, the trusted hardware no longer allows access to the
+ * key chain.
+ *
+ * <p>For now only the recovery agent itself is able to create keys, so it is expected that the
+ * recovery agent is itself the system app.
+ *
+ * <p>A recovery agent requires the privileged permission
+ * {@code android.Manifest.permission#RECOVER_KEYSTORE}.
*
* @hide
*/
-public class RecoveryManager {
+public class RecoveryController {
private static final String TAG = "RecoveryController";
/** Key has been successfully synced. */
private final ILockSettings mBinder;
- private RecoveryManager(ILockSettings binder) {
+ private RecoveryController(ILockSettings binder) {
mBinder = binder;
}
/**
* Gets a new instance of the class.
*/
- public static RecoveryManager getInstance() {
+ public static RecoveryController getInstance() {
ILockSettings lockSettings =
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
- return new RecoveryManager(lockSettings);
+ return new RecoveryController(lockSettings);
}
/**
- * Initializes key recovery service for the calling application. RecoveryManager
+ * Initializes key recovery service for the calling application. RecoveryController
* 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 RecoveryManager to select
+ * preinstalled on the device. The random selection allows RecoveryController to select
* which of a set of remote recovery service devices will be used.
*
- * <p>In addition, RecoveryManager enforces a delay of three months between
+ * <p>In addition, RecoveryController enforces a delay of three months between
* consecutive initialization attempts, to limit the ability of an attacker to often switch
* remote recovery devices and significantly increase number of recovery attempts.
*
* 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 Encoded {@code java.security.cert.X509Certificate} with Public key
* used to create the recovery blob on the source device.
* Keystore will verify the certificate using root of trust.
import java.security.GeneralSecurityException;
/**
- * Base exception for errors thrown by {@link RecoveryManager}.
+ * Base exception for errors thrown by {@link RecoveryController}.
*
* @hide
*/
private static final int SESSION_ID_LENGTH_BYTES = 16;
private final String mSessionId;
- private final RecoveryManager mRecoveryManager;
+ private final RecoveryController mRecoveryController;
- private RecoverySession(RecoveryManager recoveryManager, String sessionId) {
- mRecoveryManager = recoveryManager;
+ private RecoverySession(RecoveryController recoveryController, String sessionId) {
+ mRecoveryController = recoveryController;
mSessionId = sessionId;
}
/**
* A new session, started by {@code recoveryManager}.
*/
- static RecoverySession newInstance(RecoveryManager recoveryManager) {
- return new RecoverySession(recoveryManager, newSessionId());
+ static RecoverySession newInstance(RecoveryController recoveryController) {
+ return new RecoverySession(recoveryController, newSessionId());
}
/**
@Override
public void close() {
- mRecoveryManager.closeSession(this);
+ mRecoveryController.closeSession(this);
}
}
in byte[] token, int requestedQuality, int userId);
void unlockUserWithToken(long tokenHandle, in byte[] token, int userId);
- // Keystore RecoveryManager methods.
+ // Keystore RecoveryController methods.
// {@code ServiceSpecificException} may be thrown to signal an error, which caller can
// convert to {@code RecoveryManagerException}.
void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
package com.android.server.locksettings.recoverablekeystore;
-import static android.security.keystore.RecoveryManager.ERROR_BAD_CERTIFICATE_FORMAT;
-import static android.security.keystore.RecoveryManager.ERROR_DECRYPTION_FAILED;
-import static android.security.keystore.RecoveryManager.ERROR_INSECURE_USER;
-import static android.security.keystore.RecoveryManager.ERROR_NO_SNAPSHOT_PENDING;
-import static android.security.keystore.RecoveryManager.ERROR_SERVICE_INTERNAL_ERROR;
-import static android.security.keystore.RecoveryManager.ERROR_SESSION_EXPIRED;
+import static android.security.keystore.RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT;
+import static android.security.keystore.RecoveryController.ERROR_DECRYPTION_FAILED;
+import static android.security.keystore.RecoveryController.ERROR_INSECURE_USER;
+import static android.security.keystore.RecoveryController.ERROR_NO_SNAPSHOT_PENDING;
+import static android.security.keystore.RecoveryController.ERROR_SERVICE_INTERNAL_ERROR;
+import static android.security.keystore.RecoveryController.ERROR_SESSION_EXPIRED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.security.keystore.KeychainProtectionParams;
import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.RecoveryController;
import android.security.keystore.WrappedApplicationKey;
-import android.security.keystore.RecoveryManager;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import javax.crypto.AEADBadTagException;
/**
- * Class with {@link RecoveryManager} API implementation and internal methods to interact
+ * Class with {@link RecoveryController} API implementation and internal methods to interact
* with {@code LockSettingsService}.
*
* @hide
package com.android.server.locksettings.recoverablekeystore;
+import android.security.keystore.RecoveryController;
import android.util.Log;
-import android.security.keystore.RecoveryManager;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
/*nonce=*/ cipher.getIV(),
/*keyMaterial=*/ encryptedKeyMaterial,
/*platformKeyGenerationId=*/ wrappingKey.getGenerationId(),
- RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
}
/**
* @param keyMaterial The encrypted bytes of the key material.
* @param platformKeyGenerationId The generation ID of the key used to wrap this key.
*
- * @see RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS
+ * @see RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS
* @hide
*/
public WrappedKey(byte[] nonce, byte[] keyMaterial, int platformKeyGenerationId) {
mNonce = nonce;
mKeyMaterial = keyMaterial;
mPlatformKeyGenerationId = platformKeyGenerationId;
- mRecoveryStatus = RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS;
+ mRecoveryStatus = RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS;
}
/**
import org.junit.runner.RunWith;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.security.keystore.RecoveryManager;
+import android.security.keystore.RecoveryController;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid);
assertThat(statuses).hasSize(3);
- assertThat(statuses).containsEntry(alias, RecoveryManager.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+ assertThat(statuses).containsEntry(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
assertThat(statuses).containsEntry(alias2, status);
assertThat(statuses).containsEntry(alias3, status);