From b509c3ed399c07de02cbf777010cc4806db16cda Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 16 May 2015 23:05:58 +0200 Subject: [PATCH 01/33] fix fragment handling in ViewKeyActivity Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java --- .../keychain/ui/ViewKeyActivity.java | 40 ++++++------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 5466c0b9a..7d57538b6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -274,9 +274,19 @@ public class ViewKeyActivity extends BaseNfcActivity implements result.createNotify(this).show(); } - startFragment(savedInstanceState, mDataUri); + // Fragments are stored, no need to recreate those + if (savedInstanceState != null) { + return; + } - if (savedInstanceState == null && getIntent().hasExtra(EXTRA_NFC_AID)) { + FragmentManager manager = getSupportFragmentManager(); + // Create an instance of the fragment + final ViewKeyFragment frag = ViewKeyFragment.newInstance(mDataUri); + manager.beginTransaction() + .replace(R.id.view_key_fragment, frag) + .commit(); + + if (getIntent().hasExtra(EXTRA_NFC_AID)) { Intent intent = getIntent(); byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS); String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID); @@ -291,26 +301,6 @@ public class ViewKeyActivity extends BaseNfcActivity implements setContentView(R.layout.view_key_activity); } - private void startFragment(Bundle savedInstanceState, Uri dataUri) { - // However, if we're being restored from a previous state, - // then we don't need to do anything and should return or else - // we could end up with overlapping fragments. - if (savedInstanceState != null) { - return; - } - - // Create an instance of the fragment - ViewKeyFragment frag = ViewKeyFragment.newInstance(dataUri); - - // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! - getSupportFragmentManager().beginTransaction() - .replace(R.id.view_key_fragment, frag) - .commitAllowingStateLoss(); - // do it immediately! - getSupportFragmentManager().executePendingTransactions(); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); @@ -435,12 +425,6 @@ public class ViewKeyActivity extends BaseNfcActivity implements startActivityForResult(intent, 0); } - @Override - protected void onSaveInstanceState(Bundle outState) { - //Note:-Done due to the same weird crashes as for commitAllowingStateLoss() - //super.onSaveInstanceState(outState); - } - private void showQrCodeDialog() { Intent qrCodeIntent = new Intent(this, QrCodeViewActivity.class); From d06ae7f691e3bf1984cb79f94aa5a455c88f36f8 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 15 May 2015 01:04:25 +0200 Subject: [PATCH 02/33] yubikey: don't assume signing key is masterKeyId in ViewKeyActivity Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java --- .../ui/CreateKeyYubiKeyImportFragment.java | 10 ++- .../keychain/ui/ViewKeyActivity.java | 80 ++++++++++--------- 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java index 4c7d1dfbd..f8d79d33b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java @@ -193,13 +193,19 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe ImportKeyResult result = returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT); - if (!result.success()) { + long[] masterKeyIds = result.getImportedMasterKeyIds(); + + // TODO handle masterKeyIds.length != 1...? sorta outlandish scenario + + if (!result.success() || masterKeyIds.length == 0) { result.createNotify(getActivity()).show(); return; } Intent intent = new Intent(getActivity(), ViewKeyActivity.class); - intent.setData(KeyRings.buildGenericKeyRingUri(mNfcMasterKeyId)); + // use the imported masterKeyId, not the one from the yubikey, because + // that one might* just have been a subkey of the imported key + intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0])); intent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result); intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid); intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 7d57538b6..8c1d23d68 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -535,49 +535,54 @@ public class ViewKeyActivity extends BaseNfcActivity implements final String nfcUserId = nfcGetUserId(); final byte[] nfcAid = nfcGetAid(); - String fp = KeyFormattingUtils.convertFingerprintToHex(nfcFingerprints); - final long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints); + long yubiKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints); - if (!mFingerprint.equals(fp)) { - try { - CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing(masterKeyId); - ring.getMasterKeyId(); + try { - Notify.create(this, R.string.snack_yubi_other, Notify.LENGTH_LONG, - Style.WARN, new ActionListener() { - @Override - public void onAction() { - Intent intent = new Intent( - ViewKeyActivity.this, ViewKeyActivity.class); - intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); - startActivity(intent); - finish(); - } - }, R.string.snack_yubikey_view).show(); - return; + // if the yubikey matches a subkey in any key + CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(yubiKeyId)); + byte[] candidateFp = ring.getFingerprint(); - } catch (PgpKeyNotFoundException e) { - Notify.create(this, R.string.snack_yubi_other, Notify.LENGTH_LONG, - Style.WARN, new ActionListener() { - @Override - public void onAction() { - Intent intent = new Intent( - ViewKeyActivity.this, CreateKeyActivity.class); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); - startActivity(intent); - finish(); - } - }, R.string.snack_yubikey_import).show(); + // if the master key of that key matches this one, just show the yubikey dialog + if (KeyFormattingUtils.convertFingerprintToHex(candidateFp).equals(mFingerprint)) { + showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); return; } - } - showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); + // otherwise, offer to go to that key + final long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(candidateFp); + Notify.create(this, R.string.snack_yubi_other, Notify.LENGTH_LONG, + Style.WARN, new ActionListener() { + @Override + public void onAction() { + Intent intent = new Intent( + ViewKeyActivity.this, ViewKeyActivity.class); + intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); + startActivity(intent); + finish(); + } + }, R.string.snack_yubikey_view).show(); + + // and if it's not found, offer import + } catch (PgpKeyNotFoundException e) { + Notify.create(this, R.string.snack_yubi_other, Notify.LENGTH_LONG, + Style.WARN, new ActionListener() { + @Override + public void onAction() { + Intent intent = new Intent( + ViewKeyActivity.this, CreateKeyActivity.class); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); + startActivity(intent); + finish(); + } + }, R.string.snack_yubikey_import).show(); + } } @@ -794,6 +799,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements // old cursor once we return.) switch (loader.getId()) { case LOADER_ID_UNIFIED: { + if (data.moveToFirst()) { // get name, email, and comment from USER_ID KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); From f6643962afdf980fe50740eb3c667bf280c15c56 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 16 May 2015 23:29:59 +0200 Subject: [PATCH 03/33] allow state loss when yubikey fragment is loaded --- .../org/sufficientlysecure/keychain/ui/ViewKeyActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 8c1d23d68..320ccee5d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -596,7 +596,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements manager.beginTransaction() .addToBackStack("yubikey") .replace(R.id.view_key_fragment, frag) - .commit(); + // if this is called while the activity wasn't resumed, just forget it happened + .commitAllowingStateLoss(); } private void encrypt(Uri dataUri, boolean text) { From 71818934ca04fe3e913b919105e5fa302c5c0d99 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 16 May 2015 23:59:04 +0200 Subject: [PATCH 04/33] pass masterKeyId to yubikey fragment --- .../keychain/ui/ViewKeyActivity.java | 2 +- .../keychain/ui/ViewKeyYubiKeyFragment.java | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 320ccee5d..023483193 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -588,7 +588,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements public void showYubiKeyFragment(byte[] nfcFingerprints, String nfcUserId, byte[] nfcAid) { ViewKeyYubiKeyFragment frag = ViewKeyYubiKeyFragment.newInstance( - nfcFingerprints, nfcUserId, nfcAid); + mMasterKeyId, nfcFingerprints, nfcUserId, nfcAid); FragmentManager manager = getSupportFragmentManager(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java index 812874456..99ac73800 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java @@ -45,14 +45,15 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; public class ViewKeyYubiKeyFragment extends Fragment implements LoaderCallbacks { + public static final String ARG_MASTER_KEY_ID = "master_key_id"; public static final String ARG_FINGERPRINT = "fingerprint"; public static final String ARG_USER_ID = "user_id"; public static final String ARG_CARD_AID = "aid"; + private byte[][] mFingerprints; private String mUserId; private byte[] mCardAid; @@ -60,10 +61,12 @@ public class ViewKeyYubiKeyFragment extends Fragment private Button vButton; private TextView vStatus; - public static ViewKeyYubiKeyFragment newInstance(byte[] fingerprints, String userId, byte[] aid) { + public static ViewKeyYubiKeyFragment newInstance(long masterKeyId, + byte[] fingerprints, String userId, byte[] aid) { ViewKeyYubiKeyFragment frag = new ViewKeyYubiKeyFragment(); Bundle args = new Bundle(); + args.putLong(ARG_MASTER_KEY_ID, masterKeyId); args.putByteArray(ARG_FINGERPRINT, fingerprints); args.putString(ARG_USER_ID, userId); args.putByteArray(ARG_CARD_AID, aid); @@ -86,7 +89,7 @@ public class ViewKeyYubiKeyFragment extends Fragment mUserId = args.getString(ARG_USER_ID); mCardAid = args.getByteArray(ARG_CARD_AID); - mMasterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints[0]); + mMasterKeyId = args.getLong(ARG_MASTER_KEY_ID); getLoaderManager().initLoader(0, null, this); @@ -169,8 +172,8 @@ public class ViewKeyYubiKeyFragment extends Fragment Keys.HAS_SECRET, Keys.FINGERPRINT }; - private static final int INDEX_KEY_ID = 1; - private static final int INDEX_RANK = 2; + // private static final int INDEX_KEY_ID = 1; + // private static final int INDEX_RANK = 2; private static final int INDEX_HAS_SECRET = 3; private static final int INDEX_FINGERPRINT = 4; From 158263f2555eec0bb6e2b3738fa2edfbca71ae72 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 17 May 2015 00:35:10 +0200 Subject: [PATCH 05/33] apply promote operation to specific subkeys present on yubikey only --- .../operations/PromoteKeyOperationTest.java | 41 ++++++++++++++++++- .../operations/PromoteKeyOperation.java | 31 +++++++++++++- .../operations/results/OperationResult.java | 3 ++ .../keychain/pgp/CanonicalizedKeyRing.java | 7 +++- .../pgp/CanonicalizedPublicKeyRing.java | 17 +++++++- .../service/KeychainIntentService.java | 8 ++-- .../keychain/ui/ViewKeyYubiKeyFragment.java | 9 +++- OpenKeychain/src/main/res/values/strings.xml | 3 ++ 8 files changed, 108 insertions(+), 11 deletions(-) diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java index 617b5762c..2ca904656 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperationTest.java @@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; +import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.TestingUtils; @@ -104,7 +105,7 @@ public class PromoteKeyOperationTest { PromoteKeyOperation op = new PromoteKeyOperation(Robolectric.application, new ProviderHelper(Robolectric.application), null, null); - PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), null); + PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), null, null); Assert.assertTrue("promotion must succeed", result.success()); @@ -130,7 +131,7 @@ public class PromoteKeyOperationTest { byte[] aid = Hex.decode("D2760001240102000000012345670000"); - PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), aid); + PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), aid, null); Assert.assertTrue("promotion must succeed", result.success()); @@ -147,4 +148,40 @@ public class PromoteKeyOperationTest { } } + + @Test + public void testPromoteDivertSpecific() throws Exception { + PromoteKeyOperation op = new PromoteKeyOperation(Robolectric.application, + new ProviderHelper(Robolectric.application), null, null); + + byte[] aid = Hex.decode("D2760001240102000000012345670000"); + + // only promote the first, rest stays dummy + long keyId = KeyringTestingHelper.getSubkeyId(mStaticRing, 1); + + PromoteKeyResult result = op.execute(mStaticRing.getMasterKeyId(), aid, new long[] { + keyId + }); + + Assert.assertTrue("promotion must succeed", result.success()); + + { + CanonicalizedSecretKeyRing ring = new ProviderHelper(Robolectric.application) + .getCanonicalizedSecretKeyRing(mStaticRing.getMasterKeyId()); + + for (CanonicalizedSecretKey key : ring.secretKeyIterator()) { + if (key.getKeyId() == keyId) { + Assert.assertEquals("subkey must be divert-to-card", + SecretKeyType.DIVERT_TO_CARD, key.getSecretKeyType()); + Assert.assertArrayEquals("subkey must have correct iv", + aid, key.getIv()); + } else { + Assert.assertEquals("some subkeys must be gnu dummy", + SecretKeyType.GNU_DUMMY, key.getSecretKeyType()); + } + } + + } + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java index ef08b0b77..558756378 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java @@ -25,6 +25,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult; import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; @@ -34,6 +35,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.ProgressScaler; +import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; /** An operation which promotes a public key ring to a secret one. @@ -50,7 +52,7 @@ public class PromoteKeyOperation extends BaseOperation { super(context, providerHelper, progressable, cancelled); } - public PromoteKeyResult execute(long masterKeyId, byte[] cardAid) { + public PromoteKeyResult execute(long masterKeyId, byte[] cardAid, long[] subKeyIds) { OperationLog log = new OperationLog(); log.add(LogType.MSG_PR, 0); @@ -65,8 +67,24 @@ public class PromoteKeyOperation extends BaseOperation { CanonicalizedPublicKeyRing pubRing = mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId); + if (subKeyIds == null) { + log.add(LogType.MSG_PR_ALL, 1); + } else { + // sort for binary search + for (CanonicalizedPublicKey key : pubRing.publicKeyIterator()) { + long subKeyId = key.getKeyId(); + if (naiveIndexOf(subKeyIds, subKeyId) != null) { + log.add(LogType.MSG_PR_SUBKEY_MATCH, 1, + KeyFormattingUtils.convertKeyIdToHex(subKeyId)); + } else { + log.add(LogType.MSG_PR_SUBKEY_NOMATCH, 1, + KeyFormattingUtils.convertKeyIdToHex(subKeyId)); + } + } + } + // create divert-to-card secret key from public key - promotedRing = pubRing.createDivertSecretRing(cardAid); + promotedRing = pubRing.createDivertSecretRing(cardAid, subKeyIds); } catch (NotFoundException e) { log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2); @@ -106,4 +124,13 @@ public class PromoteKeyOperation extends BaseOperation { } + static private Integer naiveIndexOf(long[] haystack, long needle) { + for (int i = 0; i < haystack.length; i++) { + if (needle == haystack[i]) { + return i; + } + } + return null; + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 094afd4a5..d39ab3695 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -558,8 +558,11 @@ public abstract class OperationResult implements Parcelable { // promote key MSG_PR (LogLevel.START, R.string.msg_pr), + MSG_PR_ALL (LogLevel.DEBUG, R.string.msg_pr_all), MSG_PR_ERROR_KEY_NOT_FOUND (LogLevel.ERROR, R.string.msg_pr_error_key_not_found), MSG_PR_FETCHING (LogLevel.DEBUG, R.string.msg_pr_fetching), + MSG_PR_SUBKEY_MATCH (LogLevel.DEBUG, R.string.msg_pr_subkey_match), + MSG_PR_SUBKEY_NOMATCH (LogLevel.WARN, R.string.msg_pr_subkey_nomatch), MSG_PR_SUCCESS (LogLevel.OK, R.string.msg_pr_success), // messages used in UI code diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java index 4adacaf23..432ba23e9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.openpgp.PGPKeyRing; +import org.spongycastle.openpgp.PGPPublicKey; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.util.IterableIterator; @@ -127,7 +128,11 @@ public abstract class CanonicalizedKeyRing extends KeyRing { } public CanonicalizedPublicKey getPublicKey(long id) { - return new CanonicalizedPublicKey(this, getRing().getPublicKey(id)); + PGPPublicKey pubKey = getRing().getPublicKey(id); + if (pubKey == null) { + return null; + } + return new CanonicalizedPublicKey(this, pubKey); } public byte[] getEncoded() throws IOException { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java index 8432b8f9f..68fd4a428 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java @@ -103,9 +103,22 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing { } /** Create a dummy secret ring from this key */ - public UncachedKeyRing createDivertSecretRing (byte[] cardAid) { + public UncachedKeyRing createDivertSecretRing (byte[] cardAid, long[] subKeyIds) { PGPSecretKeyRing secRing = PGPSecretKeyRing.constructDummyFromPublic(getRing(), cardAid); - return new UncachedKeyRing(secRing); + + if (subKeyIds == null) { + return new UncachedKeyRing(secRing); + } + + // if only specific subkeys should be promoted, construct a + // stripped dummy, then move divert-to-card keys over + PGPSecretKeyRing newRing = PGPSecretKeyRing.constructDummyFromPublic(getRing()); + for (long subKeyId : subKeyIds) { + newRing = PGPSecretKeyRing.insertSecretKey(newRing, secRing.getSecretKey(subKeyId)); + } + + return new UncachedKeyRing(newRing); + } } \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index e0509ac9b..71e149672 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -65,7 +65,6 @@ import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableFileCache; -import org.sufficientlysecure.keychain.util.Passphrase; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -185,6 +184,7 @@ public class KeychainIntentService extends IntentService implements Progressable // promote key public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id"; public static final String PROMOTE_CARD_AID = "promote_card_aid"; + public static final String PROMOTE_SUBKEY_IDS = "promote_fingerprints"; // consolidate public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery"; @@ -476,10 +476,12 @@ public class KeychainIntentService extends IntentService implements Progressable // Input long keyRingId = data.getLong(PROMOTE_MASTER_KEY_ID); byte[] cardAid = data.getByteArray(PROMOTE_CARD_AID); + long[] subKeyIds = data.getLongArray(PROMOTE_SUBKEY_IDS); // Operation - PromoteKeyOperation op = new PromoteKeyOperation(this, providerHelper, this, mActionCanceled); - PromoteKeyResult result = op.execute(keyRingId, cardAid); + PromoteKeyOperation op = new PromoteKeyOperation( + this, providerHelper, this, mActionCanceled); + PromoteKeyResult result = op.execute(keyRingId, cardAid, subKeyIds); // Result sendMessageToHandler(MessageStatus.OKAY, result); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java index 99ac73800..ecd351965 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java @@ -45,6 +45,8 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; + public class ViewKeyYubiKeyFragment extends Fragment implements LoaderCallbacks { @@ -154,6 +156,11 @@ public class ViewKeyYubiKeyFragment extends Fragment Bundle data = new Bundle(); data.putLong(KeychainIntentService.PROMOTE_MASTER_KEY_ID, mMasterKeyId); data.putByteArray(KeychainIntentService.PROMOTE_CARD_AID, mCardAid); + long[] subKeyIds = new long[mFingerprints.length]; + for (int i = 0; i < subKeyIds.length; i++) { + subKeyIds[i] = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints[i]); + } + data.putLongArray(KeychainIntentService.PROMOTE_SUBKEY_IDS, subKeyIds); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); // Create a new Messenger for the communication back @@ -219,7 +226,7 @@ public class ViewKeyYubiKeyFragment extends Fragment } - public Integer naiveIndexOf(byte[][] haystack, byte[] needle) { + static private Integer naiveIndexOf(byte[][] haystack, byte[] needle) { for (int i = 0; i < haystack.length; i++) { if (Arrays.equals(needle, haystack[i])) { return i; diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 45b1dc26b..504c9497c 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -996,8 +996,11 @@ "Promoting public key to secret key" + "Promoting all subkeysp" "Key not found!" "Fetching key to modify (%s)" + "Promoting subkey: %s" + "Subkey not on Yubikey: %s" "Key successfully promoted" From 9a6f581400d04339f12dc008f4c38bef1134b6dc Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 17 May 2015 00:54:09 +0200 Subject: [PATCH 06/33] load yubikey fragment after mMasterKeyId is available --- .../keychain/ui/ViewKeyActivity.java | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 023483193..4237deff0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -131,6 +131,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements private boolean mIsRevoked = false; private boolean mIsExpired = false; + private boolean mShowYubikeyAfterCreation = false; + private MenuItem mRefreshItem; private boolean mIsRefreshing; private Animation mRotate, mRotateSpin; @@ -286,13 +288,9 @@ public class ViewKeyActivity extends BaseNfcActivity implements .replace(R.id.view_key_fragment, frag) .commit(); - if (getIntent().hasExtra(EXTRA_NFC_AID)) { - Intent intent = getIntent(); - byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS); - String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID); - byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID); - showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); - } + // need to postpone loading of the yubikey fragment until after mMasterKeyId + // is available, but we mark here that this should be done + mShowYubikeyAfterCreation = true; } @@ -586,18 +584,26 @@ public class ViewKeyActivity extends BaseNfcActivity implements } - public void showYubiKeyFragment(byte[] nfcFingerprints, String nfcUserId, byte[] nfcAid) { - ViewKeyYubiKeyFragment frag = ViewKeyYubiKeyFragment.newInstance( - mMasterKeyId, nfcFingerprints, nfcUserId, nfcAid); + public void showYubiKeyFragment( + final byte[] nfcFingerprints, final String nfcUserId, final byte[] nfcAid) { - FragmentManager manager = getSupportFragmentManager(); + new Handler().post(new Runnable() { + @Override + public void run() { + ViewKeyYubiKeyFragment frag = ViewKeyYubiKeyFragment.newInstance( + mMasterKeyId, nfcFingerprints, nfcUserId, nfcAid); + + FragmentManager manager = getSupportFragmentManager(); + + manager.popBackStack("yubikey", FragmentManager.POP_BACK_STACK_INCLUSIVE); + manager.beginTransaction() + .addToBackStack("yubikey") + .replace(R.id.view_key_fragment, frag) + // if this is called while the activity wasn't resumed, just forget it happened + .commitAllowingStateLoss(); + } + }); - manager.popBackStack("yubikey", FragmentManager.POP_BACK_STACK_INCLUSIVE); - manager.beginTransaction() - .addToBackStack("yubikey") - .replace(R.id.view_key_fragment, frag) - // if this is called while the activity wasn't resumed, just forget it happened - .commitAllowingStateLoss(); } private void encrypt(Uri dataUri, boolean text) { @@ -813,6 +819,16 @@ public class ViewKeyActivity extends BaseNfcActivity implements mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); mFingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT)); + // if it wasn't shown yet, display yubikey fragment + if (mShowYubikeyAfterCreation && getIntent().hasExtra(EXTRA_NFC_AID)) { + mShowYubikeyAfterCreation = false; + Intent intent = getIntent(); + byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS); + String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID); + byte[] nfcAid = intent.getByteArrayExtra(EXTRA_NFC_AID); + showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); + } + mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0; mIsRevoked = data.getInt(INDEX_IS_REVOKED) > 0; From 765990ed13c9452d1488defd30d6537b762e5628 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 17 May 2015 00:59:50 +0200 Subject: [PATCH 07/33] fingerprints are 20 bytes, not 40. duh. --- .../sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java index ecd351965..78a6ac55a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java @@ -83,7 +83,7 @@ public class ViewKeyYubiKeyFragment extends Fragment Bundle args = getArguments(); ByteBuffer buf = ByteBuffer.wrap(args.getByteArray(ARG_FINGERPRINT)); - mFingerprints = new byte[buf.remaining()/40][]; + mFingerprints = new byte[buf.remaining()/20][]; for (int i = 0; i < mFingerprints.length; i++) { mFingerprints[i] = new byte[20]; buf.get(mFingerprints[i]); From 3a27a28c0a9cee91b98b42b5e97d5312b8d25326 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 17 May 2015 01:13:41 +0200 Subject: [PATCH 08/33] re-parcel log in LogDisplayFragment --- .../keychain/ui/LogDisplayFragment.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 138f2f4e7..3c8fbf8c9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -49,7 +49,6 @@ import org.sufficientlysecure.keychain.util.Log; import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; -import java.util.Iterator; public class LogDisplayFragment extends ListFragment implements OnItemClickListener { @@ -58,6 +57,7 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe OperationResult mResult; public static final String EXTRA_RESULT = "log"; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -75,7 +75,12 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe return; } - mResult = intent.getParcelableExtra(EXTRA_RESULT); + if (savedInstanceState != null) { + mResult = savedInstanceState.getParcelable(EXTRA_RESULT); + } else { + mResult = intent.getParcelableExtra(EXTRA_RESULT); + } + if (mResult == null) { getActivity().finish(); return; @@ -91,6 +96,14 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + // need to parcel this again, logs are only single-instance parcelable + outState.putParcelable(EXTRA_RESULT, mResult); + } + @Override public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { inflater.inflate(R.menu.log_display, menu); @@ -110,7 +123,6 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe } private void exportLog() { - showExportLogDialog(new File(Constants.Path.APP_DIR, "export.log")); } @@ -142,7 +154,9 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe } } - if (!error) currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_SUCCESS, 1); + if (!error) { + currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_SUCCESS, 1); + } int opResultCode = error ? OperationResult.RESULT_ERROR : OperationResult.RESULT_OK; OperationResult opResult = new LogExportResult(opResultCode, currLog); @@ -158,8 +172,8 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe */ private String getPrintableOperationLog(OperationResult.OperationLog opLog, String basePadding) { String log = ""; - for (Iterator logIterator = opLog.iterator(); logIterator.hasNext(); ) { - log += getPrintableLogEntry(logIterator.next(), basePadding) + "\n"; + for (LogEntryParcel anOpLog : opLog) { + log += getPrintableLogEntry(anOpLog, basePadding) + "\n"; } log = log.substring(0, log.length() - 1);//gets rid of extra new line return log; From a79d7bd1c280fda732a9550e88554fd9bb84bb1f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 17 May 2015 01:41:51 +0200 Subject: [PATCH 09/33] simplify MainActivity and fix backstack issues --- .../keychain/ui/MainActivity.java | 93 +++++++------------ 1 file changed, 33 insertions(+), 60 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index f571ba1e6..78df612a7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -44,12 +44,7 @@ import org.sufficientlysecure.keychain.util.Preferences; public class MainActivity extends AppCompatActivity implements FabContainer { - public Drawer.Result result; - - private KeyListFragment mKeyListFragment ; - private AppsListFragment mAppsListFragment; - private EncryptDecryptOverviewFragment mEncryptDecryptOverviewFragment; - private Fragment mLastUsedFragment; + public Drawer.Result mDrawerResult; private Toolbar mToolbar; @Override @@ -57,25 +52,21 @@ public class MainActivity extends AppCompatActivity implements FabContainer { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); - //initialize FragmentLayout with KeyListFragment at first - Fragment mainFragment = new KeyListFragment(); - FragmentManager fm = getSupportFragmentManager(); - FragmentTransaction transaction = fm.beginTransaction(); - transaction.replace(R.id.main_fragment_container, mainFragment); - transaction.commit(); - mToolbar = (Toolbar) findViewById(R.id.toolbar); mToolbar.setTitle(R.string.app_name); setSupportActionBar(mToolbar); - result = new Drawer() + mDrawerResult = new Drawer() .withActivity(this) .withHeader(R.layout.main_drawer_header) .withToolbar(mToolbar) .addDrawerItems( - new PrimaryDrawerItem().withName(R.string.nav_keys).withIcon(CommunityMaterial.Icon.cmd_key).withIdentifier(1).withCheckable(false), - new PrimaryDrawerItem().withName(R.string.nav_encrypt_decrypt).withIcon(FontAwesome.Icon.faw_lock).withIdentifier(2).withCheckable(false), - new PrimaryDrawerItem().withName(R.string.title_api_registered_apps).withIcon(CommunityMaterial.Icon.cmd_apps).withIdentifier(3).withCheckable(false) + new PrimaryDrawerItem().withName(R.string.nav_keys).withIcon(CommunityMaterial.Icon.cmd_key) + .withIdentifier(1).withCheckable(false), + new PrimaryDrawerItem().withName(R.string.nav_encrypt_decrypt).withIcon(FontAwesome.Icon.faw_lock) + .withIdentifier(2).withCheckable(false), + new PrimaryDrawerItem().withName(R.string.title_api_registered_apps).withIcon(CommunityMaterial.Icon.cmd_apps) + .withIdentifier(3).withCheckable(false) ) .addStickyDrawerItems( // display and stick on bottom of drawer @@ -130,76 +121,58 @@ public class MainActivity extends AppCompatActivity implements FabContainer { OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); result.createNotify(this).show(); } - } - private void clearFragments() { - mKeyListFragment = null; - mAppsListFragment = null; - mEncryptDecryptOverviewFragment = null; + if (savedInstanceState == null) { + // initialize FragmentLayout with KeyListFragment at first + onKeysSelected(); + } - getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); - } - - private void setFragment(Fragment fragment) { - setFragment(fragment, true); } private void setFragment(Fragment fragment, boolean addToBackStack) { - this.mLastUsedFragment = fragment; - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); + + FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); + + FragmentTransaction ft = fragmentManager.beginTransaction(); ft.replace(R.id.main_fragment_container, fragment); if (addToBackStack) { ft.addToBackStack(null); } ft.commit(); + } - private boolean onKeysSelected() { + private void onKeysSelected() { mToolbar.setTitle(R.string.app_name); - clearFragments(); - - if (mKeyListFragment == null) { - mKeyListFragment = new KeyListFragment(); - } - - setFragment(mKeyListFragment, false); - return true; + Fragment frag = new KeyListFragment(); + setFragment(frag, false); } - private boolean onEnDecryptSelected() { + private void onEnDecryptSelected() { mToolbar.setTitle(R.string.nav_encrypt_decrypt); - clearFragments(); - if (mEncryptDecryptOverviewFragment == null) { - mEncryptDecryptOverviewFragment = new EncryptDecryptOverviewFragment(); - } - - setFragment(mEncryptDecryptOverviewFragment); - return true; + Fragment frag = new EncryptDecryptOverviewFragment(); + setFragment(frag, true); } - private boolean onAppsSelected() { + private void onAppsSelected() { mToolbar.setTitle(R.string.nav_apps); - clearFragments(); - if (mAppsListFragment == null) { - mAppsListFragment = new AppsListFragment(); - } - - setFragment(mAppsListFragment); - return true; + Fragment frag = new AppsListFragment(); + setFragment(frag, true); } @Override protected void onSaveInstanceState(Bundle outState) { - //add the values which need to be saved from the drawer to the bundle - outState = result.saveInstanceState(outState); + // add the values which need to be saved from the drawer to the bundle + outState = mDrawerResult.saveInstanceState(outState); super.onSaveInstanceState(outState); } @Override - public void onBackPressed(){ - //handle the back press :D close the drawer first and if the drawer is closed close the activity - if (result != null && result.isDrawerOpen()) { - result.closeDrawer(); + public void onBackPressed() { + // close the drawer first and if the drawer is closed do regular backstack handling + if (mDrawerResult != null && mDrawerResult.isDrawerOpen()) { + mDrawerResult.closeDrawer(); } else { super.onBackPressed(); } From fb96b6dfa9bcfd704e1237de5721583f50631b33 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 17 May 2015 01:57:26 +0200 Subject: [PATCH 10/33] open ViewKeyActivity by subkey in NfcBaseActivity --- .../keychain/ui/base/BaseNfcActivity.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java index 1d09b281f..0dad65e77 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseNfcActivity.java @@ -222,11 +222,12 @@ public abstract class BaseNfcActivity extends BaseActivity { final String nfcUserId = nfcGetUserId(); final byte[] nfcAid = nfcGetAid(); - final long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints); + final long subKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints); try { - CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId); - ring.getMasterKeyId(); + CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId)); + long masterKeyId = ring.getMasterKeyId(); Intent intent = new Intent( BaseNfcActivity.this, ViewKeyActivity.class); From b62ad4d79c1e90ca63dafcd0edece3bcb826b087 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 17 May 2015 10:36:14 +0200 Subject: [PATCH 11/33] check fingerprint length after scanning qr code Fixes #1281 --- .../operations/results/OperationResult.java | 3 +- .../keychain/ui/ImportKeysProxyActivity.java | 38 ++++++++++++------- OpenKeychain/src/main/res/values/strings.xml | 3 +- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index d39ab3695..ad7feaea6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -711,7 +711,8 @@ public abstract class OperationResult implements Parcelable { MSG_ACC_SAVED (LogLevel.INFO, R.string.api_settings_save_msg), - MSG_WRONG_QR_CODE (LogLevel.INFO, R.string.import_qr_code_wrong), + MSG_WRONG_QR_CODE (LogLevel.ERROR, R.string.import_qr_code_wrong), + MSG_WRONG_QR_CODE_FP(LogLevel.ERROR, R.string.import_qr_code_fp), MSG_NO_VALID_ENC (LogLevel.ERROR, R.string.error_invalid_data), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index dc8752d1a..77fa9cc0b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.intents.OpenKeychainIntents; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.SingletonResult; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; @@ -141,24 +142,35 @@ public class ImportKeysProxyActivity extends FragmentActivity { Log.d(Constants.TAG, "scanned: " + uri); // example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282 - if (uri != null && uri.getScheme() != null && uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { - String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH); - - if (ACTION_SCAN_WITH_RESULT.equals(action)) { - Intent result = new Intent(); - result.putExtra(EXTRA_FINGERPRINT, fingerprint); - setResult(RESULT_OK, result); - finish(); - } else { - importKeys(fingerprint); - } - } else { + if (uri == null || uri.getScheme() == null || + !uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { SingletonResult result = new SingletonResult( - SingletonResult.RESULT_ERROR, OperationResult.LogType.MSG_WRONG_QR_CODE); + SingletonResult.RESULT_ERROR, LogType.MSG_WRONG_QR_CODE); Intent intent = new Intent(); intent.putExtra(SingletonResult.EXTRA_RESULT, result); returnResult(intent); + return; } + + String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH); + if (fingerprint.matches("[a-fA-F0-9]{40}")) { + SingletonResult result = new SingletonResult( + SingletonResult.RESULT_ERROR, LogType.MSG_WRONG_QR_CODE_FP); + Intent intent = new Intent(); + intent.putExtra(SingletonResult.EXTRA_RESULT, result); + returnResult(intent); + return; + } + + if (ACTION_SCAN_WITH_RESULT.equals(action)) { + Intent result = new Intent(); + result.putExtra(EXTRA_FINGERPRINT, fingerprint); + setResult(RESULT_OK, result); + finish(); + } else { + importKeys(fingerprint); + } + } public void returnResult(Intent data) { diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 504c9497c..d09c60b0f 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -408,7 +408,8 @@ "QR Code/NFC" "Import selected keys" "QR Code malformed! Please try again!" - "Fingerprint is too short (< 16 characters)" + "Fingerprint is malformed or too short!" + "Fingerprint is too short!" "Scan QR Code" "Place your camera over the QR Code!" From 79b7cec94acfbba683e1c78d1576866c5fbd5d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 19 May 2015 18:27:04 +0200 Subject: [PATCH 12/33] Use Mode.ALPHANUMERIC for QR codes to save space --- .../keychain/ui/QrCodeViewActivity.java | 9 +++++---- .../keychain/ui/ViewKeyActivity.java | 7 +++++-- .../keychain/ui/ViewKeyAdvShareFragment.java | 7 +++++-- .../keychain/ui/util/QrCodeUtils.java | 10 +++++++++- 4 files changed, 24 insertions(+), 9 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java index d4858ee5d..e54852f1b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/QrCodeViewActivity.java @@ -85,11 +85,12 @@ public class QrCodeViewActivity extends BaseActivity { ActivityCompat.finishAfterTransition(QrCodeViewActivity.this); } - String fingerprint = KeyFormattingUtils.convertFingerprintToHex(blob); - String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; - + Uri uri = new Uri.Builder() + .scheme(Constants.FINGERPRINT_SCHEME) + .opaquePart(KeyFormattingUtils.convertFingerprintToHex(blob)) + .build(); // create a minimal size qr code, we can keep this in ram no problem - final Bitmap qrCode = QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0); + final Bitmap qrCode = QrCodeUtils.getQRCodeBitmap(uri, 0); mQrCode.getViewTreeObserver().addOnGlobalLayoutListener( new OnGlobalLayoutListener() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 4237deff0..9968855f7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -731,9 +731,12 @@ public class ViewKeyActivity extends BaseNfcActivity implements AsyncTask loadTask = new AsyncTask() { protected Bitmap doInBackground(Void... unused) { - String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; + Uri uri = new Uri.Builder() + .scheme(Constants.FINGERPRINT_SCHEME) + .opaquePart(fingerprint) + .build(); // render with minimal size - return QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0); + return QrCodeUtils.getQRCodeBitmap(uri, 0); } protected void onPostExecute(Bitmap qrCode) { 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 fde0f62fd..479ddae0b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java @@ -386,9 +386,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements AsyncTask loadTask = new AsyncTask() { protected Bitmap doInBackground(Void... unused) { - String qrCodeContent = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; + Uri uri = new Uri.Builder() + .scheme(Constants.FINGERPRINT_SCHEME) + .opaquePart(fingerprint) + .build(); // render with minimal size - return QrCodeUtils.getQRCodeBitmap(qrCodeContent, 0); + return QrCodeUtils.getQRCodeBitmap(uri, 0); } protected void onPostExecute(Bitmap qrCode) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java index 5f71abdab..a6394a3fb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.util; import android.graphics.Bitmap; import android.graphics.Color; +import android.net.Uri; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; @@ -33,17 +34,24 @@ import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.util.Log; import java.util.Hashtable; +import java.util.Locale; /** * Copied from Bitcoin Wallet */ public class QrCodeUtils { + public static Bitmap getQRCodeBitmap(final Uri uri, final int size) { + // for URIs we want alphanumeric encoding to save space, thus make everything upper case! + // zxing will then select Mode.ALPHANUMERIC internally + return getQRCodeBitmap(uri.toString().toUpperCase(Locale.ENGLISH), size); + } + /** * Generate Bitmap with QR Code based on input. * @return QR Code as Bitmap */ - public static Bitmap getQRCodeBitmap(final String input, final int size) { + private static Bitmap getQRCodeBitmap(final String input, final int size) { try { From 0df51b614015279b83b2c4059039ed3ac076ba62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 19 May 2015 19:07:58 +0200 Subject: [PATCH 13/33] Fix fingerprint length check in QR Code import --- .../sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index 77fa9cc0b..7ed8dd0b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -153,7 +153,7 @@ public class ImportKeysProxyActivity extends FragmentActivity { } String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH); - if (fingerprint.matches("[a-fA-F0-9]{40}")) { + if (!fingerprint.matches("[a-fA-F0-9]{40}")) { SingletonResult result = new SingletonResult( SingletonResult.RESULT_ERROR, LogType.MSG_WRONG_QR_CODE_FP); Intent intent = new Intent(); From 46cc7f8174d1bedabaafbab05e9461c1cfe72891 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 20 May 2015 12:46:31 +0200 Subject: [PATCH 14/33] tests: add attribute to PgpKeyOperationTest --- .../keychain/pgp/PgpKeyOperationTest.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java index 54ccccc3d..056165e01 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperationTest.java @@ -99,6 +99,14 @@ public class PgpKeyOperationTest { parcel.mAddUserIds.add("twi"); parcel.mAddUserIds.add("pink"); + + { + int type = 42; + byte[] data = new byte[] { 0, 1, 2, 3, 4 }; + WrappedUserAttribute uat = WrappedUserAttribute.fromSubpacket(type, data); + parcel.mAddUserAttribute.add(uat); + } + parcel.mNewUnlock = new ChangeUnlockParcel(passphrase); PgpKeyOperation op = new PgpKeyOperation(null); @@ -231,6 +239,17 @@ public class PgpKeyOperationTest { Assert.assertEquals("number of user ids must be two", 2, ring.getPublicKey().getUnorderedUserIds().size()); + ArrayList attributes = + ring.getPublicKey().getUnorderedUserAttributes(); + Assert.assertEquals("number of user attributes must be one", + 1, attributes.size()); + Assert.assertEquals("user attribute must be correct type", + 42, attributes.get(0).getType()); + Assert.assertEquals("user attribute must have one subpacket", + 1, attributes.get(0).getSubpackets().length); + Assert.assertArrayEquals("user attribute must have correct data", + new byte[] { 0, 1, 2, 3, 4 }, attributes.get(0).getSubpackets()[0]); + List subkeys = KeyringTestingHelper.itToList(ring.getPublicKeys()); Assert.assertEquals("number of subkeys must be three", 3, subkeys.size()); From f30900d085f38034708f62f94e46a38f222d4cb7 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 20 May 2015 12:47:02 +0200 Subject: [PATCH 15/33] add user id flags to UserAttribute self signatures --- .../keychain/pgp/PgpKeyOperation.java | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 89db378a9..2f771d8f2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -553,7 +553,8 @@ public class PgpKeyOperation { PGPSignature cert = generateUserAttributeSignature( getSignatureGenerator(masterSecretKey, cryptoInput), cryptoInput.getSignatureTime(), - masterPrivateKey, masterPublicKey, vector); + masterPrivateKey, masterPublicKey, vector, + masterKeyFlags, masterKeyExpiry); modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, vector, cert); } catch (NfcInteractionNeeded e) { nfcSignOps.addHash(e.hashToSign, e.hashAlgo); @@ -1319,11 +1320,9 @@ public class PgpKeyOperation { } - private PGPSignature generateUserIdSignature( - PGPSignatureGenerator sGen, Date creationTime, - PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, - int flags, long expiry) - throws IOException, PGPException, SignatureException { + private static PGPSignatureSubpacketGenerator generateHashedSelfSigSubpackets( + Date creationTime, PGPPublicKey pKey, boolean primary, int flags, long expiry + ) { PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); { @@ -1357,6 +1356,17 @@ public class PgpKeyOperation { } } + return hashedPacketsGen; + } + + private static PGPSignature generateUserIdSignature( + PGPSignatureGenerator sGen, Date creationTime, + PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, + int flags, long expiry) + throws IOException, PGPException, SignatureException { + + PGPSignatureSubpacketGenerator hashedPacketsGen = + generateHashedSelfSigSubpackets(creationTime, pKey, primary, flags, expiry); sGen.setHashedSubpackets(hashedPacketsGen.generate()); sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); return sGen.generateCertification(userId, pKey); @@ -1365,15 +1375,12 @@ public class PgpKeyOperation { private static PGPSignature generateUserAttributeSignature( PGPSignatureGenerator sGen, Date creationTime, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, - PGPUserAttributeSubpacketVector vector) + PGPUserAttributeSubpacketVector vector, + int flags, long expiry) throws IOException, PGPException, SignatureException { - PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - { - /* critical subpackets: we consider those important for a modern pgp implementation */ - hashedPacketsGen.setSignatureCreationTime(true, creationTime); - } - + PGPSignatureSubpacketGenerator hashedPacketsGen = + generateHashedSelfSigSubpackets(creationTime, pKey, false, flags, expiry); sGen.setHashedSubpackets(hashedPacketsGen.generate()); sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); return sGen.generateCertification(vector, pKey); From 6b6ca4c18ef96517829fcea954b3d46ff776a881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 21 May 2015 10:31:46 +0200 Subject: [PATCH 16/33] Fix language based on feedback from transifex --- .../keychain/ui/CreateKeyYubiKeyImportFragment.java | 2 +- .../sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java | 2 +- OpenKeychain/src/main/res/values/strings.xml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java index f8d79d33b..e2c36c60d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java @@ -164,7 +164,7 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe if (!mNfcUserId.isEmpty()) { vUserId.setText(getString(R.string.yubikey_key_holder, mNfcUserId)); } else { - vUserId.setText(getString(R.string.yubikey_key_holder_unset)); + vUserId.setText(getString(R.string.yubikey_key_holder_not_set)); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java index 78a6ac55a..b02bb0b83 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java @@ -110,7 +110,7 @@ public class ViewKeyYubiKeyFragment extends Fragment if (!mUserId.isEmpty()) { vUserId.setText(getString(R.string.yubikey_key_holder, mUserId)); } else { - vUserId.setText(getString(R.string.yubikey_key_holder_unset)); + vUserId.setText(getString(R.string.yubikey_key_holder_not_set)); } vButton = (Button) view.findViewById(R.id.button_bind); diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index d09c60b0f..98d91d3cf 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -1284,7 +1284,7 @@ "Bind Key" "Serial No: %s" "Key holder: " - "Key holder: <unset>" + "Key holder: <not set>" "YubiKey matches and is bound to key" "YubiKey matches, can be bound to key" "YubiKey matches, partly bound to key" From 95875d727ef76ad7558217af8a924f05e6e023d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 21 May 2015 10:34:39 +0200 Subject: [PATCH 17/33] Version 3.2.2 --- OpenKeychain/src/main/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 332930dfb..804239e0c 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -3,8 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" package="org.sufficientlysecure.keychain" android:installLocation="auto" - android:versionCode="32100" - android:versionName="3.2.1"> + android:versionCode="32200" + android:versionName="3.2.2"> - "No search parameter found. You may still attempt manually searching the keyserver." + "No search query defined. You can still manually search on this keyserver." "Details" @@ -657,7 +657,7 @@ "<none>" - "Add Keyserver" + "Add keyserver" "Keyserver verified!" "Keyserver added without verification." "Invalid URL!" @@ -801,7 +801,7 @@ "Marked secret subkey %s as available, with empty password" "Marked secret subkey %s as available, with PIN" "Marked secret subkey %s as stripped" - "Marked secret subkey %s as 'divert to smartcard/NFC'" + "Marked secret subkey %s as 'divert-to-card'" "Keyring contains no new data, nothing to do" "Successfully imported secret keyring" @@ -908,8 +908,8 @@ "Modifying keyring %s" - "Will divert to card/nfc for crypto operations" - "The serial number of a divert-to-card key must be 16 bytes! This is a programming error, please file a bug report!" + "Will divert to smart card for crypto operations" + "The serial number of a 'divert-to-card' key must be 16 bytes! This is a programming error, please file a bug report!" "Encoding exception!" "Actual key fingerprint does not match the expected one!" "No key ID. This is an internal error, please file a bug report!" @@ -925,6 +925,12 @@ "Internal OpenPGP error!" "Signature exception!" "Tried to operate on missing subkey %s!" +<<<<<<< HEAD +======= + "Cannot move key to smart card in same operation that creates an on-card signature." + "Smart card supports only one slot per key type." + "Inappropriate key flags for smart card key." +>>>>>>> 0f52097... Improve strings "Modifying master certifications" "Adding empty notation packet" "Adding PIN notation packet" @@ -936,13 +942,18 @@ "Generating new certificate for new primary user ID" "Changing to restricted operation mode" "Modifying subkey %s" - "Diverting to card/nfc for crypto operations" + "Diverting to smart card for crypto operations" "Password required for operations" "Adding new subkey of type %s" "New subkey ID: %s" "Expiry date cannot be in the past!" "Revoking subkey %s" "Stripping subkey %s" +<<<<<<< HEAD +======= + "Moving subkey %s to smart card" + "Moved %1$s to smart card %2$s" +>>>>>>> 0f52097... Improve strings "Keyring successfully modified" "Adding user ID %s" "Changing primary user ID to %s" @@ -997,11 +1008,11 @@ "Promoting public key to secret key" - "Promoting all subkeysp" + "Promoting all subkeys" "Key not found!" "Fetching key to modify (%s)" "Promoting subkey: %s" - "Subkey not on Yubikey: %s" + "Subkey not on YubiKey: %s" "Key successfully promoted" @@ -1117,7 +1128,7 @@ "Error unlocking master key!" "Certifying keyrings" "Fetching certifying master key" - "Returning for NFC input" + "Returning to NFC screen" "Saving certified key %s" "Saving keyrings" "Unlocking master key" @@ -1217,6 +1228,11 @@ "Import key from file" "Use YubiKey NEO" "Skip Setup" +<<<<<<< HEAD +======= + "Blank smart card / YubiKey detected" + "Would you like to generate a smart card compatible key?" +>>>>>>> 0f52097... Improve strings "Certifier" @@ -1234,7 +1250,7 @@ "Key not found!" "Error processing key!" "stripped" - "divert to smartcard/NFC" + "divert to smart card" "no password" "unavailable" "Your own keys can only be deleted individually!" diff --git a/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml b/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml index 08855fa22..d14f0bc40 100644 --- a/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml +++ b/OpenKeychain/src/main/res/xml/cloud_search_prefs.xml @@ -1,20 +1,21 @@ - - - + android:title="@string/pref_keyserver" /> + + + \ No newline at end of file From 79e356e04f8053d87fd2b1db5c2f9ea5dcd8f989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 21 May 2015 11:00:52 +0200 Subject: [PATCH 21/33] Change convertFingerprintToHex to use RuntimeException --- .../keychain/ui/util/KeyFormattingUtils.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java index 2dea885a7..47cbc88ca 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java @@ -218,14 +218,11 @@ public class KeyFormattingUtils { public static String convertFingerprintToHex(byte[] fingerprint) { // NOTE: Even though v3 keys are not imported we need to support both fingerprints for // display/comparison before import - // Also better cut of unneeded parts, e.g., for fingerprints returned from YubiKeys - if (fingerprint.length < 20) { - // v3 key fingerprint with 128 bit (MD5) - return Hex.toHexString(fingerprint, 0, 16).toLowerCase(Locale.ENGLISH); - } else { - // v4 key fingerprint with 160 bit (SHA1) - return Hex.toHexString(fingerprint, 0, 20).toLowerCase(Locale.ENGLISH); + if (fingerprint.length != 16 && fingerprint.length != 20) { + throw new RuntimeException("No valid v3 or v4 fingerprint!"); } + + return Hex.toHexString(fingerprint).toLowerCase(Locale.ENGLISH); } public static long getKeyIdFromFingerprint(byte[] fingerprint) { From 08afe42ea6b90d911709dcae6a9480eb0bf5ce5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 21 May 2015 13:32:22 +0200 Subject: [PATCH 22/33] AssertionError instead of RuntimeException --- .../sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java index 47cbc88ca..10c1aca0c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java @@ -219,7 +219,7 @@ public class KeyFormattingUtils { // NOTE: Even though v3 keys are not imported we need to support both fingerprints for // display/comparison before import if (fingerprint.length != 16 && fingerprint.length != 20) { - throw new RuntimeException("No valid v3 or v4 fingerprint!"); + throw new AssertionError("No valid v3 or v4 fingerprint!"); } return Hex.toHexString(fingerprint).toLowerCase(Locale.ENGLISH); From e378c478145df32a981f7b3adbacad5950b3c6cc Mon Sep 17 00:00:00 2001 From: Yoshi64Bit Date: Sat, 23 May 2015 07:49:45 +0200 Subject: [PATCH 23/33] highlight currently selected item in navigation drawer --- .../keychain/ui/MainActivity.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index e63e28667..008433f78 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -44,6 +44,12 @@ import org.sufficientlysecure.keychain.util.Preferences; public class MainActivity extends BaseNfcActivity implements FabContainer { + private static final int ID_KEYS = 1; + private static final int ID_ENCRYPT_DECRYPT = 2; + private static final int ID_APPS = 3; + private static final int ID_SETTINGS = 4; + private static final int ID_HELP = 5; + public Drawer.Result mDrawerResult; private Toolbar mToolbar; @@ -62,16 +68,16 @@ public class MainActivity extends BaseNfcActivity implements FabContainer { .withToolbar(mToolbar) .addDrawerItems( new PrimaryDrawerItem().withName(R.string.nav_keys).withIcon(CommunityMaterial.Icon.cmd_key) - .withIdentifier(1).withCheckable(false), + .withIdentifier(ID_KEYS).withCheckable(false), new PrimaryDrawerItem().withName(R.string.nav_encrypt_decrypt).withIcon(FontAwesome.Icon.faw_lock) - .withIdentifier(2).withCheckable(false), + .withIdentifier(ID_ENCRYPT_DECRYPT).withCheckable(false), new PrimaryDrawerItem().withName(R.string.title_api_registered_apps).withIcon(CommunityMaterial.Icon.cmd_apps) - .withIdentifier(3).withCheckable(false) + .withIdentifier(ID_APPS).withCheckable(false) ) .addStickyDrawerItems( // display and stick on bottom of drawer - new PrimaryDrawerItem().withName(R.string.menu_preferences).withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(4).withCheckable(false), - new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(5).withCheckable(false) + new PrimaryDrawerItem().withName(R.string.menu_preferences).withIcon(GoogleMaterial.Icon.gmd_settings).withIdentifier(ID_SETTINGS).withCheckable(false), + new PrimaryDrawerItem().withName(R.string.menu_help).withIcon(CommunityMaterial.Icon.cmd_help_circle).withIdentifier(ID_HELP).withCheckable(false) ) .withOnDrawerItemClickListener(new Drawer.OnDrawerItemClickListener() { @Override @@ -79,19 +85,19 @@ public class MainActivity extends BaseNfcActivity implements FabContainer { if (drawerItem != null) { Intent intent = null; switch(drawerItem.getIdentifier()) { - case 1: + case ID_KEYS: onKeysSelected(); break; - case 2: + case ID_ENCRYPT_DECRYPT: onEnDecryptSelected(); break; - case 3: + case ID_APPS: onAppsSelected(); break; - case 4: + case ID_SETTINGS: intent = new Intent(MainActivity.this, SettingsActivity.class); break; - case 5: + case ID_HELP: intent = new Intent(MainActivity.this, HelpActivity.class); break; } @@ -145,18 +151,21 @@ public class MainActivity extends BaseNfcActivity implements FabContainer { private void onKeysSelected() { mToolbar.setTitle(R.string.app_name); + mDrawerResult.setSelectionByIdentifier(ID_KEYS, false); Fragment frag = new KeyListFragment(); setFragment(frag, false); } private void onEnDecryptSelected() { mToolbar.setTitle(R.string.nav_encrypt_decrypt); + mDrawerResult.setSelectionByIdentifier(ID_ENCRYPT_DECRYPT, false); Fragment frag = new EncryptDecryptOverviewFragment(); setFragment(frag, true); } private void onAppsSelected() { mToolbar.setTitle(R.string.nav_apps); + mDrawerResult.setSelectionByIdentifier(ID_APPS, false); Fragment frag = new AppsListFragment(); setFragment(frag, true); } From e03e1e5cfcc19b3898b3a6397f2d429c7779d343 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 27 May 2015 18:10:18 +0200 Subject: [PATCH 24/33] fix nullpointer in encrypttextactivity. fixes #1267 --- .../keychain/ui/EncryptTextActivity.java | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index 52d098adc..82f057895 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -59,22 +59,7 @@ public class EncryptTextActivity extends BaseActivity implements } }, false); - // Handle intent actions - handleActions(getIntent(), savedInstanceState); - } - - @Override - protected void initLayout() { - setContentView(R.layout.encrypt_text_activity); - } - - - /** - * Handles all actions with this intent - * - * @param intent - */ - private void handleActions(Intent intent, Bundle savedInstanceState) { + Intent intent = getIntent(); String action = intent.getAction(); Bundle extras = intent.getExtras(); String type = intent.getType(); @@ -83,10 +68,6 @@ public class EncryptTextActivity extends BaseActivity implements extras = new Bundle(); } - /* - * Android's Action - */ - // When sending to OpenKeychain Encrypt via share menu if (Intent.ACTION_SEND.equals(action) && type != null) { Log.logDebugBundle(extras, "extras"); @@ -127,6 +108,11 @@ public class EncryptTextActivity extends BaseActivity implements } } + @Override + protected void initLayout() { + setContentView(R.layout.encrypt_text_activity); + } + @Override public void onModeChanged(boolean symmetric) { // switch fragments @@ -142,21 +128,29 @@ public class EncryptTextActivity extends BaseActivity implements @Override public void onSignatureKeyIdChanged(long signatureKeyId) { - mEncryptFragment.setSigningKeyId(signatureKeyId); + if (mEncryptFragment != null) { + mEncryptFragment.setSigningKeyId(signatureKeyId); + } } @Override public void onEncryptionKeyIdsChanged(long[] encryptionKeyIds) { - mEncryptFragment.setEncryptionKeyIds(encryptionKeyIds); + if (mEncryptFragment != null) { + mEncryptFragment.setEncryptionKeyIds(encryptionKeyIds); + } } @Override public void onEncryptionUserIdsChanged(String[] encryptionUserIds) { - mEncryptFragment.setEncryptionUserIds(encryptionUserIds); + if (mEncryptFragment != null) { + mEncryptFragment.setEncryptionUserIds(encryptionUserIds); + } } @Override public void onPassphraseChanged(Passphrase passphrase) { - mEncryptFragment.setSymmetricPassphrase(passphrase); + if (mEncryptFragment != null) { + mEncryptFragment.setSymmetricPassphrase(passphrase); + } } } From 8f3e0a91106b26e0e32451d2f39724155fd2ecc6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 27 May 2015 21:15:36 +0200 Subject: [PATCH 25/33] rewrite EncryptActivity data flow --- .../keychain/ui/CertifyKeyFragment.java | 16 +- .../keychain/ui/EncryptActivity.java | 77 ++++++++ .../keychain/ui/EncryptFilesActivity.java | 81 +------- .../keychain/ui/EncryptFilesFragment.java | 175 +++++++----------- .../ui/EncryptModeAsymmetricFragment.java | 111 ++++------- .../keychain/ui/EncryptModeFragment.java | 19 ++ .../ui/EncryptModeSymmetricFragment.java | 92 ++++----- .../keychain/ui/EncryptTextActivity.java | 70 +------ .../keychain/ui/EncryptTextFragment.java | 175 ++++++++---------- .../keychain/ui/widget/KeySpinner.java | 25 ++- 10 files changed, 355 insertions(+), 486 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeFragment.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index 59623a610..df755edef 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -77,8 +77,6 @@ public class CertifyKeyFragment extends CryptoOperationFragment private long[] mPubMasterKeyIds; - private long mSignMasterKeyId = Constants.key.none; - public static final String[] USER_IDS_PROJECTION = new String[]{ UserPackets._ID, UserPackets.MASTER_KEY_ID, @@ -149,19 +147,13 @@ public class CertifyKeyFragment extends CryptoOperationFragment vActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), PorterDuff.Mode.SRC_IN); - mCertifyKeySpinner.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() { - @Override - public void onKeyChanged(long masterKeyId) { - mSignMasterKeyId = masterKeyId; - } - }); - View vCertifyButton = view.findViewById(R.id.certify_key_certify_button); vCertifyButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - if (mSignMasterKeyId == Constants.key.none) { + long selectedKeyId = mCertifyKeySpinner.getSelectedKeyId(); + if (selectedKeyId == Constants.key.none) { Notify.create(getActivity(), getString(R.string.select_key_to_certify), Notify.Style.ERROR).show(); } else { @@ -307,8 +299,10 @@ public class CertifyKeyFragment extends CryptoOperationFragment Bundle data = new Bundle(); { + long selectedKeyId = mCertifyKeySpinner.getSelectedKeyId(); + // fill values for this action - CertifyActionsParcel parcel = new CertifyActionsParcel(mSignMasterKeyId); + CertifyActionsParcel parcel = new CertifyActionsParcel(selectedKeyId); parcel.mCertifyActions.addAll(certifyActions); data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java new file mode 100644 index 000000000..9fd3a4988 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2015 Vincent Breitmoser + * + * 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 . + */ + +package org.sufficientlysecure.keychain.ui; + +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.base.BaseActivity; + +public class EncryptActivity extends BaseActivity { + + // preselect ids, for internal use + public static final String EXTRA_SIGNATURE_KEY_ID = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_ID"; + public static final String EXTRA_ENCRYPTION_KEY_IDS = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_IDS"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + Bundle extras = intent.getExtras(); + + if (extras == null) { + extras = new Bundle(); + } + + if (savedInstanceState == null) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + + // preselect keys given by intent + long signingKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); + long[] encryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); + + Fragment modeFragment = EncryptModeAsymmetricFragment.newInstance(signingKeyId, encryptionKeyIds); + transaction.replace(R.id.encrypt_mode_container, modeFragment); + transaction.commit(); + } + } + + public void toggleModeFragment() { + boolean symmetric = getModeFragment() instanceof EncryptModeAsymmetricFragment; + + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.replace(R.id.encrypt_mode_container, + symmetric + ? EncryptModeSymmetricFragment.newInstance() + : EncryptModeAsymmetricFragment.newInstance(0, null) + ); + + // doesn't matter if the user doesn't look at the activity + transaction.commitAllowingStateLoss(); + } + + public EncryptModeFragment getModeFragment() { + return (EncryptModeFragment) + getSupportFragmentManager().findFragmentById(R.id.encrypt_mode_container); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java index b3ec60890..45700f978 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java @@ -18,39 +18,25 @@ package org.sufficientlysecure.keychain.ui; -import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.view.View; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.intents.OpenKeychainIntents; -import org.sufficientlysecure.keychain.ui.base.BaseActivity; -import org.sufficientlysecure.keychain.util.Passphrase; import java.util.ArrayList; -public class EncryptFilesActivity extends BaseActivity implements - EncryptModeAsymmetricFragment.IAsymmetric, EncryptModeSymmetricFragment.ISymmetric, - EncryptFilesFragment.IMode { +public class EncryptFilesActivity extends EncryptActivity { - /* Intents */ + // Intents public static final String ACTION_ENCRYPT_DATA = OpenKeychainIntents.ENCRYPT_DATA; // enables ASCII Armor for file encryption when uri is given public static final String EXTRA_ASCII_ARMOR = OpenKeychainIntents.ENCRYPT_EXTRA_ASCII_ARMOR; - // preselect ids, for internal use - public static final String EXTRA_SIGNATURE_KEY_ID = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_ID"; - public static final String EXTRA_ENCRYPTION_KEY_IDS = Constants.EXTRA_PREFIX + "EXTRA_ENCRYPTION_IDS"; - - Fragment mModeFragment; - EncryptFilesFragment mEncryptFragment; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -62,19 +48,7 @@ public class EncryptFilesActivity extends BaseActivity implements } }, false); - // Handle intent actions - handleActions(getIntent(), savedInstanceState); - } - - @Override - protected void initLayout() { - setContentView(R.layout.encrypt_files_activity); - } - - /** - * Handles all actions with this intent - */ - private void handleActions(Intent intent, Bundle savedInstanceState) { + Intent intent = getIntent(); String action = intent.getAction(); Bundle extras = intent.getExtras(); String type = intent.getType(); @@ -88,10 +62,6 @@ public class EncryptFilesActivity extends BaseActivity implements uris.add(intent.getData()); } - /* - * Android's Action - */ - // When sending to OpenKeychain Encrypt via share menu if (Intent.ACTION_SEND.equals(action) && type != null) { // Files via content provider, override uri and action @@ -103,56 +73,21 @@ public class EncryptFilesActivity extends BaseActivity implements uris = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM); } - long mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); - long[] mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); boolean useArmor = extras.getBoolean(EXTRA_ASCII_ARMOR, false); if (savedInstanceState == null) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - mModeFragment = EncryptModeAsymmetricFragment.newInstance(mSigningKeyId, mEncryptionKeyIds); - transaction.replace(R.id.encrypt_mode_container, mModeFragment, "mode"); - - mEncryptFragment = EncryptFilesFragment.newInstance(uris, useArmor); - transaction.replace(R.id.encrypt_file_container, mEncryptFragment, "files"); - + EncryptFilesFragment encryptFragment = EncryptFilesFragment.newInstance(uris, useArmor); + transaction.replace(R.id.encrypt_file_container, encryptFragment); transaction.commit(); - - getSupportFragmentManager().executePendingTransactions(); } + } @Override - public void onModeChanged(boolean symmetric) { - // switch fragments - getSupportFragmentManager().beginTransaction() - .replace(R.id.encrypt_mode_container, - symmetric - ? EncryptModeSymmetricFragment.newInstance() - : EncryptModeAsymmetricFragment.newInstance(0, null) - ) - .commitAllowingStateLoss(); - getSupportFragmentManager().executePendingTransactions(); - } - - @Override - public void onSignatureKeyIdChanged(long signatureKeyId) { - mEncryptFragment.setSigningKeyId(signatureKeyId); - } - - @Override - public void onEncryptionKeyIdsChanged(long[] encryptionKeyIds) { - mEncryptFragment.setEncryptionKeyIds(encryptionKeyIds); - } - - @Override - public void onEncryptionUserIdsChanged(String[] encryptionUserIds) { - mEncryptFragment.setEncryptionUserIds(encryptionUserIds); - } - - @Override - public void onPassphraseChanged(Passphrase passphrase) { - mEncryptFragment.setPassphrase(passphrase); + protected void initLayout() { + setContentView(R.layout.encrypt_files_activity); } } 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 458810541..fa35cc9ac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -72,19 +72,12 @@ import java.util.Set; public class EncryptFilesFragment extends CryptoOperationFragment { - public interface IMode { - public void onModeChanged(boolean symmetric); - } - public static final String ARG_USE_ASCII_ARMOR = "use_ascii_armor"; public static final String ARG_URIS = "uris"; private static final int REQUEST_CODE_INPUT = 0x00007003; private static final int REQUEST_CODE_OUTPUT = 0x00007007; - private IMode mModeInterface; - - private boolean mSymmetricMode = false; private boolean mUseArmor = false; private boolean mUseCompression = true; private boolean mDeleteAfterEncrypt = false; @@ -92,11 +85,6 @@ public class EncryptFilesFragment extends CryptoOperationFragment { private boolean mEncryptFilenames = true; private boolean mHiddenRecipients = false; - private long mEncryptionKeyIds[] = null; - private String mEncryptionUserIds[] = null; - private long mSigningKeyId = Constants.key.none; - private Passphrase mPassphrase = new Passphrase(); - private ArrayList mOutputUris = new ArrayList<>(); private RecyclerView mSelectedFiles; @@ -118,29 +106,11 @@ public class EncryptFilesFragment extends CryptoOperationFragment { return frag; } - public void setEncryptionKeyIds(long[] encryptionKeyIds) { - mEncryptionKeyIds = encryptionKeyIds; - } - - public void setEncryptionUserIds(String[] encryptionUserIds) { - mEncryptionUserIds = encryptionUserIds; - } - - public void setSigningKeyId(long signingKeyId) { - mSigningKeyId = signingKeyId; - } - - public void setPassphrase(Passphrase passphrase) { - mPassphrase = passphrase; - } - @Override public void onAttach(Activity activity) { super.onAttach(activity); - try { - mModeInterface = (IMode) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity + " must be IMode"); + if ( ! (activity instanceof EncryptActivity) ) { + throw new AssertionError(activity + " must inherit from EncryptionActivity"); } } @@ -293,8 +263,8 @@ public class EncryptFilesFragment extends CryptoOperationFragment { break; } case R.id.check_use_symmetric: { - mSymmetricMode = item.isChecked(); - mModeInterface.onModeChanged(mSymmetricMode); + EncryptActivity encryptActivity = (EncryptActivity) getActivity(); + encryptActivity.toggleModeFragment(); break; } case R.id.check_use_armor: { @@ -325,53 +295,6 @@ public class EncryptFilesFragment extends CryptoOperationFragment { return true; } - protected boolean inputIsValid() { - // file checks - - if (mFilesModels.isEmpty()) { - Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR) - .show(this); - return false; - } else if (mFilesModels.size() > 1 && !mShareAfterEncrypt) { - Log.e(Constants.TAG, "Aborting: mInputUris.size() > 1 && !mShareAfterEncrypt"); - // This should be impossible... - return false; - } else if (mFilesModels.size() != mOutputUris.size()) { - Log.e(Constants.TAG, "Aborting: mInputUris.size() != mOutputUris.size()"); - // This as well - return false; - } - - if (mSymmetricMode) { - // symmetric encryption checks - - if (mPassphrase == null) { - Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) - .show(this); - return false; - } - if (mPassphrase.isEmpty()) { - Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) - .show(this); - return false; - } - - } else { - // asymmetric encryption checks - - boolean gotEncryptionKeys = (mEncryptionKeyIds != null - && mEncryptionKeyIds.length > 0); - - // Files must be encrypted, only text can be signed-only right now - if (!gotEncryptionKeys) { - Notify.create(getActivity(), R.string.select_encryption_key, Notify.Style.ERROR) - .show(this); - return false; - } - } - return true; - } - public void onEncryptSuccess(final SignEncryptResult result) { if (mDeleteAfterEncrypt) { DeleteFileDialogFragment deleteFileDialog = @@ -403,6 +326,21 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } protected SignEncryptParcel createEncryptBundle() { + + if (mFilesModels.isEmpty()) { + Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR) + .show(this); + return null; + } else if (mFilesModels.size() > 1 && !mShareAfterEncrypt) { + Log.e(Constants.TAG, "Aborting: mInputUris.size() > 1 && !mShareAfterEncrypt"); + // This should be impossible... + return null; + } else if (mFilesModels.size() != mOutputUris.size()) { + Log.e(Constants.TAG, "Aborting: mInputUris.size() != mOutputUris.size()"); + // This as well + return null; + } + // fill values for this action SignEncryptParcel data = new SignEncryptParcel(); @@ -419,17 +357,39 @@ public class EncryptFilesFragment extends CryptoOperationFragment { data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); - if (mSymmetricMode) { - Log.d(Constants.TAG, "Symmetric encryption enabled!"); - Passphrase passphrase = mPassphrase; + EncryptActivity encryptActivity = (EncryptActivity) getActivity(); + EncryptModeFragment modeFragment = encryptActivity.getModeFragment(); + + if (modeFragment.isAsymmetric()) { + long[] encryptionKeyIds = modeFragment.getAsymmetricEncryptionKeyIds(); + long signingKeyId = modeFragment.getAsymmetricSigningKeyId(); + + boolean gotEncryptionKeys = (encryptionKeyIds != null + && encryptionKeyIds.length > 0); + + if (!gotEncryptionKeys && signingKeyId == 0) { + Notify.create(getActivity(), R.string.select_encryption_or_signature_key, Notify.Style.ERROR) + .show(this); + return null; + } + + data.setEncryptionMasterKeyIds(encryptionKeyIds); + data.setSignatureMasterKeyId(signingKeyId); + } else { + Passphrase passphrase = modeFragment.getSymmetricPassphrase(); + if (passphrase == null) { + Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) + .show(this); + return null; + } if (passphrase.isEmpty()) { - passphrase = null; + Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) + .show(this); + return null; } data.setSymmetricPassphrase(passphrase); - } else { - data.setEncryptionMasterKeyIds(mEncryptionKeyIds); - data.setSignatureMasterKeyId(mSigningKeyId); } + return data; } @@ -461,16 +421,27 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } sendIntent.setType(Constants.ENCRYPTED_FILES_MIME); - if (!mSymmetricMode && mEncryptionUserIds != null) { - Set users = new HashSet<>(); - for (String user : mEncryptionUserIds) { - KeyRing.UserId userId = KeyRing.splitUserId(user); - if (userId.email != null) { - users.add(userId.email); - } - } - sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); + EncryptActivity modeInterface = (EncryptActivity) getActivity(); + EncryptModeFragment modeFragment = modeInterface.getModeFragment(); + if (!modeFragment.isAsymmetric()) { + return sendIntent; } + + String[] encryptionUserIds = modeFragment.getAsymmetricEncryptionUserIds(); + if (encryptionUserIds == null) { + return sendIntent; + } + + Set users = new HashSet<>(); + for (String user : encryptionUserIds) { + KeyRing.UserId userId = KeyRing.splitUserId(user); + if (userId.email != null) { + users.add(userId.email); + } + } + // pass trough email addresses as extra for email applications + sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); + return sendIntent; } @@ -482,19 +453,17 @@ public class EncryptFilesFragment extends CryptoOperationFragment { @Override protected void cryptoOperation(CryptoInputParcel cryptoInput) { - if (!inputIsValid()) { + final SignEncryptParcel input = createEncryptBundle(); + // this is null if invalid, just return in that case + if (input == null) { // Notify was created by inputIsValid. - Log.d(Constants.TAG, "Input not valid!"); return; } - Log.d(Constants.TAG, "Input valid!"); // Send all information needed to service to edit key in other thread Intent intent = new Intent(getActivity(), KeychainIntentService.class); intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); - final SignEncryptParcel input = createEncryptBundle(); - Bundle data = new Bundle(); data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, input); data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); @@ -727,7 +696,7 @@ public class EncryptFilesFragment extends CryptoOperationFragment { for (Uri inputUri : inputUris) { ViewModel newModel = new ViewModel(mActivity, inputUri); if (mDataset.contains(newModel)) { - Log.e(Constants.TAG, "Skipped duplicate " + inputUri.toString()); + Log.e(Constants.TAG, "Skipped duplicate " + inputUri); } else { mDataset.add(newModel); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java index 6f56f2dc4..758d5f51a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java @@ -17,18 +17,13 @@ package org.sufficientlysecure.keychain.ui; -import android.app.Activity; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.tokenautocomplete.TokenCompleteTextView; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; @@ -39,38 +34,19 @@ import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem; import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView; import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -public class EncryptModeAsymmetricFragment extends Fragment { - - public interface IAsymmetric { - - public void onSignatureKeyIdChanged(long signatureKeyId); - - public void onEncryptionKeyIdsChanged(long[] encryptionKeyIds); - - public void onEncryptionUserIdsChanged(String[] encryptionUserIds); - } +public class EncryptModeAsymmetricFragment extends EncryptModeFragment { ProviderHelper mProviderHelper; - // view - private KeySpinner mSign; + private KeySpinner mSignKeySpinner; private EncryptKeyCompletionView mEncryptKeyView; - // model - private IAsymmetric mEncryptInterface; - -// @Override -// public void updateUi() { -// if (mSign != null) { -// mSign.setSelectedKeyId(mEncryptInterface.getSignatureKey()); -// } -// } - public static final String ARG_SINGATURE_KEY_ID = "signature_key_id"; public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids"; @@ -89,16 +65,6 @@ public class EncryptModeAsymmetricFragment extends Fragment { return frag; } - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - try { - mEncryptInterface = (IAsymmetric) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity + " must implement IAsymmetric"); - } - } - /** * Inflate the layout for this fragment */ @@ -106,13 +72,7 @@ public class EncryptModeAsymmetricFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false); - mSign = (KeySpinner) view.findViewById(R.id.sign); - mSign.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() { - @Override - public void onKeyChanged(long masterKeyId) { - mEncryptInterface.onSignatureKeyIdChanged(masterKeyId); - } - }); + mSignKeySpinner = (KeySpinner) view.findViewById(R.id.sign); mEncryptKeyView = (EncryptKeyCompletionView) view.findViewById(R.id.recipient_list); mEncryptKeyView.setThreshold(1); // Start working from first character @@ -128,22 +88,6 @@ public class EncryptModeAsymmetricFragment extends Fragment { long signatureKeyId = getArguments().getLong(ARG_SINGATURE_KEY_ID); long[] encryptionKeyIds = getArguments().getLongArray(ARG_ENCRYPTION_KEY_IDS); preselectKeys(signatureKeyId, encryptionKeyIds); - - mEncryptKeyView.setTokenListener(new TokenCompleteTextView.TokenListener() { - @Override - public void onTokenAdded(Object token) { - if (token instanceof KeyItem) { - updateEncryptionKeys(); - } - } - - @Override - public void onTokenRemoved(Object token) { - if (token instanceof KeyItem) { - updateEncryptionKeys(); - } - } - }); } /** @@ -155,8 +99,7 @@ public class EncryptModeAsymmetricFragment extends Fragment { CachedPublicKeyRing keyring = mProviderHelper.getCachedPublicKeyRing( KeyRings.buildUnifiedKeyRingUri(signatureKeyId)); if (keyring.hasAnySecret()) { - mEncryptInterface.onSignatureKeyIdChanged(keyring.getMasterKeyId()); - mSign.setSelectedKeyId(signatureKeyId); + mSignKeySpinner.setSelectedKeyId(signatureKeyId); } } catch (PgpKeyNotFoundException e) { Log.e(Constants.TAG, "key not found!", e); @@ -175,27 +118,55 @@ public class EncryptModeAsymmetricFragment extends Fragment { } // This is to work-around a rendering bug in TokenCompleteTextView mEncryptKeyView.requestFocus(); - updateEncryptionKeys(); } } - private void updateEncryptionKeys() { - List objects = mEncryptKeyView.getObjects(); + @Override + public boolean isAsymmetric() { + return true; + } + + @Override + public long getAsymmetricSigningKeyId() { + return mSignKeySpinner.getSelectedItemId(); + } + + @Override + public long[] getAsymmetricEncryptionKeyIds() { List keyIds = new ArrayList<>(); - List userIds = new ArrayList<>(); - for (Object object : objects) { + for (Object object : mEncryptKeyView.getObjects()) { if (object instanceof KeyItem) { keyIds.add(((KeyItem) object).mKeyId); - userIds.add(((KeyItem) object).mUserIdFull); } } + long[] keyIdsArr = new long[keyIds.size()]; Iterator iterator = keyIds.iterator(); for (int i = 0; i < keyIds.size(); i++) { keyIdsArr[i] = iterator.next(); } - mEncryptInterface.onEncryptionKeyIdsChanged(keyIdsArr); - mEncryptInterface.onEncryptionUserIdsChanged(userIds.toArray(new String[userIds.size()])); + + return keyIdsArr; } + + @Override + public String[] getAsymmetricEncryptionUserIds() { + + List userIds = new ArrayList<>(); + for (Object object : mEncryptKeyView.getObjects()) { + if (object instanceof KeyItem) { + userIds.add(((KeyItem) object).mUserIdFull); + } + } + + return userIds.toArray(new String[userIds.size()]); + + } + + @Override + public Passphrase getSymmetricPassphrase() { + throw new UnsupportedOperationException("should never happen, this is a programming error!"); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeFragment.java new file mode 100644 index 000000000..0b9672654 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeFragment.java @@ -0,0 +1,19 @@ +package org.sufficientlysecure.keychain.ui; + + +import android.support.v4.app.Fragment; + +import org.sufficientlysecure.keychain.util.Passphrase; + + +public abstract class EncryptModeFragment extends Fragment { + + public abstract boolean isAsymmetric(); + + public abstract long getAsymmetricSigningKeyId(); + public abstract long[] getAsymmetricEncryptionKeyIds(); + public abstract String[] getAsymmetricEncryptionUserIds(); + + public abstract Passphrase getSymmetricPassphrase(); + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java index 48b1f4983..b92a73731 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeSymmetricFragment.java @@ -17,11 +17,7 @@ package org.sufficientlysecure.keychain.ui; -import android.app.Activity; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.text.Editable; -import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -30,14 +26,7 @@ import android.widget.EditText; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.Passphrase; -public class EncryptModeSymmetricFragment extends Fragment { - - public interface ISymmetric { - - public void onPassphraseChanged(Passphrase passphrase); - } - - private ISymmetric mEncryptInterface; +public class EncryptModeSymmetricFragment extends EncryptModeFragment { private EditText mPassphrase; private EditText mPassphraseAgain; @@ -54,53 +43,54 @@ public class EncryptModeSymmetricFragment extends Fragment { return frag; } - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - try { - mEncryptInterface = (ISymmetric) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement ISymmetric"); - } - } - - /** - * Inflate the layout for this fragment - */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_symmetric_fragment, container, false); mPassphrase = (EditText) view.findViewById(R.id.passphrase); mPassphraseAgain = (EditText) view.findViewById(R.id.passphraseAgain); - TextWatcher textWatcher = new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - // update passphrase in EncryptActivity - Passphrase p1 = new Passphrase(mPassphrase.getText()); - Passphrase p2 = new Passphrase(mPassphraseAgain.getText()); - boolean passesEquals = (p1.equals(p2)); - p1.removeFromMemory(); - p2.removeFromMemory(); - if (passesEquals) { - mEncryptInterface.onPassphraseChanged(new Passphrase(mPassphrase.getText())); - } else { - mEncryptInterface.onPassphraseChanged(null); - } - } - }; - mPassphrase.addTextChangedListener(textWatcher); - mPassphraseAgain.addTextChangedListener(textWatcher); return view; } + @Override + public boolean isAsymmetric() { + return false; + } + + @Override + public long getAsymmetricSigningKeyId() { + throw new UnsupportedOperationException("should never happen, this is a programming error!"); + } + + @Override + public long[] getAsymmetricEncryptionKeyIds() { + throw new UnsupportedOperationException("should never happen, this is a programming error!"); + } + + @Override + public String[] getAsymmetricEncryptionUserIds() { + throw new UnsupportedOperationException("should never happen, this is a programming error!"); + } + + @Override + public Passphrase getSymmetricPassphrase() { + Passphrase p1 = null, p2 = null; + try { + p1 = new Passphrase(mPassphrase.getText()); + p2 = new Passphrase(mPassphraseAgain.getText()); + if (!p1.equals(p2)) { + return null; + } + return new Passphrase(mPassphrase.getText()); + } finally { + if (p1 != null) { + p1.removeFromMemory(); + } + if (p2 != null) { + p2.removeFromMemory(); + } + } + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java index 82f057895..cb26ea452 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -20,20 +20,14 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.view.View; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.intents.OpenKeychainIntents; -import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Passphrase; -public class EncryptTextActivity extends BaseActivity implements - EncryptModeAsymmetricFragment.IAsymmetric, EncryptModeSymmetricFragment.ISymmetric, - EncryptTextFragment.IMode { +public class EncryptTextActivity extends EncryptActivity { /* Intents */ public static final String ACTION_ENCRYPT_TEXT = OpenKeychainIntents.ENCRYPT_TEXT; @@ -41,13 +35,6 @@ public class EncryptTextActivity extends BaseActivity implements /* EXTRA keys for input */ public static final String EXTRA_TEXT = OpenKeychainIntents.ENCRYPT_EXTRA_TEXT; - // preselect ids, for internal use - public static final String EXTRA_SIGNATURE_KEY_ID = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_ID"; - public static final String EXTRA_ENCRYPTION_KEY_IDS = Constants.EXTRA_PREFIX + "EXTRA_SIGNATURE_KEY_IDS"; - - Fragment mModeFragment; - EncryptTextFragment mEncryptFragment; - @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -89,23 +76,14 @@ public class EncryptTextActivity extends BaseActivity implements textData = ""; } - // preselect keys given by intent - long mSigningKeyId = extras.getLong(EXTRA_SIGNATURE_KEY_ID); - long[] mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS); - if (savedInstanceState == null) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - mModeFragment = EncryptModeAsymmetricFragment.newInstance(mSigningKeyId, mEncryptionKeyIds); - transaction.replace(R.id.encrypt_mode_container, mModeFragment, "mode"); - - mEncryptFragment = EncryptTextFragment.newInstance(textData); - transaction.replace(R.id.encrypt_text_container, mEncryptFragment, "text"); - + EncryptTextFragment encryptFragment = EncryptTextFragment.newInstance(textData); + transaction.replace(R.id.encrypt_text_container, encryptFragment); transaction.commit(); - - getSupportFragmentManager().executePendingTransactions(); } + } @Override @@ -113,44 +91,4 @@ public class EncryptTextActivity extends BaseActivity implements setContentView(R.layout.encrypt_text_activity); } - @Override - public void onModeChanged(boolean symmetric) { - // switch fragments - getSupportFragmentManager().beginTransaction() - .replace(R.id.encrypt_mode_container, - symmetric - ? EncryptModeSymmetricFragment.newInstance() - : EncryptModeAsymmetricFragment.newInstance(0, null) - ) - .commitAllowingStateLoss(); - getSupportFragmentManager().executePendingTransactions(); - } - - @Override - public void onSignatureKeyIdChanged(long signatureKeyId) { - if (mEncryptFragment != null) { - mEncryptFragment.setSigningKeyId(signatureKeyId); - } - } - - @Override - public void onEncryptionKeyIdsChanged(long[] encryptionKeyIds) { - if (mEncryptFragment != null) { - mEncryptFragment.setEncryptionKeyIds(encryptionKeyIds); - } - } - - @Override - public void onEncryptionUserIdsChanged(String[] encryptionUserIds) { - if (mEncryptFragment != null) { - mEncryptFragment.setEncryptionUserIds(encryptionUserIds); - } - } - - @Override - public void onPassphraseChanged(Passphrase passphrase) { - if (mEncryptFragment != null) { - mEncryptFragment.setSymmetricPassphrase(passphrase); - } - } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index 3f9147cc4..89bc97e16 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java @@ -47,7 +47,6 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ShareHelper; @@ -56,44 +55,14 @@ import java.util.Set; public class EncryptTextFragment extends CryptoOperationFragment { - public interface IMode { - public void onModeChanged(boolean symmetric); - } - public static final String ARG_TEXT = "text"; - private IMode mModeInterface; - - private boolean mSymmetricMode = false; private boolean mShareAfterEncrypt = false; private boolean mUseCompression = true; private boolean mHiddenRecipients = false; - private long mEncryptionKeyIds[] = null; - private String mEncryptionUserIds[] = null; - // TODO Constants.key.none? What's wrong with a null value? - private long mSigningKeyId = Constants.key.none; - private Passphrase mSymmetricPassphrase = new Passphrase(); private String mMessage = ""; - private TextView mText; - - public void setEncryptionKeyIds(long[] encryptionKeyIds) { - mEncryptionKeyIds = encryptionKeyIds; - } - - public void setEncryptionUserIds(String[] encryptionUserIds) { - mEncryptionUserIds = encryptionUserIds; - } - - public void setSigningKeyId(long signingKeyId) { - mSigningKeyId = signingKeyId; - } - - public void setSymmetricPassphrase(Passphrase passphrase) { - mSymmetricPassphrase = passphrase; - } - /** * Creates new instance of this fragment */ @@ -110,10 +79,8 @@ public class EncryptTextFragment extends CryptoOperationFragment { @Override public void onAttach(Activity activity) { super.onAttach(activity); - try { - mModeInterface = (IMode) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement IMode"); + if ( ! (activity instanceof EncryptActivity) ) { + throw new AssertionError(activity + " must inherit from EncryptionActivity"); } } @@ -124,8 +91,8 @@ public class EncryptTextFragment extends CryptoOperationFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.encrypt_text_fragment, container, false); - mText = (TextView) view.findViewById(R.id.encrypt_text_text); - mText.addTextChangedListener(new TextWatcher() { + TextView textView = (TextView) view.findViewById(R.id.encrypt_text_text); + textView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { @@ -144,7 +111,7 @@ public class EncryptTextFragment extends CryptoOperationFragment { // set initial text if (mMessage != null) { - mText.setText(mMessage); + textView.setText(mMessage); } return view; @@ -171,8 +138,8 @@ public class EncryptTextFragment extends CryptoOperationFragment { } switch (item.getItemId()) { case R.id.check_use_symmetric: { - mSymmetricMode = item.isChecked(); - mModeInterface.onModeChanged(mSymmetricMode); + EncryptActivity modeInterface = (EncryptActivity) getActivity(); + modeInterface.toggleModeFragment(); break; } case R.id.check_enable_compression: { @@ -185,11 +152,11 @@ public class EncryptTextFragment extends CryptoOperationFragment { // break; // } case R.id.encrypt_copy: { - startEncrypt(false); + cryptoOperation(false); break; } case R.id.encrypt_share: { - startEncrypt(true); + cryptoOperation(true); break; } default: { @@ -199,7 +166,6 @@ public class EncryptTextFragment extends CryptoOperationFragment { return true; } - protected void onEncryptSuccess(SignEncryptResult result) { if (mShareAfterEncrypt) { // Share encrypted message/file @@ -215,6 +181,13 @@ public class EncryptTextFragment extends CryptoOperationFragment { } protected SignEncryptParcel createEncryptBundle() { + + if (mMessage == null || mMessage.isEmpty()) { + Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR) + .show(this); + return null; + } + // fill values for this action SignEncryptParcel data = new SignEncryptParcel(); @@ -227,22 +200,45 @@ public class EncryptTextFragment extends CryptoOperationFragment { data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED); } data.setHiddenRecipients(mHiddenRecipients); - data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); - data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); + data.setSymmetricEncryptionAlgorithm( + PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); + data.setSignatureHashAlgorithm( + PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED); // Always use armor for messages data.setEnableAsciiArmorOutput(true); - if (mSymmetricMode) { - Log.d(Constants.TAG, "Symmetric encryption enabled!"); - Passphrase passphrase = mSymmetricPassphrase; + EncryptActivity modeInterface = (EncryptActivity) getActivity(); + EncryptModeFragment modeFragment = modeInterface.getModeFragment(); + + if (modeFragment.isAsymmetric()) { + long[] encryptionKeyIds = modeFragment.getAsymmetricEncryptionKeyIds(); + long signingKeyId = modeFragment.getAsymmetricSigningKeyId(); + + boolean gotEncryptionKeys = (encryptionKeyIds != null + && encryptionKeyIds.length > 0); + + if (!gotEncryptionKeys && signingKeyId == 0L) { + Notify.create(getActivity(), R.string.select_encryption_or_signature_key, Notify.Style.ERROR) + .show(this); + return null; + } + + data.setEncryptionMasterKeyIds(encryptionKeyIds); + data.setSignatureMasterKeyId(signingKeyId); + } else { + Passphrase passphrase = modeFragment.getSymmetricPassphrase(); + if (passphrase == null) { + Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) + .show(this); + return null; + } if (passphrase.isEmpty()) { - passphrase = null; + Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) + .show(this); + return null; } data.setSymmetricPassphrase(passphrase); - } else { - data.setEncryptionMasterKeyIds(mEncryptionKeyIds); - data.setSignatureMasterKeyId(mSigningKeyId); } return data; } @@ -273,65 +269,41 @@ public class EncryptTextFragment extends CryptoOperationFragment { sendIntent.setType(Constants.ENCRYPTED_TEXT_MIME); sendIntent.putExtra(Intent.EXTRA_TEXT, new String(resultBytes)); - if (!mSymmetricMode && mEncryptionUserIds != null) { - Set users = new HashSet<>(); - for (String user : mEncryptionUserIds) { - KeyRing.UserId userId = KeyRing.splitUserId(user); - if (userId.email != null) { - users.add(userId.email); - } - } - // pass trough email addresses as extra for email applications - sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); + EncryptActivity modeInterface = (EncryptActivity) getActivity(); + EncryptModeFragment modeFragment = modeInterface.getModeFragment(); + if (!modeFragment.isAsymmetric()) { + return sendIntent; } + + String[] encryptionUserIds = modeFragment.getAsymmetricEncryptionUserIds(); + if (encryptionUserIds == null) { + return sendIntent; + } + + Set users = new HashSet<>(); + for (String user : encryptionUserIds) { + KeyRing.UserId userId = KeyRing.splitUserId(user); + if (userId.email != null) { + users.add(userId.email); + } + } + // pass trough email addresses as extra for email applications + sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()])); + return sendIntent; } - protected boolean inputIsValid() { - if (mMessage == null || mMessage.isEmpty()) { - Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR) - .show(this); - return false; - } - - if (mSymmetricMode) { - // symmetric encryption checks - - if (mSymmetricPassphrase == null) { - Notify.create(getActivity(), R.string.passphrases_do_not_match, Notify.Style.ERROR) - .show(this); - return false; - } - if (mSymmetricPassphrase.isEmpty()) { - Notify.create(getActivity(), R.string.passphrase_must_not_be_empty, Notify.Style.ERROR) - .show(this); - return false; - } - - } else { - // asymmetric encryption checks - - boolean gotEncryptionKeys = (mEncryptionKeyIds != null - && mEncryptionKeyIds.length > 0); - - if (!gotEncryptionKeys && mSigningKeyId == 0) { - Notify.create(getActivity(), R.string.select_encryption_or_signature_key, Notify.Style.ERROR) - .show(this); - return false; - } - } - return true; - } - - - public void startEncrypt(boolean share) { + public void cryptoOperation(boolean share) { mShareAfterEncrypt = share; cryptoOperation(); } @Override protected void cryptoOperation(CryptoInputParcel cryptoInput) { - if (!inputIsValid()) { + + final SignEncryptParcel input = createEncryptBundle(); + // this is null if invalid, just return in that case + if (input == null) { // Notify was created by inputIsValid. return; } @@ -340,7 +312,6 @@ public class EncryptTextFragment extends CryptoOperationFragment { Intent intent = new Intent(getActivity(), KeychainIntentService.class); intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); - final SignEncryptParcel input = createEncryptBundle(); final Bundle data = new Bundle(); data.putParcelable(KeychainIntentService.SIGN_ENCRYPT_PARCEL, input); data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java index aecc81604..579f37db2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -20,12 +20,14 @@ package org.sufficientlysecure.keychain.ui.widget; import android.content.Context; import android.database.Cursor; import android.graphics.Color; +import android.os.Bundle; +import android.os.Parcelable; +import android.support.annotation.NonNull; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v4.widget.CursorAdapter; import android.support.v7.widget.AppCompatSpinner; -import android.text.format.DateFormat; import android.text.format.DateUtils; import android.util.AttributeSet; import android.view.View; @@ -42,17 +44,18 @@ import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.util.Log; -import java.util.Calendar; -import java.util.Date; -import java.util.TimeZone; - /** * Use AppCompatSpinner from AppCompat lib instead of Spinner. Fixes white dropdown icon. * Related: http://stackoverflow.com/a/27713090 */ -public abstract class KeySpinner extends AppCompatSpinner implements LoaderManager.LoaderCallbacks { +public abstract class KeySpinner extends AppCompatSpinner implements + LoaderManager.LoaderCallbacks { + + public static final String ARG_SUPER_STATE = "super_state"; + public static final String ARG_SELECTED_KEY_ID = "select_key_id"; + public interface OnKeyChangedListener { - public void onKeyChanged(long masterKeyId); + void onKeyChanged(long masterKeyId); } protected long mSelectedKeyId = Constants.key.none; @@ -82,15 +85,17 @@ public abstract class KeySpinner extends AppCompatSpinner implements LoaderManag super.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { + mSelectedKeyId = id; if (mListener != null) { - mListener.onKeyChanged(id); + mListener.onKeyChanged(mSelectedKeyId); } } @Override public void onNothingSelected(AdapterView parent) { + mSelectedKeyId = Constants.key.none; if (mListener != null) { - mListener.onKeyChanged(Constants.key.none); + mListener.onKeyChanged(mSelectedKeyId); } } }); @@ -138,7 +143,7 @@ public abstract class KeySpinner extends AppCompatSpinner implements LoaderManag } public void setSelectedKeyId(long selectedKeyId) { - this.mSelectedKeyId = selectedKeyId; + mSelectedKeyId = selectedKeyId; } protected class SelectKeyAdapter extends BaseAdapter implements SpinnerAdapter { From 7c20c8df932e108c4156a316ddc68ff3610bba4e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 27 May 2015 22:07:34 +0200 Subject: [PATCH 26/33] preserve state in KeySpinner, and some lint fixes --- .../remote/ui/AccountSettingsFragment.java | 4 +- .../keychain/ui/CertifyKeyFragment.java | 3 +- .../ui/EncryptModeAsymmetricFragment.java | 15 ++++-- .../keychain/ui/widget/CertifyKeySpinner.java | 2 +- .../keychain/ui/widget/KeySpinner.java | 46 ++++++++++++++----- 5 files changed, 48 insertions(+), 22 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java index 81181d61d..18afd2f23 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java @@ -58,7 +58,7 @@ public class AccountSettingsFragment extends Fragment { this.mAccSettings = accountSettings; mAccNameView.setText(accountSettings.getAccountName()); - mSelectKeySpinner.setSelectedKeyId(accountSettings.getKeyId()); + mSelectKeySpinner.setPreSelectedKeyId(accountSettings.getKeyId()); } /** @@ -107,7 +107,7 @@ public class AccountSettingsFragment extends Fragment { if (resultCode == Activity.RESULT_OK) { if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { EditKeyResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); - mSelectKeySpinner.setSelectedKeyId(result.mMasterKeyId); + mSelectKeySpinner.setPreSelectedKeyId(result.mMasterKeyId); } else { Log.e(Constants.TAG, "missing result!"); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index df755edef..4506d5efa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -60,7 +60,6 @@ import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; -import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; @@ -113,7 +112,7 @@ public class CertifyKeyFragment extends CryptoOperationFragment try { CachedPublicKeyRing key = (new ProviderHelper(getActivity())).getCachedPublicKeyRing(certifyKeyId); if (key.canCertify()) { - mCertifyKeySpinner.setSelectedKeyId(certifyKeyId); + mCertifyKeySpinner.setPreSelectedKeyId(certifyKeyId); } } catch (PgpKeyNotFoundException e) { Log.e(Constants.TAG, "certify certify check failed", e); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java index 758d5f51a..3b4db6c08 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java @@ -84,10 +84,15 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment { super.onActivityCreated(savedInstanceState); mProviderHelper = new ProviderHelper(getActivity()); - // preselect keys given - long signatureKeyId = getArguments().getLong(ARG_SINGATURE_KEY_ID); - long[] encryptionKeyIds = getArguments().getLongArray(ARG_ENCRYPTION_KEY_IDS); - preselectKeys(signatureKeyId, encryptionKeyIds); + // preselect keys given, from state or arguments + long signatureKeyId, encryptionKeyIds[]; + + if (savedInstanceState == null) { + signatureKeyId = getArguments().getLong(ARG_SINGATURE_KEY_ID); + encryptionKeyIds = getArguments().getLongArray(ARG_ENCRYPTION_KEY_IDS); + preselectKeys(signatureKeyId, encryptionKeyIds); + } + } /** @@ -99,7 +104,7 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment { CachedPublicKeyRing keyring = mProviderHelper.getCachedPublicKeyRing( KeyRings.buildUnifiedKeyRingUri(signatureKeyId)); if (keyring.hasAnySecret()) { - mSignKeySpinner.setSelectedKeyId(signatureKeyId); + mSignKeySpinner.setPreSelectedKeyId(signatureKeyId); } } catch (PgpKeyNotFoundException e) { Log.e(Constants.TAG, "key not found!", e); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java index 460163a47..845b35512 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java @@ -98,7 +98,7 @@ public class CertifyKeySpinner extends KeySpinner { // - there are actually keys (not just "none" entry) // Then: // - select key that is capable of certifying, but only if there is only one key capable of it - if (mSelectedKeyId == Constants.key.none && mAdapter.getCount() > 1) { + if (mPreSelectedKeyId == Constants.key.none && mAdapter.getCount() > 1) { // preselect if key can certify int selection = -1; while (data.moveToNext()) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java index 579f37db2..ad1a14a33 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.ui.widget; +import android.annotation.SuppressLint; import android.content.Context; import android.database.Cursor; import android.graphics.Color; @@ -52,13 +53,13 @@ public abstract class KeySpinner extends AppCompatSpinner implements LoaderManager.LoaderCallbacks { public static final String ARG_SUPER_STATE = "super_state"; - public static final String ARG_SELECTED_KEY_ID = "select_key_id"; + public static final String ARG_KEY_ID = "key_id"; public interface OnKeyChangedListener { void onKeyChanged(long masterKeyId); } - protected long mSelectedKeyId = Constants.key.none; + protected long mPreSelectedKeyId = Constants.key.none; protected SelectKeyAdapter mAdapter = new SelectKeyAdapter(); protected OnKeyChangedListener mListener; @@ -85,17 +86,15 @@ public abstract class KeySpinner extends AppCompatSpinner implements super.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView parent, View view, int position, long id) { - mSelectedKeyId = id; if (mListener != null) { - mListener.onKeyChanged(mSelectedKeyId); + mListener.onKeyChanged(id); } } @Override public void onNothingSelected(AdapterView parent) { - mSelectedKeyId = Constants.key.none; if (mListener != null) { - mListener.onKeyChanged(mSelectedKeyId); + mListener.onKeyChanged(Constants.key.none); } } }); @@ -139,11 +138,11 @@ public abstract class KeySpinner extends AppCompatSpinner implements } public long getSelectedKeyId() { - return mSelectedKeyId; + return getSelectedItemId(); } - public void setSelectedKeyId(long selectedKeyId) { - mSelectedKeyId = selectedKeyId; + public void setPreSelectedKeyId(long selectedKeyId) { + mPreSelectedKeyId = selectedKeyId; } protected class SelectKeyAdapter extends BaseAdapter implements SpinnerAdapter { @@ -232,10 +231,10 @@ public abstract class KeySpinner extends AppCompatSpinner implements mIndexMasterKeyId = newCursor.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID); mIndexCreationDate = newCursor.getColumnIndex(KeychainContract.KeyRings.CREATION); - // pre-select key if mSelectedKeyId is given - if (mSelectedKeyId != Constants.key.none && newCursor.moveToFirst()) { + // pre-select key if mPreSelectedKeyId is given + if (mPreSelectedKeyId != Constants.key.none && newCursor.moveToFirst()) { do { - if (newCursor.getLong(mIndexMasterKeyId) == mSelectedKeyId) { + if (newCursor.getLong(mIndexMasterKeyId) == mPreSelectedKeyId) { setSelection(newCursor.getPosition() + 1); } } while (newCursor.moveToNext()); @@ -260,6 +259,7 @@ public abstract class KeySpinner extends AppCompatSpinner implements return inner.getItemId(position - 1); } + @SuppressLint("ViewHolder") // inflate call is for the preview only @Override public View getView(int position, View convertView, ViewGroup parent) { try { @@ -304,4 +304,26 @@ public abstract class KeySpinner extends AppCompatSpinner implements return true; } + @Override + public void onRestoreInstanceState(Parcelable state) { + Bundle bundle = (Bundle) state; + + mPreSelectedKeyId = bundle.getLong(ARG_KEY_ID); + + // restore super state + super.onRestoreInstanceState(bundle.getParcelable(ARG_SUPER_STATE)); + + } + + @NonNull + @Override + public Parcelable onSaveInstanceState() { + Bundle bundle = new Bundle(); + + // save super state + bundle.putParcelable(ARG_SUPER_STATE, super.onSaveInstanceState()); + + bundle.putLong(ARG_KEY_ID, getSelectedKeyId()); + return bundle; + } } From 40ab96539c7658b18a0c4d95786f28fda1f69516 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 27 May 2015 22:33:14 +0200 Subject: [PATCH 27/33] move synchronous encryption into activity, and preserve checkbox state --- .../keychain/ui/EncryptActivity.java | 30 +++++++++++++++++-- .../keychain/ui/EncryptFilesFragment.java | 13 ++++---- .../keychain/ui/EncryptTextFragment.java | 5 ---- .../src/main/res/menu/encrypt_activity.xml | 9 ++++++ .../main/res/menu/encrypt_file_fragment.xml | 5 ---- .../main/res/menu/encrypt_text_fragment.xml | 5 ---- 6 files changed, 42 insertions(+), 25 deletions(-) create mode 100644 OpenKeychain/src/main/res/menu/encrypt_activity.xml diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 9fd3a4988..4361705f9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -21,6 +21,8 @@ import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; +import android.view.Menu; +import android.view.MenuItem; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -56,9 +58,33 @@ public class EncryptActivity extends BaseActivity { } } - public void toggleModeFragment() { - boolean symmetric = getModeFragment() instanceof EncryptModeAsymmetricFragment; + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.check_use_symmetric: { + item.setChecked(!item.isChecked()); + setModeFragment(item.isChecked()); + return true; + } + default: { + return super.onOptionsItemSelected(item); + } + } + } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.encrypt_activity, menu); + + Fragment frag = + getSupportFragmentManager().findFragmentById(R.id.encrypt_mode_container); + boolean isSymmetric = frag instanceof EncryptModeSymmetricFragment; + menu.findItem(R.id.check_use_symmetric).setChecked(isSymmetric); + + return super.onCreateOptionsMenu(menu); + } + + private void setModeFragment(boolean symmetric) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.encrypt_mode_container, symmetric 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 fa35cc9ac..713c03fc2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -250,9 +250,6 @@ public class EncryptFilesFragment extends CryptoOperationFragment { @Override public boolean onOptionsItemSelected(MenuItem item) { - if (item.isCheckable()) { - item.setChecked(!item.isChecked()); - } switch (item.getItemId()) { case R.id.encrypt_save: { encryptClicked(false); @@ -262,24 +259,24 @@ public class EncryptFilesFragment extends CryptoOperationFragment { encryptClicked(true); break; } - case R.id.check_use_symmetric: { - EncryptActivity encryptActivity = (EncryptActivity) getActivity(); - encryptActivity.toggleModeFragment(); - break; - } case R.id.check_use_armor: { + // we can NOT do this for every item, others might care! + item.setChecked(!item.isChecked()); mUseArmor = item.isChecked(); break; } case R.id.check_delete_after_encrypt: { + item.setChecked(!item.isChecked()); mDeleteAfterEncrypt = item.isChecked(); break; } case R.id.check_enable_compression: { + item.setChecked(!item.isChecked()); mUseCompression = item.isChecked(); break; } case R.id.check_encrypt_filenames: { + item.setChecked(!item.isChecked()); mEncryptFilenames = item.isChecked(); break; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index 89bc97e16..90eb2b1f3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java @@ -137,11 +137,6 @@ public class EncryptTextFragment extends CryptoOperationFragment { item.setChecked(!item.isChecked()); } switch (item.getItemId()) { - case R.id.check_use_symmetric: { - EncryptActivity modeInterface = (EncryptActivity) getActivity(); - modeInterface.toggleModeFragment(); - break; - } case R.id.check_enable_compression: { mUseCompression = item.isChecked(); break; diff --git a/OpenKeychain/src/main/res/menu/encrypt_activity.xml b/OpenKeychain/src/main/res/menu/encrypt_activity.xml new file mode 100644 index 000000000..fdd19927e --- /dev/null +++ b/OpenKeychain/src/main/res/menu/encrypt_activity.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml b/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml index d6ed726fa..10928e4bb 100644 --- a/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml +++ b/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml @@ -14,11 +14,6 @@ android:icon="@drawable/ic_action_encrypt_share_24dp" app:showAsAction="always" /> - - - - Date: Wed, 27 May 2015 22:55:36 +0200 Subject: [PATCH 28/33] correctly preserve state in EncryptFilesFragment --- .../keychain/ui/EncryptFilesFragment.java | 43 ++++++++++++++++--- .../keychain/ui/EncryptTextFragment.java | 2 + .../main/res/menu/encrypt_file_fragment.xml | 2 - 3 files changed, 38 insertions(+), 9 deletions(-) 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 713c03fc2..801f51b04 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -72,19 +72,23 @@ import java.util.Set; public class EncryptFilesFragment extends CryptoOperationFragment { + public static final String ARG_DELETE_AFTER_ENCRYPT = "delete_after_encrypt"; + public static final String ARG_ENCRYPT_FILENAMES = "encrypt_filenames"; + public static final String ARG_USE_COMPRESSION = "use_compression"; public static final String ARG_USE_ASCII_ARMOR = "use_ascii_armor"; public static final String ARG_URIS = "uris"; private static final int REQUEST_CODE_INPUT = 0x00007003; private static final int REQUEST_CODE_OUTPUT = 0x00007007; - private boolean mUseArmor = false; - private boolean mUseCompression = true; - private boolean mDeleteAfterEncrypt = false; - private boolean mShareAfterEncrypt = false; - private boolean mEncryptFilenames = true; + private boolean mUseArmor; + private boolean mUseCompression; + private boolean mDeleteAfterEncrypt; + private boolean mEncryptFilenames; private boolean mHiddenRecipients = false; + private boolean mShareAfterEncrypt; + private ArrayList mOutputUris = new ArrayList<>(); private RecyclerView mSelectedFiles; @@ -136,19 +140,39 @@ public class EncryptFilesFragment extends CryptoOperationFragment { } }); - ArrayList inputUris = getArguments().getParcelableArrayList(ARG_URIS); + Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState; + + ArrayList inputUris = args.getParcelableArrayList(ARG_URIS); if (inputUris != null) { mFilesAdapter.addAll(inputUris); } - mUseArmor = getArguments().getBoolean(ARG_USE_ASCII_ARMOR); mSelectedFiles.setAdapter(mFilesAdapter); return view; } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putBoolean(ARG_DELETE_AFTER_ENCRYPT, mDeleteAfterEncrypt); + outState.putBoolean(ARG_USE_ASCII_ARMOR, mUseArmor); + outState.putBoolean(ARG_USE_COMPRESSION, mUseCompression); + outState.putBoolean(ARG_ENCRYPT_FILENAMES, mEncryptFilenames); + + outState.putParcelableArrayList(ARG_URIS, mFilesAdapter.getAsArrayList()); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState; + mDeleteAfterEncrypt = args.getBoolean(ARG_DELETE_AFTER_ENCRYPT, false); + mUseArmor = args.getBoolean(ARG_USE_ASCII_ARMOR, false); + mUseCompression = args.getBoolean(ARG_USE_COMPRESSION, true); + mEncryptFilenames = args.getBoolean(ARG_ENCRYPT_FILENAMES, true); + setHasOptionsMenu(true); } @@ -246,6 +270,11 @@ public class EncryptFilesFragment extends CryptoOperationFragment { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.encrypt_file_fragment, menu); + + menu.findItem(R.id.check_delete_after_encrypt).setChecked(mDeleteAfterEncrypt); + menu.findItem(R.id.check_use_armor).setChecked(mUseArmor); + menu.findItem(R.id.check_enable_compression).setChecked(mUseCompression); + menu.findItem(R.id.check_encrypt_filenames).setChecked(mEncryptFilenames); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index 90eb2b1f3..7ca2bbb92 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java @@ -129,6 +129,8 @@ public class EncryptTextFragment extends CryptoOperationFragment { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.encrypt_text_fragment, menu); + + menu.findItem(R.id.check_enable_compression).setChecked(mUseCompression); } @Override diff --git a/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml b/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml index 10928e4bb..54c6c2768 100644 --- a/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml +++ b/OpenKeychain/src/main/res/menu/encrypt_file_fragment.xml @@ -22,13 +22,11 @@ Date: Wed, 27 May 2015 23:03:04 +0200 Subject: [PATCH 29/33] correctly preserve state in EncryptTextFragment --- .../keychain/ui/EncryptTextFragment.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index 7ca2bbb92..09f2365f2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java @@ -56,9 +56,10 @@ import java.util.Set; public class EncryptTextFragment extends CryptoOperationFragment { public static final String ARG_TEXT = "text"; + public static final String ARG_USE_COMPRESSION = "use_compression"; - private boolean mShareAfterEncrypt = false; - private boolean mUseCompression = true; + private boolean mShareAfterEncrypt; + private boolean mUseCompression; private boolean mHiddenRecipients = false; private String mMessage = ""; @@ -117,10 +118,21 @@ public class EncryptTextFragment extends CryptoOperationFragment { return view; } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(ARG_USE_COMPRESSION, mUseCompression); + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mMessage = getArguments().getString(ARG_TEXT); + if (savedInstanceState == null) { + mMessage = getArguments().getString(ARG_TEXT); + } + + Bundle args = savedInstanceState == null ? getArguments() : savedInstanceState; + mUseCompression = args.getBoolean(ARG_USE_COMPRESSION, true); setHasOptionsMenu(true); } From 5c8af1c5a5ad4be2bf3f2f657fe3fbd2f1fe8a24 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 28 May 2015 02:27:44 +0200 Subject: [PATCH 30/33] don't show allowed key list if no key exists, and some minor PgpDecryptVerify changes --- .../keychain/pgp/PgpEncryptDecryptTest.java | 29 +++++++++++++-- .../results/DecryptVerifyResult.java | 11 ++++-- .../operations/results/OperationResult.java | 2 +- .../keychain/pgp/PgpDecryptVerify.java | 37 ++++++++++++------- .../keychain/remote/OpenPgpService.java | 19 +++++----- .../src/main/res/values-de/strings.xml | 2 +- .../src/main/res/values-es/strings.xml | 2 +- .../src/main/res/values-fr/strings.xml | 2 +- .../src/main/res/values-ja/strings.xml | 2 +- .../src/main/res/values-nl/strings.xml | 2 +- .../src/main/res/values-sr/strings.xml | 2 +- .../src/main/res/values-zh-rTW/strings.xml | 2 +- OpenKeychain/src/main/res/values/strings.xml | 2 +- 13 files changed, 75 insertions(+), 39 deletions(-) diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java index dabfb008c..8cc5115e9 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java @@ -29,6 +29,7 @@ import org.robolectric.shadows.ShadowLog; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.openpgp.PGPEncryptedData; +import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; @@ -214,7 +215,7 @@ public class PgpEncryptDecryptTest { String plaintext = "dies ist ein plaintext ☭" + TestingUtils.genPassphrase(true); byte[] ciphertext; - { // encrypt data with a given passphrase + { // encrypt data with key ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayInputStream in = new ByteArrayInputStream(plaintext.getBytes()); @@ -224,7 +225,7 @@ public class PgpEncryptDecryptTest { InputData data = new InputData(in, in.available()); PgpSignEncryptInputParcel b = new PgpSignEncryptInputParcel(); - b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() }); + b.setEncryptionMasterKeyIds(new long[] { mStaticRing1.getMasterKeyId() }); b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128); PgpSignEncryptResult result = op.execute(b, new CryptoInputParcel(), data, out); Assert.assertTrue("encryption must succeed", result.success()); @@ -334,7 +335,7 @@ public class PgpEncryptDecryptTest { out.toByteArray().length, metadata.getOriginalSize()); } - { // decryption with passphrase cached should succeed for the first key + { // decryption should succeed if key is allowed ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayInputStream in = new ByteArrayInputStream(ciphertext); @@ -350,12 +351,32 @@ public class PgpEncryptDecryptTest { b.setAllowedKeyIds(allowed); DecryptVerifyResult result = b.build().execute(new CryptoInputParcel()); - Assert.assertTrue("decryption with cached passphrase must succeed for the first key", result.success()); + Assert.assertTrue("decryption with cached passphrase must succeed for allowed key", result.success()); Assert.assertArrayEquals("decrypted ciphertext with cached passphrase should equal plaintext", out.toByteArray(), plaintext.getBytes()); + Assert.assertTrue("other key was skipped", result.getLog().containsType(LogType.MSG_DC_ASKIP_NOT_ALLOWED)); Assert.assertNull("signature should be empty", result.getSignatureResult()); } + { // decryption should fail if no key is allowed + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayInputStream in = new ByteArrayInputStream(ciphertext); + InputData data = new InputData(in, in.available()); + + // provide passphrase for the second, and check that the first is never asked for! + PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out, + mKeyPhrase2, mStaticRing2.getMasterKeyId(), null); + // no keys allowed! + b.setAllowedKeyIds(new HashSet()); + + DecryptVerifyResult result = b.build().execute(new CryptoInputParcel()); + Assert.assertFalse("decryption must fail if no key allowed", result.success()); + Assert.assertEquals("decryption must fail with key disllowed status", + DecryptVerifyResult.RESULT_KEY_DISALLOWED, result.getResult()); + + } + { // decryption with passphrase cached should succeed for the other key if first is gone // delete first key from database diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java index 917b3415f..7680107f8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java @@ -27,12 +27,19 @@ import org.sufficientlysecure.keychain.util.Passphrase; public class DecryptVerifyResult extends InputPendingResult { + public static final int RESULT_NO_DATA = RESULT_ERROR + 16; + public static final int RESULT_KEY_DISALLOWED = RESULT_ERROR + 32; + OpenPgpSignatureResult mSignatureResult; OpenPgpMetadata mDecryptMetadata; // This holds the charset which was specified in the ascii armor, if specified // https://tools.ietf.org/html/rfc4880#page56 String mCharset; + public boolean isKeysDisallowed () { + return (mResult & RESULT_KEY_DISALLOWED) == RESULT_KEY_DISALLOWED; + } + public OpenPgpSignatureResult getSignatureResult() { return mSignatureResult; } @@ -57,10 +64,6 @@ public class DecryptVerifyResult extends InputPendingResult { mCharset = charset; } - public boolean isPending() { - return (mResult & RESULT_PENDING) == RESULT_PENDING; - } - public DecryptVerifyResult(int result, OperationLog log) { super(result, log); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index ad7feaea6..119678f16 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -590,7 +590,7 @@ public abstract class OperationResult implements Parcelable { MSG_DC_ERROR_EXTRACT_KEY (LogLevel.ERROR, R.string.msg_dc_error_extract_key), MSG_DC_ERROR_INTEGRITY_CHECK (LogLevel.ERROR, R.string.msg_dc_error_integrity_check), MSG_DC_ERROR_INTEGRITY_MISSING (LogLevel.ERROR, R.string.msg_dc_error_integrity_missing), - MSG_DC_ERROR_INVALID_SIGLIST(LogLevel.ERROR, R.string.msg_dc_error_invalid_siglist), + MSG_DC_ERROR_INVALID_DATA (LogLevel.ERROR, R.string.msg_dc_error_invalid_data), MSG_DC_ERROR_IO (LogLevel.ERROR, R.string.msg_dc_error_io), MSG_DC_ERROR_NO_DATA (LogLevel.ERROR, R.string.msg_dc_error_no_data), MSG_DC_ERROR_NO_KEY (LogLevel.ERROR, R.string.msg_dc_error_no_key), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index f6580b85a..aa1125800 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -384,7 +384,7 @@ public class PgpDecryptVerify extends BaseOperation { } if (enc == null) { - log.add(LogType.MSG_DC_ERROR_INVALID_SIGLIST, indent); + log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } @@ -419,6 +419,7 @@ public class PgpDecryptVerify extends BaseOperation { } Passphrase passphrase = null; + boolean skippedDisallowedKey = false; // go through all objects and find one we can decrypt while (it.hasNext()) { @@ -451,13 +452,6 @@ public class PgpDecryptVerify extends BaseOperation { log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); continue; } - // get subkey which has been used for this encryption packet - secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId); - if (secretEncryptionKey == null) { - // should actually never happen, so no need to be more specific. - log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); - continue; - } // allow only specific keys for decryption? if (mAllowedKeyIds != null) { @@ -469,11 +463,20 @@ public class PgpDecryptVerify extends BaseOperation { if (!mAllowedKeyIds.contains(masterKeyId)) { // this key is in our db, but NOT allowed! // continue with the next packet in the while loop + skippedDisallowedKey = true; log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent + 1); continue; } } + // get subkey which has been used for this encryption packet + secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId); + if (secretEncryptionKey == null) { + // should actually never happen, so no need to be more specific. + log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); + continue; + } + /* secret key exists in database and is allowed! */ asymmetricPacketFound = true; @@ -604,10 +607,18 @@ public class PgpDecryptVerify extends BaseOperation { } encryptedData = encryptedDataAsymmetric; } else { - // If we didn't find any useful data, error out + // there wasn't even any useful data + if (!anyPacketFound) { + log.add(LogType.MSG_DC_ERROR_NO_DATA, indent + 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_NO_DATA, log); + } + // there was data but key wasn't allowed + if (skippedDisallowedKey) { + log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_KEY_DISALLOWED, log); + } // no packet has been found where we have the corresponding secret key in our db - log.add( - anyPacketFound ? LogType.MSG_DC_ERROR_NO_KEY : LogType.MSG_DC_ERROR_NO_DATA, indent + 1); + log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } @@ -910,7 +921,7 @@ public class PgpDecryptVerify extends BaseOperation { PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); if (sigList == null) { - log.add(LogType.MSG_DC_ERROR_INVALID_SIGLIST, 0); + log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } @@ -993,7 +1004,7 @@ public class PgpDecryptVerify extends BaseOperation { } else if (o instanceof PGPSignatureList) { sigList = (PGPSignatureList) o; } else { - log.add(LogType.MSG_DC_ERROR_INVALID_SIGLIST, 0); + log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 4a8bf9332..179b78f26 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -34,7 +34,6 @@ import org.openintents.openpgp.util.OpenPgpApi; import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel; import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; import org.sufficientlysecure.keychain.pgp.PgpConstants; @@ -601,9 +600,8 @@ public class OpenPgpService extends RemoteService { result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); return result; } else { - LogEntryParcel errorMsg = pgpResult.getLog().getLast(); - - if (errorMsg.mType == OperationResult.LogType.MSG_DC_ERROR_NO_KEY) { + // + if (pgpResult.isKeysDisallowed()) { // allow user to select allowed keys Intent result = new Intent(); result.putExtra(OpenPgpApi.RESULT_INTENT, getSelectAllowedKeysIntent(data)); @@ -611,14 +609,17 @@ public class OpenPgpService extends RemoteService { return result; } - throw new Exception(getString(errorMsg.mType.getMsgId())); + String errorMsg = getString(pgpResult.getLog().getLast().mType.getMsgId()); + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_ERROR, new OpenPgpError(OpenPgpError.GENERIC_ERROR, errorMsg)); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); + return result; } - } catch (Exception e) { - Log.d(Constants.TAG, "decryptAndVerifyImpl", e); + } catch (IOException e) { + Log.e(Constants.TAG, "decryptAndVerifyImpl", e); Intent result = new Intent(); - result.putExtra(OpenPgpApi.RESULT_ERROR, - new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); + result.putExtra(OpenPgpApi.RESULT_ERROR, new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; } finally { diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml index 4fd310837..2dc6962cb 100644 --- a/OpenKeychain/src/main/res/values-de/strings.xml +++ b/OpenKeychain/src/main/res/values-de/strings.xml @@ -929,7 +929,7 @@ Unbekannter Fehler bei Schlüsselentsperrung! Integritätsprüfungsfehler! Fehlende Integritätsprüfung Dies kann passieren, wenn die Verschlüsselungsanwendung veraltet ist oder durch einen Downgrade-Angriff. - Keine gültigen Signaturdaten gefunden! + Keine gültigen Signaturdaten gefunden! Ein-/Ausgabefehler während Vorgang aufgetreten! Keine verschlüsselten Daten in Datenstrom gefunden! Keine verschlüsselten Daten mit bekanntem geheimen Schlüssel in Datenstrom gefunden! diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml index 2d94eb6a2..f672ab84b 100644 --- a/OpenKeychain/src/main/res/values-es/strings.xml +++ b/OpenKeychain/src/main/res/values-es/strings.xml @@ -928,7 +928,7 @@ ¡Error desconocido al desbloquear clave! ¡Error de comprobación de integridad! ¡Verificación de integridad ausente! Esto puede ocurrir porque la aplicación de cifrado no está actualizada, o debido a un ataque desactualización. - ¡No se encontraron datos de firma válidos! + ¡No se encontraron datos de firma válidos! ¡Se encontró Excepción de E/S durante la operación! ¡No se encontraron datos cifrados en el flujo de datos (`stream`)! ¡No se encontraron datos cifrados con clave secreta (privada) conocida en el flujo de datos (`stream`)! diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml index f34dd8e46..c7ac082dd 100644 --- a/OpenKeychain/src/main/res/values-fr/strings.xml +++ b/OpenKeychain/src/main/res/values-fr/strings.xml @@ -928,7 +928,7 @@ Erreur inconnue de déverrouillage de la clef ! Erreur de vérification de l\'intégrité ! Vérification de l\'intégrité absente ! Ceci peut arriver car l\'application n\'est pas à jour, ou à cause d\'une attaque par mise à niveau inférieur. - Aucune donnée de signature valide trouvée ! + Aucune donnée de signature valide trouvée ! Une exception E/S a été rencontrée durant l\'opération ! Aucune donnée chiffrée n\'a été trouvée dans le flux ! Aucune donnée chiffrée avec une clef secrète connue n\'a été trouvée dans le flux ! diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml index 13647a1bb..ad66b9a37 100644 --- a/OpenKeychain/src/main/res/values-ja/strings.xml +++ b/OpenKeychain/src/main/res/values-ja/strings.xml @@ -913,7 +913,7 @@ 鍵のロック解除で不明なエラー! 完全性チェックエラー! 完全聖チェックの欠落!これは暗号化アプリケーションが期限切れになった場合、もしくは暗号強度低下攻撃がある場合に発生します。 - 正常な署名データが見付からなかった! + 正常な署名データが見付からなかった! 操作中にIO例外に当たりました! ストリーム中に暗号化されたデータが見付からなかった! ストリーム中に既知の秘密鍵で暗号化されたデータが見付からなかった! diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml index 74e2c7117..37975ab1a 100644 --- a/OpenKeychain/src/main/res/values-nl/strings.xml +++ b/OpenKeychain/src/main/res/values-nl/strings.xml @@ -928,7 +928,7 @@ Onbekende fout bij ontgrendelen van sleutel! Fout bij integriteitscontrole! Integriteitscheck ontbreekt! Dit kan gebeuren omdat de versleutelingsapplicatie verouderd is, of door een downgrade-aanval. - Geen geldige ondertekeningsgegevens gevonden! + Geen geldige ondertekeningsgegevens gevonden! I/O-uitzondering tegengekomen tijdens bewerking! Geen versleutelde gegevens gevonden! Geen versleutelde gegevens met bekende geheime sleutel gevonden! diff --git a/OpenKeychain/src/main/res/values-sr/strings.xml b/OpenKeychain/src/main/res/values-sr/strings.xml index c4c92dce5..5b3027398 100644 --- a/OpenKeychain/src/main/res/values-sr/strings.xml +++ b/OpenKeychain/src/main/res/values-sr/strings.xml @@ -915,7 +915,7 @@ Непозната грешка откључавања кључа! Грешка провере интегритета! Недостаје провера интегритета! Ово може да се деси ако је апликација за шифровање застарела, или услед напада старијег издања. - Нису нађени исправни подаци потписа! + Нису нађени исправни подаци потписа! Наиђох на У/И изузетак током радње! Шифровани подаци нису нађени у току! Подаци шифровани познатим тајним кључем нису нађени у току! diff --git a/OpenKeychain/src/main/res/values-zh-rTW/strings.xml b/OpenKeychain/src/main/res/values-zh-rTW/strings.xml index 95fa8a28c..728eeccdd 100644 --- a/OpenKeychain/src/main/res/values-zh-rTW/strings.xml +++ b/OpenKeychain/src/main/res/values-zh-rTW/strings.xml @@ -510,7 +510,7 @@ 找不到金鑰! - 找不到有效的簽名資訊! + 找不到有效的簽名資訊! 開始解密… diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index f01b2cace..3ae4afa35 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -1041,7 +1041,7 @@ "Unknown error unlocking key!" "Integrity check error!" "Missing integrity check! This can happen because the encrypting application is out of date, or from a downgrade attack." - "No valid signature data found!" + "No valid OpenPGP encrypted or signed data found!" "Encountered IO Exception during operation!" "No encrypted data found in stream!" "No encrypted data with known secret key found in stream!" From eb830c6786c500d69ce2a06203b7cf336ae8a9bf Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 28 May 2015 11:40:35 +0200 Subject: [PATCH 31/33] warn on signature earlier than key creation, err on significantly earlier --- .../operations/results/OperationResult.java | 1 + .../keychain/pgp/UncachedKeyRing.java | 21 +++++++++++++++++++ OpenKeychain/src/main/res/values/strings.xml | 1 + 3 files changed, 23 insertions(+) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 119678f16..1cbff8a0d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -401,6 +401,7 @@ public abstract class OperationResult implements Parcelable { MSG_KC_SUB_BAD_LOCAL(LogLevel.WARN, R.string.msg_kc_sub_bad_local), MSG_KC_SUB_BAD_KEYID(LogLevel.WARN, R.string.msg_kc_sub_bad_keyid), MSG_KC_SUB_BAD_TIME(LogLevel.WARN, R.string.msg_kc_sub_bad_time), + MSG_KC_SUB_BAD_TIME_EARLY(LogLevel.WARN, R.string.msg_kc_sub_bad_time_early), MSG_KC_SUB_BAD_TYPE(LogLevel.WARN, R.string.msg_kc_sub_bad_type), MSG_KC_SUB_DUP (LogLevel.DEBUG, R.string.msg_kc_sub_dup), MSG_KC_SUB_PRIMARY_BAD(LogLevel.WARN, R.string.msg_kc_sub_primary_bad), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 2bb4f7dc4..ecf68890e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -820,6 +820,15 @@ public class UncachedKeyRing { continue; } + Date keyCreationTime = key.getCreationTime(), keyCreationTimeLenient; + { + Calendar keyCreationCal = Calendar.getInstance(); + keyCreationCal.setTime(keyCreationTime); + // allow for diverging clocks up to one day when checking creation time + keyCreationCal.add(Calendar.MINUTE, -5); + keyCreationTimeLenient = keyCreationCal.getTime(); + } + // A subkey needs exactly one subkey binding certificate, and optionally one revocation // certificate. PGPPublicKey modified = key; @@ -851,6 +860,18 @@ public class UncachedKeyRing { continue; } + if (cert.getCreationTime().before(keyCreationTime)) { + // Signature is earlier than key creation time + log.add(LogType.MSG_KC_SUB_BAD_TIME_EARLY, indent); + // due to an earlier accident, we generated keys which had creation timestamps + // a few seconds after their signature timestamp. for compatibility, we only + // error out with some margin of error + if (cert.getCreationTime().before(keyCreationTimeLenient)) { + badCerts += 1; + continue; + } + } + if (cert.isLocal()) { // Creation date in the future? No way! log.add(LogType.MSG_KC_SUB_BAD_LOCAL, indent); diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 3ae4afa35..d2563a38b 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -829,6 +829,7 @@ "Removing subkey binding certificate with 'local' flag" "Subkey binding issuer id mismatch" "Removing subkey binding certificate with future timestamp" + "Subkey binding certificate has earlier timestamp than its key!" "Unknown subkey certificate type: %s" "Removing redundant subkey binding certificate" "Removing subkey binding certificate due to invalid primary binding certificate" From 8de0d9e6da63c6755b86c6b9850def057e4bd8c0 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 28 May 2015 14:41:26 +0200 Subject: [PATCH 32/33] create keys with fixed timestamp --- .../keychain/pgp/PgpKeyOperation.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 2f771d8f2..991d013ae 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -151,7 +151,7 @@ public class PgpKeyOperation { } /** Creates new secret key. */ - private PGPKeyPair createKey(SubkeyAdd add, OperationLog log, int indent) { + private PGPKeyPair createKey(SubkeyAdd add, Date creationTime, OperationLog log, int indent) { try { // Some safety checks @@ -249,7 +249,7 @@ public class PgpKeyOperation { } // build new key pair - return new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), new Date()); + return new JcaPGPKeyPair(algorithm, keyGen.generateKeyPair(), creationTime); } catch(NoSuchProviderException | InvalidAlgorithmParameterException e) { throw new RuntimeException(e); @@ -295,8 +295,10 @@ public class PgpKeyOperation { return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); } + Date creationTime = new Date(); + subProgressPush(10, 30); - PGPKeyPair keyPair = createKey(add, log, indent); + PGPKeyPair keyPair = createKey(add, creationTime, log, indent); subProgressPop(); // return null if this failed (an error will already have been logged by createKey) @@ -323,8 +325,8 @@ public class PgpKeyOperation { masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator()); subProgressPush(50, 100); - CryptoInputParcel cryptoInput = new CryptoInputParcel(new Date(), new Passphrase("")); - return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, cryptoInput, saveParcel, log); + CryptoInputParcel cryptoInput = new CryptoInputParcel(creationTime, new Passphrase("")); + return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, cryptoInput, saveParcel, log, indent); } catch (PGPException e) { log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent); @@ -900,7 +902,7 @@ public class PgpKeyOperation { (i-1) * (100 / saveParcel.mAddSubKeys.size()), i * (100 / saveParcel.mAddSubKeys.size()) ); - PGPKeyPair keyPair = createKey(add, log, indent); + PGPKeyPair keyPair = createKey(add, cryptoInput.getSignatureTime(), log, indent); subProgressPop(); if (keyPair == null) { log.add(LogType.MSG_MF_ERROR_PGP, indent +1); From ee02e110e7f1a90d2a91c4fc09834b8c073cbba7 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 28 May 2015 15:02:50 +0200 Subject: [PATCH 33/33] don't keep an activity reference in CreateKeyFinalFragment --- .../keychain/ui/CreateKeyFinalFragment.java | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index b0a13c897..bdb534757 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -54,8 +54,6 @@ public class CreateKeyFinalFragment extends Fragment { public static final int REQUEST_EDIT_KEY = 0x00008007; - CreateKeyActivity mCreateKeyActivity; - TextView mNameEdit; TextView mEmailEdit; CheckBox mUploadCheckbox; @@ -66,9 +64,6 @@ public class CreateKeyFinalFragment extends Fragment { SaveKeyringParcel mSaveKeyringParcel; - /** - * Creates new instance of this fragment - */ public static CreateKeyFinalFragment newInstance() { CreateKeyFinalFragment frag = new CreateKeyFinalFragment(); @@ -90,11 +85,13 @@ public class CreateKeyFinalFragment extends Fragment { mEditText = (TextView) view.findViewById(R.id.create_key_edit_text); mEditButton = view.findViewById(R.id.create_key_edit_button); + CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity(); + // set values - mNameEdit.setText(mCreateKeyActivity.mName); - if (mCreateKeyActivity.mAdditionalEmails != null && mCreateKeyActivity.mAdditionalEmails.size() > 0) { - String emailText = mCreateKeyActivity.mEmail + ", "; - Iterator it = mCreateKeyActivity.mAdditionalEmails.iterator(); + mNameEdit.setText(createKeyActivity.mName); + if (createKeyActivity.mAdditionalEmails != null && createKeyActivity.mAdditionalEmails.size() > 0) { + String emailText = createKeyActivity.mEmail + ", "; + Iterator it = createKeyActivity.mAdditionalEmails.iterator(); while (it.hasNext()) { Object next = it.next(); emailText += next; @@ -104,7 +101,7 @@ public class CreateKeyFinalFragment extends Fragment { } mEmailEdit.setText(emailText); } else { - mEmailEdit.setText(mCreateKeyActivity.mEmail); + mEmailEdit.setText(createKeyActivity.mEmail); } mCreateButton.setOnClickListener(new View.OnClickListener() { @@ -117,7 +114,10 @@ public class CreateKeyFinalFragment extends Fragment { mBackButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT); + CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity(); + if (createKeyActivity != null) { + createKeyActivity.loadFragment(null, FragAction.TO_LEFT); + } } }); @@ -133,12 +133,6 @@ public class CreateKeyFinalFragment extends Fragment { return view; } - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - mCreateKeyActivity = (CreateKeyActivity) getActivity(); - } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { @@ -159,7 +153,7 @@ public class CreateKeyFinalFragment extends Fragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - mCreateKeyActivity = (CreateKeyActivity) getActivity(); + CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity(); if (mSaveKeyringParcel == null) { mSaveKeyringParcel = new SaveKeyringParcel(); @@ -170,21 +164,21 @@ public class CreateKeyFinalFragment extends Fragment { mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L)); String userId = KeyRing.createUserId( - new KeyRing.UserId(mCreateKeyActivity.mName, mCreateKeyActivity.mEmail, null) + new KeyRing.UserId(createKeyActivity.mName, createKeyActivity.mEmail, null) ); mSaveKeyringParcel.mAddUserIds.add(userId); mSaveKeyringParcel.mChangePrimaryUserId = userId; - if (mCreateKeyActivity.mAdditionalEmails != null - && mCreateKeyActivity.mAdditionalEmails.size() > 0) { - for (String email : mCreateKeyActivity.mAdditionalEmails) { + if (createKeyActivity.mAdditionalEmails != null + && createKeyActivity.mAdditionalEmails.size() > 0) { + for (String email : createKeyActivity.mAdditionalEmails) { String thisUserId = KeyRing.createUserId( - new KeyRing.UserId(mCreateKeyActivity.mName, email, null) + new KeyRing.UserId(createKeyActivity.mName, email, null) ); mSaveKeyringParcel.mAddUserIds.add(thisUserId); } } - mSaveKeyringParcel.mNewUnlock = mCreateKeyActivity.mPassphrase != null - ? new ChangeUnlockParcel(mCreateKeyActivity.mPassphrase, null) + mSaveKeyringParcel.mNewUnlock = createKeyActivity.mPassphrase != null + ? new ChangeUnlockParcel(createKeyActivity.mPassphrase, null) : null; } }