Big error screen if signature is invalid or key is revoked/expired, also fixes signature status for expired and revoked keys

This commit is contained in:
Dominik Schürmann 2014-10-08 18:31:31 +02:00
parent 2eb776594f
commit 0d6d4653b4
6 changed files with 188 additions and 96 deletions

View File

@ -25,6 +25,7 @@ import org.sufficientlysecure.keychain.util.IterableIterator;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date;
/** A generic wrapped PGPKeyRing object. /** A generic wrapped PGPKeyRing object.
* *
@ -76,6 +77,16 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
return getRing().getPublicKey().isRevoked(); return getRing().getPublicKey().isRevoked();
} }
public boolean isExpired() throws PgpGeneralException {
// Is the master key expired?
Date creationDate = getRing().getPublicKey().getCreationTime();
Date expiryDate = getRing().getPublicKey().getValidSeconds() > 0
? new Date(creationDate.getTime() + getRing().getPublicKey().getValidSeconds() * 1000) : null;
Date now = new Date();
return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
}
public boolean canCertify() throws PgpGeneralException { public boolean canCertify() throws PgpGeneralException {
return getRing().getPublicKey().isEncryptionKey(); return getRing().getPublicKey().isEncryptionKey();
} }

View File

@ -103,9 +103,14 @@ public class OpenPgpSignatureResultBuilder {
Log.d(Constants.TAG, "signingRing.getUnorderedUserIds(): " + signingRing.getUnorderedUserIds()); Log.d(Constants.TAG, "signingRing.getUnorderedUserIds(): " + signingRing.getUnorderedUserIds());
setUserIds(signingRing.getUnorderedUserIds()); setUserIds(signingRing.getUnorderedUserIds());
// from KEY // either master key is expired/revoked or this specific subkey is expired/revoked
setKeyExpired(signingKey.isExpired()); try {
setKeyRevoked(signingKey.isRevoked()); setKeyExpired(signingRing.isExpired() || signingKey.isExpired());
setKeyRevoked(signingRing.isRevoked() || signingKey.isRevoked());
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "shouldn't happen!");
setKeyRevoked(true);
}
} }
public OpenPgpSignatureResult build() { public OpenPgpSignatureResult build() {

View File

@ -112,9 +112,15 @@ public abstract class DecryptFragment extends Fragment {
startActivityForResult(intent, REQUEST_CODE_NFC_DECRYPT); startActivityForResult(intent, REQUEST_CODE_NFC_DECRYPT);
} }
protected void onResult(DecryptVerifyResult decryptVerifyResult) { /**
*
* @return returns false if signature is invalid, key is revoked or expired.
*/
protected boolean onResult(DecryptVerifyResult decryptVerifyResult) {
final OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult(); final OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult();
boolean valid = false;
mSignatureKeyId = 0; mSignatureKeyId = 0;
mResultLayout.setVisibility(View.VISIBLE); mResultLayout.setVisibility(View.VISIBLE);
if (signatureResult != null) { if (signatureResult != null) {
@ -147,14 +153,9 @@ public abstract class DecryptFragment extends Fragment {
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_VERIFIED); KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_VERIFIED);
setSignatureLayoutVisibility(View.VISIBLE); setSignatureLayoutVisibility(View.VISIBLE);
mSignatureAction.setText(R.string.decrypt_result_action_show); setShowAction(mSignatureKeyId);
mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_accounts, 0);
mSignatureLayout.setOnClickListener(new View.OnClickListener() { valid = true;
@Override
public void onClick(View v) {
showKey(mSignatureKeyId);
}
});
break; break;
} }
@ -163,25 +164,9 @@ public abstract class DecryptFragment extends Fragment {
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_UNVERIFIED); KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_UNVERIFIED);
setSignatureLayoutVisibility(View.VISIBLE); setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(mSignatureAction, mSignatureKeyId); setShowAction(mSignatureKeyId);
break;
}
case OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED: { valid = true;
mSignatureText.setText(R.string.decrypt_result_signature_expired_key);
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_EXPIRED);
setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(mSignatureAction, mSignatureKeyId);
break;
}
case OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED: {
mSignatureText.setText(R.string.decrypt_result_signature_revoked_key);
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_REVOKED);
setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(mSignatureAction, mSignatureKeyId);
break; break;
} }
@ -198,6 +183,30 @@ public abstract class DecryptFragment extends Fragment {
lookupUnknownKey(mSignatureKeyId); lookupUnknownKey(mSignatureKeyId);
} }
}); });
valid = true;
break;
}
case OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED: {
mSignatureText.setText(R.string.decrypt_result_signature_expired_key);
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_EXPIRED);
setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(mSignatureKeyId);
valid = false;
break;
}
case OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED: {
mSignatureText.setText(R.string.decrypt_result_signature_revoked_key);
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_REVOKED);
setSignatureLayoutVisibility(View.VISIBLE);
setShowAction(mSignatureKeyId);
valid = false;
break; break;
} }
@ -206,6 +215,8 @@ public abstract class DecryptFragment extends Fragment {
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_INVALID); KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_INVALID);
setSignatureLayoutVisibility(View.GONE); setSignatureLayoutVisibility(View.GONE);
valid = false;
break; break;
} }
} }
@ -216,7 +227,11 @@ public abstract class DecryptFragment extends Fragment {
KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_NOT_SIGNED); KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, KeyFormattingUtils.STATE_NOT_SIGNED);
mEncryptionText.setText(R.string.decrypt_result_encrypted); mEncryptionText.setText(R.string.decrypt_result_encrypted);
KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, KeyFormattingUtils.STATE_ENCRYPTED); KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, KeyFormattingUtils.STATE_ENCRYPTED);
valid = true;
} }
return valid;
} }
private void setSignatureLayoutVisibility(int visibility) { private void setSignatureLayoutVisibility(int visibility) {
@ -225,10 +240,10 @@ public abstract class DecryptFragment extends Fragment {
mSignatureDivider2.setVisibility(visibility); mSignatureDivider2.setVisibility(visibility);
} }
private void setShowAction(TextView signatureAction, final long signatureKeyId) { private void setShowAction(final long signatureKeyId) {
signatureAction.setText(R.string.decrypt_result_action_show); mSignatureAction.setText(R.string.decrypt_result_action_show);
signatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_accounts, 0); mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_action_accounts, 0);
signatureAction.setOnClickListener(new View.OnClickListener() { mSignatureLayout.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
showKey(signatureKeyId); showKey(signatureKeyId);

View File

@ -27,6 +27,8 @@ import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
@ -44,6 +46,9 @@ public class DecryptTextFragment extends DecryptFragment {
public static final String ARG_CIPHERTEXT = "ciphertext"; public static final String ARG_CIPHERTEXT = "ciphertext";
// view // view
private LinearLayout mValidLayout;
private LinearLayout mInvalidLayout;
private Button mInvalidButton;
private TextView mText; private TextView mText;
private View mShareButton; private View mShareButton;
private View mCopyButton; private View mCopyButton;
@ -71,7 +76,9 @@ public class DecryptTextFragment extends DecryptFragment {
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.decrypt_text_fragment, container, false); View view = inflater.inflate(R.layout.decrypt_text_fragment, container, false);
mValidLayout = (LinearLayout) view.findViewById(R.id.decrypt_text_valid);
mInvalidLayout = (LinearLayout) view.findViewById(R.id.decrypt_text_invalid);
mInvalidButton = (Button) view.findViewById(R.id.decrypt_text_invalid_button);
mText = (TextView) view.findViewById(R.id.decrypt_text_plaintext); mText = (TextView) view.findViewById(R.id.decrypt_text_plaintext);
mShareButton = view.findViewById(R.id.action_decrypt_share_plaintext); mShareButton = view.findViewById(R.id.action_decrypt_share_plaintext);
mCopyButton = view.findViewById(R.id.action_decrypt_copy_plaintext); mCopyButton = view.findViewById(R.id.action_decrypt_copy_plaintext);
@ -87,6 +94,13 @@ public class DecryptTextFragment extends DecryptFragment {
copyToClipboard(mText.getText().toString()); copyToClipboard(mText.getText().toString());
} }
}); });
mInvalidButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mInvalidLayout.setVisibility(View.GONE);
mValidLayout.setVisibility(View.VISIBLE);
}
});
return view; return view;
} }
@ -186,9 +200,18 @@ public class DecryptTextFragment extends DecryptFragment {
pgpResult.createNotify(getActivity()).show(); pgpResult.createNotify(getActivity()).show();
// display signature result in activity // display signature result in activity
onResult(pgpResult); boolean valid = onResult(pgpResult);
if (valid) {
mInvalidLayout.setVisibility(View.GONE);
mValidLayout.setVisibility(View.VISIBLE);
} else {
mInvalidLayout.setVisibility(View.VISIBLE);
mValidLayout.setVisibility(View.GONE);
}
} else { } else {
pgpResult.createNotify(getActivity()).show(); pgpResult.createNotify(getActivity()).show();
// TODO: show also invalid layout with different text?
} }
} }
} }

View File

@ -6,73 +6,109 @@
<include layout="@layout/decrypt_result_include" /> <include layout="@layout/decrypt_result_include" />
<ScrollView
android:fillViewport="true"
android:paddingTop="8dp"
android:layout_width="match_parent"
android:scrollbars="vertical"
android:layout_height="0dp"
android:layout_weight="1">
<TextView
android:id="@+id/decrypt_text_plaintext"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:hint=""
android:textIsSelectable="true" />
</ScrollView>
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:background="?android:attr/listDivider" />
<LinearLayout <LinearLayout
android:id="@+id/action_decrypt_share_plaintext" android:visibility="gone"
android:id="@+id/decrypt_text_valid"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginLeft="16dp" android:orientation="vertical">
android:layout_marginRight="16dp"
android:clickable="true"
style="@style/SelectableItem"
android:orientation="horizontal">
<TextView <ScrollView
android:paddingLeft="8dp" android:fillViewport="true"
android:paddingRight="8dp" android:paddingTop="8dp"
android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="match_parent"
android:layout_width="0dp" android:scrollbars="vertical"
android:layout_height="wrap_content" android:layout_height="0dp"
android:minHeight="?android:attr/listPreferredItemHeight" android:layout_weight="1">
android:text="@string/btn_add_share_decrypted_text"
android:drawableRight="@drawable/ic_action_share" <TextView
android:drawablePadding="8dp" android:id="@+id/decrypt_text_plaintext"
android:gravity="center_vertical" android:paddingLeft="16dp"
android:layout_weight="1" /> android:paddingRight="16dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:hint=""
android:textIsSelectable="true" />
</ScrollView>
<View <View
android:layout_width="1dip" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="1dip"
android:gravity="right" android:layout_marginLeft="16dp"
android:layout_marginBottom="8dp" android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:background="?android:attr/listDivider" /> android:background="?android:attr/listDivider" />
<ImageButton <LinearLayout
android:id="@+id/action_decrypt_copy_plaintext" android:id="@+id/action_decrypt_share_plaintext"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:padding="8dp" android:layout_marginLeft="16dp"
android:src="@drawable/ic_action_copy" android:layout_marginRight="16dp"
android:layout_gravity="center_vertical" android:clickable="true"
style="@style/SelectableItem" /> style="@style/SelectableItem"
android:orientation="horizontal">
<TextView
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight"
android:text="@string/btn_add_share_decrypted_text"
android:drawableRight="@drawable/ic_action_share"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:layout_weight="1" />
<View
android:layout_width="1dip"
android:layout_height="match_parent"
android:gravity="right"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:background="?android:attr/listDivider" />
<ImageButton
android:id="@+id/action_decrypt_copy_plaintext"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="8dp"
android:src="@drawable/ic_action_copy"
android:layout_gravity="center_vertical"
style="@style/SelectableItem" />
</LinearLayout>
</LinearLayout> </LinearLayout>
<LinearLayout
android:visibility="gone"
android:id="@+id/decrypt_text_invalid"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/decrypt_invalid_text"
android:padding="8dp"
android:layout_gravity="center"
android:textColor="@color/android_red_dark" />
<Button
android:id="@+id/decrypt_text_invalid_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/button_edgy"
android:textColor="@color/android_red_dark"
android:text="@string/decrypt_invalid_button"
android:layout_gravity="center_horizontal" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -277,6 +277,8 @@
<string name="decrypt_result_not_encrypted">"Not Encrypted"</string> <string name="decrypt_result_not_encrypted">"Not Encrypted"</string>
<string name="decrypt_result_action_show">"Show"</string> <string name="decrypt_result_action_show">"Show"</string>
<string name="decrypt_result_action_Lookup">"Lookup"</string> <string name="decrypt_result_action_Lookup">"Lookup"</string>
<string name="decrypt_invalid_text">"Either the signature is invalid or the key has been revoked/is expired. You can not be sure who wrote the text. Do you still want to display it?"</string>
<string name="decrypt_invalid_button">"I understand the risks, display it!"</string>
<!-- Add keys --> <!-- Add keys -->
<string name="add_keys_section_secure_exchange">"Exchange"</string> <string name="add_keys_section_secure_exchange">"Exchange"</string>