From ba60e8a8ee196209760caaa20f42ba2960b92ca4 Mon Sep 17 00:00:00 2001 From: jubb Date: Tue, 21 Jun 2022 16:17:01 +1000 Subject: [PATCH] fix: fallback on biometric key failures and retry creating key, fix up notification issues --- app/build.gradle | 4 +- .../securesms/PassphrasePromptActivity.java | 38 +++++++++++++++++-- .../crypto/BiometricSecretProvider.kt | 18 +++++++-- .../notifications/DefaultMessageNotifier.java | 4 +- .../notifications/PushNotificationService.kt | 5 ++- .../messaging/jobs/BatchMessageReceiveJob.kt | 4 +- 6 files changed, 58 insertions(+), 15 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 33e1563f0..4215d6d6e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -159,8 +159,8 @@ dependencies { testImplementation 'org.robolectric:shadows-multidex:4.4' } -def canonicalVersionCode = 286 -def canonicalVersionName = "1.13.5" +def canonicalVersionCode = 287 +def canonicalVersionName = "1.13.6" def postFixSize = 10 def abiPostFix = ['armeabi-v7a' : 1, diff --git a/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java index 09f8efafe..7f7f6f75d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java @@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.crypto.BiometricSecretProvider; import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.util.AnimationCompleteListener; +import java.security.InvalidKeyException; import java.security.Signature; import network.loki.messenger.R; @@ -68,6 +69,7 @@ public class PassphrasePromptActivity extends BaseActionBarActivity { private boolean authenticated; private boolean failure; + private boolean hasSignatureObject = true; private KeyCachingService keyCachingService; @@ -205,7 +207,23 @@ public class PassphrasePromptActivity extends BaseActionBarActivity { if (fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) { Log.i(TAG, "Listening for fingerprints..."); fingerprintCancellationSignal = new CancellationSignal(); - fingerprintManager.authenticate(new FingerprintManagerCompat.CryptoObject(biometricSecretProvider.getOrCreateBiometricSignature(this)), 0, fingerprintCancellationSignal, fingerprintListener, null); + Signature signature; + try { + signature = biometricSecretProvider.getOrCreateBiometricSignature(this); + hasSignatureObject = true; + throw new InvalidKeyException("e"); + } catch (InvalidKeyException e) { + signature = null; + hasSignatureObject = false; + Log.e(TAG, "Error getting / creating signature", e); + } + fingerprintManager.authenticate( + signature == null ? null : new FingerprintManagerCompat.CryptoObject(signature), + 0, + fingerprintCancellationSignal, + fingerprintListener, + null + ); } else { Log.i(TAG, "firing intent..."); Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("Unlock Session", ""); @@ -230,8 +248,22 @@ public class PassphrasePromptActivity extends BaseActionBarActivity { public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) { Log.i(TAG, "onAuthenticationSucceeded"); if (result.getCryptoObject() == null || result.getCryptoObject().getSignature() == null) { - // authentication failed - onAuthenticationFailed(); + if (hasSignatureObject) { + // authentication failed + onAuthenticationFailed(); + } else { + fingerprintPrompt.setImageResource(R.drawable.ic_check_white_48dp); + fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.green_500), PorterDuff.Mode.SRC_IN); + fingerprintPrompt.animate().setInterpolator(new BounceInterpolator()).scaleX(1.1f).scaleY(1.1f).setDuration(500).setListener(new AnimationCompleteListener() { + @Override + public void onAnimationEnd(Animator animation) { + handleAuthenticated(); + + fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp); + fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_primary), PorterDuff.Mode.SRC_IN); + } + }).start(); + } return; } // Signature object now successfully unlocked diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt index 882fbc804..ac12a69bb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt @@ -6,6 +6,7 @@ import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyProperties import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.Util +import java.security.InvalidKeyException import java.security.KeyPairGenerator import java.security.KeyStore import java.security.PrivateKey @@ -58,9 +59,20 @@ class BiometricSecretProvider { createAsymmetricKey(context) TextSecurePreferences.setFingerprintKeyGenerated(context) } - val key = ks.getKey(BIOMETRIC_ASYM_KEY_ALIAS, null) as PrivateKey - val signature = Signature.getInstance(SIGNATURE_ALGORITHM) - signature.initSign(key) + val signature = try { + val key = ks.getKey(BIOMETRIC_ASYM_KEY_ALIAS, null) as PrivateKey + val signature = Signature.getInstance(SIGNATURE_ALGORITHM) + signature.initSign(key) + signature + } catch (e: InvalidKeyException) { + ks.deleteEntry(BIOMETRIC_ASYM_KEY_ALIAS) + createAsymmetricKey(context) + TextSecurePreferences.setFingerprintKeyGenerated(context) + val key = ks.getKey(BIOMETRIC_ASYM_KEY_ALIAS, null) as PrivateKey + val signature = Signature.getInstance(SIGNATURE_ALGORITHM) + signature.initSign(key) + signature + } return signature } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java index e6953cabf..cb277fcf7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/DefaultMessageNotifier.java @@ -278,10 +278,10 @@ public class DefaultMessageNotifier implements MessageNotifier { try { if (notificationState.hasMultipleThreads()) { - sendMultipleThreadNotification(context, notificationState, signal); for (long threadId : notificationState.getThreads()) { sendSingleThreadNotification(context, new NotificationState(notificationState.getNotificationsForThread(threadId)), false, true); } + sendMultipleThreadNotification(context, notificationState, signal); } else if (notificationState.getMessageCount() > 0){ sendSingleThreadNotification(context, notificationState, signal, false); } else { @@ -305,7 +305,7 @@ public class DefaultMessageNotifier implements MessageNotifier { String trimmedText = ""; if (text != null) { int trimEnd = Math.min(text.length(), 50); - trimmedText = text.subSequence(0,trimEnd) + (text.length() > 50 ? "..." : ""); + trimmedText = text.subSequence(0,trimEnd) + (text.length() >= 50 ? "..." : ""); } return trimmedText; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushNotificationService.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushNotificationService.kt index e035bbb7e..fc399d293 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushNotificationService.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushNotificationService.kt @@ -4,8 +4,9 @@ import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage +import org.session.libsession.messaging.jobs.BatchMessageReceiveJob import org.session.libsession.messaging.jobs.JobQueue -import org.session.libsession.messaging.jobs.MessageReceiveJob +import org.session.libsession.messaging.jobs.MessageReceiveParameters import org.session.libsession.messaging.utilities.MessageWrapper import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Base64 @@ -27,7 +28,7 @@ class PushNotificationService : FirebaseMessagingService() { if (data != null) { try { val envelopeAsData = MessageWrapper.unwrap(data).toByteArray() - val job = MessageReceiveJob(envelopeAsData) + val job = BatchMessageReceiveJob(listOf(MessageReceiveParameters(envelopeAsData)), null) JobQueue.shared.add(job) } catch (e: Exception) { Log.d("Loki", "Failed to unwrap data for message due to error: $e.") diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt index 5ca0e09bb..77922ff1d 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt @@ -135,14 +135,12 @@ class BatchMessageReceiveJob( } storage.incrementUnread(threadId, trueUnreadCount) storage.updateThread(threadId, true) - SSKEnvironment.shared.notificationManager.updateNotification(context, threadId) } } - // await all thread processing deferredThreadMap.awaitAll() } - + SSKEnvironment.shared.notificationManager.updateNotification(context) if (failures.isEmpty()) { handleSuccess() } else {