mirror of
https://github.com/moparisthebest/open-keychain
synced 2025-02-25 16:01:52 -05:00
add proper async check for correct passphrase to passphrasedialog
This commit is contained in:
parent
02663de191
commit
fd7bdbf54f
@ -23,6 +23,7 @@ import android.app.Dialog;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.DialogInterface.OnClickListener;
|
import android.content.DialogInterface.OnClickListener;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
@ -64,6 +65,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
|||||||
|
|
||||||
private Messenger mMessenger;
|
private Messenger mMessenger;
|
||||||
private EditText mPassphraseEditText;
|
private EditText mPassphraseEditText;
|
||||||
|
private View mInput, mProgress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows passphrase dialog to cache a new passphrase the user enters for using it later for
|
* Shows passphrase dialog to cache a new passphrase the user enters for using it later for
|
||||||
@ -77,8 +79,8 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
|||||||
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
|
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
PassphraseDialogFragment passphraseDialog = PassphraseDialogFragment.newInstance(context,
|
PassphraseDialogFragment passphraseDialog =
|
||||||
messenger, keyId);
|
PassphraseDialogFragment.newInstance(messenger, keyId);
|
||||||
|
|
||||||
passphraseDialog.show(context.getSupportFragmentManager(), "passphraseDialog");
|
passphraseDialog.show(context.getSupportFragmentManager(), "passphraseDialog");
|
||||||
} catch (PgpGeneralException e) {
|
} catch (PgpGeneralException e) {
|
||||||
@ -98,8 +100,8 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
|||||||
* @return
|
* @return
|
||||||
* @throws PgpGeneralException
|
* @throws PgpGeneralException
|
||||||
*/
|
*/
|
||||||
public static PassphraseDialogFragment newInstance(Context context, Messenger messenger,
|
public static PassphraseDialogFragment newInstance(Messenger messenger, long secretKeyId)
|
||||||
long secretKeyId) throws PgpGeneralException {
|
throws PgpGeneralException {
|
||||||
// do NOT check if the key even needs a passphrase. that's not our job here.
|
// do NOT check if the key even needs a passphrase. that's not our job here.
|
||||||
PassphraseDialogFragment frag = new PassphraseDialogFragment();
|
PassphraseDialogFragment frag = new PassphraseDialogFragment();
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
@ -111,46 +113,48 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
|||||||
return frag;
|
return frag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CanonicalizedSecretKeyRing mSecretRing = null;
|
||||||
|
boolean mIsCancelled = false;
|
||||||
|
long mSubKeyId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates dialog
|
* Creates dialog
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
final Activity activity = getActivity();
|
final Activity activity = getActivity();
|
||||||
final long subKeyId = getArguments().getLong(ARG_SECRET_KEY_ID);
|
mSubKeyId = getArguments().getLong(ARG_SECRET_KEY_ID);
|
||||||
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
|
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
|
||||||
|
|
||||||
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
|
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
|
||||||
|
|
||||||
alert.setTitle(R.string.title_authentication);
|
alert.setTitle(R.string.title_authentication);
|
||||||
|
|
||||||
final CanonicalizedSecretKeyRing secretRing;
|
|
||||||
String userId;
|
String userId;
|
||||||
|
|
||||||
if (subKeyId == Constants.key.symmetric || subKeyId == Constants.key.none) {
|
if (mSubKeyId == Constants.key.symmetric || mSubKeyId == Constants.key.none) {
|
||||||
alert.setMessage(R.string.passphrase_for_symmetric_encryption);
|
alert.setMessage(R.string.passphrase_for_symmetric_encryption);
|
||||||
secretRing = null;
|
|
||||||
} else {
|
} else {
|
||||||
String message;
|
String message;
|
||||||
try {
|
try {
|
||||||
ProviderHelper helper = new ProviderHelper(activity);
|
ProviderHelper helper = new ProviderHelper(activity);
|
||||||
secretRing = helper.getCanonicalizedSecretKeyRing(
|
mSecretRing = helper.getCanonicalizedSecretKeyRing(
|
||||||
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId));
|
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(mSubKeyId));
|
||||||
// yes the inner try/catch block is necessary, otherwise the final variable
|
// yes the inner try/catch block is necessary, otherwise the final variable
|
||||||
// above can't be statically verified to have been set in all cases because
|
// above can't be statically verified to have been set in all cases because
|
||||||
// the catch clause doesn't return.
|
// the catch clause doesn't return.
|
||||||
try {
|
try {
|
||||||
userId = secretRing.getPrimaryUserIdWithFallback();
|
userId = mSecretRing.getPrimaryUserIdWithFallback();
|
||||||
} catch (PgpGeneralException e) {
|
} catch (PgpGeneralException e) {
|
||||||
userId = null;
|
userId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get key type for message */
|
/* Get key type for message */
|
||||||
// find a master key id for our key
|
// find a master key id for our key
|
||||||
long masterKeyId = new ProviderHelper(getActivity()).getMasterKeyId(subKeyId);
|
long masterKeyId = new ProviderHelper(getActivity()).getMasterKeyId(mSubKeyId);
|
||||||
CachedPublicKeyRing keyRing = new ProviderHelper(getActivity()).getCachedPublicKeyRing(masterKeyId);
|
CachedPublicKeyRing keyRing = new ProviderHelper(getActivity()).getCachedPublicKeyRing(masterKeyId);
|
||||||
// get the type of key (from the database)
|
// get the type of key (from the database)
|
||||||
CanonicalizedSecretKey.SecretKeyType keyType = keyRing.getSecretKeyType(subKeyId);
|
CanonicalizedSecretKey.SecretKeyType keyType = keyRing.getSecretKeyType(mSubKeyId);
|
||||||
switch (keyType) {
|
switch (keyType) {
|
||||||
case PASSPHRASE:
|
case PASSPHRASE:
|
||||||
message = getString(R.string.passphrase_for, userId);
|
message = getString(R.string.passphrase_for, userId);
|
||||||
@ -165,7 +169,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
|||||||
|
|
||||||
} catch (ProviderHelper.NotFoundException e) {
|
} catch (ProviderHelper.NotFoundException e) {
|
||||||
alert.setTitle(R.string.title_key_not_found);
|
alert.setTitle(R.string.title_key_not_found);
|
||||||
alert.setMessage(getString(R.string.key_not_found, subKeyId));
|
alert.setMessage(getString(R.string.key_not_found, mSubKeyId));
|
||||||
alert.setPositiveButton(android.R.string.ok, new OnClickListener() {
|
alert.setPositiveButton(android.R.string.ok, new OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
dismiss();
|
dismiss();
|
||||||
@ -183,54 +187,8 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
|||||||
alert.setView(view);
|
alert.setView(view);
|
||||||
|
|
||||||
mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase);
|
mPassphraseEditText = (EditText) view.findViewById(R.id.passphrase_passphrase);
|
||||||
|
mInput = view.findViewById(R.id.input);
|
||||||
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
mProgress = view.findViewById(R.id.progress);
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
|
||||||
dismiss();
|
|
||||||
|
|
||||||
String passphrase = mPassphraseEditText.getText().toString();
|
|
||||||
|
|
||||||
// Early breakout if we are dealing with a symmetric key
|
|
||||||
if (secretRing == null) {
|
|
||||||
PassphraseCacheService.addCachedPassphrase(activity, Constants.key.symmetric,
|
|
||||||
passphrase, getString(R.string.passp_cache_notif_pwd));
|
|
||||||
// also return passphrase back to activity
|
|
||||||
Bundle data = new Bundle();
|
|
||||||
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
|
|
||||||
sendMessageToHandler(MESSAGE_OKAY, data);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// make sure this unlocks
|
|
||||||
// TODO this is a very costly operation, we should not be doing this here!
|
|
||||||
secretRing.getSecretKey(subKeyId).unlock(passphrase);
|
|
||||||
} catch (PgpGeneralException e) {
|
|
||||||
Toast.makeText(activity, R.string.error_could_not_extract_private_key,
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
|
|
||||||
sendMessageToHandler(MESSAGE_CANCEL);
|
|
||||||
return; // ran out of keys to try
|
|
||||||
}
|
|
||||||
|
|
||||||
// cache the new passphrase
|
|
||||||
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
|
|
||||||
|
|
||||||
try {
|
|
||||||
PassphraseCacheService.addCachedPassphrase(activity, subKeyId, passphrase,
|
|
||||||
secretRing.getPrimaryUserIdWithFallback());
|
|
||||||
} catch (PgpGeneralException e) {
|
|
||||||
Log.e(Constants.TAG, "adding of a passphrase failed", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// also return passphrase back to activity
|
|
||||||
Bundle data = new Bundle();
|
|
||||||
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
|
|
||||||
sendMessageToHandler(MESSAGE_OKAY, data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
||||||
|
|
||||||
@ -263,13 +221,112 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
|||||||
mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
|
mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
|
||||||
mPassphraseEditText.setOnEditorActionListener(this);
|
mPassphraseEditText.setOnEditorActionListener(this);
|
||||||
|
|
||||||
return alert.show();
|
AlertDialog dialog = alert.create();
|
||||||
|
dialog.setButton(DialogInterface.BUTTON_POSITIVE,
|
||||||
|
activity.getString(android.R.string.ok), (DialogInterface.OnClickListener) null);
|
||||||
|
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
// Override the default behavior so the dialog is NOT dismissed on click
|
||||||
|
final Button positive = ((AlertDialog) getDialog()).getButton(DialogInterface.BUTTON_POSITIVE);
|
||||||
|
positive.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
final String passphrase = mPassphraseEditText.getText().toString();
|
||||||
|
|
||||||
|
// Early breakout if we are dealing with a symmetric key
|
||||||
|
if (mSecretRing == null) {
|
||||||
|
PassphraseCacheService.addCachedPassphrase(getActivity(), Constants.key.symmetric,
|
||||||
|
passphrase, getString(R.string.passp_cache_notif_pwd));
|
||||||
|
// also return passphrase back to activity
|
||||||
|
Bundle data = new Bundle();
|
||||||
|
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
|
||||||
|
sendMessageToHandler(MESSAGE_OKAY, data);
|
||||||
|
dismiss();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mInput.setVisibility(View.GONE);
|
||||||
|
mProgress.setVisibility(View.VISIBLE);
|
||||||
|
positive.setEnabled(false);
|
||||||
|
|
||||||
|
new AsyncTask<Void,Void,Boolean>() {
|
||||||
|
@Override
|
||||||
|
protected Boolean doInBackground(Void... params) {
|
||||||
|
try {
|
||||||
|
// wait some 100ms here, give the user time to appreciate the progress bar
|
||||||
|
try {
|
||||||
|
Thread.sleep(100);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// never mind
|
||||||
|
}
|
||||||
|
// make sure this unlocks
|
||||||
|
return mSecretRing.getSecretKey(mSubKeyId).unlock(passphrase);
|
||||||
|
} catch (PgpGeneralException e) {
|
||||||
|
Toast.makeText(getActivity(), R.string.error_could_not_extract_private_key,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
sendMessageToHandler(MESSAGE_CANCEL);
|
||||||
|
dismiss();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handle a good or bad passphrase. This happens in the UI thread! */
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Boolean result) {
|
||||||
|
super.onPostExecute(result);
|
||||||
|
|
||||||
|
// if we were cancelled in the meantime, the result isn't relevant anymore
|
||||||
|
if (mIsCancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the passphrase was wrong, reset and re-enable the dialogue
|
||||||
|
if (!result) {
|
||||||
|
// TODO add a "bad passphrase" dialogue?
|
||||||
|
mPassphraseEditText.setText("");
|
||||||
|
mInput.setVisibility(View.VISIBLE);
|
||||||
|
mProgress.setVisibility(View.GONE);
|
||||||
|
positive.setEnabled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cache the new passphrase
|
||||||
|
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
|
||||||
|
|
||||||
|
try {
|
||||||
|
PassphraseCacheService.addCachedPassphrase(getActivity(), mSubKeyId,
|
||||||
|
passphrase, mSecretRing.getPrimaryUserIdWithFallback());
|
||||||
|
} catch (PgpGeneralException e) {
|
||||||
|
Log.e(Constants.TAG, "adding of a passphrase failed", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// also return passphrase back to activity
|
||||||
|
Bundle data = new Bundle();
|
||||||
|
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
|
||||||
|
sendMessageToHandler(MESSAGE_OKAY, data);
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCancel(DialogInterface dialog) {
|
public void onCancel(DialogInterface dialog) {
|
||||||
super.onCancel(dialog);
|
super.onCancel(dialog);
|
||||||
|
|
||||||
|
// note we need no synchronization here, this variable is only accessed in the ui thread
|
||||||
|
mIsCancelled = true;
|
||||||
|
|
||||||
|
// dismiss the dialogue
|
||||||
dismiss();
|
dismiss();
|
||||||
sendMessageToHandler(MESSAGE_CANCEL);
|
sendMessageToHandler(MESSAGE_CANCEL);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,16 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:paddingLeft="16dp"
|
android:paddingLeft="16dp"
|
||||||
android:paddingRight="16dp" >
|
android:paddingRight="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:id="@+id/input">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/passphrase_label_passphrase"
|
android:id="@+id/passphrase_label_passphrase"
|
||||||
@ -20,5 +29,29 @@
|
|||||||
android:imeOptions="actionDone"
|
android:imeOptions="actionDone"
|
||||||
android:inputType="textPassword"
|
android:inputType="textPassword"
|
||||||
android:padding="4dp" />
|
android:padding="4dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:id="@+id/progress"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:text="@string/label_unlock"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
style="?android:attr/progressBarStyleSmall"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -87,6 +87,7 @@
|
|||||||
<string name="label_file_colon">File:</string>
|
<string name="label_file_colon">File:</string>
|
||||||
<string name="label_no_passphrase">No Passphrase</string>
|
<string name="label_no_passphrase">No Passphrase</string>
|
||||||
<string name="label_passphrase">Passphrase</string>
|
<string name="label_passphrase">Passphrase</string>
|
||||||
|
<string name="label_unlock">Unlocking…</string>
|
||||||
<string name="label_passphrase_again">Repeat Passphrase</string>
|
<string name="label_passphrase_again">Repeat Passphrase</string>
|
||||||
<string name="label_algorithm">Algorithm</string>
|
<string name="label_algorithm">Algorithm</string>
|
||||||
<string name="label_ascii_armor">File ASCII Armor</string>
|
<string name="label_ascii_armor">File ASCII Armor</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user