#635 - First pass at launching the background task when authenticating
Began disabling the password input and unlock buttons while the task is running, for some UI feedback. Right now it's just waiting for the task to finish if the activity gets paused while it's running; this isn't great since there's potential there to lock the main thread, but the task is so short-lived that it may not be an issue.
This commit is contained in:
parent
8c8de98763
commit
1780d24955
1 changed files with 56 additions and 55 deletions
|
@ -28,10 +28,11 @@ import android.os.Bundle;
|
|||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.text.InputType;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewStub;
|
||||
|
@ -42,28 +43,30 @@ import android.widget.EditText;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.shadowice.flocke.andotp.R;
|
||||
import org.shadowice.flocke.andotp.Tasks.AuthenticationTask;
|
||||
import org.shadowice.flocke.andotp.Tasks.AuthenticationTask.Callback;
|
||||
import org.shadowice.flocke.andotp.Tasks.AuthenticationTask.Result;
|
||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper.PBKDF2Credentials;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod;
|
||||
|
||||
public class AuthenticateActivity extends ThemedActivity
|
||||
implements EditText.OnEditorActionListener, View.OnClickListener {
|
||||
|
||||
private static final String KEY_WAS_TASK_ACTIVE = "AuthenticateActivity.WasTaskActive";
|
||||
|
||||
private AuthMethod authMethod;
|
||||
private String newEncryption = "";
|
||||
private String existingAuthCredentials;
|
||||
private boolean isAuthUpgrade = false;
|
||||
|
||||
private TextInputEditText passwordInput;
|
||||
private Button unlockButton;
|
||||
|
||||
private AuthenticationTask activeTask;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -146,78 +149,76 @@ public class AuthenticateActivity extends ThemedActivity
|
|||
}
|
||||
|
||||
private void initUnlockButtonView(View v) {
|
||||
Button unlockButton = v.findViewById(R.id.buttonUnlock);
|
||||
unlockButton = v.findViewById(R.id.buttonUnlock);
|
||||
unlockButton.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
checkPassword(passwordInput.getText().toString());
|
||||
startAuthTask(passwordInput.getText().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
checkPassword(v.getText().toString());
|
||||
startAuthTask(v.getText().toString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void checkPassword(String plainPassword) {
|
||||
if (!isAuthUpgrade) {
|
||||
try {
|
||||
PBKDF2Credentials credentials = EncryptionHelper.generatePBKDF2Credentials(plainPassword, settings.getSalt(), settings.getIterations());
|
||||
byte[] passwordArray = Base64.decode(existingAuthCredentials, Base64.URL_SAFE);
|
||||
|
||||
if (Arrays.equals(passwordArray, credentials.password)) {
|
||||
finishWithResult(true, credentials.key);
|
||||
} else {
|
||||
finishWithResult(false, null);
|
||||
private void startAuthTask(String plainPassword) {
|
||||
passwordInput.setEnabled(false);
|
||||
unlockButton.setEnabled(false);
|
||||
Callback callback = this::handleResult;
|
||||
activeTask = new AuthenticationTask(this, callback, isAuthUpgrade, existingAuthCredentials, plainPassword);
|
||||
activeTask.execute();
|
||||
}
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException | IllegalArgumentException e) {
|
||||
e.printStackTrace();
|
||||
finishWithResult(false, null);
|
||||
}
|
||||
} else {
|
||||
String hashedPassword = new String(Hex.encodeHex(DigestUtils.sha256(plainPassword)));
|
||||
|
||||
if (hashedPassword.equals(existingAuthCredentials)) {
|
||||
byte[] key = settings.setAuthCredentials(plainPassword);
|
||||
|
||||
if (key == null)
|
||||
private void handleResult(Result result) {
|
||||
activeTask = null;
|
||||
if (result.authUpgradeFailed) {
|
||||
Toast.makeText(this, R.string.settings_toast_auth_upgrade_failed, Toast.LENGTH_LONG).show();
|
||||
|
||||
if (authMethod == AuthMethod.PASSWORD)
|
||||
settings.removeAuthPasswordHash();
|
||||
else if (authMethod == AuthMethod.PIN)
|
||||
settings.removeAuthPINHash();
|
||||
|
||||
finishWithResult(true, key);
|
||||
} else {
|
||||
finishWithResult(false, null);
|
||||
}
|
||||
}
|
||||
finishWithResult(result.encryptionKey != null, result.encryptionKey);
|
||||
}
|
||||
|
||||
// End with a result
|
||||
public void finishWithResult(boolean success, byte[] key) {
|
||||
private void finishWithResult(boolean success, byte[] encryptionKey) {
|
||||
Intent data = new Intent();
|
||||
|
||||
if (newEncryption != null && !newEncryption.isEmpty())
|
||||
data.putExtra(Constants.EXTRA_AUTH_NEW_ENCRYPTION, newEncryption);
|
||||
if (key != null)
|
||||
data.putExtra(Constants.EXTRA_AUTH_PASSWORD_KEY, key);
|
||||
if (encryptionKey != null)
|
||||
data.putExtra(Constants.EXTRA_AUTH_PASSWORD_KEY, encryptionKey);
|
||||
if (success)
|
||||
setResult(RESULT_OK, data);
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
// Go back to the main activity
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
completeTaskIfActive();
|
||||
}
|
||||
|
||||
/** @return true if the task was active and was completed, false otherwise. */
|
||||
private boolean completeTaskIfActive() {
|
||||
try {
|
||||
// This will cause the main thread to wait, but it'll ensure that the task completes.
|
||||
if (activeTask != null) {
|
||||
handleResult(activeTask.get());
|
||||
return true;
|
||||
}
|
||||
} catch (ExecutionException | InterruptedException e) {
|
||||
Log.e("AuthenticateActivity", "Could not finish active authentication task", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!completeTaskIfActive()) {
|
||||
finishWithResult(false, null);
|
||||
}
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue