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

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

Merge "Use ServiceSpecificException in RecoverableKeyStoreMgr"

parents a068cd68 97e55583
Loading
Loading
Loading
Loading
+51 −22
Original line number Diff line number Diff line
@@ -43,18 +43,61 @@ public class RecoverableKeyStoreLoader {

    public static final int NO_ERROR = KeyStore.NO_ERROR;
    public static final int SYSTEM_ERROR = KeyStore.SYSTEM_ERROR;

    /**
     * Failed because the loader has not been initialized with a recovery public key yet.
     */
    public static final int ERROR_UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20;

    /**
     * Failed because no snapshot is yet pending to be synced for the user.
     */
    public static final int ERROR_NO_SNAPSHOT_PENDING = 21;

    /**
     * Failed due to an error internal to AndroidKeyStore.
     */
    public static final int ERROR_KEYSTORE_INTERNAL_ERROR = 22;

    /**
     * Failed because the user does not have a lock screen set.
     */
    public static final int ERROR_INSECURE_USER = 24;

    /**
     * Failed because of an internal database error.
     */
    public static final int ERROR_DATABASE_ERROR = 25;

    /**
     * Failed because the provided certificate was not a valid X509 certificate.
     */
    public static final int ERROR_BAD_X509_CERTIFICATE = 26;

    /**
     * Should never be thrown - some algorithm that all AOSP implementations must support is
     * not available.
     */
    public static final int ERROR_UNEXPECTED_MISSING_ALGORITHM = 27;

    /**
     * The caller is attempting to perform an operation that is not yet fully supported in the API.
     */
    public static final int ERROR_NOT_YET_SUPPORTED = 28;

    /**
     * Error thrown if decryption failed. This might be because the tag is wrong, the key is wrong,
     * the data has become corrupted, the data has been tampered with, etc.
     */
    public static final int ERROR_DECRYPTION_FAILED = 29;

    /**
     * Rate limit is enforced to prevent using too many trusted remote devices, since each device
     * can have its own number of user secret guesses allowed.
     *
     * @hide
     */
    public static final int RATE_LIMIT_EXCEEDED = 21;
    public static final int ERROR_RATE_LIMIT_EXCEEDED = 30;

    /** Key has been successfully synced. */
    public static final int RECOVERY_STATUS_SYNCED = 0;
@@ -89,12 +132,13 @@ public class RecoverableKeyStoreLoader {
        /**
         * Creates new {@link #RecoverableKeyStoreLoaderException} instance from the error code.
         *
         * @param errorCode
         * @param errorCode An error code, as listed at the top of this file.
         * @param message The associated error message.
         * @hide
         */
        public static RecoverableKeyStoreLoaderException fromErrorCode(int errorCode) {
            return new RecoverableKeyStoreLoaderException(
                    errorCode, getMessageFromErrorCode(errorCode));
        public static RecoverableKeyStoreLoaderException fromErrorCode(
                int errorCode, String message) {
            return new RecoverableKeyStoreLoaderException(errorCode, message);
        }

        /**
@@ -106,33 +150,18 @@ public class RecoverableKeyStoreLoader {
         */
        static RecoverableKeyStoreLoaderException fromServiceSpecificException(
                ServiceSpecificException e) throws RecoverableKeyStoreLoaderException {
            throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode);
            throw RecoverableKeyStoreLoaderException.fromErrorCode(e.errorCode, e.getMessage());
        }

        private RecoverableKeyStoreLoaderException(int errorCode, String message) {
            super(message);
            mErrorCode = errorCode;
        }

        /** Returns errorCode. */
        public int getErrorCode() {
            return mErrorCode;
        }

        /** @hide */
        private static String getMessageFromErrorCode(int errorCode) {
            switch (errorCode) {
                case NO_ERROR:
                    return "OK";
                case SYSTEM_ERROR:
                    return "System error";
                case ERROR_UNINITIALIZED_RECOVERY_PUBLIC_KEY:
                    return "Recovery service is not initialized";
                case RATE_LIMIT_EXCEEDED:
                    return "Rate limit exceeded";
                default:
                    return String.valueOf("Unknown error code " + errorCode);
            }
        }
    }

    /**
+31 −34
Original line number Diff line number Diff line
@@ -16,6 +16,19 @@

package com.android.server.locksettings.recoverablekeystore;

import static android.security.recoverablekeystore.RecoverableKeyStoreLoader
        .ERROR_BAD_X509_CERTIFICATE;
import static android.security.recoverablekeystore.RecoverableKeyStoreLoader.ERROR_DATABASE_ERROR;
import static android.security.recoverablekeystore.RecoverableKeyStoreLoader
        .ERROR_DECRYPTION_FAILED;
import static android.security.recoverablekeystore.RecoverableKeyStoreLoader.ERROR_INSECURE_USER;
import static android.security.recoverablekeystore.RecoverableKeyStoreLoader
        .ERROR_KEYSTORE_INTERNAL_ERROR;
import static android.security.recoverablekeystore.RecoverableKeyStoreLoader
        .ERROR_NOT_YET_SUPPORTED;
import static android.security.recoverablekeystore.RecoverableKeyStoreLoader
        .ERROR_UNEXPECTED_MISSING_ALGORITHM;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
@@ -63,12 +76,6 @@ import javax.crypto.AEADBadTagException;
public class RecoverableKeyStoreManager {
    private static final String TAG = "RecoverableKeyStoreMgr";

    // TODO: move error codes to RecoverableKeyStoreLoader.
    private static int ERROR_INSECURE_USER = 1;
    private static int ERROR_KEYSTORE_INTERNAL_ERROR = 2;
    private static int ERROR_DATABASE_ERROR = 3;
    private static int ERROR_RECOVERY_SESSION_NOT_FOUND = 4;

    private static RecoverableKeyStoreManager mInstance;

    private final Context mContext;
@@ -115,8 +122,8 @@ public class RecoverableKeyStoreManager {
        try {
            mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
        } catch (NoSuchAlgorithmException e) {
            // Impossible: all AOSP implementations must support AES.
            throw new RuntimeException(e);
            Log.wtf(TAG, "AES keygen algorithm not available. AOSP must support this.", e);
            throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage());
        }
    }

@@ -133,10 +140,11 @@ public class RecoverableKeyStoreManager {
            X509EncodedKeySpec pkSpec = new X509EncodedKeySpec(signedPublicKeyList);
            publicKey = kf.generatePublic(pkSpec);
        } catch (NoSuchAlgorithmException e) {
            // Should never happen
            throw new RuntimeException(e);
            Log.wtf(TAG, "EC algorithm not available. AOSP must support this.", e);
            throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage());
        } catch (InvalidKeySpecException e) {
            throw new RemoteException("Invalid public key for the recovery service");
            throw new ServiceSpecificException(
                    ERROR_BAD_X509_CERTIFICATE, "Not a valid X509 certificate.");
        }
        mDatabase.setRecoveryServicePublicKey(userId, Binder.getCallingUid(), publicKey);
    }
@@ -295,7 +303,9 @@ public class RecoverableKeyStoreManager {

        if (secrets.size() != 1) {
            // TODO: support multiple secrets
            throw new RemoteException("Only a single KeyStoreRecoveryMetadata is supported");
            throw new ServiceSpecificException(
                    ERROR_NOT_YET_SUPPORTED,
                    "Only a single KeyStoreRecoveryMetadata is supported");
        }

        byte[] keyClaimant = KeySyncUtils.generateKeyClaimant();
@@ -314,18 +324,11 @@ public class RecoverableKeyStoreManager {
                    thmKfHash,
                    keyClaimant);
        } catch (NoSuchAlgorithmException e) {
            // Should never happen: all the algorithms used are required by AOSP implementations.
            throw new RemoteException(
                    "Missing required algorithm",
                    e,
                    /*enableSuppression=*/ true,
                    /*writeableStackTrace=*/ true);
            Log.wtf(TAG, "SecureBox algorithm missing. AOSP must support this.", e);
            throw new ServiceSpecificException(ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage());
        } catch (InvalidKeySpecException | InvalidKeyException e) {
            throw new RemoteException(
                    "Not a valid X509 key",
                    e,
                    /*enableSuppression=*/ true,
                    /*writeableStackTrace=*/ true);
            throw new ServiceSpecificException(ERROR_BAD_X509_CERTIFICATE,
                    "Not a valid X509 key");
        }
    }

@@ -439,18 +442,12 @@ public class RecoverableKeyStoreManager {
                        KeySyncUtils.decryptApplicationKey(recoveryKey, encryptedKeyMaterial);
                keyMaterialByAlias.put(alias, keyMaterial);
            } catch (NoSuchAlgorithmException e) {
                // Should never happen: all the algorithms used are required by AOSP implementations
                throw new RemoteException(
                        "Missing required algorithm",
                        e,
                    /*enableSuppression=*/ true,
                    /*writeableStackTrace=*/ true);
                Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e);
                throw new ServiceSpecificException(
                        ERROR_UNEXPECTED_MISSING_ALGORITHM, e.getMessage());
            } catch (InvalidKeyException | AEADBadTagException e) {
                throw new RemoteException(
                        "Failed to recover key with alias '" + alias + "'",
                        e,
                    /*enableSuppression=*/ true,
                    /*writeableStackTrace=*/ true);
                throw new ServiceSpecificException(ERROR_DECRYPTION_FAILED,
                        "Failed to recover key with alias '" + alias + "': " + e.getMessage());
            }
        }
        return keyMaterialByAlias;