Merge remote-tracking branch 'origin/development' into development

Conflicts:
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/EditKeyOperation.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/PgpSignEncryptResult.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptInputParcel.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/SignEncryptParcel.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/SetPassphraseDialogFragment.java
This commit is contained in:
Vincent Breitmoser 2015-03-20 11:32:15 +01:00
commit abce05d529
86 changed files with 1220 additions and 822 deletions

View File

@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils; import org.sufficientlysecure.keychain.util.TestingUtils;
@ -65,8 +66,8 @@ import java.util.Random;
public class CertifyOperationTest { public class CertifyOperationTest {
static UncachedKeyRing mStaticRing1, mStaticRing2; static UncachedKeyRing mStaticRing1, mStaticRing2;
static String mKeyPhrase1 = TestingUtils.genPassphrase(true); static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
static String mKeyPhrase2 = TestingUtils.genPassphrase(true); static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true);
static PrintStream oldShadowStream; static PrintStream oldShadowStream;
@ -255,13 +256,13 @@ public class CertifyOperationTest {
} }
private CertifyOperation operationWithFakePassphraseCache( 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, return new CertifyOperation(Robolectric.application,
new ProviderHelper(Robolectric.application), new ProviderHelper(Robolectric.application),
null, null) { null, null) {
@Override @Override
public String getCachedPassphrase(long masterKeyId, long subKeyId) public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId)
throws NoSecretKeyException { throws NoSecretKeyException {
if (checkMasterKeyId != null) { if (checkMasterKeyId != null) {
Assert.assertEquals("requested passphrase should be for expected master key id", Assert.assertEquals("requested passphrase should be for expected master key id",

View File

@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils; 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 @org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
public class ExportTest { public class ExportTest {
static String mPassphrase = TestingUtils.genPassphrase(true); static Passphrase mPassphrase = TestingUtils.genPassphrase(true);
static UncachedKeyRing mStaticRing1, mStaticRing2; static UncachedKeyRing mStaticRing1, mStaticRing2;
static String mKeyPhrase1 = TestingUtils.genPassphrase(true); static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
static String mKeyPhrase2 = TestingUtils.genPassphrase(true); static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true);
static PrintStream oldShadowStream; static PrintStream oldShadowStream;
@ -94,7 +95,7 @@ public class ExportTest {
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L)); Algorithm.ELGAMAL, 1024, null, KeyFlags.ENCRYPT_COMMS, 0L));
parcel.mAddUserIds.add("snails"); parcel.mAddUserIds.add("snails");
parcel.mNewUnlock = new ChangeUnlockParcel(null, "1234"); parcel.mNewUnlock = new ChangeUnlockParcel(null, new Passphrase("1234"));
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(parcel);
Assert.assertTrue("initial test key creation must succeed", result.success()); Assert.assertTrue("initial test key creation must succeed", result.success());

View File

@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils; import org.sufficientlysecure.keychain.util.TestingUtils;
@ -50,7 +51,7 @@ import java.util.Iterator;
public class PromoteKeyOperationTest { public class PromoteKeyOperationTest {
static UncachedKeyRing mStaticRing; static UncachedKeyRing mStaticRing;
static String mKeyPhrase1 = TestingUtils.genPassphrase(true); static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
static PrintStream oldShadowStream; static PrintStream oldShadowStream;

View File

@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils; import org.sufficientlysecure.keychain.util.TestingUtils;
@ -47,17 +48,18 @@ import java.io.ByteArrayOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import java.security.Security; import java.security.Security;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 @org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
public class PgpEncryptDecryptTest { public class PgpEncryptDecryptTest {
static String mPassphrase = TestingUtils.genPassphrase(true); static Passphrase mPassphrase = TestingUtils.genPassphrase(true);
static UncachedKeyRing mStaticRing1, mStaticRing2; static UncachedKeyRing mStaticRing1, mStaticRing2;
static String mKeyPhrase1 = TestingUtils.genPassphrase(true); static Passphrase mKeyPhrase1 = TestingUtils.genPassphrase(true);
static String mKeyPhrase2 = TestingUtils.genPassphrase(true); static Passphrase mKeyPhrase2 = TestingUtils.genPassphrase(true);
static PrintStream oldShadowStream; static PrintStream oldShadowStream;
@ -180,7 +182,7 @@ public class PgpEncryptDecryptTest {
new ProviderHelper(Robolectric.application), new ProviderHelper(Robolectric.application),
null, // new DummyPassphraseCache(mPassphrase, 0L), null, // new DummyPassphraseCache(mPassphrase, 0L),
data, out); data, out);
b.setPassphrase(mPassphrase + "x"); b.setPassphrase(new Passphrase(Arrays.toString(mPassphrase.getCharArray()) + "x"));
DecryptVerifyResult result = b.build().execute(); DecryptVerifyResult result = b.build().execute();
Assert.assertFalse("decryption must succeed", result.success()); Assert.assertFalse("decryption must succeed", result.success());
Assert.assertEquals("decrypted plaintext should be empty", 0, out.size()); Assert.assertEquals("decrypted plaintext should be empty", 0, out.size());
@ -511,7 +513,7 @@ public class PgpEncryptDecryptTest {
private PgpDecryptVerify.Builder builderWithFakePassphraseCache ( private PgpDecryptVerify.Builder builderWithFakePassphraseCache (
InputData data, OutputStream out, 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, return new PgpDecryptVerify.Builder(Robolectric.application,
new ProviderHelper(Robolectric.application), new ProviderHelper(Robolectric.application),
@ -520,7 +522,7 @@ public class PgpEncryptDecryptTest {
public PgpDecryptVerify build() { public PgpDecryptVerify build() {
return new PgpDecryptVerify(this) { return new PgpDecryptVerify(this) {
@Override @Override
public String getCachedPassphrase(long masterKeyId, long subKeyId) public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId)
throws NoSecretKeyException { throws NoSecretKeyException {
if (checkMasterKeyId != null) { if (checkMasterKeyId != null) {
Assert.assertEquals("requested passphrase should be for expected master key id", Assert.assertEquals("requested passphrase should be for expected master key id",

View File

@ -53,6 +53,7 @@ import org.sufficientlysecure.keychain.support.KeyringBuilder;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
import org.sufficientlysecure.keychain.support.TestDataUtil; import org.sufficientlysecure.keychain.support.TestDataUtil;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.TestingUtils; import org.sufficientlysecure.keychain.util.TestingUtils;
@ -72,7 +73,7 @@ import java.util.Random;
public class PgpKeyOperationTest { public class PgpKeyOperationTest {
static UncachedKeyRing staticRing; static UncachedKeyRing staticRing;
final static String passphrase = TestingUtils.genPassphrase(); final static Passphrase passphrase = TestingUtils.genPassphrase();
UncachedKeyRing ring; UncachedKeyRing ring;
PgpKeyOperation op; PgpKeyOperation op;
@ -295,9 +296,9 @@ public class PgpKeyOperationTest {
} }
{ {
String badphrase = ""; Passphrase badphrase = new Passphrase();
if (badphrase.equals(passphrase)) { if (badphrase.equals(passphrase)) {
badphrase = "a"; badphrase = new Passphrase("a");
} }
assertModifyFailure("keyring modification with bad passphrase should fail", assertModifyFailure("keyring modification with bad passphrase should fail",
@ -1036,7 +1037,7 @@ public class PgpKeyOperationTest {
public void testPassphraseChange() throws Exception { public void testPassphraseChange() throws Exception {
// change passphrase to empty // change passphrase to empty
parcel.mNewUnlock = new ChangeUnlockParcel(""); parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
// note that canonicalization here necessarily strips the empty notation packet // note that canonicalization here necessarily strips the empty notation packet
UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB, UncachedKeyRing modified = applyModificationWithChecks(parcel, ring, onlyA, onlyB,
passphrase); passphrase);
@ -1050,9 +1051,9 @@ public class PgpKeyOperationTest {
PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag); PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag);
// modify keyring, change to non-empty passphrase // modify keyring, change to non-empty passphrase
String otherPassphrase = TestingUtils.genPassphrase(true); Passphrase otherPassphrase = TestingUtils.genPassphrase(true);
parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase); 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)", Assert.assertEquals("exactly three packets should have been modified (the secret keys)",
3, onlyB.size()); 3, onlyB.size());
@ -1075,7 +1076,7 @@ public class PgpKeyOperationTest {
Assert.assertEquals("extracted packet should be a secret subkey", Assert.assertEquals("extracted packet should be a secret subkey",
PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag); PacketTags.SECRET_SUBKEY, sKeyNoPassphrase.tag);
String otherPassphrase2 = TestingUtils.genPassphrase(true); Passphrase otherPassphrase2 = TestingUtils.genPassphrase(true);
parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase2); parcel.mNewUnlock = new ChangeUnlockParcel(otherPassphrase2);
{ {
// if we replace a secret key with one without passphrase // if we replace a secret key with one without passphrase
@ -1112,7 +1113,7 @@ public class PgpKeyOperationTest {
@Test @Test
public void testUnlockPin() throws Exception { public void testUnlockPin() throws Exception {
String pin = "5235125"; Passphrase pin = new Passphrase("5235125");
// change passphrase to a pin type // change passphrase to a pin type
parcel.mNewUnlock = new ChangeUnlockParcel(null, pin); parcel.mNewUnlock = new ChangeUnlockParcel(null, pin);
@ -1138,7 +1139,7 @@ public class PgpKeyOperationTest {
Thread.sleep(1000); 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); applyModificationWithChecks(parcel, modified, onlyA, onlyB, pin, true, false);
Assert.assertEquals("exactly four packets should have been removed (the secret keys + notation packet)", Assert.assertEquals("exactly four packets should have been removed (the secret keys + notation packet)",
@ -1171,7 +1172,7 @@ public class PgpKeyOperationTest {
UncachedKeyRing ring, UncachedKeyRing ring,
ArrayList<RawPacket> onlyA, ArrayList<RawPacket> onlyA,
ArrayList<RawPacket> onlyB, ArrayList<RawPacket> onlyB,
String passphrase) { Passphrase passphrase) {
return applyModificationWithChecks(parcel, ring, onlyA, onlyB, passphrase, true, true); return applyModificationWithChecks(parcel, ring, onlyA, onlyB, passphrase, true, true);
} }
@ -1180,7 +1181,7 @@ public class PgpKeyOperationTest {
UncachedKeyRing ring, UncachedKeyRing ring,
ArrayList<RawPacket> onlyA, ArrayList<RawPacket> onlyA,
ArrayList<RawPacket> onlyB, ArrayList<RawPacket> onlyB,
String passphrase, Passphrase passphrase,
boolean canonicalize, boolean canonicalize,
boolean constantCanonicalize) { boolean constantCanonicalize) {
@ -1257,7 +1258,7 @@ public class PgpKeyOperationTest {
} }
private void assertModifyFailure(String reason, UncachedKeyRing ring, private void assertModifyFailure(String reason, UncachedKeyRing ring,
SaveKeyringParcel parcel, String passphrase, LogType expected) SaveKeyringParcel parcel, Passphrase passphrase, LogType expected)
throws Exception { throws Exception {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0); CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(ring.getEncoded(), false, 0);

View File

@ -61,6 +61,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.security.Security; import java.security.Security;
@ -111,7 +112,7 @@ public class UncachedKeyringCanonicalizeTest {
} }
// passphrase is tested in PgpKeyOperationTest, just use empty here // passphrase is tested in PgpKeyOperationTest, just use empty here
parcel.mNewUnlock = new ChangeUnlockParcel(""); parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(parcel);
@ -546,7 +547,7 @@ public class UncachedKeyringCanonicalizeTest {
CanonicalizedSecretKeyRing canonicalized = (CanonicalizedSecretKeyRing) ring.canonicalize(log, 0); CanonicalizedSecretKeyRing canonicalized = (CanonicalizedSecretKeyRing) ring.canonicalize(log, 0);
CanonicalizedSecretKey masterSecretKey = canonicalized.getSecretKey(); CanonicalizedSecretKey masterSecretKey = canonicalized.getSecretKey();
masterSecretKey.unlock(""); masterSecretKey.unlock(new Passphrase());
PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey(); PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature( PGPSignature cert = PgpKeyOperation.generateSubkeyBindingSignature(
masterPublicKey, masterSecretKey.getPrivateKey(), masterSecretKey.getPrivateKey(), masterPublicKey, masterSecretKey.getPrivateKey(), masterSecretKey.getPrivateKey(),

View File

@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper; import org.sufficientlysecure.keychain.support.KeyringTestingHelper;
import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket; import org.sufficientlysecure.keychain.support.KeyringTestingHelper.RawPacket;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -105,7 +106,7 @@ public class UncachedKeyringMergeTest {
} }
// passphrase is tested in PgpKeyOperationTest, just use empty here // passphrase is tested in PgpKeyOperationTest, just use empty here
parcel.mNewUnlock = new ChangeUnlockParcel(""); parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
OperationResult.OperationLog log = new OperationResult.OperationLog(); OperationResult.OperationLog log = new OperationResult.OperationLog();
@ -122,7 +123,7 @@ public class UncachedKeyringMergeTest {
parcel.mAddUserIds.add("shy"); parcel.mAddUserIds.add("shy");
// passphrase is tested in PgpKeyOperationTest, just use empty here // passphrase is tested in PgpKeyOperationTest, just use empty here
parcel.mNewUnlock = new ChangeUnlockParcel(""); parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
OperationResult.OperationLog log = new OperationResult.OperationLog(); OperationResult.OperationLog log = new OperationResult.OperationLog();
@ -185,11 +186,11 @@ public class UncachedKeyringMergeTest {
parcel.reset(); parcel.reset();
parcel.mAddUserIds.add("flim"); parcel.mAddUserIds.add("flim");
modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); modifiedA = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
parcel.reset(); parcel.reset();
parcel.mAddUserIds.add("flam"); parcel.mAddUserIds.add("flam");
modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); modifiedB = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
} }
{ // merge A into base { // merge A into base
@ -226,8 +227,8 @@ public class UncachedKeyringMergeTest {
parcel.reset(); parcel.reset();
parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, 1024, null, KeyFlags.SIGN_DATA, 0L));
modifiedA = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); modifiedA = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
modifiedB = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); modifiedB = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2); subKeyIdA = KeyringTestingHelper.getSubkeyId(modifiedA, 2);
subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2); subKeyIdB = KeyringTestingHelper.getSubkeyId(modifiedB, 2);
@ -268,7 +269,7 @@ public class UncachedKeyringMergeTest {
parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(ringA, 1)); parcel.mRevokeSubKeys.add(KeyringTestingHelper.getSubkeyId(ringA, 1));
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing( CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
ringA.getEncoded(), false, 0); 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( CanonicalizedSecretKey secretKey = new CanonicalizedSecretKeyRing(
ringB.getEncoded(), false, 0).getSecretKey(); ringB.getEncoded(), false, 0).getSecretKey();
secretKey.unlock(""); secretKey.unlock(new Passphrase());
// sign all user ids // sign all user ids
modified = secretKey.certifyUserIds(publicRing, publicRing.getPublicKey().getUnorderedUserIds(), null, null); modified = secretKey.certifyUserIds(publicRing, publicRing.getPublicKey().getUnorderedUserIds(), null, null);
} }
@ -362,7 +363,7 @@ public class UncachedKeyringMergeTest {
CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing( CanonicalizedSecretKeyRing secretRing = new CanonicalizedSecretKeyRing(
ringA.getEncoded(), false, 0); ringA.getEncoded(), false, 0);
modified = op.modifySecretKeyRing(secretRing, parcel, "").getRing(); modified = op.modifySecretKeyRing(secretRing, parcel, new Passphrase()).getRing();
} }
{ {

View File

@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -70,7 +71,7 @@ public class UncachedKeyringTest {
parcel.mAddUserAttribute.add(uat); parcel.mAddUserAttribute.add(uat);
} }
// passphrase is tested in PgpKeyOperationTest, just use empty here // passphrase is tested in PgpKeyOperationTest, just use empty here
parcel.mNewUnlock = new ChangeUnlockParcel(""); parcel.mNewUnlock = new ChangeUnlockParcel(new Passphrase());
PgpKeyOperation op = new PgpKeyOperation(null); PgpKeyOperation op = new PgpKeyOperation(null);
PgpEditKeyResult result = op.createSecretKeyRing(parcel); PgpEditKeyResult result = op.createSecretKeyRing(parcel);

View File

@ -1,16 +1,30 @@
/*
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.util; package org.sufficientlysecure.keychain.util;
import java.util.Random; import java.util.Random;
/**
* Created by valodim on 9/15/14.
*/
public class TestingUtils { public class TestingUtils {
public static String genPassphrase() { public static Passphrase genPassphrase() {
return genPassphrase(false); return genPassphrase(false);
} }
public static String genPassphrase(boolean noEmpty) { public static Passphrase genPassphrase(boolean noEmpty) {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789!@#$%^&*()-_="; String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789!@#$%^&*()-_=";
Random r = new Random(); Random r = new Random();
StringBuilder passbuilder = new StringBuilder(); StringBuilder passbuilder = new StringBuilder();
@ -19,6 +33,6 @@ public class TestingUtils {
passbuilder.append(chars.charAt(r.nextInt(chars.length()))); passbuilder.append(chars.charAt(r.nextInt(chars.length())));
} }
System.out.println("Generated passphrase: '" + passbuilder.toString() + "'"); System.out.println("Generated passphrase: '" + passbuilder.toString() + "'");
return passbuilder.toString(); return new Passphrase(passbuilder.toString());
} }
} }

View File

@ -86,16 +86,10 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".ui.FirstTimeActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/app_name"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity <activity
android:name=".ui.CreateKeyActivity" android:name=".ui.CreateKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:label="@string/title_create_key" android:label="@string/title_manage_my_keys"
android:parentActivityName=".ui.MainActivity"> android:parentActivityName=".ui.MainActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"

View File

@ -70,6 +70,8 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> {
modified = true; modified = true;
} }
// keep track if this key result is from a HKP keyserver
boolean incomingFromHkpServer = true;
// were going to want to try to fetch the key from everywhere we found it, so remember // were going to want to try to fetch the key from everywhere we found it, so remember
// all the origins // all the origins
for (String origin : incoming.getOrigins()) { for (String origin : incoming.getOrigins()) {
@ -78,13 +80,24 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> {
// to work properly, Keybase-sourced entries need to pass along the extra // to work properly, Keybase-sourced entries need to pass along the extra
if (KeybaseKeyserver.ORIGIN.equals(origin)) { if (KeybaseKeyserver.ORIGIN.equals(origin)) {
existing.setExtraData(incoming.getExtraData()); existing.setExtraData(incoming.getExtraData());
// one of the origins is not a HKP keyserver
incomingFromHkpServer = false;
} }
} }
ArrayList<String> incomingIDs = incoming.getUserIds(); ArrayList<String> incomingIDs = incoming.getUserIds();
ArrayList<String> existingIDs = existing.getUserIds(); ArrayList<String> existingIDs = existing.getUserIds();
for (String incomingID : incomingIDs) { for (String incomingID : incomingIDs) {
if (!existingIDs.contains(incomingID)) { if (!existingIDs.contains(incomingID)) {
existingIDs.add(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 {
existingIDs.add(incomingID);
}
modified = true; modified = true;
} }
} }

View File

@ -24,6 +24,7 @@ import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -101,7 +102,7 @@ public abstract class BaseOperation implements PassphraseCacheInterface {
} }
@Override @Override
public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException { public Passphrase getCachedPassphrase(long subKeyId) throws NoSecretKeyException {
try { try {
long masterKeyId = mProviderHelper.getMasterKeyId(subKeyId); long masterKeyId = mProviderHelper.getMasterKeyId(subKeyId);
return getCachedPassphrase(masterKeyId, subKeyId); return getCachedPassphrase(masterKeyId, subKeyId);
@ -111,7 +112,7 @@ public abstract class BaseOperation implements PassphraseCacheInterface {
} }
@Override @Override
public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException { public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException {
try { try {
return PassphraseCacheService.getCachedPassphrase( return PassphraseCacheService.getCachedPassphrase(
mContext, masterKeyId, subKeyId); mContext, masterKeyId, subKeyId);

View File

@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -83,7 +84,7 @@ public class CertifyOperation extends BaseOperation {
} }
// certification is always with the master key id, so use that one // certification is always with the master key id, so use that one
char[] passphrase = parcel.mCryptoInput.getPassphrase(); Passphrase passphrase = parcel.mCryptoInput.getPassphrase();
if (!certificationKey.unlock(passphrase)) { if (!certificationKey.unlock(passphrase)) {
log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2); log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2);

View File

@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;

View File

@ -22,6 +22,7 @@ import android.os.Parcel;
import org.openintents.openpgp.OpenPgpMetadata; import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.util.Passphrase;
public class DecryptVerifyResult extends OperationResult { public class DecryptVerifyResult extends OperationResult {
@ -37,7 +38,7 @@ public class DecryptVerifyResult extends OperationResult {
long mNfcSubKeyId; long mNfcSubKeyId;
byte[] mNfcSessionKey; byte[] mNfcSessionKey;
String mNfcPassphrase; Passphrase mNfcPassphrase;
OpenPgpSignatureResult mSignatureResult; OpenPgpSignatureResult mSignatureResult;
OpenPgpMetadata mDecryptMetadata; OpenPgpMetadata mDecryptMetadata;
@ -53,7 +54,7 @@ public class DecryptVerifyResult extends OperationResult {
mKeyIdPassphraseNeeded = keyIdPassphraseNeeded; mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
} }
public void setNfcState(long subKeyId, byte[] sessionKey, String passphrase) { public void setNfcState(long subKeyId, byte[] sessionKey, Passphrase passphrase) {
mNfcSubKeyId = subKeyId; mNfcSubKeyId = subKeyId;
mNfcSessionKey = sessionKey; mNfcSessionKey = sessionKey;
mNfcPassphrase = passphrase; mNfcPassphrase = passphrase;
@ -67,7 +68,7 @@ public class DecryptVerifyResult extends OperationResult {
return mNfcSessionKey; return mNfcSessionKey;
} }
public String getNfcPassphrase() { public Passphrase getNfcPassphrase() {
return mNfcPassphrase; return mNfcPassphrase;
} }
@ -109,7 +110,7 @@ public class DecryptVerifyResult extends OperationResult {
mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader()); mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader());
mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader()); mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader());
mNfcSessionKey = source.readInt() != 0 ? source.createByteArray() : null; mNfcSessionKey = source.readInt() != 0 ? source.createByteArray() : null;
mNfcPassphrase = source.readString(); mNfcPassphrase = source.readParcelable(Passphrase.class.getClassLoader());
} }
public int describeContents() { public int describeContents() {
@ -127,7 +128,7 @@ public class DecryptVerifyResult extends OperationResult {
} else { } else {
dest.writeInt(0); dest.writeInt(0);
} }
dest.writeString(mNfcPassphrase); dest.writeParcelable(mNfcPassphrase, flags);
} }
public static final Creator<DecryptVerifyResult> CREATOR = new Creator<DecryptVerifyResult>() { public static final Creator<DecryptVerifyResult> CREATOR = new Creator<DecryptVerifyResult>() {

View File

@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel; import android.os.Parcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.Date; import java.util.Date;
public class PgpSignEncryptResult extends OperationResult { public class PgpSignEncryptResult extends OperationResult {
@ -35,7 +37,7 @@ public class PgpSignEncryptResult extends OperationResult {
long mNfcKeyId; long mNfcKeyId;
byte[] mNfcHash; byte[] mNfcHash;
int mNfcAlgo; int mNfcAlgo;
String mNfcPassphrase; Passphrase mNfcPassphrase;
byte[] mDetachedSignature; byte[] mDetachedSignature;
public long getKeyIdPassphraseNeeded() { public long getKeyIdPassphraseNeeded() {
@ -46,7 +48,7 @@ public class PgpSignEncryptResult extends OperationResult {
mKeyIdPassphraseNeeded = keyIdPassphraseNeeded; mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
} }
public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, String passphrase) { public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Passphrase passphrase) {
mNfcKeyId = nfcKeyId; mNfcKeyId = nfcKeyId;
mNfcHash = nfcHash; mNfcHash = nfcHash;
mNfcAlgo = nfcAlgo; mNfcAlgo = nfcAlgo;
@ -69,7 +71,7 @@ public class PgpSignEncryptResult extends OperationResult {
return mNfcAlgo; return mNfcAlgo;
} }
public String getNfcPassphrase() { public Passphrase getNfcPassphrase() {
return mNfcPassphrase; return mNfcPassphrase;
} }
@ -123,4 +125,4 @@ public class PgpSignEncryptResult extends OperationResult {
} }
}; };
} }

View File

@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
@ -149,14 +150,10 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
} }
public boolean unlock(String passphrase) throws PgpGeneralException {
return unlock(passphrase.toCharArray());
}
/** /**
* Returns true on right passphrase * Returns true on right passphrase
*/ */
public boolean unlock(char[] passphrase) throws PgpGeneralException { public boolean unlock(Passphrase passphrase) throws PgpGeneralException {
// handle keys on OpenPGP cards like they were unlocked // handle keys on OpenPGP cards like they were unlocked
if (mSecretKey.getS2K() != null if (mSecretKey.getS2K() != null
&& mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K && mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K
@ -168,7 +165,7 @@ public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
// try to extract keys using the passphrase // try to extract keys using the passphrase
try { try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase); Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor); mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor);
mPrivateKeyState = PRIVATE_KEY_STATE_UNLOCKED; mPrivateKeyState = PRIVATE_KEY_STATE_UNLOCKED;
} catch (PGPException e) { } catch (PGPException e) {

View File

@ -18,14 +18,16 @@
package org.sufficientlysecure.keychain.pgp; package org.sufficientlysecure.keychain.pgp;
import org.sufficientlysecure.keychain.util.Passphrase;
public interface PassphraseCacheInterface { public interface PassphraseCacheInterface {
public static class NoSecretKeyException extends Exception { public static class NoSecretKeyException extends Exception {
public NoSecretKeyException() { 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;
} }

View File

@ -60,6 +60,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
@ -83,7 +84,7 @@ public class PgpDecryptVerify extends BaseOperation {
private OutputStream mOutStream; private OutputStream mOutStream;
private boolean mAllowSymmetricDecryption; private boolean mAllowSymmetricDecryption;
private String mPassphrase; private Passphrase mPassphrase;
private Set<Long> mAllowedKeyIds; private Set<Long> mAllowedKeyIds;
private boolean mDecryptMetadataOnly; private boolean mDecryptMetadataOnly;
private byte[] mDecryptedSessionKey; private byte[] mDecryptedSessionKey;
@ -118,7 +119,7 @@ public class PgpDecryptVerify extends BaseOperation {
private OutputStream mOutStream = null; private OutputStream mOutStream = null;
private Progressable mProgressable = null; private Progressable mProgressable = null;
private boolean mAllowSymmetricDecryption = true; private boolean mAllowSymmetricDecryption = true;
private String mPassphrase = null; private Passphrase mPassphrase = null;
private Set<Long> mAllowedKeyIds = null; private Set<Long> mAllowedKeyIds = null;
private boolean mDecryptMetadataOnly = false; private boolean mDecryptMetadataOnly = false;
private byte[] mDecryptedSessionKey = null; private byte[] mDecryptedSessionKey = null;
@ -159,7 +160,7 @@ public class PgpDecryptVerify extends BaseOperation {
return this; return this;
} }
public Builder setPassphrase(String passphrase) { public Builder setPassphrase(Passphrase passphrase) {
mPassphrase = passphrase; mPassphrase = passphrase;
return this; return this;
} }
@ -572,7 +573,7 @@ public class PgpDecryptVerify extends BaseOperation {
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build();
PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder(
digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
mPassphrase.toCharArray()); mPassphrase.getCharArray());
clear = encryptedDataSymmetric.getDataStream(decryptorFactory); clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
encryptedData = encryptedDataSymmetric; encryptedData = encryptedDataSymmetric;

View File

@ -63,6 +63,7 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSign
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.Primes;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
@ -328,7 +329,7 @@ public class PgpKeyOperation {
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator()); masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
subProgressPush(50, 100); subProgressPush(50, 100);
CryptoInputParcel cryptoInput = new CryptoInputParcel(new Date(), ""); mCryptoInput = new CryptoInputParcel(new Date(), new Passphrase(""));
return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, log); return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, log);
} catch (PGPException e) { } catch (PGPException e) {
@ -444,7 +445,7 @@ public class PgpKeyOperation {
{ {
try { try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mCryptoInput.getPassphrase()); Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mCryptoInput.getPassphrase().getCharArray());
masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor); masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) { } catch (PGPException e) {
log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1); log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1);
@ -815,7 +816,7 @@ public class PgpKeyOperation {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder() PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
mCryptoInput.getPassphrase()); mCryptoInput.getPassphrase().getCharArray());
PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor); PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
PGPSignature sig = generateSubkeyBindingSignature( PGPSignature sig = generateSubkeyBindingSignature(
getSignatureGenerator(masterSecretKey, mCryptoInput), getSignatureGenerator(masterSecretKey, mCryptoInput),
@ -911,17 +912,14 @@ public class PgpKeyOperation {
} }
PGPSecretKey sKey; { PGPSecretKey sKey; {
char[] passphrase = mCryptoInput.getPassphrase();
if (passphrase == null) {
passphrase = new char[] { };
}
// Build key encrypter and decrypter based on passphrase // Build key encrypter and decrypter based on passphrase
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder() PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
.build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO); .build().get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT) PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
mCryptoInput.getPassphrase().getCharArray());
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder() PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
.build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO); .build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
@ -1055,7 +1053,7 @@ public class PgpKeyOperation {
PGPSecretKeyRing sKR, PGPSecretKeyRing sKR,
PGPPublicKey masterPublicKey, PGPPublicKey masterPublicKey,
PGPPrivateKey masterPrivateKey, PGPPrivateKey masterPrivateKey,
char[] passphrase, Passphrase passphrase,
ChangeUnlockParcel newUnlock, ChangeUnlockParcel newUnlock,
OperationLog log, int indent) throws PGPException { OperationLog log, int indent) throws PGPException {
@ -1139,20 +1137,19 @@ public class PgpKeyOperation {
private static PGPSecretKeyRing applyNewPassphrase( private static PGPSecretKeyRing applyNewPassphrase(
PGPSecretKeyRing sKR, PGPSecretKeyRing sKR,
PGPPublicKey masterPublicKey, PGPPublicKey masterPublicKey,
char[] passphrase, Passphrase passphrase,
String newPassphrase, Passphrase newPassphrase,
OperationLog log, int indent) throws PGPException { OperationLog log, int indent) throws PGPException {
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build() PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
.get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO); .get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase); Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
// Build key encryptor based on new passphrase // Build key encryptor based on new passphrase
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT) PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
newPassphrase.toCharArray());
// noinspection unchecked // noinspection unchecked
for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) { for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {

View File

@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Date; import java.util.Date;
@ -36,12 +37,12 @@ public class PgpSignEncryptInputParcel implements Parcelable {
protected boolean mEnableAsciiArmorOutput = false; protected boolean mEnableAsciiArmorOutput = false;
protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED; protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
protected long[] mEncryptionMasterKeyIds = null; protected long[] mEncryptionMasterKeyIds = null;
protected String mSymmetricPassphrase = null; protected Passphrase mSymmetricPassphrase = null;
protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED; protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED;
protected long mSignatureMasterKeyId = Constants.key.none; protected long mSignatureMasterKeyId = Constants.key.none;
protected Long mSignatureSubKeyId = null; protected Long mSignatureSubKeyId = null;
protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED; protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;
protected String mSignaturePassphrase = null; protected Passphrase mSignaturePassphrase = null;
protected long mAdditionalEncryptId = Constants.key.none; protected long mAdditionalEncryptId = Constants.key.none;
protected boolean mFailOnMissingEncryptionKeyIds = false; protected boolean mFailOnMissingEncryptionKeyIds = false;
protected String mCharset; protected String mCharset;
@ -56,17 +57,19 @@ public class PgpSignEncryptInputParcel implements Parcelable {
PgpSignEncryptInputParcel(Parcel source) { PgpSignEncryptInputParcel(Parcel source) {
ClassLoader loader = getClass().getClassLoader();
// we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable // we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable
mVersionHeader = source.readString(); mVersionHeader = source.readString();
mEnableAsciiArmorOutput = source.readInt() == 1; mEnableAsciiArmorOutput = source.readInt() == 1;
mCompressionId = source.readInt(); mCompressionId = source.readInt();
mEncryptionMasterKeyIds = source.createLongArray(); mEncryptionMasterKeyIds = source.createLongArray();
mSymmetricPassphrase = source.readString(); mSymmetricPassphrase = source.readParcelable(loader);
mSymmetricEncryptionAlgorithm = source.readInt(); mSymmetricEncryptionAlgorithm = source.readInt();
mSignatureMasterKeyId = source.readLong(); mSignatureMasterKeyId = source.readLong();
mSignatureSubKeyId = source.readInt() == 1 ? source.readLong() : null; mSignatureSubKeyId = source.readInt() == 1 ? source.readLong() : null;
mSignatureHashAlgorithm = source.readInt(); mSignatureHashAlgorithm = source.readInt();
mSignaturePassphrase = source.readString(); mSignaturePassphrase = source.readParcelable(loader);
mAdditionalEncryptId = source.readLong(); mAdditionalEncryptId = source.readLong();
mFailOnMissingEncryptionKeyIds = source.readInt() == 1; mFailOnMissingEncryptionKeyIds = source.readInt() == 1;
mCharset = source.readString(); mCharset = source.readString();
@ -74,7 +77,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
mDetachedSignature = source.readInt() == 1; mDetachedSignature = source.readInt() == 1;
mHiddenRecipients = source.readInt() == 1; mHiddenRecipients = source.readInt() == 1;
mCryptoInput = source.readParcelable(PgpSignEncryptInputParcel.class.getClassLoader()); mCryptoInput = source.readParcelable(loader);
} }
@Override @Override
@ -88,7 +91,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0); dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
dest.writeInt(mCompressionId); dest.writeInt(mCompressionId);
dest.writeLongArray(mEncryptionMasterKeyIds); dest.writeLongArray(mEncryptionMasterKeyIds);
dest.writeString(mSymmetricPassphrase); dest.writeParcelable(mSymmetricPassphrase, 0);
dest.writeInt(mSymmetricEncryptionAlgorithm); dest.writeInt(mSymmetricEncryptionAlgorithm);
dest.writeLong(mSignatureMasterKeyId); dest.writeLong(mSignatureMasterKeyId);
if (mSignatureSubKeyId != null) { if (mSignatureSubKeyId != null) {
@ -98,7 +101,7 @@ public class PgpSignEncryptInputParcel implements Parcelable {
dest.writeInt(0); dest.writeInt(0);
} }
dest.writeInt(mSignatureHashAlgorithm); dest.writeInt(mSignatureHashAlgorithm);
dest.writeString(mSignaturePassphrase); dest.writeParcelable(mSignaturePassphrase, 0);
dest.writeLong(mAdditionalEncryptId); dest.writeLong(mAdditionalEncryptId);
dest.writeInt(mFailOnMissingEncryptionKeyIds ? 1 : 0); dest.writeInt(mFailOnMissingEncryptionKeyIds ? 1 : 0);
dest.writeString(mCharset); dest.writeString(mCharset);
@ -130,11 +133,11 @@ public class PgpSignEncryptInputParcel implements Parcelable {
return this; return this;
} }
public String getSignaturePassphrase() { public Passphrase getSignaturePassphrase() {
return mSignaturePassphrase; return mSignaturePassphrase;
} }
public PgpSignEncryptInputParcel setSignaturePassphrase(String signaturePassphrase) { public PgpSignEncryptInputParcel setSignaturePassphrase(Passphrase signaturePassphrase) {
mSignaturePassphrase = signaturePassphrase; mSignaturePassphrase = signaturePassphrase;
return this; return this;
} }
@ -175,11 +178,11 @@ public class PgpSignEncryptInputParcel implements Parcelable {
return this; return this;
} }
public String getSymmetricPassphrase() { public Passphrase getSymmetricPassphrase() {
return mSymmetricPassphrase; return mSymmetricPassphrase;
} }
public PgpSignEncryptInputParcel setSymmetricPassphrase(String symmetricPassphrase) { public PgpSignEncryptInputParcel setSymmetricPassphrase(Passphrase symmetricPassphrase) {
mSymmetricPassphrase = symmetricPassphrase; mSymmetricPassphrase = symmetricPassphrase;
return this; return this;
} }

View File

@ -243,7 +243,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
log.add(LogType.MSG_PSE_SYMMETRIC, indent); log.add(LogType.MSG_PSE_SYMMETRIC, indent);
JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator = JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().toCharArray()); new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().getCharArray());
cPk.addMethod(symmetricEncryptionGenerator); cPk.addMethod(symmetricEncryptionGenerator);
} else { } else {
log.add(LogType.MSG_PSE_ASYMMETRIC, indent); log.add(LogType.MSG_PSE_ASYMMETRIC, indent);

View File

@ -21,6 +21,8 @@ package org.sufficientlysecure.keychain.pgp;
import android.net.Uri; import android.net.Uri;
import android.os.Parcel; import android.os.Parcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;

View File

@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -180,7 +181,7 @@ public class OpenPgpService extends RemoteService {
return result; 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 // build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(getBaseContext(), NfcActivity.class); Intent intent = new Intent(getBaseContext(), NfcActivity.class);
intent.setAction(NfcActivity.ACTION_SIGN_HASH); intent.setAction(NfcActivity.ACTION_SIGN_HASH);
@ -196,7 +197,7 @@ public class OpenPgpService extends RemoteService {
PendingIntent.FLAG_CANCEL_CURRENT); 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 // build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(getBaseContext(), NfcActivity.class); Intent intent = new Intent(getBaseContext(), NfcActivity.class);
intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY); intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY);
@ -519,7 +520,7 @@ public class OpenPgpService extends RemoteService {
KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg)); KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg));
} }
String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); Passphrase passphrase = data.getParcelableExtra(OpenPgpApi.EXTRA_PASSPHRASE);
long inputLength = is.available(); long inputLength = is.available();
InputData inputData = new InputData(is, inputLength); InputData inputData = new InputData(is, inputLength);

View File

@ -66,6 +66,7 @@ import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache; import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -283,7 +284,7 @@ public class KeychainIntentService extends IntentService implements Progressable
try { try {
/* Input */ /* Input */
String passphrase = data.getString(DECRYPT_PASSPHRASE); Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE);
byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
InputData inputData = createDecryptInputData(data); InputData inputData = createDecryptInputData(data);
@ -413,7 +414,7 @@ public class KeychainIntentService extends IntentService implements Progressable
try { try {
/* Input */ /* Input */
String passphrase = data.getString(DECRYPT_PASSPHRASE); Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE);
byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY); byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
InputData inputData = createDecryptInputData(data); InputData inputData = createDecryptInputData(data);

View File

@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.util.Date; 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. * 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, public static void addCachedPassphrase(Context context, long masterKeyId, long subKeyId,
String passphrase, Passphrase passphrase,
String primaryUserId) { String primaryUserId) {
Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + masterKeyId); 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) * @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 " Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphrase() for masterKeyId "
+ masterKeyId + ", subKeyId " + subKeyId); + masterKeyId + ", subKeyId " + subKeyId);
@ -190,7 +191,9 @@ public class PassphraseCacheService extends Service {
switch (returnMessage.what) { switch (returnMessage.what) {
case MSG_PASSPHRASE_CACHE_GET_OKAY: 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: case MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND:
throw new KeyNotFoundException(); throw new KeyNotFoundException();
default: default:
@ -202,11 +205,11 @@ public class PassphraseCacheService extends Service {
/** /**
* Internal implementation to get cached passphrase. * 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? // passphrase for symmetric encryption?
if (masterKeyId == Constants.key.symmetric) { if (masterKeyId == Constants.key.symmetric) {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption"); 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) { if (cachedPassphrase == null) {
return null; return null;
} }
@ -232,13 +235,13 @@ public class PassphraseCacheService extends Service {
case DIVERT_TO_CARD: case DIVERT_TO_CARD:
if (Preferences.getPreferences(this).useDefaultYubikeyPin()) { if (Preferences.getPreferences(this).useDefaultYubikeyPin()) {
Log.d(Constants.TAG, "PassphraseCacheService: Using default Yubikey PIN: 123456"); 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 { } else {
Log.d(Constants.TAG, "PassphraseCacheService: NOT using default Yubikey PIN"); Log.d(Constants.TAG, "PassphraseCacheService: NOT using default Yubikey PIN");
break; break;
} }
case PASSPHRASE_EMPTY: case PASSPHRASE_EMPTY:
return ""; return new Passphrase("");
case UNAVAILABLE: case UNAVAILABLE:
throw new ProviderHelper.NotFoundException("secret key for this subkey is not available"); throw new ProviderHelper.NotFoundException("secret key for this subkey is not available");
case GNU_DUMMY: case GNU_DUMMY:
@ -331,7 +334,7 @@ public class PassphraseCacheService extends Service {
long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1); long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_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); String primaryUserID = intent.getStringExtra(EXTRA_USER_ID);
Log.d(Constants.TAG, Log.d(Constants.TAG,
@ -373,10 +376,10 @@ public class PassphraseCacheService extends Service {
Log.e(Constants.TAG, "PassphraseCacheService: Bad request, missing masterKeyId or subKeyId!"); Log.e(Constants.TAG, "PassphraseCacheService: Bad request, missing masterKeyId or subKeyId!");
msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND; msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND;
} else { } else {
String passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId); Passphrase passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId);
msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY; msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY;
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putString(EXTRA_PASSPHRASE, passphrase); bundle.putParcelable(EXTRA_PASSPHRASE, passphrase);
msg.setData(bundle); msg.setData(bundle);
} }
} catch (ProviderHelper.NotFoundException e) { } catch (ProviderHelper.NotFoundException e) {
@ -412,7 +415,10 @@ public class PassphraseCacheService extends Service {
* Called when one specific passphrase for keyId timed out * Called when one specific passphrase for keyId timed out
*/ */
private void timeout(Context context, long keyId) { 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); mPassphraseCache.remove(keyId);
Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!"); Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!");
@ -517,9 +523,9 @@ public class PassphraseCacheService extends Service {
public class CachedPassphrase { public class CachedPassphrase {
private String primaryUserID; private String primaryUserID;
private String passphrase; private Passphrase passphrase;
public CachedPassphrase(String passphrase, String primaryUserID) { public CachedPassphrase(Passphrase passphrase, String primaryUserID) {
setPassphrase(passphrase); setPassphrase(passphrase);
setPrimaryUserID(primaryUserID); setPrimaryUserID(primaryUserID);
} }
@ -528,7 +534,7 @@ public class PassphraseCacheService extends Service {
return primaryUserID; return primaryUserID;
} }
public String getPassphrase() { public Passphrase getPassphrase() {
return passphrase; return passphrase;
} }
@ -536,7 +542,7 @@ public class PassphraseCacheService extends Service {
this.primaryUserID = primaryUserID; this.primaryUserID = primaryUserID;
} }
public void setPassphrase(String passphrase) { public void setPassphrase(Passphrase passphrase) {
this.passphrase = passphrase; this.passphrase = passphrase;
} }
} }

View File

@ -22,6 +22,7 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
@ -296,33 +297,30 @@ public class SaveKeyringParcel implements Parcelable {
public static class ChangeUnlockParcel implements Parcelable { public static class ChangeUnlockParcel implements Parcelable {
// The new passphrase to use // The new passphrase to use
public final String mNewPassphrase; public final Passphrase mNewPassphrase;
// A new pin to use. Must only contain [0-9]+ // 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); this(newPassphrase, null);
} }
public ChangeUnlockParcel(String newPassphrase, String newPin) { public ChangeUnlockParcel(Passphrase newPassphrase, Passphrase newPin) {
if (newPassphrase == null && newPin == null) { if (newPassphrase == null && newPin == null) {
throw new RuntimeException("Cannot set both passphrase and pin. THIS IS A BUG!"); 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; mNewPassphrase = newPassphrase;
mNewPin = newPin; mNewPin = newPin;
} }
public ChangeUnlockParcel(Parcel source) { public ChangeUnlockParcel(Parcel source) {
mNewPassphrase = source.readString(); mNewPassphrase = source.readParcelable(Passphrase.class.getClassLoader());
mNewPin = source.readString(); mNewPin = source.readParcelable(Passphrase.class.getClassLoader());
} }
@Override @Override
public void writeToParcel(Parcel destination, int flags) { public void writeToParcel(Parcel destination, int flags) {
destination.writeString(mNewPassphrase); destination.writeParcelable(mNewPassphrase, flags);
destination.writeString(mNewPin); destination.writeParcelable(mNewPin, flags);
} }
@Override @Override

View File

@ -9,6 +9,8 @@ import java.util.Map;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import org.sufficientlysecure.keychain.util.Passphrase;
/** This is a base class for the input of crypto operations. /** This is a base class for the input of crypto operations.
* *
@ -16,13 +18,13 @@ import android.os.Parcelable;
public class CryptoInputParcel implements Parcelable { public class CryptoInputParcel implements Parcelable {
final Date mSignatureTime; final Date mSignatureTime;
final String mPassphrase; final Passphrase mPassphrase;
// this map contains both decrypted session keys and signed hashes to be // this map contains both decrypted session keys and signed hashes to be
// used in the crypto operation described by this parcel. // used in the crypto operation described by this parcel.
private HashMap<ByteBuffer,byte[]> mCryptoData = new HashMap<>(); private HashMap<ByteBuffer,byte[]> mCryptoData = new HashMap<>();
public CryptoInputParcel(Date signatureTime, String passphrase) { public CryptoInputParcel(Date signatureTime, Passphrase passphrase) {
mSignatureTime = signatureTime == null ? new Date() : signatureTime; mSignatureTime = signatureTime == null ? new Date() : signatureTime;
mPassphrase = passphrase; mPassphrase = passphrase;
} }
@ -34,7 +36,7 @@ public class CryptoInputParcel implements Parcelable {
protected CryptoInputParcel(Parcel source) { protected CryptoInputParcel(Parcel source) {
mSignatureTime = new Date(source.readLong()); mSignatureTime = new Date(source.readLong());
mPassphrase = source.readString(); mPassphrase = source.readParcelable(getClass().getClassLoader());
{ {
int count = source.readInt(); int count = source.readInt();
@ -56,7 +58,7 @@ public class CryptoInputParcel implements Parcelable {
@Override @Override
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mSignatureTime.getTime()); dest.writeLong(mSignatureTime.getTime());
dest.writeString(mPassphrase); dest.writeParcelable(mPassphrase, 0);
dest.writeInt(mCryptoData.size()); dest.writeInt(mCryptoData.size());
for (HashMap.Entry<ByteBuffer,byte[]> entry : mCryptoData.entrySet()) { for (HashMap.Entry<ByteBuffer,byte[]> entry : mCryptoData.entrySet()) {
@ -81,8 +83,8 @@ public class CryptoInputParcel implements Parcelable {
return mPassphrase != null; return mPassphrase != null;
} }
public char[] getPassphrase() { public Passphrase getPassphrase() {
return mPassphrase == null ? null : mPassphrase.toCharArray(); return mPassphrase;
} }
public static final Creator<CryptoInputParcel> CREATOR = new Creator<CryptoInputParcel>() { public static final Creator<CryptoInputParcel> CREATOR = new Creator<CryptoInputParcel>() {

View File

@ -62,6 +62,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.ui.widget.KeySpinner;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.lang.reflect.Method; import java.lang.reflect.Method;

View File

@ -20,31 +20,74 @@ package org.sufficientlysecure.keychain.ui;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction; import android.support.v4.app.FragmentTransaction;
import android.view.View;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList;
public class CreateKeyActivity extends BaseActivity { public class CreateKeyActivity extends BaseActivity {
public static final String EXTRA_NAME = "name"; public static final String EXTRA_NAME = "name";
public static final String EXTRA_EMAIL = "email"; public static final String EXTRA_EMAIL = "email";
public static final String EXTRA_FIRST_TIME = "first_time";
public static final String EXTRA_ADDITIONAL_EMAILS = "additional_emails";
public static final String EXTRA_PASSPHRASE = "passphrase";
public static enum FragAction { public static final String FRAGMENT_TAG = "currentFragment";
START,
TO_RIGHT, String mName;
TO_LEFT String mEmail;
} ArrayList<String> mAdditionalEmails;
Passphrase mPassphrase;
boolean mFirstTime;
Fragment mCurrentFragment;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// pass extras into fragment // Check whether we're recreating a previously destroyed instance
CreateKeyNameFragment frag = if (savedInstanceState != null) {
CreateKeyNameFragment.newInstance( // Restore value of members from saved state
getIntent().getStringExtra(EXTRA_NAME), mName = savedInstanceState.getString(EXTRA_NAME);
getIntent().getStringExtra(EXTRA_EMAIL) mEmail = savedInstanceState.getString(EXTRA_EMAIL);
); mAdditionalEmails = savedInstanceState.getStringArrayList(EXTRA_ADDITIONAL_EMAILS);
loadFragment(null, frag, FragAction.START); 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);
mToolbar.setNavigationOnClickListener(null);
} 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 @Override
@ -52,23 +95,23 @@ public class CreateKeyActivity extends BaseActivity {
setContentView(R.layout.create_key_activity); setContentView(R.layout.create_key_activity);
} }
public void loadFragment(Bundle savedInstanceState, Fragment fragment, FragAction action) { public static enum FragAction {
// However, if we're being restored from a previous state, START,
// then we don't need to do anything and should return or else TO_RIGHT,
// we could end up with overlapping fragments. TO_LEFT
if (savedInstanceState != null) { }
return;
} public void loadFragment(Fragment fragment, FragAction action) {
mCurrentFragment = fragment;
// Add the fragment to the 'fragment_container' FrameLayout // Add the fragment to the 'fragment_container' FrameLayout
// NOTE: We use commitAllowingStateLoss() to prevent weird crashes!
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
switch (action) { switch (action) {
case START: case START:
transaction.setCustomAnimations(0, 0); transaction.setCustomAnimations(0, 0);
transaction.replace(R.id.create_key_fragment_container, fragment) transaction.replace(R.id.create_key_fragment_container, fragment, FRAGMENT_TAG)
.commitAllowingStateLoss(); .commit();
break; break;
case TO_LEFT: case TO_LEFT:
getSupportFragmentManager().popBackStackImmediate(); getSupportFragmentManager().popBackStackImmediate();
@ -77,8 +120,8 @@ public class CreateKeyActivity extends BaseActivity {
transaction.setCustomAnimations(R.anim.frag_slide_in_from_right, R.anim.frag_slide_out_to_left, 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); R.anim.frag_slide_in_from_left, R.anim.frag_slide_out_to_right);
transaction.addToBackStack(null); transaction.addToBackStack(null);
transaction.replace(R.id.create_key_fragment_container, fragment) transaction.replace(R.id.create_key_fragment_container, fragment, FRAGMENT_TAG)
.commitAllowingStateLoss(); .commit();
break; break;
} }

View File

@ -46,16 +46,12 @@ import java.util.List;
public class CreateKeyEmailFragment extends Fragment { public class CreateKeyEmailFragment extends Fragment {
public static final String ARG_NAME = "name";
public static final String ARG_EMAIL = "email";
CreateKeyActivity mCreateKeyActivity; CreateKeyActivity mCreateKeyActivity;
EmailEditText mEmailEdit; EmailEditText mEmailEdit;
RecyclerView mEmailsRecyclerView; RecyclerView mEmailsRecyclerView;
View mBackButton; View mBackButton;
View mNextButton; View mNextButton;
String mName;
ArrayList<EmailAdapter.ViewModel> mAdditionalEmailModels; ArrayList<EmailAdapter.ViewModel> mAdditionalEmailModels;
EmailAdapter mEmailAdapter; EmailAdapter mEmailAdapter;
@ -63,13 +59,10 @@ public class CreateKeyEmailFragment extends Fragment {
/** /**
* Creates new instance of this fragment * Creates new instance of this fragment
*/ */
public static CreateKeyEmailFragment newInstance(String name, String email) { public static CreateKeyEmailFragment newInstance() {
CreateKeyEmailFragment frag = new CreateKeyEmailFragment(); CreateKeyEmailFragment frag = new CreateKeyEmailFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(ARG_NAME, name);
args.putString(ARG_EMAIL, email);
frag.setArguments(args); frag.setArguments(args);
return frag; return frag;
@ -85,7 +78,7 @@ public class CreateKeyEmailFragment extends Fragment {
*/ */
private static boolean isEditTextNotEmpty(Context context, EditText editText) { private static boolean isEditTextNotEmpty(Context context, EditText editText) {
boolean output = true; boolean output = true;
if (editText.getText().toString().length() == 0) { if (editText.getText().length() == 0) {
editText.setError(context.getString(R.string.create_key_empty)); editText.setError(context.getString(R.string.create_key_empty));
editText.requestFocus(); editText.requestFocus();
output = false; output = false;
@ -106,31 +99,33 @@ public class CreateKeyEmailFragment extends Fragment {
mEmailsRecyclerView = (RecyclerView) view.findViewById(R.id.create_key_emails); mEmailsRecyclerView = (RecyclerView) view.findViewById(R.id.create_key_emails);
// initial values // initial values
mName = getArguments().getString(ARG_NAME); mEmailEdit.setText(mCreateKeyActivity.mEmail);
String email = getArguments().getString(ARG_EMAIL);
mEmailEdit.setText(email);
// focus empty edit fields // focus empty edit fields
if (email == null) { if (mCreateKeyActivity.mEmail == null) {
mEmailEdit.requestFocus(); mEmailEdit.requestFocus();
} }
mBackButton.setOnClickListener(new View.OnClickListener() { mBackButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT); mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
} }
}); });
mNextButton.setOnClickListener(new View.OnClickListener() { mNextButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
createKeyCheck(); nextClicked();
} }
}); });
mEmailsRecyclerView.setHasFixedSize(true); mEmailsRecyclerView.setHasFixedSize(true);
mEmailsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); mEmailsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mEmailsRecyclerView.setItemAnimator(new DefaultItemAnimator()); mEmailsRecyclerView.setItemAnimator(new DefaultItemAnimator());
// initial values
mAdditionalEmailModels = new ArrayList<>(); mAdditionalEmailModels = new ArrayList<>();
if (mCreateKeyActivity.mAdditionalEmails != null) {
setAdditionalEmails(mCreateKeyActivity.mAdditionalEmails);
}
mEmailAdapter = new EmailAdapter(mAdditionalEmailModels, new View.OnClickListener() { mEmailAdapter = new EmailAdapter(mAdditionalEmailModels, new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -171,25 +166,38 @@ public class CreateKeyEmailFragment extends Fragment {
mCreateKeyActivity = (CreateKeyActivity) getActivity(); mCreateKeyActivity = (CreateKeyActivity) getActivity();
} }
private void createKeyCheck() { private void nextClicked() {
if (isEditTextNotEmpty(getActivity(), mEmailEdit)) { if (isEditTextNotEmpty(getActivity(), mEmailEdit)) {
// save state
mCreateKeyActivity.mEmail = mEmailEdit.getText().toString();
mCreateKeyActivity.mAdditionalEmails = getAdditionalEmails();
ArrayList<String> emails = new ArrayList<>(); CreateKeyPassphraseFragment frag = CreateKeyPassphraseFragment.newInstance();
for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) { mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
emails.add(holder.toString());
}
CreateKeyPassphraseFragment frag =
CreateKeyPassphraseFragment.newInstance(
mName,
mEmailEdit.getText().toString(),
emails
);
mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT);
} }
} }
private ArrayList<String> getAdditionalEmails() {
ArrayList<String> emails = new ArrayList<>();
for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) {
emails.add(holder.toString());
}
return emails;
}
private void setAdditionalEmails(ArrayList<String> 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<RecyclerView.ViewHolder> { public static class EmailAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<ViewModel> mDataset; private List<ViewModel> mDataset;
private View.OnClickListener mFooterOnClickListener; private View.OnClickListener mFooterOnClickListener;

View File

@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList; import java.util.ArrayList;
@ -64,32 +65,15 @@ public class CreateKeyFinalFragment extends Fragment {
TextView mEditText; TextView mEditText;
View mEditButton; 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<String> mAdditionalEmails;
String mPassphrase;
SaveKeyringParcel mSaveKeyringParcel; SaveKeyringParcel mSaveKeyringParcel;
/** /**
* Creates new instance of this fragment * Creates new instance of this fragment
*/ */
public static CreateKeyFinalFragment newInstance(String name, String email, public static CreateKeyFinalFragment newInstance() {
ArrayList<String> additionalEmails,
String passphrase) {
CreateKeyFinalFragment frag = new CreateKeyFinalFragment(); CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
Bundle args = new Bundle(); 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);
frag.setArguments(args); frag.setArguments(args);
return frag; return frag;
@ -107,17 +91,11 @@ public class CreateKeyFinalFragment extends Fragment {
mEditText = (TextView) view.findViewById(R.id.create_key_edit_text); mEditText = (TextView) view.findViewById(R.id.create_key_edit_text);
mEditButton = view.findViewById(R.id.create_key_edit_button); 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().getString(ARG_PASSPHRASE);
// set values // set values
mNameEdit.setText(mName); mNameEdit.setText(mCreateKeyActivity.mName);
if (mAdditionalEmails != null && mAdditionalEmails.size() > 0) { if (mCreateKeyActivity.mAdditionalEmails != null && mCreateKeyActivity.mAdditionalEmails.size() > 0) {
String emailText = mEmail + ", "; String emailText = mCreateKeyActivity.mEmail + ", ";
Iterator<?> it = mAdditionalEmails.iterator(); Iterator<?> it = mCreateKeyActivity.mAdditionalEmails.iterator();
while (it.hasNext()) { while (it.hasNext()) {
Object next = it.next(); Object next = it.next();
emailText += next; emailText += next;
@ -127,7 +105,7 @@ public class CreateKeyFinalFragment extends Fragment {
} }
mEmailEdit.setText(emailText); mEmailEdit.setText(emailText);
} else { } else {
mEmailEdit.setText(mEmail); mEmailEdit.setText(mCreateKeyActivity.mEmail);
} }
mCreateButton.setOnClickListener(new View.OnClickListener() { mCreateButton.setOnClickListener(new View.OnClickListener() {
@ -140,7 +118,7 @@ public class CreateKeyFinalFragment extends Fragment {
mBackButton.setOnClickListener(new View.OnClickListener() { mBackButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT); mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
} }
}); });
@ -156,6 +134,12 @@ public class CreateKeyFinalFragment extends Fragment {
return view; return view;
} }
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCreateKeyActivity = (CreateKeyActivity) getActivity();
}
@Override @Override
public void onActivityResult(int requestCode, int resultCode, Intent data) { public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) { switch (requestCode) {
@ -186,17 +170,22 @@ public class CreateKeyFinalFragment extends Fragment {
Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L)); Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L));
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd( mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L)); 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.mAddUserIds.add(userId);
mSaveKeyringParcel.mChangePrimaryUserId = userId; mSaveKeyringParcel.mChangePrimaryUserId = userId;
if (mAdditionalEmails != null && mAdditionalEmails.size() > 0) { if (mCreateKeyActivity.mAdditionalEmails != null
for (String email : mAdditionalEmails) { && mCreateKeyActivity.mAdditionalEmails.size() > 0) {
String thisUserId = KeyRing.createUserId(new KeyRing.UserId(mName, email, null)); for (String email : mCreateKeyActivity.mAdditionalEmails) {
String thisUserId = KeyRing.createUserId(
new KeyRing.UserId(mCreateKeyActivity.mName, email, null)
);
mSaveKeyringParcel.mAddUserIds.add(thisUserId); mSaveKeyringParcel.mAddUserIds.add(thisUserId);
} }
} }
mSaveKeyringParcel.mNewUnlock = mPassphrase != null mSaveKeyringParcel.mNewUnlock = mCreateKeyActivity.mPassphrase != null
? new ChangeUnlockParcel(mPassphrase, null) ? new ChangeUnlockParcel(mCreateKeyActivity.mPassphrase, null)
: null; : null;
} }
} }

View File

@ -24,34 +24,26 @@ import android.support.v4.app.Fragment;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText; import android.widget.EditText;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.widget.EmailEditText;
import org.sufficientlysecure.keychain.ui.widget.NameEditText; import org.sufficientlysecure.keychain.ui.widget.NameEditText;
public class CreateKeyNameFragment extends Fragment { public class CreateKeyNameFragment extends Fragment {
public static final String ARG_NAME = "name";
public static final String ARG_EMAIL = "email";
CreateKeyActivity mCreateKeyActivity; CreateKeyActivity mCreateKeyActivity;
NameEditText mNameEdit; NameEditText mNameEdit;
View mBackButton;
View mNextButton; View mNextButton;
String mEmail;
/** /**
* Creates new instance of this fragment * Creates new instance of this fragment
*/ */
public static CreateKeyNameFragment newInstance(String name, String email) { public static CreateKeyNameFragment newInstance() {
CreateKeyNameFragment frag = new CreateKeyNameFragment(); CreateKeyNameFragment frag = new CreateKeyNameFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(ARG_NAME, name);
args.putString(ARG_EMAIL, email);
frag.setArguments(args); frag.setArguments(args);
@ -68,7 +60,7 @@ public class CreateKeyNameFragment extends Fragment {
*/ */
private static boolean isEditTextNotEmpty(Context context, EditText editText) { private static boolean isEditTextNotEmpty(Context context, EditText editText) {
boolean output = true; boolean output = true;
if (editText.getText().toString().length() == 0) { if (editText.getText().length() == 0) {
editText.setError(context.getString(R.string.create_key_empty)); editText.setError(context.getString(R.string.create_key_empty));
editText.requestFocus(); editText.requestFocus();
output = false; output = false;
@ -79,39 +71,31 @@ public class CreateKeyNameFragment extends Fragment {
return output; 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 @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.create_key_name_fragment, container, false); View view = inflater.inflate(R.layout.create_key_name_fragment, container, false);
mNameEdit = (NameEditText) view.findViewById(R.id.create_key_name); 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); mNextButton = view.findViewById(R.id.create_key_next_button);
// initial values // initial values
String name = getArguments().getString(ARG_NAME); mNameEdit.setText(mCreateKeyActivity.mName);
mEmail = getArguments().getString(ARG_EMAIL);
mNameEdit.setText(name);
// focus empty edit fields // focus empty edit fields
if (name == null) { if (mCreateKeyActivity.mName == null) {
mNameEdit.requestFocus(); mNameEdit.requestFocus();
} }
mBackButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
}
});
mNextButton.setOnClickListener(new View.OnClickListener() { mNextButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
createKeyCheck(); nextClicked();
} }
}); });
@ -124,16 +108,13 @@ public class CreateKeyNameFragment extends Fragment {
mCreateKeyActivity = (CreateKeyActivity) getActivity(); mCreateKeyActivity = (CreateKeyActivity) getActivity();
} }
private void createKeyCheck() { private void nextClicked() {
if (isEditTextNotEmpty(getActivity(), mNameEdit)) { if (isEditTextNotEmpty(getActivity(), mNameEdit)) {
// save state
mCreateKeyActivity.mName = mNameEdit.getText().toString();
CreateKeyEmailFragment frag = CreateKeyEmailFragment frag = CreateKeyEmailFragment.newInstance();
CreateKeyEmailFragment.newInstance( mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
mNameEdit.getText().toString(),
mEmail
);
mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT);
} }
} }

View File

@ -21,6 +21,7 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.method.HideReturnsTransformationMethod; import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod; import android.text.method.PasswordTransformationMethod;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -34,20 +35,13 @@ import android.widget.EditText;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText; import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
public class CreateKeyPassphraseFragment extends Fragment { 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<String> mAdditionalEmails;
// view // view
CreateKeyActivity mCreateKeyActivity; CreateKeyActivity mCreateKeyActivity;
PassphraseEditText mPassphraseEdit; PassphraseEditText mPassphraseEdit;
@ -59,15 +53,10 @@ public class CreateKeyPassphraseFragment extends Fragment {
/** /**
* Creates new instance of this fragment * Creates new instance of this fragment
*/ */
public static CreateKeyPassphraseFragment newInstance(String name, String email, public static CreateKeyPassphraseFragment newInstance() {
ArrayList<String> additionalEmails) {
CreateKeyPassphraseFragment frag = new CreateKeyPassphraseFragment(); CreateKeyPassphraseFragment frag = new CreateKeyPassphraseFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putString(ARG_NAME, name);
args.putString(ARG_EMAIL, email);
args.putStringArrayList(ARG_ADDITIONAL_EMAILS, additionalEmails);
frag.setArguments(args); frag.setArguments(args);
return frag; return frag;
@ -83,7 +72,7 @@ public class CreateKeyPassphraseFragment extends Fragment {
*/ */
private static boolean isEditTextNotEmpty(Context context, EditText editText) { private static boolean isEditTextNotEmpty(Context context, EditText editText) {
boolean output = true; boolean output = true;
if (editText.getText().toString().length() == 0) { if (editText.getText().length() == 0) {
editText.setError(context.getString(R.string.create_key_empty)); editText.setError(context.getString(R.string.create_key_empty));
editText.requestFocus(); editText.requestFocus();
output = false; output = false;
@ -95,11 +84,13 @@ public class CreateKeyPassphraseFragment extends Fragment {
} }
private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) { private static boolean areEditTextsEqual(Context context, EditText editText1, EditText editText2) {
boolean output = true; Passphrase p1 = new Passphrase(editText1);
if (!editText1.getText().toString().equals(editText2.getText().toString())) { Passphrase p2 = new Passphrase(editText2);
boolean output = (p1.equals(p2));
if (!output) {
editText2.setError(context.getString(R.string.create_key_passphrases_not_equal)); editText2.setError(context.getString(R.string.create_key_passphrases_not_equal));
editText2.requestFocus(); editText2.requestFocus();
output = false;
} else { } else {
editText2.setError(null); editText2.setError(null);
} }
@ -118,9 +109,12 @@ public class CreateKeyPassphraseFragment extends Fragment {
mNextButton = view.findViewById(R.id.create_key_next_button); mNextButton = view.findViewById(R.id.create_key_next_button);
// initial values // initial values
mName = getArguments().getString(ARG_NAME); // TODO: using String here is unsafe...
mEmail = getArguments().getString(ARG_EMAIL); if (mCreateKeyActivity.mPassphrase != null) {
mAdditionalEmails = getArguments().getStringArrayList(ARG_ADDITIONAL_EMAILS); mPassphraseEdit.setText(Arrays.toString(mCreateKeyActivity.mPassphrase.getCharArray()));
mPassphraseEditAgain.setText(Arrays.toString(mCreateKeyActivity.mPassphrase.getCharArray()));
}
mPassphraseEdit.requestFocus(); mPassphraseEdit.requestFocus();
mBackButton.setOnClickListener(new View.OnClickListener() { mBackButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
@ -131,7 +125,7 @@ public class CreateKeyPassphraseFragment extends Fragment {
mNextButton.setOnClickListener(new View.OnClickListener() { mNextButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
createKeyCheck(); nextClicked();
} }
}); });
mShowPassphrase.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { mShowPassphrase.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@ -159,23 +153,19 @@ public class CreateKeyPassphraseFragment extends Fragment {
private void back() { private void back() {
hideKeyboard(); hideKeyboard();
mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT); mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
} }
private void createKeyCheck() { private void nextClicked() {
if (isEditTextNotEmpty(getActivity(), mPassphraseEdit) if (isEditTextNotEmpty(getActivity(), mPassphraseEdit)
&& areEditTextsEqual(getActivity(), mPassphraseEdit, mPassphraseEditAgain)) { && areEditTextsEqual(getActivity(), mPassphraseEdit, mPassphraseEditAgain)) {
CreateKeyFinalFragment frag = // save state
CreateKeyFinalFragment.newInstance( mCreateKeyActivity.mPassphrase = new Passphrase(mPassphraseEdit);
mName,
mEmail,
mAdditionalEmails,
mPassphraseEdit.getText().toString()
);
CreateKeyFinalFragment frag = CreateKeyFinalFragment.newInstance();
hideKeyboard(); hideKeyboard();
mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT); mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
} }
} }

View File

@ -0,0 +1,157 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
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();
}
}

View File

@ -191,7 +191,7 @@ public class DecryptFilesFragment extends DecryptFragment {
data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal()); data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal());
data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri); 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); data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data); intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@ -265,7 +265,7 @@ public class DecryptFilesFragment extends DecryptFragment {
data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal()); data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal());
data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri); 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); data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data); intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@ -341,7 +341,7 @@ public class DecryptFilesFragment extends DecryptFragment {
switch (requestCode) { switch (requestCode) {
case REQUEST_CODE_PASSPHRASE: { case REQUEST_CODE_PASSPHRASE: {
if (resultCode == Activity.RESULT_OK && data != null) { if (resultCode == Activity.RESULT_OK && data != null) {
mPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); mPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
decryptOriginalFilename(); decryptOriginalFilename();
} }
return; return;

View File

@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.util.Passphrase;
public abstract class DecryptFragment extends Fragment { public abstract class DecryptFragment extends Fragment {
private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006; private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006;
@ -57,7 +58,7 @@ public abstract class DecryptFragment extends Fragment {
// State // State
protected String mPassphrase; protected Passphrase mPassphrase;
protected byte[] mNfcDecryptedSessionKey; protected byte[] mNfcDecryptedSessionKey;
@Override @Override
@ -100,7 +101,7 @@ public abstract class DecryptFragment extends Fragment {
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); 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 // build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(getActivity(), NfcActivity.class); Intent intent = new Intent(getActivity(), NfcActivity.class);
intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY); intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY);

View File

@ -161,7 +161,7 @@ public class DecryptTextFragment extends DecryptFragment {
// data // data
data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal()); data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal());
data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes()); 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); data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data); intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@ -247,7 +247,7 @@ public class DecryptTextFragment extends DecryptFragment {
case REQUEST_CODE_PASSPHRASE: { case REQUEST_CODE_PASSPHRASE: {
if (resultCode == Activity.RESULT_OK && data != null) { if (resultCode == Activity.RESULT_OK && data != null) {
mPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); mPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
decryptStart(); decryptStart();
} else { } else {
getActivity().finish(); getActivity().finish();

View File

@ -69,6 +69,8 @@ import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
public class EditKeyFragment extends CryptoOperationFragment implements public class EditKeyFragment extends CryptoOperationFragment implements
LoaderManager.LoaderCallbacks<Cursor> { LoaderManager.LoaderCallbacks<Cursor> {
@ -337,7 +339,7 @@ public class EditKeyFragment extends CryptoOperationFragment implements
// cache new returned passphrase! // cache new returned passphrase!
mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel( mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(
data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE), (Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
null null
); );
} }

View File

@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.util.Passphrase;
public abstract class EncryptActivity extends BaseActivity { public abstract class EncryptActivity extends BaseActivity {
@ -41,7 +42,7 @@ public abstract class EncryptActivity extends BaseActivity {
public static final int REQUEST_CODE_NFC = 0x00008002; public static final int REQUEST_CODE_NFC = 0x00008002;
// For NFC data // For NFC data
protected String mSigningKeyPassphrase = null; protected Passphrase mSigningKeyPassphrase = null;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -76,7 +77,7 @@ public abstract class EncryptActivity extends BaseActivity {
switch (requestCode) { switch (requestCode) {
case REQUEST_CODE_PASSPHRASE: { case REQUEST_CODE_PASSPHRASE: {
if (resultCode == RESULT_OK && data != null) { if (resultCode == RESULT_OK && data != null) {
mSigningKeyPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); mSigningKeyPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
startEncrypt(); startEncrypt();
return; return;
} }

View File

@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui;
import android.net.Uri; import android.net.Uri;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList; import java.util.ArrayList;
public interface EncryptActivityInterface { public interface EncryptActivityInterface {
@ -39,7 +41,7 @@ public interface EncryptActivityInterface {
public void setEncryptionKeys(long[] encryptionKeys); public void setEncryptionKeys(long[] encryptionKeys);
public void setEncryptionUsers(String[] encryptionUsers); public void setEncryptionUsers(String[] encryptionUsers);
public void setPassphrase(String passphrase); public void setPassphrase(Passphrase passphrase);
// ArrayList on purpose as only those are parcelable // ArrayList on purpose as only those are parcelable
public ArrayList<Uri> getInputUris(); public ArrayList<Uri> getInputUris();

View File

@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ShareHelper; import org.sufficientlysecure.keychain.util.ShareHelper;
import java.util.ArrayList; import java.util.ArrayList;
@ -72,7 +73,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
private long mEncryptionKeyIds[] = null; private long mEncryptionKeyIds[] = null;
private String mEncryptionUserIds[] = null; private String mEncryptionUserIds[] = null;
private long mSigningKeyId = Constants.key.none; private long mSigningKeyId = Constants.key.none;
private String mPassphrase = ""; private Passphrase mPassphrase = new Passphrase();
private ArrayList<Uri> mInputUris; private ArrayList<Uri> mInputUris;
private ArrayList<Uri> mOutputUris; private ArrayList<Uri> mOutputUris;
@ -136,7 +137,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
} }
@Override @Override
public void setPassphrase(String passphrase) { public void setPassphrase(Passphrase passphrase) {
mPassphrase = passphrase; mPassphrase = passphrase;
} }
@ -243,8 +244,8 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
if (isModeSymmetric()) { if (isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!"); Log.d(Constants.TAG, "Symmetric encryption enabled!");
String passphrase = mPassphrase; Passphrase passphrase = mPassphrase;
if (passphrase.length() == 0) { if (passphrase.isEmpty()) {
passphrase = null; passphrase = null;
} }
data.setSymmetricPassphrase(passphrase); data.setSymmetricPassphrase(passphrase);

View File

@ -28,6 +28,7 @@ import android.view.ViewGroup;
import android.widget.EditText; import android.widget.EditText;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.util.Passphrase;
public class EncryptSymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener { public class EncryptSymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener {
@ -67,8 +68,13 @@ public class EncryptSymmetricFragment extends Fragment implements EncryptActivit
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
// update passphrase in EncryptActivity // update passphrase in EncryptActivity
if (mPassphrase.getText().toString().equals(mPassphraseAgain.getText().toString())) { Passphrase p1 = new Passphrase(mPassphrase.getText());
mEncryptInterface.setPassphrase(s.toString()); Passphrase p2 = new Passphrase(mPassphraseAgain.getText());
boolean passesEquals = (p1.equals(p2));
p1.removeFromMemory();
p2.removeFromMemory();
if (passesEquals) {
mEncryptInterface.setPassphrase(new Passphrase(mPassphrase.getText()));
} else { } else {
mEncryptInterface.setPassphrase(null); mEncryptInterface.setPassphrase(null);
} }

View File

@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.PgpConstants;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ShareHelper; import org.sufficientlysecure.keychain.util.ShareHelper;
import java.util.ArrayList; import java.util.ArrayList;
@ -70,7 +71,7 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
private String mEncryptionUserIds[] = null; private String mEncryptionUserIds[] = null;
// TODO Constants.key.none? What's wrong with a null value? // TODO Constants.key.none? What's wrong with a null value?
private long mSigningKeyId = Constants.key.none; private long mSigningKeyId = Constants.key.none;
private String mPassphrase = ""; private Passphrase mPassphrase = new Passphrase();
private ArrayList<Uri> mInputUris; private ArrayList<Uri> mInputUris;
private ArrayList<Uri> mOutputUris; private ArrayList<Uri> mOutputUris;
@ -134,7 +135,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
} }
@Override @Override
public void setPassphrase(String passphrase) { public void setPassphrase(Passphrase passphrase) {
mPassphrase.removeFromMemory();
mPassphrase = passphrase; mPassphrase = passphrase;
} }
@ -223,8 +225,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
if (isModeSymmetric()) { if (isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!"); Log.d(Constants.TAG, "Symmetric encryption enabled!");
String passphrase = mPassphrase; Passphrase passphrase = mPassphrase;
if (passphrase.length() == 0) { if (passphrase.isEmpty()) {
passphrase = null; passphrase = null;
} }
data.setSymmetricPassphrase(passphrase); data.setSymmetricPassphrase(passphrase);

View File

@ -1,110 +0,0 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.ui;
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);
}
}

View File

@ -489,7 +489,8 @@ public class KeyListFragment extends LoaderFragment
case R.id.menu_key_list_debug_first_time: case R.id.menu_key_list_debug_first_time:
Preferences prefs = Preferences.getPreferences(getActivity()); Preferences prefs = Preferences.getPreferences(getActivity());
prefs.setFirstTime(true); 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); startActivity(intent);
getActivity().finish(); getActivity().finish();
return true; return true;

View File

@ -54,7 +54,9 @@ public class MainActivity extends MaterialNavigationDrawer implements FabContain
// if this is the first time show first time activity // if this is the first time show first time activity
Preferences prefs = Preferences.getPreferences(this); Preferences prefs = Preferences.getPreferences(this);
if (prefs.isFirstTime()) { 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(); finish();
return; return;
} }

View File

@ -59,6 +59,7 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
/** /**
@ -333,7 +334,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
positive.setOnClickListener(new View.OnClickListener() { positive.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { 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 // Early breakout if we are dealing with a symmetric key
if (mSecretRing == null) { if (mSecretRing == null) {
@ -410,7 +411,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. // any indication this isn't needed anymore, don't do it.
if (mIsCancelled || getActivity() == null) { if (mIsCancelled || getActivity() == null) {
return; return;

View File

@ -21,18 +21,12 @@ package org.sufficientlysecure.keychain.ui;
import android.animation.ArgbEvaluator; import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.app.ActivityOptions; import android.app.ActivityOptions;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; 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.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -40,7 +34,6 @@ import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader; import android.support.v4.content.CursorLoader;
@ -58,9 +51,7 @@ import android.widget.ImageView;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.getbase.floatingactionbutton.FloatingActionButton; import com.getbase.floatingactionbutton.FloatingActionButton;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
@ -74,6 +65,8 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus; 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.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
@ -82,6 +75,7 @@ import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.ContactHelper;
import org.sufficientlysecure.keychain.util.ExportHelper; import org.sufficientlysecure.keychain.util.ExportHelper;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList; import java.util.ArrayList;
@ -91,8 +85,8 @@ public class ViewKeyActivity extends BaseActivity implements
LoaderManager.LoaderCallbacks<Cursor> { LoaderManager.LoaderCallbacks<Cursor> {
static final int REQUEST_QR_FINGERPRINT = 1; static final int REQUEST_QR_FINGERPRINT = 1;
static final int REQUEST_DELETE= 2; static final int REQUEST_DELETE = 2;
static final int REQUEST_EXPORT= 3; static final int REQUEST_EXPORT = 3;
ExportHelper mExportHelper; ExportHelper mExportHelper;
ProviderHelper mProviderHelper; ProviderHelper mProviderHelper;
@ -113,11 +107,7 @@ public class ViewKeyActivity extends BaseActivity implements
private CardView mQrCodeLayout; private CardView mQrCodeLayout;
// NFC // NFC
private NfcAdapter mNfcAdapter; private NfcHelper mNfcHelper;
private NfcAdapter.CreateNdefMessageCallback mNdefCallback;
private NfcAdapter.OnNdefPushCompleteCallback mNdefCompleteCallback;
private byte[] mNfcKeyringBytes;
private static final int NFC_SENT = 1;
private static final int LOADER_ID_UNIFIED = 0; private static final int LOADER_ID_UNIFIED = 0;
@ -254,7 +244,7 @@ public class ViewKeyActivity extends BaseActivity implements
mActionNfc.setOnClickListener(new View.OnClickListener() { mActionNfc.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
invokeNfcBeam(); mNfcHelper.invokeNfcBeam();
} }
}); });
@ -262,7 +252,8 @@ public class ViewKeyActivity extends BaseActivity implements
// or start new ones. // or start new ones.
getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
initNfc(mDataUri); mNfcHelper = new NfcHelper(this, mProviderHelper);
mNfcHelper.initNfc(mDataUri);
startFragment(savedInstanceState, mDataUri); startFragment(savedInstanceState, mDataUri);
} }
@ -310,31 +301,31 @@ public class ViewKeyActivity extends BaseActivity implements
return true; return true;
} }
case R.id.menu_key_view_export_file: { case R.id.menu_key_view_export_file: {
Intent mIntent = new Intent(this,PassphraseDialogActivity.class);
long keyId=0;
try { try {
keyId = new ProviderHelper(this) if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) {
.getCachedPublicKeyRing(mDataUri) exportToFile(mDataUri, mExportHelper, mProviderHelper);
.extractOrGetMasterKeyId(); return true;
} catch (PgpKeyNotFoundException e) { }
e.printStackTrace();
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; return true;
} }
case R.id.menu_key_view_delete: { case R.id.menu_key_view_delete: {
Intent mIntent = new Intent(this,PassphraseDialogActivity.class);
long keyId=0;
try { try {
keyId = new ProviderHelper(this) if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) {
.getCachedPublicKeyRing(mDataUri) deleteKey();
.extractOrGetMasterKeyId(); return true;
} catch (PgpKeyNotFoundException e) { }
e.printStackTrace();
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; return true;
} }
case R.id.menu_key_view_advanced: { case R.id.menu_key_view_advanced: {
@ -373,41 +364,6 @@ public class ViewKeyActivity extends BaseActivity implements
return true; 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() { private void scanQrCode() {
Intent scanQrCode = new Intent(this, ImportKeysProxyActivity.class); Intent scanQrCode = new Intent(this, ImportKeysProxyActivity.class);
@ -424,7 +380,7 @@ public class ViewKeyActivity extends BaseActivity implements
private void certifyImmediate() { private void certifyImmediate() {
Intent intent = new Intent(this, CertifyKeyActivity.class); 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); startCertifyIntent(intent);
} }
@ -473,22 +429,32 @@ public class ViewKeyActivity extends BaseActivity implements
ActivityCompat.startActivity(this, qrCodeIntent, opts); ActivityCompat.startActivity(this, qrCodeIntent, opts);
} }
private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) private void startPassphraseActivity(int requestCode) {
throws ProviderHelper.NotFoundException { Intent intent = new Intent(this, PassphraseDialogActivity.class);
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri); intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mMasterKeyId);
startActivityForResult(intent, requestCode);
HashMap<String, Object> 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 deleteKey(Uri dataUri, ExportHelper exportHelper) { private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) {
try {
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri);
HashMap<String, Object> 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 // Message is received after key is deleted
Handler returnHandler = new Handler() { Handler returnHandler = new Handler() {
@Override @Override
@ -500,7 +466,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 @Override
@ -530,18 +500,13 @@ public class ViewKeyActivity extends BaseActivity implements
return; return;
} }
if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK){ if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK) {
deleteKey(mDataUri, mExportHelper); deleteKey();
}
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);
}
} }
if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK) {
exportToFile(mDataUri, mExportHelper, mProviderHelper);
}
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
@ -561,7 +526,7 @@ public class ViewKeyActivity extends BaseActivity implements
long keyId = new ProviderHelper(this) long keyId = new ProviderHelper(this)
.getCachedPublicKeyRing(dataUri) .getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId(); .extractOrGetMasterKeyId();
long[] encryptionKeyIds = new long[]{keyId}; long[] encryptionKeyIds = new long[] {keyId};
Intent intent; Intent intent;
if (text) { if (text) {
intent = new Intent(this, EncryptTextActivity.class); intent = new Intent(this, EncryptTextActivity.class);
@ -699,98 +664,9 @@ public class ViewKeyActivity extends BaseActivity implements
loadTask.execute(); 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<Void, Void, Void> initTask =
new AsyncTask<Void, Void, Void>() {
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. // 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._ID,
KeychainContract.KeyRings.MASTER_KEY_ID, KeychainContract.KeyRings.MASTER_KEY_ID,
KeychainContract.KeyRings.USER_ID, KeychainContract.KeyRings.USER_ID,
@ -1007,4 +883,4 @@ public class ViewKeyActivity extends BaseActivity implements
public void onLoaderReset(Loader<Cursor> loader) { public void onLoaderReset(Loader<Cursor> loader) {
} }
} }

View File

@ -52,6 +52,7 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper;
import java.io.IOException; import java.io.IOException;
@ -68,10 +69,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
private View mFingerprintClipboardButton; private View mFingerprintClipboardButton;
private View mKeyShareButton; private View mKeyShareButton;
private View mKeyClipboardButton; private View mKeyClipboardButton;
private View mKeyNfcButton;
private ImageButton mKeySafeSlingerButton; private ImageButton mKeySafeSlingerButton;
private View mKeyUploadButton; private View mKeyUploadButton;
ProviderHelper mProviderHelper; ProviderHelper mProviderHelper;
NfcHelper mNfcHelper;
private static final int LOADER_ID_UNIFIED = 0; 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()); View view = inflater.inflate(R.layout.view_key_adv_share_fragment, getContainer());
mProviderHelper = new ProviderHelper(ViewKeyAdvShareFragment.this.getActivity()); mProviderHelper = new ProviderHelper(ViewKeyAdvShareFragment.this.getActivity());
mNfcHelper = new NfcHelper(getActivity(), mProviderHelper);
mFingerprint = (TextView) view.findViewById(R.id.view_key_fingerprint); mFingerprint = (TextView) view.findViewById(R.id.view_key_fingerprint);
mQrCode = (ImageView) view.findViewById(R.id.view_key_qr_code); 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); mFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share);
mFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard); mFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard);
mKeyShareButton = view.findViewById(R.id.view_key_action_key_share); 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); mKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard);
mKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger); mKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger);
mKeyUploadButton = view.findViewById(R.id.view_key_action_upload); mKeyUploadButton = view.findViewById(R.id.view_key_action_upload);
@ -128,6 +133,14 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
share(mDataUri, mProviderHelper, false, true); share(mDataUri, mProviderHelper, false, true);
} }
}); });
mKeyNfcButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mNfcHelper.invokeNfcBeam();
}
});
mKeySafeSlingerButton.setOnClickListener(new View.OnClickListener() { mKeySafeSlingerButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { 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, // Prepare the loaders. Either re-connect with an existing ones,
// or start new ones. // or start new ones.
getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); 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._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET,
KeyRings.USER_ID, KeyRings.FINGERPRINT, KeyRings.USER_ID, KeyRings.FINGERPRINT,
KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.IS_EXPIRED, KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.IS_EXPIRED,
@ -362,4 +378,5 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
startActivityForResult(uploadIntent, 0); startActivityForResult(uploadIntent, 0);
} }
}
}

View File

@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText; import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener { public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener {
private static final String ARG_MESSENGER = "messenger"; private static final String ARG_MESSENGER = "messenger";
@ -113,12 +114,12 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
public void onClick(DialogInterface dialog, int id) { public void onClick(DialogInterface dialog, int id) {
dismiss(); dismiss();
String passphrase1; Passphrase passphrase1 = new Passphrase();
if (mNoPassphraseCheckBox.isChecked()) { if (mNoPassphraseCheckBox.isChecked()) {
passphrase1 = ""; passphrase1.setEmpty();
} else { } else {
passphrase1 = mPassphraseEditText.getText().toString(); passphrase1 = new Passphrase(mPassphraseEditText);
String passphrase2 = mPassphraseAgainEditText.getText().toString(); Passphrase passphrase2 = new Passphrase(mPassphraseAgainEditText);
if (!passphrase1.equals(passphrase2)) { if (!passphrase1.equals(passphrase2)) {
Toast.makeText( Toast.makeText(
activity, activity,
@ -129,7 +130,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
return; return;
} }
if (passphrase1.equals("")) { if (passphrase1.isEmpty()) {
Toast.makeText( Toast.makeText(
activity, activity,
getString(R.string.error_message, getString(R.string.error_message,
@ -142,7 +143,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
// return resulting data back to activity // return resulting data back to activity
Bundle data = new Bundle(); Bundle data = new Bundle();
data.putString(MESSAGE_NEW_PASSPHRASE, passphrase1); data.putParcelable(MESSAGE_NEW_PASSPHRASE, passphrase1);
sendMessageToHandler(MESSAGE_OKAY, data); sendMessageToHandler(MESSAGE_OKAY, data);
} }

View File

@ -19,6 +19,11 @@ package org.sufficientlysecure.keychain.ui.util;
import android.app.Activity; import android.app.Activity;
import android.content.res.Resources; 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;
import com.nispok.snackbar.Snackbar.SnackbarDuration; import com.nispok.snackbar.Snackbar.SnackbarDuration;
@ -61,11 +66,11 @@ public class Notify {
break; 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) final Snackbar bar = getSnackbar(activity)
.text(resId); .text(resId);
@ -90,7 +95,7 @@ public class Notify {
return new Showable () { return new Showable () {
@Override @Override
public void show() { public void show() {
SnackbarManager.show(bar); showSnackbar(activity, bar);
} }
}; };
} }
@ -104,7 +109,7 @@ public class Notify {
return createNotify(activity, msg, duration, style, null, 0); 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 ActionListener listener, int resIdAction) {
final Snackbar bar = getSnackbar(activity) final Snackbar bar = getSnackbar(activity)
@ -141,7 +146,7 @@ public class Notify {
return new Showable () { return new Showable () {
@Override @Override
public void show() { public void show() {
SnackbarManager.show(bar); showSnackbar(activity, bar);
} }
}; };
@ -178,6 +183,26 @@ public class Notify {
return bar; 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 interface Showable {
public void show(); public void show();

View File

@ -58,9 +58,10 @@ public class EmailEditText extends AutoCompleteTextView {
} }
private void init() { private void init() {
this.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
this.addTextChangedListener(textWatcher); reenableKeyboardSuggestions();
removeFlag();
addTextChangedListener(textWatcher);
initAdapter(); initAdapter();
} }
@ -104,7 +105,7 @@ public class EmailEditText extends AutoCompleteTextView {
* Hack to re-enable keyboard auto correction in AutoCompleteTextView. * Hack to re-enable keyboard auto correction in AutoCompleteTextView.
* From http://stackoverflow.com/a/22512858 * From http://stackoverflow.com/a/22512858
*/ */
private void removeFlag() { private void reenableKeyboardSuggestions() {
int inputType = getInputType(); int inputType = getInputType();
inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE;
setRawInputType(inputType); setRawInputType(inputType);

View File

@ -50,7 +50,7 @@ public class NameEditText extends AutoCompleteTextView {
} }
private void init() { private void init() {
removeFlag(); reenableKeyboardSuggestions();
initAdapter(); 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 * From http://stackoverflow.com/a/22512858
*/ */
private void removeFlag() { private void reenableKeyboardSuggestions() {
int inputType = getInputType(); int inputType = getInputType();
inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE; inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE;
setRawInputType(inputType); setRawInputType(inputType);

View File

@ -47,21 +47,6 @@ public class ExportHelper {
this.mActivity = activity; 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 * Show dialog where to export keys
*/ */

View File

@ -0,0 +1,218 @@
/*
* Copyright (C) 2013-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Kent Nguyen <kentnguyen@moneylover.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.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.
*/
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<Void, Void, Void> initTask =
new AsyncTask<Void, Void, Void>() {
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<Activity> 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;
}
}
}
}
}

View File

@ -0,0 +1,163 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain.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;
/**
* 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:
* <p/>
* 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;
/**
* 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()
}
public Passphrase(EditText editText) {
this(editText.getText());
}
public Passphrase(char[] passphrase) {
mPassphrase = passphrase;
}
public Passphrase(String passphrase) {
mPassphrase = passphrase.toCharArray();
}
/**
* Creates a passphrase object with an empty ("") passphrase
*/
public Passphrase() {
setEmpty();
}
public char[] getCharArray() {
return mPassphrase;
}
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, ' ');
}
}
@Override
public void finalize() throws Throwable {
removeFromMemory();
super.finalize();
}
@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<Passphrase> CREATOR = new Creator<Passphrase>() {
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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -56,11 +56,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="@string/btn_back"
android:textAllCaps="true" android:textAllCaps="true"
android:minHeight="?android:attr/listPreferredItemHeight" android:minHeight="?android:attr/listPreferredItemHeight"
android:drawableLeft="@drawable/ic_chevron_left_grey_24dp"
android:drawablePadding="8dp" android:drawablePadding="8dp"
android:gravity="left|center_vertical" android:gravity="left|center_vertical"
android:clickable="false" android:clickable="true"
style="?android:attr/borderlessButtonStyle" /> style="?android:attr/borderlessButtonStyle" />
<TextView <TextView

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_above="@+id/create_key_buttons">
<ImageView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:adjustViewBounds="true"
android:src="@drawable/first_time_1"
android:layout_gravity="center_horizontal"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="@color/holo_gray_bright"
android:id="@+id/create_key_buttons">
<TextView
android:id="@+id/create_key_create_key_button"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/first_time_create_key"
android:textAllCaps="true"
android:minHeight="?android:attr/listPreferredItemHeight"
android:drawableRight="@drawable/ic_chevron_right_grey_24dp"
android:drawablePadding="8dp"
android:gravity="right|center_vertical"
android:clickable="true"
style="?android:attr/borderlessButtonStyle" />
<!--<TextView-->
<!--android:id="@+id/create_key_yubikey_button"-->
<!--android:paddingLeft="16dp"-->
<!--android:paddingRight="16dp"-->
<!--android:textAppearance="?android:attr/textAppearanceMedium"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content"-->
<!--android:layout_weight="1"-->
<!--android:text="@string/first_time_yubikey"-->
<!--android:textAllCaps="true"-->
<!--android:minHeight="?android:attr/listPreferredItemHeight"-->
<!--android:drawableRight="@drawable/ic_chevron_right_grey_24dp"-->
<!--android:drawablePadding="8dp"-->
<!--android:gravity="right|center_vertical"-->
<!--android:clickable="true"-->
<!--style="?android:attr/borderlessButtonStyle" />-->
<TextView
android:id="@+id/create_key_import_button"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/first_time_import_key"
android:textAllCaps="true"
android:minHeight="?android:attr/listPreferredItemHeight"
android:drawableRight="@drawable/ic_chevron_right_grey_24dp"
android:drawablePadding="8dp"
android:gravity="right|center_vertical"
android:clickable="true"
style="?android:attr/borderlessButtonStyle" />
<TextView
android:id="@+id/create_key_cancel"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_do_not_save"
android:textAllCaps="true"
android:minHeight="?android:attr/listPreferredItemHeight"
android:drawableRight="@drawable/ic_close_grey_24dp"
android:drawablePadding="8dp"
android:gravity="right|center_vertical"
android:clickable="true"
style="?android:attr/borderlessButtonStyle" />
</LinearLayout>
</RelativeLayout>

View File

@ -1,137 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="16dp">
<ImageView
android:id="@+id/status_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/statusbar_height" />
<LinearLayout
android:id="@+id/first_time_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:layout_alignParentBottom="true"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/first_time_import_key"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/first_time_import_key"
android:minHeight="?android:attr/listPreferredItemHeight"
android:drawableRight="@drawable/ic_folder_grey_24dp"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:clickable="true"
android:background="?android:selectableItemBackground" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="?android:attr/listDivider" />
<TextView
android:id="@+id/first_time_create_key"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/first_time_create_key"
android:minHeight="?android:attr/listPreferredItemHeight"
android:drawableRight="@drawable/ic_key_plus_grey600_24dp"
android:drawablePadding="8dp"
android:gravity="center_vertical"
android:layout_gravity="center_vertical"
android:clickable="true"
android:background="?android:selectableItemBackground" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dip"
android:background="?android:attr/listDivider" />
<TextView
android:id="@+id/first_time_cancel"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/first_time_skip"
android:minHeight="?android:attr/listPreferredItemHeight"
android:gravity="center"
android:clickable="true"
android:background="?android:selectableItemBackground"
android:layout_gravity="center_horizontal" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_below="@+id/status_bar"
android:layout_above="@+id/first_time_buttons">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="@string/app_name"
android:drawableLeft="@drawable/ic_launcher"
android:drawablePadding="8dp"
android:gravity="center"
android:layout_gravity="center_horizontal" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:adjustViewBounds="true"
android:src="@drawable/first_time_1"
android:layout_gravity="center_horizontal"
android:layout_weight="1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="64dp"
android:layout_marginRight="64dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/first_time_text1"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:layout_marginBottom="16dp" />
</LinearLayout>
</RelativeLayout>

View File

@ -99,14 +99,12 @@
android:layout_weight="1" /> android:layout_weight="1" />
<LinearLayout <LinearLayout
android:id="@+id/view_key_action_key_share"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight" android:layout_height="?android:attr/listPreferredItemHeight"
android:clickable="true"
android:background="?android:selectableItemBackground"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
android:id="@+id/view_key_action_key_share"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:textAppearance="?android:attr/textAppearanceMedium" android:textAppearance="?android:attr/textAppearanceMedium"
@ -116,7 +114,25 @@
android:layout_weight="1" android:layout_weight="1"
android:drawableRight="@drawable/ic_share_grey_24dp" android:drawableRight="@drawable/ic_share_grey_24dp"
android:drawablePadding="8dp" android:drawablePadding="8dp"
android:gravity="center_vertical" /> android:gravity="center_vertical"
android:background="?android:selectableItemBackground"/>
<View
android:layout_width="1dip"
android:layout_height="match_parent"
android:gravity="right"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:background="?android:attr/listDivider" />
<ImageButton
android:id="@+id/view_key_action_key_nfc"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="8dp"
android:src="@drawable/ic_nfc_grey_24dp"
android:layout_gravity="center_vertical"
android:background="?android:selectableItemBackground" />
<View <View
android:layout_width="1dip" android:layout_width="1dip"
@ -154,6 +170,7 @@
</LinearLayout> </LinearLayout>
<View <View
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dip" android:layout_height="1dip"

View File

@ -16,7 +16,7 @@
<item <item
android:id="@+id/menu_key_list_create" android:id="@+id/menu_key_list_create"
android:title="@string/menu_create_key" android:title="@string/menu_manage_keys"
app:showAsAction="never" /> app:showAsAction="never" />
<item <item

View File

@ -71,7 +71,7 @@
<string name="menu_help">Nápověda</string> <string name="menu_help">Nápověda</string>
<string name="menu_export_key">Exportovat do souboru</string> <string name="menu_export_key">Exportovat do souboru</string>
<string name="menu_delete_key">Smazat klíč</string> <string name="menu_delete_key">Smazat klíč</string>
<string name="menu_create_key">Vytvořit moje klíče</string> <string name="menu_manage_keys">Vytvořit moje klíče</string>
<string name="menu_import_existing_key">Importovat ze souboru</string> <string name="menu_import_existing_key">Importovat ze souboru</string>
<string name="menu_search">Hledat</string> <string name="menu_search">Hledat</string>
<string name="menu_beam_preferences">Beam settings</string> <string name="menu_beam_preferences">Beam settings</string>

View File

@ -88,7 +88,7 @@
<string name="menu_help">Hilfe</string> <string name="menu_help">Hilfe</string>
<string name="menu_export_key">In Datei exportieren</string> <string name="menu_export_key">In Datei exportieren</string>
<string name="menu_delete_key">Schlüssel löschen</string> <string name="menu_delete_key">Schlüssel löschen</string>
<string name="menu_create_key">Erzeuge meinen Schlüssel</string> <string name="menu_manage_keys">Erzeuge meinen Schlüssel</string>
<string name="menu_import_existing_key">Von Datei importieren</string> <string name="menu_import_existing_key">Von Datei importieren</string>
<string name="menu_search">Suchen</string> <string name="menu_search">Suchen</string>
<string name="menu_nfc_preferences">NFC-Einstellungen</string> <string name="menu_nfc_preferences">NFC-Einstellungen</string>

View File

@ -89,7 +89,7 @@
<string name="menu_help">Ayuda</string> <string name="menu_help">Ayuda</string>
<string name="menu_export_key">Exportar hacia archivo</string> <string name="menu_export_key">Exportar hacia archivo</string>
<string name="menu_delete_key">Borrar clave</string> <string name="menu_delete_key">Borrar clave</string>
<string name="menu_create_key">Crear mi clave</string> <string name="menu_manage_keys">Crear mi clave</string>
<string name="menu_import_existing_key">Importar desde fichero</string> <string name="menu_import_existing_key">Importar desde fichero</string>
<string name="menu_search">Buscar</string> <string name="menu_search">Buscar</string>
<string name="menu_nfc_preferences">Configuraciones NFC</string> <string name="menu_nfc_preferences">Configuraciones NFC</string>

View File

@ -76,7 +76,7 @@
<string name="menu_help">Laguntza</string> <string name="menu_help">Laguntza</string>
<string name="menu_export_key">Esportatu agirira</string> <string name="menu_export_key">Esportatu agirira</string>
<string name="menu_delete_key">Ezabatu giltza</string> <string name="menu_delete_key">Ezabatu giltza</string>
<string name="menu_create_key">Sortu nire giltza</string> <string name="menu_manage_keys">Sortu nire giltza</string>
<string name="menu_import_existing_key">inportatu agiritik</string> <string name="menu_import_existing_key">inportatu agiritik</string>
<string name="menu_search">Bilatu</string> <string name="menu_search">Bilatu</string>
<string name="menu_nfc_preferences">NFC ezarpenak</string> <string name="menu_nfc_preferences">NFC ezarpenak</string>

View File

@ -70,7 +70,7 @@
<string name="menu_help">Apua</string> <string name="menu_help">Apua</string>
<string name="menu_export_key">Vie tiedostoon</string> <string name="menu_export_key">Vie tiedostoon</string>
<string name="menu_delete_key">Poista avain</string> <string name="menu_delete_key">Poista avain</string>
<string name="menu_create_key">Luo minun avaimeni</string> <string name="menu_manage_keys">Luo minun avaimeni</string>
<string name="menu_import_existing_key">Tuo tiedostosta</string> <string name="menu_import_existing_key">Tuo tiedostosta</string>
<string name="menu_search">Etsi</string> <string name="menu_search">Etsi</string>
<string name="menu_beam_preferences">Beam asetukset</string> <string name="menu_beam_preferences">Beam asetukset</string>

View File

@ -89,7 +89,7 @@
<string name="menu_help">Aide</string> <string name="menu_help">Aide</string>
<string name="menu_export_key">Exporter vers un fichier</string> <string name="menu_export_key">Exporter vers un fichier</string>
<string name="menu_delete_key">Supprimer la clef</string> <string name="menu_delete_key">Supprimer la clef</string>
<string name="menu_create_key">Créer ma clef</string> <string name="menu_manage_keys">Créer ma clef</string>
<string name="menu_import_existing_key">Importer depuis un fichier</string> <string name="menu_import_existing_key">Importer depuis un fichier</string>
<string name="menu_search">Rechercher</string> <string name="menu_search">Rechercher</string>
<string name="menu_nfc_preferences">Paramètres NFC</string> <string name="menu_nfc_preferences">Paramètres NFC</string>

View File

@ -69,7 +69,7 @@
<string name="menu_help">Aiuto</string> <string name="menu_help">Aiuto</string>
<string name="menu_export_key">Esporta su un file</string> <string name="menu_export_key">Esporta su un file</string>
<string name="menu_delete_key">Cancella chiave</string> <string name="menu_delete_key">Cancella chiave</string>
<string name="menu_create_key">Crea mia chiave</string> <string name="menu_manage_keys">Crea mia chiave</string>
<string name="menu_import_existing_key">Importa da file</string> <string name="menu_import_existing_key">Importa da file</string>
<string name="menu_search">Cerca</string> <string name="menu_search">Cerca</string>
<string name="menu_beam_preferences">Impostazioni Beam</string> <string name="menu_beam_preferences">Impostazioni Beam</string>

View File

@ -89,7 +89,7 @@
<string name="menu_help">ヘルプ</string> <string name="menu_help">ヘルプ</string>
<string name="menu_export_key">ファイルへのエクスポート</string> <string name="menu_export_key">ファイルへのエクスポート</string>
<string name="menu_delete_key">鍵の削除</string> <string name="menu_delete_key">鍵の削除</string>
<string name="menu_create_key">自分の鍵の生成</string> <string name="menu_manage_keys">自分の鍵の生成</string>
<string name="menu_import_existing_key">ファイルからインポート</string> <string name="menu_import_existing_key">ファイルからインポート</string>
<string name="menu_search">検索</string> <string name="menu_search">検索</string>
<string name="menu_nfc_preferences">NFC設定</string> <string name="menu_nfc_preferences">NFC設定</string>

View File

@ -96,7 +96,7 @@
<string name="menu_help">Help</string> <string name="menu_help">Help</string>
<string name="menu_export_key">Exporteren naar bestand</string> <string name="menu_export_key">Exporteren naar bestand</string>
<string name="menu_delete_key">Sleutel verwijderen</string> <string name="menu_delete_key">Sleutel verwijderen</string>
<string name="menu_create_key">Mijn sleutel aanmaken</string> <string name="menu_manage_keys">Mijn sleutel aanmaken</string>
<string name="menu_import_existing_key">Importeren van bestand</string> <string name="menu_import_existing_key">Importeren van bestand</string>
<string name="menu_search">Zoeken</string> <string name="menu_search">Zoeken</string>
<string name="menu_nfc_preferences">NFC-instellingen</string> <string name="menu_nfc_preferences">NFC-instellingen</string>

View File

@ -78,7 +78,7 @@
<string name="menu_help">Pomoc</string> <string name="menu_help">Pomoc</string>
<string name="menu_export_key">Eksportuj do pliku</string> <string name="menu_export_key">Eksportuj do pliku</string>
<string name="menu_delete_key">Usuń klucz</string> <string name="menu_delete_key">Usuń klucz</string>
<string name="menu_create_key">Utwórz mój klucz</string> <string name="menu_manage_keys">Utwórz mój klucz</string>
<string name="menu_import_existing_key">Importuj z pliku</string> <string name="menu_import_existing_key">Importuj z pliku</string>
<string name="menu_search">Szukaj</string> <string name="menu_search">Szukaj</string>
<string name="menu_beam_preferences">Ustawienia Beam</string> <string name="menu_beam_preferences">Ustawienia Beam</string>

View File

@ -85,7 +85,7 @@
<string name="menu_help">Помощь</string> <string name="menu_help">Помощь</string>
<string name="menu_export_key">Экспорт в файл</string> <string name="menu_export_key">Экспорт в файл</string>
<string name="menu_delete_key">Удалить ключ</string> <string name="menu_delete_key">Удалить ключ</string>
<string name="menu_create_key">Создать свой ключ</string> <string name="menu_manage_keys">Создать свой ключ</string>
<string name="menu_import_existing_key">Импорт из файла</string> <string name="menu_import_existing_key">Импорт из файла</string>
<string name="menu_search">Поиск</string> <string name="menu_search">Поиск</string>
<string name="menu_nfc_preferences">Настройки NFC</string> <string name="menu_nfc_preferences">Настройки NFC</string>

View File

@ -75,7 +75,7 @@
<string name="menu_help">Pomoč</string> <string name="menu_help">Pomoč</string>
<string name="menu_export_key">Izvozi v datoteko</string> <string name="menu_export_key">Izvozi v datoteko</string>
<string name="menu_delete_key">Izbriši ključ</string> <string name="menu_delete_key">Izbriši ključ</string>
<string name="menu_create_key">Ustvari zasebni ključ</string> <string name="menu_manage_keys">Ustvari zasebni ključ</string>
<string name="menu_import_existing_key">Uvozi iz datoteke</string> <string name="menu_import_existing_key">Uvozi iz datoteke</string>
<string name="menu_search">Išči</string> <string name="menu_search">Išči</string>
<string name="menu_beam_preferences">Nastavitve Beam</string> <string name="menu_beam_preferences">Nastavitve Beam</string>

View File

@ -89,7 +89,7 @@
<string name="menu_help">Помоћ</string> <string name="menu_help">Помоћ</string>
<string name="menu_export_key">Извези у фајл</string> <string name="menu_export_key">Извези у фајл</string>
<string name="menu_delete_key">Обриши кључ</string> <string name="menu_delete_key">Обриши кључ</string>
<string name="menu_create_key">Направи ми кључ</string> <string name="menu_manage_keys">Направи ми кључ</string>
<string name="menu_import_existing_key">Увези из фајла</string> <string name="menu_import_existing_key">Увези из фајла</string>
<string name="menu_search">Претрага</string> <string name="menu_search">Претрага</string>
<string name="menu_nfc_preferences">НФЦ поставке</string> <string name="menu_nfc_preferences">НФЦ поставке</string>

View File

@ -78,7 +78,7 @@
<string name="menu_help">Hjälp</string> <string name="menu_help">Hjälp</string>
<string name="menu_export_key">Exportera till fil</string> <string name="menu_export_key">Exportera till fil</string>
<string name="menu_delete_key">Radera nyckel</string> <string name="menu_delete_key">Radera nyckel</string>
<string name="menu_create_key">Skapa min nyckel</string> <string name="menu_manage_keys">Skapa min nyckel</string>
<string name="menu_import_existing_key">Importera från fil</string> <string name="menu_import_existing_key">Importera från fil</string>
<string name="menu_search">Sök</string> <string name="menu_search">Sök</string>
<string name="menu_beam_preferences">Beam-inställningar</string> <string name="menu_beam_preferences">Beam-inställningar</string>

View File

@ -69,7 +69,7 @@
<string name="menu_help">Yardım</string> <string name="menu_help">Yardım</string>
<string name="menu_export_key">Dosyaya ver</string> <string name="menu_export_key">Dosyaya ver</string>
<string name="menu_delete_key">Anahtar sil</string> <string name="menu_delete_key">Anahtar sil</string>
<string name="menu_create_key">Anahtarımı oluştur</string> <string name="menu_manage_keys">Anahtarımı oluştur</string>
<string name="menu_import_existing_key">Dosyadan al</string> <string name="menu_import_existing_key">Dosyadan al</string>
<string name="menu_search">Ara</string> <string name="menu_search">Ara</string>
<string name="menu_beam_preferences">NFC ayarları</string> <string name="menu_beam_preferences">NFC ayarları</string>

View File

@ -69,7 +69,7 @@
<string name="menu_help">Довідка</string> <string name="menu_help">Довідка</string>
<string name="menu_export_key">Експорт до файлу</string> <string name="menu_export_key">Експорт до файлу</string>
<string name="menu_delete_key">Вилучити ключ</string> <string name="menu_delete_key">Вилучити ключ</string>
<string name="menu_create_key">Створити мій ключ</string> <string name="menu_manage_keys">Створити мій ключ</string>
<string name="menu_import_existing_key">Імпорт з файлу</string> <string name="menu_import_existing_key">Імпорт з файлу</string>
<string name="menu_search">Пошук</string> <string name="menu_search">Пошук</string>
<string name="menu_beam_preferences">Налаштування променя</string> <string name="menu_beam_preferences">Налаштування променя</string>

View File

@ -65,7 +65,7 @@
<string name="menu_help">說明</string> <string name="menu_help">說明</string>
<string name="menu_export_key">匯出到檔案</string> <string name="menu_export_key">匯出到檔案</string>
<string name="menu_delete_key">刪除金鑰</string> <string name="menu_delete_key">刪除金鑰</string>
<string name="menu_create_key">建立金鑰</string> <string name="menu_manage_keys">建立金鑰</string>
<string name="menu_import_existing_key">從檔案匯入</string> <string name="menu_import_existing_key">從檔案匯入</string>
<string name="menu_search">搜尋</string> <string name="menu_search">搜尋</string>
<string name="menu_beam_preferences">Beam 設定</string> <string name="menu_beam_preferences">Beam 設定</string>

View File

@ -57,7 +57,7 @@
<string name="menu_help">帮助</string> <string name="menu_help">帮助</string>
<string name="menu_export_key">导出密钥</string> <string name="menu_export_key">导出密钥</string>
<string name="menu_delete_key">删除密钥</string> <string name="menu_delete_key">删除密钥</string>
<string name="menu_create_key">创建密钥</string> <string name="menu_manage_keys">创建密钥</string>
<string name="menu_search">搜索</string> <string name="menu_search">搜索</string>
<string name="menu_beam_preferences">参数</string> <string name="menu_beam_preferences">参数</string>
<string name="menu_key_edit_cancel">取消</string> <string name="menu_key_edit_cancel">取消</string>

View File

@ -44,6 +44,7 @@
<string name="title_keys">"Keys"</string> <string name="title_keys">"Keys"</string>
<string name="title_delete_secret_key">"Delete YOUR key '%s'?"</string> <string name="title_delete_secret_key">"Delete YOUR key '%s'?"</string>
<string name="title_export_log">"Export Log"</string> <string name="title_export_log">"Export Log"</string>
<string name="title_manage_my_keys">"Manage my keys"</string>
<!-- section --> <!-- section -->
<string name="section_user_ids">"Identities"</string> <string name="section_user_ids">"Identities"</string>
@ -106,7 +107,7 @@
<string name="menu_help">"Help"</string> <string name="menu_help">"Help"</string>
<string name="menu_export_key">"Export to file"</string> <string name="menu_export_key">"Export to file"</string>
<string name="menu_delete_key">"Delete key"</string> <string name="menu_delete_key">"Delete key"</string>
<string name="menu_create_key">"Create my key"</string> <string name="menu_manage_keys">"Manage my keys"</string>
<string name="menu_import_existing_key">"Import from file"</string> <string name="menu_import_existing_key">"Import from file"</string>
<string name="menu_search">"Search"</string> <string name="menu_search">"Search"</string>
<string name="menu_nfc_preferences">"NFC settings"</string> <string name="menu_nfc_preferences">"NFC settings"</string>
@ -1198,8 +1199,9 @@
<!-- First Time --> <!-- First Time -->
<string name="first_time_text1">"Take back your privacy with OpenKeychain!"</string> <string name="first_time_text1">"Take back your privacy with OpenKeychain!"</string>
<string name="first_time_create_key">"Create my key"</string> <string name="first_time_create_key">"Create my key (recommended)"</string>
<string name="first_time_import_key">"Import from file"</string> <string name="first_time_import_key">"Import key from file"</string>
<string name="first_time_yubikey">"Use YubiKey NEO"</string>
<string name="first_time_skip">"Skip Setup"</string> <string name="first_time_skip">"Skip Setup"</string>
<!-- unsorted --> <!-- unsorted -->