android.security.KeyStore.java Source code

Java tutorial

Introduction

Here is the source code for android.security.KeyStore.java

Source

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.security;

import android.annotation.UnsupportedAppUsage;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.Application;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.security.KeyStoreException;
import android.security.keymaster.ExportResult;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterBlob;
import android.security.keymaster.KeymasterCertificateChain;
import android.security.keymaster.KeymasterDefs;
import android.security.keymaster.OperationResult;
import android.security.keystore.IKeystoreService;
import android.security.keystore.KeyExpiredException;
import android.security.keystore.KeyNotYetValidException;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore.KeystoreResponse;
import android.security.keystore.StrongBoxUnavailableException;
import android.security.keystore.UserNotAuthenticatedException;
import android.util.Log;
import com.android.org.bouncycastle.asn1.ASN1InputStream;
import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import java.math.BigInteger;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import sun.security.util.ObjectIdentifier;
import sun.security.x509.AlgorithmId;

/**
 * @hide This should not be made public in its present form because it
 * assumes that private and secret key bytes are available and would
 * preclude the use of hardware crypto.
 */
public class KeyStore {
    private static final String TAG = "KeyStore";

    // ResponseCodes - see system/security/keystore/include/keystore/keystore.h
    @UnsupportedAppUsage
    public static final int NO_ERROR = 1;
    public static final int LOCKED = 2;
    public static final int UNINITIALIZED = 3;
    public static final int SYSTEM_ERROR = 4;
    public static final int PROTOCOL_ERROR = 5;
    public static final int PERMISSION_DENIED = 6;
    public static final int KEY_NOT_FOUND = 7;
    public static final int VALUE_CORRUPTED = 8;
    public static final int UNDEFINED_ACTION = 9;
    public static final int WRONG_PASSWORD = 10;
    public static final int KEY_ALREADY_EXISTS = 16;
    public static final int CANNOT_ATTEST_IDS = -66;
    public static final int HARDWARE_TYPE_UNAVAILABLE = -68;

    /**
     * Per operation authentication is needed before this operation is valid.
     * This is returned from {@link #begin} when begin succeeds but the operation uses
     * per-operation authentication and must authenticate before calling {@link #update} or
     * {@link #finish}.
     */
    public static final int OP_AUTH_NEEDED = 15;

    // Used when a user changes their pin, invalidating old auth bound keys.
    public static final int KEY_PERMANENTLY_INVALIDATED = 17;

    // Used for UID field to indicate the calling UID.
    public static final int UID_SELF = -1;

    // Flags for "put" "import" and "generate"
    public static final int FLAG_NONE = 0;

    /**
     * Indicates that this key (or key pair) must be encrypted at rest. This will protect the key
     * (or key pair) with the secure lock screen credential (e.g., password, PIN, or pattern).
     *
     * <p>Note that this requires that the secure lock screen (e.g., password, PIN, pattern) is set
     * up, otherwise key (or key pair) generation or import will fail. Moreover, this key (or key
     * pair) will be deleted when the secure lock screen is disabled or reset (e.g., by the user or
     * a Device Administrator). Finally, this key (or key pair) cannot be used until the user
     * unlocks the secure lock screen after boot.
     *
     * @see KeyguardManager#isDeviceSecure()
     */
    public static final int FLAG_ENCRYPTED = 1;

    /**
     * Select Software keymaster device, which as of this writing is the lowest security
     * level available on an android device. If neither FLAG_STRONGBOX nor FLAG_SOFTWARE is provided
     * A TEE based keymaster implementation is implied.
     *
     * Need to be in sync with KeyStoreFlag in system/security/keystore/include/keystore/keystore.h
     * For historical reasons this corresponds to the KEYSTORE_FLAG_FALLBACK flag.
     */
    public static final int FLAG_SOFTWARE = 1 << 1;

    /**
     * A private flag that's only available to system server to indicate that this key is part of
     * device encryption flow so it receives special treatment from keystore. For example this key
     * will not be super encrypted, and it will be stored separately under an unique UID instead
     * of the caller UID i.e. SYSTEM.
     *
     * Need to be in sync with KeyStoreFlag in system/security/keystore/include/keystore/keystore.h
     */
    public static final int FLAG_CRITICAL_TO_DEVICE_ENCRYPTION = 1 << 3;

    /**
     * Select Strongbox keymaster device, which as of this writing the the highest security level
     * available an android devices. If neither FLAG_STRONGBOX nor FLAG_SOFTWARE is provided
     * A TEE based keymaster implementation is implied.
     *
     * Need to be in sync with KeyStoreFlag in system/security/keystore/include/keystore/keystore.h
     */
    public static final int FLAG_STRONGBOX = 1 << 4;

    // States
    public enum State {
        @UnsupportedAppUsage
        UNLOCKED, @UnsupportedAppUsage
        LOCKED, UNINITIALIZED
    };

    private int mError = NO_ERROR;

    private final IKeystoreService mBinder;
    private final Context mContext;

    private IBinder mToken;

    private KeyStore(IKeystoreService binder) {
        mBinder = binder;
        mContext = getApplicationContext();
    }

    @UnsupportedAppUsage
    public static Context getApplicationContext() {
        Application application = ActivityThread.currentApplication();
        if (application == null) {
            throw new IllegalStateException("Failed to obtain application Context from ActivityThread");
        }
        return application;
    }

    @UnsupportedAppUsage
    public static KeyStore getInstance() {
        IKeystoreService keystore = IKeystoreService.Stub
                .asInterface(ServiceManager.getService("android.security.keystore"));
        return new KeyStore(keystore);
    }

    private synchronized IBinder getToken() {
        if (mToken == null) {
            mToken = new Binder();
        }
        return mToken;
    }

    @UnsupportedAppUsage
    public State state(int userId) {
        final int ret;
        try {
            ret = mBinder.getState(userId);
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            throw new AssertionError(e);
        }

        switch (ret) {
        case NO_ERROR:
            return State.UNLOCKED;
        case LOCKED:
            return State.LOCKED;
        case UNINITIALIZED:
            return State.UNINITIALIZED;
        default:
            throw new AssertionError(mError);
        }
    }

    @UnsupportedAppUsage
    public State state() {
        return state(UserHandle.myUserId());
    }

    public boolean isUnlocked() {
        return state() == State.UNLOCKED;
    }

    public byte[] get(String key, int uid) {
        return get(key, uid, false);
    }

    @UnsupportedAppUsage
    public byte[] get(String key) {
        return get(key, UID_SELF);
    }

    public byte[] get(String key, int uid, boolean suppressKeyNotFoundWarning) {
        try {
            key = key != null ? key : "";
            return mBinder.get(key, uid);
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return null;
        } catch (android.os.ServiceSpecificException e) {
            if (!suppressKeyNotFoundWarning || e.errorCode != KEY_NOT_FOUND) {
                Log.w(TAG, "KeyStore exception", e);
            }
            return null;
        }
    }

    public byte[] get(String key, boolean suppressKeyNotFoundWarning) {
        return get(key, UID_SELF, suppressKeyNotFoundWarning);
    }

    public boolean put(String key, byte[] value, int uid, int flags) {
        return insert(key, value, uid, flags) == NO_ERROR;
    }

    public int insert(String key, byte[] value, int uid, int flags) {
        try {
            if (value == null) {
                value = new byte[0];
            }
            int error = mBinder.insert(key, value, uid, flags);
            if (error == KEY_ALREADY_EXISTS) {
                mBinder.del(key, uid);
                error = mBinder.insert(key, value, uid, flags);
            }
            return error;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        }
    }

    int delete2(String key, int uid) {
        try {
            return mBinder.del(key, uid);
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        }
    }

    public boolean delete(String key, int uid) {
        int ret = delete2(key, uid);
        return ret == NO_ERROR || ret == KEY_NOT_FOUND;
    }

    @UnsupportedAppUsage
    public boolean delete(String key) {
        return delete(key, UID_SELF);
    }

    public boolean contains(String key, int uid) {
        try {
            return mBinder.exist(key, uid) == NO_ERROR;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        }
    }

    public boolean contains(String key) {
        return contains(key, UID_SELF);
    }

    /**
     * List all entries in the keystore for {@code uid} starting with {@code prefix}.
     */
    public String[] list(String prefix, int uid) {
        try {
            return mBinder.list(prefix, uid);
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return null;
        } catch (android.os.ServiceSpecificException e) {
            Log.w(TAG, "KeyStore exception", e);
            return null;
        }
    }

    /**
     * List uids of all keys that are auth bound to the current user.
     * Only system is allowed to call this method.
     */
    @UnsupportedAppUsage
    public int[] listUidsOfAuthBoundKeys() {
        // uids are returned as a list of strings because list of integers
        // as an output parameter is not supported by aidl-cpp.
        List<String> uidsOut = new ArrayList<>();
        try {
            int rc = mBinder.listUidsOfAuthBoundKeys(uidsOut);
            if (rc != NO_ERROR) {
                Log.w(TAG, String.format("listUidsOfAuthBoundKeys failed with error code %d", rc));
                return null;
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return null;
        } catch (android.os.ServiceSpecificException e) {
            Log.w(TAG, "KeyStore exception", e);
            return null;
        }
        // Turn list of strings into an array of uid integers.
        return uidsOut.stream().mapToInt(Integer::parseInt).toArray();
    }

    public String[] list(String prefix) {
        return list(prefix, UID_SELF);
    }

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public boolean reset() {
        try {
            return mBinder.reset() == NO_ERROR;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        }
    }

    /**
     * Attempt to lock the keystore for {@code user}.
     *
     * @param userId Android user to lock.
     * @return whether {@code user}'s keystore was locked.
     */
    public boolean lock(int userId) {
        try {
            return mBinder.lock(userId) == NO_ERROR;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        }
    }

    public boolean lock() {
        return lock(UserHandle.myUserId());
    }

    /**
     * Attempt to unlock the keystore for {@code user} with the password {@code password}.
     * This is required before keystore entries created with FLAG_ENCRYPTED can be accessed or
     * created.
     *
     * @param userId Android user ID to operate on
     * @param password user's keystore password. Should be the most recent value passed to
     * {@link #onUserPasswordChanged} for the user.
     *
     * @return whether the keystore was unlocked.
     */
    public boolean unlock(int userId, String password) {
        try {
            password = password != null ? password : "";
            mError = mBinder.unlock(userId, password);
            return mError == NO_ERROR;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        }
    }

    @UnsupportedAppUsage
    public boolean unlock(String password) {
        return unlock(UserHandle.getUserId(Process.myUid()), password);
    }

    /**
     * Check if the keystore for {@code userId} is empty.
     */
    public boolean isEmpty(int userId) {
        try {
            return mBinder.isEmpty(userId) != 0;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        }
    }

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    public boolean isEmpty() {
        return isEmpty(UserHandle.myUserId());
    }

    public String grant(String key, int uid) {
        try {
            String grantAlias = mBinder.grant(key, uid);
            if (grantAlias == "")
                return null;
            return grantAlias;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return null;
        }
    }

    public boolean ungrant(String key, int uid) {
        try {
            return mBinder.ungrant(key, uid) == NO_ERROR;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        }
    }

    /**
     * Returns the last modification time of the key in milliseconds since the
     * epoch. Will return -1L if the key could not be found or other error.
     */
    public long getmtime(String key, int uid) {
        try {
            final long millis = mBinder.getmtime(key, uid);
            if (millis == -1L) {
                return -1L;
            }

            return millis * 1000L;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return -1L;
        }
    }

    public long getmtime(String key) {
        return getmtime(key, UID_SELF);
    }

    // TODO: remove this when it's removed from Settings
    public boolean isHardwareBacked() {
        return isHardwareBacked("RSA");
    }

    public boolean isHardwareBacked(String keyType) {
        try {
            return mBinder.is_hardware_backed(keyType.toUpperCase(Locale.US)) == NO_ERROR;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        }
    }

    public boolean clearUid(int uid) {
        try {
            return mBinder.clear_uid(uid) == NO_ERROR;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        }
    }

    public int getLastError() {
        return mError;
    }

    public boolean addRngEntropy(byte[] data, int flags) {
        try {
            KeystoreResultPromise promise = new KeystoreResultPromise();
            int errorCode = mBinder.addRngEntropy(promise, data, flags);
            if (errorCode == NO_ERROR) {
                return promise.getFuture().get().getErrorCode() == NO_ERROR;
            } else {
                return false;
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "AddRngEntropy completed with exception", e);
            return false;
        }
    }

    private class KeyCharacteristicsCallbackResult {
        private KeystoreResponse keystoreResponse;
        private KeyCharacteristics keyCharacteristics;

        public KeyCharacteristicsCallbackResult(KeystoreResponse keystoreResponse,
                KeyCharacteristics keyCharacteristics) {
            this.keystoreResponse = keystoreResponse;
            this.keyCharacteristics = keyCharacteristics;
        }

        public KeystoreResponse getKeystoreResponse() {
            return keystoreResponse;
        }

        public void setKeystoreResponse(KeystoreResponse keystoreResponse) {
            this.keystoreResponse = keystoreResponse;
        }

        public KeyCharacteristics getKeyCharacteristics() {
            return keyCharacteristics;
        }

        public void setKeyCharacteristics(KeyCharacteristics keyCharacteristics) {
            this.keyCharacteristics = keyCharacteristics;
        }
    }

    private class KeyCharacteristicsPromise
            extends android.security.keystore.IKeystoreKeyCharacteristicsCallback.Stub {
        final private CompletableFuture<KeyCharacteristicsCallbackResult> future = new CompletableFuture<KeyCharacteristicsCallbackResult>();

        @Override
        public void onFinished(KeystoreResponse keystoreResponse, KeyCharacteristics keyCharacteristics)
                throws android.os.RemoteException {
            future.complete(new KeyCharacteristicsCallbackResult(keystoreResponse, keyCharacteristics));
        }

        public final CompletableFuture<KeyCharacteristicsCallbackResult> getFuture() {
            return future;
        }
    };

    private int generateKeyInternal(String alias, KeymasterArguments args, byte[] entropy, int uid, int flags,
            KeyCharacteristics outCharacteristics)
            throws RemoteException, ExecutionException, InterruptedException {
        KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
        int error = mBinder.generateKey(promise, alias, args, entropy, uid, flags);
        if (error != NO_ERROR) {
            Log.e(TAG, "generateKeyInternal failed on request " + error);
            return error;
        }

        KeyCharacteristicsCallbackResult result = promise.getFuture().get();
        error = result.getKeystoreResponse().getErrorCode();
        if (error != NO_ERROR) {
            Log.e(TAG, "generateKeyInternal failed on response " + error);
            return error;
        }
        KeyCharacteristics characteristics = result.getKeyCharacteristics();
        if (characteristics == null) {
            Log.e(TAG, "generateKeyInternal got empty key cheractariestics " + error);
            return SYSTEM_ERROR;
        }
        outCharacteristics.shallowCopyFrom(characteristics);
        return NO_ERROR;
    }

    public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int uid, int flags,
            KeyCharacteristics outCharacteristics) {
        try {
            entropy = entropy != null ? entropy : new byte[0];
            args = args != null ? args : new KeymasterArguments();
            int error = generateKeyInternal(alias, args, entropy, uid, flags, outCharacteristics);
            if (error == KEY_ALREADY_EXISTS) {
                mBinder.del(alias, uid);
                error = generateKeyInternal(alias, args, entropy, uid, flags, outCharacteristics);
            }
            return error;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "generateKey completed with exception", e);
            return SYSTEM_ERROR;
        }
    }

    public int generateKey(String alias, KeymasterArguments args, byte[] entropy, int flags,
            KeyCharacteristics outCharacteristics) {
        return generateKey(alias, args, entropy, UID_SELF, flags, outCharacteristics);
    }

    public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId, int uid,
            KeyCharacteristics outCharacteristics) {
        try {
            clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
            appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
            KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
            int error = mBinder.getKeyCharacteristics(promise, alias, clientId, appId, uid);
            if (error != NO_ERROR)
                return error;

            KeyCharacteristicsCallbackResult result = promise.getFuture().get();
            error = result.getKeystoreResponse().getErrorCode();
            if (error != NO_ERROR)
                return error;

            KeyCharacteristics characteristics = result.getKeyCharacteristics();
            if (characteristics == null)
                return SYSTEM_ERROR;
            outCharacteristics.shallowCopyFrom(characteristics);
            return NO_ERROR;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "GetKeyCharacteristics completed with exception", e);
            return SYSTEM_ERROR;
        }
    }

    public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
            KeyCharacteristics outCharacteristics) {
        return getKeyCharacteristics(alias, clientId, appId, UID_SELF, outCharacteristics);
    }

    private int importKeyInternal(String alias, KeymasterArguments args, int format, byte[] keyData, int uid,
            int flags, KeyCharacteristics outCharacteristics)
            throws RemoteException, ExecutionException, InterruptedException {
        KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
        int error = mBinder.importKey(promise, alias, args, format, keyData, uid, flags);
        if (error != NO_ERROR)
            return error;

        KeyCharacteristicsCallbackResult result = promise.getFuture().get();
        error = result.getKeystoreResponse().getErrorCode();
        if (error != NO_ERROR)
            return error;

        KeyCharacteristics characteristics = result.getKeyCharacteristics();
        if (characteristics == null)
            return SYSTEM_ERROR;
        outCharacteristics.shallowCopyFrom(characteristics);
        return NO_ERROR;
    }

    public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData, int uid, int flags,
            KeyCharacteristics outCharacteristics) {
        try {
            int error = importKeyInternal(alias, args, format, keyData, uid, flags, outCharacteristics);
            if (error == KEY_ALREADY_EXISTS) {
                mBinder.del(alias, uid);
                error = importKeyInternal(alias, args, format, keyData, uid, flags, outCharacteristics);
            }
            return error;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "ImportKey completed with exception", e);
            return SYSTEM_ERROR;
        }
    }

    public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData, int flags,
            KeyCharacteristics outCharacteristics) {
        return importKey(alias, args, format, keyData, UID_SELF, flags, outCharacteristics);
    }

    private String getAlgorithmFromPKCS8(byte[] keyData) {
        try {
            final ASN1InputStream bIn = new ASN1InputStream(new ByteArrayInputStream(keyData));
            final PrivateKeyInfo pki = PrivateKeyInfo.getInstance(bIn.readObject());
            final String algOid = pki.getPrivateKeyAlgorithm().getAlgorithm().getId();
            return new AlgorithmId(new ObjectIdentifier(algOid)).getName();
        } catch (IOException e) {
            Log.e(TAG, "getAlgorithmFromPKCS8 Failed to parse key data");
            Log.e(TAG, Log.getStackTraceString(e));
            return null;
        }
    }

    private KeymasterArguments makeLegacyArguments(String algorithm) {
        KeymasterArguments args = new KeymasterArguments();
        args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM,
                KeyProperties.KeyAlgorithm.toKeymasterAsymmetricKeyAlgorithm(algorithm));
        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_SIGN);
        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_VERIFY);
        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
        args.addEnum(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
        args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
        if (algorithm.equalsIgnoreCase(KeyProperties.KEY_ALGORITHM_RSA)) {
            args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_OAEP);
            args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
            args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN);
            args.addEnum(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_RSA_PSS);
        }
        args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_NONE);
        args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_MD5);
        args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA1);
        args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_224);
        args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_256);
        args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_384);
        args.addEnum(KeymasterDefs.KM_TAG_DIGEST, KeymasterDefs.KM_DIGEST_SHA_2_512);
        args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
        args.addDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME, new Date(Long.MAX_VALUE));
        args.addDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME, new Date(Long.MAX_VALUE));
        args.addDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, new Date(0));
        return args;
    }

    public boolean importKey(String alias, byte[] keyData, int uid, int flags) {
        String algorithm = getAlgorithmFromPKCS8(keyData);
        if (algorithm == null)
            return false;
        KeymasterArguments args = makeLegacyArguments(algorithm);
        KeyCharacteristics out = new KeyCharacteristics();
        int result = importKey(alias, args, KeymasterDefs.KM_KEY_FORMAT_PKCS8, keyData, uid, flags, out);
        if (result != NO_ERROR) {
            Log.e(TAG, Log.getStackTraceString(new KeyStoreException(result, "legacy key import failed")));
            return false;
        }
        return true;
    }

    private int importWrappedKeyInternal(String wrappedKeyAlias, byte[] wrappedKey, String wrappingKeyAlias,
            byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid,
            KeyCharacteristics outCharacteristics)
            throws RemoteException, ExecutionException, InterruptedException {
        KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
        int error = mBinder.importWrappedKey(promise, wrappedKeyAlias, wrappedKey, wrappingKeyAlias, maskingKey,
                args, rootSid, fingerprintSid);
        if (error != NO_ERROR)
            return error;

        KeyCharacteristicsCallbackResult result = promise.getFuture().get();
        error = result.getKeystoreResponse().getErrorCode();
        if (error != NO_ERROR)
            return error;

        KeyCharacteristics characteristics = result.getKeyCharacteristics();
        if (characteristics == null)
            return SYSTEM_ERROR;
        outCharacteristics.shallowCopyFrom(characteristics);
        return NO_ERROR;
    }

    public int importWrappedKey(String wrappedKeyAlias, byte[] wrappedKey, String wrappingKeyAlias,
            byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid, int uid,
            KeyCharacteristics outCharacteristics) {
        // TODO b/119217337 uid parameter gets silently ignored.
        try {
            int error = importWrappedKeyInternal(wrappedKeyAlias, wrappedKey, wrappingKeyAlias, maskingKey, args,
                    rootSid, fingerprintSid, outCharacteristics);
            if (error == KEY_ALREADY_EXISTS) {
                mBinder.del(wrappedKeyAlias, UID_SELF);
                error = importWrappedKeyInternal(wrappedKeyAlias, wrappedKey, wrappingKeyAlias, maskingKey, args,
                        rootSid, fingerprintSid, outCharacteristics);
            }
            return error;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "ImportWrappedKey completed with exception", e);
            return SYSTEM_ERROR;
        }
    }

    private class ExportKeyPromise extends android.security.keystore.IKeystoreExportKeyCallback.Stub {
        final private CompletableFuture<ExportResult> future = new CompletableFuture<ExportResult>();

        @Override
        public void onFinished(ExportResult exportKeyResult) throws android.os.RemoteException {
            future.complete(exportKeyResult);
        }

        public final CompletableFuture<ExportResult> getFuture() {
            return future;
        }
    };

    public ExportResult exportKey(String alias, int format, KeymasterBlob clientId, KeymasterBlob appId, int uid) {
        try {
            clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
            appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
            ExportKeyPromise promise = new ExportKeyPromise();
            int error = mBinder.exportKey(promise, alias, format, clientId, appId, uid);
            if (error == NO_ERROR) {
                return promise.getFuture().get();
            } else {
                return new ExportResult(error);
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return null;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "ExportKey completed with exception", e);
            return null;
        }
    }

    public ExportResult exportKey(String alias, int format, KeymasterBlob clientId, KeymasterBlob appId) {
        return exportKey(alias, format, clientId, appId, UID_SELF);
    }

    private class OperationPromise extends android.security.keystore.IKeystoreOperationResultCallback.Stub {
        final private CompletableFuture<OperationResult> future = new CompletableFuture<OperationResult>();

        @Override
        public void onFinished(OperationResult operationResult) throws android.os.RemoteException {
            future.complete(operationResult);
        }

        public final CompletableFuture<OperationResult> getFuture() {
            return future;
        }
    };

    public OperationResult begin(String alias, int purpose, boolean pruneable, KeymasterArguments args,
            byte[] entropy, int uid) {
        try {
            args = args != null ? args : new KeymasterArguments();
            entropy = entropy != null ? entropy : new byte[0];
            OperationPromise promise = new OperationPromise();
            int errorCode = mBinder.begin(promise, getToken(), alias, purpose, pruneable, args, entropy, uid);
            if (errorCode == NO_ERROR) {
                return promise.getFuture().get();
            } else {
                return new OperationResult(errorCode);
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return null;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "Begin completed with exception", e);
            return null;
        }
    }

    public OperationResult begin(String alias, int purpose, boolean pruneable, KeymasterArguments args,
            byte[] entropy) {
        entropy = entropy != null ? entropy : new byte[0];
        args = args != null ? args : new KeymasterArguments();
        return begin(alias, purpose, pruneable, args, entropy, UID_SELF);
    }

    public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) {
        try {
            arguments = arguments != null ? arguments : new KeymasterArguments();
            input = input != null ? input : new byte[0];
            OperationPromise promise = new OperationPromise();
            int errorCode = mBinder.update(promise, token, arguments, input);
            if (errorCode == NO_ERROR) {
                return promise.getFuture().get();
            } else {
                return new OperationResult(errorCode);
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return null;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "Update completed with exception", e);
            return null;
        }
    }

    public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature, byte[] entropy) {
        try {
            arguments = arguments != null ? arguments : new KeymasterArguments();
            entropy = entropy != null ? entropy : new byte[0];
            signature = signature != null ? signature : new byte[0];
            OperationPromise promise = new OperationPromise();
            int errorCode = mBinder.finish(promise, token, arguments, signature, entropy);
            if (errorCode == NO_ERROR) {
                return promise.getFuture().get();
            } else {
                return new OperationResult(errorCode);
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return null;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "Finish completed with exception", e);
            return null;
        }
    }

    public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature) {
        return finish(token, arguments, signature, null);
    }

    private class KeystoreResultPromise extends android.security.keystore.IKeystoreResponseCallback.Stub {
        final private CompletableFuture<KeystoreResponse> future = new CompletableFuture<KeystoreResponse>();

        @Override
        public void onFinished(KeystoreResponse keystoreResponse) throws android.os.RemoteException {
            future.complete(keystoreResponse);
        }

        public final CompletableFuture<KeystoreResponse> getFuture() {
            return future;
        }
    };

    public int abort(IBinder token) {
        try {
            KeystoreResultPromise promise = new KeystoreResultPromise();
            int errorCode = mBinder.abort(promise, token);
            if (errorCode == NO_ERROR) {
                return promise.getFuture().get().getErrorCode();
            } else {
                return errorCode;
            }
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "Abort completed with exception", e);
            return SYSTEM_ERROR;
        }
    }

    /**
     * Add an authentication record to the keystore authorization table.
     *
     * @param authToken The packed bytes of a hw_auth_token_t to be provided to keymaster.
     * @return {@code KeyStore.NO_ERROR} on success, otherwise an error value corresponding to
     * a {@code KeymasterDefs.KM_ERROR_} value or {@code KeyStore} ResponseCode.
     */
    public int addAuthToken(byte[] authToken) {
        try {
            return mBinder.addAuthToken(authToken);
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        }
    }

    /**
     * Notify keystore that a user's password has changed.
     *
     * @param userId the user whose password changed.
     * @param newPassword the new password or "" if the password was removed.
     */
    public boolean onUserPasswordChanged(int userId, String newPassword) {
        // Parcel.cpp doesn't support deserializing null strings and treats them as "". Make that
        // explicit here.
        if (newPassword == null) {
            newPassword = "";
        }
        try {
            return mBinder.onUserPasswordChanged(userId, newPassword) == NO_ERROR;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        }
    }

    /**
     * Notify keystore that a user was added.
     *
     * @param userId the new user.
     * @param parentId the parent of the new user, or -1 if the user has no parent. If parentId is
     * specified then the new user's keystore will be intialized with the same secure lockscreen
     * password as the parent.
     */
    public void onUserAdded(int userId, int parentId) {
        try {
            mBinder.onUserAdded(userId, parentId);
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
        }
    }

    /**
     * Notify keystore that a user was added.
     *
     * @param userId the new user.
     */
    public void onUserAdded(int userId) {
        onUserAdded(userId, -1);
    }

    /**
     * Notify keystore that a user was removed.
     *
     * @param userId the removed user.
     */
    public void onUserRemoved(int userId) {
        try {
            mBinder.onUserRemoved(userId);
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
        }
    }

    public boolean onUserPasswordChanged(String newPassword) {
        return onUserPasswordChanged(UserHandle.getUserId(Process.myUid()), newPassword);
    }

    private class KeyAttestationCallbackResult {
        private KeystoreResponse keystoreResponse;
        private KeymasterCertificateChain certificateChain;

        public KeyAttestationCallbackResult(KeystoreResponse keystoreResponse,
                KeymasterCertificateChain certificateChain) {
            this.keystoreResponse = keystoreResponse;
            this.certificateChain = certificateChain;
        }

        public KeystoreResponse getKeystoreResponse() {
            return keystoreResponse;
        }

        public void setKeystoreResponse(KeystoreResponse keystoreResponse) {
            this.keystoreResponse = keystoreResponse;
        }

        public KeymasterCertificateChain getCertificateChain() {
            return certificateChain;
        }

        public void setCertificateChain(KeymasterCertificateChain certificateChain) {
            this.certificateChain = certificateChain;
        }
    }

    private class CertificateChainPromise extends android.security.keystore.IKeystoreCertificateChainCallback.Stub {
        final private CompletableFuture<KeyAttestationCallbackResult> future = new CompletableFuture<KeyAttestationCallbackResult>();

        @Override
        public void onFinished(KeystoreResponse keystoreResponse, KeymasterCertificateChain certificateChain)
                throws android.os.RemoteException {
            future.complete(new KeyAttestationCallbackResult(keystoreResponse, certificateChain));
        }

        public final CompletableFuture<KeyAttestationCallbackResult> getFuture() {
            return future;
        }
    };

    public int attestKey(String alias, KeymasterArguments params, KeymasterCertificateChain outChain) {
        try {
            if (params == null) {
                params = new KeymasterArguments();
            }
            if (outChain == null) {
                outChain = new KeymasterCertificateChain();
            }
            CertificateChainPromise promise = new CertificateChainPromise();
            int error = mBinder.attestKey(promise, alias, params);
            if (error != NO_ERROR)
                return error;
            KeyAttestationCallbackResult result = promise.getFuture().get();
            error = result.getKeystoreResponse().getErrorCode();
            if (error == NO_ERROR) {
                outChain.shallowCopyFrom(result.getCertificateChain());
            }
            return error;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "AttestKey completed with exception", e);
            return SYSTEM_ERROR;
        }
    }

    public int attestDeviceIds(KeymasterArguments params, KeymasterCertificateChain outChain) {
        try {
            if (params == null) {
                params = new KeymasterArguments();
            }
            if (outChain == null) {
                outChain = new KeymasterCertificateChain();
            }
            CertificateChainPromise promise = new CertificateChainPromise();
            int error = mBinder.attestDeviceIds(promise, params);
            if (error != NO_ERROR)
                return error;
            KeyAttestationCallbackResult result = promise.getFuture().get();
            error = result.getKeystoreResponse().getErrorCode();
            if (error == NO_ERROR) {
                outChain.shallowCopyFrom(result.getCertificateChain());
            }
            return error;
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return SYSTEM_ERROR;
        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "AttestDevicdeIds completed with exception", e);
            return SYSTEM_ERROR;
        }
    }

    /**
     * Notify keystore that the device went off-body.
     */
    public void onDeviceOffBody() {
        try {
            mBinder.onDeviceOffBody();
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
        }
    }

    // Keep in sync with confirmationui/1.0/types.hal.
    public static final int CONFIRMATIONUI_OK = 0;
    public static final int CONFIRMATIONUI_CANCELED = 1;
    public static final int CONFIRMATIONUI_ABORTED = 2;
    public static final int CONFIRMATIONUI_OPERATION_PENDING = 3;
    public static final int CONFIRMATIONUI_IGNORED = 4;
    public static final int CONFIRMATIONUI_SYSTEM_ERROR = 5;
    public static final int CONFIRMATIONUI_UNIMPLEMENTED = 6;
    public static final int CONFIRMATIONUI_UNEXPECTED = 7;
    public static final int CONFIRMATIONUI_UIERROR = 0x10000;
    public static final int CONFIRMATIONUI_UIERROR_MISSING_GLYPH = 0x10001;
    public static final int CONFIRMATIONUI_UIERROR_MESSAGE_TOO_LONG = 0x10002;
    public static final int CONFIRMATIONUI_UIERROR_MALFORMED_UTF8_ENCODING = 0x10003;

    /**
     * Requests keystore call into the confirmationui HAL to display a prompt.
     *
     * @param listener the binder to use for callbacks.
     * @param promptText the prompt to display.
     * @param extraData extra data / nonce from application.
     * @param locale the locale as a BCP 47 langauge tag.
     * @param uiOptionsAsFlags the UI options to use, as flags.
     * @return one of the {@code CONFIRMATIONUI_*} constants, for
     * example {@code KeyStore.CONFIRMATIONUI_OK}.
     */
    public int presentConfirmationPrompt(IBinder listener, String promptText, byte[] extraData, String locale,
            int uiOptionsAsFlags) {
        try {
            return mBinder.presentConfirmationPrompt(listener, promptText, extraData, locale, uiOptionsAsFlags);
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return CONFIRMATIONUI_SYSTEM_ERROR;
        }
    }

    /**
     * Requests keystore call into the confirmationui HAL to cancel displaying a prompt.
     *
     * @param listener the binder passed to the {@link #presentConfirmationPrompt} method.
     * @return one of the {@code CONFIRMATIONUI_*} constants, for
     * example {@code KeyStore.CONFIRMATIONUI_OK}.
     */
    public int cancelConfirmationPrompt(IBinder listener) {
        try {
            return mBinder.cancelConfirmationPrompt(listener);
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return CONFIRMATIONUI_SYSTEM_ERROR;
        }
    }

    /**
     * Requests keystore to check if the confirmationui HAL is available.
     *
     * @return whether the confirmationUI HAL is available.
     */
    public boolean isConfirmationPromptSupported() {
        try {
            return mBinder.isConfirmationPromptSupported();
        } catch (RemoteException e) {
            Log.w(TAG, "Cannot connect to keystore", e);
            return false;
        }
    }

    /**
     * Returns a {@link KeyStoreException} corresponding to the provided keystore/keymaster error
     * code.
     */
    @UnsupportedAppUsage
    public static KeyStoreException getKeyStoreException(int errorCode) {
        if (errorCode > 0) {
            // KeyStore layer error
            switch (errorCode) {
            case NO_ERROR:
                return new KeyStoreException(errorCode, "OK");
            case LOCKED:
                return new KeyStoreException(errorCode, "User authentication required");
            case UNINITIALIZED:
                return new KeyStoreException(errorCode, "Keystore not initialized");
            case SYSTEM_ERROR:
                return new KeyStoreException(errorCode, "System error");
            case PERMISSION_DENIED:
                return new KeyStoreException(errorCode, "Permission denied");
            case KEY_NOT_FOUND:
                return new KeyStoreException(errorCode, "Key not found");
            case VALUE_CORRUPTED:
                return new KeyStoreException(errorCode, "Key blob corrupted");
            case OP_AUTH_NEEDED:
                return new KeyStoreException(errorCode, "Operation requires authorization");
            case KEY_PERMANENTLY_INVALIDATED:
                return new KeyStoreException(errorCode, "Key permanently invalidated");
            default:
                return new KeyStoreException(errorCode, String.valueOf(errorCode));
            }
        } else {
            // Keymaster layer error
            switch (errorCode) {
            case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
                // The name of this parameter significantly differs between Keymaster and
                // framework APIs. Use the framework wording to make life easier for developers.
                return new KeyStoreException(errorCode, "Invalid user authentication validity duration");
            default:
                return new KeyStoreException(errorCode, KeymasterDefs.getErrorMessage(errorCode));
            }
        }
    }

    /**
     * Returns an {@link InvalidKeyException} corresponding to the provided
     * {@link KeyStoreException}.
     */
    public InvalidKeyException getInvalidKeyException(String keystoreKeyAlias, int uid, KeyStoreException e) {
        switch (e.getErrorCode()) {
        case LOCKED:
            return new UserNotAuthenticatedException();
        case KeymasterDefs.KM_ERROR_KEY_EXPIRED:
            return new KeyExpiredException();
        case KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID:
            return new KeyNotYetValidException();
        case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED:
        case OP_AUTH_NEEDED: {
            // We now need to determine whether the key/operation can become usable if user
            // authentication is performed, or whether it can never become usable again.
            // User authentication requirements are contained in the key's characteristics. We
            // need to check whether these requirements can be be satisfied by asking the user
            // to authenticate.
            KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
            int getKeyCharacteristicsErrorCode = getKeyCharacteristics(keystoreKeyAlias, null, null, uid,
                    keyCharacteristics);
            if (getKeyCharacteristicsErrorCode != NO_ERROR) {
                return new InvalidKeyException("Failed to obtained key characteristics",
                        getKeyStoreException(getKeyCharacteristicsErrorCode));
            }
            List<BigInteger> keySids = keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID);
            if (keySids.isEmpty()) {
                // Key is not bound to any SIDs -- no amount of authentication will help here.
                return new KeyPermanentlyInvalidatedException();
            }
            long rootSid = GateKeeper.getSecureUserId();
            if ((rootSid != 0) && (keySids.contains(KeymasterArguments.toUint64(rootSid)))) {
                // One of the key's SIDs is the current root SID -- user can be authenticated
                // against that SID.
                return new UserNotAuthenticatedException();
            }

            long fingerprintOnlySid = getFingerprintOnlySid();
            if ((fingerprintOnlySid != 0) && (keySids.contains(KeymasterArguments.toUint64(fingerprintOnlySid)))) {
                // One of the key's SIDs is the current fingerprint SID -- user can be
                // authenticated against that SID.
                return new UserNotAuthenticatedException();
            }

            // None of the key's SIDs can ever be authenticated
            return new KeyPermanentlyInvalidatedException();
        }
        case UNINITIALIZED:
            return new KeyPermanentlyInvalidatedException();
        default:
            return new InvalidKeyException("Keystore operation failed", e);
        }
    }

    private long getFingerprintOnlySid() {
        final PackageManager packageManager = mContext.getPackageManager();
        if (!packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
            return 0;
        }
        FingerprintManager fingerprintManager = mContext.getSystemService(FingerprintManager.class);
        if (fingerprintManager == null) {
            return 0;
        }

        // TODO: Restore USE_FINGERPRINT permission check in
        // FingerprintManager.getAuthenticatorId once the ID is no longer needed here.
        return fingerprintManager.getAuthenticatorId();
    }

    /**
     * Returns an {@link InvalidKeyException} corresponding to the provided keystore/keymaster error
     * code.
     */
    public InvalidKeyException getInvalidKeyException(String keystoreKeyAlias, int uid, int errorCode) {
        return getInvalidKeyException(keystoreKeyAlias, uid, getKeyStoreException(errorCode));
    }
}