/*
 * Copyright (C) 2015 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.IntDef;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.security.keymaster.KeymasterDefs;
import android.system.keystore2.ResponseCode;
import android.util.Log;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;

/**
 * Exception containing information about the failure at the Keystore / KeyMint layer while
 * generating or using a key.
 *
 * The public error codes indicate the cause of the error and the methods indicate whether
 * it's a system/key issue and whether re-trying the operation (with the same key or a new key)
 * is likely to succeed.
 */
public class KeyStoreException extends Exception {
    private static final String TAG = "KeyStoreException";

    /**
     * This error code is for mapping errors that the caller will not know about. If the caller is
     * targeting an API level earlier than the one the error was introduced in, then the error will
     * be mapped to this one.
     * In API level 33 no errors map to this error.
     */
    public static final int ERROR_OTHER = 1;
    /**
     * Indicating the key could not be used because the user needs to authenticate first.
     * See
     * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserAuthenticationRequired(boolean)}.
     */
    public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2;
    /**
     * Indicating that {@code load()} has not been called on the Keystore instance, or an attempt
     * has been made to generate an authorization bound key while the user has not set a lock
     * screen knowledge factor (LSKF). Instruct the user to set an LSKF and retry.
     */
    public static final int ERROR_KEYSTORE_UNINITIALIZED = 3;
    /**
     * An internal system error - refer to {@link #isTransientFailure()} to determine whether
     * re-trying the operation is likely to yield different results.
     */
    public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4;
    /**
     * The caller has requested key parameters or operation which are only available to system
     * or privileged apps.
     */
    public static final int ERROR_PERMISSION_DENIED = 5;
    /**
     * The key the operation refers to doesn't exist.
     */
    public static final int ERROR_KEY_DOES_NOT_EXIST = 6;
    /**
     * The key is corrupted and could not be recovered.
     */
    public static final int ERROR_KEY_CORRUPTED = 7;
    /**
     * The error related to inclusion of device identifiers in the attestation record.
     */
    public static final int ERROR_ID_ATTESTATION_FAILURE = 8;
    /**
     * The attestation challenge specified is too large.
     */
    public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9;
    /**
     * General error in the KeyMint layer.
     */
    public static final int ERROR_KEYMINT_FAILURE = 10;
    /**
     * Failure in the Keystore layer.
     */
    public static final int ERROR_KEYSTORE_FAILURE = 11;
    /**
     * The feature the caller is trying to use is not implemented by the underlying
     * KeyMint implementation.
     * This could happen when an unsupported algorithm is requested, or when trying to import
     * a key in a format other than raw or PKCS#8.
     */
    public static final int ERROR_UNIMPLEMENTED = 12;
    /**
     * The feature the caller is trying to use is not compatible with the parameters used to
     * generate the key. For example, trying to use a key generated for a different signature
     * algorithm, or a digest not specified during key creation.
     * Another case is the attempt to generate a symmetric AES key and requesting key attestation.
     */
    public static final int ERROR_INCORRECT_USAGE = 13;
    /**
     * The key is not currently valid: Either at has expired or it will be valid for use in the
     * future.
     */
    public static final int ERROR_KEY_NOT_TEMPORALLY_VALID = 14;
    /**
     * The crypto object the caller has been using held a reference to a KeyMint operation that
     * has been evacuated (likely due to other concurrent operations taking place).
     * The caller should re-create the crypto object and try again.
     */
    public static final int ERROR_KEY_OPERATION_EXPIRED = 15;
    /**
     * There are no keys available for attestation.
     * This error is returned only on devices that rely solely on remotely-provisioned keys (see
     * <a href=
     * "https://android-developers.googleblog.com/2022/03/upgrading-android-attestation-remote.html"
     * >Remote Key Provisioning</a>).
     *
     * <p>On such a device, if the caller requests key generation and includes an attestation
     * challenge (indicating key attestation is required), the error will be returned in one of
     * the following cases:
     * <ul>
     *     <li>The pool of remotely-provisioned keys has been exhausted.</li>
     *     <li>The device is not registered with the key provisioning server.</li>
     * </ul>
     * </p>
     *
     * <p>This error is a transient one if the pool of remotely-provisioned keys has been
     * exhausted. However, if the device is not registered with the server, or the key
     * provisioning server refuses key issuance, this is a permanent error.</p>
     */
    public static final int ERROR_ATTESTATION_KEYS_UNAVAILABLE = 16;
    /**
     * This device requires a software upgrade to use the key provisioning server. The software
     * is outdated and this error is returned only on devices that rely solely on
     * remotely-provisioned keys (see <a href=
     * "https://android-developers.googleblog.com/2022/03/upgrading-android-attestation-remote.html"
     * >Remote Key Provisioning</a>).
     *
     * @hide
     */
    public static final int ERROR_DEVICE_REQUIRES_UPGRADE_FOR_ATTESTATION = 17;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = {"ERROR_"}, value = {
            ERROR_OTHER,
            ERROR_USER_AUTHENTICATION_REQUIRED,
            ERROR_KEYSTORE_UNINITIALIZED,
            ERROR_INTERNAL_SYSTEM_ERROR,
            ERROR_PERMISSION_DENIED,
            ERROR_KEY_DOES_NOT_EXIST,
            ERROR_KEY_CORRUPTED,
            ERROR_ID_ATTESTATION_FAILURE,
            ERROR_ATTESTATION_CHALLENGE_TOO_LARGE,
            ERROR_KEYMINT_FAILURE,
            ERROR_KEYSTORE_FAILURE,
            ERROR_UNIMPLEMENTED,
            ERROR_INCORRECT_USAGE,
            ERROR_KEY_NOT_TEMPORALLY_VALID,
            ERROR_KEY_OPERATION_EXPIRED,
            ERROR_ATTESTATION_KEYS_UNAVAILABLE,
            ERROR_DEVICE_REQUIRES_UPGRADE_FOR_ATTESTATION,
    })
    public @interface PublicErrorCode {
    }

    /**
     * Never re-try the operation that led to this error, since it's a permanent error.
     *
     * This value is always returned when {@link #isTransientFailure()} is {@code false}.
     */
    public static final int RETRY_NEVER = 1;
    /**
     * Re-try the operation that led to this error with an exponential back-off delay.
     * The first delay should be between 5 to 30 seconds, and each subsequent re-try should double
     * the delay time.
     *
     * This value is returned when {@link #isTransientFailure()} is {@code true}.
     */
    public static final int RETRY_WITH_EXPONENTIAL_BACKOFF = 2;
    /**
     * Re-try the operation that led to this error when the device regains connectivity.
     * Remote provisioning of keys requires reaching the remote server, and the device is
     * currently unable to due that due to lack of network connectivity.
     *
     * This value is returned when {@link #isTransientFailure()} is {@code true}.
     */
    public static final int RETRY_WHEN_CONNECTIVITY_AVAILABLE = 3;
    /**
     * Re-try the operation that led to this error when the device has a software update
     * downloaded and on the next reboot. The Remote provisioning server recognizes
     * the device, but refuses issuance of attestation keys because it contains a software
     * version that could potentially be vulnerable and needs an update. Re-trying after the
     * device has upgraded and rebooted may alleviate the problem.
     *
     * <p>This value is returned when {@link #isTransientFailure()} is {@code true}.
     */
    public static final int RETRY_AFTER_NEXT_REBOOT = 4;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(flag = true, prefix = {"RETRY_"}, value = {
            RETRY_NEVER,
            RETRY_WITH_EXPONENTIAL_BACKOFF,
            RETRY_WHEN_CONNECTIVITY_AVAILABLE,
            RETRY_AFTER_NEXT_REBOOT,
    })
    public @interface RetryPolicy {
    }

    // RKP-specific error information.
    /**
     * Remote provisioning of attestation keys has completed successfully.
     * @hide */
    public static final int RKP_SUCCESS = 0;
    /**
     * Remotely-provisioned keys are temporarily unavailable. This could be because of RPC
     * error when talking to the remote provisioner or keys are being currently fetched and will
     * be available soon.
     * @hide */
    public static final int RKP_TEMPORARILY_UNAVAILABLE = 1;
    /**
     * Permanent failure: The RKP server has declined issuance of keys to this device. Either
     * because the device is not registered with the server or the server considers the device
     * not to be trustworthy.
     * @hide */
    public static final int RKP_SERVER_REFUSED_ISSUANCE = 2;
    /**
     * The RKP server is unavailable due to lack of connectivity. The caller should re-try
     * when the device has connectivity again.
     * @hide */
    public static final int RKP_FETCHING_PENDING_CONNECTIVITY = 3;
    /**
     * The RKP server recognizes the device, but the device may be running vulnerable software,
     * and thus refusing issuance of RKP keys to it.
     *
     * @hide
     */
    public static final int RKP_FETCHING_PENDING_SOFTWARE_REBOOT = 4;

    // Constants for encoding information about the error encountered:
    // Whether the error relates to the system state/implementation as a whole, or a specific key.
    private static final int IS_SYSTEM_ERROR = 1 << 1;
    // Whether the error is permanent.
    private static final int IS_TRANSIENT_ERROR = 1 << 2;
    // Whether the cause of the error is the user not having authenticated recently.
    private static final int REQUIRES_USER_AUTHENTICATION = 1 << 3;

    // The internal error code. NOT to be returned directly to callers or made part of the
    // public API.
    private final int mErrorCode;
    // The Remote Key Provisioning status. Applicable if and only if {@link #mErrorCode} is equal
    // to {@link ResponseCode.OUT_OF_KEYS}.
    private final int mRkpStatus;

    private static int initializeRkpStatusForRegularErrors(int errorCode) {
        // Check if the system code mistakenly called a constructor of KeyStoreException with
        // the OUT_OF_KEYS error code but without RKP status.
        if (errorCode == ResponseCode.OUT_OF_KEYS) {
            Log.e(TAG, "RKP error code without RKP status");
            // Set RKP status to RKP_SERVER_REFUSED_ISSUANCE so that the caller never retries.
            return RKP_SERVER_REFUSED_ISSUANCE;
        } else {
            return RKP_SUCCESS;
        }
    }

    /**
     * @hide
     */
    public KeyStoreException(int errorCode, @Nullable String message) {
        super(message);
        mErrorCode = errorCode;
        mRkpStatus = initializeRkpStatusForRegularErrors(errorCode);
    }

    /**
     * @hide
     */
    public KeyStoreException(int errorCode, @Nullable String message,
            @Nullable String keystoreErrorMessage) {
        super(message + " (internal Keystore code: " + errorCode + " message: "
                + keystoreErrorMessage + ")");
        mErrorCode = errorCode;
        mRkpStatus = initializeRkpStatusForRegularErrors(errorCode);
    }

    /**
     * @hide
     */
    public KeyStoreException(int errorCode, @Nullable String message, int rkpStatus) {
        super(message);
        mErrorCode = errorCode;
        mRkpStatus = rkpStatus;
        if (mErrorCode != ResponseCode.OUT_OF_KEYS) {
            Log.e(TAG, "Providing RKP status for error code " + errorCode + " has no effect.");
        }
    }

    /**
     * Returns the internal error code. Only for use by the platform.
     *
     * @hide
     */
    @TestApi
    public int getErrorCode() {
        return mErrorCode;
    }

    /**
     * Returns one of the error codes exported by the class.
     *
     * @return a public error code, one of the values in {@link PublicErrorCode}.
     */
    @PublicErrorCode
    public int getNumericErrorCode() {
        PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
        return failureInfo.errorCode;
    }

    /**
     * Returns true if the failure is a transient failure - that is, performing the same operation
     * again at a late time is likely to succeed.
     *
     * If {@link #isSystemError()} returns true, the transient nature of the failure relates to the
     * device, otherwise relates to the key (so a permanent failure with an existing key likely
     * requires creating another key to repeat the operation with).
     */
    public boolean isTransientFailure() {
        PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
        // Special-case handling for RKP failures:
        if (mRkpStatus != RKP_SUCCESS && mErrorCode == ResponseCode.OUT_OF_KEYS) {
            switch (mRkpStatus) {
                case RKP_TEMPORARILY_UNAVAILABLE:
                case RKP_FETCHING_PENDING_CONNECTIVITY:
                case RKP_FETCHING_PENDING_SOFTWARE_REBOOT:
                    return true;
                case RKP_SERVER_REFUSED_ISSUANCE:
                default:
                    return false;
            }
        }
        return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0;
    }

    /**
     * Indicates whether the failure is due to the device being locked.
     *
     * @return true if the key operation failed because the user has to authenticate
     * (e.g. by unlocking the device).
     */
    public boolean requiresUserAuthentication() {
        PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
        return (failureInfo.indicators & REQUIRES_USER_AUTHENTICATION) != 0;
    }

    /**
     * Indicates whether the error related to the Keystore/KeyMint implementation and not
     * a specific key.
     *
     * @return true if the error is related to the system, not the key in use. System
     * errors indicate a feature isn't working, whereas key-related errors are likely
     * to succeed with a new key.
     */
    public boolean isSystemError() {
        PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
        return (failureInfo.indicators & IS_SYSTEM_ERROR) != 0;
    }

    /**
     * Returns the re-try policy for transient failures. Valid only if
     * {@link #isTransientFailure()} returns {@code True}.
     */
    @RetryPolicy
    public int getRetryPolicy() {
        PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
        // Special-case handling for RKP failures (To be removed in API 34)
        if (mRkpStatus != RKP_SUCCESS) {
            switch (mRkpStatus) {
                case RKP_TEMPORARILY_UNAVAILABLE:
                    return RETRY_WITH_EXPONENTIAL_BACKOFF;
                case RKP_FETCHING_PENDING_CONNECTIVITY:
                    return RETRY_WHEN_CONNECTIVITY_AVAILABLE;
                case RKP_SERVER_REFUSED_ISSUANCE:
                    return RETRY_NEVER;
                case RKP_FETCHING_PENDING_SOFTWARE_REBOOT:
                    return RETRY_AFTER_NEXT_REBOOT;
                default:
                    return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0
                            ? RETRY_WITH_EXPONENTIAL_BACKOFF : RETRY_NEVER;
            }
        }
        switch (mErrorCode) {
            case ResponseCode.OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE:
                return RETRY_AFTER_NEXT_REBOOT;
            case ResponseCode.OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY:
                return RETRY_WHEN_CONNECTIVITY_AVAILABLE;
            default:
                return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0
                        ? RETRY_WITH_EXPONENTIAL_BACKOFF : RETRY_NEVER;
        }
    }

    @Override
    public String toString() {
        String errorCodes = String.format(" (public error code: %d internal Keystore code: %d)",
                getNumericErrorCode(), mErrorCode);
        return super.toString() + errorCodes;
    }

    private static PublicErrorInformation getErrorInformation(int internalErrorCode) {
        PublicErrorInformation errorInfo = sErrorCodeToFailureInfo.get(internalErrorCode);
        if (errorInfo != null) {
            return errorInfo;
        }

        /**
         * KeyStore/keymaster exception with positive error codes coming from the KeyStore and
         * negative ones from keymaster.
         * This is a safety fall-back: All error codes should be present in the map.
         */
        if (internalErrorCode > 0) {
            return GENERAL_KEYSTORE_ERROR;
        } else {
            return GENERAL_KEYMINT_ERROR;
        }
    }

    private static final class PublicErrorInformation {
        public final int indicators;
        public final int errorCode;

        PublicErrorInformation(int indicators, @PublicErrorCode int errorCode) {
            this.indicators = indicators;
            this.errorCode = errorCode;
        }
    }

    private static final PublicErrorInformation GENERAL_KEYMINT_ERROR =
            new PublicErrorInformation(0, ERROR_KEYMINT_FAILURE);

    private static final PublicErrorInformation GENERAL_KEYSTORE_ERROR =
            new PublicErrorInformation(0, ERROR_KEYSTORE_FAILURE);

    private static final PublicErrorInformation KEYMINT_UNIMPLEMENTED_ERROR =
            new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_UNIMPLEMENTED);

    private static final PublicErrorInformation KEYMINT_RETRYABLE_ERROR =
            new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
                    ERROR_KEYMINT_FAILURE);

    private static final PublicErrorInformation KEYMINT_INCORRECT_USAGE_ERROR =
            new PublicErrorInformation(0, ERROR_INCORRECT_USAGE);

    private static final PublicErrorInformation KEYMINT_TEMPORAL_VALIDITY_ERROR =
            new PublicErrorInformation(0, ERROR_KEY_NOT_TEMPORALLY_VALID);


    private static final Map<Integer, PublicErrorInformation> sErrorCodeToFailureInfo =
            new HashMap();

    /**
     * @hide
     */
    @TestApi
    public static boolean hasFailureInfoForError(int internalErrorCode) {
        return sErrorCodeToFailureInfo.containsKey(internalErrorCode);
    }

    static {
        // KeyMint error codes
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OK, GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ROOT_OF_TRUST_ALREADY_SET,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_PURPOSE,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_PURPOSE,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_ALGORITHM,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_ALGORITHM,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_SIZE,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_BLOCK_MODE,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_BLOCK_MODE,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MAC_LENGTH,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_PADDING_MODE,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_PADDING_MODE,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_DIGEST,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_DIGEST,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_EXPIRATION_TIME,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_USER_ID,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_FORMAT,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_KEY_FORMAT,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_VERIFICATION_ALGORITHM,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_EXPORT_OPTIONS_INVALID,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_DELEGATION_NOT_ALLOWED,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID,
                KEYMINT_TEMPORAL_VALIDITY_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_EXPIRED,
                KEYMINT_TEMPORAL_VALIDITY_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
                new PublicErrorInformation(REQUIRES_USER_AUTHENTICATION,
                        ERROR_USER_AUTHENTICATION_REQUIRED));
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OUTPUT_PARAMETER_NULL,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE,
                new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
                        ERROR_KEY_OPERATION_EXPIRED));
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INSUFFICIENT_BUFFER_SPACE,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_VERIFICATION_FAILED,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_TOO_MANY_OPERATIONS,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNEXPECTED_NULL_POINTER,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_KEY_BLOB,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_NOT_ENCRYPTED,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_DECRYPTION_FAILED,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_NOT_SIGNED,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_VERIFICATION_FAILED,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_ARGUMENT,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_TAG,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_TAG,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MEMORY_ALLOCATION_FAILED,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORT_PARAMETER_MISMATCH,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_ACCESS_DENIED,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OPERATION_CANCELLED,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CONCURRENT_ACCESS_CONFLICT,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_BUSY,
                KEYMINT_RETRYABLE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_COMMUNICATION_FAILED,
                KEYMINT_RETRYABLE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_EC_FIELD,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NONCE,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_NONCE,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_MAC_LENGTH,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
                KEYMINT_RETRYABLE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CALLER_NONCE_PROHIBITED,
                GENERAL_KEYMINT_ERROR);
        // Error related to MAX_USES_PER_BOOT, restricting the number of uses per boot.
        // It is not re-tryable.
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_MAX_OPS_EXCEEDED,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_MAC_LENGTH,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_MIN_MAC_LENGTH,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KDF,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_EC_CURVE,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ATTESTATION_CHALLENGE_MISSING,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEYMINT_NOT_CONFIGURED,
                new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_KEYMINT_FAILURE));
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING,
                KEYMINT_RETRYABLE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CANNOT_ATTEST_IDS,
                new PublicErrorInformation(IS_SYSTEM_ERROR,
                        ERROR_ID_ATTESTATION_FAILURE));
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_DEVICE_LOCKED,
                new PublicErrorInformation(IS_SYSTEM_ERROR | REQUIRES_USER_AUTHENTICATION,
                        ERROR_USER_AUTHENTICATION_REQUIRED));
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_STORAGE_KEY_UNSUPPORTED,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_MGF_DIGEST,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MGF_DIGEST,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NOT_BEFORE,
                KEYMINT_INCORRECT_USAGE_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NOT_AFTER,
                KEYMINT_INCORRECT_USAGE_ERROR);
        // This should not be exposed to apps as it's handled by Keystore.
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_HARDWARE_NOT_YET_AVAILABLE,
                GENERAL_KEYMINT_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNIMPLEMENTED,
                KEYMINT_UNIMPLEMENTED_ERROR);
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
                new PublicErrorInformation(IS_SYSTEM_ERROR,
                        ERROR_KEYMINT_FAILURE));
        sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_VERSION_MISMATCH, GENERAL_KEYMINT_ERROR);

        // Keystore error codes
        sErrorCodeToFailureInfo.put(ResponseCode.LOCKED,
                new PublicErrorInformation(REQUIRES_USER_AUTHENTICATION,
                        ERROR_USER_AUTHENTICATION_REQUIRED));
        sErrorCodeToFailureInfo.put(ResponseCode.UNINITIALIZED,
                new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_KEYSTORE_UNINITIALIZED));
        sErrorCodeToFailureInfo.put(ResponseCode.SYSTEM_ERROR,
                new PublicErrorInformation(IS_SYSTEM_ERROR,
                        ERROR_INTERNAL_SYSTEM_ERROR));
        sErrorCodeToFailureInfo.put(ResponseCode.PERMISSION_DENIED,
                new PublicErrorInformation(0, ERROR_PERMISSION_DENIED));
        sErrorCodeToFailureInfo.put(ResponseCode.KEY_NOT_FOUND,
                new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
        sErrorCodeToFailureInfo.put(ResponseCode.VALUE_CORRUPTED,
                new PublicErrorInformation(0, ERROR_KEY_CORRUPTED));
        sErrorCodeToFailureInfo.put(ResponseCode.KEY_PERMANENTLY_INVALIDATED,
                new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
        sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS,
                new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_ATTESTATION_KEYS_UNAVAILABLE));
        sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
                new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
                        ERROR_DEVICE_REQUIRES_UPGRADE_FOR_ATTESTATION));
        sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
                new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
                        ERROR_ATTESTATION_KEYS_UNAVAILABLE));
        sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS_TRANSIENT_ERROR,
                new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
                        ERROR_ATTESTATION_KEYS_UNAVAILABLE));
        sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS_PERMANENT_ERROR,
                new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_ATTESTATION_KEYS_UNAVAILABLE));
    }
}
