diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java')
-rw-r--r-- | OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java | 270 |
1 files changed, 136 insertions, 134 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index c6431bfaf..e71349880 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -17,16 +17,18 @@ package org.sufficientlysecure.keychain.ui; + import android.app.Activity; -import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; +import android.support.v7.app.AlertDialog; import android.text.InputType; import android.text.method.PasswordTransformationMethod; import android.view.ContextThemeWrapper; @@ -43,20 +45,21 @@ import android.widget.Toast; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.remote.CryptoInputParcelCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; +import org.sufficientlysecure.keychain.ui.util.ThemeChanger; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; @@ -64,6 +67,8 @@ import org.sufficientlysecure.keychain.util.Preferences; /** * We can not directly create a dialog on the application context. * This activity encapsulates a DialogFragment to emulate a dialog. + * NOTE: If no CryptoInputParcel is passed via EXTRA_CRYPTO_INPUT, the CryptoInputParcel is created + * internally and is NOT meant to be used by signing operations before adding a signature time */ public class PassphraseDialogActivity extends FragmentActivity { @@ -71,11 +76,13 @@ public class PassphraseDialogActivity extends FragmentActivity { public static final String EXTRA_REQUIRED_INPUT = "required_input"; public static final String EXTRA_SUBKEY_ID = "secret_key_id"; + public static final String EXTRA_CRYPTO_INPUT = "crypto_input"; // special extra for OpenPgpService public static final String EXTRA_SERVICE_INTENT = "data"; + private long mSubKeyId; - private static final int REQUEST_CODE_ENTER_PATTERN = 2; + private CryptoInputParcel mCryptoInputParcel; @Override protected void onCreate(Bundle savedInstanceState) { @@ -90,20 +97,52 @@ public class PassphraseDialogActivity extends FragmentActivity { ); } + mCryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT); + + if (mCryptoInputParcel == null) { + // not all usages of PassphraseActivity are from CryptoInputOperation + // NOTE: This CryptoInputParcel cannot be used for signing operations without setting + // signature time + mCryptoInputParcel = new CryptoInputParcel(); + } + // this activity itself has no content view (see manifest) - long keyId; if (getIntent().hasExtra(EXTRA_SUBKEY_ID)) { - keyId = getIntent().getLongExtra(EXTRA_SUBKEY_ID, 0); + mSubKeyId = getIntent().getLongExtra(EXTRA_SUBKEY_ID, 0); } else { RequiredInputParcel requiredInput = getIntent().getParcelableExtra(EXTRA_REQUIRED_INPUT); switch (requiredInput.mType) { case PASSPHRASE_SYMMETRIC: { - keyId = Constants.key.symmetric; + mSubKeyId = Constants.key.symmetric; break; } case PASSPHRASE: { - keyId = requiredInput.getSubKeyId(); + + // handle empty passphrases by directly returning an empty crypto input parcel + try { + CanonicalizedSecretKeyRing pubRing = + new ProviderHelper(this).getCanonicalizedSecretKeyRing( + requiredInput.getMasterKeyId()); + // use empty passphrase for empty passphrase + if (pubRing.getSecretKey(requiredInput.getSubKeyId()).getSecretKeyType() == + SecretKeyType.PASSPHRASE_EMPTY) { + // also return passphrase back to activity + Intent returnIntent = new Intent(); + mCryptoInputParcel.mPassphrase = new Passphrase(""); + returnIntent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel); + setResult(RESULT_OK, returnIntent); + finish(); + return; + } + } catch (NotFoundException e) { + Log.e(Constants.TAG, "Key not found?!", e); + setResult(RESULT_CANCELED); + finish(); + return; + } + + mSubKeyId = requiredInput.getSubKeyId(); break; } default: { @@ -112,64 +151,35 @@ public class PassphraseDialogActivity extends FragmentActivity { } } - Intent serviceIntent = getIntent().getParcelableExtra(EXTRA_SERVICE_INTENT); - - show(this, keyId, serviceIntent); } @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case REQUEST_CODE_ENTER_PATTERN: { - /* - * NOTE that there are 4 possible result codes!!! - */ - switch (resultCode) { - case RESULT_OK: - // The user passed - break; - case RESULT_CANCELED: - // The user cancelled the task - break; -// case LockPatternActivity.RESULT_FAILED: -// // The user failed to enter the pattern -// break; -// case LockPatternActivity.RESULT_FORGOT_PATTERN: -// // The user forgot the pattern and invoked your recovery Activity. -// break; - } + protected void onResumeFragments() { + super.onResumeFragments(); - /* - * In any case, there's always a key EXTRA_RETRY_COUNT, which holds - * the number of tries that the user did. - */ -// int retryCount = data.getIntExtra( -// LockPatternActivity.EXTRA_RETRY_COUNT, 0); + /* Show passphrase dialog to cache a new passphrase the user enters for using it later for + * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks + * for a symmetric passphrase + */ - break; - } - } + Intent serviceIntent = getIntent().getParcelableExtra(EXTRA_SERVICE_INTENT); + + PassphraseDialogFragment frag = new PassphraseDialogFragment(); + Bundle args = new Bundle(); + args.putLong(EXTRA_SUBKEY_ID, mSubKeyId); + args.putParcelable(EXTRA_SERVICE_INTENT, serviceIntent); + frag.setArguments(args); + frag.show(getSupportFragmentManager(), "passphraseDialog"); } - /** - * Shows passphrase dialog to cache a new passphrase the user enters for using it later for - * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks - * for a symmetric passphrase - */ - public static void show(final FragmentActivity context, final long keyId, final Intent serviceIntent) { - DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { - public void run() { - // do NOT check if the key even needs a passphrase. that's not our job here. - PassphraseDialogFragment frag = new PassphraseDialogFragment(); - Bundle args = new Bundle(); - args.putLong(EXTRA_SUBKEY_ID, keyId); - args.putParcelable(EXTRA_SERVICE_INTENT, serviceIntent); - - frag.setArguments(args); - - frag.show(context.getSupportFragmentManager(), "passphraseDialog"); - } - }); + @Override + protected void onPause() { + super.onPause(); + + DialogFragment dialog = (DialogFragment) getSupportFragmentManager().findFragmentByTag("passphraseDialog"); + if (dialog != null) { + dialog.dismiss(); + } } public static class PassphraseDialogFragment extends DialogFragment implements TextView.OnEditorActionListener { @@ -183,17 +193,12 @@ public class PassphraseDialogActivity extends FragmentActivity { private Intent mServiceIntent; - /** - * Creates dialog - */ + @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final Activity activity = getActivity(); - // if the dialog is displayed from the application class, design is missing - // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay - ContextThemeWrapper theme = new ContextThemeWrapper(activity, - R.style.Theme_AppCompat_Light_Dialog); + ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity); mSubKeyId = getArguments().getLong(EXTRA_SUBKEY_ID); mServiceIntent = getArguments().getParcelable(EXTRA_SERVICE_INTENT); @@ -246,12 +251,7 @@ public class PassphraseDialogActivity extends FragmentActivity { userId = null; } - /* Get key type for message */ - // find a master key id for our key - long masterKeyId = new ProviderHelper(activity).getMasterKeyId(mSubKeyId); - CachedPublicKeyRing keyRing = new ProviderHelper(activity).getCachedPublicKeyRing(masterKeyId); - // get the type of key (from the database) - keyType = keyRing.getSecretKeyType(mSubKeyId); + keyType = mSecretRing.getSecretKey(mSubKeyId).getSecretKeyType(); switch (keyType) { case PASSPHRASE: message = getString(R.string.passphrase_for, userId); @@ -284,51 +284,42 @@ public class PassphraseDialogActivity extends FragmentActivity { mPassphraseText.setText(message); - if (keyType == CanonicalizedSecretKey.SecretKeyType.PATTERN) { - // start pattern dialog and show progress circle here... -// Intent patternActivity = new Intent(getActivity(), LockPatternActivity.class); -// patternActivity.putExtra(LockPatternActivity.EXTRA_PATTERN, "123"); -// startActivityForResult(patternActivity, REQUEST_CODE_ENTER_PATTERN); - mInput.setVisibility(View.INVISIBLE); - mProgress.setVisibility(View.VISIBLE); - } else { - // Hack to open keyboard. - // This is the only method that I found to work across all Android versions - // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/ - // Notes: * onCreateView can't be used because we want to add buttons to the dialog - // * opening in onActivityCreated does not work on Android 4.4 - mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - mPassphraseEditText.post(new Runnable() { - @Override - public void run() { - if (getActivity() == null || mPassphraseEditText == null) { - return; - } - InputMethodManager imm = (InputMethodManager) getActivity() - .getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT); + // Hack to open keyboard. + // This is the only method that I found to work across all Android versions + // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/ + // Notes: * onCreateView can't be used because we want to add buttons to the dialog + // * opening in onActivityCreated does not work on Android 4.4 + mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + mPassphraseEditText.post(new Runnable() { + @Override + public void run() { + if (getActivity() == null || mPassphraseEditText == null) { + return; } - }); - } - }); - mPassphraseEditText.requestFocus(); - - mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE); - mPassphraseEditText.setOnEditorActionListener(this); - - if (keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubiKeyPin()) { - mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_PASSWORD); - } else if (keyType == CanonicalizedSecretKey.SecretKeyType.PIN) { - mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_TEXT_VARIATION_PASSWORD); - } else { - mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + InputMethodManager imm = (InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT); + } + }); } + }); + mPassphraseEditText.requestFocus(); + + mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE); + mPassphraseEditText.setOnEditorActionListener(this); + if ((keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubiKeyPin()) + || keyType == CanonicalizedSecretKey.SecretKeyType.PIN) { + mPassphraseEditText.setInputType(InputType.TYPE_CLASS_NUMBER); mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance()); + } else { + mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); } + mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance()); + AlertDialog dialog = alert.create(); dialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.btn_unlock), (DialogInterface.OnClickListener) null); @@ -347,11 +338,16 @@ public class PassphraseDialogActivity extends FragmentActivity { public void onClick(View v) { final Passphrase passphrase = new Passphrase(mPassphraseEditText); + CryptoInputParcel cryptoInputParcel = + ((PassphraseDialogActivity) getActivity()).mCryptoInputParcel; + // Early breakout if we are dealing with a symmetric key if (mSecretRing == null) { - PassphraseCacheService.addCachedPassphrase(getActivity(), - Constants.key.symmetric, Constants.key.symmetric, passphrase, - getString(R.string.passp_cache_notif_pwd)); + if (cryptoInputParcel.mCachePassphrase) { + PassphraseCacheService.addCachedPassphrase(getActivity(), + Constants.key.symmetric, Constants.key.symmetric, passphrase, + getString(R.string.passp_cache_notif_pwd)); + } finishCaching(passphrase); return; @@ -404,15 +400,24 @@ public class PassphraseDialogActivity extends FragmentActivity { return; } - // cache the new passphrase - Log.d(Constants.TAG, "Everything okay! Caching entered passphrase"); + // cache the new passphrase as specified in CryptoInputParcel + Log.d(Constants.TAG, "Everything okay!"); - try { - PassphraseCacheService.addCachedPassphrase(getActivity(), - mSecretRing.getMasterKeyId(), mSubKeyId, passphrase, - mSecretRing.getPrimaryUserIdWithFallback()); - } catch (PgpKeyNotFoundException e) { - Log.e(Constants.TAG, "adding of a passphrase failed", e); + CryptoInputParcel cryptoInputParcel + = ((PassphraseDialogActivity) getActivity()).mCryptoInputParcel; + + if (cryptoInputParcel.mCachePassphrase) { + Log.d(Constants.TAG, "Caching entered passphrase"); + + try { + PassphraseCacheService.addCachedPassphrase(getActivity(), + mSecretRing.getMasterKeyId(), mSubKeyId, passphrase, + mSecretRing.getPrimaryUserIdWithFallback()); + } catch (PgpKeyNotFoundException e) { + Log.e(Constants.TAG, "adding of a passphrase failed", e); + } + } else { + Log.d(Constants.TAG, "Not caching entered passphrase!"); } finishCaching(passphrase); @@ -428,9 +433,12 @@ public class PassphraseDialogActivity extends FragmentActivity { return; } - CryptoInputParcel inputParcel = new CryptoInputParcel(null, passphrase); + CryptoInputParcel inputParcel = + ((PassphraseDialogActivity) getActivity()).mCryptoInputParcel; + inputParcel.mPassphrase = passphrase; if (mServiceIntent != null) { - CryptoInputParcelCacheService.addCryptoInputParcel(getActivity(), mServiceIntent, inputParcel); + CryptoInputParcelCacheService.addCryptoInputParcel(getActivity(), mServiceIntent, + inputParcel); getActivity().setResult(RESULT_OK, mServiceIntent); } else { // also return passphrase back to activity @@ -449,20 +457,16 @@ public class PassphraseDialogActivity extends FragmentActivity { // note we need no synchronization here, this variable is only accessed in the ui thread mIsCancelled = true; + + getActivity().setResult(RESULT_CANCELED); + getActivity().finish(); } @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); - if (getActivity() == null) { - return; - } - hideKeyboard(); - - getActivity().setResult(RESULT_CANCELED); - getActivity().finish(); } private void hideKeyboard() { @@ -476,11 +480,9 @@ public class PassphraseDialogActivity extends FragmentActivity { inputManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } - /** - * Associate the "done" button on the soft keyboard with the okay button in the view - */ @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + // Associate the "done" button on the soft keyboard with the okay button in the view if (EditorInfo.IME_ACTION_DONE == actionId) { AlertDialog dialog = ((AlertDialog) getDialog()); Button bt = dialog.getButton(AlertDialog.BUTTON_POSITIVE); |