fix: fallback on biometric key failures and retry creating key, fix up notification issues

This commit is contained in:
jubb 2022-06-21 16:17:01 +10:00
parent 58dfd3b7cd
commit ba60e8a8ee
6 changed files with 58 additions and 15 deletions

View File

@ -159,8 +159,8 @@ dependencies {
testImplementation 'org.robolectric:shadows-multidex:4.4' testImplementation 'org.robolectric:shadows-multidex:4.4'
} }
def canonicalVersionCode = 286 def canonicalVersionCode = 287
def canonicalVersionName = "1.13.5" def canonicalVersionName = "1.13.6"
def postFixSize = 10 def postFixSize = 10
def abiPostFix = ['armeabi-v7a' : 1, def abiPostFix = ['armeabi-v7a' : 1,

View File

@ -46,6 +46,7 @@ import org.thoughtcrime.securesms.crypto.BiometricSecretProvider;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.AnimationCompleteListener; import org.thoughtcrime.securesms.util.AnimationCompleteListener;
import java.security.InvalidKeyException;
import java.security.Signature; import java.security.Signature;
import network.loki.messenger.R; import network.loki.messenger.R;
@ -68,6 +69,7 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
private boolean authenticated; private boolean authenticated;
private boolean failure; private boolean failure;
private boolean hasSignatureObject = true;
private KeyCachingService keyCachingService; private KeyCachingService keyCachingService;
@ -205,7 +207,23 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
if (fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) { if (fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) {
Log.i(TAG, "Listening for fingerprints..."); Log.i(TAG, "Listening for fingerprints...");
fingerprintCancellationSignal = new CancellationSignal(); 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 { } else {
Log.i(TAG, "firing intent..."); Log.i(TAG, "firing intent...");
Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("Unlock Session", ""); Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("Unlock Session", "");
@ -230,8 +248,22 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) { public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
Log.i(TAG, "onAuthenticationSucceeded"); Log.i(TAG, "onAuthenticationSucceeded");
if (result.getCryptoObject() == null || result.getCryptoObject().getSignature() == null) { if (result.getCryptoObject() == null || result.getCryptoObject().getSignature() == null) {
// authentication failed if (hasSignatureObject) {
onAuthenticationFailed(); // 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; return;
} }
// Signature object now successfully unlocked // Signature object now successfully unlocked

View File

@ -6,6 +6,7 @@ import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties import android.security.keystore.KeyProperties
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.Util import org.session.libsession.utilities.Util
import java.security.InvalidKeyException
import java.security.KeyPairGenerator import java.security.KeyPairGenerator
import java.security.KeyStore import java.security.KeyStore
import java.security.PrivateKey import java.security.PrivateKey
@ -58,9 +59,20 @@ class BiometricSecretProvider {
createAsymmetricKey(context) createAsymmetricKey(context)
TextSecurePreferences.setFingerprintKeyGenerated(context) TextSecurePreferences.setFingerprintKeyGenerated(context)
} }
val key = ks.getKey(BIOMETRIC_ASYM_KEY_ALIAS, null) as PrivateKey val signature = try {
val signature = Signature.getInstance(SIGNATURE_ALGORITHM) val key = ks.getKey(BIOMETRIC_ASYM_KEY_ALIAS, null) as PrivateKey
signature.initSign(key) 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 return signature
} }

View File

@ -278,10 +278,10 @@ public class DefaultMessageNotifier implements MessageNotifier {
try { try {
if (notificationState.hasMultipleThreads()) { if (notificationState.hasMultipleThreads()) {
sendMultipleThreadNotification(context, notificationState, signal);
for (long threadId : notificationState.getThreads()) { for (long threadId : notificationState.getThreads()) {
sendSingleThreadNotification(context, new NotificationState(notificationState.getNotificationsForThread(threadId)), false, true); sendSingleThreadNotification(context, new NotificationState(notificationState.getNotificationsForThread(threadId)), false, true);
} }
sendMultipleThreadNotification(context, notificationState, signal);
} else if (notificationState.getMessageCount() > 0){ } else if (notificationState.getMessageCount() > 0){
sendSingleThreadNotification(context, notificationState, signal, false); sendSingleThreadNotification(context, notificationState, signal, false);
} else { } else {
@ -305,7 +305,7 @@ public class DefaultMessageNotifier implements MessageNotifier {
String trimmedText = ""; String trimmedText = "";
if (text != null) { if (text != null) {
int trimEnd = Math.min(text.length(), 50); 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; return trimmedText;
} }

View File

@ -4,8 +4,9 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage 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.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.messaging.utilities.MessageWrapper
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.utilities.Base64 import org.session.libsignal.utilities.Base64
@ -27,7 +28,7 @@ class PushNotificationService : FirebaseMessagingService() {
if (data != null) { if (data != null) {
try { try {
val envelopeAsData = MessageWrapper.unwrap(data).toByteArray() val envelopeAsData = MessageWrapper.unwrap(data).toByteArray()
val job = MessageReceiveJob(envelopeAsData) val job = BatchMessageReceiveJob(listOf(MessageReceiveParameters(envelopeAsData)), null)
JobQueue.shared.add(job) JobQueue.shared.add(job)
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Failed to unwrap data for message due to error: $e.") Log.d("Loki", "Failed to unwrap data for message due to error: $e.")

View File

@ -135,14 +135,12 @@ class BatchMessageReceiveJob(
} }
storage.incrementUnread(threadId, trueUnreadCount) storage.incrementUnread(threadId, trueUnreadCount)
storage.updateThread(threadId, true) storage.updateThread(threadId, true)
SSKEnvironment.shared.notificationManager.updateNotification(context, threadId)
} }
} }
// await all thread processing // await all thread processing
deferredThreadMap.awaitAll() deferredThreadMap.awaitAll()
} }
SSKEnvironment.shared.notificationManager.updateNotification(context)
if (failures.isEmpty()) { if (failures.isEmpty()) {
handleSuccess() handleSuccess()
} else { } else {