aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2015-09-28 18:27:29 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2015-09-28 18:27:29 +0200
commit0e613aff2e617ca12c1b2e1032b21334c2ca674a (patch)
tree4e16d6a086cbe12875454fba520241ac84e277c7 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui
parentb65a23f2d46f905160b47e044de12cf9177dfd51 (diff)
parent3df9bea4554c0edddce57aa6a2e32cfe5250ed72 (diff)
downloadopen-keychain-0e613aff2e617ca12c1b2e1032b21334c2ca674a.tar.gz
open-keychain-0e613aff2e617ca12c1b2e1032b21334c2ca674a.tar.bz2
open-keychain-0e613aff2e617ca12c1b2e1032b21334c2ca674a.zip
Merge remote-tracking branch 'origin/master' into encrypted-export
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java42
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java45
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java22
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java35
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java80
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java234
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java126
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;
- }
-}