Merge "Migrate recoverablekeystore to KeyStore V2." am: dbf8a7c1bd am: 26085ee2ff

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1603470

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ie5931135d2d5d4acdfe18284344f99775f64ce44
This commit is contained in:
Dmitry Dementyev
2021-02-26 10:03:11 +00:00
committed by Automerger Merge Worker
8 changed files with 105 additions and 45 deletions

View File

@@ -27,8 +27,11 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.security.KeyStore;
import android.security.keystore.AndroidKeyStoreProvider;
import android.security.KeyStore2;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore2.AndroidKeyStoreProvider;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
import com.android.internal.widget.ILockSettings;
@@ -709,10 +712,34 @@ public class RecoveryController {
*/
@NonNull Key getKeyFromGrant(@NonNull String grantAlias)
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
mKeyStore,
grantAlias,
KeyStore.UID_SELF);
if (grantAlias.startsWith(APPLICATION_KEY_GRANT_PREFIX)) {
return AndroidKeyStoreProvider
.loadAndroidKeyStoreSecretKeyFromKeystore(
KeyStore2.getInstance(),
getGrantDescriptor(grantAlias));
}
// TODO(b/171305545): remove KeyStore1 logic.
return android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
mKeyStore,
grantAlias,
KeyStore.UID_SELF);
}
private static final String APPLICATION_KEY_GRANT_PREFIX = "recoverable_key:";
private static @Nullable KeyDescriptor getGrantDescriptor(String grantAlias) {
KeyDescriptor result = new KeyDescriptor();
result.domain = Domain.GRANT;
result.blob = null;
result.alias = null;
try {
result.nspace = Long.parseUnsignedLong(
grantAlias.substring(APPLICATION_KEY_GRANT_PREFIX.length()), 16);
} catch (NumberFormatException e) {
return null;
}
return result;
}
/**

View File

@@ -43,6 +43,7 @@ import java.security.interfaces.RSAPublicKey;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
/**
* A provider focused on providing JCA interfaces for the Android KeyStore.
@@ -299,13 +300,26 @@ public class AndroidKeyStoreProvider extends Provider {
}
}
/** @hide **/
@NonNull
public static SecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
@NonNull KeyStore2 keyStore, @NonNull KeyDescriptor descriptor)
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
AndroidKeyStoreKey key =
loadAndroidKeyStoreKeyFromKeystore(keyStore, descriptor);
if (key instanceof SecretKey) {
return (SecretKey) key;
} else {
throw new UnrecoverableKeyException("No secret key found by the given alias.");
}
}
@NonNull
private static AndroidKeyStoreSecretKey makeAndroidKeyStoreSecretKeyFromKeyEntryResponse(
@NonNull KeyDescriptor descriptor,
@NonNull KeyEntryResponse response, int algorithm, int digest)
throws UnrecoverableKeyException {
@KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
try {
keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
@@ -337,7 +351,6 @@ public class AndroidKeyStoreProvider extends Provider {
public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
@NonNull KeyStore2 keyStore, @NonNull String alias, int namespace)
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
KeyDescriptor descriptor = new KeyDescriptor();
if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
descriptor.nspace = KeyProperties.NAMESPACE_APPLICATION; // ignored;

View File

@@ -489,8 +489,8 @@ public class LockSettingsService extends ILockSettings.Stub {
return KeyStore.getInstance();
}
public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
return RecoverableKeyStoreManager.getInstance(mContext, keyStore);
public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
return RecoverableKeyStoreManager.getInstance(mContext);
}
public IStorageManager getStorageManager() {
@@ -571,7 +571,7 @@ public class LockSettingsService extends ILockSettings.Stub {
mInjector = injector;
mContext = injector.getContext();
mKeyStore = injector.getKeyStore();
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager(mKeyStore);
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
mHandler = injector.getHandler(injector.getServiceThread());
mStrongAuth = injector.getStrongAuth();
mActivityManager = injector.getActivityManager();

View File

@@ -16,8 +16,6 @@
package com.android.server.locksettings.recoverablekeystore;
import android.security.keystore2.AndroidKeyStoreProvider;
import java.io.IOException;
import java.security.Key;
import java.security.KeyStore;
@@ -31,23 +29,10 @@ import java.security.cert.CertificateException;
*/
public class KeyStoreProxyImpl implements KeyStoreProxy {
public static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private final KeyStore mKeyStore;
/**
* TODO This function redirects keystore access to the legacy keystore during a transitional
* phase during which not all calling code has been adjusted to use Keystore 2.0.
* This can be reverted to a constant of "AndroidKeyStore" when b/171305684 is complete.
* The specific bug for this component is b/171305545.
*/
static String androidKeystoreProviderName() {
if (AndroidKeyStoreProvider.isInstalled()) {
return "AndroidKeyStoreLegacy";
} else {
return "AndroidKeyStore";
}
}
/**
* A new instance, delegating to {@code keyStore}.
*/
@@ -84,7 +69,7 @@ public class KeyStoreProxyImpl implements KeyStoreProxy {
* @throws KeyStoreException if there was a problem getting or initializing the key store.
*/
public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName());
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
try {
keyStore.load(/*param=*/ null);
} catch (CertificateException | IOException | NoSuchAlgorithmException e) {

View File

@@ -484,7 +484,7 @@ public class PlatformKeyManager {
* @throws KeyStoreException if there was a problem getting or initializing the key store.
*/
private static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.androidKeystoreProviderName());
KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.ANDROID_KEY_STORE_PROVIDER);
try {
keyStore.load(/*param=*/ null);
} catch (CertificateException | IOException | NoSuchAlgorithmException e) {

View File

@@ -34,7 +34,6 @@ import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.security.KeyStore;
import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryCertPath;
@@ -110,14 +109,14 @@ public class RecoverableKeyStoreManager {
* @hide
*/
public static synchronized RecoverableKeyStoreManager
getInstance(Context context, KeyStore keystore) {
getInstance(Context context) {
if (mInstance == null) {
RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context);
PlatformKeyManager platformKeyManager;
ApplicationKeyStorage applicationKeyStorage;
try {
platformKeyManager = PlatformKeyManager.getInstance(context, db);
applicationKeyStorage = ApplicationKeyStorage.getInstance(keystore);
applicationKeyStorage = ApplicationKeyStorage.getInstance();
} catch (NoSuchAlgorithmException e) {
// Impossible: all algorithms must be supported by AOSP
throw new RuntimeException(e);

View File

@@ -21,9 +21,13 @@ import static android.security.keystore.recovery.RecoveryController.ERROR_SERVIC
import android.annotation.Nullable;
import android.os.ServiceSpecificException;
import android.security.Credentials;
import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.KeyStore;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyPermission;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -47,32 +51,37 @@ public class ApplicationKeyStorage {
private static final String APPLICATION_KEY_ALIAS_PREFIX =
"com.android.server.locksettings.recoverablekeystore/application/";
private static final String APPLICATION_KEY_GRANT_PREFIX = "recoverable_key:";
private final KeyStoreProxy mKeyStore;
private final KeyStore mKeystoreService;
public static ApplicationKeyStorage getInstance(KeyStore keystoreService)
/**
* Creates a new instance.
*/
public static ApplicationKeyStorage getInstance()
throws KeyStoreException {
return new ApplicationKeyStorage(
new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()),
keystoreService);
new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()));
}
@VisibleForTesting
ApplicationKeyStorage(KeyStoreProxy keyStore, KeyStore keystoreService) {
ApplicationKeyStorage(KeyStoreProxy keyStore) {
mKeyStore = keyStore;
mKeystoreService = keystoreService;
}
/**
* Returns grant alias, valid in Applications namespace.
* Returns String representation of {@code KeyDescriptor} valid in application's namespace.
*/
public @Nullable String getGrantAlias(int userId, int uid, String alias) {
// Aliases used by {@link KeyStore} are different than used by public API.
// {@code USER_PRIVATE_KEY} prefix is used secret keys.
Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias));
String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias);
return mKeystoreService.grant(keystoreAlias, uid);
String keystoreAlias = getInternalAlias(userId, uid, alias);
if (useKeyStore2()) {
return makeKeystoreEngineGrantString(uid, keystoreAlias);
} else {
// Aliases used by {@link KeyStore} are different than used by public API.
// {@code USER_PRIVATE_KEY} prefix is used secret keys.
return KeyStore.getInstance().grant(Credentials.USER_PRIVATE_KEY + keystoreAlias, uid);
}
}
public void setSymmetricKeyEntry(int userId, int uid, String alias, byte[] secretKey)
@@ -117,4 +126,31 @@ public class ApplicationKeyStorage {
private String getInternalAlias(int userId, int uid, String alias) {
return APPLICATION_KEY_ALIAS_PREFIX + userId + "/" + uid + "/" + alias;
}
private String makeKeystoreEngineGrantString(int uid, String alias) {
if (alias == null) {
return null;
}
KeyDescriptor key = new KeyDescriptor();
key.domain = Domain.APP;
key.nspace = KeyProperties.NAMESPACE_APPLICATION;
key.alias = alias;
key.blob = null;
int grantAccessVector = KeyPermission.USE | KeyPermission.GET_INFO | KeyPermission.DELETE;
try {
key = KeyStore2.getInstance().grant(key, uid, grantAccessVector);
} catch (android.security.KeyStoreException e) {
Log.e(TAG, "Failed to get grant for KeyStore key.", e);
throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
}
return String.format("%s%016X", APPLICATION_KEY_GRANT_PREFIX, key.nspace);
}
private static boolean useKeyStore2() {
return android.security.keystore2.AndroidKeyStoreProvider.isInstalled();
}
}

View File

@@ -150,7 +150,7 @@ public class LockSettingsServiceTestable extends LockSettingsService {
}
@Override
public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
return mRecoverableKeyStoreManager;
}