Merge "Migrate recoverablekeystore to KeyStore V2."
This commit is contained in:
@@ -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)) {
|
||||||
|
return AndroidKeyStoreProvider
|
||||||
|
.loadAndroidKeyStoreSecretKeyFromKeystore(
|
||||||
|
KeyStore2.getInstance(),
|
||||||
|
getGrantDescriptor(grantAlias));
|
||||||
|
}
|
||||||
|
// TODO(b/171305545): remove KeyStore1 logic.
|
||||||
|
return android.security.keystore.AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(
|
||||||
mKeyStore,
|
mKeyStore,
|
||||||
grantAlias,
|
grantAlias,
|
||||||
KeyStore.UID_SELF);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias));
|
||||||
|
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.
|
// Aliases used by {@link KeyStore} are different than used by public API.
|
||||||
// {@code USER_PRIVATE_KEY} prefix is used secret keys.
|
// {@code USER_PRIVATE_KEY} prefix is used secret keys.
|
||||||
Log.i(TAG, String.format(Locale.US, "Get %d/%d/%s", userId, uid, alias));
|
return KeyStore.getInstance().grant(Credentials.USER_PRIVATE_KEY + keystoreAlias, uid);
|
||||||
String keystoreAlias = Credentials.USER_PRIVATE_KEY + getInternalAlias(userId, uid, alias);
|
}
|
||||||
return mKeystoreService.grant(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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ public class LockSettingsServiceTestable extends LockSettingsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RecoverableKeyStoreManager getRecoverableKeyStoreManager(KeyStore keyStore) {
|
public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
|
||||||
return mRecoverableKeyStoreManager;
|
return mRecoverableKeyStoreManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user