Merge "Migrate recoverablekeystore to KeyStore V2."

This commit is contained in:
Dmitry Dementyev
2021-02-26 06:13:35 +00:00
committed by Gerrit Code Review
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.ServiceManager;
import android.os.ServiceSpecificException; import android.os.ServiceSpecificException;
import android.security.KeyStore; import android.security.KeyStore;
import android.security.keystore.AndroidKeyStoreProvider; import android.security.KeyStore2;
import android.security.keystore.KeyPermanentlyInvalidatedException; 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; import com.android.internal.widget.ILockSettings;
@@ -709,10 +712,34 @@ public class RecoveryController {
*/ */
@NonNull Key getKeyFromGrant(@NonNull String grantAlias) @NonNull Key getKeyFromGrant(@NonNull String grantAlias)
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore( if (grantAlias.startsWith(APPLICATION_KEY_GRANT_PREFIX)) {
mKeyStore, return AndroidKeyStoreProvider
grantAlias, .loadAndroidKeyStoreSecretKeyFromKeystore(
KeyStore.UID_SELF); 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.Cipher;
import javax.crypto.Mac; import javax.crypto.Mac;
import javax.crypto.SecretKey;
/** /**
* A provider focused on providing JCA interfaces for the Android KeyStore. * 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 @NonNull
private static AndroidKeyStoreSecretKey makeAndroidKeyStoreSecretKeyFromKeyEntryResponse( private static AndroidKeyStoreSecretKey makeAndroidKeyStoreSecretKeyFromKeyEntryResponse(
@NonNull KeyDescriptor descriptor, @NonNull KeyDescriptor descriptor,
@NonNull KeyEntryResponse response, int algorithm, int digest) @NonNull KeyEntryResponse response, int algorithm, int digest)
throws UnrecoverableKeyException { throws UnrecoverableKeyException {
@KeyProperties.KeyAlgorithmEnum String keyAlgorithmString; @KeyProperties.KeyAlgorithmEnum String keyAlgorithmString;
try { try {
keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm( keyAlgorithmString = KeyProperties.KeyAlgorithm.fromKeymasterSecretKeyAlgorithm(
@@ -337,7 +351,6 @@ public class AndroidKeyStoreProvider extends Provider {
public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore( public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
@NonNull KeyStore2 keyStore, @NonNull String alias, int namespace) @NonNull KeyStore2 keyStore, @NonNull String alias, int namespace)
throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException { throws UnrecoverableKeyException, KeyPermanentlyInvalidatedException {
KeyDescriptor descriptor = new KeyDescriptor(); KeyDescriptor descriptor = new KeyDescriptor();
if (namespace == KeyProperties.NAMESPACE_APPLICATION) { if (namespace == KeyProperties.NAMESPACE_APPLICATION) {
descriptor.nspace = KeyProperties.NAMESPACE_APPLICATION; // ignored; descriptor.nspace = KeyProperties.NAMESPACE_APPLICATION; // ignored;

View File

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

View File

@@ -16,8 +16,6 @@
package com.android.server.locksettings.recoverablekeystore; package com.android.server.locksettings.recoverablekeystore;
import android.security.keystore2.AndroidKeyStoreProvider;
import java.io.IOException; import java.io.IOException;
import java.security.Key; import java.security.Key;
import java.security.KeyStore; import java.security.KeyStore;
@@ -31,23 +29,10 @@ import java.security.cert.CertificateException;
*/ */
public class KeyStoreProxyImpl implements KeyStoreProxy { public class KeyStoreProxyImpl implements KeyStoreProxy {
public static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
private final KeyStore mKeyStore; 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}. * 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. * @throws KeyStoreException if there was a problem getting or initializing the key store.
*/ */
public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException { public static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
KeyStore keyStore = KeyStore.getInstance(androidKeystoreProviderName()); KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
try { try {
keyStore.load(/*param=*/ null); keyStore.load(/*param=*/ null);
} catch (CertificateException | IOException | NoSuchAlgorithmException e) { } 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. * @throws KeyStoreException if there was a problem getting or initializing the key store.
*/ */
private static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException { private static KeyStore getAndLoadAndroidKeyStore() throws KeyStoreException {
KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.androidKeystoreProviderName()); KeyStore keyStore = KeyStore.getInstance(KeyStoreProxyImpl.ANDROID_KEY_STORE_PROVIDER);
try { try {
keyStore.load(/*param=*/ null); keyStore.load(/*param=*/ null);
} catch (CertificateException | IOException | NoSuchAlgorithmException e) { } catch (CertificateException | IOException | NoSuchAlgorithmException e) {

View File

@@ -34,7 +34,6 @@ import android.os.Binder;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceSpecificException; import android.os.ServiceSpecificException;
import android.os.UserHandle; import android.os.UserHandle;
import android.security.KeyStore;
import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.KeyChainProtectionParams;
import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyChainSnapshot;
import android.security.keystore.recovery.RecoveryCertPath; import android.security.keystore.recovery.RecoveryCertPath;
@@ -110,14 +109,14 @@ public class RecoverableKeyStoreManager {
* @hide * @hide
*/ */
public static synchronized RecoverableKeyStoreManager public static synchronized RecoverableKeyStoreManager
getInstance(Context context, KeyStore keystore) { getInstance(Context context) {
if (mInstance == null) { if (mInstance == null) {
RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context); RecoverableKeyStoreDb db = RecoverableKeyStoreDb.newInstance(context);
PlatformKeyManager platformKeyManager; PlatformKeyManager platformKeyManager;
ApplicationKeyStorage applicationKeyStorage; ApplicationKeyStorage applicationKeyStorage;
try { try {
platformKeyManager = PlatformKeyManager.getInstance(context, db); platformKeyManager = PlatformKeyManager.getInstance(context, db);
applicationKeyStorage = ApplicationKeyStorage.getInstance(keystore); applicationKeyStorage = ApplicationKeyStorage.getInstance();
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
// Impossible: all algorithms must be supported by AOSP // Impossible: all algorithms must be supported by AOSP
throw new RuntimeException(e); 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.annotation.Nullable;
import android.os.ServiceSpecificException; import android.os.ServiceSpecificException;
import android.security.Credentials; import android.security.Credentials;
import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.keystore.KeyProperties; import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection; 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 android.util.Log;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -47,32 +51,37 @@ public class ApplicationKeyStorage {
private static final String APPLICATION_KEY_ALIAS_PREFIX = private static final String APPLICATION_KEY_ALIAS_PREFIX =
"com.android.server.locksettings.recoverablekeystore/application/"; "com.android.server.locksettings.recoverablekeystore/application/";
private static final String APPLICATION_KEY_GRANT_PREFIX = "recoverable_key:";
private final KeyStoreProxy mKeyStore; private final KeyStoreProxy mKeyStore;
private final KeyStore mKeystoreService;
public static ApplicationKeyStorage getInstance(KeyStore keystoreService) /**
* Creates a new instance.
*/
public static ApplicationKeyStorage getInstance()
throws KeyStoreException { throws KeyStoreException {
return new ApplicationKeyStorage( return new ApplicationKeyStorage(
new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()), new KeyStoreProxyImpl(KeyStoreProxyImpl.getAndLoadAndroidKeyStore()));
keystoreService);
} }
@VisibleForTesting @VisibleForTesting
ApplicationKeyStorage(KeyStoreProxy keyStore, KeyStore keystoreService) { ApplicationKeyStorage(KeyStoreProxy keyStore) {
mKeyStore = 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) { 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)); Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias));
String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias); String keystoreAlias = getInternalAlias(userId, uid, alias);
return mKeystoreService.grant(keystoreAlias, uid); 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) 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) { private String getInternalAlias(int userId, int uid, String alias) {
return APPLICATION_KEY_ALIAS_PREFIX + userId + "/" + uid + "/" + 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 @Override
public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) { public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
return mRecoverableKeyStoreManager; return mRecoverableKeyStoreManager;
} }