Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 14cddc46 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement generateAndStoreKey"

parents 25f5d017 cfc990a4
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -445,4 +445,21 @@ public class RecoverableKeyStoreLoader {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }

    /**
     * Generates a key called {@code alias} and loads it into the recoverable key store. Returns the
     * raw material of the key.
     *
     * @throws RecoverableKeyStoreLoaderException if an error occurred generating and storing the
     *     key.
     */
    public byte[] generateAndStoreKey(String alias) throws RecoverableKeyStoreLoaderException {
        try {
            return mBinder.generateAndStoreKey(alias);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
            throw RecoverableKeyStoreLoaderException.fromServiceSpecificException(e);
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ interface ILockSettings {
    void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList,
            int userId);
    KeyStoreRecoveryData getRecoveryData(in byte[] account, int userId);
    byte[] generateAndStoreKey(String alias);
    void setSnapshotCreatedPendingIntent(in PendingIntent intent, int userId);
    Map getRecoverySnapshotVersions(int userId);
    void setServerParameters(long serverParameters, int userId);
+5 −0
Original line number Diff line number Diff line
@@ -2028,6 +2028,11 @@ public class LockSettingsService extends ILockSettings.Stub {
                sessionId, recoveryKeyBlob, applicationKeys, userId);
    }

    @Override
    public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException {
        return mRecoverableKeyStoreManager.generateAndStoreKey(alias);
    }

    private static final String[] VALID_SETTINGS = new String[] {
            LockPatternUtils.LOCKOUT_PERMANENT_KEY,
            LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
+7 −60
Original line number Diff line number Diff line
@@ -16,22 +16,15 @@

package com.android.server.locksettings.recoverablekeystore;

import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.util.Log;

import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;

import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Locale;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.security.auth.DestroyFailedException;

/**
 * Generates keys and stores them both in AndroidKeyStore and on disk, in wrapped form.
@@ -43,8 +36,6 @@ import javax.security.auth.DestroyFailedException;
 * @hide
 */
public class RecoverableKeyGenerator {
    private static final String TAG = "RecoverableKeyGenerator";

    private static final int RESULT_CANNOT_INSERT_ROW = -1;
    private static final String KEY_GENERATOR_ALGORITHM = "AES";
    private static final int KEY_SIZE_BITS = 256;
@@ -62,20 +53,16 @@ public class RecoverableKeyGenerator {
        // NB: This cannot use AndroidKeyStore as the provider, as we need access to the raw key
        // material, so that it can be synced to disk in encrypted form.
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_GENERATOR_ALGORITHM);
        return new RecoverableKeyGenerator(
                keyGenerator, database, new AndroidKeyStoreFactory.Impl());
        return new RecoverableKeyGenerator(keyGenerator, database);
    }

    private final KeyGenerator mKeyGenerator;
    private final RecoverableKeyStoreDb mDatabase;
    private final AndroidKeyStoreFactory mAndroidKeyStoreFactory;

    private RecoverableKeyGenerator(
            KeyGenerator keyGenerator,
            RecoverableKeyStoreDb recoverableKeyStoreDb,
            AndroidKeyStoreFactory androidKeyStoreFactory) {
            RecoverableKeyStoreDb recoverableKeyStoreDb) {
        mKeyGenerator = keyGenerator;
        mAndroidKeyStoreFactory = androidKeyStoreFactory;
        mDatabase = recoverableKeyStoreDb;
    }

@@ -89,69 +76,29 @@ public class RecoverableKeyGenerator {
     * @param platformKey The user's platform key, with which to wrap the generated key.
     * @param userId The user ID of the profile to which the calling app belongs.
     * @param uid The uid of the application that will own the key.
     * @param alias The alias by which the key will be known in AndroidKeyStore.
     * @param alias The alias by which the key will be known in the recoverable key store.
     * @throws RecoverableKeyStorageException if there is some error persisting the key either to
     *     the AndroidKeyStore or the database.
     *     the database.
     * @throws KeyStoreException if there is a KeyStore error wrapping the generated key.
     * @throws InvalidKeyException if the platform key cannot be used to wrap keys.
     *
     * @hide
     */
    public void generateAndStoreKey(
    public byte[] generateAndStoreKey(
            PlatformEncryptionKey platformKey, int userId, int uid, String alias)
            throws RecoverableKeyStorageException, KeyStoreException, InvalidKeyException {
        mKeyGenerator.init(KEY_SIZE_BITS);
        SecretKey key = mKeyGenerator.generateKey();

        KeyStoreProxy keyStore;

        try {
            keyStore = mAndroidKeyStoreFactory.getKeyStoreForUid(uid);
        } catch (NoSuchProviderException e) {
            throw new RecoverableKeyStorageException(
                    "Impossible: AndroidKeyStore provider did not exist", e);
        } catch (KeyStoreException e) {
            throw new RecoverableKeyStorageException(
                    "Could not load AndroidKeyStore for " + uid, e);
        }

        try {
            keyStore.setEntry(
                    alias,
                    new KeyStore.SecretKeyEntry(key),
                    new KeyProtection.Builder(
                            KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                            .build());
        } catch (KeyStoreException e) {
            throw new RecoverableKeyStorageException(
                    "Failed to load (%d, %s) into AndroidKeyStore", e);
        }

        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, key);
        try {
            // Keep raw key material in memory for minimum possible time.
            key.destroy();
        } catch (DestroyFailedException e) {
            Log.w(TAG, "Could not destroy SecretKey.");
        }
        long result = mDatabase.insertKey(userId, uid, alias, wrappedKey);

        if (result == RESULT_CANNOT_INSERT_ROW) {
            // Attempt to clean up
            try {
                keyStore.deleteEntry(alias);
            } catch (KeyStoreException e) {
                Log.e(TAG, String.format(Locale.US,
                        "Could not delete recoverable key (%d, %s) from "
                                + "AndroidKeyStore after error writing to database.", uid, alias),
                        e);
            }

            throw new RecoverableKeyStorageException(
                    String.format(
                            Locale.US, "Failed writing (%d, %s) to database.", uid, alias));
        }

        return key.getEncoded();
    }
}
+49 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.UnrecoverableKeyException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -59,6 +60,11 @@ import javax.crypto.AEADBadTagException;
 */
public class RecoverableKeyStoreManager {
    private static final String TAG = "RecoverableKeyStoreMgr";

    private static final int ERROR_INSECURE_USER = 1;
    private static final int ERROR_KEYSTORE_INTERNAL_ERROR = 2;
    private static final int ERROR_DATABASE_ERROR = 3;

    private static RecoverableKeyStoreManager mInstance;

    private final Context mContext;
@@ -66,6 +72,7 @@ public class RecoverableKeyStoreManager {
    private final RecoverySessionStorage mRecoverySessionStorage;
    private final ExecutorService mExecutorService;
    private final ListenersStorage mListenersStorage;
    private final RecoverableKeyGenerator mRecoverableKeyGenerator;

    /**
     * Returns a new or existing instance.
@@ -97,6 +104,12 @@ public class RecoverableKeyStoreManager {
        mRecoverySessionStorage = recoverySessionStorage;
        mExecutorService = executorService;
        mListenersStorage = listenersStorage;
        try {
            mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
        } catch (NoSuchAlgorithmException e) {
            // Impossible: all AOSP implementations must support AES.
            throw new RuntimeException(e);
        }
    }

    public int initRecoveryService(
@@ -342,6 +355,42 @@ public class RecoverableKeyStoreManager {
        }
    }

    /**
     * Generates a key named {@code alias} in the recoverable store for the calling uid. Then
     * returns the raw key material.
     *
     * <p>TODO: Once AndroidKeyStore has added move api, do not return raw bytes.
     *
     * @hide
     */
    public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException {
        int uid = Binder.getCallingUid();
        int userId = Binder.getCallingUserHandle().getIdentifier();

        PlatformEncryptionKey encryptionKey;

        try {
            PlatformKeyManager platformKeyManager = PlatformKeyManager.getInstance(
                    mContext, mDatabase, userId);
            encryptionKey = platformKeyManager.getEncryptKey();
        } catch (NoSuchAlgorithmException e) {
            // Impossible: all algorithms must be supported by AOSP
            throw new RuntimeException(e);
        } catch (KeyStoreException | UnrecoverableKeyException e) {
            throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage());
        } catch (InsecureUserException e) {
            throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
        }

        try {
            return mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
        } catch (KeyStoreException | InvalidKeyException e) {
            throw new ServiceSpecificException(ERROR_KEYSTORE_INTERNAL_ERROR, e.getMessage());
        } catch (RecoverableKeyStorageException e) {
            throw new ServiceSpecificException(ERROR_DATABASE_ERROR, e.getMessage());
        }
    }

    private byte[] decryptRecoveryKey(
            RecoverySessionStorage.Entry sessionEntry, byte[] encryptedClaimResponse)
            throws RemoteException {
Loading