add override button to security problem dialog

This commit is contained in:
Vincent Breitmoser
2017-04-28 22:46:08 +02:00
parent 78c3e17d0a
commit 10112eeea8
8 changed files with 282 additions and 40 deletions

View File

@@ -331,7 +331,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
// Check for insecure encryption algorithms!
EncryptionAlgorithmProblem symmetricSecurityProblem =
PgpSecurityConstants.checkSecureSymmetricAlgorithm(esResult.symmetricEncryptionAlgo);
PgpSecurityConstants.checkSecureSymmetricAlgorithm(
esResult.symmetricEncryptionAlgo, esResult.sessionKey);
if (symmetricSecurityProblem != null) {
log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
securityProblemBuilder.addSymmetricSecurityProblem(symmetricSecurityProblem);
@@ -542,7 +543,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
// Handle missing integrity protection like failed integrity protection!
// The MDC packet can be stripped by an attacker!
log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent);
securityProblemBuilder.addSymmetricSecurityProblem(new MissingMdc());
securityProblemBuilder.addSymmetricSecurityProblem(new MissingMdc(esResult.sessionKey));
decryptionResultBuilder.setInsecure(true);
}
}

View File

@@ -74,9 +74,9 @@ public class PgpSecurityConstants {
// CAMELLIA_256: not used widely
));
public static EncryptionAlgorithmProblem checkSecureSymmetricAlgorithm(int id) {
public static EncryptionAlgorithmProblem checkSecureSymmetricAlgorithm(int id, byte[] sessionKey) {
if (!sSymmetricAlgorithmsWhitelist.contains(id)) {
return new InsecureEncryptionAlgorithm(id);
return new InsecureEncryptionAlgorithm(sessionKey, id);
}
return null;
}

View File

@@ -18,10 +18,40 @@
package org.sufficientlysecure.keychain.pgp;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.bouncycastle.util.encoders.Base64;
public abstract class SecurityProblem implements Serializable {
String getIdentifier() {
if (!isIdentifiable()) {
return null;
}
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(this);
oos.close();
byte[] digest = MessageDigest.getInstance("SHA1").digest(out.toByteArray());
return Base64.toBase64String(digest);
} catch (NoSuchAlgorithmException | IOException e) {
throw new IllegalStateException(e);
}
}
public boolean isIdentifiable() {
return false;
}
public static abstract class KeySecurityProblem extends SecurityProblem {
public final long masterKeyId;
public final long subKeyId;
@@ -32,10 +62,25 @@ public abstract class SecurityProblem implements Serializable {
this.subKeyId = subKeyId;
this.algorithm = algorithm;
}
@Override
public boolean isIdentifiable() {
return true;
}
}
public static abstract class EncryptionAlgorithmProblem extends SecurityProblem {
@SuppressWarnings("unused") // used for identifying this specific problem
private final byte[] sessionKey;
private EncryptionAlgorithmProblem(byte[] sessionKey) {
this.sessionKey = sessionKey;
}
@Override
public boolean isIdentifiable() {
return sessionKey != null;
}
}
public static class InsecureBitStrength extends KeySecurityProblem {
@@ -73,12 +118,16 @@ public abstract class SecurityProblem implements Serializable {
public static class InsecureEncryptionAlgorithm extends EncryptionAlgorithmProblem {
public final int symmetricAlgorithm;
InsecureEncryptionAlgorithm(int symmetricAlgorithm) {
InsecureEncryptionAlgorithm(byte[] sessionKey, int symmetricAlgorithm) {
super(sessionKey);
this.symmetricAlgorithm = symmetricAlgorithm;
}
}
public static class MissingMdc extends EncryptionAlgorithmProblem {
MissingMdc(byte[] sessionKey) {
super(sessionKey);
}
}
}

View File

@@ -39,12 +39,14 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.ViewAnimator;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.remote.ui.SecurityProblemPresenter.RemoteSecurityProblemView;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
import org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator;
public class RemoteSecurityProblemDialogActivity extends FragmentActivity {
@@ -79,11 +81,17 @@ public class RemoteSecurityProblemDialogActivity extends FragmentActivity {
}
public static class RemoteRegisterDialogFragment extends DialogFragment {
public static final int SECONDARY_CHILD_NONE = 0;
public static final int SECONDARY_CHILD_RECOMMENDATION = 1;
public static final int SECONDARY_CHILD_OVERRIDE = 2;
public static final int SECONDARY_CHILD_OVERRIDE_OK = 3;
private SecurityProblemPresenter presenter;
private RemoteSecurityProblemView mvpView;
private Button buttonGotIt;
private Button buttonViewKey;
private Button buttonOverride;
private Button buttonOverrideUndo;
@NonNull
@Override
@@ -99,6 +107,8 @@ public class RemoteSecurityProblemDialogActivity extends FragmentActivity {
buttonGotIt = (Button) view.findViewById(R.id.button_allow);
buttonViewKey = (Button) view.findViewById(R.id.button_view_key);
buttonOverride = (Button) view.findViewById(R.id.button_override);
buttonOverrideUndo = (Button) view.findViewById(R.id.button_override_undo);
setupListenersForPresenter();
mvpView = createMvpView(view);
@@ -139,9 +149,13 @@ public class RemoteSecurityProblemDialogActivity extends FragmentActivity {
final ImageView iconClientApp = (ImageView) view.findViewById(R.id.icon_client_app);
final TextView explanationText = (TextView) insecureWarningLayout.findViewById(R.id.dialog_insecure_text);
final TextView recommendText = (TextView) insecureWarningLayout.findViewById(R.id.dialog_insecure_recommend_text);
final View recommendLayout = insecureWarningLayout.findViewById(R.id.dialog_insecure_recommend_layout);
final TextView overrideText = (TextView) insecureWarningLayout.findViewById(R.id.dialog_insecure_override_text);
final ToolableViewAnimator secondaryLayoutAnimator =
(ToolableViewAnimator) insecureWarningLayout.findViewById(R.id.dialog_insecure_secondary_layout);
return new RemoteSecurityProblemView() {
private boolean layoutInitialized = false;
@Override
public void finishAsCancelled() {
FragmentActivity activity = getActivity();
@@ -167,26 +181,30 @@ public class RemoteSecurityProblemDialogActivity extends FragmentActivity {
private void showGeneric(@StringRes int explanationStringRes) {
explanationText.setText(explanationStringRes);
recommendLayout.setVisibility(View.GONE);
secondaryLayoutAnimator.setDisplayedChild(SECONDARY_CHILD_NONE, layoutInitialized);
layoutInitialized = true;
}
private void showGeneric(String explanationString) {
explanationText.setText(explanationString);
recommendLayout.setVisibility(View.GONE);
secondaryLayoutAnimator.setDisplayedChild(SECONDARY_CHILD_NONE, layoutInitialized);
layoutInitialized = true;
}
private void showGenericWithRecommendation(
@StringRes int explanationStringRes, @StringRes int recommendationStringRes) {
explanationText.setText(explanationStringRes);
recommendText.setText(recommendationStringRes);
recommendLayout.setVisibility(View.VISIBLE);
secondaryLayoutAnimator.setDisplayedChild(SECONDARY_CHILD_RECOMMENDATION, layoutInitialized);
layoutInitialized = true;
}
private void showGenericWithRecommendation(
String explanationString, @StringRes int recommendationStringRes) {
explanationText.setText(explanationString);
recommendText.setText(recommendationStringRes);
recommendLayout.setVisibility(View.VISIBLE);
secondaryLayoutAnimator.setDisplayedChild(SECONDARY_CHILD_RECOMMENDATION, layoutInitialized);
layoutInitialized = true;
}
@Override
@@ -246,10 +264,33 @@ public class RemoteSecurityProblemDialogActivity extends FragmentActivity {
showGeneric(R.string.insecure_sign_unidentified);
}
@Override
public void showOverrideMessage(int countdown) {
secondaryLayoutAnimator.setDisplayedChild(SECONDARY_CHILD_OVERRIDE, true);
overrideText.setText(getString(R.string.dialog_insecure_override, countdown));
}
@Override
public void showOverrideOk() {
secondaryLayoutAnimator.setDisplayedChild(SECONDARY_CHILD_OVERRIDE_OK, true);
}
@Override
public void showViewKeyButton() {
buttonViewKey.setVisibility(View.VISIBLE);
}
@Override
public void showOverrideButton() {
buttonOverride.setVisibility(View.VISIBLE);
buttonOverrideUndo.setVisibility(View.GONE);
}
@Override
public void showOverrideUndoButton() {
buttonOverride.setVisibility(View.GONE);
buttonOverrideUndo.setVisibility(View.VISIBLE);
}
};
}
@@ -266,6 +307,18 @@ public class RemoteSecurityProblemDialogActivity extends FragmentActivity {
presenter.onClickViewKey();
}
});
buttonOverride.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
presenter.onClickOverride();
}
});
buttonOverrideUndo.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
presenter.onClickOverrideUndo();
}
});
}
}

View File

@@ -24,12 +24,18 @@ import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
class SecurityProblemPresenter {
private static final int OVERRIDE_REQUIRED_COUNT = 3;
private final Context context;
private final PackageManager packageManager;
private RemoteSecurityProblemView view;
private Long viewKeyMasterKeyId;
private int overrideCounter;
private String packageName;
private Serializable securityProblem;
SecurityProblemPresenter(Context context) {
@@ -42,17 +48,19 @@ class SecurityProblemPresenter {
}
void setupFromIntentData(String packageName, Serializable securityProblem) {
this.packageName = packageName;
this.securityProblem = securityProblem;
refreshSecurityProblemDisplay();
refreshPackageInfo();
}
private void refreshSecurityProblemDisplay() {
if (securityProblem instanceof DecryptVerifySecurityProblem) {
setupFromDecryptVerifySecurityProblem((DecryptVerifySecurityProblem) securityProblem);
} else {
throw new IllegalArgumentException("Unhandled security problem type!");
}
try {
setPackageInfo(packageName);
} catch (NameNotFoundException e) {
throw new IllegalStateException("Unable to find info of calling app!", e);
}
}
private void setupFromDecryptVerifySecurityProblem(DecryptVerifySecurityProblem securityProblem) {
@@ -71,6 +79,10 @@ class SecurityProblemPresenter {
viewKeyMasterKeyId = keySecurityProblem.masterKeyId;
view.showViewKeyButton();
if (keySecurityProblem.isIdentifiable()) {
view.showOverrideButton();
}
if (keySecurityProblem instanceof InsecureBitStrength) {
InsecureBitStrength problem = (InsecureBitStrength) keySecurityProblem;
view.showLayoutEncryptInsecureBitsize(problem.algorithm, problem.bitStrength);
@@ -116,14 +128,30 @@ class SecurityProblemPresenter {
view.showLayoutInsecureHashAlgorithm(signatureSecurityProblem.hashAlgorithm);
}
private void setPackageInfo(String packageName) throws NameNotFoundException {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(packageName, 0);
private void refreshPackageInfo() {
ApplicationInfo applicationInfo;
try {
applicationInfo = packageManager.getApplicationInfo(packageName, 0);
} catch (NameNotFoundException e) {
throw new IllegalStateException("Could not retrieve package info!");
}
Drawable appIcon = packageManager.getApplicationIcon(applicationInfo);
// CharSequence appName = packageManager.getApplicationLabel(applicationInfo);
view.setTitleClientIcon(appIcon);
}
private void incrementOverrideAndDisplayOrTrigger() {
int overrideCountLeft = OVERRIDE_REQUIRED_COUNT - overrideCounter;
if (overrideCountLeft > 0) {
overrideCounter++;
view.showOverrideMessage(overrideCountLeft);
} else {
view.showOverrideOk();
view.showOverrideUndoButton();
}
}
void onClickGotIt() {
view.finishAsCancelled();
}
@@ -134,6 +162,15 @@ class SecurityProblemPresenter {
context.startActivity(viewKeyIntent);
}
void onClickOverride() {
incrementOverrideAndDisplayOrTrigger();
}
void onClickOverrideUndo() {
overrideCounter = 0;
refreshSecurityProblemDisplay();
}
void onCancel() {
view.finishAsCancelled();
}
@@ -154,6 +191,11 @@ class SecurityProblemPresenter {
void showLayoutInsecureHashAlgorithm(int hashAlgorithm);
void showOverrideMessage(int countdown);
void showOverrideOk();
void showViewKeyButton();
void showOverrideButton();
void showOverrideUndoButton();
}
}

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:custom="http://schemas.android.com/apk/res-auto"
tools:showIn="@layout/remote_security_issue_dialog">
<TextView
@@ -21,32 +22,98 @@
tools:text="The key this message was sent by is using an outdated algorithm, and is no longer considered secure!\n\nThe algorithm in use is DSA 1024 bit, which has been considered insecure since 2010."
/>
<LinearLayout
<org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/dialog_insecure_recommend_layout">
android:id="@+id/dialog_insecure_secondary_layout"
android:inAnimation="@anim/fade_in"
android:outAnimation="@anim/fade_out"
android:measureAllChildren="true"
custom:initialView="3">
<TextView
<android.support.v4.widget.Space
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="?android:textAppearanceMedium"
android:text="@string/dialog_insecure_recommend_title"
android:id="@+id/dialog_title_2"
/>
android:layout_height="wrap_content" />
<TextView
android:id="@+id/dialog_insecure_recommend_text"
android:layout_width="wrap_content"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceSmall"
tools:text="The key in use is insecure, and cannot be updated. To communicate securely, the sender must create a new key!"
/>
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="?android:textAppearanceMedium"
android:text="@string/dialog_insecure_recommend_title"
android:id="@+id/dialog_title_2"
/>
<TextView
android:id="@+id/dialog_insecure_recommend_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceSmall"
tools:text="The key in use is insecure, and cannot be updated. To communicate securely, the sender must create a new key!"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="?android:textAppearanceMedium"
android:textColor="@color/android_red_light"
android:text="@string/dialog_insecure_override_title"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:id="@+id/dialog_insecure_override_text"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceSmall"
tools:text="@string/dialog_insecure_override"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:textAppearance="?android:textAppearanceMedium"
android:text="@string/dialog_insecure_override_ok_title"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="@string/dialog_insecure_override_ok"
/>
</LinearLayout>
</org.sufficientlysecure.keychain.ui.widget.ToolableViewAnimator>
</LinearLayout>
</merge>

View File

@@ -54,8 +54,6 @@
tools:ignore="UselessParent">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -85,7 +83,31 @@
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="View Key"
android:text="@string/dialog_insecure_button_override"
android:id="@+id/button_override"
android:visibility="gone"
tools:visibility="visible"
style="?buttonBarButtonStyle" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_insecure_button_undo"
android:id="@+id/button_override_undo"
android:visibility="gone"
style="?buttonBarButtonStyle" />
<android.support.v4.widget.Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_insecure_button_view_key"
android:id="@+id/button_view_key"
android:visibility="gone"
tools:visibility="visible"
@@ -94,7 +116,7 @@
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Got it"
android:text="@string/dialog_insecure_button_ok"
android:id="@+id/button_allow"
style="?buttonBarButtonStyle" />

View File

@@ -1869,5 +1869,13 @@
<string name="insecure_symmetric_algo">"This message was encrypted with an insecure algorithm."</string>
<string name="insecure_hash_algo">"This message was signed using an insecure algorithm."</string>
<string name="dialog_insecure_override">If you don\'t want to be warned about this security problem in the future, you can suppress this warning. To do so, press the Suppress button %d more times.</string>
<string name="dialog_insecure_override_title">Suppress this warning</string>
<string name="dialog_insecure_override_ok_title">Warning suppressed</string>
<string name="dialog_insecure_override_ok">The security warning (for this key/message) will not be shown again in the future.</string>
<string name="dialog_insecure_button_override">Suppress</string>
<string name="dialog_insecure_button_undo">Undo</string>
<string name="dialog_insecure_button_view_key">View Key</string>
<string name="dialog_insecure_button_ok">Got it</string>
</resources>