Add support for extending key validity while on body.

Bug: 21563854
Change-Id: I3b622d2af77ec4ac3ba42407fc391112c153ef0f
diff --git a/api/current.txt b/api/current.txt
index 9e96249..9b7f2e6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -34077,6 +34077,7 @@
     method public boolean isDigestsSpecified();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
+    method public boolean isUserAuthenticationValidWhileOnBody();
   }
 
   public static final class KeyGenParameterSpec.Builder {
@@ -34099,6 +34100,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int);
   }
 
@@ -34118,6 +34120,7 @@
     method public boolean isInsideSecureHardware();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
+    method public boolean isUserAuthenticationValidWhileOnBody();
   }
 
   public class KeyNotYetValidException extends java.security.InvalidKeyException {
@@ -34180,6 +34183,7 @@
     method public boolean isDigestsSpecified();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
+    method public boolean isUserAuthenticationValidWhileOnBody();
   }
 
   public static final class KeyProtection.Builder {
@@ -34195,6 +34199,7 @@
     method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
+    method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
   }
 
diff --git a/api/system-current.txt b/api/system-current.txt
index 12187dd..125dce8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -36559,6 +36559,7 @@
     method public boolean isDigestsSpecified();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
+    method public boolean isUserAuthenticationValidWhileOnBody();
   }
 
   public static final class KeyGenParameterSpec.Builder {
@@ -36581,6 +36582,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int);
   }
 
@@ -36600,6 +36602,7 @@
     method public boolean isInsideSecureHardware();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
+    method public boolean isUserAuthenticationValidWhileOnBody();
   }
 
   public class KeyNotYetValidException extends java.security.InvalidKeyException {
@@ -36662,6 +36665,7 @@
     method public boolean isDigestsSpecified();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
+    method public boolean isUserAuthenticationValidWhileOnBody();
   }
 
   public static final class KeyProtection.Builder {
@@ -36677,6 +36681,7 @@
     method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
+    method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 74c6787..d432a39 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -34092,6 +34092,7 @@
     method public boolean isDigestsSpecified();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
+    method public boolean isUserAuthenticationValidWhileOnBody();
   }
 
   public static final class KeyGenParameterSpec.Builder {
@@ -34114,6 +34115,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int);
   }
 
@@ -34133,6 +34135,7 @@
     method public boolean isInsideSecureHardware();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationRequirementEnforcedBySecureHardware();
+    method public boolean isUserAuthenticationValidWhileOnBody();
   }
 
   public class KeyNotYetValidException extends java.security.InvalidKeyException {
@@ -34195,6 +34198,7 @@
     method public boolean isDigestsSpecified();
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isUserAuthenticationRequired();
+    method public boolean isUserAuthenticationValidWhileOnBody();
   }
 
   public static final class KeyProtection.Builder {
@@ -34210,6 +34214,7 @@
     method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
+    method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
   }
 
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index e01f2a0..eb3d031 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -72,6 +72,7 @@
     public static final int KM_TAG_NO_AUTH_REQUIRED = KM_BOOL | 503;
     public static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
     public static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
+    public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
 
     public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
     public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index e6276a4..1321a83 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -233,7 +233,8 @@
                 // not set up).
                 KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
                         spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds());
+                        spec.getUserAuthenticationValidityDurationSeconds(),
+                        spec.isUserAuthenticationValidWhileOnBody());
             } catch (IllegalStateException | IllegalArgumentException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -271,7 +272,8 @@
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
         KeymasterUtils.addUserAuthArgs(args,
                 spec.isUserAuthenticationRequired(),
-                spec.getUserAuthenticationValidityDurationSeconds());
+                spec.getUserAuthenticationValidityDurationSeconds(),
+                spec.isUserAuthenticationValidWhileOnBody());
         KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                 args,
                 mKeymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index 3a0ff1c..830402a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -344,7 +344,8 @@
                 // not set up).
                 KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
                         mSpec.isUserAuthenticationRequired(),
-                        mSpec.getUserAuthenticationValidityDurationSeconds());
+                        mSpec.getUserAuthenticationValidityDurationSeconds(),
+                        mSpec.isUserAuthenticationValidWhileOnBody());
             } catch (IllegalArgumentException | IllegalStateException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -529,7 +530,8 @@
 
         KeymasterUtils.addUserAuthArgs(args,
                 mSpec.isUserAuthenticationRequired(),
-                mSpec.getUserAuthenticationValidityDurationSeconds());
+                mSpec.getUserAuthenticationValidityDurationSeconds(),
+                mSpec.isUserAuthenticationValidWhileOnBody());
         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 8d606bf..5f5f2c2 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -167,6 +167,8 @@
         boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired)
                 && (keymasterHwEnforcedUserAuthenticators != 0)
                 && (keymasterSwEnforcedUserAuthenticators == 0);
+        boolean userAuthenticationValidWhileOnBody =
+                keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
 
         return new KeyInfo(entryAlias,
                 insideSecureHardware,
@@ -182,7 +184,8 @@
                 blockModes,
                 userAuthenticationRequired,
                 (int) userAuthenticationValidityDurationSeconds,
-                userAuthenticationRequirementEnforcedBySecureHardware);
+                userAuthenticationRequirementEnforcedBySecureHardware,
+                userAuthenticationValidWhileOnBody);
     }
 
     @Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index cdcc7a2..d660020 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -498,7 +498,8 @@
                         KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
                 KeymasterUtils.addUserAuthArgs(importArgs,
                         spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds());
+                        spec.getUserAuthenticationValidityDurationSeconds(),
+                        spec.isUserAuthenticationValidWhileOnBody());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
                         spec.getKeyValidityStart());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
@@ -692,7 +693,8 @@
             args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
             KeymasterUtils.addUserAuthArgs(args,
                     params.isUserAuthenticationRequired(),
-                    params.getUserAuthenticationValidityDurationSeconds());
+                    params.getUserAuthenticationValidityDurationSeconds(),
+                    params.isUserAuthenticationValidWhileOnBody());
             KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                     args,
                     keymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index f3fd129..a84e7f34 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -252,6 +252,7 @@
     private final int mUserAuthenticationValidityDurationSeconds;
     private final byte[] mAttestationChallenge;
     private final boolean mUniqueIdIncluded;
+    private final boolean mUserAuthenticationValidWhileOnBody;
 
     /**
      * @hide should be built with Builder
@@ -277,7 +278,8 @@
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
             byte[] attestationChallenge,
-            boolean uniqueIdIncluded) {
+            boolean uniqueIdIncluded,
+            boolean userAuthenticationValidWhileOnBody) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -321,6 +323,7 @@
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
         mUniqueIdIncluded = uniqueIdIncluded;
+        mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
     }
 
     /**
@@ -587,6 +590,23 @@
     }
 
     /**
+     * Returns {@code true} if the key will remain authorized while the device is on the user's
+     * body, even after the validity duration has expired.  This option has no effect on keys that
+     * don't have an authentication validity duration, and has no effect if the device lacks a
+     * secure on-body sensor.
+     *
+     * <p>Authorization applies only to secret key and private key operations. Public key operations
+     * are not restricted.
+     *
+     * @see #isUserAuthenticationRequired()
+     * @see #getUserAuthenticationValidityDurationSeconds()
+     * @see Builder#setUserAuthenticationValidWhileOnBody(boolean)
+     */
+    public boolean isUserAuthenticationValidWhileOnBody() {
+        return mUserAuthenticationValidWhileOnBody;
+    }
+
+    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
@@ -612,6 +632,7 @@
         private int mUserAuthenticationValidityDurationSeconds = -1;
         private byte[] mAttestationChallenge = null;
         private boolean mUniqueIdIncluded = false;
+        private boolean mUserAuthenticationValidWhileOnBody;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -1061,6 +1082,34 @@
         }
 
         /**
+         * Sets whether the key is authorized for use after the authentication validity period is
+         * expired (see {@link #setUserAuthenticationValidityDurationSeconds} and {@link
+         * #setUserAuthenticationRequired}) if the device has a secure on-body sensor and if the
+         * device has not been removed from the user's body since the last successful
+         * authentication.
+         *
+         * <p>On devices that do not have a secure on-body sensor, creating a key with this
+         * parameter set to {@code true} will have no effect; the private or secret key will no
+         * longer be authorized for use after the validity period ends, and a fresh authentication
+         * will be required to use it again.
+         *
+         * <p>Note that "secure" on-body sensors are required by Android to have a secure path to
+         * the secure hardware, but the sensors themselves may not be difficult to fool.  It is
+         * recommended that this feature be used to increase slightly the security of keys which
+         * would otherwise have to allow unauthenticated access, or have a very long validity
+         * period. Keys that require high assurance of user authorization should not use this
+         * feature and should set a short validity period.
+         *
+         * @param remainsValid if {@code true}, and if the device supports secure on-body detection,
+         * key will remain valid after authentication validity duration has expired.
+         */
+        @NonNull
+        public Builder setUserAuthenticationValidWhileOnBody(boolean remainsValid) {
+            mUserAuthenticationValidWhileOnBody = remainsValid;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@code KeyGenParameterSpec}.
          */
         @NonNull
@@ -1086,7 +1135,8 @@
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
                     mAttestationChallenge,
-                    mUniqueIdIncluded);
+                    mUniqueIdIncluded,
+                    mUserAuthenticationValidWhileOnBody);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index d726880..f77b5ba 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -79,6 +79,7 @@
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware;
+    private final boolean mUserAuthenticationValidWhileOnBody;
 
     /**
      * @hide
@@ -97,7 +98,8 @@
             @KeyProperties.BlockModeEnum String[] blockModes,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
-            boolean userAuthenticationRequirementEnforcedBySecureHardware) {
+            boolean userAuthenticationRequirementEnforcedBySecureHardware,
+            boolean userAuthenticationValidWhileOnBody) {
         mKeystoreAlias = keystoreKeyAlias;
         mInsideSecureHardware = insideSecureHardware;
         mOrigin = origin;
@@ -116,6 +118,7 @@
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mUserAuthenticationRequirementEnforcedBySecureHardware =
                 userAuthenticationRequirementEnforcedBySecureHardware;
+        mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
     }
 
     /**
@@ -277,4 +280,14 @@
     public boolean isUserAuthenticationRequirementEnforcedBySecureHardware() {
         return mUserAuthenticationRequirementEnforcedBySecureHardware;
     }
+
+    /**
+     * Returns {@code true} if this key will remain usable after its specified validity duration
+     * for as long as the device remains on the user's body.  This is possible only for keys with
+     * a specified validity duration.  Always returns {@code false} on devices that lack a secure
+     * on-body sensor.
+     */
+    public boolean isUserAuthenticationValidWhileOnBody() {
+        return mUserAuthenticationValidWhileOnBody;
+    }
 }
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index c984439..4700b68 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -214,6 +214,7 @@
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
+    private final boolean mUserAuthenticationValidWhileOnBody;
 
     private KeyProtection(
             Date keyValidityStart,
@@ -226,7 +227,8 @@
             @KeyProperties.BlockModeEnum String[] blockModes,
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
-            int userAuthenticationValidityDurationSeconds) {
+            int userAuthenticationValidityDurationSeconds,
+            boolean userAuthenticationValidWhileOnBody) {
         mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
         mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
         mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -240,6 +242,7 @@
         mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticationRequired = userAuthenticationRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
     }
 
     /**
@@ -392,6 +395,23 @@
     }
 
     /**
+     * Returns {@code true} if the key will remain authorized while the device is on the user's
+     * body, even after the validity duration has expired.  This option has no effect on keys that
+     * don't have an authentication validity duration, and has no effect if the device lacks a
+     * secure on-body sensor.
+     *
+     * <p>Authorization applies only to secret key and private key operations. Public key operations
+     * are not restricted.
+     *
+     * @see #isUserAuthenticationRequired()
+     * @see #getUserAuthenticationValidityDurationSeconds()
+     * @see Builder#setUserAuthenticationValidWhileOnBody(boolean)
+     */
+    public boolean isUserAuthenticationValidWhileOnBody() {
+        return mUserAuthenticationValidWhileOnBody;
+    }
+
+    /**
      * Builder of {@link KeyProtection} instances.
      */
     public final static class Builder {
@@ -407,6 +427,7 @@
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
+        private boolean mUserAuthenticationValidWhileOnBody;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -680,6 +701,34 @@
         }
 
         /**
+         * Sets whether the key is authorized for use after the authentication validity period is
+         * expired (see {@link #setUserAuthenticationValidityDurationSeconds} and {@link
+         * #setUserAuthenticationRequired}) if the device has a secure on-body sensor and if the
+         * device has not been removed from the user's body since the last successful
+         * authentication.
+         *
+         * <p>On devices that do not have a secure on-body sensor, creating a key with this
+         * parameter set to {@code true} will have no effect; the private or secret key will no
+         * longer be authorized for use after the validity period ends, and a fresh authentication
+         * will be required to use it again.
+         *
+         * <p>Note that "secure" on-body sensors are required by Android to have a secure path to
+         * the secure hardware, but the sensors themselves may not be difficult to fool.  It is
+         * recommended that this feature be used to increase slightly the security of keys which
+         * would otherwise have to allow unauthenticated access, or have a very long validity
+         * period. Keys that require high assurance of user authorization should not use this
+         * feature and should set a short validity period.
+         *
+         * @param remainsValid if {@code true}, and if the device supports secure on-body detection,
+         * key will remain valid after authentication validity duration has expired.
+         */
+        @NonNull
+        public Builder setUserAuthenticationValidWhileOnBody(boolean remainsValid) {
+            mUserAuthenticationValidWhileOnBody = remainsValid;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@link KeyProtection}.
          *
          * @throws IllegalArgumentException if a required field is missing
@@ -697,7 +746,8 @@
                     mBlockModes,
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
-                    mUserAuthenticationValidityDurationSeconds);
+                    mUserAuthenticationValidityDurationSeconds,
+                    mUserAuthenticationValidWhileOnBody);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index feafbfa..3a008bc 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -96,7 +96,8 @@
      */
     public static void addUserAuthArgs(KeymasterArguments args,
             boolean userAuthenticationRequired,
-            int userAuthenticationValidityDurationSeconds) {
+            int userAuthenticationValidityDurationSeconds,
+            boolean userAuthenticationValidWhileOnBody) {
         if (!userAuthenticationRequired) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
             return;
@@ -119,6 +120,10 @@
             args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
                     KeymasterArguments.toUint64(fingerprintOnlySid));
             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 "
+                        + "supported for keys requiring fingerprint authentication");
+            }
         } 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.
@@ -133,6 +138,9 @@
                     KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
                     userAuthenticationValidityDurationSeconds);
+            if (userAuthenticationValidWhileOnBody) {
+                args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
+            }
         }
     }