Merge "Keystore.java: Make apps tolerant to keystore crashes"
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index bfce17c..f21fa5e 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -17,7 +17,6 @@
 package android.security;
 
 import android.annotation.UnsupportedAppUsage;
-import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.Application;
 import android.app.KeyguardManager;
@@ -45,24 +44,24 @@
 import android.security.keystore.KeyNotYetValidException;
 import android.security.keystore.KeyPermanentlyInvalidatedException;
 import android.security.keystore.KeyProperties;
-import android.security.keystore.KeyProtection;
 import android.security.keystore.KeystoreResponse;
-import android.security.keystore.StrongBoxUnavailableException;
 import android.security.keystore.UserNotAuthenticatedException;
 import android.util.Log;
+
 import com.android.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import java.math.BigInteger;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.math.BigInteger;
 import java.security.InvalidKeyException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
+
 import sun.security.util.ObjectIdentifier;
 import sun.security.x509.AlgorithmId;
 
@@ -492,8 +491,9 @@
     }
 
     public boolean addRngEntropy(byte[] data, int flags) {
+        KeystoreResultPromise promise = new KeystoreResultPromise();
         try {
-            KeystoreResultPromise promise = new KeystoreResultPromise();
+            mBinder.asBinder().linkToDeath(promise, 0);
             int errorCode = mBinder.addRngEntropy(promise, data, flags);
             if (errorCode == NO_ERROR) {
                 return promise.getFuture().get().getErrorCode() == NO_ERROR;
@@ -506,6 +506,8 @@
         } catch (ExecutionException | InterruptedException e) {
             Log.e(TAG, "AddRngEntropy completed with exception", e);
             return false;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
         }
     }
 
@@ -537,7 +539,8 @@
     }
 
     private class KeyCharacteristicsPromise
-    extends android.security.keystore.IKeystoreKeyCharacteristicsCallback.Stub {
+            extends android.security.keystore.IKeystoreKeyCharacteristicsCallback.Stub
+            implements IBinder.DeathRecipient {
         final private CompletableFuture<KeyCharacteristicsCallbackResult> future =
                 new CompletableFuture<KeyCharacteristicsCallbackResult>();
         @Override
@@ -550,19 +553,30 @@
         public final CompletableFuture<KeyCharacteristicsCallbackResult> getFuture() {
             return future;
         }
+        @Override
+        public void binderDied() {
+            future.completeExceptionally(new RemoteException("Keystore died"));
+        }
     };
 
     private int generateKeyInternal(String alias, KeymasterArguments args, byte[] entropy, int uid,
             int flags, KeyCharacteristics outCharacteristics)
                     throws RemoteException, ExecutionException, InterruptedException {
         KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
-        int error = mBinder.generateKey(promise, alias, args, entropy, uid, flags);
-        if (error != NO_ERROR) {
-            Log.e(TAG, "generateKeyInternal failed on request " + error);
-            return error;
+        int error = NO_ERROR;
+        KeyCharacteristicsCallbackResult result = null;
+        try {
+            mBinder.asBinder().linkToDeath(promise, 0);
+            error = mBinder.generateKey(promise, alias, args, entropy, uid, flags);
+            if (error != NO_ERROR) {
+                Log.e(TAG, "generateKeyInternal failed on request " + error);
+                return error;
+            }
+            result = promise.getFuture().get();
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
         }
 
-        KeyCharacteristicsCallbackResult result = promise.getFuture().get();
         error = result.getKeystoreResponse().getErrorCode();
         if (error != NO_ERROR) {
             Log.e(TAG, "generateKeyInternal failed on response " + error);
@@ -604,10 +618,12 @@
 
     public int getKeyCharacteristics(String alias, KeymasterBlob clientId, KeymasterBlob appId,
             int uid, KeyCharacteristics outCharacteristics) {
+        KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
         try {
+            mBinder.asBinder().linkToDeath(promise, 0);
             clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
             appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
-            KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
+
             int error = mBinder.getKeyCharacteristics(promise, alias, clientId, appId, uid);
             if (error != NO_ERROR) return error;
 
@@ -625,6 +641,8 @@
         } catch (ExecutionException | InterruptedException e) {
             Log.e(TAG, "GetKeyCharacteristics completed with exception", e);
             return SYSTEM_ERROR;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
         }
     }
 
@@ -637,17 +655,23 @@
             int uid, int flags, KeyCharacteristics outCharacteristics)
                     throws RemoteException, ExecutionException, InterruptedException {
         KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
-        int error = mBinder.importKey(promise, alias, args, format, keyData, uid, flags);
-        if (error != NO_ERROR) return error;
+        mBinder.asBinder().linkToDeath(promise, 0);
+        try {
+            int error = mBinder.importKey(promise, alias, args, format, keyData, uid, flags);
+            if (error != NO_ERROR) return error;
 
-        KeyCharacteristicsCallbackResult result = promise.getFuture().get();
-        error = result.getKeystoreResponse().getErrorCode();
-        if (error != NO_ERROR) return error;
+            KeyCharacteristicsCallbackResult result = promise.getFuture().get();
 
-        KeyCharacteristics characteristics = result.getKeyCharacteristics();
-        if (characteristics == null) return SYSTEM_ERROR;
-        outCharacteristics.shallowCopyFrom(characteristics);
-        return NO_ERROR;
+            error = result.getKeystoreResponse().getErrorCode();
+            if (error != NO_ERROR) return error;
+
+            KeyCharacteristics characteristics = result.getKeyCharacteristics();
+            if (characteristics == null) return SYSTEM_ERROR;
+            outCharacteristics.shallowCopyFrom(characteristics);
+            return NO_ERROR;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
+        }
     }
 
     public int importKey(String alias, KeymasterArguments args, int format, byte[] keyData,
@@ -738,18 +762,25 @@
             KeyCharacteristics outCharacteristics)
                     throws RemoteException, ExecutionException, InterruptedException {
         KeyCharacteristicsPromise promise = new KeyCharacteristicsPromise();
-        int error = mBinder.importWrappedKey(promise, wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
-                maskingKey, args, rootSid, fingerprintSid);
-        if (error != NO_ERROR) return error;
+        mBinder.asBinder().linkToDeath(promise, 0);
+        try {
+            KeyCharacteristicsCallbackResult result = null;
+            int error = mBinder.importWrappedKey(promise, wrappedKeyAlias, wrappedKey,
+                    wrappingKeyAlias, maskingKey, args, rootSid, fingerprintSid);
+            if (error != NO_ERROR) return error;
 
-        KeyCharacteristicsCallbackResult result = promise.getFuture().get();
-        error = result.getKeystoreResponse().getErrorCode();
-        if (error != NO_ERROR) return error;
+            KeyCharacteristicsCallbackResult esult = promise.getFuture().get();
 
-        KeyCharacteristics characteristics = result.getKeyCharacteristics();
-        if (characteristics == null) return SYSTEM_ERROR;
-        outCharacteristics.shallowCopyFrom(characteristics);
-        return NO_ERROR;
+            error = result.getKeystoreResponse().getErrorCode();
+            if (error != NO_ERROR) return error;
+
+            KeyCharacteristics characteristics = result.getKeyCharacteristics();
+            if (characteristics == null) return SYSTEM_ERROR;
+            outCharacteristics.shallowCopyFrom(characteristics);
+            return NO_ERROR;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
+        }
     }
 
     public int importWrappedKey(String wrappedKeyAlias, byte[] wrappedKey,
@@ -776,7 +807,8 @@
     }
 
     private class ExportKeyPromise
-    extends android.security.keystore.IKeystoreExportKeyCallback.Stub {
+            extends android.security.keystore.IKeystoreExportKeyCallback.Stub
+            implements IBinder.DeathRecipient {
         final private CompletableFuture<ExportResult> future = new CompletableFuture<ExportResult>();
         @Override
         public void onFinished(ExportResult exportKeyResult) throws android.os.RemoteException {
@@ -785,14 +817,19 @@
         public final CompletableFuture<ExportResult> getFuture() {
             return future;
         }
+        @Override
+        public void binderDied() {
+            future.completeExceptionally(new RemoteException("Keystore died"));
+        }
     };
 
     public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
             KeymasterBlob appId, int uid) {
+        ExportKeyPromise promise = new ExportKeyPromise();
         try {
+            mBinder.asBinder().linkToDeath(promise, 0);
             clientId = clientId != null ? clientId : new KeymasterBlob(new byte[0]);
             appId = appId != null ? appId : new KeymasterBlob(new byte[0]);
-            ExportKeyPromise promise = new ExportKeyPromise();
             int error = mBinder.exportKey(promise, alias, format, clientId, appId, uid);
             if (error == NO_ERROR) {
                 return promise.getFuture().get();
@@ -805,6 +842,8 @@
         } catch (ExecutionException | InterruptedException e) {
             Log.e(TAG, "ExportKey completed with exception", e);
             return null;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
         }
     }
     public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
@@ -813,7 +852,8 @@
     }
 
     private class OperationPromise
-    extends android.security.keystore.IKeystoreOperationResultCallback.Stub {
+            extends android.security.keystore.IKeystoreOperationResultCallback.Stub
+            implements IBinder.DeathRecipient {
         final private CompletableFuture<OperationResult> future = new CompletableFuture<OperationResult>();
         @Override
         public void onFinished(OperationResult operationResult) throws android.os.RemoteException {
@@ -822,14 +862,19 @@
         public final CompletableFuture<OperationResult> getFuture() {
             return future;
         }
+        @Override
+        public void binderDied() {
+            future.completeExceptionally(new RemoteException("Keystore died"));
+        }
     };
 
     public OperationResult begin(String alias, int purpose, boolean pruneable,
             KeymasterArguments args, byte[] entropy, int uid) {
+        OperationPromise promise = new OperationPromise();
         try {
+            mBinder.asBinder().linkToDeath(promise, 0);
             args = args != null ? args : new KeymasterArguments();
             entropy = entropy != null ? entropy : new byte[0];
-            OperationPromise promise = new OperationPromise();
             int errorCode =  mBinder.begin(promise, getToken(), alias, purpose, pruneable, args,
                                            entropy, uid);
             if (errorCode == NO_ERROR) {
@@ -843,6 +888,8 @@
         } catch (ExecutionException | InterruptedException e) {
             Log.e(TAG, "Begin completed with exception", e);
             return null;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
         }
     }
 
@@ -854,10 +901,11 @@
     }
 
     public OperationResult update(IBinder token, KeymasterArguments arguments, byte[] input) {
+        OperationPromise promise = new OperationPromise();
         try {
+            mBinder.asBinder().linkToDeath(promise, 0);
             arguments = arguments != null ? arguments : new KeymasterArguments();
             input = input != null ? input : new byte[0];
-            OperationPromise promise = new OperationPromise();
             int errorCode =  mBinder.update(promise, token, arguments, input);
             if (errorCode == NO_ERROR) {
                 return promise.getFuture().get();
@@ -870,16 +918,19 @@
         } catch (ExecutionException | InterruptedException e) {
             Log.e(TAG, "Update completed with exception", e);
             return null;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
         }
     }
 
     public OperationResult finish(IBinder token, KeymasterArguments arguments, byte[] signature,
             byte[] entropy) {
+        OperationPromise promise = new OperationPromise();
         try {
+            mBinder.asBinder().linkToDeath(promise, 0);
             arguments = arguments != null ? arguments : new KeymasterArguments();
             entropy = entropy != null ? entropy : new byte[0];
             signature = signature != null ? signature : new byte[0];
-            OperationPromise promise = new OperationPromise();
             int errorCode = mBinder.finish(promise, token, arguments, signature, entropy);
             if (errorCode == NO_ERROR) {
                 return promise.getFuture().get();
@@ -892,6 +943,8 @@
         } catch (ExecutionException | InterruptedException e) {
             Log.e(TAG, "Finish completed with exception", e);
             return null;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
         }
     }
 
@@ -900,7 +953,8 @@
     }
 
     private class KeystoreResultPromise
-    extends android.security.keystore.IKeystoreResponseCallback.Stub {
+            extends android.security.keystore.IKeystoreResponseCallback.Stub
+            implements IBinder.DeathRecipient {
         final private CompletableFuture<KeystoreResponse> future = new CompletableFuture<KeystoreResponse>();
         @Override
         public void onFinished(KeystoreResponse keystoreResponse) throws android.os.RemoteException {
@@ -909,11 +963,16 @@
         public final CompletableFuture<KeystoreResponse> getFuture() {
             return future;
         }
+        @Override
+        public void binderDied() {
+            future.completeExceptionally(new RemoteException("Keystore died"));
+        }
     };
 
     public int abort(IBinder token) {
+        KeystoreResultPromise promise = new KeystoreResultPromise();
         try {
-            KeystoreResultPromise promise = new KeystoreResultPromise();
+            mBinder.asBinder().linkToDeath(promise, 0);
             int errorCode = mBinder.abort(promise, token);
             if (errorCode == NO_ERROR) {
                 return promise.getFuture().get().getErrorCode();
@@ -926,6 +985,8 @@
         } catch (ExecutionException | InterruptedException e) {
             Log.e(TAG, "Abort completed with exception", e);
             return SYSTEM_ERROR;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
         }
     }
 
@@ -1035,7 +1096,8 @@
     }
 
     private class CertificateChainPromise
-    extends android.security.keystore.IKeystoreCertificateChainCallback.Stub {
+            extends android.security.keystore.IKeystoreCertificateChainCallback.Stub
+            implements IBinder.DeathRecipient {
         final private CompletableFuture<KeyAttestationCallbackResult> future = new CompletableFuture<KeyAttestationCallbackResult>();
         @Override
         public void onFinished(KeystoreResponse keystoreResponse,
@@ -1045,19 +1107,24 @@
         public final CompletableFuture<KeyAttestationCallbackResult> getFuture() {
             return future;
         }
+        @Override
+        public void binderDied() {
+            future.completeExceptionally(new RemoteException("Keystore died"));
+        }
     };
 
 
     public int attestKey(
             String alias, KeymasterArguments params, KeymasterCertificateChain outChain) {
+        CertificateChainPromise promise = new CertificateChainPromise();
         try {
+            mBinder.asBinder().linkToDeath(promise, 0);
             if (params == null) {
                 params = new KeymasterArguments();
             }
             if (outChain == null) {
                 outChain = new KeymasterCertificateChain();
             }
-            CertificateChainPromise promise = new CertificateChainPromise();
             int error = mBinder.attestKey(promise, alias, params);
             if (error != NO_ERROR) return error;
             KeyAttestationCallbackResult result = promise.getFuture().get();
@@ -1072,18 +1139,21 @@
         } catch (ExecutionException | InterruptedException e) {
             Log.e(TAG, "AttestKey completed with exception", e);
             return SYSTEM_ERROR;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
         }
     }
 
     public int attestDeviceIds(KeymasterArguments params, KeymasterCertificateChain outChain) {
+        CertificateChainPromise promise = new CertificateChainPromise();
         try {
+            mBinder.asBinder().linkToDeath(promise, 0);
             if (params == null) {
                 params = new KeymasterArguments();
             }
             if (outChain == null) {
                 outChain = new KeymasterCertificateChain();
             }
-            CertificateChainPromise promise = new CertificateChainPromise();
             int error = mBinder.attestDeviceIds(promise, params);
             if (error != NO_ERROR) return error;
             KeyAttestationCallbackResult result = promise.getFuture().get();
@@ -1098,6 +1168,8 @@
         } catch (ExecutionException | InterruptedException e) {
             Log.e(TAG, "AttestDevicdeIds completed with exception", e);
             return SYSTEM_ERROR;
+        } finally {
+            mBinder.asBinder().unlinkToDeath(promise, 0);
         }
     }