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

Commit 7d8c78a2 authored by Dmitry Dementyev's avatar Dmitry Dementyev
Browse files

Refactor KeyStore Recovery Manager.

1) Parameters -> Params
2) Use byte[] for serivice parameters.
2) Move Exception into separate class.

Bug: 66499222
Test: adb shell am instrument -w -e package \
com.android.server.locksettings.recoverablekeystore \
com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner

Change-Id: I1b9a8748830f7deb9eeb57693f5a818a49a7aabe
parent 66af0e82
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -17,4 +17,4 @@
package android.security.keystore;

/* @hide */
parcelable KeyDerivationParameters;
parcelable KeyDerivationParams;
+13 −18
Original line number Diff line number Diff line
@@ -28,27 +28,22 @@ import java.lang.annotation.RetentionPolicy;

/**
 * Collection of parameters which define a key derivation function.
 * Supports
 * Currently only supports salted SHA-256
 *
 * <ul>
 * <li>SHA256
 * <li>Argon2id
 * </ul>
 * @hide
 */
public final class KeyDerivationParameters implements Parcelable {
public final class KeyDerivationParams implements Parcelable {
    private final int mAlgorithm;
    private byte[] mSalt;

    /** @hide */
    @Retention(RetentionPolicy.SOURCE)
    @IntDef({ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
    @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
    public @interface KeyDerivationAlgorithm {
    }

    /**
     * Salted SHA256
     * @hide
     */
    public static final int ALGORITHM_SHA256 = 1;

@@ -62,11 +57,11 @@ public final class KeyDerivationParameters implements Parcelable {
    /**
     * Creates instance of the class to to derive key using salted SHA256 hash.
     */
    public static KeyDerivationParameters createSha256Parameters(@NonNull byte[] salt) {
        return new KeyDerivationParameters(ALGORITHM_SHA256, salt);
    public static KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
        return new KeyDerivationParams(ALGORITHM_SHA256, salt);
    }

    private KeyDerivationParameters(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
    private KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
        mAlgorithm = algorithm;
        mSalt = Preconditions.checkNotNull(salt);
    }
@@ -85,14 +80,14 @@ public final class KeyDerivationParameters implements Parcelable {
        return mSalt;
    }

    public static final Parcelable.Creator<KeyDerivationParameters> CREATOR =
            new Parcelable.Creator<KeyDerivationParameters>() {
        public KeyDerivationParameters createFromParcel(Parcel in) {
                return new KeyDerivationParameters(in);
    public static final Parcelable.Creator<KeyDerivationParams> CREATOR =
            new Parcelable.Creator<KeyDerivationParams>() {
        public KeyDerivationParams createFromParcel(Parcel in) {
                return new KeyDerivationParams(in);
        }

        public KeyDerivationParameters[] newArray(int length) {
            return new KeyDerivationParameters[length];
        public KeyDerivationParams[] newArray(int length) {
            return new KeyDerivationParams[length];
        }
    };

@@ -108,7 +103,7 @@ public final class KeyDerivationParameters implements Parcelable {
    /**
     * @hide
     */
    protected KeyDerivationParameters(Parcel in) {
    protected KeyDerivationParams(Parcel in) {
        mAlgorithm = in.readInt();
        mSalt = in.createByteArray();
    }
+1 −2
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ import java.util.List;
 * @hide
 */
public final class RecoveryData implements Parcelable {
    private Integer mSnapshotVersion;
    private int mSnapshotVersion;
    private List<RecoveryMetadata> mRecoveryMetadata;
    private List<EntryRecoveryData> mEntryRecoveryData;
    private byte[] mEncryptedRecoveryKeyBlob;
@@ -163,7 +163,6 @@ public final class RecoveryData implements Parcelable {
         * @throws NullPointerException if some required fields were not set.
         */
        public @NonNull RecoveryData build() {
            Preconditions.checkNotNull(mInstance.mSnapshotVersion);
            Preconditions.checkCollectionElementsNotNull(mInstance.mRecoveryMetadata,
                    "recoveryMetadata");
            Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
+8 −112
Original line number Diff line number Diff line
@@ -23,8 +23,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.security.KeyStore;
import android.util.AndroidException;

import com.android.internal.widget.ILockSettings;

@@ -39,64 +37,6 @@ import java.util.Map;
 */
public class RecoveryManager {

    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 ERROR_RATE_LIMIT_EXCEEDED = 30;

    /** Key has been successfully synced. */
    public static final int RECOVERY_STATUS_SYNCED = 0;
    /** Waiting for recovery agent to sync the key. */
@@ -121,47 +61,6 @@ public class RecoveryManager {
        return new RecoveryManager(lockSettings);
    }

    /**
     * Exceptions returned by {@link RecoveryManager}.
     */
    public static class RecoveryManagerException extends AndroidException {
        private int mErrorCode;

        /**
         * Creates new {@link #RecoveryManagerException} instance from the error code.
         *
         * @param errorCode An error code, as listed at the top of this file.
         * @param message The associated error message.
         * @hide
         */
        public static RecoveryManagerException fromErrorCode(
                int errorCode, String message) {
            return new RecoveryManagerException(errorCode, message);
        }

        /**
         * Creates new {@link #RecoveryManagerException} from {@link
         * ServiceSpecificException}.
         *
         * @param e exception thrown on service side.
         * @hide
         */
        static RecoveryManagerException fromServiceSpecificException(
                ServiceSpecificException e) throws RecoveryManagerException {
            throw RecoveryManagerException.fromErrorCode(e.errorCode, e.getMessage());
        }

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

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

    /**
     * Initializes key recovery service for the calling application. RecoveryManager
     * randomly chooses one of the keys from the list and keeps it to use for future key export
@@ -260,15 +159,14 @@ public class RecoveryManager {
     * {@code RecoveryData.getEncryptedRecoveryKeyBlob()}. The same value must be included
     * in vaultParams {@link #startRecoverySession}
     *
     * @param serverParameters included in recovery key blob.
     * @param serverParams included in recovery key blob.
     * @see #getRecoveryData
     * @throws RecoveryManagerException If parameters rotation is rate limited.
     * @hide
     */
    public void setServerParameters(long serverParameters)
            throws RecoveryManagerException {
    public void setServerParams(byte[] serverParams) throws RecoveryManagerException {
        try {
            mBinder.setServerParameters(serverParameters);
            mBinder.setServerParams(serverParams);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        } catch (ServiceSpecificException e) {
@@ -309,20 +207,17 @@ public class RecoveryManager {
     *   <li>{@link #RECOVERY_STATUS_PERMANENT_FAILURE}
     * </ul>
     *
     * @param packageName Application whose recoverable keys' statuses are to be retrieved. if
     *     {@code null} caller's package will be used.
     * @return {@code Map} from KeyStore alias to recovery status.
     * @see #setRecoveryStatus
     * @hide
     */
    public Map<String, Integer> getRecoveryStatus(@Nullable String packageName)
    public Map<String, Integer> getRecoveryStatus()
            throws RecoveryManagerException {
        try {
            // IPC doesn't support generic Maps.
            @SuppressWarnings("unchecked")
            Map<String, Integer> result =
                    (Map<String, Integer>)
                            mBinder.getRecoveryStatus(packageName);
                    (Map<String, Integer>) mBinder.getRecoveryStatus(/*packageName=*/ null);
            return result;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
@@ -414,8 +309,9 @@ public class RecoveryManager {
     * return recovery key.
     *
     * @param sessionId ID for recovery session.
     * @param verifierPublicKey Certificate with Public key used to create the recovery blob on the
     *     source device. Keystore will verify the certificate using root of trust.
     * @param verifierPublicKey Encoded {@code java.security.cert.X509Certificate} with Public key
     * used to create the recovery blob on the source device.
     * Keystore will verify the certificate using root of trust.
     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
     *     Used to limit number of guesses.
     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
+111 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.keystore;

import android.os.ServiceSpecificException;

/**
 * Exception thrown by {@link RecoveryManager} methods.
 *
 * @hide
 */
public class RecoveryManagerException extends Exception {
    /**
     * 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;

    /**
     * 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 = 28;

    /**
     * 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 ERROR_RATE_LIMIT_EXCEEDED = 29;

    private int mErrorCode;

    /**
     * Creates new {@link #RecoveryManagerException} instance from the error code.
     *
     * @param errorCode An error code, as listed at the top of this file.
     * @param message The associated error message.
     * @hide
     */
    public static RecoveryManagerException fromErrorCode(
            int errorCode, String message) {
        return new RecoveryManagerException(errorCode, message);
    }
    /**
     * Creates new {@link #RecoveryManagerException} from {@link
     * ServiceSpecificException}.
     *
     * @param e exception thrown on service side.
     * @hide
     */
    static RecoveryManagerException fromServiceSpecificException(
            ServiceSpecificException e) throws RecoveryManagerException {
        throw RecoveryManagerException.fromErrorCode(e.errorCode, e.getMessage());
    }

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

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