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

Commit b6e62864 authored by Alex Klyubin's avatar Alex Klyubin
Browse files

Avoid IllegalStateException when generating/importing keys.

This avoids IllegalStateException when generating/importing keys which
require user authentication when the system is not configured to
generate/import such keys (e.g., secure lock screen not set up).

The documentation states that before generating/importing such keys
apps should check (using public API) whether the system is in a
suitable state. However, some apps are not doing that and instead
catching the IllegalStateException thrown during key
generation/import. This is a bad practice because this exception is an
undocumented implementation detail and should thus not be depended
upon.

This CL addresses this issue as follows:
1. Key(Pair)Generator.init now throws a checked
   InvalidAlgorithmParameterException when the system is in a wrong
   state. Because in most uses of Key(Pair)Generator .init is
   immediately followed by .generate, this prevents .generate from
   encountering this state and does so using a checked exception
   which is part of public API.
2. Key import rethrows the IllegalStateException as a checked
   KeyStoreException which is meant to be thrown if the key cannot be
   imported for any reason. Key(Pair)Generator.generate unfortunately
   cannot throw any checked exceptions and thus has to continue
   throwing unchecked exceptions.

Bug: 22262809
Change-Id: Ic0f7b7a90e0ba63df9139c79b80a8649d2645d2a
parent 7fe86c47
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -239,6 +239,13 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
                                "At least one digest algorithm must be specified");
                    }
                }

                // Check that user authentication related parameters are acceptable. This method
                // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                // not set up).
                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
                        spec.isUserAuthenticationRequired(),
                        spec.getUserAuthenticationValidityDurationSeconds());
            } catch (IllegalStateException | IllegalArgumentException e) {
                throw new InvalidAlgorithmParameterException(e);
            }
+8 −1
Original line number Diff line number Diff line
@@ -310,7 +310,14 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
                } else {
                    mKeymasterDigests = EmptyArray.INT;
                }
            } catch (IllegalArgumentException e) {

                // Check that user authentication related parameters are acceptable. This method
                // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                // not set up).
                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
                        mSpec.isUserAuthenticationRequired(),
                        mSpec.getUserAuthenticationValidityDurationSeconds());
            } catch (IllegalArgumentException | IllegalStateException e) {
                throw new InvalidAlgorithmParameterException(e);
            }

+86 −88
Original line number Diff line number Diff line
@@ -484,8 +484,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
                        spec.getKeyValidityForOriginationEnd());
                importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
                        spec.getKeyValidityForConsumptionEnd());
            } catch (IllegalArgumentException e) {
                throw new KeyStoreException("Invalid parameter", e);
            } catch (IllegalArgumentException | IllegalStateException e) {
                throw new KeyStoreException(e);
            }
        }

@@ -598,21 +598,14 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
                    + " RAW format export");
        }

        String keyAlgorithmString = key.getAlgorithm();
        int keymasterAlgorithm;
        int keymasterDigest;
        try {
            keymasterAlgorithm =
                    KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(keyAlgorithmString);
            keymasterDigest = KeyProperties.KeyAlgorithm.toKeymasterDigest(keyAlgorithmString);
        } catch (IllegalArgumentException e) {
            throw new KeyStoreException("Unsupported secret key algorithm: " + keyAlgorithmString);
        }

        KeymasterArguments args = new KeymasterArguments();
        try {
            int keymasterAlgorithm =
                    KeyProperties.KeyAlgorithm.toKeymasterSecretKeyAlgorithm(key.getAlgorithm());
            args.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, keymasterAlgorithm);

            int[] keymasterDigests;
            int keymasterDigest = KeyProperties.KeyAlgorithm.toKeymasterDigest(key.getAlgorithm());
            if (params.isDigestsSpecified()) {
                // Digest(s) specified in parameters
                keymasterDigests = KeyProperties.Digest.allToKeymaster(params.getDigests());
@@ -620,9 +613,9 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
                    // Digest also specified in the JCA key algorithm name.
                    if (!com.android.internal.util.ArrayUtils.contains(
                            keymasterDigests, keymasterDigest)) {
                    throw new KeyStoreException("Key digest mismatch"
                            + ". Key: " + keyAlgorithmString
                            + ", parameter spec: " + Arrays.asList(params.getDigests()));
                        throw new KeyStoreException("Digest specified in key algorithm "
                                + key.getAlgorithm() + " not specified in protection parameters: "
                                + Arrays.asList(params.getDigests()));
                    }
                    // When the key is read back from keystore we reconstruct the JCA key algorithm
                    // name from the KM_TAG_ALGORITHM and the first KM_TAG_DIGEST. Thus we need to
@@ -653,7 +646,7 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
            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);
                            + " for key algorithm " + key.getAlgorithm());
                }
            }

@@ -666,14 +659,15 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
                    if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatibleWithSymmetricCrypto(
                            keymasterBlockMode)) {
                        throw new KeyStoreException(
                            "Randomized encryption (IND-CPA) required but may be violated by block"
                            + " mode: "
                                "Randomized encryption (IND-CPA) required but may be violated by"
                                + " block mode: "
                                + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode)
                                + ". See KeyProtection documentation.");
                    }
                }
            }
        args.addEnums(KeymasterDefs.KM_TAG_PURPOSE, KeyProperties.Purpose.allToKeymaster(purposes));
            args.addEnums(KeymasterDefs.KM_TAG_PURPOSE,
                    KeyProperties.Purpose.allToKeymaster(purposes));
            args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockModes);
            if (params.getSignaturePaddings().length > 0) {
                throw new KeyStoreException("Signature paddings not supported for symmetric keys");
@@ -684,7 +678,8 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
            KeymasterUtils.addUserAuthArgs(args,
                    params.isUserAuthenticationRequired(),
                    params.getUserAuthenticationValidityDurationSeconds());
        args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, params.getKeyValidityStart());
            args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
                    params.getKeyValidityStart());
            args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
                    params.getKeyValidityForOriginationEnd());
            args.addDateIfNotNull(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME,
@@ -695,6 +690,9 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
                // Permit caller-provided IV when encrypting with this key
                args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
            }
        } catch (IllegalArgumentException | IllegalStateException e) {
            throw new KeyStoreException(e);
        }

        Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias);
        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
+4 −0
Original line number Diff line number Diff line
@@ -87,6 +87,10 @@ public abstract class KeymasterUtils {
     * @param userAuthenticationValidityDurationSeconds duration of time (seconds) for which user
     *        authentication is valid as authorization for using the key or {@code -1} if every
     *        use of the key needs authorization.
     *
     * @throws IllegalStateException if user authentication is required but the system is in a wrong
     *         state (e.g., secure lock screen not set up) for generating or importing keys that
     *         require user authentication.
     */
    public static void addUserAuthArgs(KeymasterArguments args,
            boolean userAuthenticationRequired,