diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui')
11 files changed, 179 insertions, 434 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java index 552fa34c0..2409523bc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java @@ -33,12 +33,14 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.experimental.SentenceConfirm; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.ui.util.ExperimentalWordConfirm; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; +import java.io.IOException; + public class CertifyFingerprintFragment extends LoaderFragment implements LoaderManager.LoaderCallbacks<Cursor> { @@ -46,24 +48,26 @@ public class CertifyFingerprintFragment extends LoaderFragment implements static final int REQUEST_CERTIFY = 1; public static final String ARG_DATA_URI = "uri"; - public static final String ARG_ENABLE_WORD_CONFIRM = "enable_word_confirm"; + public static final String ARG_ENABLE_PHRASES_CONFIRM = "enable_word_confirm"; + private TextView mActionYes; private TextView mFingerprint; private TextView mIntro; + private TextView mHeader; private static final int LOADER_ID_UNIFIED = 0; private Uri mDataUri; - private boolean mEnableWordConfirm; + private boolean mEnablePhrasesConfirm; /** * Creates new instance of this fragment */ - public static CertifyFingerprintFragment newInstance(Uri dataUri, boolean enableWordConfirm) { + public static CertifyFingerprintFragment newInstance(Uri dataUri, boolean enablePhrasesConfirm) { CertifyFingerprintFragment frag = new CertifyFingerprintFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_DATA_URI, dataUri); - args.putBoolean(ARG_ENABLE_WORD_CONFIRM, enableWordConfirm); + args.putBoolean(ARG_ENABLE_PHRASES_CONFIRM, enablePhrasesConfirm); frag.setArguments(args); @@ -75,11 +79,12 @@ public class CertifyFingerprintFragment extends LoaderFragment implements View root = super.onCreateView(inflater, superContainer, savedInstanceState); View view = inflater.inflate(R.layout.certify_fingerprint_fragment, getContainer()); - View actionNo = view.findViewById(R.id.certify_fingerprint_button_no); - View actionYes = view.findViewById(R.id.certify_fingerprint_button_yes); + TextView actionNo = (TextView) view.findViewById(R.id.certify_fingerprint_button_no); + mActionYes = (TextView) view.findViewById(R.id.certify_fingerprint_button_yes); mFingerprint = (TextView) view.findViewById(R.id.certify_fingerprint_fingerprint); mIntro = (TextView) view.findViewById(R.id.certify_fingerprint_intro); + mHeader = (TextView) view.findViewById(R.id.certify_fingerprint_fingerprint_header); actionNo.setOnClickListener(new View.OnClickListener() { @Override @@ -87,7 +92,7 @@ public class CertifyFingerprintFragment extends LoaderFragment implements getActivity().finish(); } }); - actionYes.setOnClickListener(new View.OnClickListener() { + mActionYes.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { certify(mDataUri); @@ -107,10 +112,12 @@ public class CertifyFingerprintFragment extends LoaderFragment implements getActivity().finish(); return; } - mEnableWordConfirm = getArguments().getBoolean(ARG_ENABLE_WORD_CONFIRM); + mEnablePhrasesConfirm = getArguments().getBoolean(ARG_ENABLE_PHRASES_CONFIRM); - if (mEnableWordConfirm) { - mIntro.setText(R.string.certify_fingerprint_text_words); + if (mEnablePhrasesConfirm) { + mIntro.setText(R.string.certify_fingerprint_text_phrases); + mHeader.setText(R.string.section_phrases); + mActionYes.setText(R.string.btn_match_phrases); } loadData(dataUri); @@ -160,7 +167,7 @@ public class CertifyFingerprintFragment extends LoaderFragment implements if (data.moveToFirst()) { byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT); - if (mEnableWordConfirm) { + if (mEnablePhrasesConfirm) { displayWordConfirm(fingerprintBlob); } else { displayHexConfirm(fingerprintBlob); @@ -180,9 +187,16 @@ public class CertifyFingerprintFragment extends LoaderFragment implements } private void displayWordConfirm(byte[] fingerprintBlob) { - String fingerprint = ExperimentalWordConfirm.getWords(getActivity(), fingerprintBlob); +// String fingerprint = ExperimentalWordConfirm.getWords(getActivity(), fingerprintBlob); + + String fingerprint; + try { + fingerprint = new SentenceConfirm(getActivity()).fromBytes(fingerprintBlob, 16); + } catch (IOException ioe) { + fingerprint = "-"; + } - mFingerprint.setTextSize(24); + mFingerprint.setTextSize(18); mFingerprint.setTypeface(Typeface.DEFAULT, Typeface.BOLD); mFingerprint.setText(fingerprint); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java index 5eb9963f5..4e9a6f17d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -168,7 +168,7 @@ public class DecryptActivity extends BaseActivity { return; } - uris.add(intent.getData()); + uris.add(uri); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java index dcba595e9..a0650f8b1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -257,7 +257,6 @@ public class DecryptListFragment } OpenPgpMetadata metadata = result.mMetadata.get(index); - Uri saveUri = Uri.fromFile(activity.getExternalFilesDir(metadata.getMimeType())); mCurrentSaveFileUri = result.getOutputUris().get(index); String filename = metadata.getFilename(); @@ -266,8 +265,8 @@ public class DecryptListFragment filename = "decrypted" + (ext != null ? "."+ext : ""); } - FileHelper.saveDocument(this, filename, saveUri, metadata.getMimeType(), - R.string.title_decrypt_to_file, R.string.specify_file_to_decrypt_to, REQUEST_CODE_OUTPUT); + FileHelper.saveDocument(this, filename, metadata.getMimeType(), + REQUEST_CODE_OUTPUT); } private void saveFile(Uri saveUri) { @@ -376,10 +375,12 @@ public class DecryptListFragment // noinspection deprecation, this should be called from Context, but not available in minSdk icon = getResources().getDrawable(R.drawable.ic_chat_black_24dp); } else if (ClipDescription.compareMimeTypes(type, "image/*")) { - int px = FormattingUtils.dpToPx(context, 48); + int px = FormattingUtils.dpToPx(context, 32); Bitmap bitmap = FileHelper.getThumbnail(context, outputUri, new Point(px, px)); icon = new BitmapDrawable(context.getResources(), bitmap); - } else { + } + + if (icon == null) { final Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(outputUri, type); @@ -445,6 +446,7 @@ public class DecryptListFragment displayWithViewIntent(result, index, true, true); break; case R.id.decrypt_save: + // only inside the menu xml for Android >= 4.4 saveFileDialog(result, index); break; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index 8572a5712..0e357cfcd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.ui; +import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Date; @@ -224,9 +225,8 @@ public class EncryptFilesFragment String targetName = (mEncryptFilenames ? "1" : FileHelper.getFilename(getActivity(), model.inputUri)) + (mUseArmor ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN); - Uri inputUri = model.inputUri; - FileHelper.saveDocument(this, targetName, inputUri, - R.string.title_encrypt_to_file, R.string.specify_file_to_encrypt_to, REQUEST_CODE_OUTPUT); + FileHelper.saveDocument(this, targetName, + REQUEST_CODE_OUTPUT); } public void addFile(Intent data) { @@ -308,6 +308,17 @@ public class EncryptFilesFragment return true; } + @Override + public void onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + + // Show save only on Android >= 4.4 (Document Provider) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + MenuItem save = menu.findItem(R.id.encrypt_save); + save.setVisible(false); + } + } + public void toggleUseArmor(MenuItem item, final boolean useArmor) { mUseArmor = useArmor; @@ -441,9 +452,29 @@ public class EncryptFilesFragment } - // prepares mOutputUris, either directly and returns false, or indirectly - // which returns true and will call cryptoOperation after mOutputUris has - // been set at a later point. + /** + * Checks that the input uris are not linked to our own internal storage. + * This prevents the encryption of our own database (-> export of whole database) + */ + private void securityCheckInternalStorage() { + for (FilesAdapter.ViewModel model : mFilesAdapter.mDataset) { + File fileInput = new File(model.inputUri.getPath()); + try { + // the canonical path of the file must not start with /data/data/org.sufficientlysecure.keychain/ + if (fileInput.getCanonicalPath().startsWith(getActivity().getApplicationInfo().dataDir)) { + throw new RuntimeException("Encrypting OpenKeychain's private files is not allowed!"); + } + } catch (IOException e) { + Log.e(Constants.TAG, "Getting canonical path failed!", e); + } + } + } + + /** + * Prepares mOutputUris, either directly and returns false, or indirectly + * which returns true and will call cryptoOperation after mOutputUris has + * been set at a later point. + */ private boolean prepareOutputStreams() { switch (mAfterEncryptAction) { @@ -519,6 +550,8 @@ public class EncryptFilesFragment } + securityCheckInternalStorage(); + return actionsParcel; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java index d8edbe4f8..5a8ab36bc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java @@ -155,7 +155,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC data.getBoolean(AddEditKeyserverDialogFragment.MESSAGE_VERIFIED); if (verified) { Notify.create(getActivity(), - R.string.add_keyserver_verified, Notify.Style.OK).show(); + R.string.add_keyserver_connection_verified, Notify.Style.OK).show(); } else { Notify.create(getActivity(), R.string.add_keyserver_without_verification, @@ -177,26 +177,6 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC } break; } - case AddEditKeyserverDialogFragment.MESSAGE_VERIFICATION_FAILED: { - AddEditKeyserverDialogFragment.FailureReason failureReason = - (AddEditKeyserverDialogFragment.FailureReason) data.getSerializable( - AddEditKeyserverDialogFragment.MESSAGE_FAILURE_REASON); - switch (failureReason) { - case CONNECTION_FAILED: { - Notify.create(getActivity(), - R.string.add_keyserver_connection_failed, - Notify.Style.ERROR).show(); - break; - } - case INVALID_URL: { - Notify.create(getActivity(), - R.string.add_keyserver_invalid_url, - Notify.Style.ERROR).show(); - break; - } - } - break; - } } } }; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java index 4a46896bc..6331aa384 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java @@ -107,7 +107,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements View vFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share); View vFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard); View vKeyShareButton = view.findViewById(R.id.view_key_action_key_share); - View vKeySafeButton = view.findViewById(R.id.view_key_action_key_export); + View vKeySaveButton = view.findViewById(R.id.view_key_action_key_export); View vKeyNfcButton = view.findViewById(R.id.view_key_action_key_nfc); View vKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard); ImageButton vKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger); @@ -133,7 +133,11 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements share(false, false); } }); - vKeySafeButton.setOnClickListener(new View.OnClickListener() { + // Show save only on Android >= 4.4 (Document Provider) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + vKeySaveButton.setVisibility(View.GONE); + } + vKeySaveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { exportToFile(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java index 266633061..11c032517 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java @@ -40,6 +40,7 @@ import android.widget.TableRow; import android.widget.TextView; import com.textuality.keybase.lib.KeybaseException; +import com.textuality.keybase.lib.KeybaseQuery; import com.textuality.keybase.lib.Proof; import com.textuality.keybase.lib.User; @@ -51,6 +52,7 @@ import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.OkHttpKeybaseClient; import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; @@ -224,8 +226,9 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements } } - // look for evidence from keybase in the background, make tabular version of result - // + /** + * look for evidence from keybase in the background, make tabular version of result + */ private class DescribeKey extends AsyncTask<String, Void, ResultPage> { ParcelableProxy mParcelableProxy; @@ -240,7 +243,9 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements final ArrayList<CharSequence> proofList = new ArrayList<CharSequence>(); final Hashtable<Integer, ArrayList<Proof>> proofs = new Hashtable<Integer, ArrayList<Proof>>(); try { - User keybaseUser = User.findByFingerprint(fingerprint, mParcelableProxy.getProxy()); + KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient()); + keybaseQuery.setProxy(mParcelableProxy.getProxy()); + User keybaseUser = User.findByFingerprint(keybaseQuery, fingerprint); for (Proof proof : keybaseUser.getProofs()) { Integer proofType = proof.getType(); appendIfOK(proofs, proofType, proof); @@ -266,7 +271,12 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements } catch (KeybaseException ignored) { } - return new ResultPage(getString(R.string.key_trust_results_prefix), proofList); + String prefix = ""; + if (isAdded()) { + prefix = getString(R.string.key_trust_results_prefix); + } + + return new ResultPage(prefix, proofList); } private SpannableStringBuilder formatSpannableString(SpannableStringBuilder proofLinks, String proofType) { @@ -291,7 +301,10 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements if (haveProofFor(proof.getType())) { ssb.append("\u00a0["); startAt = ssb.length(); - String verify = getString(R.string.keybase_verify); + String verify = ""; + if (isAdded()) { + verify = getString(R.string.keybase_verify); + } ssb.append(verify); ClickableSpan clicker = new ClickableSpan() { @Override @@ -308,6 +321,11 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements @Override protected void onPostExecute(ResultPage result) { super.onPostExecute(result); + // stop if fragment is no longer added to an activity + if(!isAdded()) { + return; + } + if (result.mProofs.isEmpty()) { result.mHeader = getActivity().getString(R.string.key_trust_no_cloud_evidence); } @@ -356,7 +374,12 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements default: stringIndex = R.string.keybase_narrative_unknown; } - return getActivity().getString(stringIndex); + + if (isAdded()) { + return getString(stringIndex); + } else { + return ""; + } } private void appendIfOK(Hashtable<Integer, ArrayList<Proof>> table, Integer proofType, Proof proof) throws KeybaseException { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java index 47bc7dfda..3d96f3c6d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java @@ -24,6 +24,7 @@ import java.net.URI; import java.net.URISyntaxException; import android.app.Activity; +import android.support.design.widget.TextInputLayout; import android.support.v7.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; @@ -44,6 +45,7 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.EditText; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; @@ -54,6 +56,7 @@ import com.squareup.okhttp.Request; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; +import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.TlsHelper; @@ -68,11 +71,9 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On private static final String ARG_KEYSERVER = "arg_keyserver"; public static final int MESSAGE_OKAY = 1; - public static final int MESSAGE_VERIFICATION_FAILED = 2; public static final String MESSAGE_KEYSERVER = "new_keyserver"; public static final String MESSAGE_VERIFIED = "verified"; - public static final String MESSAGE_FAILURE_REASON = "failure_reason"; public static final String MESSAGE_KEYSERVER_DELETED = "keyserver_deleted"; public static final String MESSAGE_DIALOG_ACTION = "message_dialog_action"; public static final String MESSAGE_EDIT_POSITION = "keyserver_edited_position"; @@ -82,7 +83,9 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On private int mPosition; private EditText mKeyserverEditText; + private TextInputLayout mKeyserverEditTextLayout; private CheckBox mVerifyKeyserverCheckBox; + private CheckBox mOnlyTrustedKeyserverCheckBox; public enum DialogAction { ADD, @@ -91,7 +94,8 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On public enum FailureReason { INVALID_URL, - CONNECTION_FAILED + CONNECTION_FAILED, + NO_PINNED_CERTIFICATE } public static AddEditKeyserverDialogFragment newInstance(Messenger messenger, @@ -126,7 +130,15 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On alert.setView(view); mKeyserverEditText = (EditText) view.findViewById(R.id.keyserver_url_edit_text); - mVerifyKeyserverCheckBox = (CheckBox) view.findViewById(R.id.verify_keyserver_checkbox); + mKeyserverEditTextLayout = (TextInputLayout) view.findViewById(R.id.keyserver_url_edit_text_layout); + mVerifyKeyserverCheckBox = (CheckBox) view.findViewById(R.id.verify_connection_checkbox); + mOnlyTrustedKeyserverCheckBox = (CheckBox) view.findViewById(R.id.only_trusted_keyserver_checkbox); + mVerifyKeyserverCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + mOnlyTrustedKeyserverCheckBox.setEnabled(isChecked); + } + }); switch (mDialogAction) { case ADD: { @@ -212,6 +224,8 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On positiveButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + mKeyserverEditTextLayout.setErrorEnabled(false); + // behaviour same for edit and add final String keyserverUrl = mKeyserverEditText.getText().toString(); if (mVerifyKeyserverCheckBox.isChecked()) { @@ -220,13 +234,20 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() { @Override public void onOrbotStarted() { - verifyConnection(keyserverUrl, - proxyPrefs.parcelableProxy.getProxy()); + verifyConnection( + keyserverUrl, + proxyPrefs.parcelableProxy.getProxy(), + mOnlyTrustedKeyserverCheckBox.isChecked() + ); } @Override public void onNeutralButton() { - verifyConnection(keyserverUrl, null); + verifyConnection( + keyserverUrl, + null, + mOnlyTrustedKeyserverCheckBox.isChecked() + ); } @Override @@ -236,7 +257,11 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On }; if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) { - verifyConnection(keyserverUrl, proxyPrefs.parcelableProxy.getProxy()); + verifyConnection( + keyserverUrl, + proxyPrefs.parcelableProxy.getProxy(), + mOnlyTrustedKeyserverCheckBox.isChecked() + ); } } else { dismiss(); @@ -272,14 +297,28 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On sendMessageToHandler(MESSAGE_OKAY, data); } - public void verificationFailed(FailureReason reason) { - Bundle data = new Bundle(); - data.putSerializable(MESSAGE_FAILURE_REASON, reason); + public void verificationFailed(FailureReason failureReason) { + switch (failureReason) { + case CONNECTION_FAILED: { + mKeyserverEditTextLayout.setError( + getString(R.string.add_keyserver_connection_failed)); + break; + } + case INVALID_URL: { + mKeyserverEditTextLayout.setError( + getString(R.string.add_keyserver_invalid_url)); + break; + } + case NO_PINNED_CERTIFICATE: { + mKeyserverEditTextLayout.setError( + getString(R.string.add_keyserver_keyserver_not_trusted)); + break; + } + } - sendMessageToHandler(MESSAGE_VERIFICATION_FAILED, data); } - public void verifyConnection(String keyserver, final Proxy proxy) { + public void verifyConnection(String keyserver, final Proxy proxy, final boolean onlyTrustedKeyserver) { new AsyncTask<String, Void, FailureReason>() { ProgressDialog mProgressDialog; @@ -288,7 +327,7 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On @Override protected void onPreExecute() { mProgressDialog = new ProgressDialog(getActivity()); - mProgressDialog.setMessage(getString(R.string.progress_verifying_keyserver_url)); + mProgressDialog.setMessage(getString(R.string.progress_verifying_keyserver_connection)); mProgressDialog.setCancelable(false); mProgressDialog.show(); } @@ -316,7 +355,18 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On Log.d("Converted URL", newKeyserver.toString()); OkHttpClient client = HkpKeyserver.getClient(newKeyserver.toURL(), proxy); - TlsHelper.pinCertificateIfNecessary(client, newKeyserver.toURL()); + + // don't follow any redirects + client.setFollowRedirects(false); + client.setFollowSslRedirects(false); + + if (onlyTrustedKeyserver + && !TlsHelper.usePinnedCertificateIfAvailable(client, newKeyserver.toURL())) { + Log.w(Constants.TAG, "No pinned certificate for this host in OpenKeychain's assets."); + reason = FailureReason.NO_PINNED_CERTIFICATE; + return reason; + } + client.newCall(new Request.Builder().url(newKeyserver.toURL()).build()).execute(); } catch (TlsHelper.TlsHelperException e) { reason = FailureReason.CONNECTION_FAILED; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java deleted file mode 100644 index 84774ae5e..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package org.sufficientlysecure.keychain.ui.dialog; - -import android.app.Activity; -import android.app.Dialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.support.v4.app.DialogFragment; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.ImageButton; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.util.FileHelper; -import org.sufficientlysecure.keychain.util.Log; - -import java.io.File; - -/** - * This is a file chooser dialog no longer used with KitKat - */ -public class FileDialogFragment extends DialogFragment { - private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_TITLE = "title"; - private static final String ARG_MESSAGE = "message"; - private static final String ARG_DEFAULT_FILE = "default_file"; - private static final String ARG_CHECKBOX_TEXT = "checkbox_text"; - - public static final int MESSAGE_OKAY = 1; - - public static final String MESSAGE_DATA_FILE = "file"; - public static final String MESSAGE_DATA_CHECKED = "checked"; - - private Messenger mMessenger; - - private EditText mFilename; - private ImageButton mBrowse; - private CheckBox mCheckBox; - private TextView mMessageTextView; - - private File mFile; - - private static final int REQUEST_CODE = 0x00007004; - - /** - * Creates new instance of this file dialog fragment - */ - public static FileDialogFragment newInstance(Messenger messenger, String title, String message, - File defaultFile, String checkboxText) { - FileDialogFragment frag = new FileDialogFragment(); - Bundle args = new Bundle(); - args.putParcelable(ARG_MESSENGER, messenger); - - args.putString(ARG_TITLE, title); - args.putString(ARG_MESSAGE, message); - args.putString(ARG_DEFAULT_FILE, defaultFile.getAbsolutePath()); - args.putString(ARG_CHECKBOX_TEXT, checkboxText); - - frag.setArguments(args); - - return frag; - } - - /** - * Creates dialog - */ - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final Activity activity = getActivity(); - - mMessenger = getArguments().getParcelable(ARG_MESSENGER); - - String title = getArguments().getString(ARG_TITLE); - String message = getArguments().getString(ARG_MESSAGE); - mFile = new File(getArguments().getString(ARG_DEFAULT_FILE)); - if (!mFile.isAbsolute()) { - // We use OK dir by default - mFile = new File(Constants.Path.APP_DIR.getAbsolutePath(), mFile.getName()); - } - String checkboxText = getArguments().getString(ARG_CHECKBOX_TEXT); - - LayoutInflater inflater = (LayoutInflater) activity - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); - alert.setTitle(title); - - View view = inflater.inflate(R.layout.file_dialog, null); - - mMessageTextView = (TextView) view.findViewById(R.id.message); - mMessageTextView.setText(message); - - mFilename = (EditText) view.findViewById(R.id.input); - mFilename.setText(mFile.getName()); - mBrowse = (ImageButton) view.findViewById(R.id.btn_browse); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - mBrowse.setVisibility(View.GONE); - } else { - mBrowse.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - // only .asc or .gpg files - // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc - // or gpg types! - FileHelper.saveDocumentKitKat( - FileDialogFragment.this, "*/*", mFile.getName(), REQUEST_CODE); - } - }); - } - - mCheckBox = (CheckBox) view.findViewById(R.id.checkbox); - if (checkboxText == null) { - mCheckBox.setEnabled(false); - mCheckBox.setVisibility(View.GONE); - } else { - mCheckBox.setEnabled(true); - mCheckBox.setVisibility(View.VISIBLE); - mCheckBox.setText(checkboxText); - mCheckBox.setChecked(true); - } - - alert.setView(view); - - alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - - String currentFilename = mFilename.getText().toString(); - if (currentFilename == null || currentFilename.isEmpty()) { - // No file is like pressing cancel, UI: maybe disable positive button in this case? - return; - } - - if (mFile == null || currentFilename.startsWith("/")) { - mFile = new File(currentFilename); - } else if (!mFile.getName().equals(currentFilename)) { - // We update our File object if user changed name! - mFile = new File(mFile.getParentFile(), currentFilename); - } - - boolean checked = mCheckBox.isEnabled() && mCheckBox.isChecked(); - - // return resulting data back to activity - Bundle data = new Bundle(); - data.putString(MESSAGE_DATA_FILE, mFile.getAbsolutePath()); - data.putBoolean(MESSAGE_DATA_CHECKED, checked); - - sendMessageToHandler(MESSAGE_OKAY, data); - } - }); - - alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, int id) { - dismiss(); - } - }); - return alert.show(); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode & 0xFFFF) { - case REQUEST_CODE: { - if (resultCode == Activity.RESULT_OK && data != null) { - File file = new File(data.getData().getPath()); - if (file.getParentFile().exists()) { - mFile = file; - mFilename.setText(mFile.getName()); - } else { - Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show(); - } - } - - break; - } - - default: - super.onActivityResult(requestCode, resultCode, data); - - break; - } - } - - /** - * Send message back to handler which is initialized in a activity - * - * @param what Message integer you want to send - */ - private void sendMessageToHandler(Integer what, Bundle data) { - Message msg = Message.obtain(); - msg.what = what; - if (data != null) { - msg.setData(data); - } - - try { - mMessenger.send(msg); - } catch (RemoteException e) { - Log.w(Constants.TAG, "Exception sending message, Is handler present?", e); - } catch (NullPointerException e) { - Log.w(Constants.TAG, "Messenger is null!", e); - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java index 22a201ba3..44323543f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java @@ -27,7 +27,6 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.EditText; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.linked.resources.GenericHttpsResource; @@ -35,7 +34,6 @@ import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.FileHelper; -import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.net.URI; @@ -134,9 +132,10 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen String targetName = "pgpkey.txt"; + // TODO: not supported on Android < 4.4 FileHelper.saveDocument(this, - targetName, Uri.fromFile(new File(Constants.Path.APP_DIR, targetName)), - "text/plain", R.string.title_decrypt_to_file, R.string.specify_file_to_decrypt_to, + targetName, + "text/plain", REQUEST_CODE_OUTPUT); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java deleted file mode 100644 index 43ccac24f..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package org.sufficientlysecure.keychain.ui.util; - -import android.content.Context; - -import org.spongycastle.util.Arrays; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.util.Log; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.BitSet; - -public class ExperimentalWordConfirm { - - public static String getWords(Context context, byte[] fingerprintBlob) { - ArrayList<String> words = new ArrayList<>(); - - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader( - context.getAssets().open("word_confirm_list.txt"), - "UTF-8" - )); - - String line = reader.readLine(); - while (line != null) { - words.add(line); - - line = reader.readLine(); - } - } catch (IOException e) { - throw new RuntimeException("IOException", e); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException ignored) { - } - } - } - - String fingerprint = ""; - - // NOTE: 160 bit SHA-1 truncated to 156 bit - byte[] fingerprintBlobTruncated = Arrays.copyOfRange(fingerprintBlob, 0, 156 / 8); - - // TODO: implement key stretching to minimize fp length? - - // BitSet bits = BitSet.valueOf(fingerprintBlob); // min API 19 and little endian! - BitSet bits = bitSetToByteArray(fingerprintBlobTruncated); - Log.d(Constants.TAG, "bits: " + bits.toString()); - - final int CHUNK_SIZE = 13; - final int LAST_CHUNK_INDEX = fingerprintBlobTruncated.length * 8 / CHUNK_SIZE; // 12 - Log.d(Constants.TAG, "LAST_CHUNK_INDEX: " + LAST_CHUNK_INDEX); - - int from = 0; - int to = CHUNK_SIZE; - for (int i = 0; i < (LAST_CHUNK_INDEX + 1); i++) { - Log.d(Constants.TAG, "from: " + from + " to: " + to); - - BitSet setIndex = bits.get(from, to); - int wordIndex = (int) bitSetToLong(setIndex); - // int wordIndex = (int) setIndex.toLongArray()[0]; // min API 19 - - fingerprint += words.get(wordIndex); - - if (i != LAST_CHUNK_INDEX) { - // line break every 3 words - if (to % (CHUNK_SIZE * 3) == 0) { - fingerprint += "\n"; - } else { - fingerprint += " "; - } - } - - from = to; - to += CHUNK_SIZE; - } - - return fingerprint; - } - - /** - * Returns a BitSet containing the values in bytes. - * BIG ENDIAN! - */ - private static BitSet bitSetToByteArray(byte[] bytes) { - int arrayLength = bytes.length * 8; - BitSet bits = new BitSet(); - - for (int i = 0; i < arrayLength; i++) { - if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) { - bits.set(i); - } - } - return bits; - } - - private static long bitSetToLong(BitSet bits) { - long value = 0L; - for (int i = 0; i < bits.length(); ++i) { - value += bits.get(i) ? (1L << i) : 0L; - } - return value; - } -} |