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

Commit d1af38a4 authored by Alex Klyubin's avatar Alex Klyubin Committed by Gerrit Code Review
Browse files

Merge "Use JCA names for block modes, paddings, and digests."

parents 8f0e0c1f 5927c9f1
Loading
Loading
Loading
Loading
+53 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package android.security.keymaster;
import android.os.Parcel;
import android.os.Parcelable;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
@@ -30,10 +32,12 @@ public class KeyCharacteristics implements Parcelable {

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

                @Override
                public KeyCharacteristics[] newArray(int length) {
                    return new KeyCharacteristics[length];
                }
@@ -50,6 +54,7 @@ public class KeyCharacteristics implements Parcelable {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        swEnforced.writeToParcel(out, flags);
        hwEnforced.writeToParcel(out, flags);
@@ -59,5 +64,53 @@ public class KeyCharacteristics implements Parcelable {
        swEnforced = KeymasterArguments.CREATOR.createFromParcel(in);
        hwEnforced = KeymasterArguments.CREATOR.createFromParcel(in);
    }

    public Integer getInteger(int tag) {
        if (hwEnforced.containsTag(tag)) {
            return hwEnforced.getInt(tag, -1);
        } else if (swEnforced.containsTag(tag)) {
            return swEnforced.getInt(tag, -1);
        } else {
            return null;
        }
    }

    public int getInt(int tag, int defaultValue) {
        Integer result = getInteger(tag);
        return (result != null) ? result : defaultValue;
    }

    public List<Integer> getInts(int tag) {
        List<Integer> result = new ArrayList<Integer>();
        result.addAll(hwEnforced.getInts(tag));
        result.addAll(swEnforced.getInts(tag));
        return result;
    }

    public Date getDate(int tag) {
        Date result = hwEnforced.getDate(tag, null);
        if (result == null) {
            result = swEnforced.getDate(tag, null);
        }
        return result;
    }

    public Date getDate(int tag, Date defaultValue) {
        if (hwEnforced.containsTag(tag)) {
            return hwEnforced.getDate(tag, null);
        } else if (hwEnforced.containsTag(tag)) {
            return swEnforced.getDate(tag, null);
        } else {
            return defaultValue;
        }
    }

    public boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) {
        if (keyCharacteristics.hwEnforced.containsTag(tag)) {
            return keyCharacteristics.hwEnforced.getBoolean(tag, false);
        } else {
            return keyCharacteristics.swEnforced.getBoolean(tag, false);
        }
    }
}
+9 −0
Original line number Diff line number Diff line
@@ -34,9 +34,12 @@ public class KeymasterArguments implements Parcelable {

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

                @Override
                public KeymasterArguments[] newArray(int size) {
                    return new KeymasterArguments[size];
                }
@@ -54,6 +57,12 @@ public class KeymasterArguments implements Parcelable {
        mArguments.add(new KeymasterIntArgument(tag, value));
    }

    public void addInts(int tag, int... values) {
        for (int value : values) {
            addInt(tag, value);
        }
    }

    public void addBoolean(int tag) {
        mArguments.add(new KeymasterBooleanArgument(tag));
    }
+52 −63
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package android.security;
import com.android.org.conscrypt.OpenSSLEngine;
import com.android.org.conscrypt.OpenSSLKeyHolder;

import libcore.util.EmptyArray;

import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
@@ -46,6 +48,7 @@ import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@@ -112,13 +115,6 @@ public class AndroidKeyStore extends KeyStoreSpi {
            if (keymasterAlgorithm == -1) {
                throw new UnrecoverableKeyException("Key algorithm unknown");
            }
            @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm;
            try {
                keyAlgorithm = KeyStoreKeyConstraints.Algorithm.fromKeymaster(keymasterAlgorithm);
            } catch (IllegalArgumentException e) {
                throw (UnrecoverableKeyException)
                        new UnrecoverableKeyException("Unsupported key algorithm").initCause(e);
            }

            int keymasterDigest =
                    keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1);
@@ -126,20 +122,11 @@ public class AndroidKeyStore extends KeyStoreSpi {
                keymasterDigest =
                        keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_DIGEST, -1);
            }
            @KeyStoreKeyConstraints.DigestEnum Integer digest = null;
            if (keymasterDigest != -1) {
                try {
                    digest = KeyStoreKeyConstraints.Digest.fromKeymaster(keymasterDigest);
                } catch (IllegalArgumentException e) {
                    throw (UnrecoverableKeyException)
                            new UnrecoverableKeyException("Unsupported digest").initCause(e);
                }
            }

            String keyAlgorithmString;
            try {
                keyAlgorithmString = KeyStoreKeyConstraints.Algorithm.toJCASecretKeyAlgorithm(
                    keyAlgorithm, digest);
                keyAlgorithmString = KeymasterUtils.getJcaSecretKeyAlgorithm(
                        keymasterAlgorithm, keymasterDigest);
            } catch (IllegalArgumentException e) {
                throw (UnrecoverableKeyException)
                        new UnrecoverableKeyException("Unsupported secret key type").initCause(e);
@@ -456,90 +443,92 @@ public class AndroidKeyStore extends KeyStoreSpi {
        }

        String keyAlgorithmString = key.getAlgorithm();
        @KeyStoreKeyConstraints.AlgorithmEnum int keyAlgorithm;
        @KeyStoreKeyConstraints.DigestEnum Integer digest;
        int keymasterAlgorithm;
        int keymasterDigest;
        try {
            keyAlgorithm =
                    KeyStoreKeyConstraints.Algorithm.fromJCASecretKeyAlgorithm(keyAlgorithmString);
            digest = KeyStoreKeyConstraints.Digest.fromJCASecretKeyAlgorithm(keyAlgorithmString);
            keymasterAlgorithm = KeymasterUtils.getKeymasterAlgorithmFromJcaSecretKeyAlgorithm(
                    keyAlgorithmString);
            keymasterDigest =
                    KeymasterUtils.getKeymasterDigestfromJcaSecretKeyAlgorithm(keyAlgorithmString);
        } catch (IllegalArgumentException e) {
            throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
        }

        KeymasterArguments args = new KeymasterArguments();
        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM,
                KeyStoreKeyConstraints.Algorithm.toKeymaster(keyAlgorithm));
        args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);

        @KeyStoreKeyConstraints.DigestEnum int digests;
        int[] keymasterDigests;
        if (params.isDigestsSpecified()) {
            // Digest(s) specified in parameters
            if (digest != null) {
            keymasterDigests =
                    KeymasterUtils.getKeymasterDigestsFromJcaDigestAlgorithms(params.getDigests());
            if (keymasterDigest != -1) {
                // Digest also specified in the JCA key algorithm name.
                if ((params.getDigests() & digest) != digest) {
                if (!com.android.internal.util.ArrayUtils.contains(
                        keymasterDigests, keymasterDigest)) {
                    throw new KeyStoreException("Key digest mismatch"
                            + ". Key: " + keyAlgorithmString
                            + ", parameter spec: "
                            + KeyStoreKeyConstraints.Digest.allToString(params.getDigests()));
                            + ", parameter spec: " + Arrays.asList(params.getDigests()));
                }
            }
            digests = params.getDigests();
        } else {
            // No digest specified in parameters
            if (digest != null) {
            if (keymasterDigest != -1) {
                // Digest specified in the JCA key algorithm name.
                digests = digest;
                keymasterDigests = new int[] {keymasterDigest};
            } else {
                digests = 0;
            }
                keymasterDigests = EmptyArray.INT;
            }
        for (int keymasterDigest : KeyStoreKeyConstraints.Digest.allToKeymaster(digests)) {
            args.addInt(KeymasterDefs.KM_TAG_DIGEST, keymasterDigest);
        }
        if (digests != 0) {
        args.addInts(KeymasterDefs.KM_TAG_DIGEST, keymasterDigests);
        if (keymasterDigests.length > 0) {
            // TODO: Remove MAC length constraint once Keymaster API no longer requires it.
            // This code will blow up if mode than one digest is specified.
            Integer digestOutputSizeBytes =
                    KeyStoreKeyConstraints.Digest.getOutputSizeBytes(digest);
            if (digestOutputSizeBytes != null) {
            int digestOutputSizeBytes =
                    KeymasterUtils.getDigestOutputSizeBytes(keymasterDigests[0]);
            if (digestOutputSizeBytes != -1) {
                // TODO: Switch to bits instead of bytes, once this is fixed in Keymaster
                args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, digestOutputSizeBytes);
            }
        }
        if (keyAlgorithm == KeyStoreKeyConstraints.Algorithm.HMAC) {
            if (digests == 0) {
        if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
            if (keymasterDigests.length == 0) {
                throw new KeyStoreException("At least one digest algorithm must be specified"
                        + " for key algorithm " + keyAlgorithmString);
            }
        }

        @KeyStoreKeyConstraints.PurposeEnum int purposes = params.getPurposes();
        @KeyStoreKeyConstraints.BlockModeEnum int blockModes = params.getBlockModes();
        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
        @KeyStoreKeyProperties.PurposeEnum int purposes = params.getPurposes();
        int[] keymasterBlockModes = KeymasterUtils.getKeymasterBlockModesFromJcaBlockModes(
                params.getBlockModes());
        if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
                && (params.isRandomizedEncryptionRequired())) {
            @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes =
                    blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES;
            if (incompatibleBlockModes != 0) {
                throw new KeyStoreException("Randomized encryption (IND-CPA) required but may be"
                        + " violated by block mode(s): "
                        + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes)
            for (int keymasterBlockMode : keymasterBlockModes) {
                if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) {
                    throw new KeyStoreException(
                            "Randomized encryption (IND-CPA) required but may be violated by block"
                            + " mode: "
                            + KeymasterUtils.getJcaBlockModeFromKeymasterBlockMode(
                                    keymasterBlockMode)
                            + ". See KeyStoreParameter documentation.");
                }
            }
        for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
        }
        for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) {
            args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
        }
        for (int keymasterPadding :
            KeyStoreKeyConstraints.Padding.allToKeymaster(params.getPaddings())) {
            args.addInt(KeymasterDefs.KM_TAG_PADDING, keymasterPadding);
        for (int keymasterPurpose : KeyStoreKeyProperties.Purpose.allToKeymaster(purposes)) {
            args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
        }
        args.addInts(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
        int[] keymasterPaddings = ArrayUtils.concat(
                KeymasterUtils.getKeymasterPaddingsFromJcaEncryptionPaddings(
                        params.getEncryptionPaddings()),
                KeymasterUtils.getKeymasterPaddingsFromJcaSignaturePaddings(
                        params.getSignaturePaddings()));
        args.addInts(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
        if (params.getUserAuthenticators() == 0) {
            args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
        } else {
            args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
                    KeyStoreKeyConstraints.UserAuthenticator.allToKeymaster(
                    KeyStoreKeyProperties.UserAuthenticator.allToKeymaster(
                            params.getUserAuthenticators()));
        }
        if (params.getUserAuthenticationValidityDurationSeconds() != -1) {
@@ -559,7 +548,7 @@ public class AndroidKeyStore extends KeyStoreSpi {
        // TODO: Remove this once keymaster does not require us to specify the size of imported key.
        args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);

        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
        if (((purposes & KeyStoreKeyProperties.Purpose.ENCRYPT) != 0)
                && (!params.isRandomizedEncryptionRequired())) {
            // Permit caller-provided IV when encrypting with this key
            args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
+62 −0
Original line number Diff line number Diff line
package android.security;

import libcore.util.EmptyArray;

/**
 * @hide
 */
abstract class ArrayUtils {
    private ArrayUtils() {}

    public static String[] nullToEmpty(String[] array) {
        return (array != null) ? array : EmptyArray.STRING;
    }

    public static String[] cloneIfNotEmpty(String[] array) {
        return ((array != null) && (array.length > 0)) ? array.clone() : array;
    }

    public static byte[] concat(byte[] arr1, byte[] arr2) {
        return concat(arr1, 0, (arr1 != null) ? arr1.length : 0,
                arr2, 0, (arr2 != null) ? arr2.length : 0);
    }

    public static byte[] concat(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2,
            int len2) {
        if (len1 == 0) {
            return subarray(arr2, offset2, len2);
        } else if (len2 == 0) {
            return subarray(arr1, offset1, len1);
        } else {
            byte[] result = new byte[len1 + len2];
            System.arraycopy(arr1, offset1, result, 0, len1);
            System.arraycopy(arr2, offset2, result, len1, len2);
            return result;
        }
    }

    public static byte[] subarray(byte[] arr, int offset, int len) {
        if (len == 0) {
            return EmptyArray.BYTE;
        }
        if ((offset == 0) && (len == arr.length)) {
            return arr;
        }
        byte[] result = new byte[len];
        System.arraycopy(arr, offset, result, 0, len);
        return result;
    }

    public static int[] concat(int[] arr1, int[] arr2) {
        if ((arr1 == null) || (arr1.length == 0)) {
            return arr2;
        } else if ((arr2 == null) || (arr2.length == 0)) {
            return arr1;
        } else {
            int[] result = new int[arr1.length + arr2.length];
            System.arraycopy(arr1, 0, result, 0, arr1.length);
            System.arraycopy(arr2, 0, result, arr1.length, arr2.length);
            return result;
        }
    }
}
+39 −37
Original line number Diff line number Diff line
@@ -49,11 +49,11 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
    private final Date mKeyValidityStart;
    private final Date mKeyValidityForOriginationEnd;
    private final Date mKeyValidityForConsumptionEnd;
    private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
    private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
    private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
    private final @KeyStoreKeyProperties.PurposeEnum int mPurposes;
    private final String[] mEncryptionPaddings;
    private final String[] mBlockModes;
    private final boolean mRandomizedEncryptionRequired;
    private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
    private final @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators;
    private final int mUserAuthenticationValidityDurationSeconds;

    private KeyGeneratorSpec(
@@ -64,11 +64,11 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
            Date keyValidityStart,
            Date keyValidityForOriginationEnd,
            Date keyValidityForConsumptionEnd,
            @KeyStoreKeyConstraints.PurposeEnum int purposes,
            @KeyStoreKeyConstraints.PaddingEnum int paddings,
            @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
            @KeyStoreKeyProperties.PurposeEnum int purposes,
            String[] encryptionPaddings,
            String[] blockModes,
            boolean randomizedEncryptionRequired,
            @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
            @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators,
            int userAuthenticationValidityDurationSeconds) {
        if (context == null) {
            throw new IllegalArgumentException("context == null");
@@ -88,8 +88,9 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
        mKeyValidityForOriginationEnd = keyValidityForOriginationEnd;
        mKeyValidityForConsumptionEnd = keyValidityForConsumptionEnd;
        mPurposes = purposes;
        mPaddings = paddings;
        mBlockModes = blockModes;
        mEncryptionPaddings =
                ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(encryptionPaddings));
        mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
        mRandomizedEncryptionRequired = randomizedEncryptionRequired;
        mUserAuthenticators = userAuthenticators;
        mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
@@ -154,22 +155,22 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
    /**
     * Gets the set of purposes for which the key can be used.
     */
    public @KeyStoreKeyConstraints.PurposeEnum int getPurposes() {
    public @KeyStoreKeyProperties.PurposeEnum int getPurposes() {
        return mPurposes;
    }

    /**
     * Gets the set of padding schemes to which the key is restricted.
     * Gets the set of padding schemes with which the key can be used when encrypting/decrypting.
     */
    public @KeyStoreKeyConstraints.PaddingEnum int getPaddings() {
        return mPaddings;
    public String[] getEncryptionPaddings() {
        return ArrayUtils.cloneIfNotEmpty(mEncryptionPaddings);
    }

    /**
     * Gets the set of block modes to which the key is restricted.
     * Gets the set of block modes with which the key can be used.
     */
    public @KeyStoreKeyConstraints.BlockModeEnum int getBlockModes() {
        return mBlockModes;
    public String[] getBlockModes() {
        return ArrayUtils.cloneIfNotEmpty(mBlockModes);
    }

    /**
@@ -191,7 +192,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
     *
     * @return user authenticators or {@code 0} if the key can be used without user authentication.
     */
    public @KeyStoreKeyConstraints.UserAuthenticatorEnum int getUserAuthenticators() {
    public @KeyStoreKeyProperties.UserAuthenticatorEnum int getUserAuthenticators() {
        return mUserAuthenticators;
    }

@@ -221,11 +222,11 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
        private Date mKeyValidityStart;
        private Date mKeyValidityForOriginationEnd;
        private Date mKeyValidityForConsumptionEnd;
        private @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
        private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
        private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
        private @KeyStoreKeyProperties.PurposeEnum int mPurposes;
        private String[] mEncryptionPaddings;
        private String[] mBlockModes;
        private boolean mRandomizedEncryptionRequired = true;
        private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
        private @KeyStoreKeyProperties.UserAuthenticatorEnum int mUserAuthenticators;
        private int mUserAuthenticationValidityDurationSeconds = -1;

        /**
@@ -332,34 +333,35 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
        }

        /**
         * Restricts the key to being used only for the provided set of purposes.
         * Sets the set of purposes for which the key can be used.
         *
         * <p>This restriction must be specified. There is no default.
         * <p>This must be specified for all keys. There is no default.
         */
        public Builder setPurposes(@KeyStoreKeyConstraints.PurposeEnum int purposes) {
        public Builder setPurposes(@KeyStoreKeyProperties.PurposeEnum int purposes) {
            mPurposes = purposes;
            return this;
        }

        /**
         * Restricts the key to being used only with the provided padding schemes. Attempts to use
         * the key with any other padding will be rejected.
         * Sets the set of padding schemes with which the key can be used when
         * encrypting/decrypting. Attempts to use the key with any other padding scheme will be
         * rejected.
         *
         * <p>This restriction must be specified for keys which are used for encryption/decryption.
         * <p>This must be specified for keys which are used for encryption/decryption.
         */
        public Builder setPaddings(@KeyStoreKeyConstraints.PaddingEnum int paddings) {
            mPaddings = paddings;
        public Builder setEncryptionPaddings(String... paddings) {
            mEncryptionPaddings = ArrayUtils.cloneIfNotEmpty(paddings);
            return this;
        }

        /**
         * Restricts the key to being used only with the provided block modes. Attempts to use the
         * key with any other block modes will be rejected.
         * Sets the set of block modes with which the key can be used when encrypting/decrypting.
         * Attempts to use the key with any other block modes will be rejected.
         *
         * <p>This restriction must be specified for keys which are used for encryption/decryption.
         * <p>This must be specified for encryption/decryption keys.
         */
        public Builder setBlockModes(@KeyStoreKeyConstraints.BlockModeEnum int blockModes) {
            mBlockModes = blockModes;
        public Builder setBlockModes(String... blockModes) {
            mBlockModes = ArrayUtils.cloneIfNotEmpty(blockModes);
            return this;
        }

@@ -412,7 +414,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
         * @see #setUserAuthenticationValidityDurationSeconds(int)
         */
        public Builder setUserAuthenticators(
                @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators) {
                @KeyStoreKeyProperties.UserAuthenticatorEnum int userAuthenticators) {
            mUserAuthenticators = userAuthenticators;
            return this;
        }
@@ -447,7 +449,7 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec {
                    mKeyValidityForOriginationEnd,
                    mKeyValidityForConsumptionEnd,
                    mPurposes,
                    mPaddings,
                    mEncryptionPaddings,
                    mBlockModes,
                    mRandomizedEncryptionRequired,
                    mUserAuthenticators,
Loading