That fuck shit the fascists are using
at master 414 lines 16 kB view raw
1/* 2 * Copyright (C) 2011 Whisper Systems 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17package org.tm.archive; 18 19import android.animation.Animator; 20import android.app.KeyguardManager; 21import android.content.Context; 22import android.content.Intent; 23import android.graphics.PorterDuff; 24import android.os.Bundle; 25import android.text.Editable; 26import android.text.InputType; 27import android.text.SpannableString; 28import android.text.Spanned; 29import android.text.style.RelativeSizeSpan; 30import android.text.style.TypefaceSpan; 31import android.view.KeyEvent; 32import android.view.Menu; 33import android.view.MenuInflater; 34import android.view.MenuItem; 35import android.view.View; 36import android.view.View.OnClickListener; 37import android.view.animation.Animation; 38import android.view.animation.BounceInterpolator; 39import android.view.animation.TranslateAnimation; 40import android.view.inputmethod.EditorInfo; 41import android.widget.EditText; 42import android.widget.ImageButton; 43import android.widget.ImageView; 44import android.widget.TextView; 45 46import androidx.annotation.NonNull; 47import androidx.appcompat.widget.Toolbar; 48import androidx.biometric.BiometricManager; 49import androidx.biometric.BiometricPrompt; 50 51import org.signal.core.util.ThreadUtil; 52import org.signal.core.util.logging.Log; 53import org.tm.archive.animation.AnimationCompleteListener; 54import org.tm.archive.components.AnimatingToggle; 55import org.tm.archive.crypto.InvalidPassphraseException; 56import org.tm.archive.crypto.MasterSecret; 57import org.tm.archive.crypto.MasterSecretUtil; 58import org.tm.archive.logsubmit.SubmitDebugLogActivity; 59import org.tm.archive.util.CommunicationActions; 60import org.tm.archive.util.DynamicIntroTheme; 61import org.tm.archive.util.DynamicLanguage; 62import org.tm.archive.util.SupportEmailUtil; 63import org.tm.archive.util.TextSecurePreferences; 64 65import kotlin.Unit; 66 67/** 68 * Activity that prompts for a user's passphrase. 69 * 70 * @author Moxie Marlinspike 71 */ 72public class PassphrasePromptActivity extends PassphraseActivity { 73 74 private static final String TAG = Log.tag(PassphrasePromptActivity.class); 75 private static final short AUTHENTICATE_REQUEST_CODE = 1007; 76 private static final String BUNDLE_ALREADY_SHOWN = "bundle_already_shown"; 77 public static final String FROM_FOREGROUND = "from_foreground"; 78 79 private DynamicIntroTheme dynamicTheme = new DynamicIntroTheme(); 80 private DynamicLanguage dynamicLanguage = new DynamicLanguage(); 81 82 private View passphraseAuthContainer; 83 private ImageView fingerprintPrompt; 84 private TextView lockScreenButton; 85 86 private EditText passphraseText; 87 private ImageButton showButton; 88 private ImageButton hideButton; 89 private AnimatingToggle visibilityToggle; 90 91 private BiometricManager biometricManager; 92 private BiometricPrompt biometricPrompt; 93 private BiometricDeviceAuthentication biometricAuth; 94 95 private boolean authenticated; 96 private boolean hadFailure; 97 private boolean alreadyShown; 98 99 private final Runnable resumeScreenLockRunnable = () -> { 100 resumeScreenLock(!alreadyShown); 101 alreadyShown = true; 102 }; 103 104 @Override 105 public void onCreate(Bundle savedInstanceState) { 106 Log.i(TAG, "onCreate()"); 107 dynamicTheme.onCreate(this); 108 dynamicLanguage.onCreate(this); 109 super.onCreate(savedInstanceState); 110 111 setContentView(R.layout.prompt_passphrase_activity); 112 initializeResources(); 113 114 alreadyShown = (savedInstanceState != null && savedInstanceState.getBoolean(BUNDLE_ALREADY_SHOWN)) || 115 getIntent().getBooleanExtra(FROM_FOREGROUND, false); 116 } 117 118 @Override 119 protected void onSaveInstanceState(@NonNull Bundle outState) { 120 super.onSaveInstanceState(outState); 121 outState.putBoolean(BUNDLE_ALREADY_SHOWN, alreadyShown); 122 } 123 124 @Override 125 public void onResume() { 126 super.onResume(); 127 dynamicTheme.onResume(this); 128 dynamicLanguage.onResume(this); 129 130 setLockTypeVisibility(); 131 132 if (TextSecurePreferences.isScreenLockEnabled(this) && !authenticated && !hadFailure) { 133 ThreadUtil.postToMain(resumeScreenLockRunnable); 134 } 135 136 hadFailure = false; 137 138 fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp); 139 fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_accent_primary), PorterDuff.Mode.SRC_IN); 140 } 141 142 @Override 143 public void onPause() { 144 super.onPause(); 145 ThreadUtil.cancelRunnableOnMain(resumeScreenLockRunnable); 146 biometricPrompt.cancelAuthentication(); 147 } 148 149 @Override 150 protected void onNewIntent(Intent intent) { 151 super.onNewIntent(intent); 152 setIntent(intent); 153 } 154 155 @Override 156 public boolean onCreateOptionsMenu(Menu menu) { 157 MenuInflater inflater = this.getMenuInflater(); 158 menu.clear(); 159 160 inflater.inflate(R.menu.passphrase_prompt, menu); 161 162 super.onCreateOptionsMenu(menu); 163 return true; 164 } 165 166 @Override 167 public boolean onOptionsItemSelected(MenuItem item) { 168 super.onOptionsItemSelected(item); 169 if (item.getItemId() == R.id.menu_submit_debug_logs) { 170 handleLogSubmit(); 171 return true; 172 } else if (item.getItemId() == R.id.menu_contact_support) { 173 sendEmailToSupport(); 174 return true; 175 } 176 177 return false; 178 } 179 180 @Override 181 public void onActivityResult(int requestCode, int resultCode, Intent data) { 182 super.onActivityResult(requestCode, resultCode, data); 183 184 if (requestCode != AUTHENTICATE_REQUEST_CODE) return; 185 186 if (resultCode == RESULT_OK) { 187 handleAuthenticated(); 188 } else { 189 Log.w(TAG, "Authentication failed"); 190 hadFailure = true; 191 } 192 } 193 194 private void handleLogSubmit() { 195 Intent intent = new Intent(this, SubmitDebugLogActivity.class); 196 startActivity(intent); 197 } 198 199 private void handlePassphrase() { 200 try { 201 Editable text = passphraseText.getText(); 202 String passphrase = (text == null ? "" : text.toString()); 203 MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, passphrase); 204 205 setMasterSecret(masterSecret); 206 } catch (InvalidPassphraseException ipe) { 207 passphraseText.setText(""); 208 passphraseText.setError( 209 getString(R.string.PassphrasePromptActivity_invalid_passphrase_exclamation)); 210 } 211 } 212 213 private void handleAuthenticated() { 214 try { 215 authenticated = true; 216 217 MasterSecret masterSecret = MasterSecretUtil.getMasterSecret(this, MasterSecretUtil.UNENCRYPTED_PASSPHRASE); 218 setMasterSecret(masterSecret); 219 } catch (InvalidPassphraseException e) { 220 throw new AssertionError(e); 221 } 222 } 223 224 private void setPassphraseVisibility(boolean visibility) { 225 int cursorPosition = passphraseText.getSelectionStart(); 226 if (visibility) { 227 passphraseText.setInputType(InputType.TYPE_CLASS_TEXT | 228 InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); 229 } else { 230 passphraseText.setInputType(InputType.TYPE_CLASS_TEXT | 231 InputType.TYPE_TEXT_VARIATION_PASSWORD); 232 } 233 passphraseText.setSelection(cursorPosition); 234 } 235 236 private void initializeResources() { 237 238 ImageButton okButton = findViewById(R.id.ok_button); 239 Toolbar toolbar = findViewById(R.id.toolbar); 240 241 showButton = findViewById(R.id.passphrase_visibility); 242 hideButton = findViewById(R.id.passphrase_visibility_off); 243 visibilityToggle = findViewById(R.id.button_toggle); 244 passphraseText = findViewById(R.id.passphrase_edit); 245 passphraseAuthContainer = findViewById(R.id.password_auth_container); 246 fingerprintPrompt = findViewById(R.id.fingerprint_auth_container); 247 lockScreenButton = findViewById(R.id.lock_screen_auth_container); 248 biometricManager = BiometricManager.from(this); 249 biometricPrompt = new BiometricPrompt(this, new BiometricAuthenticationListener()); 250 BiometricPrompt.PromptInfo biometricPromptInfo = new BiometricPrompt.PromptInfo 251 .Builder() 252 .setAllowedAuthenticators(BiometricDeviceAuthentication.ALLOWED_AUTHENTICATORS) 253 .setTitle(getString(R.string.PassphrasePromptActivity_unlock_signal)) 254 .build(); 255 biometricAuth = new BiometricDeviceAuthentication(biometricManager, biometricPrompt, biometricPromptInfo); 256 setSupportActionBar(toolbar); 257 getSupportActionBar().setTitle(""); 258 259 SpannableString hint = new SpannableString(" " + getString(R.string.PassphrasePromptActivity_enter_passphrase)); 260 hint.setSpan(new RelativeSizeSpan(0.9f), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 261 hint.setSpan(new TypefaceSpan("sans-serif"), 0, hint.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); 262 263 passphraseText.setHint(hint); 264 okButton.setOnClickListener(new OkButtonClickListener()); 265 showButton.setOnClickListener(new ShowButtonOnClickListener()); 266 hideButton.setOnClickListener(new HideButtonOnClickListener()); 267 passphraseText.setOnEditorActionListener(new PassphraseActionListener()); 268 passphraseText.setImeActionLabel(getString(R.string.prompt_passphrase_activity__unlock), 269 EditorInfo.IME_ACTION_DONE); 270 271 fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp); 272 fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.core_ultramarine), PorterDuff.Mode.SRC_IN); 273 274 lockScreenButton.setOnClickListener(v -> resumeScreenLock(true)); 275 } 276 277 private void setLockTypeVisibility() { 278 if (TextSecurePreferences.isScreenLockEnabled(this)) { 279 passphraseAuthContainer.setVisibility(View.GONE); 280 fingerprintPrompt.setVisibility(biometricManager.canAuthenticate(BiometricDeviceAuthentication.BIOMETRIC_AUTHENTICATORS) == BiometricManager.BIOMETRIC_SUCCESS ? View.VISIBLE 281 : View.GONE); 282 lockScreenButton.setVisibility(View.VISIBLE); 283 } else { 284 passphraseAuthContainer.setVisibility(View.VISIBLE); 285 fingerprintPrompt.setVisibility(View.GONE); 286 lockScreenButton.setVisibility(View.GONE); 287 } 288 } 289 290 private void resumeScreenLock(boolean force) { 291 if (!biometricAuth.authenticate(getApplicationContext(), force, this::showConfirmDeviceCredentialIntent)) { 292 handleAuthenticated(); 293 } 294 } 295 296 private void sendEmailToSupport() { 297 String body = SupportEmailUtil.generateSupportEmailBody(this, 298 R.string.PassphrasePromptActivity_signal_android_lock_screen, 299 null, 300 null); 301 CommunicationActions.openEmail(this, 302 SupportEmailUtil.getSupportEmailAddress(this), 303 getString(R.string.PassphrasePromptActivity_signal_android_lock_screen), 304 body); 305 } 306 307 public Unit showConfirmDeviceCredentialIntent() { 308 KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE); 309 Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(getString(R.string.PassphrasePromptActivity_unlock_signal), ""); 310 311 startActivityForResult(intent, AUTHENTICATE_REQUEST_CODE); 312 return Unit.INSTANCE; 313 } 314 315 private class PassphraseActionListener implements TextView.OnEditorActionListener { 316 @Override 317 public boolean onEditorAction(TextView exampleView, int actionId, KeyEvent keyEvent) { 318 if ((keyEvent == null && actionId == EditorInfo.IME_ACTION_DONE) || 319 (keyEvent != null && keyEvent.getAction() == KeyEvent.ACTION_DOWN && 320 (actionId == EditorInfo.IME_NULL))) 321 { 322 handlePassphrase(); 323 return true; 324 } else if (keyEvent != null && keyEvent.getAction() == KeyEvent.ACTION_UP && 325 actionId == EditorInfo.IME_NULL) 326 { 327 return true; 328 } 329 330 return false; 331 } 332 } 333 334 private class OkButtonClickListener implements OnClickListener { 335 @Override 336 public void onClick(View v) { 337 handlePassphrase(); 338 } 339 } 340 341 private class ShowButtonOnClickListener implements OnClickListener { 342 @Override 343 public void onClick(View v) { 344 visibilityToggle.display(hideButton); 345 setPassphraseVisibility(true); 346 } 347 } 348 349 private class HideButtonOnClickListener implements OnClickListener { 350 @Override 351 public void onClick(View v) { 352 visibilityToggle.display(showButton); 353 setPassphraseVisibility(false); 354 } 355 } 356 357 @Override 358 protected void cleanup() { 359 this.passphraseText.setText(""); 360 System.gc(); 361 } 362 363 private class BiometricAuthenticationListener extends BiometricPrompt.AuthenticationCallback { 364 @Override 365 public void onAuthenticationError(int errorCode, @NonNull CharSequence errorString) { 366 Log.w(TAG, "Authentication error: " + errorCode); 367 hadFailure = true; 368 369 if (errorCode != BiometricPrompt.ERROR_CANCELED && errorCode != BiometricPrompt.ERROR_USER_CANCELED) { 370 onAuthenticationFailed(); 371 } 372 } 373 374 @Override 375 public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { 376 Log.i(TAG, "onAuthenticationSucceeded"); 377 fingerprintPrompt.setImageResource(R.drawable.symbol_check_white_48); 378 fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.green_500), PorterDuff.Mode.SRC_IN); 379 fingerprintPrompt.animate().setInterpolator(new BounceInterpolator()).scaleX(1.1f).scaleY(1.1f).setDuration(500).setListener(new AnimationCompleteListener() { 380 @Override 381 public void onAnimationEnd(Animator animation) { 382 handleAuthenticated(); 383 } 384 }).start(); 385 } 386 387 @Override 388 public void onAuthenticationFailed() { 389 Log.w(TAG, "onAuthenticationFailed()"); 390 391 fingerprintPrompt.setImageResource(R.drawable.symbol_x_white_48); 392 fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.red_500), PorterDuff.Mode.SRC_IN); 393 394 TranslateAnimation shake = new TranslateAnimation(0, 30, 0, 0); 395 shake.setDuration(50); 396 shake.setRepeatCount(7); 397 shake.setAnimationListener(new Animation.AnimationListener() { 398 @Override 399 public void onAnimationStart(Animation animation) {} 400 401 @Override 402 public void onAnimationEnd(Animation animation) { 403 fingerprintPrompt.setImageResource(R.drawable.ic_fingerprint_white_48dp); 404 fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.signal_accent_primary), PorterDuff.Mode.SRC_IN); 405 } 406 407 @Override 408 public void onAnimationRepeat(Animation animation) {} 409 }); 410 411 fingerprintPrompt.startAnimation(shake); 412 } 413 } 414}