Add option to allow key validity after fingerprint enrollment.

Bug: 21563854
Change-Id: I4f601e59fbfcd601e6a80ddcbc7b83ced6cc18c8
diff --git a/api/current.txt b/api/current.txt
index 3a371cd..fd47ecd 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34092,6 +34092,7 @@
     method public java.lang.String[] getSignaturePaddings();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isDigestsSpecified();
+    method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
@@ -34109,6 +34110,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateSubject(javax.security.auth.x500.X500Principal);
     method public android.security.keystore.KeyGenParameterSpec.Builder setDigests(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setEncryptionPaddings(java.lang.String...);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setInvalidatedByBiometricEnrollment(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeySize(int);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityEnd(java.util.Date);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForConsumptionEnd(java.util.Date);
@@ -34135,6 +34137,7 @@
     method public java.lang.String[] getSignaturePaddings();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isInsideSecureHardware();
+    method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
     method public boolean isUserAuthenticationValidWhileOnBody();
@@ -34198,6 +34201,7 @@
     method public java.lang.String[] getSignaturePaddings();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isDigestsSpecified();
+    method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
@@ -34209,6 +34213,7 @@
     method public android.security.keystore.KeyProtection.Builder setBlockModes(java.lang.String...);
     method public android.security.keystore.KeyProtection.Builder setDigests(java.lang.String...);
     method public android.security.keystore.KeyProtection.Builder setEncryptionPaddings(java.lang.String...);
+    method public android.security.keystore.KeyProtection.Builder setInvalidatedByBiometricEnrollment(boolean);
     method public android.security.keystore.KeyProtection.Builder setKeyValidityEnd(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setKeyValidityForConsumptionEnd(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
diff --git a/api/system-current.txt b/api/system-current.txt
index 7ed4742..d4bb2b4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -36581,6 +36581,7 @@
     method public java.lang.String[] getSignaturePaddings();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isDigestsSpecified();
+    method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
@@ -36598,6 +36599,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateSubject(javax.security.auth.x500.X500Principal);
     method public android.security.keystore.KeyGenParameterSpec.Builder setDigests(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setEncryptionPaddings(java.lang.String...);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setInvalidatedByBiometricEnrollment(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeySize(int);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityEnd(java.util.Date);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForConsumptionEnd(java.util.Date);
@@ -36624,6 +36626,7 @@
     method public java.lang.String[] getSignaturePaddings();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isInsideSecureHardware();
+    method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
     method public boolean isUserAuthenticationValidWhileOnBody();
@@ -36687,6 +36690,7 @@
     method public java.lang.String[] getSignaturePaddings();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isDigestsSpecified();
+    method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
@@ -36698,6 +36702,7 @@
     method public android.security.keystore.KeyProtection.Builder setBlockModes(java.lang.String...);
     method public android.security.keystore.KeyProtection.Builder setDigests(java.lang.String...);
     method public android.security.keystore.KeyProtection.Builder setEncryptionPaddings(java.lang.String...);
+    method public android.security.keystore.KeyProtection.Builder setInvalidatedByBiometricEnrollment(boolean);
     method public android.security.keystore.KeyProtection.Builder setKeyValidityEnd(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setKeyValidityForConsumptionEnd(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
diff --git a/api/test-current.txt b/api/test-current.txt
index cf7563c..f08d261 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34107,6 +34107,7 @@
     method public java.lang.String[] getSignaturePaddings();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isDigestsSpecified();
+    method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
@@ -34124,6 +34125,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setCertificateSubject(javax.security.auth.x500.X500Principal);
     method public android.security.keystore.KeyGenParameterSpec.Builder setDigests(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setEncryptionPaddings(java.lang.String...);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setInvalidatedByBiometricEnrollment(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeySize(int);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityEnd(java.util.Date);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForConsumptionEnd(java.util.Date);
@@ -34150,6 +34152,7 @@
     method public java.lang.String[] getSignaturePaddings();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isInsideSecureHardware();
+    method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
     method public boolean isUserAuthenticationValidWhileOnBody();
@@ -34213,6 +34216,7 @@
     method public java.lang.String[] getSignaturePaddings();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isDigestsSpecified();
+    method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
@@ -34224,6 +34228,7 @@
     method public android.security.keystore.KeyProtection.Builder setBlockModes(java.lang.String...);
     method public android.security.keystore.KeyProtection.Builder setDigests(java.lang.String...);
     method public android.security.keystore.KeyProtection.Builder setEncryptionPaddings(java.lang.String...);
+    method public android.security.keystore.KeyProtection.Builder setInvalidatedByBiometricEnrollment(boolean);
     method public android.security.keystore.KeyProtection.Builder setKeyValidityEnd(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setKeyValidityForConsumptionEnd(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setKeyValidityForOriginationEnd(java.util.Date);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 1321a83..b234d0f 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -234,7 +234,8 @@
                 KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
                         spec.isUserAuthenticationRequired(),
                         spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody());
+                        spec.isUserAuthenticationValidWhileOnBody(),
+                        spec.isInvalidatedByBiometricEnrollment());
             } catch (IllegalStateException | IllegalArgumentException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -273,7 +274,8 @@
         KeymasterUtils.addUserAuthArgs(args,
                 spec.isUserAuthenticationRequired(),
                 spec.getUserAuthenticationValidityDurationSeconds(),
-                spec.isUserAuthenticationValidWhileOnBody());
+                spec.isUserAuthenticationValidWhileOnBody(),
+                spec.isInvalidatedByBiometricEnrollment());
         KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                 args,
                 mKeymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 830402a..1818f52 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -345,7 +345,8 @@
                 KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
                         mSpec.isUserAuthenticationRequired(),
                         mSpec.getUserAuthenticationValidityDurationSeconds(),
-                        mSpec.isUserAuthenticationValidWhileOnBody());
+                        mSpec.isUserAuthenticationValidWhileOnBody(),
+                        mSpec.isInvalidatedByBiometricEnrollment());
             } catch (IllegalArgumentException | IllegalStateException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -531,7 +532,8 @@
         KeymasterUtils.addUserAuthArgs(args,
                 mSpec.isUserAuthenticationRequired(),
                 mSpec.getUserAuthenticationValidityDurationSeconds(),
-                mSpec.isUserAuthenticationValidWhileOnBody());
+                mSpec.isUserAuthenticationValidWhileOnBody(),
+                mSpec.isInvalidatedByBiometricEnrollment());
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart());
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
                 mSpec.getKeyValidityForOriginationEnd());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 5f5f2c2..40298ad 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -17,10 +17,12 @@
 package android.security.keystore;
 
 import android.security.Credentials;
+import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.security.keymaster.KeyCharacteristics;
 import android.security.keymaster.KeymasterDefs;
 
+import java.math.BigInteger;
 import java.security.InvalidKeyException;
 import java.security.ProviderException;
 import java.security.spec.InvalidKeySpecException;
@@ -170,6 +172,16 @@
         boolean userAuthenticationValidWhileOnBody =
                 keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
 
+        boolean invalidatedByBiometricEnrollment = false;
+        if (keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE)
+                == KeymasterDefs.HW_AUTH_FINGERPRINT) {
+            // Fingerprint-only key; will be invalidated if the root SID isn't in the list.
+            BigInteger rootSid = BigInteger.valueOf(GateKeeper.getSecureUserId());
+            List<BigInteger> sids = keyCharacteristics.getUnsignedLongs(
+                    KeymasterDefs.KM_TAG_USER_SECURE_ID);
+            invalidatedByBiometricEnrollment = !sids.isEmpty() && !sids.contains(rootSid);
+        }
+
         return new KeyInfo(entryAlias,
                 insideSecureHardware,
                 origin,
@@ -185,7 +197,8 @@
                 userAuthenticationRequired,
                 (int) userAuthenticationValidityDurationSeconds,
                 userAuthenticationRequirementEnforcedBySecureHardware,
-                userAuthenticationValidWhileOnBody);
+                userAuthenticationValidWhileOnBody,
+                invalidatedByBiometricEnrollment);
     }
 
     @Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index d660020..d7d4f1c 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -499,7 +499,8 @@
                 KeymasterUtils.addUserAuthArgs(importArgs,
                         spec.isUserAuthenticationRequired(),
                         spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody());
+                        spec.isUserAuthenticationValidWhileOnBody(),
+                        spec.isInvalidatedByBiometricEnrollment());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
                         spec.getKeyValidityStart());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
@@ -694,7 +695,8 @@
             KeymasterUtils.addUserAuthArgs(args,
                     params.isUserAuthenticationRequired(),
                     params.getUserAuthenticationValidityDurationSeconds(),
-                    params.isUserAuthenticationValidWhileOnBody());
+                    params.isUserAuthenticationValidWhileOnBody(),
+                    params.isInvalidatedByBiometricEnrollment());
             KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                     args,
                     keymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index a84e7f34..127d756 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -253,6 +253,7 @@
     private final byte[] mAttestationChallenge;
     private final boolean mUniqueIdIncluded;
     private final boolean mUserAuthenticationValidWhileOnBody;
+    private final boolean mInvalidatedByBiometricEnrollment;
 
     /**
      * @hide should be built with Builder
@@ -279,7 +280,8 @@
             int userAuthenticationValidityDurationSeconds,
             byte[] attestationChallenge,
             boolean uniqueIdIncluded,
-            boolean userAuthenticationValidWhileOnBody) {
+            boolean userAuthenticationValidWhileOnBody,
+            boolean invalidatedByBiometricEnrollment) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -324,6 +326,7 @@
         mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
         mUniqueIdIncluded = uniqueIdIncluded;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
+        mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
     }
 
     /**
@@ -607,6 +610,19 @@
     }
 
     /**
+     * Returns {@code true} if the key is irreversibly invalidated when a new fingerprint is
+     * enrolled or all enrolled fingerprints are removed. This has effect only for keys that
+     * require fingerprint user authentication for every use.
+     *
+     * @see #isUserAuthenticationRequired()
+     * @see #getUserAuthenticationValidityDurationSeconds()
+     * @see Builder#setInvalidatedByBiometricEnrollment(boolean)
+     */
+    public boolean isInvalidatedByBiometricEnrollment() {
+        return mInvalidatedByBiometricEnrollment;
+    }
+
+    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
@@ -633,6 +649,7 @@
         private byte[] mAttestationChallenge = null;
         private boolean mUniqueIdIncluded = false;
         private boolean mUserAuthenticationValidWhileOnBody;
+        private boolean mInvalidatedByBiometricEnrollment = true;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -966,8 +983,10 @@
          * or when the secure lock screen is forcibly reset (e.g., by a Device Administrator).
          * Additionally, if the key requires that user authentication takes place for every use of
          * the key, it is also irreversibly invalidated once a new fingerprint is enrolled or once\
-         * no more fingerprints are enrolled. Attempts to initialize cryptographic operations using
-         * such keys will throw {@link KeyPermanentlyInvalidatedException}.</li>
+         * no more fingerprints are enrolled, unless {@link
+         * #setInvalidatedByBiometricEnrollment(boolean)} is used to allow validity after
+         * enrollment. Attempts to initialize cryptographic operations using such keys will throw
+         * {@link KeyPermanentlyInvalidatedException}.</li>
          * </ul>
          *
          * <p>This authorization applies only to secret key and private key operations. Public key
@@ -1110,6 +1129,30 @@
         }
 
         /**
+         * Sets whether this key should be invalidated on fingerprint enrollment.  This
+         * applies only to keys which require user authentication (see {@link
+         * #setUserAuthenticationRequired(boolean)}) and if no positive validity duration has been
+         * set (see {@link #setUserAuthenticationValidityDurationSeconds(int)}, meaning the key is
+         * valid for fingerprint authentication only.
+         *
+         * <p>By default, {@code invalidateKey} is {@code true}, so keys that are valid for
+         * fingerprint authentication only are <em>irreversibly invalidated</em> when a new
+         * fingerprint is enrolled, or when all existing fingerprints are deleted.  That may be
+         * changed by calling this method with {@code invalidateKey} set to {@code false}.
+         *
+         * <p>Invalidating keys on enrollment of a new finger or unenrollment of all fingers
+         * improves security by ensuring that an unauthorized person who obtains the password can't
+         * gain the use of fingerprint-authenticated keys by enrolling their own finger.  However,
+         * invalidating keys makes key-dependent operations impossible, requiring some fallback
+         * procedure to authenticate the user and set up a new key.
+         */
+        @NonNull
+        public Builder setInvalidatedByBiometricEnrollment(boolean invalidateKey) {
+            mInvalidatedByBiometricEnrollment = invalidateKey;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@code KeyGenParameterSpec}.
          */
         @NonNull
@@ -1136,7 +1179,8 @@
                     mUserAuthenticationValidityDurationSeconds,
                     mAttestationChallenge,
                     mUniqueIdIncluded,
-                    mUserAuthenticationValidWhileOnBody);
+                    mUserAuthenticationValidWhileOnBody,
+                    mInvalidatedByBiometricEnrollment);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index f77b5ba..fa6d8b3 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -80,6 +80,7 @@
     private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware;
     private final boolean mUserAuthenticationValidWhileOnBody;
+    private final boolean mInvalidatedByBiometricEnrollment;
 
     /**
      * @hide
@@ -99,7 +100,8 @@
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
             boolean userAuthenticationRequirementEnforcedBySecureHardware,
-            boolean userAuthenticationValidWhileOnBody) {
+            boolean userAuthenticationValidWhileOnBody,
+            boolean invalidatedByBiometricEnrollment) {
         mKeystoreAlias = keystoreKeyAlias;
         mInsideSecureHardware = insideSecureHardware;
         mOrigin = origin;
@@ -119,6 +121,7 @@
         mUserAuthenticationRequirementEnforcedBySecureHardware =
                 userAuthenticationRequirementEnforcedBySecureHardware;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
+        mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
     }
 
     /**
@@ -290,4 +293,12 @@
     public boolean isUserAuthenticationValidWhileOnBody() {
         return mUserAuthenticationValidWhileOnBody;
     }
+
+    /**
+     * Returns {@code true} if the key will be invalidated by enrollment of a new fingerprint or
+     * removal of all fingerprints.
+     */
+    public boolean isInvalidatedByBiometricEnrollment() {
+        return mInvalidatedByBiometricEnrollment;
+    }
 }
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 4700b68..fa57bdb 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -215,6 +215,7 @@
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mUserAuthenticationValidWhileOnBody;
+    private final boolean mInvalidatedByBiometricEnrollment;
 
     private KeyProtection(
             Date keyValidityStart,
@@ -228,7 +229,8 @@
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
-            boolean userAuthenticationValidWhileOnBody) {
+            boolean userAuthenticationValidWhileOnBody,
+            boolean invalidatedByBiometricEnrollment) {
         mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
         mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
         mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -243,6 +245,7 @@
         mUserAuthenticationRequired = userAuthenticationRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
+        mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
     }
 
     /**
@@ -412,6 +415,19 @@
     }
 
     /**
+     * Returns {@code true} if the key is irreversibly invalidated when a new fingerprint is
+     * enrolled or all enrolled fingerprints are removed. This has effect only for keys that
+     * require fingerprint user authentication for every use.
+     *
+     * @see #isUserAuthenticationRequired()
+     * @see #getUserAuthenticationValidityDurationSeconds()
+     * @see Builder#setInvalidatedByBiometricEnrollment(boolean)
+     */
+    public boolean isInvalidatedByBiometricEnrollment() {
+        return mInvalidatedByBiometricEnrollment;
+    }
+
+    /**
      * Builder of {@link KeyProtection} instances.
      */
     public final static class Builder {
@@ -428,6 +444,7 @@
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
         private boolean mUserAuthenticationValidWhileOnBody;
+        private boolean mInvalidatedByBiometricEnrollment = true;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -638,9 +655,10 @@
          * or when the secure lock screen is forcibly reset (e.g., by a Device Administrator).
          * Additionally, if the key requires that user authentication takes place for every use of
          * the key, it is also irreversibly invalidated once a new fingerprint is enrolled or once\
-         * no more fingerprints are enrolled. Attempts to initialize cryptographic operations using
-         * such keys will throw {@link KeyPermanentlyInvalidatedException}.</li>
-         * </ul>
+         * no more fingerprints are enrolled, unless {@link
+         * #setInvalidatedByBiometricEnrollment(boolean)} is used to allow validity after
+         * enrollment. Attempts to initialize cryptographic operations using such keys will throw
+         * {@link KeyPermanentlyInvalidatedException}.</li> </ul>
          *
          * <p>This authorization applies only to secret key and private key operations. Public key
          * operations are not restricted.
@@ -729,6 +747,30 @@
         }
 
         /**
+         * Sets whether this key should be invalidated on fingerprint enrollment.  This
+         * applies only to keys which require user authentication (see {@link
+         * #setUserAuthenticationRequired(boolean)}) and if no positive validity duration has been
+         * set (see {@link #setUserAuthenticationValidityDurationSeconds(int)}, meaning the key is
+         * valid for fingerprint authentication only.
+         *
+         * <p>By default, {@code invalidateKey} is {@code true}, so keys that are valid for
+         * fingerprint authentication only are <em>irreversibly invalidated</em> when a new
+         * fingerprint is enrolled, or when all existing fingerprints are deleted.  That may be
+         * changed by calling this method with {@code invalidateKey} set to {@code false}.
+         *
+         * <p>Invalidating keys on enrollment of a new finger or unenrollment of all fingers
+         * improves security by ensuring that an unauthorized person who obtains the password can't
+         * gain the use of fingerprint-authenticated keys by enrolling their own finger.  However,
+         * invalidating keys makes key-dependent operations impossible, requiring some fallback
+         * procedure to authenticate the user and set up a new key.
+         */
+        @NonNull
+        public Builder setInvalidatedByBiometricEnrollment(boolean invalidateKey) {
+            mInvalidatedByBiometricEnrollment = invalidateKey;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@link KeyProtection}.
          *
          * @throws IllegalArgumentException if a required field is missing
@@ -747,7 +789,8 @@
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
-                    mUserAuthenticationValidWhileOnBody);
+                    mUserAuthenticationValidWhileOnBody,
+                    mInvalidatedByBiometricEnrollment);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 3a008bc..f5272aa 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -97,7 +97,8 @@
     public static void addUserAuthArgs(KeymasterArguments args,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
-            boolean userAuthenticationValidWhileOnBody) {
+            boolean userAuthenticationValidWhileOnBody,
+            boolean invalidatedByBiometricEnrollment) {
         if (!userAuthenticationRequired) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
             return;
@@ -117,8 +118,20 @@
                         "At least one fingerprint must be enrolled to create keys requiring user"
                         + " authentication for every use");
             }
-            args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
-                    KeymasterArguments.toUint64(fingerprintOnlySid));
+
+            long sid;
+            if (invalidatedByBiometricEnrollment) {
+                // The fingerprint-only SID will change on fingerprint enrollment or removal of all,
+                // enrolled fingerprints, invalidating the key.
+                sid = fingerprintOnlySid;
+            } else {
+                // The root SID will *not* change on fingerprint enrollment, or removal of all
+                // enrolled fingerprints, allowing the key to remain valid.
+                sid = getRootSid();
+            }
+
+            args.addUnsignedLong(
+                    KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid));
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
             if (userAuthenticationValidWhileOnBody) {
                 throw new ProviderException("Key validity extension while device is on-body is not "
@@ -127,11 +140,7 @@
         } else {
             // The key is authorized for use for the specified amount of time after the user has
             // authenticated. Whatever unlocks the secure lock screen should authorize this key.
-            long rootSid = GateKeeper.getSecureUserId();
-            if (rootSid == 0) {
-                throw new IllegalStateException("Secure lock screen must be enabled"
-                        + " to create keys requiring user authentication");
-            }
+            long rootSid = getRootSid();
             args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
                     KeymasterArguments.toUint64(rootSid));
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
@@ -184,4 +193,13 @@
                 break;
         }
     }
+
+    private static long getRootSid() {
+        long rootSid = GateKeeper.getSecureUserId();
+        if (rootSid == 0) {
+            throw new IllegalStateException("Secure lock screen must be enabled"
+                    + " to create keys requiring user authentication");
+        }
+        return rootSid;
+    }
 }