From 74c474e3e26c428153f15b7fab6b29738e882682 Mon Sep 17 00:00:00 2001 From: Max Mertens Date: Wed, 18 Mar 2015 17:49:57 +0100 Subject: [PATCH 01/13] Show HKP server results first in key cloud search. Fixes #1066 --- .../keychain/keyimport/ImportKeysList.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java index 02cb502d0..ed096e9dc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java @@ -70,6 +70,7 @@ public class ImportKeysList extends ArrayList { modified = true; } + boolean incomingFromHkpServer = true; // we’re going to want to try to fetch the key from everywhere we found it, so remember // all the origins for (String origin : incoming.getOrigins()) { @@ -78,13 +79,19 @@ public class ImportKeysList extends ArrayList { // to work properly, Keybase-sourced entries need to pass along the extra if (KeybaseKeyserver.ORIGIN.equals(origin)) { existing.setExtraData(incoming.getExtraData()); + incomingFromHkpServer = false; } } + ArrayList incomingIDs = incoming.getUserIds(); ArrayList existingIDs = existing.getUserIds(); for (String incomingID : incomingIDs) { if (!existingIDs.contains(incomingID)) { - existingIDs.add(incomingID); + if (incomingFromHkpServer) { + existingIDs.add(0, incomingID); + } else { + existingIDs.add(incomingID); + } modified = true; } } From aab32b81b9b54e1a978006f33718a9525772ce53 Mon Sep 17 00:00:00 2001 From: Max Mertens Date: Wed, 18 Mar 2015 22:23:35 +0100 Subject: [PATCH 02/13] Added comments for server result sorting in key search --- .../keychain/keyimport/ImportKeysList.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java index ed096e9dc..03439228b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java @@ -70,6 +70,7 @@ public class ImportKeysList extends ArrayList { modified = true; } + // keep track if this key result is from a HKP keyserver boolean incomingFromHkpServer = true; // we’re going to want to try to fetch the key from everywhere we found it, so remember // all the origins @@ -79,6 +80,7 @@ public class ImportKeysList extends ArrayList { // to work properly, Keybase-sourced entries need to pass along the extra if (KeybaseKeyserver.ORIGIN.equals(origin)) { existing.setExtraData(incoming.getExtraData()); + // one of the origins is not a HKP keyserver incomingFromHkpServer = false; } } @@ -87,6 +89,10 @@ public class ImportKeysList extends ArrayList { ArrayList existingIDs = existing.getUserIds(); for (String incomingID : incomingIDs) { if (!existingIDs.contains(incomingID)) { + // prepend HKP server results to the start of the list, + // so that the UI (for cloud key search, which is picking the first list item) + // shows the right main email address, as mail addresses returned by HKP servers + // are preferred over keybase.io IDs if (incomingFromHkpServer) { existingIDs.add(0, incomingID); } else { From 9c9f95c7acb92aad1f02e65271610fb3dca6c0dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 19 Mar 2015 03:03:46 +0100 Subject: [PATCH 03/13] New Passphrase class for safer passphrase handling in memory --- .../keychain/operations/BaseOperation.java | 5 +- .../keychain/operations/CertifyOperation.java | 3 +- .../keychain/operations/EditKeyOperation.java | 3 +- .../results/DecryptVerifyResult.java | 11 +- .../results/PgpSignEncryptResult.java | 8 +- .../keychain/pgp/CanonicalizedSecretKey.java | 5 +- .../pgp/PassphraseCacheInterface.java | 6 +- .../keychain/pgp/PgpDecryptVerify.java | 9 +- .../keychain/pgp/PgpKeyOperation.java | 25 ++-- .../keychain/pgp/PgpSignEncryptInput.java | 13 +- .../keychain/pgp/PgpSignEncryptOperation.java | 2 +- .../keychain/pgp/SignEncryptParcel.java | 10 +- .../keychain/remote/OpenPgpService.java | 7 +- .../service/KeychainIntentService.java | 7 +- .../service/PassphraseCacheService.java | 36 +++-- .../keychain/service/SaveKeyringParcel.java | 20 ++- .../keychain/ui/CertifyKeyFragment.java | 4 +- .../keychain/ui/CreateKeyActivity.java | 11 +- .../keychain/ui/CreateKeyEmailFragment.java | 2 +- .../keychain/ui/CreateKeyFinalFragment.java | 9 +- .../keychain/ui/CreateKeyNameFragment.java | 15 +- .../ui/CreateKeyPassphraseFragment.java | 13 +- .../keychain/ui/DecryptFilesFragment.java | 6 +- .../keychain/ui/DecryptFragment.java | 5 +- .../keychain/ui/DecryptTextFragment.java | 4 +- .../keychain/ui/EditKeyFragment.java | 11 +- .../keychain/ui/EncryptActivity.java | 7 +- .../keychain/ui/EncryptActivityInterface.java | 4 +- .../keychain/ui/EncryptFilesActivity.java | 9 +- .../keychain/ui/EncryptSymmetricFragment.java | 10 +- .../keychain/ui/EncryptTextActivity.java | 10 +- .../keychain/ui/PassphraseDialogActivity.java | 5 +- .../dialog/SetPassphraseDialogFragment.java | 21 +-- .../keychain/util/Passphrase.java | 131 ++++++++++++++++++ 34 files changed, 307 insertions(+), 140 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java index 40dcbd78d..a824e73d7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BaseOperation.java @@ -24,6 +24,7 @@ import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.concurrent.atomic.AtomicBoolean; @@ -101,7 +102,7 @@ public abstract class BaseOperation implements PassphraseCacheInterface { } @Override - public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException { + public Passphrase getCachedPassphrase(long subKeyId) throws NoSecretKeyException { try { long masterKeyId = mProviderHelper.getMasterKeyId(subKeyId); return getCachedPassphrase(masterKeyId, subKeyId); @@ -111,7 +112,7 @@ public abstract class BaseOperation implements PassphraseCacheInterface { } @Override - public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException { + public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException { try { return PassphraseCacheService.getCachedPassphrase( mContext, masterKeyId, subKeyId); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java index ebf0dc70b..4ceb34722 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyActio import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; @@ -79,7 +80,7 @@ public class CertifyOperation extends BaseOperation { } // certification is always with the master key id, so use that one - String passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId); + Passphrase passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId); if (!certificationKey.unlock(passphrase)) { log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java index bcd842dd0..a179b53ee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java @@ -35,6 +35,7 @@ import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import java.util.concurrent.atomic.AtomicBoolean; @@ -55,7 +56,7 @@ public class EditKeyOperation extends BaseOperation { super(context, providerHelper, progressable, cancelled); } - public EditKeyResult execute(SaveKeyringParcel saveParcel, String passphrase) { + public EditKeyResult execute(SaveKeyringParcel saveParcel, Passphrase passphrase) { OperationLog log = new OperationLog(); log.add(LogType.MSG_ED, 0); 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 86b37fea6..7df37cd9b 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 @@ -22,6 +22,7 @@ import android.os.Parcel; import org.openintents.openpgp.OpenPgpMetadata; import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.util.Passphrase; public class DecryptVerifyResult extends OperationResult { @@ -37,7 +38,7 @@ public class DecryptVerifyResult extends OperationResult { long mNfcSubKeyId; byte[] mNfcSessionKey; - String mNfcPassphrase; + Passphrase mNfcPassphrase; OpenPgpSignatureResult mSignatureResult; OpenPgpMetadata mDecryptMetadata; @@ -53,7 +54,7 @@ public class DecryptVerifyResult extends OperationResult { mKeyIdPassphraseNeeded = keyIdPassphraseNeeded; } - public void setNfcState(long subKeyId, byte[] sessionKey, String passphrase) { + public void setNfcState(long subKeyId, byte[] sessionKey, Passphrase passphrase) { mNfcSubKeyId = subKeyId; mNfcSessionKey = sessionKey; mNfcPassphrase = passphrase; @@ -67,7 +68,7 @@ public class DecryptVerifyResult extends OperationResult { return mNfcSessionKey; } - public String getNfcPassphrase() { + public Passphrase getNfcPassphrase() { return mNfcPassphrase; } @@ -109,7 +110,7 @@ public class DecryptVerifyResult extends OperationResult { mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader()); mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader()); mNfcSessionKey = source.readInt() != 0 ? source.createByteArray() : null; - mNfcPassphrase = source.readString(); + mNfcPassphrase = source.readParcelable(Passphrase.class.getClassLoader()); } public int describeContents() { @@ -127,7 +128,7 @@ public class DecryptVerifyResult extends OperationResult { } else { dest.writeInt(0); } - dest.writeString(mNfcPassphrase); + dest.writeParcelable(mNfcPassphrase, flags); } public static final Creator CREATOR = new Creator() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java index de2f64404..cf40001b3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java @@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.operations.results; import android.os.Parcel; +import org.sufficientlysecure.keychain.util.Passphrase; + import java.util.Date; public class PgpSignEncryptResult extends OperationResult { @@ -36,7 +38,7 @@ public class PgpSignEncryptResult extends OperationResult { byte[] mNfcHash; int mNfcAlgo; Date mNfcTimestamp; - String mNfcPassphrase; + Passphrase mNfcPassphrase; byte[] mDetachedSignature; public long getKeyIdPassphraseNeeded() { @@ -47,7 +49,7 @@ public class PgpSignEncryptResult extends OperationResult { mKeyIdPassphraseNeeded = keyIdPassphraseNeeded; } - public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, String passphrase) { + public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, Passphrase passphrase) { mNfcKeyId = nfcKeyId; mNfcHash = nfcHash; mNfcAlgo = nfcAlgo; @@ -75,7 +77,7 @@ public class PgpSignEncryptResult extends OperationResult { return mNfcTimestamp; } - public String getNfcPassphrase() { + public Passphrase getNfcPassphrase() { return mNfcPassphrase; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java index ab91d7747..6ce77394c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java @@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.ArrayList; import java.util.Date; @@ -149,7 +150,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { /** * Returns true on right passphrase */ - public boolean unlock(String passphrase) throws PgpGeneralException { + public boolean unlock(Passphrase passphrase) throws PgpGeneralException { // handle keys on OpenPGP cards like they were unlocked if (mSecretKey.getS2K() != null && mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K @@ -161,7 +162,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey { // try to extract keys using the passphrase try { PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray()); mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor); mPrivateKeyState = PRIVATE_KEY_STATE_UNLOCKED; } catch (PGPException e) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java index 0066bd23e..88ccccc6a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java @@ -18,14 +18,16 @@ package org.sufficientlysecure.keychain.pgp; +import org.sufficientlysecure.keychain.util.Passphrase; + public interface PassphraseCacheInterface { public static class NoSecretKeyException extends Exception { public NoSecretKeyException() { } } - public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException; + public Passphrase getCachedPassphrase(long subKeyId) throws NoSecretKeyException; - public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException; + public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException; } 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 14bc56538..364a1067d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -60,6 +60,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import java.io.BufferedInputStream; @@ -83,7 +84,7 @@ public class PgpDecryptVerify extends BaseOperation { private OutputStream mOutStream; private boolean mAllowSymmetricDecryption; - private String mPassphrase; + private Passphrase mPassphrase; private Set mAllowedKeyIds; private boolean mDecryptMetadataOnly; private byte[] mDecryptedSessionKey; @@ -118,7 +119,7 @@ public class PgpDecryptVerify extends BaseOperation { private OutputStream mOutStream = null; private Progressable mProgressable = null; private boolean mAllowSymmetricDecryption = true; - private String mPassphrase = null; + private Passphrase mPassphrase = null; private Set mAllowedKeyIds = null; private boolean mDecryptMetadataOnly = false; private byte[] mDecryptedSessionKey = null; @@ -159,7 +160,7 @@ public class PgpDecryptVerify extends BaseOperation { return this; } - public Builder setPassphrase(String passphrase) { + public Builder setPassphrase(Passphrase passphrase) { mPassphrase = passphrase; return this; } @@ -572,7 +573,7 @@ public class PgpDecryptVerify extends BaseOperation { .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(); PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - mPassphrase.toCharArray()); + mPassphrase.getCharArray()); clear = encryptedDataSymmetric.getDataStream(decryptorFactory); encryptedData = encryptedDataSymmetric; 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 8fb5392e3..b3bf92364 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -57,6 +57,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -316,7 +317,7 @@ public class PgpKeyOperation { masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator()); subProgressPush(50, 100); - return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, "", log); + return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, new Passphrase(), log); } catch (PGPException e) { log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent); @@ -348,7 +349,7 @@ public class PgpKeyOperation { * */ public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel, - String passphrase) { + Passphrase passphrase) { OperationLog log = new OperationLog(); int indent = 0; @@ -404,7 +405,7 @@ public class PgpKeyOperation { private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey, int masterKeyFlags, long masterKeyExpiry, - SaveKeyringParcel saveParcel, String passphrase, + SaveKeyringParcel saveParcel, Passphrase passphrase, OperationLog log) { int indent = 1; @@ -420,7 +421,7 @@ public class PgpKeyOperation { { try { PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray()); masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); } catch (PGPException e) { log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1); @@ -839,7 +840,7 @@ public class PgpKeyOperation { PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray()); PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder() .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO); @@ -967,7 +968,7 @@ public class PgpKeyOperation { PGPSecretKeyRing sKR, PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, - String passphrase, + Passphrase passphrase, ChangeUnlockParcel newUnlock, OperationLog log, int indent) throws PGPException { @@ -1051,20 +1052,20 @@ public class PgpKeyOperation { private static PGPSecretKeyRing applyNewPassphrase( PGPSecretKeyRing sKR, PGPPublicKey masterPublicKey, - String passphrase, - String newPassphrase, + Passphrase passphrase, + Passphrase newPassphrase, OperationLog log, int indent) throws PGPException { PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build() .get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO); PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray()); // Build key encryptor based on new passphrase PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - newPassphrase.toCharArray()); + newPassphrase.getCharArray()); // noinspection unchecked for (PGPSecretKey sKey : new IterableIterator(sKR.getSecretKeys())) { @@ -1295,11 +1296,11 @@ public class PgpKeyOperation { private static PGPSignature generateSubkeyBindingSignature( PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, - PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, String passphrase) + PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, Passphrase passphrase) throws IOException, PGPException, SignatureException { PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - passphrase.toCharArray()); + passphrase.getCharArray()); PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor); return generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, subPrivateKey, pKey, flags, expiry); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java index 2dec4b9c2..4a920685a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInput.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.Date; @@ -29,12 +30,12 @@ public class PgpSignEncryptInput { protected boolean mEnableAsciiArmorOutput = false; protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED; protected long[] mEncryptionMasterKeyIds = null; - protected String mSymmetricPassphrase = null; + protected Passphrase mSymmetricPassphrase = null; protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED; protected long mSignatureMasterKeyId = Constants.key.none; protected Long mSignatureSubKeyId = null; protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED; - protected String mSignaturePassphrase = null; + protected Passphrase mSignaturePassphrase = null; protected long mAdditionalEncryptId = Constants.key.none; protected byte[] mNfcSignedHash = null; protected Date mNfcCreationTimestamp = null; @@ -73,11 +74,11 @@ public class PgpSignEncryptInput { return this; } - public String getSignaturePassphrase() { + public Passphrase getSignaturePassphrase() { return mSignaturePassphrase; } - public PgpSignEncryptInput setSignaturePassphrase(String signaturePassphrase) { + public PgpSignEncryptInput setSignaturePassphrase(Passphrase signaturePassphrase) { mSignaturePassphrase = signaturePassphrase; return this; } @@ -118,11 +119,11 @@ public class PgpSignEncryptInput { return this; } - public String getSymmetricPassphrase() { + public Passphrase getSymmetricPassphrase() { return mSymmetricPassphrase; } - public PgpSignEncryptInput setSymmetricPassphrase(String symmetricPassphrase) { + public PgpSignEncryptInput setSymmetricPassphrase(Passphrase symmetricPassphrase) { mSymmetricPassphrase = symmetricPassphrase; return this; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java index 94e04060d..bd3c31d3a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java @@ -243,7 +243,7 @@ public class PgpSignEncryptOperation extends BaseOperation { log.add(LogType.MSG_PSE_SYMMETRIC, indent); JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = - new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().toCharArray()); + new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().getCharArray()); cPk.addMethod(symmetricEncryptionGenerator); } else { log.add(LogType.MSG_PSE_ASYMMETRIC, indent); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java index 8e71e8815..975548c95 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java @@ -22,6 +22,8 @@ import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import org.sufficientlysecure.keychain.util.Passphrase; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -57,12 +59,12 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable mEnableAsciiArmorOutput = src.readInt() == 1; mCompressionId = src.readInt(); mEncryptionMasterKeyIds = src.createLongArray(); - mSymmetricPassphrase = src.readString(); + mSymmetricPassphrase = src.readParcelable(Passphrase.class.getClassLoader()); mSymmetricEncryptionAlgorithm = src.readInt(); mSignatureMasterKeyId = src.readLong(); mSignatureSubKeyId = src.readInt() == 1 ? src.readLong() : null; mSignatureHashAlgorithm = src.readInt(); - mSignaturePassphrase = src.readString(); + mSignaturePassphrase = src.readParcelable(Passphrase.class.getClassLoader()); mAdditionalEncryptId = src.readLong(); mNfcSignedHash = src.createByteArray(); mNfcCreationTimestamp = src.readInt() == 1 ? new Date(src.readLong()) : null; @@ -112,7 +114,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0); dest.writeInt(mCompressionId); dest.writeLongArray(mEncryptionMasterKeyIds); - dest.writeString(mSymmetricPassphrase); + dest.writeParcelable(mSymmetricPassphrase, flags); dest.writeInt(mSymmetricEncryptionAlgorithm); dest.writeLong(mSignatureMasterKeyId); if (mSignatureSubKeyId != null) { @@ -122,7 +124,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable dest.writeInt(0); } dest.writeInt(mSignatureHashAlgorithm); - dest.writeString(mSignaturePassphrase); + dest.writeParcelable(mSignaturePassphrase, flags); dest.writeLong(mAdditionalEncryptId); dest.writeByteArray(mNfcSignedHash); if (mNfcCreationTimestamp != null) { 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 a4bc95602..98a9ff44f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -54,6 +54,7 @@ import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.ViewKeyActivity; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import java.io.IOException; import java.io.InputStream; @@ -179,7 +180,7 @@ public class OpenPgpService extends RemoteService { return result; } - private PendingIntent getNfcSignPendingIntent(Intent data, long keyId, String pin, byte[] hashToSign, int hashAlgo) { + private PendingIntent getNfcSignPendingIntent(Intent data, long keyId, Passphrase pin, byte[] hashToSign, int hashAlgo) { // build PendingIntent for Yubikey NFC operations Intent intent = new Intent(getBaseContext(), NfcActivity.class); intent.setAction(NfcActivity.ACTION_SIGN_HASH); @@ -195,7 +196,7 @@ public class OpenPgpService extends RemoteService { PendingIntent.FLAG_CANCEL_CURRENT); } - private PendingIntent getNfcDecryptPendingIntent(Intent data, long subKeyId, String pin, byte[] encryptedSessionKey) { + private PendingIntent getNfcDecryptPendingIntent(Intent data, long subKeyId, Passphrase pin, byte[] encryptedSessionKey) { // build PendingIntent for Yubikey NFC operations Intent intent = new Intent(getBaseContext(), NfcActivity.class); intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY); @@ -508,7 +509,7 @@ public class OpenPgpService extends RemoteService { KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg)); } - String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); + Passphrase passphrase = data.getParcelableExtra(OpenPgpApi.EXTRA_PASSPHRASE); long inputLength = is.available(); InputData inputData = new InputData(is, inputLength); 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 bf6a62782..d5f13f7ce 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -65,6 +65,7 @@ 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; @@ -281,7 +282,7 @@ public class KeychainIntentService extends IntentService implements Progressable try { /* Input */ - String passphrase = data.getString(DECRYPT_PASSPHRASE); + Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE); byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); InputData inputData = createDecryptInputData(data); @@ -411,7 +412,7 @@ public class KeychainIntentService extends IntentService implements Progressable try { /* Input */ - String passphrase = data.getString(DECRYPT_PASSPHRASE); + Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE); byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); InputData inputData = createDecryptInputData(data); @@ -469,7 +470,7 @@ public class KeychainIntentService extends IntentService implements Progressable // Input SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL); - String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE); + Passphrase passphrase = data.getParcelable(EDIT_KEYRING_PASSPHRASE); // Operation EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index 57881f8ee..ee481ad31 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; import java.util.Date; @@ -121,7 +122,7 @@ public class PassphraseCacheService extends Service { * new events to the alarm manager for new passphrases to let them timeout in the future. */ public static void addCachedPassphrase(Context context, long masterKeyId, long subKeyId, - String passphrase, + Passphrase passphrase, String primaryUserId) { Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + masterKeyId); @@ -143,7 +144,7 @@ public class PassphraseCacheService extends Service { * @return passphrase or null (if no passphrase is cached for this keyId) */ - public static String getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException { + public static Passphrase getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException { Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphrase() for masterKeyId " + masterKeyId + ", subKeyId " + subKeyId); @@ -190,7 +191,9 @@ public class PassphraseCacheService extends Service { switch (returnMessage.what) { case MSG_PASSPHRASE_CACHE_GET_OKAY: - return returnMessage.getData().getString(EXTRA_PASSPHRASE); + Bundle returnData = returnMessage.getData(); + returnData.setClassLoader(context.getClassLoader()); + return returnData.getParcelable(EXTRA_PASSPHRASE); case MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND: throw new KeyNotFoundException(); default: @@ -202,11 +205,11 @@ public class PassphraseCacheService extends Service { /** * Internal implementation to get cached passphrase. */ - private String getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException { + private Passphrase getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException { // passphrase for symmetric encryption? if (masterKeyId == Constants.key.symmetric) { Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption"); - String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase(); + Passphrase cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase(); if (cachedPassphrase == null) { return null; } @@ -232,13 +235,13 @@ public class PassphraseCacheService extends Service { case DIVERT_TO_CARD: if (Preferences.getPreferences(this).useDefaultYubikeyPin()) { Log.d(Constants.TAG, "PassphraseCacheService: Using default Yubikey PIN: 123456"); - return "123456"; // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/ + return new Passphrase("123456"); // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/ } else { Log.d(Constants.TAG, "PassphraseCacheService: NOT using default Yubikey PIN"); break; } case PASSPHRASE_EMPTY: - return ""; + return new Passphrase(""); case UNAVAILABLE: throw new ProviderHelper.NotFoundException("secret key for this subkey is not available"); case GNU_DUMMY: @@ -331,7 +334,7 @@ public class PassphraseCacheService extends Service { long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1); long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1); - String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE); + Passphrase passphrase = intent.getParcelableExtra(EXTRA_PASSPHRASE); String primaryUserID = intent.getStringExtra(EXTRA_USER_ID); Log.d(Constants.TAG, @@ -373,10 +376,10 @@ public class PassphraseCacheService extends Service { Log.e(Constants.TAG, "PassphraseCacheService: Bad request, missing masterKeyId or subKeyId!"); msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND; } else { - String passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId); + Passphrase passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId); msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY; Bundle bundle = new Bundle(); - bundle.putString(EXTRA_PASSPHRASE, passphrase); + bundle.putParcelable(EXTRA_PASSPHRASE, passphrase); msg.setData(bundle); } } catch (ProviderHelper.NotFoundException e) { @@ -412,7 +415,10 @@ public class PassphraseCacheService extends Service { * Called when one specific passphrase for keyId timed out */ private void timeout(Context context, long keyId) { - // remove passphrase corresponding to keyId from memory + CachedPassphrase cPass = mPassphraseCache.get(keyId); + // clean internal char[] from memory! + cPass.getPassphrase().removeFromMemory(); + // remove passphrase object mPassphraseCache.remove(keyId); Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!"); @@ -517,9 +523,9 @@ public class PassphraseCacheService extends Service { public class CachedPassphrase { private String primaryUserID; - private String passphrase; + private Passphrase passphrase; - public CachedPassphrase(String passphrase, String primaryUserID) { + public CachedPassphrase(Passphrase passphrase, String primaryUserID) { setPassphrase(passphrase); setPrimaryUserID(primaryUserID); } @@ -528,7 +534,7 @@ public class PassphraseCacheService extends Service { return primaryUserID; } - public String getPassphrase() { + public Passphrase getPassphrase() { return passphrase; } @@ -536,7 +542,7 @@ public class PassphraseCacheService extends Service { this.primaryUserID = primaryUserID; } - public void setPassphrase(String passphrase) { + public void setPassphrase(Passphrase passphrase) { this.passphrase = passphrase; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index e2d0c03c9..9fd278c13 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -22,6 +22,7 @@ import android.os.Parcel; import android.os.Parcelable; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.util.Passphrase; import java.io.Serializable; import java.util.ArrayList; @@ -296,33 +297,30 @@ public class SaveKeyringParcel implements Parcelable { public static class ChangeUnlockParcel implements Parcelable { // The new passphrase to use - public final String mNewPassphrase; + public final Passphrase mNewPassphrase; // A new pin to use. Must only contain [0-9]+ - public final String mNewPin; + public final Passphrase mNewPin; - public ChangeUnlockParcel(String newPassphrase) { + public ChangeUnlockParcel(Passphrase newPassphrase) { this(newPassphrase, null); } - public ChangeUnlockParcel(String newPassphrase, String newPin) { + public ChangeUnlockParcel(Passphrase newPassphrase, Passphrase newPin) { if (newPassphrase == null && newPin == null) { throw new RuntimeException("Cannot set both passphrase and pin. THIS IS A BUG!"); } - if (newPin != null && !newPin.matches("[0-9]+")) { - throw new RuntimeException("Pin must be numeric digits only. THIS IS A BUG!"); - } mNewPassphrase = newPassphrase; mNewPin = newPin; } public ChangeUnlockParcel(Parcel source) { - mNewPassphrase = source.readString(); - mNewPin = source.readString(); + mNewPassphrase = source.readParcelable(Passphrase.class.getClassLoader()); + mNewPin = source.readParcelable(Passphrase.class.getClassLoader()); } @Override public void writeToParcel(Parcel destination, int flags) { - destination.writeString(mNewPassphrase); - destination.writeString(mNewPin); + destination.writeParcelable(mNewPassphrase, flags); + destination.writeParcelable(mNewPin, flags); } @Override 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 9cb4e5f65..a0f89b06a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -62,6 +62,7 @@ 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.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; import java.lang.reflect.Method; @@ -318,7 +319,7 @@ public class CertifyKeyFragment extends LoaderFragment */ private void initiateCertifying() { // get the user's passphrase for this key (if required) - String passphrase; + Passphrase passphrase; try { passphrase = PassphraseCacheService.getCachedPassphrase(getActivity(), mSignMasterKeyId, mSignMasterKeyId); } catch (PassphraseCacheService.KeyNotFoundException e) { @@ -341,7 +342,6 @@ public class CertifyKeyFragment extends LoaderFragment switch (requestCode) { case REQUEST_CODE_PASSPHRASE: { if (resultCode == Activity.RESULT_OK && data != null) { - String passphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); startCertifying(); } return; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java index 2da5511b8..0eb9e948a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java @@ -23,11 +23,20 @@ import android.support.v4.app.FragmentTransaction; import org.sufficientlysecure.keychain.R; +import java.util.ArrayList; + public class CreateKeyActivity extends BaseActivity { public static final String EXTRA_NAME = "name"; public static final String EXTRA_EMAIL = "email"; + public class State { + String name; + String email; + ArrayList additionalEmails; + char[] passphrase; + } + public static enum FragAction { START, TO_RIGHT, @@ -44,7 +53,7 @@ public class CreateKeyActivity extends BaseActivity { getIntent().getStringExtra(EXTRA_NAME), getIntent().getStringExtra(EXTRA_EMAIL) ); - loadFragment(null, frag, FragAction.START); + loadFragment(savedInstanceState, frag, FragAction.START); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java index 66424e012..6a4dd7a41 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java @@ -85,7 +85,7 @@ public class CreateKeyEmailFragment extends Fragment { */ private static boolean isEditTextNotEmpty(Context context, EditText editText) { boolean output = true; - if (editText.getText().toString().length() == 0) { + if (editText.getText().length() == 0) { editText.setError(context.getString(R.string.create_key_empty)); editText.requestFocus(); output = false; 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 4871b5ae2..5f6bfdb11 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; import java.util.ArrayList; @@ -72,7 +73,7 @@ public class CreateKeyFinalFragment extends Fragment { String mName; String mEmail; ArrayList mAdditionalEmails; - String mPassphrase; + Passphrase mPassphrase; SaveKeyringParcel mSaveKeyringParcel; @@ -81,14 +82,14 @@ public class CreateKeyFinalFragment extends Fragment { */ public static CreateKeyFinalFragment newInstance(String name, String email, ArrayList additionalEmails, - String passphrase) { + Passphrase passphrase) { CreateKeyFinalFragment frag = new CreateKeyFinalFragment(); Bundle args = new Bundle(); args.putString(ARG_NAME, name); args.putString(ARG_EMAIL, email); args.putStringArrayList(ARG_ADDITIONAL_EMAILS, additionalEmails); - args.putString(ARG_PASSPHRASE, passphrase); + args.putParcelable(ARG_PASSPHRASE, passphrase); frag.setArguments(args); @@ -111,7 +112,7 @@ public class CreateKeyFinalFragment extends Fragment { mName = getArguments().getString(ARG_NAME); mEmail = getArguments().getString(ARG_EMAIL); mAdditionalEmails = getArguments().getStringArrayList(ARG_ADDITIONAL_EMAILS); - mPassphrase = getArguments().getString(ARG_PASSPHRASE); + mPassphrase = getArguments().getParcelable(ARG_PASSPHRASE); // set values mNameEdit.setText(mName); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java index 50a3bd655..093d427b7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java @@ -68,7 +68,7 @@ public class CreateKeyNameFragment extends Fragment { */ private static boolean isEditTextNotEmpty(Context context, EditText editText) { boolean output = true; - if (editText.getText().toString().length() == 0) { + if (editText.getText().length() == 0) { editText.setError(context.getString(R.string.create_key_empty)); editText.requestFocus(); output = false; @@ -79,19 +79,6 @@ public class CreateKeyNameFragment extends Fragment { return output; } - private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) { - boolean output = true; - if (!editText1.getText().toString().equals(editText2.getText().toString())) { - editText2.setError(context.getString(R.string.create_key_passphrases_not_equal)); - editText2.requestFocus(); - output = false; - } else { - editText2.setError(null); - } - - return output; - } - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.create_key_name_fragment, container, false); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java index 055ea608b..a10c94093 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java @@ -34,6 +34,7 @@ import android.widget.EditText; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.ArrayList; @@ -83,7 +84,7 @@ public class CreateKeyPassphraseFragment extends Fragment { */ private static boolean isEditTextNotEmpty(Context context, EditText editText) { boolean output = true; - if (editText.getText().toString().length() == 0) { + if (editText.getText().length() == 0) { editText.setError(context.getString(R.string.create_key_empty)); editText.requestFocus(); output = false; @@ -95,11 +96,13 @@ public class CreateKeyPassphraseFragment extends Fragment { } private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) { - boolean output = true; - if (!editText1.getText().toString().equals(editText2.getText().toString())) { + Passphrase p1 = new Passphrase(editText1); + Passphrase p2 = new Passphrase(editText2); + boolean output = (p1.equals(p2)); + + if (!output) { editText2.setError(context.getString(R.string.create_key_passphrases_not_equal)); editText2.requestFocus(); - output = false; } else { editText2.setError(null); } @@ -171,7 +174,7 @@ public class CreateKeyPassphraseFragment extends Fragment { mName, mEmail, mAdditionalEmails, - mPassphraseEdit.getText().toString() + new Passphrase(mPassphraseEdit.getText()) ); hideKeyboard(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java index 7e33843ea..71832daa5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java @@ -191,7 +191,7 @@ public class DecryptFilesFragment extends DecryptFragment { data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal()); data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri); - data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); + data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -265,7 +265,7 @@ public class DecryptFilesFragment extends DecryptFragment { data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal()); data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri); - data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); + data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -341,7 +341,7 @@ public class DecryptFilesFragment extends DecryptFragment { switch (requestCode) { case REQUEST_CODE_PASSPHRASE: { if (resultCode == Activity.RESULT_OK && data != null) { - mPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + mPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); decryptOriginalFilename(); } return; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index 513300cc5..63508e530 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; +import org.sufficientlysecure.keychain.util.Passphrase; public abstract class DecryptFragment extends Fragment { private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006; @@ -57,7 +58,7 @@ public abstract class DecryptFragment extends Fragment { // State - protected String mPassphrase; + protected Passphrase mPassphrase; protected byte[] mNfcDecryptedSessionKey; @Override @@ -100,7 +101,7 @@ public abstract class DecryptFragment extends Fragment { startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); } - protected void startNfcDecrypt(long subKeyId, String pin, byte[] encryptedSessionKey) { + protected void startNfcDecrypt(long subKeyId, Passphrase pin, byte[] encryptedSessionKey) { // build PendingIntent for Yubikey NFC operations Intent intent = new Intent(getActivity(), NfcActivity.class); intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java index 1b34f6bf0..30cf739fc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java @@ -161,7 +161,7 @@ public class DecryptTextFragment extends DecryptFragment { // data data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal()); data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes()); - data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); + data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase); data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -247,7 +247,7 @@ public class DecryptTextFragment extends DecryptFragment { case REQUEST_CODE_PASSPHRASE: { if (resultCode == Activity.RESULT_OK && data != null) { - mPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + mPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); decryptStart(); } else { getActivity().finish(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index 8d16fe47e..d2c1ab74c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -67,6 +67,7 @@ import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; public class EditKeyFragment extends LoaderFragment implements LoaderManager.LoaderCallbacks { @@ -100,7 +101,7 @@ public class EditKeyFragment extends LoaderFragment implements private SaveKeyringParcel mSaveKeyringParcel; private String mPrimaryUserId; - private String mCurrentPassphrase; + private Passphrase mCurrentPassphrase; /** * Creates new instance of this fragment @@ -267,7 +268,7 @@ public class EditKeyFragment extends LoaderFragment implements switch (requestCode) { case REQUEST_CODE_PASSPHRASE: { if (resultCode == Activity.RESULT_OK && data != null) { - mCurrentPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + mCurrentPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); // Prepare the loaders. Either re-connect with an existing ones, // or start new ones. getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditKeyFragment.this); @@ -386,7 +387,7 @@ public class EditKeyFragment extends LoaderFragment implements // cache new returned passphrase! mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel( - data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE), + (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE), null ); } @@ -593,7 +594,7 @@ public class EditKeyFragment extends LoaderFragment implements getActivity().finish(); } - private void saveInDatabase(String passphrase) { + private void saveInDatabase(Passphrase passphrase) { Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString()); KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( @@ -640,7 +641,7 @@ public class EditKeyFragment extends LoaderFragment implements // fill values for this action Bundle data = new Bundle(); - data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); 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 35dfcb87c..5438f667c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.util.Passphrase; import java.util.Date; @@ -41,7 +42,7 @@ public abstract class EncryptActivity extends BaseActivity { public static final int REQUEST_CODE_NFC = 0x00008002; // For NFC data - protected String mSigningKeyPassphrase = null; + protected Passphrase mSigningKeyPassphrase = null; protected Date mNfcTimestamp = null; protected byte[] mNfcHash = null; @@ -64,7 +65,7 @@ public abstract class EncryptActivity extends BaseActivity { startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); } - protected void startNfcSign(long keyId, String pin, byte[] hashToSign, int hashAlgo) { + protected void startNfcSign(long keyId, Passphrase pin, byte[] hashToSign, int hashAlgo) { // build PendingIntent for Yubikey NFC operations Intent intent = new Intent(this, NfcActivity.class); intent.setAction(NfcActivity.ACTION_SIGN_HASH); @@ -84,7 +85,7 @@ public abstract class EncryptActivity extends BaseActivity { switch (requestCode) { case REQUEST_CODE_PASSPHRASE: { if (resultCode == RESULT_OK && data != null) { - mSigningKeyPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + mSigningKeyPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); startEncrypt(); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java index c89707c34..2a102c6c4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivityInterface.java @@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui; import android.net.Uri; +import org.sufficientlysecure.keychain.util.Passphrase; + import java.util.ArrayList; public interface EncryptActivityInterface { @@ -39,7 +41,7 @@ public interface EncryptActivityInterface { public void setEncryptionKeys(long[] encryptionKeys); public void setEncryptionUsers(String[] encryptionUsers); - public void setPassphrase(String passphrase); + public void setPassphrase(Passphrase passphrase); // ArrayList on purpose as only those are parcelable public ArrayList getInputUris(); 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 f1784fab3..0dd672c90 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesActivity.java @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; 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; import java.util.ArrayList; @@ -72,7 +73,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi private long mEncryptionKeyIds[] = null; private String mEncryptionUserIds[] = null; private long mSigningKeyId = Constants.key.none; - private String mPassphrase = ""; + private Passphrase mPassphrase = new Passphrase(); private ArrayList mInputUris; private ArrayList mOutputUris; @@ -136,7 +137,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi } @Override - public void setPassphrase(String passphrase) { + public void setPassphrase(Passphrase passphrase) { mPassphrase = passphrase; } @@ -243,8 +244,8 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi if (isModeSymmetric()) { Log.d(Constants.TAG, "Symmetric encryption enabled!"); - String passphrase = mPassphrase; - if (passphrase.length() == 0) { + Passphrase passphrase = mPassphrase; + if (passphrase.isEmpty()) { passphrase = null; } data.setSymmetricPassphrase(passphrase); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java index 86731b162..36b3c08f9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptSymmetricFragment.java @@ -28,6 +28,7 @@ import android.view.ViewGroup; import android.widget.EditText; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Passphrase; public class EncryptSymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener { @@ -67,8 +68,13 @@ public class EncryptSymmetricFragment extends Fragment implements EncryptActivit @Override public void afterTextChanged(Editable s) { // update passphrase in EncryptActivity - if (mPassphrase.getText().toString().equals(mPassphraseAgain.getText().toString())) { - mEncryptInterface.setPassphrase(s.toString()); + Passphrase p1 = new Passphrase(mPassphrase.getText()); + Passphrase p2 = new Passphrase(mPassphraseAgain.getText()); + boolean passesEquals = (p1.equals(p2)); + p1.removeFromMemory(); + p2.removeFromMemory(); + if (passesEquals) { + mEncryptInterface.setPassphrase(new Passphrase(mPassphrase.getText())); } else { mEncryptInterface.setPassphrase(null); } 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 14f2c492d..847f745d7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextActivity.java @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.PgpConstants; import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; 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; import java.util.ArrayList; @@ -70,7 +71,7 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv private String mEncryptionUserIds[] = null; // TODO Constants.key.none? What's wrong with a null value? private long mSigningKeyId = Constants.key.none; - private String mPassphrase = ""; + private Passphrase mPassphrase = new Passphrase(); private ArrayList mInputUris; private ArrayList mOutputUris; @@ -134,7 +135,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv } @Override - public void setPassphrase(String passphrase) { + public void setPassphrase(Passphrase passphrase) { + mPassphrase.removeFromMemory(); mPassphrase = passphrase; } @@ -223,8 +225,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv if (isModeSymmetric()) { Log.d(Constants.TAG, "Symmetric encryption enabled!"); - String passphrase = mPassphrase; - if (passphrase.length() == 0) { + Passphrase passphrase = mPassphrase; + if (passphrase.isEmpty()) { passphrase = null; } data.setSymmetricPassphrase(passphrase); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index 48509710a..360d30c82 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; /** @@ -318,7 +319,7 @@ public class PassphraseDialogActivity extends FragmentActivity { positive.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - final String passphrase = mPassphraseEditText.getText().toString(); + final Passphrase passphrase = new Passphrase(mPassphraseEditText); // Early breakout if we are dealing with a symmetric key if (mSecretRing == null) { @@ -395,7 +396,7 @@ public class PassphraseDialogActivity extends FragmentActivity { }); } - private void finishCaching(String passphrase) { + private void finishCaching(Passphrase passphrase) { // any indication this isn't needed anymore, don't do it. if (mIsCancelled || getActivity() == null) { return; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java index b34dc2edc..947c316e0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java @@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { private static final String ARG_MESSENGER = "messenger"; @@ -67,12 +68,12 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi * @param messenger to communicate back after setting the passphrase * @return */ - public static SetPassphraseDialogFragment newInstance(Messenger messenger, String oldPassphrase, int title) { + public static SetPassphraseDialogFragment newInstance(Messenger messenger, Passphrase oldPassphrase, int title) { SetPassphraseDialogFragment frag = new SetPassphraseDialogFragment(); Bundle args = new Bundle(); args.putInt(ARG_TITLE, title); args.putParcelable(ARG_MESSENGER, messenger); - args.putString(ARG_OLD_PASSPHRASE, oldPassphrase); + args.putParcelable(ARG_OLD_PASSPHRASE, oldPassphrase); frag.setArguments(args); @@ -88,7 +89,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi int title = getArguments().getInt(ARG_TITLE); mMessenger = getArguments().getParcelable(ARG_MESSENGER); - String oldPassphrase = getArguments().getString(ARG_OLD_PASSPHRASE); + Passphrase oldPassphrase = getArguments().getParcelable(ARG_OLD_PASSPHRASE); CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity); @@ -103,7 +104,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi mNoPassphraseCheckBox = (CheckBox) view.findViewById(R.id.passphrase_no_passphrase); - if (TextUtils.isEmpty(oldPassphrase)) { + if (oldPassphrase.isEmpty()) { mNoPassphraseCheckBox.setChecked(true); mPassphraseEditText.setEnabled(false); mPassphraseAgainEditText.setEnabled(false); @@ -123,12 +124,12 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi public void onClick(DialogInterface dialog, int id) { dismiss(); - String passphrase1; + Passphrase passphrase1 = new Passphrase(); if (mNoPassphraseCheckBox.isChecked()) { - passphrase1 = ""; + passphrase1.setEmpty(); } else { - passphrase1 = mPassphraseEditText.getText().toString(); - String passphrase2 = mPassphraseAgainEditText.getText().toString(); + passphrase1 = new Passphrase(mPassphraseEditText); + Passphrase passphrase2 = new Passphrase(mPassphraseAgainEditText); if (!passphrase1.equals(passphrase2)) { Toast.makeText( activity, @@ -139,7 +140,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi return; } - if (passphrase1.equals("")) { + if (passphrase1.isEmpty()) { Toast.makeText( activity, getString(R.string.error_message, @@ -152,7 +153,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi // return resulting data back to activity Bundle data = new Bundle(); - data.putString(MESSAGE_NEW_PASSPHRASE, passphrase1); + data.putParcelable(MESSAGE_NEW_PASSPHRASE, passphrase1); sendMessageToHandler(MESSAGE_OKAY, data); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java new file mode 100644 index 000000000..34cb9af8e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 Dominik Schürmann + * + * 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.util; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.Editable; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.Constants; + +import java.util.Arrays; + +public class Passphrase implements Parcelable { + private char[] mPassphrase; + + /** + * According to http://stackoverflow.com/a/15844273 EditText is not using String internally + * but char[]. Thus, we can get the char[] directly from it. + */ + public Passphrase(Editable editable) { + int pl = editable.length(); + mPassphrase = new char[pl]; + editable.getChars(0, pl, mPassphrase, 0); + // TODO: clean up internal char[] of EditText after getting the passphrase? +// editText.getText().replace() + System.gc(); + } + + public Passphrase(EditText editText) { + this(editText.getText()); + } + + public Passphrase(String passphrase) { + mPassphrase = passphrase.toCharArray(); + } + + public Passphrase() { + setEmpty(); + } + + public char[] getCharArray() { + return mPassphrase; + } + + public boolean isEmpty() { + return (mPassphrase.length == 0); + } + + public void setEmpty() { + removeFromMemory(); + mPassphrase = new char[0]; + } + + public void removeFromMemory() { + if (mPassphrase != null) { + Arrays.fill(mPassphrase, ' '); + System.gc(); + } + } + + @Override + public String toString() { + if (Constants.DEBUG) { + return "Passphrase{" + + "mPassphrase=" + Arrays.toString(mPassphrase) + + '}'; + } else { + return "Passphrase: hidden"; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Passphrase that = (Passphrase) o; + if (!Arrays.equals(mPassphrase, that.mPassphrase)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + return mPassphrase != null ? Arrays.hashCode(mPassphrase) : 0; + } + + private Passphrase(Parcel source) { + mPassphrase = source.createCharArray(); + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeCharArray(mPassphrase); + } + + public static final Creator CREATOR = new Creator() { + public Passphrase createFromParcel(final Parcel source) { + return new Passphrase(source); + } + + public Passphrase[] newArray(final int size) { + return new Passphrase[size]; + } + }; + + public int describeContents() { + return 0; + } +} From e65efce333f700eb7f5303e5994e6a1b36f81462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 19 Mar 2015 03:30:12 +0100 Subject: [PATCH 04/13] Fix tests for Passphrase class --- .../operations/CertifyOperationTest.java | 9 ++++--- .../keychain/operations/ExportTest.java | 9 ++++--- .../operations/PromoteKeyOperationTest.java | 3 ++- .../keychain/pgp/PgpEncryptDecryptTest.java | 14 ++++++----- .../keychain/pgp/PgpKeyOperationTest.java | 25 ++++++++++--------- .../pgp/UncachedKeyringCanonicalizeTest.java | 5 ++-- .../pgp/UncachedKeyringMergeTest.java | 19 +++++++------- .../keychain/pgp/UncachedKeyringTest.java | 3 ++- .../keychain/util/TestingUtils.java | 25 ++++++++++++++----- .../keychain/util/Passphrase.java | 4 +++ 10 files changed, 71 insertions(+), 45 deletions(-) diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java index 0af87ada4..7c4b2e91e 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java @@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.TestingUtils; @@ -65,8 +66,8 @@ import java.util.Random; public class CertifyOperationTest { static UncachedKeyRing mStaticRing1, mStaticRing2; - static String mKeyPhrase1 = TestingUtils.genPassphrase(true); - static String mKeyPhrase2 = TestingUtils.genPassphrase(true); + static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true); + static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true); static PrintStream oldShadowStream; @@ -255,13 +256,13 @@ public class CertifyOperationTest { } private CertifyOperation operationWithFakePassphraseCache( - final Long checkMasterKeyId, final Long checkSubKeyId, final String passphrase) { + final Long checkMasterKeyId, final Long checkSubKeyId, final Passphrase passphrase) { return new CertifyOperation(Robolectric.application, new ProviderHelper(Robolectric.application), null, null) { @Override - public String getCachedPassphrase(long masterKeyId, long subKeyId) + public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException { if (checkMasterKeyId != null) { Assert.assertEquals("requested passphrase should be for expected master key id", diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java index b6fdbfc6c..23ea356c8 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java @@ -38,6 +38,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.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.TestingUtils; @@ -51,11 +52,11 @@ import java.util.Iterator; @org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 public class ExportTest { - static String mPassphrase = TestingUtils.genPassphrase(true); + static Passphrase mPassphrase = TestingUtils.genPassphrase(true); static UncachedKeyRing mStaticRing1, mStaticRing2; - static String mKeyPhrase1 = TestingUtils.genPassphrase(true); - static String mKeyPhrase2 = TestingUtils.genPassphrase(true); + static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true); + static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true); static PrintStream oldShadowStream; @@ -94,7 +95,7 @@ public class ExportTest { parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L)); parcel.mAddUserIds.add("snails"); - parcel.mNewUnlock = new ChangeUnlockParcel(null, "1234"); + parcel.mNewUnlock = new ChangeUnlockParcel(null, new Passphrase("1234")); PgpEditKeyResult result = op.createSecretKeyRing(parcel); Assert.assertTrue("initial test key creation must succeed", result.success()); 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 40ade064b..34a4bed25 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 @@ -38,6 +38,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.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.TestingUtils; @@ -50,7 +51,7 @@ import java.util.Iterator; public class PromoteKeyOperationTest { static UncachedKeyRing mStaticRing; - static String mKeyPhrase1 = TestingUtils.genPassphrase(true); + static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true); static PrintStream oldShadowStream; 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 d782230c7..98713c6db 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 @@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.TestingUtils; @@ -47,17 +48,18 @@ import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.io.PrintStream; import java.security.Security; +import java.util.Arrays; import java.util.HashSet; @RunWith(RobolectricTestRunner.class) @org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 public class PgpEncryptDecryptTest { - static String mPassphrase = TestingUtils.genPassphrase(true); + static Passphrase mPassphrase = TestingUtils.genPassphrase(true); static UncachedKeyRing mStaticRing1, mStaticRing2; - static String mKeyPhrase1 = TestingUtils.genPassphrase(true); - static String mKeyPhrase2 = TestingUtils.genPassphrase(true); + static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true); + static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true); static PrintStream oldShadowStream; @@ -180,7 +182,7 @@ public class PgpEncryptDecryptTest { new ProviderHelper(Robolectric.application), null, // new DummyPassphraseCache(mPassphrase, 0L), data, out); - b.setPassphrase(mPassphrase + "x"); + b.setPassphrase(new Passphrase(Arrays.toString(mPassphrase.getCharArray()) + "x")); DecryptVerifyResult result = b.build().execute(); Assert.assertFalse("decryption must succeed", result.success()); Assert.assertEquals("decrypted plaintext should be empty", 0, out.size()); @@ -511,7 +513,7 @@ public class PgpEncryptDecryptTest { private PgpDecryptVerify.Builder builderWithFakePassphraseCache ( InputData data, OutputStream out, - final String passphrase, final Long checkMasterKeyId, final Long checkSubKeyId) { + final Passphrase passphrase, final Long checkMasterKeyId, final Long checkSubKeyId) { return new PgpDecryptVerify.Builder(Robolectric.application, new ProviderHelper(Robolectric.application), @@ -520,7 +522,7 @@ public class PgpEncryptDecryptTest { public PgpDecryptVerify build() { return new PgpDecryptVerify(this) { @Override - public String getCachedPassphrase(long masterKeyId, long subKeyId) + public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException { if (checkMasterKeyId != null) { Assert.assertEquals("requested passphrase should be for expected master key id", 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 8feff4cc4..144501c89 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 @@ -53,6 +53,7 @@ import org.sufficientlysecure.keychain.support.KeyringBuilder; import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import org.sufficientlysecure.keychain.support.TestDataUtil; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.TestingUtils; @@ -72,7 +73,7 @@ import java.util.Random; public class PgpKeyOperationTest { static UncachedKeyRing staticRing; - final static String passphrase = TestingUtils.genPassphrase(); + final static Passphrase passphrase = TestingUtils.genPassphrase(); UncachedKeyRing ring; PgpKeyOperation op; @@ -295,9 +296,9 @@ public class PgpKeyOperationTest { } { - String badphrase = ""; + Passphrase badphrase = new Passphrase(); if (badphrase.equals(passphrase)) { - badphrase = "a"; + badphrase = new Passphrase("a"); } assertModifyFailure("keyring modification with bad passphrase should fail", @@ -1036,7 +1037,7 @@ public class PgpKeyOperationTest { public void testPassphraseChange() throws Exception { // change passphrase to empty - parcel.mNewUnlock = new ChangeUnlockParcel(""); + parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase()); // note that canonicalization here necessarily strips the empty notation packet UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, passphrase); @@ -1050,9 +1051,9 @@ public class PgpKeyOperationTest { PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag); // modify keyring, change to non-empty passphrase - String otherPassphrase = TestingUtils.genPassphrase(true); + Passphrase otherPassphrase = TestingUtils.genPassphrase(true); parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase); - modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB, ""); + modified = applyModificationWithChecks(parcel, modified, onlyA, onlyB, new Passphrase()); Assert.assertEquals("exactly three packets should have been modified (the secret keys)", 3, onlyB.size()); @@ -1075,7 +1076,7 @@ public class PgpKeyOperationTest { Assert.assertEquals("extracted packet should be a secret subkey", PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag); - String otherPassphrase2 = TestingUtils.genPassphrase(true); + Passphrase otherPassphrase2 = TestingUtils.genPassphrase(true); parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase2); { // if we replace a secret key with one without passphrase @@ -1112,7 +1113,7 @@ public class PgpKeyOperationTest { @Test public void testUnlockPin() throws Exception { - String pin = "5235125"; + Passphrase pin = new Passphrase("5235125"); // change passphrase to a pin type parcel.mNewUnlock = new ChangeUnlockParcel(null, pin); @@ -1138,7 +1139,7 @@ public class PgpKeyOperationTest { Thread.sleep(1000); { - parcel.mNewUnlock = new ChangeUnlockParcel("phrayse", null); + parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase("phrayse"), null); applyModificationWithChecks(parcel, modified, onlyA, onlyB, pin, true, false); Assert.assertEquals("exactly four packets should have been removed (the secret keys + notation packet)", @@ -1171,7 +1172,7 @@ public class PgpKeyOperationTest { UncachedKeyRing ring, ArrayList onlyA, ArrayList onlyB, - String passphrase) { + Passphrase passphrase) { return applyModificationWithChecks(parcel, ring, onlyA, onlyB, passphrase, true, true); } @@ -1180,7 +1181,7 @@ public class PgpKeyOperationTest { UncachedKeyRing ring, ArrayList onlyA, ArrayList onlyB, - String passphrase, + Passphrase passphrase, boolean canonicalize, boolean constantCanonicalize) { @@ -1257,7 +1258,7 @@ public class PgpKeyOperationTest { } private void assertModifyFailure(String reason, UncachedKeyRing ring, - SaveKeyringParcel parcel, String passphrase, LogType expected) + SaveKeyringParcel parcel, Passphrase passphrase, LogType expected) throws Exception { CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0); diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java index f9e0d52c3..bfe34b14b 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringCanonicalizeTest.java @@ -61,6 +61,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; +import org.sufficientlysecure.keychain.util.Passphrase; import java.io.ByteArrayInputStream; import java.security.Security; @@ -111,7 +112,7 @@ public class UncachedKeyringCanonicalizeTest { } // passphrase is tested in PgpKeyOperationTest, just use empty here - parcel.mNewUnlock = new ChangeUnlockParcel(""); + parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase()); PgpKeyOperation op = new PgpKeyOperation(null); PgpEditKeyResult result = op.createSecretKeyRing(parcel); @@ -546,7 +547,7 @@ public class UncachedKeyringCanonicalizeTest { CanonicalizedSecretKeyRing canonicalized = (CanonicalizedSecretKeyRing) ring.canonicalize(log, 0); CanonicalizedSecretKey masterSecretKey = canonicalized.getSecretKey(); - masterSecretKey.unlock(""); + masterSecretKey.unlock(new Passphrase()); PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature( masterPublicKey, masterSecretKey.getPrivateKey(), masterSecretKey.getPrivateKey(), diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java index ccd47d0ee..712f0563d 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringMergeTest.java @@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; +import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.ProgressScaler; import java.io.ByteArrayInputStream; @@ -105,7 +106,7 @@ public class UncachedKeyringMergeTest { } // passphrase is tested in PgpKeyOperationTest, just use empty here - parcel.mNewUnlock = new ChangeUnlockParcel(""); + parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase()); PgpKeyOperation op = new PgpKeyOperation(null); OperationResult.OperationLog log = new OperationResult.OperationLog(); @@ -122,7 +123,7 @@ public class UncachedKeyringMergeTest { parcel.mAddUserIds.add("shy"); // passphrase is tested in PgpKeyOperationTest, just use empty here - parcel.mNewUnlock = new ChangeUnlockParcel(""); + parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase()); PgpKeyOperation op = new PgpKeyOperation(null); OperationResult.OperationLog log = new OperationResult.OperationLog(); @@ -185,11 +186,11 @@ public class UncachedKeyringMergeTest { parcel.reset(); parcel.mAddUserIds.add("flim"); - modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); + modifiedA = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing(); parcel.reset(); parcel.mAddUserIds.add("flam"); - modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); + modifiedB = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing(); } { // merge A into base @@ -226,8 +227,8 @@ public class UncachedKeyringMergeTest { parcel.reset(); parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L)); - modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); - modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); + modifiedA = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing(); + modifiedB = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing(); subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2); subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2); @@ -268,7 +269,7 @@ public class UncachedKeyringMergeTest { parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(ringA, 1)); CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing( ringA.getEncoded(), false, 0); - modified = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); + modified = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing(); } { @@ -293,7 +294,7 @@ public class UncachedKeyringMergeTest { CanonicalizedSecretKey secretKey = new CanonicalizedSecretKeyRing( ringB.getEncoded(), false, 0).getSecretKey(); - secretKey.unlock(""); + secretKey.unlock(new Passphrase()); // sign all user ids modified = secretKey.certifyUserIds(publicRing, publicRing.getPublicKey().getUnorderedUserIds(), null, null); } @@ -362,7 +363,7 @@ public class UncachedKeyringMergeTest { CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing( ringA.getEncoded(), false, 0); - modified = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); + modified = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing(); } { diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java index 65395f1ab..a6159de4e 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/UncachedKeyringTest.java @@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; +import org.sufficientlysecure.keychain.util.Passphrase; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -70,7 +71,7 @@ public class UncachedKeyringTest { parcel.mAddUserAttribute.add(uat); } // passphrase is tested in PgpKeyOperationTest, just use empty here - parcel.mNewUnlock = new ChangeUnlockParcel(""); + parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase()); PgpKeyOperation op = new PgpKeyOperation(null); PgpEditKeyResult result = op.createSecretKeyRing(parcel); diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java index ee0379653..f45e82c3e 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java @@ -1,16 +1,29 @@ package org.sufficientlysecure.keychain.util; +/* + * Copyright (C) 2014 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 . + */ import java.util.Random; -/** - * Created by valodim on 9/15/14. - */ public class TestingUtils { - public static String genPassphrase() { + public static Passphrase genPassphrase() { return genPassphrase(false); } - public static String genPassphrase(boolean noEmpty) { + public static Passphrase genPassphrase(boolean noEmpty) { String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789!@#$%^&*()-_="; Random r = new Random(); StringBuilder passbuilder = new StringBuilder(); @@ -19,6 +32,6 @@ public class TestingUtils { passbuilder.append(chars.charAt(r.nextInt(chars.length()))); } System.out.println("Generated passphrase: '" + passbuilder.toString() + "'"); - return passbuilder.toString(); + return new Passphrase(passbuilder.toString()); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java index 34cb9af8e..45dac3b64 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java @@ -46,6 +46,10 @@ public class Passphrase implements Parcelable { this(editText.getText()); } + public Passphrase(char[] passphrase) { + mPassphrase = passphrase; + } + public Passphrase(String passphrase) { mPassphrase = passphrase.toCharArray(); } From 834ce03f9e4de465782b67bf7827445c6028eb8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 19 Mar 2015 03:30:49 +0100 Subject: [PATCH 05/13] Fix header --- .../org/sufficientlysecure/keychain/util/TestingUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java index f45e82c3e..1d7952464 100644 --- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java +++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/util/TestingUtils.java @@ -1,4 +1,3 @@ -package org.sufficientlysecure.keychain.util; /* * Copyright (C) 2014 Vincent Breitmoser * @@ -16,6 +15,8 @@ package org.sufficientlysecure.keychain.util; * along with this program. If not, see . */ +package org.sufficientlysecure.keychain.util; + import java.util.Random; public class TestingUtils { From 0bcc2793c334ad04d3f7fd9f401eb6ab485f8c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 19 Mar 2015 13:58:26 +0100 Subject: [PATCH 06/13] Improve Passphrase class with finalize() --- .../keychain/util/Passphrase.java | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java index 45dac3b64..06efdde4d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Passphrase.java @@ -26,6 +26,16 @@ import org.sufficientlysecure.keychain.Constants; import java.util.Arrays; +/** + * Passwords should not be stored as Strings in memory. + * This class wraps a char[] that can be erased after it is no longer used. + * See also: + *

+ * http://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#PBEEx + * https://github.com/c-a-m/passfault/blob/master/core/src/main/java/org/owasp/passfault/SecureString.java + * http://stackoverflow.com/q/8881291 + * http://stackoverflow.com/a/15844273 + */ public class Passphrase implements Parcelable { private char[] mPassphrase; @@ -39,7 +49,6 @@ public class Passphrase implements Parcelable { editable.getChars(0, pl, mPassphrase, 0); // TODO: clean up internal char[] of EditText after getting the passphrase? // editText.getText().replace() - System.gc(); } public Passphrase(EditText editText) { @@ -54,6 +63,9 @@ public class Passphrase implements Parcelable { mPassphrase = passphrase.toCharArray(); } + /** + * Creates a passphrase object with an empty ("") passphrase + */ public Passphrase() { setEmpty(); } @@ -62,22 +74,38 @@ public class Passphrase implements Parcelable { return mPassphrase; } - public boolean isEmpty() { - return (mPassphrase.length == 0); - } - public void setEmpty() { removeFromMemory(); mPassphrase = new char[0]; } + public boolean isEmpty() { + return (length() == 0); + } + + public int length() { + return mPassphrase.length; + } + + public char charAt(int index) { + return mPassphrase[index]; + } + + /** + * Manually clear the underlying array holding the characters + */ public void removeFromMemory() { if (mPassphrase != null) { Arrays.fill(mPassphrase, ' '); - System.gc(); } } + @Override + public void finalize() throws Throwable { + removeFromMemory(); + super.finalize(); + } + @Override public String toString() { if (Constants.DEBUG) { From d122d27ebadbc883ba85e2256f1a0a29f791ee60 Mon Sep 17 00:00:00 2001 From: Manoj Khanna Date: Wed, 18 Mar 2015 17:53:24 +0530 Subject: [PATCH 07/13] Fixed a crash on ViewKeyActivity This occurs when deleting a key with a stripped master key from the ViewKeyActivity. --- .../keychain/ui/ViewKeyActivity.java | 95 +++++++++++-------- .../keychain/util/ExportHelper.java | 15 --- 2 files changed, 53 insertions(+), 57 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 13a5ac269..5834fa502 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -74,6 +74,8 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; @@ -310,31 +312,31 @@ public class ViewKeyActivity extends BaseActivity implements return true; } case R.id.menu_key_view_export_file: { - Intent mIntent = new Intent(this,PassphraseDialogActivity.class); - long keyId=0; try { - keyId = new ProviderHelper(this) - .getCachedPublicKeyRing(mDataUri) - .extractOrGetMasterKeyId(); - } catch (PgpKeyNotFoundException e) { - e.printStackTrace(); + if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) { + exportToFile(mDataUri, mExportHelper, mProviderHelper); + return true; + } + + startPassphraseActivity(REQUEST_EXPORT); + } catch (PassphraseCacheService.KeyNotFoundException e) { + // This happens when the master key is stripped + exportToFile(mDataUri, mExportHelper, mProviderHelper); } - mIntent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID,keyId); - startActivityForResult(mIntent,REQUEST_EXPORT); return true; } case R.id.menu_key_view_delete: { - Intent mIntent = new Intent(this,PassphraseDialogActivity.class); - long keyId=0; try { - keyId = new ProviderHelper(this) - .getCachedPublicKeyRing(mDataUri) - .extractOrGetMasterKeyId(); - } catch (PgpKeyNotFoundException e) { - e.printStackTrace(); + if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) { + deleteKey(); + return true; + } + + startPassphraseActivity(REQUEST_DELETE); + } catch (PassphraseCacheService.KeyNotFoundException e) { + // This happens when the master key is stripped + deleteKey(); } - mIntent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID,keyId); - startActivityForResult(mIntent,REQUEST_DELETE); return true; } case R.id.menu_key_view_advanced: { @@ -473,22 +475,32 @@ public class ViewKeyActivity extends BaseActivity implements ActivityCompat.startActivity(this, qrCodeIntent, opts); } - private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) - throws ProviderHelper.NotFoundException { - Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri); - - HashMap data = providerHelper.getGenericData( - baseUri, - new String[]{KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET}, - new int[]{ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER}); - - exportHelper.showExportKeysDialog( - new long[]{(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)}, - Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0) - ); + private void startPassphraseActivity(int requestCode) { + Intent intent = new Intent(this, PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mMasterKeyId); + startActivityForResult(intent, requestCode); } - private void deleteKey(Uri dataUri, ExportHelper exportHelper) { + private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) { + try { + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri); + + HashMap data = providerHelper.getGenericData( + baseUri, + new String[]{KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET}, + new int[]{ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER}); + + exportHelper.showExportKeysDialog( + new long[]{(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)}, + Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0) + ); + } catch (ProviderHelper.NotFoundException e) { + Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR); + Log.e(Constants.TAG, "Key not found", e); + } + } + + private void deleteKey() { // Message is received after key is deleted Handler returnHandler = new Handler() { @Override @@ -500,7 +512,11 @@ public class ViewKeyActivity extends BaseActivity implements } }; - exportHelper.deleteKey(dataUri, returnHandler); + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(returnHandler); + DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, + new long[]{ mMasterKeyId }); + deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog"); } @Override @@ -531,17 +547,12 @@ public class ViewKeyActivity extends BaseActivity implements } if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK){ - deleteKey(mDataUri, mExportHelper); - } - if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK){ - try { - exportToFile(mDataUri, mExportHelper, mProviderHelper); - } catch (ProviderHelper.NotFoundException e) { - Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR); - Log.e(Constants.TAG, "Key not found", e); - } + deleteKey(); } + if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK){ + exportToFile(mDataUri, mExportHelper, mProviderHelper); + } if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java index cda5892fe..7b164f2b2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java @@ -47,21 +47,6 @@ public class ExportHelper { this.mActivity = activity; } - public void deleteKey(Uri dataUri, Handler deleteHandler) { - try { - long masterKeyId = new ProviderHelper(mActivity).getCachedPublicKeyRing(dataUri) - .extractOrGetMasterKeyId(); - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(deleteHandler); - DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - new long[]{ masterKeyId }); - deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog"); - } catch (PgpKeyNotFoundException e) { - Log.e(Constants.TAG, "key not found!", e); - } - } - /** * Show dialog where to export keys */ From 6120365ee1e425acbc270b17c5cad621710777a9 Mon Sep 17 00:00:00 2001 From: Manoj Khanna Date: Thu, 19 Mar 2015 19:58:24 +0530 Subject: [PATCH 08/13] Fix for Issue #1114 Displays the snackbar above the keyboard on all activities. --- .../keychain/ui/util/Notify.java | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java index 3bc29edb6..9736b5765 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/Notify.java @@ -19,6 +19,11 @@ package org.sufficientlysecure.keychain.ui.util; import android.app.Activity; import android.content.res.Resources; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentManager; +import android.view.View; +import android.view.ViewGroup; import com.nispok.snackbar.Snackbar; import com.nispok.snackbar.Snackbar.SnackbarDuration; @@ -61,11 +66,11 @@ public class Notify { break; } - SnackbarManager.show(bar); + showSnackbar(activity, bar); } - public static Showable createNotify (Activity activity, int resId, int duration, Style style) { + public static Showable createNotify (final Activity activity, int resId, int duration, Style style) { final Snackbar bar = getSnackbar(activity) .text(resId); @@ -90,7 +95,7 @@ public class Notify { return new Showable () { @Override public void show() { - SnackbarManager.show(bar); + showSnackbar(activity, bar); } }; } @@ -104,7 +109,7 @@ public class Notify { return createNotify(activity, msg, duration, style, null, 0); } - public static Showable createNotify(Activity activity, String msg, int duration, Style style, + public static Showable createNotify(final Activity activity, String msg, int duration, Style style, final ActionListener listener, int resIdAction) { final Snackbar bar = getSnackbar(activity) @@ -141,7 +146,7 @@ public class Notify { return new Showable () { @Override public void show() { - SnackbarManager.show(bar); + showSnackbar(activity, bar); } }; @@ -178,6 +183,26 @@ public class Notify { return bar; } + private static void showSnackbar(Activity activity, Snackbar snackbar) { + if (activity instanceof FragmentActivity) { + FragmentManager fragmentManager = ((FragmentActivity) activity).getSupportFragmentManager(); + + int count = fragmentManager.getBackStackEntryCount(); + Fragment fragment = fragmentManager.getFragments().get(count > 0 ? count - 1 : 0); + + if (fragment != null) { + View view = fragment.getView(); + + if (view != null) { + SnackbarManager.show(snackbar, (ViewGroup) view); + return; + } + } + } + + SnackbarManager.show(snackbar); + } + public interface Showable { public void show(); From 98817d13ee2e33b92f3a339df312709be027e964 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 19 Mar 2015 17:16:23 +0100 Subject: [PATCH 09/13] Manage my keys wizard --- OpenKeychain/src/main/AndroidManifest.xml | 8 +- .../keychain/ui/CreateKeyActivity.java | 102 ++++++++---- .../keychain/ui/CreateKeyEmailFragment.java | 64 +++---- .../keychain/ui/CreateKeyFinalFragment.java | 60 +++---- .../keychain/ui/CreateKeyNameFragment.java | 40 ++--- .../ui/CreateKeyPassphraseFragment.java | 45 ++--- .../keychain/ui/CreateKeyStartFragment.java | 157 ++++++++++++++++++ .../keychain/ui/FirstTimeActivity.java | 110 ------------ .../keychain/ui/KeyListFragment.java | 3 +- .../keychain/ui/MainActivity.java | 4 +- .../keychain/ui/widget/EmailEditText.java | 9 +- .../keychain/ui/widget/NameEditText.java | 6 +- .../res/layout/create_key_name_fragment.xml | 4 +- .../res/layout/create_key_start_fragment.xml | 104 ++++++++++++ .../main/res/layout/first_time_activity.xml | 137 --------------- OpenKeychain/src/main/res/menu/key_list.xml | 2 +- .../src/main/res/values-cs/strings.xml | 2 +- .../src/main/res/values-de/strings.xml | 2 +- .../src/main/res/values-es/strings.xml | 2 +- .../src/main/res/values-eu/strings.xml | 2 +- .../src/main/res/values-fi/strings.xml | 2 +- .../src/main/res/values-fr/strings.xml | 2 +- .../src/main/res/values-it/strings.xml | 2 +- .../src/main/res/values-ja/strings.xml | 2 +- .../src/main/res/values-nl/strings.xml | 2 +- .../src/main/res/values-pl/strings.xml | 2 +- .../src/main/res/values-ru/strings.xml | 2 +- .../src/main/res/values-sl/strings.xml | 2 +- .../src/main/res/values-sr/strings.xml | 2 +- .../src/main/res/values-sv/strings.xml | 2 +- .../src/main/res/values-tr/strings.xml | 2 +- .../src/main/res/values-uk/strings.xml | 2 +- .../src/main/res/values-zh-rTW/strings.xml | 2 +- .../src/main/res/values-zh/strings.xml | 2 +- OpenKeychain/src/main/res/values/strings.xml | 8 +- 35 files changed, 462 insertions(+), 437 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java create mode 100644 OpenKeychain/src/main/res/layout/create_key_start_fragment.xml delete mode 100644 OpenKeychain/src/main/res/layout/first_time_activity.xml diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 91c84fb18..d9f9766c9 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -86,16 +86,10 @@ - additionalEmails; - char[] passphrase; + public static final String FRAGMENT_TAG = "currentFragment"; + + String mName; + String mEmail; + ArrayList mAdditionalEmails; + Passphrase mPassphrase; + boolean mFirstTime; + + Fragment mCurrentFragment; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Check whether we're recreating a previously destroyed instance + if (savedInstanceState != null) { + // Restore value of members from saved state + mName = savedInstanceState.getString(EXTRA_NAME); + mEmail = savedInstanceState.getString(EXTRA_EMAIL); + mAdditionalEmails = savedInstanceState.getStringArrayList(EXTRA_ADDITIONAL_EMAILS); + mPassphrase = savedInstanceState.getParcelable(EXTRA_PASSPHRASE); + mFirstTime = savedInstanceState.getBoolean(EXTRA_FIRST_TIME); + + mCurrentFragment = getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); + } else { + // Initialize members with default values for a new instance + mName = getIntent().getStringExtra(EXTRA_NAME); + mEmail = getIntent().getStringExtra(EXTRA_EMAIL); + mFirstTime = getIntent().getBooleanExtra(EXTRA_FIRST_TIME, false); + + // Start with first fragment of wizard + CreateKeyStartFragment frag = CreateKeyStartFragment.newInstance(); + loadFragment(frag, FragAction.START); + } + + if (mFirstTime) { + setTitle(R.string.app_name); + setActionBarIcon(R.drawable.ic_launcher); + } else { + setTitle(R.string.title_manage_my_keys); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putString(EXTRA_NAME, mName); + outState.putString(EXTRA_EMAIL, mEmail); + outState.putStringArrayList(EXTRA_ADDITIONAL_EMAILS, mAdditionalEmails); + outState.putParcelable(EXTRA_PASSPHRASE, mPassphrase); + outState.putBoolean(EXTRA_FIRST_TIME, mFirstTime); + } + + @Override + protected void initLayout() { + setContentView(R.layout.create_key_activity); } public static enum FragAction { @@ -43,41 +99,17 @@ public class CreateKeyActivity extends BaseActivity { TO_LEFT } - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // pass extras into fragment - CreateKeyNameFragment frag = - CreateKeyNameFragment.newInstance( - getIntent().getStringExtra(EXTRA_NAME), - getIntent().getStringExtra(EXTRA_EMAIL) - ); - loadFragment(savedInstanceState, frag, FragAction.START); - } - - @Override - protected void initLayout() { - setContentView(R.layout.create_key_activity); - } - - public void loadFragment(Bundle savedInstanceState, Fragment fragment, FragAction action) { - // 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; - } + public void loadFragment(Fragment fragment, FragAction action) { + mCurrentFragment = fragment; // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); switch (action) { case START: transaction.setCustomAnimations(0, 0); - transaction.replace(R.id.create_key_fragment_container, fragment) - .commitAllowingStateLoss(); + transaction.replace(R.id.create_key_fragment_container, fragment, FRAGMENT_TAG) + .commit(); break; case TO_LEFT: getSupportFragmentManager().popBackStackImmediate(); @@ -86,8 +118,8 @@ public class CreateKeyActivity extends BaseActivity { transaction.setCustomAnimations(R.anim.frag_slide_in_from_right, R.anim.frag_slide_out_to_left, R.anim.frag_slide_in_from_left, R.anim.frag_slide_out_to_right); transaction.addToBackStack(null); - transaction.replace(R.id.create_key_fragment_container, fragment) - .commitAllowingStateLoss(); + transaction.replace(R.id.create_key_fragment_container, fragment, FRAGMENT_TAG) + .commit(); break; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java index 6a4dd7a41..2e8a1f370 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyEmailFragment.java @@ -46,16 +46,12 @@ import java.util.List; public class CreateKeyEmailFragment extends Fragment { - public static final String ARG_NAME = "name"; - public static final String ARG_EMAIL = "email"; - CreateKeyActivity mCreateKeyActivity; EmailEditText mEmailEdit; RecyclerView mEmailsRecyclerView; View mBackButton; View mNextButton; - String mName; ArrayList mAdditionalEmailModels; EmailAdapter mEmailAdapter; @@ -63,13 +59,10 @@ public class CreateKeyEmailFragment extends Fragment { /** * Creates new instance of this fragment */ - public static CreateKeyEmailFragment newInstance(String name, String email) { + public static CreateKeyEmailFragment newInstance() { CreateKeyEmailFragment frag = new CreateKeyEmailFragment(); Bundle args = new Bundle(); - args.putString(ARG_NAME, name); - args.putString(ARG_EMAIL, email); - frag.setArguments(args); return frag; @@ -106,31 +99,33 @@ public class CreateKeyEmailFragment extends Fragment { mEmailsRecyclerView = (RecyclerView) view.findViewById(R.id.create_key_emails); // initial values - mName = getArguments().getString(ARG_NAME); - String email = getArguments().getString(ARG_EMAIL); - mEmailEdit.setText(email); + mEmailEdit.setText(mCreateKeyActivity.mEmail); // focus empty edit fields - if (email == null) { + if (mCreateKeyActivity.mEmail == null) { mEmailEdit.requestFocus(); } mBackButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT); + mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT); } }); mNextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - createKeyCheck(); + nextClicked(); } }); mEmailsRecyclerView.setHasFixedSize(true); mEmailsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); mEmailsRecyclerView.setItemAnimator(new DefaultItemAnimator()); + // initial values mAdditionalEmailModels = new ArrayList<>(); + if (mCreateKeyActivity.mAdditionalEmails != null) { + setAdditionalEmails(mCreateKeyActivity.mAdditionalEmails); + } mEmailAdapter = new EmailAdapter(mAdditionalEmailModels, new View.OnClickListener() { @Override public void onClick(View v) { @@ -171,25 +166,38 @@ public class CreateKeyEmailFragment extends Fragment { mCreateKeyActivity = (CreateKeyActivity) getActivity(); } - private void createKeyCheck() { + private void nextClicked() { if (isEditTextNotEmpty(getActivity(), mEmailEdit)) { + // save state + mCreateKeyActivity.mEmail = mEmailEdit.getText().toString(); + mCreateKeyActivity.mAdditionalEmails = getAdditionalEmails(); - ArrayList emails = new ArrayList<>(); - for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) { - emails.add(holder.toString()); - } - - CreateKeyPassphraseFragment frag = - CreateKeyPassphraseFragment.newInstance( - mName, - mEmailEdit.getText().toString(), - emails - ); - - mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT); + CreateKeyPassphraseFragment frag = CreateKeyPassphraseFragment.newInstance(); + mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); } } + private ArrayList getAdditionalEmails() { + ArrayList emails = new ArrayList<>(); + for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) { + emails.add(holder.toString()); + } + return emails; + } + + private void setAdditionalEmails(ArrayList emails) { + for (String email : emails) { + mAdditionalEmailModels.add(new EmailAdapter.ViewModel(email)); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + // save state in activity + mCreateKeyActivity.mAdditionalEmails = getAdditionalEmails(); + } + public static class EmailAdapter extends RecyclerView.Adapter { private List mDataset; private View.OnClickListener mFooterOnClickListener; 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 5f6bfdb11..75f49a426 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -65,32 +65,15 @@ public class CreateKeyFinalFragment extends Fragment { TextView mEditText; View mEditButton; - public static final String ARG_NAME = "name"; - public static final String ARG_EMAIL = "email"; - public static final String ARG_ADDITIONAL_EMAILS = "emails"; - public static final String ARG_PASSPHRASE = "passphrase"; - - String mName; - String mEmail; - ArrayList mAdditionalEmails; - Passphrase mPassphrase; - SaveKeyringParcel mSaveKeyringParcel; /** * Creates new instance of this fragment */ - public static CreateKeyFinalFragment newInstance(String name, String email, - ArrayList additionalEmails, - Passphrase passphrase) { + public static CreateKeyFinalFragment newInstance() { CreateKeyFinalFragment frag = new CreateKeyFinalFragment(); Bundle args = new Bundle(); - args.putString(ARG_NAME, name); - args.putString(ARG_EMAIL, email); - args.putStringArrayList(ARG_ADDITIONAL_EMAILS, additionalEmails); - args.putParcelable(ARG_PASSPHRASE, passphrase); - frag.setArguments(args); return frag; @@ -108,17 +91,11 @@ public class CreateKeyFinalFragment extends Fragment { mEditText = (TextView) view.findViewById(R.id.create_key_edit_text); mEditButton = view.findViewById(R.id.create_key_edit_button); - // get args - mName = getArguments().getString(ARG_NAME); - mEmail = getArguments().getString(ARG_EMAIL); - mAdditionalEmails = getArguments().getStringArrayList(ARG_ADDITIONAL_EMAILS); - mPassphrase = getArguments().getParcelable(ARG_PASSPHRASE); - // set values - mNameEdit.setText(mName); - if (mAdditionalEmails != null && mAdditionalEmails.size() > 0) { - String emailText = mEmail + ", "; - Iterator it = mAdditionalEmails.iterator(); + mNameEdit.setText(mCreateKeyActivity.mName); + if (mCreateKeyActivity.mAdditionalEmails != null && mCreateKeyActivity.mAdditionalEmails.size() > 0) { + String emailText = mCreateKeyActivity.mEmail + ", "; + Iterator it = mCreateKeyActivity.mAdditionalEmails.iterator(); while (it.hasNext()) { Object next = it.next(); emailText += next; @@ -128,7 +105,7 @@ public class CreateKeyFinalFragment extends Fragment { } mEmailEdit.setText(emailText); } else { - mEmailEdit.setText(mEmail); + mEmailEdit.setText(mCreateKeyActivity.mEmail); } mCreateButton.setOnClickListener(new View.OnClickListener() { @@ -141,7 +118,7 @@ public class CreateKeyFinalFragment extends Fragment { mBackButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT); + mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT); } }); @@ -157,6 +134,12 @@ 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) { @@ -187,17 +170,22 @@ public class CreateKeyFinalFragment extends Fragment { Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L)); mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L)); - String userId = KeyRing.createUserId(new KeyRing.UserId(mName, mEmail, null)); + String userId = KeyRing.createUserId( + new KeyRing.UserId(mCreateKeyActivity.mName, mCreateKeyActivity.mEmail, null) + ); mSaveKeyringParcel.mAddUserIds.add(userId); mSaveKeyringParcel.mChangePrimaryUserId = userId; - if (mAdditionalEmails != null && mAdditionalEmails.size() > 0) { - for (String email : mAdditionalEmails) { - String thisUserId = KeyRing.createUserId(new KeyRing.UserId(mName, email, null)); + if (mCreateKeyActivity.mAdditionalEmails != null + && mCreateKeyActivity.mAdditionalEmails.size() > 0) { + for (String email : mCreateKeyActivity.mAdditionalEmails) { + String thisUserId = KeyRing.createUserId( + new KeyRing.UserId(mCreateKeyActivity.mName, email, null) + ); mSaveKeyringParcel.mAddUserIds.add(thisUserId); } } - mSaveKeyringParcel.mNewUnlock = mPassphrase != null - ? new ChangeUnlockParcel(mPassphrase, null) + mSaveKeyringParcel.mNewUnlock = mCreateKeyActivity.mPassphrase != null + ? new ChangeUnlockParcel(mCreateKeyActivity.mPassphrase, null) : null; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java index 093d427b7..7480367bb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyNameFragment.java @@ -24,34 +24,26 @@ import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.InputMethodManager; import android.widget.EditText; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; -import org.sufficientlysecure.keychain.ui.widget.EmailEditText; import org.sufficientlysecure.keychain.ui.widget.NameEditText; public class CreateKeyNameFragment extends Fragment { - public static final String ARG_NAME = "name"; - public static final String ARG_EMAIL = "email"; - CreateKeyActivity mCreateKeyActivity; NameEditText mNameEdit; + View mBackButton; View mNextButton; - String mEmail; - /** * Creates new instance of this fragment */ - public static CreateKeyNameFragment newInstance(String name, String email) { + public static CreateKeyNameFragment newInstance() { CreateKeyNameFragment frag = new CreateKeyNameFragment(); Bundle args = new Bundle(); - args.putString(ARG_NAME, name); - args.putString(ARG_EMAIL, email); frag.setArguments(args); @@ -84,21 +76,26 @@ public class CreateKeyNameFragment extends Fragment { View view = inflater.inflate(R.layout.create_key_name_fragment, container, false); mNameEdit = (NameEditText) view.findViewById(R.id.create_key_name); + mBackButton = view.findViewById(R.id.create_key_back_button); mNextButton = view.findViewById(R.id.create_key_next_button); // initial values - String name = getArguments().getString(ARG_NAME); - mEmail = getArguments().getString(ARG_EMAIL); - mNameEdit.setText(name); + mNameEdit.setText(mCreateKeyActivity.mName); // focus empty edit fields - if (name == null) { + if (mCreateKeyActivity.mName == null) { mNameEdit.requestFocus(); } + mBackButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT); + } + }); mNextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - createKeyCheck(); + nextClicked(); } }); @@ -111,16 +108,13 @@ public class CreateKeyNameFragment extends Fragment { mCreateKeyActivity = (CreateKeyActivity) getActivity(); } - private void createKeyCheck() { + private void nextClicked() { if (isEditTextNotEmpty(getActivity(), mNameEdit)) { + // save state + mCreateKeyActivity.mName = mNameEdit.getText().toString(); - CreateKeyEmailFragment frag = - CreateKeyEmailFragment.newInstance( - mNameEdit.getText().toString(), - mEmail - ); - - mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT); + CreateKeyEmailFragment frag = CreateKeyEmailFragment.newInstance(); + mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java index a10c94093..32173edf7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyPassphraseFragment.java @@ -21,6 +21,7 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.support.v4.app.Fragment; +import android.text.Editable; import android.text.method.HideReturnsTransformationMethod; import android.text.method.PasswordTransformationMethod; import android.view.LayoutInflater; @@ -37,18 +38,10 @@ import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText; import org.sufficientlysecure.keychain.util.Passphrase; import java.util.ArrayList; +import java.util.Arrays; public class CreateKeyPassphraseFragment extends Fragment { - public static final String ARG_NAME = "name"; - public static final String ARG_EMAIL = "email"; - public static final String ARG_ADDITIONAL_EMAILS = "emails"; - - // model - String mName; - String mEmail; - ArrayList mAdditionalEmails; - // view CreateKeyActivity mCreateKeyActivity; PassphraseEditText mPassphraseEdit; @@ -60,15 +53,10 @@ public class CreateKeyPassphraseFragment extends Fragment { /** * Creates new instance of this fragment */ - public static CreateKeyPassphraseFragment newInstance(String name, String email, - ArrayList additionalEmails) { + public static CreateKeyPassphraseFragment newInstance() { CreateKeyPassphraseFragment frag = new CreateKeyPassphraseFragment(); Bundle args = new Bundle(); - args.putString(ARG_NAME, name); - args.putString(ARG_EMAIL, email); - args.putStringArrayList(ARG_ADDITIONAL_EMAILS, additionalEmails); - frag.setArguments(args); return frag; @@ -121,9 +109,12 @@ public class CreateKeyPassphraseFragment extends Fragment { mNextButton = view.findViewById(R.id.create_key_next_button); // initial values - mName = getArguments().getString(ARG_NAME); - mEmail = getArguments().getString(ARG_EMAIL); - mAdditionalEmails = getArguments().getStringArrayList(ARG_ADDITIONAL_EMAILS); + // TODO: using String here is unsafe... + if (mCreateKeyActivity.mPassphrase != null) { + mPassphraseEdit.setText(Arrays.toString(mCreateKeyActivity.mPassphrase.getCharArray())); + mPassphraseEditAgain.setText(Arrays.toString(mCreateKeyActivity.mPassphrase.getCharArray())); + } + mPassphraseEdit.requestFocus(); mBackButton.setOnClickListener(new View.OnClickListener() { @Override @@ -134,7 +125,7 @@ public class CreateKeyPassphraseFragment extends Fragment { mNextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - createKeyCheck(); + nextClicked(); } }); mShowPassphrase.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @@ -162,23 +153,19 @@ public class CreateKeyPassphraseFragment extends Fragment { private void back() { hideKeyboard(); - mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT); + mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT); } - private void createKeyCheck() { + private void nextClicked() { if (isEditTextNotEmpty(getActivity(), mPassphraseEdit) && areEditTextsEqual(getActivity(), mPassphraseEdit, mPassphraseEditAgain)) { - CreateKeyFinalFragment frag = - CreateKeyFinalFragment.newInstance( - mName, - mEmail, - mAdditionalEmails, - new Passphrase(mPassphraseEdit.getText()) - ); + // save state + mCreateKeyActivity.mPassphrase = new Passphrase(mPassphraseEdit); + CreateKeyFinalFragment frag = CreateKeyFinalFragment.newInstance(); hideKeyboard(); - mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT); + mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java new file mode 100644 index 000000000..180a52a1c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyStartFragment.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.Fragment; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; +import org.sufficientlysecure.keychain.ui.dialog.AddEmailDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; +import org.sufficientlysecure.keychain.ui.widget.EmailEditText; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; + +import java.util.ArrayList; +import java.util.List; + +public class CreateKeyStartFragment extends Fragment { + + CreateKeyActivity mCreateKeyActivity; + + View mCreateKey; + View mImportKey; + View mYubiKey; + TextView mCancel; + public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012; + + /** + * Creates new instance of this fragment + */ + public static CreateKeyStartFragment newInstance() { + CreateKeyStartFragment frag = new CreateKeyStartFragment(); + + Bundle args = new Bundle(); + + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.create_key_start_fragment, container, false); + + mCreateKey = view.findViewById(R.id.create_key_create_key_button); + mImportKey = view.findViewById(R.id.create_key_import_button); +// mYubiKey = view.findViewById(R.id.create_key_yubikey_button); + mCancel = (TextView) view.findViewById(R.id.create_key_cancel); + + if (mCreateKeyActivity.mFirstTime) { + mCancel.setText(R.string.first_time_skip); + } else { + mCancel.setText(R.string.btn_do_not_save); + } + + mCreateKey.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + CreateKeyNameFragment frag = CreateKeyNameFragment.newInstance(); + mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT); + } + }); + + mImportKey.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(mCreateKeyActivity, ImportKeysActivity.class); + intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN); + startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY); + } + }); + + mCancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finishSetup(null); + } + }); + + return view; + } + + + private void finishSetup(Intent srcData) { + if (mCreateKeyActivity.mFirstTime) { + Preferences prefs = Preferences.getPreferences(mCreateKeyActivity); + prefs.setFirstTime(false); + } + Intent intent = new Intent(mCreateKeyActivity, MainActivity.class); + // give intent through to display notify + if (srcData != null) { + intent.putExtras(srcData); + } + startActivity(intent); + mCreateKeyActivity.finish(); + } + + // workaround for https://code.google.com/p/android/issues/detail?id=61394 +// @Override +// public boolean onKeyDown(int keyCode, KeyEvent event) { +// return keyCode == KeyEvent.KEYCODE_MENU || super.onKeyDown(keyCode, event); +// } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == REQUEST_CODE_CREATE_OR_IMPORT_KEY) { + if (resultCode == Activity.RESULT_OK) { + finishSetup(data); + } + } else { + Log.e(Constants.TAG, "No valid request code!"); + } + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + mCreateKeyActivity = (CreateKeyActivity) getActivity(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java deleted file mode 100644 index 393e15cfa..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.view.KeyEvent; -import android.view.View; -import android.view.Window; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Preferences; - -public class FirstTimeActivity extends BaseActivity { - - View mCreateKey; - View mImportKey; - View mSkipSetup; - - public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012; - - @Override - protected void onCreate(Bundle savedInstanceState) { - supportRequestWindowFeature(Window.FEATURE_NO_TITLE); - - super.onCreate(savedInstanceState); - - mCreateKey = findViewById(R.id.first_time_create_key); - mImportKey = findViewById(R.id.first_time_import_key); - mSkipSetup = findViewById(R.id.first_time_cancel); - - mSkipSetup.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finishSetup(null); - } - }); - - mImportKey.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(FirstTimeActivity.this, ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN); - startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY); - } - }); - - mCreateKey.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(FirstTimeActivity.this, CreateKeyActivity.class); - startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY); - } - }); - } - - @Override - protected void initLayout() { - setContentView(R.layout.first_time_activity); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - if (requestCode == REQUEST_CODE_CREATE_OR_IMPORT_KEY) { - if (resultCode == RESULT_OK) { - finishSetup(data); - } - } else { - Log.e(Constants.TAG, "No valid request code!"); - } - } - - private void finishSetup(Intent srcData) { - Preferences prefs = Preferences.getPreferences(this); - prefs.setFirstTime(false); - Intent intent = new Intent(this, MainActivity.class); - // give intent through to display notify - if (srcData != null) { - intent.putExtras(srcData); - } - startActivity(intent); - finish(); - } - - // workaround for https://code.google.com/p/android/issues/detail?id=61394 - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return keyCode == KeyEvent.KEYCODE_MENU || super.onKeyDown(keyCode, event); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 4b604add8..f8939f3d1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -489,7 +489,8 @@ public class KeyListFragment extends LoaderFragment case R.id.menu_key_list_debug_first_time: Preferences prefs = Preferences.getPreferences(getActivity()); prefs.setFirstTime(true); - Intent intent = new Intent(getActivity(), FirstTimeActivity.class); + Intent intent = new Intent(getActivity(), CreateKeyActivity.class); + intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true); startActivity(intent); getActivity().finish(); return true; 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 b6b2fcb8a..5fa3edba4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -54,7 +54,9 @@ public class MainActivity extends MaterialNavigationDrawer implements FabContain // if this is the first time show first time activity Preferences prefs = Preferences.getPreferences(this); if (prefs.isFirstTime()) { - startActivity(new Intent(this, FirstTimeActivity.class)); + Intent intent = new Intent(this, CreateKeyActivity.class); + intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true); + startActivity(intent); finish(); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java index 1bdec7b84..e21c5d510 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EmailEditText.java @@ -58,9 +58,10 @@ public class EmailEditText extends AutoCompleteTextView { } private void init() { - this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); - this.addTextChangedListener(textWatcher); - removeFlag(); + setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); + reenableKeyboardSuggestions(); + + addTextChangedListener(textWatcher); initAdapter(); } @@ -104,7 +105,7 @@ public class EmailEditText extends AutoCompleteTextView { * Hack to re-enable keyboard auto correction in AutoCompleteTextView. * From http://stackoverflow.com/a/22512858 */ - private void removeFlag() { + private void reenableKeyboardSuggestions() { int inputType = getInputType(); inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; setRawInputType(inputType); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java index f086c5696..153bf2ff2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NameEditText.java @@ -50,7 +50,7 @@ public class NameEditText extends AutoCompleteTextView { } private void init() { - removeFlag(); + reenableKeyboardSuggestions(); initAdapter(); } @@ -62,10 +62,10 @@ public class NameEditText extends AutoCompleteTextView { } /** - * Hack to re-enable keyboard auto correction in AutoCompleteTextView. + * Hack to re-enable keyboard suggestions in AutoCompleteTextView. * From http://stackoverflow.com/a/22512858 */ - private void removeFlag() { + private void reenableKeyboardSuggestions() { int inputType = getInputType(); inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; setRawInputType(inputType); diff --git a/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml b/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml index ea064b00c..a2f81f74c 100644 --- a/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml +++ b/OpenKeychain/src/main/res/layout/create_key_name_fragment.xml @@ -56,11 +56,13 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" + android:text="@string/btn_back" android:textAllCaps="true" android:minHeight="?android:attr/listPreferredItemHeight" + android:drawableLeft="@drawable/ic_chevron_left_grey_24dp" android:drawablePadding="8dp" android:gravity="left|center_vertical" - android:clickable="false" + android:clickable="true" style="?android:attr/borderlessButtonStyle" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/first_time_activity.xml b/OpenKeychain/src/main/res/layout/first_time_activity.xml deleted file mode 100644 index 9b37766e9..000000000 --- a/OpenKeychain/src/main/res/layout/first_time_activity.xml +++ /dev/null @@ -1,137 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenKeychain/src/main/res/menu/key_list.xml b/OpenKeychain/src/main/res/menu/key_list.xml index 386a9d46e..15e098138 100644 --- a/OpenKeychain/src/main/res/menu/key_list.xml +++ b/OpenKeychain/src/main/res/menu/key_list.xml @@ -16,7 +16,7 @@ Nápověda Exportovat do souboru Smazat klíč - Vytvořit moje klíče + Vytvořit moje klíče Importovat ze souboru Hledat Beam settings diff --git a/OpenKeychain/src/main/res/values-de/strings.xml b/OpenKeychain/src/main/res/values-de/strings.xml index fbae159a3..20c8d933d 100644 --- a/OpenKeychain/src/main/res/values-de/strings.xml +++ b/OpenKeychain/src/main/res/values-de/strings.xml @@ -88,7 +88,7 @@ Hilfe In Datei exportieren Schlüssel löschen - Erzeuge meinen Schlüssel + Erzeuge meinen Schlüssel Von Datei importieren Suchen NFC-Einstellungen diff --git a/OpenKeychain/src/main/res/values-es/strings.xml b/OpenKeychain/src/main/res/values-es/strings.xml index d7557074a..6ba096420 100644 --- a/OpenKeychain/src/main/res/values-es/strings.xml +++ b/OpenKeychain/src/main/res/values-es/strings.xml @@ -89,7 +89,7 @@ Ayuda Exportar hacia archivo Borrar clave - Crear mi clave + Crear mi clave Importar desde fichero Buscar Configuraciones NFC diff --git a/OpenKeychain/src/main/res/values-eu/strings.xml b/OpenKeychain/src/main/res/values-eu/strings.xml index 32fdfc4cb..73ac12403 100644 --- a/OpenKeychain/src/main/res/values-eu/strings.xml +++ b/OpenKeychain/src/main/res/values-eu/strings.xml @@ -76,7 +76,7 @@ Laguntza Esportatu agirira Ezabatu giltza - Sortu nire giltza + Sortu nire giltza inportatu agiritik Bilatu NFC ezarpenak diff --git a/OpenKeychain/src/main/res/values-fi/strings.xml b/OpenKeychain/src/main/res/values-fi/strings.xml index 4b75d203d..508b17d0a 100644 --- a/OpenKeychain/src/main/res/values-fi/strings.xml +++ b/OpenKeychain/src/main/res/values-fi/strings.xml @@ -70,7 +70,7 @@ Apua Vie tiedostoon Poista avain - Luo minun avaimeni + Luo minun avaimeni Tuo tiedostosta Etsi Beam asetukset diff --git a/OpenKeychain/src/main/res/values-fr/strings.xml b/OpenKeychain/src/main/res/values-fr/strings.xml index ba6f2e2af..3b3add86a 100644 --- a/OpenKeychain/src/main/res/values-fr/strings.xml +++ b/OpenKeychain/src/main/res/values-fr/strings.xml @@ -89,7 +89,7 @@ Aide Exporter vers un fichier Supprimer la clef - Créer ma clef + Créer ma clef Importer depuis un fichier Rechercher Paramètres NFC diff --git a/OpenKeychain/src/main/res/values-it/strings.xml b/OpenKeychain/src/main/res/values-it/strings.xml index 5de5a1579..4b88aa494 100644 --- a/OpenKeychain/src/main/res/values-it/strings.xml +++ b/OpenKeychain/src/main/res/values-it/strings.xml @@ -69,7 +69,7 @@ Aiuto Esporta su un file Cancella chiave - Crea mia chiave + Crea mia chiave Importa da file Cerca Impostazioni Beam diff --git a/OpenKeychain/src/main/res/values-ja/strings.xml b/OpenKeychain/src/main/res/values-ja/strings.xml index 183938811..16b9c3cc4 100644 --- a/OpenKeychain/src/main/res/values-ja/strings.xml +++ b/OpenKeychain/src/main/res/values-ja/strings.xml @@ -89,7 +89,7 @@ ヘルプ ファイルへのエクスポート 鍵の削除 - 自分の鍵の生成 + 自分の鍵の生成 ファイルからインポート 検索 NFC設定 diff --git a/OpenKeychain/src/main/res/values-nl/strings.xml b/OpenKeychain/src/main/res/values-nl/strings.xml index 0d3cb0094..d77d4e331 100644 --- a/OpenKeychain/src/main/res/values-nl/strings.xml +++ b/OpenKeychain/src/main/res/values-nl/strings.xml @@ -96,7 +96,7 @@ Help Exporteren naar bestand Sleutel verwijderen - Mijn sleutel aanmaken + Mijn sleutel aanmaken Importeren van bestand Zoeken NFC-instellingen diff --git a/OpenKeychain/src/main/res/values-pl/strings.xml b/OpenKeychain/src/main/res/values-pl/strings.xml index 1ee5a86e7..e4fce6bcc 100644 --- a/OpenKeychain/src/main/res/values-pl/strings.xml +++ b/OpenKeychain/src/main/res/values-pl/strings.xml @@ -78,7 +78,7 @@ Pomoc Eksportuj do pliku Usuń klucz - Utwórz mój klucz + Utwórz mój klucz Importuj z pliku Szukaj Ustawienia Beam diff --git a/OpenKeychain/src/main/res/values-ru/strings.xml b/OpenKeychain/src/main/res/values-ru/strings.xml index 9559ecf51..7cd0cec9b 100644 --- a/OpenKeychain/src/main/res/values-ru/strings.xml +++ b/OpenKeychain/src/main/res/values-ru/strings.xml @@ -85,7 +85,7 @@ Помощь Экспорт в файл Удалить ключ - Создать свой ключ + Создать свой ключ Импорт из файла Поиск Настройки NFC diff --git a/OpenKeychain/src/main/res/values-sl/strings.xml b/OpenKeychain/src/main/res/values-sl/strings.xml index 3eb8eeba8..3f0def752 100644 --- a/OpenKeychain/src/main/res/values-sl/strings.xml +++ b/OpenKeychain/src/main/res/values-sl/strings.xml @@ -75,7 +75,7 @@ Pomoč Izvozi v datoteko Izbriši ključ - Ustvari zasebni ključ + Ustvari zasebni ključ Uvozi iz datoteke Išči Nastavitve Beam diff --git a/OpenKeychain/src/main/res/values-sr/strings.xml b/OpenKeychain/src/main/res/values-sr/strings.xml index b5ba0b361..c4c92dce5 100644 --- a/OpenKeychain/src/main/res/values-sr/strings.xml +++ b/OpenKeychain/src/main/res/values-sr/strings.xml @@ -89,7 +89,7 @@ Помоћ Извези у фајл Обриши кључ - Направи ми кључ + Направи ми кључ Увези из фајла Претрага НФЦ поставке diff --git a/OpenKeychain/src/main/res/values-sv/strings.xml b/OpenKeychain/src/main/res/values-sv/strings.xml index 93b833590..605e14695 100644 --- a/OpenKeychain/src/main/res/values-sv/strings.xml +++ b/OpenKeychain/src/main/res/values-sv/strings.xml @@ -78,7 +78,7 @@ Hjälp Exportera till fil Radera nyckel - Skapa min nyckel + Skapa min nyckel Importera från fil Sök Beam-inställningar diff --git a/OpenKeychain/src/main/res/values-tr/strings.xml b/OpenKeychain/src/main/res/values-tr/strings.xml index 2b60c9758..b939faf0d 100644 --- a/OpenKeychain/src/main/res/values-tr/strings.xml +++ b/OpenKeychain/src/main/res/values-tr/strings.xml @@ -69,7 +69,7 @@ Yardım Dosyaya ver Anahtar sil - Anahtarımı oluştur + Anahtarımı oluştur Dosyadan al Ara NFC ayarları diff --git a/OpenKeychain/src/main/res/values-uk/strings.xml b/OpenKeychain/src/main/res/values-uk/strings.xml index b3a161758..f192cb07a 100644 --- a/OpenKeychain/src/main/res/values-uk/strings.xml +++ b/OpenKeychain/src/main/res/values-uk/strings.xml @@ -69,7 +69,7 @@ Довідка Експорт до файлу Вилучити ключ - Створити мій ключ + Створити мій ключ Імпорт з файлу Пошук Налаштування променя diff --git a/OpenKeychain/src/main/res/values-zh-rTW/strings.xml b/OpenKeychain/src/main/res/values-zh-rTW/strings.xml index 286e98b79..bb8462bf3 100644 --- a/OpenKeychain/src/main/res/values-zh-rTW/strings.xml +++ b/OpenKeychain/src/main/res/values-zh-rTW/strings.xml @@ -65,7 +65,7 @@ 說明 匯出到檔案 刪除金鑰 - 建立金鑰 + 建立金鑰 從檔案匯入 搜尋 Beam 設定 diff --git a/OpenKeychain/src/main/res/values-zh/strings.xml b/OpenKeychain/src/main/res/values-zh/strings.xml index d59b2a2e0..31df7e587 100644 --- a/OpenKeychain/src/main/res/values-zh/strings.xml +++ b/OpenKeychain/src/main/res/values-zh/strings.xml @@ -57,7 +57,7 @@ 帮助 导出密钥 删除密钥 - 创建密钥 + 创建密钥 搜索 参数 取消 diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index f1c14bd32..f22a0c533 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -44,6 +44,7 @@ "Keys" "Delete YOUR key '%s'?" "Export Log" + "Manage my keys" "Identities" @@ -106,7 +107,7 @@ "Help" "Export to file" "Delete key" - "Create my key" + "Manage my keys" "Import from file" "Search" "NFC settings" @@ -1197,8 +1198,9 @@ "Take back your privacy with OpenKeychain!" - "Create my key" - "Import from file" + "Create my key (recommended)" + "Import key from file" + "Use YubiKey NEO" "Skip Setup" From cfef53be01f72da477d0cb66e40865d7dcd9b125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 19 Mar 2015 17:21:26 +0100 Subject: [PATCH 10/13] Remove on click on icon --- .../org/sufficientlysecure/keychain/ui/CreateKeyActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java index eb484302c..ab76f693e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyActivity.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; +import android.view.View; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.util.Passphrase; @@ -72,6 +73,7 @@ public class CreateKeyActivity extends BaseActivity { if (mFirstTime) { setTitle(R.string.app_name); setActionBarIcon(R.drawable.ic_launcher); + mToolbar.setNavigationOnClickListener(null); } else { setTitle(R.string.title_manage_my_keys); } From 8d6d4328f23b527be9ea794433b3f160cd58e5a5 Mon Sep 17 00:00:00 2001 From: Kent Date: Fri, 20 Mar 2015 06:31:18 +0800 Subject: [PATCH 11/13] Implemented #1162: Invoke NFC in Advanced View - Extracted NFC code from ViewKeyActivity to NfcHelper to share code between classes - Changed the private anonymous Handler for NFC into a static private subclass, that uses WeakReference to avoid memory leaks - Added resources needed (retrieved from Graphics) for the NFC button inside ViewKeyAdvShareFragment. - Fixed the ripple boundary of the Share With... button to prevent it from bleeding to other buttons on the right (UX improvement) --- .../keychain/ui/ViewKeyActivity.java | 169 ++------------- .../keychain/ui/ViewKeyAdvShareFragment.java | 21 +- .../keychain/util/NfcHelper.java | 204 ++++++++++++++++++ .../res/drawable-hdpi/ic_nfc_grey_24dp.png | Bin 0 -> 1059 bytes .../res/drawable-mdpi/ic_nfc_grey_24dp.png | Bin 0 -> 701 bytes .../res/drawable-xhdpi/ic_nfc_grey_24dp.png | Bin 0 -> 1359 bytes .../res/drawable-xxhdpi/ic_nfc_grey_24dp.png | Bin 0 -> 2040 bytes .../res/drawable-xxxhdpi/ic_nfc_grey_24dp.png | Bin 0 -> 2756 bytes .../layout/view_key_adv_share_fragment.xml | 25 ++- 9 files changed, 261 insertions(+), 158 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java create mode 100644 OpenKeychain/src/main/res/drawable-hdpi/ic_nfc_grey_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-mdpi/ic_nfc_grey_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/ic_nfc_grey_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/ic_nfc_grey_24dp.png create mode 100644 OpenKeychain/src/main/res/drawable-xxxhdpi/ic_nfc_grey_24dp.png 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 5834fa502..45ad944c4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -21,18 +21,12 @@ package org.sufficientlysecure.keychain.ui; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityOptions; import android.content.Intent; -import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; -import android.nfc.NdefMessage; -import android.nfc.NdefRecord; -import android.nfc.NfcAdapter; -import android.nfc.NfcEvent; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; @@ -40,7 +34,6 @@ import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.provider.ContactsContract; -import android.provider.Settings; import android.support.v4.app.ActivityCompat; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; @@ -58,9 +51,7 @@ import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; - import com.getbase.floatingactionbutton.FloatingActionButton; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; @@ -84,6 +75,7 @@ import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.ExportHelper; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.NfcHelper; import org.sufficientlysecure.keychain.util.Preferences; import java.util.ArrayList; @@ -93,8 +85,8 @@ public class ViewKeyActivity extends BaseActivity implements LoaderManager.LoaderCallbacks { static final int REQUEST_QR_FINGERPRINT = 1; - static final int REQUEST_DELETE= 2; - static final int REQUEST_EXPORT= 3; + static final int REQUEST_DELETE = 2; + static final int REQUEST_EXPORT = 3; ExportHelper mExportHelper; ProviderHelper mProviderHelper; @@ -115,11 +107,7 @@ public class ViewKeyActivity extends BaseActivity implements private CardView mQrCodeLayout; // NFC - private NfcAdapter mNfcAdapter; - private NfcAdapter.CreateNdefMessageCallback mNdefCallback; - private NfcAdapter.OnNdefPushCompleteCallback mNdefCompleteCallback; - private byte[] mNfcKeyringBytes; - private static final int NFC_SENT = 1; + private NfcHelper mNfcHelper; private static final int LOADER_ID_UNIFIED = 0; @@ -256,7 +244,7 @@ public class ViewKeyActivity extends BaseActivity implements mActionNfc.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - invokeNfcBeam(); + mNfcHelper.invokeNfcBeam(); } }); @@ -264,7 +252,8 @@ public class ViewKeyActivity extends BaseActivity implements // or start new ones. getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); - initNfc(mDataUri); + mNfcHelper = new NfcHelper(this, mProviderHelper); + mNfcHelper.initNfc(mDataUri); startFragment(savedInstanceState, mDataUri); } @@ -375,41 +364,6 @@ public class ViewKeyActivity extends BaseActivity implements return true; } - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void invokeNfcBeam() { - // Check if device supports NFC - if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { - Notify.createNotify(this, R.string.no_nfc_support, Notify.LENGTH_LONG, Notify.Style.ERROR).show(); - return; - } - // Check for available NFC Adapter - mNfcAdapter = NfcAdapter.getDefaultAdapter(this); - if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) { - Notify.createNotify(this, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { - @Override - public void onAction() { - Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS); - startActivity(intentSettings); - } - }, R.string.menu_nfc_preferences).show(); - - return; - } - - if (!mNfcAdapter.isNdefPushEnabled()) { - Notify.createNotify(this, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { - @Override - public void onAction() { - Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS); - startActivity(intentSettings); - } - }, R.string.menu_beam_preferences).show(); - - return; - } - - mNfcAdapter.invokeBeam(this); - } private void scanQrCode() { Intent scanQrCode = new Intent(this, ImportKeysProxyActivity.class); @@ -426,7 +380,7 @@ public class ViewKeyActivity extends BaseActivity implements private void certifyImmediate() { Intent intent = new Intent(this, CertifyKeyActivity.class); - intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId}); + intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] {mMasterKeyId}); startCertifyIntent(intent); } @@ -487,11 +441,11 @@ public class ViewKeyActivity extends BaseActivity implements HashMap data = providerHelper.getGenericData( baseUri, - new String[]{KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET}, - new int[]{ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER}); + new String[] {KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET}, + new int[] {ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER}); exportHelper.showExportKeysDialog( - new long[]{(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)}, + new long[] {(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)}, Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0) ); } catch (ProviderHelper.NotFoundException e) { @@ -515,7 +469,7 @@ public class ViewKeyActivity extends BaseActivity implements // Create a new Messenger for the communication back Messenger messenger = new Messenger(returnHandler); DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - new long[]{ mMasterKeyId }); + new long[] {mMasterKeyId}); deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog"); } @@ -546,11 +500,11 @@ public class ViewKeyActivity extends BaseActivity implements return; } - if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK){ + if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK) { deleteKey(); } - if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK){ + if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK) { exportToFile(mDataUri, mExportHelper, mProviderHelper); } @@ -572,7 +526,7 @@ public class ViewKeyActivity extends BaseActivity implements long keyId = new ProviderHelper(this) .getCachedPublicKeyRing(dataUri) .extractOrGetMasterKeyId(); - long[] encryptionKeyIds = new long[]{keyId}; + long[] encryptionKeyIds = new long[] {keyId}; Intent intent; if (text) { intent = new Intent(this, EncryptTextActivity.class); @@ -710,98 +664,9 @@ public class ViewKeyActivity extends BaseActivity implements loadTask.execute(); } - /** - * NFC: Initialize NFC sharing if OS and device supports it - */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - private void initNfc(final Uri dataUri) { - // check if NFC Beam is supported (>= Android 4.1) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - - // Implementation for the CreateNdefMessageCallback interface - mNdefCallback = new NfcAdapter.CreateNdefMessageCallback() { - @Override - public NdefMessage createNdefMessage(NfcEvent event) { - /* - * When a device receives a push with an AAR in it, the application specified in the AAR is - * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to - * guarantee that this activity starts when receiving a beamed message. For now, this code - * uses the tag dispatch system. - */ - return new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME, - mNfcKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME)); - } - }; - - // Implementation for the OnNdefPushCompleteCallback interface - mNdefCompleteCallback = new NfcAdapter.OnNdefPushCompleteCallback() { - @Override - public void onNdefPushComplete(NfcEvent event) { - // A handler is needed to send messages to the activity when this - // callback occurs, because it happens from a binder thread - mNfcHandler.obtainMessage(NFC_SENT).sendToTarget(); - } - }; - - // Check for available NFC Adapter - mNfcAdapter = NfcAdapter.getDefaultAdapter(this); - if (mNfcAdapter != null) { - /* - * Retrieve mNfcKeyringBytes here asynchronously (to not block the UI) - * and init nfc adapter afterwards. - * mNfcKeyringBytes can not be retrieved in createNdefMessage, because this process - * has no permissions to query the Uri. - */ - AsyncTask initTask = - new AsyncTask() { - protected Void doInBackground(Void... unused) { - try { - Uri blobUri = - KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri); - mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData( - blobUri, - KeychainContract.KeyRingData.KEY_RING_DATA, - ProviderHelper.FIELD_TYPE_BLOB); - } catch (ProviderHelper.NotFoundException e) { - Log.e(Constants.TAG, "key not found!", e); - } - - // no AsyncTask return (Void) - return null; - } - - protected void onPostExecute(Void unused) { - // Register callback to set NDEF message - mNfcAdapter.setNdefPushMessageCallback(mNdefCallback, - ViewKeyActivity.this); - // Register callback to listen for message-sent success - mNfcAdapter.setOnNdefPushCompleteCallback(mNdefCompleteCallback, - ViewKeyActivity.this); - } - }; - - initTask.execute(); - } - } - } - - /** - * NFC: This handler receives a message from onNdefPushComplete - */ - private final Handler mNfcHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case NFC_SENT: - Notify.showNotify( - ViewKeyActivity.this, R.string.nfc_successful, Notify.Style.INFO); - break; - } - } - }; // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[]{ + static final String[] PROJECTION = new String[] { KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID, KeychainContract.KeyRings.USER_ID, @@ -1018,4 +883,4 @@ public class ViewKeyActivity extends BaseActivity implements public void onLoaderReset(Loader loader) { } -} +} \ No newline at end of file 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 95a6faea9..29586ae9f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvShareFragment.java @@ -52,6 +52,7 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.NfcHelper; import java.io.IOException; @@ -68,10 +69,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements private View mFingerprintClipboardButton; private View mKeyShareButton; private View mKeyClipboardButton; + private View mKeyNfcButton; private ImageButton mKeySafeSlingerButton; private View mKeyUploadButton; ProviderHelper mProviderHelper; + NfcHelper mNfcHelper; private static final int LOADER_ID_UNIFIED = 0; @@ -83,6 +86,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements View view = inflater.inflate(R.layout.view_key_adv_share_fragment, getContainer()); mProviderHelper = new ProviderHelper(ViewKeyAdvShareFragment.this.getActivity()); + mNfcHelper = new NfcHelper(getActivity(), mProviderHelper); mFingerprint = (TextView) view.findViewById(R.id.view_key_fingerprint); mQrCode = (ImageView) view.findViewById(R.id.view_key_qr_code); @@ -90,6 +94,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements mFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share); mFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard); mKeyShareButton = view.findViewById(R.id.view_key_action_key_share); + mKeyNfcButton = view.findViewById(R.id.view_key_action_key_nfc); mKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard); mKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger); mKeyUploadButton = view.findViewById(R.id.view_key_action_upload); @@ -128,6 +133,14 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements share(mDataUri, mProviderHelper, false, true); } }); + + mKeyNfcButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mNfcHelper.invokeNfcBeam(); + } + }); + mKeySafeSlingerButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -255,9 +268,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements // Prepare the loaders. Either re-connect with an existing ones, // or start new ones. getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); + + // Prepare the NfcHelper + mNfcHelper.initNfc(mDataUri); } - static final String[] UNIFIED_PROJECTION = new String[]{ + static final String[] UNIFIED_PROJECTION = new String[] { KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET, KeyRings.USER_ID, KeyRings.FINGERPRINT, KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.IS_EXPIRED, @@ -362,4 +378,5 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements startActivityForResult(uploadIntent, 0); } -} + +} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java new file mode 100644 index 000000000..bae42d965 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java @@ -0,0 +1,204 @@ +package org.sufficientlysecure.keychain.util; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.nfc.NdefMessage; +import android.nfc.NdefRecord; +import android.nfc.NfcAdapter; +import android.nfc.NfcEvent; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Handler; +import android.os.Message; +import android.provider.Settings; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.util.Notify; + +import java.lang.ref.WeakReference; + +/** + * This class contains NFC functionality that can be shared across Fragments or Activities. + *

+ * Created on Mar 20, 2015. + * + * @author Kent + */ + +public class NfcHelper { + + private Activity mActivity; + private ProviderHelper mProviderHelper; + + /** + * NFC: This handler receives a message from onNdefPushComplete + */ + private static NfcHandler mNfcHandler; + + private NfcAdapter mNfcAdapter; + private NfcAdapter.CreateNdefMessageCallback mNdefCallback; + private NfcAdapter.OnNdefPushCompleteCallback mNdefCompleteCallback; + private byte[] mNfcKeyringBytes; + private static final int NFC_SENT = 1; + + /** + * Initializes the NfcHelper. + */ + public NfcHelper(final Activity activity, final ProviderHelper providerHelper) { + mActivity = activity; + mProviderHelper = providerHelper; + + mNfcHandler = new NfcHandler(mActivity); + } + + /** + * Return true if the NFC Adapter of this Helper has any features enabled. + * + * @return true if this NFC Adapter has any features enabled + */ + public boolean isEnabled() { + return mNfcAdapter.isEnabled(); + } + + /** + * NFC: Initialize NFC sharing if OS and device supports it + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + public void initNfc(final Uri dataUri) { + // check if NFC Beam is supported (>= Android 4.1) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + + // Implementation for the CreateNdefMessageCallback interface + mNdefCallback = new NfcAdapter.CreateNdefMessageCallback() { + @Override + public NdefMessage createNdefMessage(NfcEvent event) { + /* + * When a device receives a push with an AAR in it, the application specified in the AAR is + * guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to + * guarantee that this activity starts when receiving a beamed message. For now, this code + * uses the tag dispatch system. + */ + return new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME, + mNfcKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME)); + } + }; + + // Implementation for the OnNdefPushCompleteCallback interface + mNdefCompleteCallback = new NfcAdapter.OnNdefPushCompleteCallback() { + @Override + public void onNdefPushComplete(NfcEvent event) { + // A handler is needed to send messages to the activity when this + // callback occurs, because it happens from a binder thread + mNfcHandler.obtainMessage(NFC_SENT).sendToTarget(); + } + }; + + // Check for available NFC Adapter + mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity); + if (mNfcAdapter != null) { + /* + * Retrieve mNfcKeyringBytes here asynchronously (to not block the UI) + * and init nfc adapter afterwards. + * mNfcKeyringBytes can not be retrieved in createNdefMessage, because this process + * has no permissions to query the Uri. + */ + AsyncTask initTask = + new AsyncTask() { + protected Void doInBackground(Void... unused) { + try { + Uri blobUri = + KeychainContract.KeyRingData.buildPublicKeyRingUri(dataUri); + mNfcKeyringBytes = (byte[]) mProviderHelper.getGenericData( + blobUri, + KeychainContract.KeyRingData.KEY_RING_DATA, + ProviderHelper.FIELD_TYPE_BLOB); + } catch (ProviderHelper.NotFoundException e) { + Log.e(Constants.TAG, "key not found!", e); + } + + // no AsyncTask return (Void) + return null; + } + + protected void onPostExecute(Void unused) { + // Register callback to set NDEF message + mNfcAdapter.setNdefPushMessageCallback(mNdefCallback, + mActivity); + // Register callback to listen for message-sent success + mNfcAdapter.setOnNdefPushCompleteCallback(mNdefCompleteCallback, + mActivity); + } + }; + + initTask.execute(); + } + } + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public void invokeNfcBeam() { + // Check if device supports NFC + if (!mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) { + Notify.createNotify(mActivity, R.string.no_nfc_support, Notify.LENGTH_LONG, Notify.Style.ERROR).show(); + return; + } + // Check for available NFC Adapter + mNfcAdapter = NfcAdapter.getDefaultAdapter(mActivity); + if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) { + Notify.createNotify(mActivity, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { + @Override + public void onAction() { + Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS); + mActivity.startActivity(intentSettings); + } + }, R.string.menu_nfc_preferences).show(); + + return; + } + + if (!mNfcAdapter.isNdefPushEnabled()) { + Notify.createNotify(mActivity, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() { + @Override + public void onAction() { + Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS); + mActivity.startActivity(intentSettings); + } + }, R.string.menu_beam_preferences).show(); + + return; + } + + mNfcAdapter.invokeBeam(mActivity); + } + + /** + * A static subclass of {@link Handler} with a {@link WeakReference} to an {@link Activity} to avoid memory leaks. + */ + private static class NfcHandler extends Handler { + private final WeakReference mActivityReference; + + public NfcHandler(Activity activity) { + mActivityReference = new WeakReference<>(activity); + } + + @Override + public void handleMessage(Message msg) { + Activity activity = mActivityReference.get(); + + if (activity != null) { + switch (msg.what) { + case NFC_SENT: + Notify.showNotify( + activity, R.string.nfc_successful, Notify.Style.INFO); + break; + } + } + } + } + +} \ No newline at end of file diff --git a/OpenKeychain/src/main/res/drawable-hdpi/ic_nfc_grey_24dp.png b/OpenKeychain/src/main/res/drawable-hdpi/ic_nfc_grey_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..fedf390138eb286be0e2ab911b43613d2c6d36fa GIT binary patch literal 1059 zcmV+;1l;?HP)k>o?mgcgkksv6a8S@g0$8pt@SzAb)UVKAR@YC%sm4G0|yem zTrU4kMBf;9`{3ZN5t z*l`>`%rM;O{Q7>7RwUEo@ZcE38VfZE1Z_aQk<}9-t-{6h!6f*TP3PU1 znp7Hb95>}R6$D_F?Pizn`yWdwhluDa7BgC@RK7O-=S~*?3yW!dAN<)6LTqDpy`2jE zm|k>hYHB%~%{~F(bpVkWdw_^e0eCbC&SWxYShCyMkzHWx-^_$wO6T7oD4WfmNUB6c zjXvRdURW#^UlBs=1(19nHr5;H*~!VtUsFMeB;yB*j-`}5aHeM_iFD@-D z?FI0vIl`X-1j)GTy4!>h!^Zv1w(ai$pin66fays>h(+_@{sS3of|;3_bGQcP=jS5; zueq)}U$58Kf*?50hF<{CV-g?pJTEr<<4FK}TG|uTE_~nrArQ^*8S7&ZV@I5!4LTYPD=xRt>;%sZ=_B-3xZn dMHl}Oe*-GcURS>JwHg2b002ovPDHLkV1m%8>`edw literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-mdpi/ic_nfc_grey_24dp.png b/OpenKeychain/src/main/res/drawable-mdpi/ic_nfc_grey_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..f8f6c3812e112d39901955316854e474f914d84f GIT binary patch literal 701 zcmV;u0z&=R3a!GScssFRc!7f*(R)& zcYA@HEY^xmDn&%C6fHC;BoKrYwn47YA_=bdk?hUbOu`a#ftZL*{>_{B=Dq*_y*Dth zhyM++8zhB7A*r>#FQxQKrP6%#PVS^R*LC9nN~rw(V0v5F~^U zRiGmliybW%i;LTyAeYMxWV6}Rkx&Sntkr6lCnhF-X|10EiB_w1we5-=$LY>yvnMv) zhY&?6<;#(gkxt`31khUF0FY9?HSxi=0$~_FkWzlh=kxb6nM`bhz#PzGo=&G_4M8e0Rn;61`o7-;mVutF1oYaNLWo%)t(5wh%jK@OTCIl3 zSzb@pT5ndrmB51OHmKL@=KvqDgb-7}3zPFLl79@qvaBW7b@u^>%-Y%tyfU5HzVA27 zx2#5G_MIBRm|0LI3~x_~1<)pgyV(P$)1ZMCgH7>46Qh})KBeO-%rp7&fz*$;dI zYC?#|CikLwhY!Xd1dvkBL^cJtZlisi`UIx^6m)axu&07F7@51yN zi81p&$8p}VEGr%+prWGU2!L_Lw9#>#+trxS(a`|_6Iy}lvF2M%P~Z#*n?f6kh}ua+ z{b5te^Sr-NxD^sJKdHt{opFW~Q^K;W>O7g4m^cq*(K7R`3lXp;z<#gq`vbOZKOXcT z5gnUtFaVs_cul_1+uM7Eh=vv4Z3_{wGQ@W?fRAn4ei=nhw7&13(@ez%W2B%E!zdVK zW>%+RH3ASOK%eWsYMcPKNLTFhTqB7_MTRoXWJKsudv48wQ~z(p;#7Qh+TbvFZmVHnjR zxc~snT&0<=Xk2}3fft)F0cw$v%F4=h0FX>3j{;Z^;8QKWN=kXyw(TTYED{Qz}TsocZk3=H(6VWFDsV;PNbq#8{>rl9}^s9bmuGRQZK<;1xp687- z^ELoy033_QY~QG@BxHLKb RYRCWp002ovPDHLkV1is>a?bz& literal 0 HcmV?d00001 diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/ic_nfc_grey_24dp.png b/OpenKeychain/src/main/res/drawable-xxhdpi/ic_nfc_grey_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..7e8fa6ba2a71863a84244358daf567a7cb3ae955 GIT binary patch literal 2040 zcmVi5&brRJP6;%P3lz5nLJ&>VpQJ!Q1gKC|gisNtpsIn0Dix(h zyV||84i9J$BDW?BxoaS+rIqm=Agwc z;!>bkEZz_V!A}9)0^lSO{m6CQ-3!TOEOs%M0L09{#6@ucU?nr}%;)nTT}ZMeCy;I1 zea!sf2AWLM%q}F~k`t&_tDOhHjXX1dGn>u!#m(1o0%fz=zCxjJt->MP}P`FNu*Gw&p#F9~Tj1NZ@i9;QRb z^q{G!DSJ*F03f1uzVEw|Z%29n@e^^L+c5$$^GyPMr)}G78cD}-0%m?h zljp8D&+Qn20GyH9&&)#_As*7?8E_mYa@~t6(7?ceSu7UU6$*v5XuW9N4j>TtyRzBr z6^-P`$jDIuhZ=ci?wg*TzTq7&kmES1{{H?0K@c1wqQgq5UxkU}dEPMqzZOhg>2&&Q zQVcUcCGkNJYW_Kna4DyyCsr{p3?Y#_L2$YIL>8l(rBe1 z!1it1w!K>yP^nZ70QiRxw^15q6B83J0+Sy_U{ylF; z9m6nA3H0Uldc6=5#sKyUyqQj?Ki5d^*|Vnx;3dI-MX^}CrmX_`zP}y7Z2(LF-!cs2 zNTE>p;z9*7P4i^{^(FJi>ue`zt@;DG zXEc0W7ZW0oT%=+ACyxI@1d>kRCOpr(jhR0WVfjA~7J%JKsRsuK2b+H3R4OG3Oho_I zB@BWf@`>PF$fXuYH|9~PRGzEVYU_yT(H1%G?ds}kS}*y&uNz7ex5H0vK0H4LOdJG7RHm8o$4Xoj@7-n{Vy zDwoU0nfV35RBshKH{<~9jUmu)b%vp#q3a>6%ZTVOgdZ^of)8p^y@m*gd|*aI-xnHi zEK027I8&bIZ9(fH2LNE^{W@E{Ue}#0ysY7+?`dG>KetsN&-0!qqWkeyuQ`=UrCP0h z8guM53}XZIlRf~P?&;}yMrR}<@kk{If^kiVD5zikZdLmjb`A2Q* zqJCNRJa4aQnrn&Zhs?Y+lgSJ?jx(e4H%;?Cjp=a>f4hc%HfkFe{rb!7^%bX;UpAY) zf|-uVZ>pR9KdE8H696h<+Yd9AF6wW?BuMc8$A1C! WWQJPmDWI?b00006pQBLI$HFLHwR`Da}^)fV)a;mHmA#Ki-*`gPdgk)BksKwqI2_2@8 zLq&TV>qVKfa*CwQVHSSh-v55T=eq9ab3M;E7{P3E zT-*|^E|UgH^)FT~K$WFSp@^1{zV1Vy9cDfLh{C8yE$1O|ly4r!O_22iuP&_X;KP@*mmrU=M9+d|KlxZe~ zLU~+Mv#>v=wr5#`>=7Yw?*ZP^93@TP^mr|t!8aWfhFqd2w;eoOAv+c9wM> zvZl6naRw@T5dB7!y0LNO_Q6*u)K#44@={C4we ztVn??I4N7#0+p@znUym(LzemBhr)=Ghq$(6+QL>s%fZbHlHNawo9_!3K7IOh4d_bm zJW8L^Cfk%OL7)|JwT!l?7}^*9$VhVm4UcT!jI>+2y#&Ed61UF3`|#m|xw`Y95hd^& za+W#r$O|AJMeD&9&HS6w*4uy${nfpb;*Ga;Coc3ELUSr4fQc9zCGDw{;c;ssfq;-i z7~Oo$sXWUXF|$yJj*5y43=I4bGRbgfLVy)8@QpWCPp65#nzO4fDJY#%4L?T2JfeK0 zi7vp_nns)D9ZML|jqTdqJl;=TpkKKsh4afatm*yAdON-%Z=wi5#n>Rrc1k(Tby(Zm zHz~KC&!#!__w`MNM({CZWT@;ifS8{RnS6Zj-o#x!3%NgArD5z~glP2LyLXfr9Er4P ztz@USy6}oH)~Z`qkFWG<0Rway(2R1smN#*dx<9XPO%3O2l)%1^6yW98p6r`{2$vWd z8e-9>N=HES@zSDnc6PeuQeXrySgT*3a#(2D_Ih9Hk@GuXy>Hjrw+DSyS-REcQfA-2?S zNf;thrvCsgo!D%U0h&*m)qJ0xwuX6dtfi%;O#u2X&pgaUH6YnvnKFIF+Eg|j9acPB zYxI8h+yg1a9(`c4H-CGg)|3TTVAjUDkTiUzo3GL*wm@4c=t`4ZjT77kG8q`h1a?9- zmIsVx(Wi!bhllOWqkr8pjl08_H!zG(Of*Au=u{X=oO}I+kg-`;_<>XA#((+`R`QUOu*1U0v-NGVxR>6uxvuJ!B+W=fyoR#*}-jhfVrP-Eg_TB9UPZr0$F6 z`}|In5TBC0>W6#wp6ox6m5e7PtYITgASbx$$*~2C{CSqA%@u2g7tD-7HP>^S_V^d9l1)T*>-*XxuyEs_uCz$ z^|d9gMP2>1Pu^zK9UhAAo&fUgu!NK1IpEtwJi~YP=~;A37IO9cSq&vt;;9v6gtm-~ zj4tCJ&Cbm1LeI-q>hZ@6n=}j&f|vF4Ya~MeST?N4vg0;FS-MCEe0PwiMfSOOSj+yq z_+9lYjOb&U_UKzuoIpsx~mhFncP+H18s1-~6{ratS}lWYfbhxn3BIeE|XD zVp9&VmP(8r&8WM(x+hn#lYcL*dg*&e-iLo_z||(lm{DzuXm~HBxXTmP*DunOibF$Z zv9Q8w3;-abZd@zRt=hB63`Ms*_Wq9~c_;kDOYO`vyOm0JVD-)n8UR*})j_%ZQ$FrA zx>_4Tuwf(SSaTRU9+JZpl@bvtvl4XiYQj2eOR{AI0rqp(K7T@RQc@C2W>%|{O1lQ> z4&++V)Z>}k-{=rb0+fGL-~DRG<>UFJ-&ye_MKC!5{89%Djmg=Vyoz%T3u4!3F=<90 z0u=cyv;xS55Ti@)6rBeI>aOl#!tGU-^SPd2ktQ>JBMmER0K@?Td>c)4bY$h3-QQ5N z420k+Wc+(bcSmD3jX}#>lq_*CmMS@&iKB*I?HDi3o6wRN^@6+~xIq&WN1FQ+hQ0!r zf?W8`oW(jFn6h7NLtoAAUsKx#ym^>G_u`J{mGWya^uo-HNc`i+4iVbZ9px^gVr+gy-qJr3g2mod&MXhM`*Q27+ko}{{lcowDj#M^qJUnE z3bXg;QKoAm;B;dNJ3Bjbi@4z2mj*{3MMK(tH08SRGI}TAFjRc56R8#akGI+NeN2bp z4=*(b1_!keg8GMfG<=t)%IZSC6c)<#O2{m_azn3x7Q$87>U0;l1AX4SGqe1;zyDPZ z1Z2CoxH!QgC)MKVz5|aW2$AZUq)yE^cy9pj8BOGY*nzG&c;LVgF6VP^?*TQ(xng~# zSL&H(<{pTrr>j<#q>(ioULU(@2Y8Wk%gx?q4MX`hF`Lca<$5$;MM>N5;Sey80WCpv z@B+)On1&ZewsLl8{&Ijv@6=OoihOdsZwz3I|IphX zd68%48`hjP8l`x>y$w>W1#$Au4tD5*@h3JwU^rCsA=P2EFt~K=*Yz z_*RDilbh{rO$MNJ00>OqHBBW`8G<%Bs6;fLYESx-vlEV^{wE#k%}J%GmSm@$#(z!B z0u|lewU5%2v|#`MY1bII@0B;q3k(j%ze>!qg~GEmg=Jv@>vwXFI3Bu@e()&Mx0+6I z2DK3s7STD;M_Uu&Zj;q>I>skZ@zDx1+6>>A2VI>)6?o&ehbLI + android:gravity="center_vertical" + android:background="?android:selectableItemBackground"/> + + + + + Date: Fri, 20 Mar 2015 12:24:06 +0800 Subject: [PATCH 12/13] Removed create and added GPLv3 header --- .../keychain/util/NfcHelper.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java index bae42d965..3d225eb04 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2014 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.util; import android.annotation.TargetApi; @@ -24,10 +41,6 @@ import java.lang.ref.WeakReference; /** * This class contains NFC functionality that can be shared across Fragments or Activities. - *

- * Created on Mar 20, 2015. - * - * @author Kent */ public class NfcHelper { From 50913d0ff1f29d4d32c36b1924b20fc8ef3a76bc Mon Sep 17 00:00:00 2001 From: Kent Date: Fri, 20 Mar 2015 12:38:16 +0800 Subject: [PATCH 13/13] Updated GNU header --- .../java/org/sufficientlysecure/keychain/util/NfcHelper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java index 3d225eb04..a65576613 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/NfcHelper.java @@ -1,5 +1,6 @@ /* - * Copyright (C) 2014 Vincent Breitmoser + * Copyright (C) 2013-2014 Dominik Schrmann + * Copyright (C) 2015 Kent Nguyen * * 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