Merge branch 'development' into linked-identities

Conflicts:
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
This commit is contained in:
Vincent Breitmoser 2015-03-24 15:29:02 +01:00
commit f9ef1160ca
136 changed files with 1910 additions and 1282 deletions

3
.gitmodules vendored
View File

@ -34,3 +34,6 @@
path = extern/safeslinger-exchange
url = https://github.com/open-keychain/exchange-android
ignore = dirty
[submodule "extern/snackbar"]
path = extern/snackbar
url = https://github.com/open-keychain/snackbar

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

View File

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

View File

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

View File

@ -0,0 +1,53 @@
/*
* 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.pgp;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19
public class KeyRingTest {
@Test
public void splitCompleteUserIdShouldReturnAll3Components() throws Exception {
KeyRing.UserId info = KeyRing.splitUserId("Max Mustermann (this is a comment) <max@example.com>");
Assert.assertEquals("Max Mustermann", info.name);
Assert.assertEquals("this is a comment", info.comment);
Assert.assertEquals("max@example.com", info.email);
}
@Test
public void splitUserIdWithAllButCommentShouldReturnNameAndEmail() throws Exception {
KeyRing.UserId info = KeyRing.splitUserId("Max Mustermann <max@example.com>");
Assert.assertEquals("Max Mustermann", info.name);
Assert.assertNull(info.comment);
Assert.assertEquals("max@example.com", info.email);
}
@Test
public void splitUserIdWithAllButEmailShouldReturnNameAndComment() throws Exception {
KeyRing.UserId info = KeyRing.splitUserId("Max Mustermann (this is a comment)");
Assert.assertEquals(info.name, "Max Mustermann");
Assert.assertEquals(info.comment, "this is a comment");
Assert.assertNull(info.email);
}
}

View File

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

View File

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

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

View File

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

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

View File

@ -1,13 +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;
import java.util.Random;
public class TestingUtils {
public static String genPassphrase() {
public static Passphrase genPassphrase() {
return genPassphrase(false);
}
public static String genPassphrase(boolean noEmpty) {
public static Passphrase genPassphrase(boolean noEmpty) {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789!@#$%^&*()-_=";
Random r = new Random();
StringBuilder passbuilder = new StringBuilder();
@ -16,6 +33,6 @@ public class TestingUtils {
passbuilder.append(chars.charAt(r.nextInt(chars.length())));
}
System.out.println("Generated passphrase: '" + passbuilder.toString() + "'");
return passbuilder.toString();
return new Passphrase(passbuilder.toString());
}
}

View File

@ -18,7 +18,6 @@ dependencies {
compile 'com.google.zxing:core:3.0.1'
compile 'com.jpardogo.materialtabstrip:library:1.0.9'
compile 'it.neokree:MaterialNavigationDrawer:1.3.1'
compile 'com.nispok:snackbar:2.9.1'
compile 'com.getbase:floatingactionbutton:1.8.0'
compile 'com.ocpsoft:ocpsoft-pretty-time:1.0.6'
@ -35,6 +34,7 @@ dependencies {
compile project(':extern:KeybaseLib:Lib')
compile project(':extern:TokenAutoComplete:library')
compile project(':extern:safeslinger-exchange')
compile project(':extern:snackbar:lib')
}
// Output of ./gradlew -q calculateChecksums
@ -51,7 +51,7 @@ dependencyVerification {
'com.google.zxing:core:38c49045765281e4c170062fa3f48e4e988629bf985cab850c7497be5eaa72a1',
'com.jpardogo.materialtabstrip:library:c6ef812fba4f74be7dc4a905faa4c2908cba261a94c13d4f96d5e67e4aad4aaa',
'it.neokree:MaterialNavigationDrawer:1174d751a54689fccf53c1fbcdf439745926ae19024f4f1017afb6b29643c57d',
'com.nispok:snackbar:59dc092a44c877e9ce5f9040c632d99e62d8932b0a4d67ba0ec9e35467d9047c',
// 'com.nispok:snackbar:59dc092a44c877e9ce5f9040c632d99e62d8932b0a4d67ba0ec9e35467d9047c',
'com.getbase:floatingactionbutton:e63966148212e9685afad2370780ea239b6dbd2a06f6a3f919b98882318e6a32',
'com.android.support:support-annotations:fdee2354787ef66b268e75958de3f7f6c4f8f325510a6dac9f49c929f83a63de',
'com.balysv:material-ripple:587f19c1e27f16c7dc67ff9ac73838aa1451086ef05a15cee38bee3e4e1454ae',

View File

@ -86,16 +86,10 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.FirstTimeActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/app_name"
android:windowSoftInputMode="stateAlwaysHidden" />
<activity
android:name=".ui.CreateKeyActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:windowSoftInputMode="adjustResize"
android:label="@string/title_create_key"
android:label="@string/title_manage_my_keys"
android:parentActivityName=".ui.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@ -418,7 +412,7 @@
<activity
android:name=".ui.ViewKeyAdvActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_advanced_key_info"/>
android:label="@string/title_advanced_key_info" />
<activity
android:name=".ui.SettingsActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
@ -660,6 +654,10 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.MainActivity" />
</activity>
<activity
android:name=".ui.LogDisplayActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_log_display" />
<activity
android:name=".ui.ConsolidateDialogActivity"
android:theme="@android:style/Theme.NoDisplay" />
@ -714,14 +712,12 @@
android:name=".remote.ui.RemoteServiceActivity"
android:exported="false"
android:label="@string/app_name"
android:launchMode="singleTop"
android:process=":remote_api" />
android:launchMode="singleTop" />
<activity
android:name=".remote.ui.SelectSignKeyIdActivity"
android:exported="false"
android:label="@string/app_name"
android:launchMode="singleTop"
android:process=":remote_api" />
android:launchMode="singleTop" />
<activity
android:name=".remote.ui.AppSettingsActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
@ -749,13 +745,10 @@
</intent-filter>
</service>
<activity
android:name=".ui.LogDisplayActivity"
android:configChanges="orientation|screenSize|keyboardHidden|keyboard"
android:label="@string/title_log_display"
android:exported="false" />
<service android:name=".service.DummyAccountService">
<!-- Contact Sync services -->
<service
android:name=".service.DummyAccountService"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
@ -767,7 +760,8 @@
<service
android:name=".service.ContactSyncAdapterService"
android:exported="true"
android:process=":sync">
android:process=":sync"
tools:ignore="ExportedService">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
@ -780,6 +774,7 @@
android:resource="@xml/custom_pgp_contacts_structure" />
</service>
<!-- Storage Provider for temporary decrypted files -->
<provider
android:name=".provider.TemporaryStorageProvider"
android:authorities="org.sufficientlysecure.keychain.tempstorage"

View File

@ -42,6 +42,15 @@ public final class Constants {
// as defined in http://tools.ietf.org/html/rfc3156, section 7
public static final String NFC_MIME = "application/pgp-keys";
// as defined in http://tools.ietf.org/html/rfc3156
// we don't use application/pgp-encrypted as it only holds the version number
public static final String ENCRYPTED_FILES_MIME = "application/octet-stream";
public static final String ENCRYPTED_TEXT_MIME = "text/plain";
public static final String FILE_EXTENSION_PGP_MAIN = ".gpg";
public static final String FILE_EXTENSION_PGP_ALTERNATE = ".pgp";
public static final String FILE_EXTENSION_ASC = ".asc";
// used by QR Codes (Guardian Project, Monkeysphere compatiblity)
public static final String FINGERPRINT_SCHEME = "openpgp4fpr";

View File

@ -118,24 +118,21 @@ public class KeychainApplication extends Application {
* @param context
*/
public static void setupAccountAsNeeded(Context context) {
// only enabled for Jelly Bean because we need some newer methods in our sync adapter
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
try {
AccountManager manager = AccountManager.get(context);
Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE);
if (accounts == null || accounts.length == 0) {
Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
if (manager.addAccountExplicitly(account, null, null)) {
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
} else {
Log.e(Constants.TAG, "Adding account failed!");
}
try {
AccountManager manager = AccountManager.get(context);
Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE);
if (accounts == null || accounts.length == 0) {
Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
if (manager.addAccountExplicitly(account, null, null)) {
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
} else {
Log.e(Constants.TAG, "Adding account failed!");
}
} catch (SecurityException e) {
Log.e(Constants.TAG, "SecurityException when adding the account", e);
Toast.makeText(context, R.string.reinstall_openkeychain, Toast.LENGTH_LONG).show();
}
} catch (SecurityException e) {
Log.e(Constants.TAG, "SecurityException when adding the account", e);
Toast.makeText(context, R.string.reinstall_openkeychain, Toast.LENGTH_LONG).show();
}
}

View File

@ -70,6 +70,8 @@ public class ImportKeysList extends ArrayList<ImportKeysListEntry> {
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
// all the origins
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
if (KeybaseKeyserver.ORIGIN.equals(origin)) {
existing.setExtraData(incoming.getExtraData());
// one of the origins is not a HKP keyserver
incomingFromHkpServer = false;
}
}
ArrayList<String> incomingIDs = incoming.getUserIds();
ArrayList<String> existingIDs = existing.getUserIds();
for (String incomingID : incomingIDs) {
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;
}
}

View File

@ -307,24 +307,22 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
public void updateMergedUserIds() {
mMergedUserIds = new HashMap<>();
for (String userId : mUserIds) {
String[] userIdSplit = KeyRing.splitUserId(userId);
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
// TODO: comment field?
// name
if (userIdSplit[0] != null) {
// email
if (userIdSplit[1] != null) {
if (!mMergedUserIds.containsKey(userIdSplit[0])) {
if (userIdSplit.name != null) {
if (userIdSplit.email != null) {
if (!mMergedUserIds.containsKey(userIdSplit.name)) {
HashSet<String> emails = new HashSet<>();
emails.add(userIdSplit[1]);
mMergedUserIds.put(userIdSplit[0], emails);
emails.add(userIdSplit.email);
mMergedUserIds.put(userIdSplit.name, emails);
} else {
mMergedUserIds.get(userIdSplit[0]).add(userIdSplit[1]);
mMergedUserIds.get(userIdSplit.name).add(userIdSplit.email);
}
} else {
// name only
mMergedUserIds.put(userIdSplit[0], new HashSet<String>());
mMergedUserIds.put(userIdSplit.name, new HashSet<String>());
}
} else {
// fallback

View File

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

View File

@ -40,6 +40,7 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyActio
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
@ -79,7 +80,7 @@ public class CertifyOperation extends BaseOperation {
}
// certification is always with the master key id, so use that one
String passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId);
Passphrase passphrase = getCachedPassphrase(parcel.mMasterKeyId, parcel.mMasterKeyId);
if (!certificationKey.unlock(passphrase)) {
log.add(LogType.MSG_CRT_ERROR_UNLOCK, 2);

View File

@ -35,6 +35,7 @@ import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import java.util.concurrent.atomic.AtomicBoolean;
@ -55,7 +56,7 @@ public class EditKeyOperation extends BaseOperation {
super(context, providerHelper, progressable, cancelled);
}
public EditKeyResult execute(SaveKeyringParcel saveParcel, String passphrase) {
public EditKeyResult execute(SaveKeyringParcel saveParcel, Passphrase passphrase) {
OperationLog log = new OperationLog();
log.add(LogType.MSG_ED, 0);

View File

@ -120,7 +120,7 @@ public class CertifyResult extends OperationResult {
mCertifyError, mCertifyError);
}
return Notify.createNotify(activity, str, duration, style, new ActionListener() {
return Notify.create(activity, str, duration, style, new ActionListener() {
@Override
public void onAction() {
Intent intent = new Intent(

View File

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

View File

@ -116,7 +116,7 @@ public class DeleteResult extends OperationResult {
}
}
return Notify.createNotify(activity, str, duration, style, new ActionListener() {
return Notify.create(activity, str, duration, style, new ActionListener() {
@Override
public void onAction() {
Intent intent = new Intent(

View File

@ -179,7 +179,7 @@ public class ImportKeyResult extends OperationResult {
}
}
return Notify.createNotify(activity, str, duration, style, new ActionListener() {
return Notify.create(activity, str, duration, style, new ActionListener() {
@Override
public void onAction() {
Intent intent = new Intent(

View File

@ -273,19 +273,19 @@ public abstract class OperationResult implements Parcelable {
}
if (getLog() == null || getLog().isEmpty()) {
return Notify.createNotify(activity, msgId, Notify.LENGTH_LONG, style);
return Notify.create(activity, msgId, Notify.LENGTH_LONG, style);
}
return Notify.createNotify(activity, msgId, Notify.LENGTH_LONG, style,
new ActionListener() {
@Override
public void onAction() {
Intent intent = new Intent(
activity, LogDisplayActivity.class);
intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this);
activity.startActivity(intent);
}
}, R.string.view_log);
return Notify.create(activity, msgId, Notify.LENGTH_LONG, style,
new ActionListener() {
@Override
public void onAction() {
Intent intent = new Intent(
activity, LogDisplayActivity.class);
intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this);
activity.startActivity(intent);
}
}, R.string.view_log);
}

View File

@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.operations.results;
import android.os.Parcel;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.Date;
public class PgpSignEncryptResult extends OperationResult {
@ -36,7 +38,7 @@ public class PgpSignEncryptResult extends OperationResult {
byte[] mNfcHash;
int mNfcAlgo;
Date mNfcTimestamp;
String mNfcPassphrase;
Passphrase mNfcPassphrase;
byte[] mDetachedSignature;
public long getKeyIdPassphraseNeeded() {
@ -47,7 +49,7 @@ public class PgpSignEncryptResult extends OperationResult {
mKeyIdPassphraseNeeded = keyIdPassphraseNeeded;
}
public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, String passphrase) {
public void setNfcData(long nfcKeyId, byte[] nfcHash, int nfcAlgo, Date nfcTimestamp, Passphrase passphrase) {
mNfcKeyId = nfcKeyId;
mNfcHash = nfcHash;
mNfcAlgo = nfcAlgo;
@ -75,7 +77,7 @@ public class PgpSignEncryptResult extends OperationResult {
return mNfcTimestamp;
}
public String getNfcPassphrase() {
public Passphrase getNfcPassphrase() {
return mNfcPassphrase;
}

View File

@ -55,8 +55,8 @@ public class CanonicalizedPublicKey extends UncachedPublicKey {
return new IterableIterator<String>(mPublicKey.getUserIDs());
}
JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() {
return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey);
JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator(boolean hiddenRecipients) {
return new JcePublicKeyKeyEncryptionMethodGenerator(mPublicKey, hiddenRecipients);
}
public boolean canSign() {

View File

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

View File

@ -44,7 +44,7 @@ public abstract class KeyRing {
abstract public String getPrimaryUserIdWithFallback() throws PgpKeyNotFoundException;
public String[] getSplitPrimaryUserIdWithFallback() throws PgpKeyNotFoundException {
public UserId getSplitPrimaryUserIdWithFallback() throws PgpKeyNotFoundException {
return splitUserId(getPrimaryUserIdWithFallback());
}
@ -62,55 +62,45 @@ public abstract class KeyRing {
/**
* Splits userId string into naming part, email part, and comment part
*
* @param userId
* @return array with naming (0), email (1), comment (2)
* <p/>
* User ID matching:
* http://fiddle.re/t4p6f
*/
public static String[] splitUserId(String userId) {
String[] result = new String[]{null, null, null};
if (userId == null || userId.equals("")) {
return result;
public static UserId splitUserId(final String userId) {
if (!TextUtils.isEmpty(userId)) {
final Matcher matcher = USER_ID_PATTERN.matcher(userId);
if (matcher.matches()) {
return new UserId(matcher.group(1), matcher.group(3), matcher.group(2));
}
}
/*
* User ID matching:
* http://fiddle.re/t4p6f
*
* test cases:
* "Max Mustermann (this is a comment) <max@example.com>"
* "Max Mustermann <max@example.com>"
* "Max Mustermann (this is a comment)"
* "Max Mustermann [this is nothing]"
*/
Matcher matcher = USER_ID_PATTERN.matcher(userId);
if (matcher.matches()) {
result[0] = matcher.group(1);
result[1] = matcher.group(3);
result[2] = matcher.group(2);
}
return result;
return new UserId(null, null, null);
}
/**
* Returns a composed user id. Returns null if name is null!
*
* @param name
* @param email
* @param comment
* @return
*/
public static String createUserId(String name, String email, String comment) {
String userId = name; // consider name a required value
if (userId != null && !TextUtils.isEmpty(comment)) {
userId += " (" + comment + ")";
public static String createUserId(UserId userId) {
String userIdString = userId.name; // consider name a required value
if (userIdString != null && !TextUtils.isEmpty(userId.comment)) {
userIdString += " (" + userId.comment + ")";
}
if (userId != null && !TextUtils.isEmpty(email)) {
userId += " <" + email + ">";
if (userIdString != null && !TextUtils.isEmpty(userId.email)) {
userIdString += " <" + userId.email + ">";
}
return userId;
return userIdString;
}
public static class UserId {
public final String name;
public final String email;
public final String comment;
public UserId(String name, String email, String comment) {
this.name = name;
this.email = email;
this.comment = comment;
}
}
}

View File

@ -1,13 +1,33 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* 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.pgp;
import org.sufficientlysecure.keychain.util.Passphrase;
public interface PassphraseCacheInterface {
public static class NoSecretKeyException extends Exception {
public NoSecretKeyException() {
}
}
public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException;
public Passphrase getCachedPassphrase(long subKeyId) throws NoSecretKeyException;
public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException;
public Passphrase getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException;
}

View File

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

View File

@ -57,6 +57,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Primes;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@ -316,7 +317,7 @@ public class PgpKeyOperation {
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
subProgressPush(50, 100);
return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, "", log);
return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, new Passphrase(), log);
} catch (PGPException e) {
log.add(LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
@ -348,7 +349,7 @@ public class PgpKeyOperation {
*
*/
public PgpEditKeyResult modifySecretKeyRing(CanonicalizedSecretKeyRing wsKR, SaveKeyringParcel saveParcel,
String passphrase) {
Passphrase passphrase) {
OperationLog log = new OperationLog();
int indent = 0;
@ -404,7 +405,7 @@ public class PgpKeyOperation {
private PgpEditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
int masterKeyFlags, long masterKeyExpiry,
SaveKeyringParcel saveParcel, String passphrase,
SaveKeyringParcel saveParcel, Passphrase passphrase,
OperationLog log) {
int indent = 1;
@ -420,7 +421,7 @@ public class PgpKeyOperation {
{
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
masterPrivateKey = masterSecretKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
log.add(LogType.MSG_MF_UNLOCK_ERROR, indent + 1);
@ -839,7 +840,7 @@ public class PgpKeyOperation {
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
.build().get(PgpConstants.SECRET_KEY_SIGNATURE_CHECKSUM_HASH_ALGO);
@ -967,7 +968,7 @@ public class PgpKeyOperation {
PGPSecretKeyRing sKR,
PGPPublicKey masterPublicKey,
PGPPrivateKey masterPrivateKey,
String passphrase,
Passphrase passphrase,
ChangeUnlockParcel newUnlock,
OperationLog log, int indent) throws PGPException {
@ -1051,20 +1052,20 @@ public class PgpKeyOperation {
private static PGPSecretKeyRing applyNewPassphrase(
PGPSecretKeyRing sKR,
PGPPublicKey masterPublicKey,
String passphrase,
String newPassphrase,
Passphrase passphrase,
Passphrase newPassphrase,
OperationLog log, int indent) throws PGPException {
PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
.get(PgpConstants.SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.getCharArray());
// Build key encryptor based on new passphrase
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
PgpConstants.SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc,
PgpConstants.SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
newPassphrase.toCharArray());
newPassphrase.getCharArray());
// noinspection unchecked
for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
@ -1295,11 +1296,11 @@ public class PgpKeyOperation {
private static PGPSignature generateSubkeyBindingSignature(
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, String passphrase)
PGPSecretKey sKey, PGPPublicKey pKey, int flags, long expiry, Passphrase passphrase)
throws IOException, PGPException, SignatureException {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
passphrase.toCharArray());
passphrase.getCharArray());
PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
return generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey, subPrivateKey,
pKey, flags, expiry);

View File

@ -1,7 +1,26 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* 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.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.Date;
@ -11,19 +30,20 @@ public class PgpSignEncryptInput {
protected boolean mEnableAsciiArmorOutput = false;
protected int mCompressionId = CompressionAlgorithmTags.UNCOMPRESSED;
protected long[] mEncryptionMasterKeyIds = null;
protected String mSymmetricPassphrase = null;
protected Passphrase mSymmetricPassphrase = null;
protected int mSymmetricEncryptionAlgorithm = PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED;
protected long mSignatureMasterKeyId = Constants.key.none;
protected Long mSignatureSubKeyId = null;
protected int mSignatureHashAlgorithm = PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED;
protected String mSignaturePassphrase = null;
protected Passphrase mSignaturePassphrase = null;
protected long mAdditionalEncryptId = Constants.key.none;
protected byte[] mNfcSignedHash = null;
protected Date mNfcCreationTimestamp = null;
protected boolean mFailOnMissingEncryptionKeyIds = false;
protected String mCharset;
protected boolean mCleartextSignature;
protected boolean mDetachedSignature;
protected boolean mDetachedSignature = false;
protected boolean mHiddenRecipients = false;
public String getCharset() {
return mCharset;
@ -33,7 +53,7 @@ public class PgpSignEncryptInput {
this.mCharset = mCharset;
}
public boolean ismFailOnMissingEncryptionKeyIds() {
public boolean isFailOnMissingEncryptionKeyIds() {
return mFailOnMissingEncryptionKeyIds;
}
@ -54,11 +74,11 @@ public class PgpSignEncryptInput {
return this;
}
public String getSignaturePassphrase() {
public Passphrase getSignaturePassphrase() {
return mSignaturePassphrase;
}
public PgpSignEncryptInput setSignaturePassphrase(String signaturePassphrase) {
public PgpSignEncryptInput setSignaturePassphrase(Passphrase signaturePassphrase) {
mSignaturePassphrase = signaturePassphrase;
return this;
}
@ -99,11 +119,11 @@ public class PgpSignEncryptInput {
return this;
}
public String getSymmetricPassphrase() {
public Passphrase getSymmetricPassphrase() {
return mSymmetricPassphrase;
}
public PgpSignEncryptInput setSymmetricPassphrase(String symmetricPassphrase) {
public PgpSignEncryptInput setSymmetricPassphrase(Passphrase symmetricPassphrase) {
mSymmetricPassphrase = symmetricPassphrase;
return this;
}
@ -126,7 +146,7 @@ public class PgpSignEncryptInput {
return this;
}
public boolean ismEnableAsciiArmorOutput() {
public boolean isEnableAsciiArmorOutput() {
return mEnableAsciiArmorOutput;
}
@ -172,5 +192,14 @@ public class PgpSignEncryptInput {
public boolean isDetachedSignature() {
return mDetachedSignature;
}
public PgpSignEncryptInput setHiddenRecipients(boolean hiddenRecipients) {
this.mHiddenRecipients = hiddenRecipients;
return this;
}
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2012-2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
*
@ -60,7 +60,6 @@ import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean;
/** This class supports a single, low-level, sign/encrypt operation.
@ -117,7 +116,8 @@ public class PgpSignEncryptOperation extends BaseOperation {
Log.d(Constants.TAG, "enableSignature:" + enableSignature
+ "\nenableEncryption:" + enableEncryption
+ "\nenableCompression:" + enableCompression
+ "\nenableAsciiArmorOutput:" + input.ismEnableAsciiArmorOutput());
+ "\nenableAsciiArmorOutput:" + input.isEnableAsciiArmorOutput()
+ "\nisHiddenRecipients:" + input.isHiddenRecipients());
// add additional key id to encryption ids (mostly to do self-encryption)
if (enableEncryption && input.getAdditionalEncryptId() != Constants.key.none) {
@ -127,7 +127,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
ArmoredOutputStream armorOut = null;
OutputStream out;
if (input.ismEnableAsciiArmorOutput()) {
if (input.isEnableAsciiArmorOutput()) {
armorOut = new ArmoredOutputStream(outputStream);
if (input.getVersionHeader() != null) {
armorOut.setHeader("Version", input.getVersionHeader());
@ -243,7 +243,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
log.add(LogType.MSG_PSE_SYMMETRIC, indent);
JcePBEKeyEncryptionMethodGenerator symmetricEncryptionGenerator =
new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().toCharArray());
new JcePBEKeyEncryptionMethodGenerator(input.getSymmetricPassphrase().getCharArray());
cPk.addMethod(symmetricEncryptionGenerator);
} else {
log.add(LogType.MSG_PSE_ASYMMETRIC, indent);
@ -254,19 +254,19 @@ public class PgpSignEncryptOperation extends BaseOperation {
CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(id));
CanonicalizedPublicKey key = keyRing.getEncryptionSubKey();
cPk.addMethod(key.getPubKeyEncryptionGenerator());
cPk.addMethod(key.getPubKeyEncryptionGenerator(input.isHiddenRecipients()));
log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
} catch (PgpKeyNotFoundException e) {
log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
if (input.ismFailOnMissingEncryptionKeyIds()) {
if (input.isFailOnMissingEncryptionKeyIds()) {
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
} catch (ProviderHelper.NotFoundException e) {
log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1,
KeyFormattingUtils.convertKeyIdToHex(id));
if (input.ismFailOnMissingEncryptionKeyIds()) {
if (input.isFailOnMissingEncryptionKeyIds()) {
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
}
}
@ -280,7 +280,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
updateProgress(R.string.progress_preparing_signature, 4, 100);
try {
boolean cleartext = input.isCleartextSignature() && input.ismEnableAsciiArmorOutput() && !enableEncryption;
boolean cleartext = input.isCleartextSignature() && input.isEnableAsciiArmorOutput() && !enableEncryption;
signatureGenerator = signingKey.getSignatureGenerator(
input.getSignatureHashAlgorithm(), cleartext, input.getNfcSignedHash(), input.getNfcCreationTimestamp());
} catch (PgpGeneralException e) {
@ -358,7 +358,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
literalGen.close();
indent -= 1;
} else if (enableSignature && input.isCleartextSignature() && input.ismEnableAsciiArmorOutput()) {
} else if (enableSignature && input.isCleartextSignature() && input.isEnableAsciiArmorOutput()) {
/* cleartext signature: sign-only of ascii text */
updateProgress(R.string.progress_signing, 8, 100);
@ -404,7 +404,7 @@ public class PgpSignEncryptOperation extends BaseOperation {
// handle output stream separately for detached signatures
detachedByteOut = new ByteArrayOutputStream();
OutputStream detachedOut = detachedByteOut;
if (input.ismEnableAsciiArmorOutput()) {
if (input.isEnableAsciiArmorOutput()) {
detachedArmorOut = new ArmoredOutputStream(detachedOut);
if (input.getVersionHeader() != null) {
detachedArmorOut.setHeader("Version", input.getVersionHeader());

View File

@ -1,9 +1,29 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* 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.pgp;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -39,12 +59,12 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
mEnableAsciiArmorOutput = src.readInt() == 1;
mCompressionId = src.readInt();
mEncryptionMasterKeyIds = src.createLongArray();
mSymmetricPassphrase = src.readString();
mSymmetricPassphrase = src.readParcelable(Passphrase.class.getClassLoader());
mSymmetricEncryptionAlgorithm = src.readInt();
mSignatureMasterKeyId = src.readLong();
mSignatureSubKeyId = src.readInt() == 1 ? src.readLong() : null;
mSignatureHashAlgorithm = src.readInt();
mSignaturePassphrase = src.readString();
mSignaturePassphrase = src.readParcelable(Passphrase.class.getClassLoader());
mAdditionalEncryptId = src.readLong();
mNfcSignedHash = src.createByteArray();
mNfcCreationTimestamp = src.readInt() == 1 ? new Date(src.readLong()) : null;
@ -52,6 +72,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
mCharset = src.readString();
mCleartextSignature = src.readInt() == 1;
mDetachedSignature = src.readInt() == 1;
mHiddenRecipients = src.readInt() == 1;
mInputUris = src.createTypedArrayList(Uri.CREATOR);
mOutputUris = src.createTypedArrayList(Uri.CREATOR);
@ -93,7 +114,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
dest.writeInt(mEnableAsciiArmorOutput ? 1 : 0);
dest.writeInt(mCompressionId);
dest.writeLongArray(mEncryptionMasterKeyIds);
dest.writeString(mSymmetricPassphrase);
dest.writeParcelable(mSymmetricPassphrase, flags);
dest.writeInt(mSymmetricEncryptionAlgorithm);
dest.writeLong(mSignatureMasterKeyId);
if (mSignatureSubKeyId != null) {
@ -103,7 +124,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
dest.writeInt(0);
}
dest.writeInt(mSignatureHashAlgorithm);
dest.writeString(mSignaturePassphrase);
dest.writeParcelable(mSignaturePassphrase, flags);
dest.writeLong(mAdditionalEncryptId);
dest.writeByteArray(mNfcSignedHash);
if (mNfcCreationTimestamp != null) {
@ -116,6 +137,7 @@ public class SignEncryptParcel extends PgpSignEncryptInput implements Parcelable
dest.writeString(mCharset);
dest.writeInt(mCleartextSignature ? 1 : 0);
dest.writeInt(mDetachedSignature ? 1 : 0);
dest.writeInt(mHiddenRecipients ? 1 : 0);
dest.writeTypedList(mInputUris);
dest.writeTypedList(mOutputUris);

View File

@ -54,6 +54,7 @@ import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.IOException;
import java.io.InputStream;
@ -179,7 +180,7 @@ public class OpenPgpService extends RemoteService {
return result;
}
private PendingIntent getNfcSignPendingIntent(Intent data, long keyId, String pin, byte[] hashToSign, int hashAlgo) {
private PendingIntent getNfcSignPendingIntent(Intent data, long keyId, Passphrase pin, byte[] hashToSign, int hashAlgo) {
// build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(getBaseContext(), NfcActivity.class);
intent.setAction(NfcActivity.ACTION_SIGN_HASH);
@ -195,7 +196,7 @@ public class OpenPgpService extends RemoteService {
PendingIntent.FLAG_CANCEL_CURRENT);
}
private PendingIntent getNfcDecryptPendingIntent(Intent data, long subKeyId, String pin, byte[] encryptedSessionKey) {
private PendingIntent getNfcDecryptPendingIntent(Intent data, long subKeyId, Passphrase pin, byte[] encryptedSessionKey) {
// build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(getBaseContext(), NfcActivity.class);
intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY);
@ -239,6 +240,11 @@ public class OpenPgpService extends RemoteService {
try {
boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
Passphrase passphrase = null;
if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) {
passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
if (nfcSignedHash != null) {
Log.d(Constants.TAG, "nfcSignedHash:" + Hex.toHexString(nfcSignedHash));
@ -277,6 +283,7 @@ public class OpenPgpService extends RemoteService {
// sign-only
PgpSignEncryptInput pseInput = new PgpSignEncryptInput()
.setSignaturePassphrase(passphrase)
.setEnableAsciiArmorOutput(asciiArmor)
.setCleartextSignature(cleartextSign)
.setDetachedSignature(!cleartextSign)
@ -365,6 +372,11 @@ public class OpenPgpService extends RemoteService {
compressionId = CompressionAlgorithmTags.UNCOMPRESSED;
}
Passphrase passphrase = null;
if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) {
passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
// first try to get key ids from non-ambiguous key id extra
long[] keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS);
if (keyIds == null) {
@ -390,7 +402,8 @@ public class OpenPgpService extends RemoteService {
InputData inputData = new InputData(is, inputLength, originalFilename);
PgpSignEncryptInput pseInput = new PgpSignEncryptInput();
pseInput.setEnableAsciiArmorOutput(asciiArmor)
pseInput.setSignaturePassphrase(passphrase)
.setEnableAsciiArmorOutput(asciiArmor)
.setVersionHeader(null)
.setCompressionId(compressionId)
.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED)
@ -498,6 +511,11 @@ public class OpenPgpService extends RemoteService {
os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
}
Passphrase passphrase = null;
if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) {
passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
}
String currentPkg = getCurrentCallingPackage();
Set<Long> allowedKeyIds;
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 7) {
@ -508,7 +526,6 @@ public class OpenPgpService extends RemoteService {
KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg));
}
String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
long inputLength = is.available();
InputData inputData = new InputData(is, inputLength);
@ -554,15 +571,16 @@ public class OpenPgpService extends RemoteService {
}
} else if (pgpResult.success()) {
Intent result = new Intent();
int resultType = OpenPgpApi.RESULT_TYPE_UNENCRYPTED_UNSIGNED;
OpenPgpSignatureResult signatureResult = pgpResult.getSignatureResult();
// TODO: currently RESULT_TYPE_UNENCRYPTED_UNSIGNED is never returned
// instead an error is returned when no pgp data has been found
int resultType = OpenPgpApi.RESULT_TYPE_UNENCRYPTED_UNSIGNED;
if (signatureResult != null) {
resultType |= OpenPgpApi.RESULT_TYPE_SIGNED;
if (!signatureResult.isSignatureOnly()) {
resultType |= OpenPgpApi.RESULT_TYPE_ENCRYPTED;
}
result.putExtra(OpenPgpApi.RESULT_TYPE, resultType);
result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult);
@ -582,7 +600,10 @@ public class OpenPgpService extends RemoteService {
// If signature key is known, return PendingIntent to show key
result.putExtra(OpenPgpApi.RESULT_INTENT, getShowKeyPendingIntent(signatureResult.getKeyId()));
}
} else {
resultType |= OpenPgpApi.RESULT_TYPE_ENCRYPTED;
}
result.putExtra(OpenPgpApi.RESULT_TYPE, resultType);
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) >= 4) {
OpenPgpMetadata metadata = pgpResult.getDecryptMetadata();

View File

@ -92,11 +92,11 @@ public class AccountSettingsFragment extends Fragment {
}
private void createKey() {
String[] userId = KeyRing.splitUserId(mAccSettings.getAccountName());
KeyRing.UserId userId = KeyRing.splitUserId(mAccSettings.getAccountName());
Intent intent = new Intent(getActivity(), CreateKeyActivity.class);
intent.putExtra(CreateKeyActivity.EXTRA_NAME, userId[0]);
intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userId[1]);
intent.putExtra(CreateKeyActivity.EXTRA_NAME, userId.name);
intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userId.email);
startActivityForResult(intent, REQUEST_CODE_CREATE_KEY);
}

View File

@ -186,7 +186,7 @@ public class RemoteServiceActivity extends BaseActivity {
// user needs to select a key if it has explicitly requested (None is only allowed for new accounts)
if (mUpdateExistingAccount && mAccSettingsFragment.getAccSettings().getKeyId() == Constants.key.none) {
Notify.showNotify(RemoteServiceActivity.this, getString(R.string.api_register_error_select_key), Notify.Style.ERROR);
Notify.create(RemoteServiceActivity.this, getString(R.string.api_register_error_select_key), Notify.Style.ERROR).show();
} else {
if (mUpdateExistingAccount) {
Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(packageName);

View File

@ -42,9 +42,11 @@ public class SelectSignKeyIdActivity extends BaseActivity {
private Uri mAppUri;
private String mPreferredUserId;
private Intent mData;
private SelectSignKeyIdListFragment mListFragment;
private TextView mActionCreateKey;
private TextView mNone;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -67,27 +69,38 @@ public class SelectSignKeyIdActivity extends BaseActivity {
createKey(mPreferredUserId);
}
});
mNone = (TextView) findViewById(R.id.api_select_sign_key_none);
mNone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 0 is "none"
mData.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, 0);
setResult(Activity.RESULT_OK, mData);
finish();
}
});
Intent intent = getIntent();
mAppUri = intent.getData();
mPreferredUserId = intent.getStringExtra(EXTRA_USER_ID);
Intent data = intent.getParcelableExtra(EXTRA_DATA);
mData = intent.getParcelableExtra(EXTRA_DATA);
if (mAppUri == null) {
Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!");
finish();
return;
} else {
Log.d(Constants.TAG, "uri: " + mAppUri);
startListFragments(savedInstanceState, mAppUri, data);
startListFragments(savedInstanceState, mAppUri, mData);
}
}
private void createKey(String userId) {
String[] userIdSplit = KeyRing.splitUserId(userId);
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
Intent intent = new Intent(this, CreateKeyActivity.class);
intent.putExtra(CreateKeyActivity.EXTRA_NAME, userIdSplit[0]);
intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userIdSplit[1]);
intent.putExtra(CreateKeyActivity.EXTRA_NAME, userIdSplit.name);
intent.putExtra(CreateKeyActivity.EXTRA_EMAIL, userIdSplit.email);
startActivityForResult(intent, REQUEST_CODE_CREATE_KEY);
}

View File

@ -73,6 +73,7 @@ import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@ -289,7 +290,7 @@ public class KeychainIntentService extends IntentService implements Progressable
try {
/* Input */
String passphrase = data.getString(DECRYPT_PASSPHRASE);
Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE);
byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
InputData inputData = createDecryptInputData(data);
@ -419,7 +420,7 @@ public class KeychainIntentService extends IntentService implements Progressable
try {
/* Input */
String passphrase = data.getString(DECRYPT_PASSPHRASE);
Passphrase passphrase = data.getParcelable(DECRYPT_PASSPHRASE);
byte[] nfcDecryptedSessionKey = data.getByteArray(DECRYPT_NFC_DECRYPTED_SESSION_KEY);
InputData inputData = createDecryptInputData(data);
@ -477,7 +478,7 @@ public class KeychainIntentService extends IntentService implements Progressable
// Input
SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
String passphrase = data.getString(EDIT_KEYRING_PASSPHRASE);
Passphrase passphrase = data.getParcelable(EDIT_KEYRING_PASSPHRASE);
// Operation
EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled);

View File

@ -129,9 +129,9 @@ public class KeychainIntentServiceHandler extends Handler {
// show error from service
if (data.containsKey(DATA_ERROR)) {
Notify.showNotify(mActivity,
Notify.create(mActivity,
mActivity.getString(R.string.error_message, data.getString(DATA_ERROR)),
Notify.Style.ERROR);
Notify.Style.ERROR).show();
}
break;

View File

@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.Date;
@ -121,7 +122,7 @@ public class PassphraseCacheService extends Service {
* new events to the alarm manager for new passphrases to let them timeout in the future.
*/
public static void addCachedPassphrase(Context context, long masterKeyId, long subKeyId,
String passphrase,
Passphrase passphrase,
String primaryUserId) {
Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + masterKeyId);
@ -143,7 +144,7 @@ public class PassphraseCacheService extends Service {
* @return passphrase or null (if no passphrase is cached for this keyId)
*/
public static String getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException {
public static Passphrase getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphrase() for masterKeyId "
+ masterKeyId + ", subKeyId " + subKeyId);
@ -190,7 +191,9 @@ public class PassphraseCacheService extends Service {
switch (returnMessage.what) {
case MSG_PASSPHRASE_CACHE_GET_OKAY:
return returnMessage.getData().getString(EXTRA_PASSPHRASE);
Bundle returnData = returnMessage.getData();
returnData.setClassLoader(context.getClassLoader());
return returnData.getParcelable(EXTRA_PASSPHRASE);
case MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND:
throw new KeyNotFoundException();
default:
@ -202,11 +205,11 @@ public class PassphraseCacheService extends Service {
/**
* Internal implementation to get cached passphrase.
*/
private String getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException {
private Passphrase getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException {
// passphrase for symmetric encryption?
if (masterKeyId == Constants.key.symmetric) {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption");
String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase();
Passphrase cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase();
if (cachedPassphrase == null) {
return null;
}
@ -232,13 +235,13 @@ public class PassphraseCacheService extends Service {
case DIVERT_TO_CARD:
if (Preferences.getPreferences(this).useDefaultYubikeyPin()) {
Log.d(Constants.TAG, "PassphraseCacheService: Using default Yubikey PIN: 123456");
return "123456"; // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/
return new Passphrase("123456"); // default Yubikey PIN, see http://www.yubico.com/2012/12/yubikey-neo-openpgp/
} else {
Log.d(Constants.TAG, "PassphraseCacheService: NOT using default Yubikey PIN");
break;
}
case PASSPHRASE_EMPTY:
return "";
return new Passphrase("");
case UNAVAILABLE:
throw new ProviderHelper.NotFoundException("secret key for this subkey is not available");
case GNU_DUMMY:
@ -331,7 +334,7 @@ public class PassphraseCacheService extends Service {
long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1);
String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE);
Passphrase passphrase = intent.getParcelableExtra(EXTRA_PASSPHRASE);
String primaryUserID = intent.getStringExtra(EXTRA_USER_ID);
Log.d(Constants.TAG,
@ -373,10 +376,10 @@ public class PassphraseCacheService extends Service {
Log.e(Constants.TAG, "PassphraseCacheService: Bad request, missing masterKeyId or subKeyId!");
msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND;
} else {
String passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId);
Passphrase passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId);
msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY;
Bundle bundle = new Bundle();
bundle.putString(EXTRA_PASSPHRASE, passphrase);
bundle.putParcelable(EXTRA_PASSPHRASE, passphrase);
msg.setData(bundle);
}
} catch (ProviderHelper.NotFoundException e) {
@ -412,7 +415,10 @@ public class PassphraseCacheService extends Service {
* Called when one specific passphrase for keyId timed out
*/
private void timeout(Context context, long keyId) {
// remove passphrase corresponding to keyId from memory
CachedPassphrase cPass = mPassphraseCache.get(keyId);
// clean internal char[] from memory!
cPass.getPassphrase().removeFromMemory();
// remove passphrase object
mPassphraseCache.remove(keyId);
Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!");
@ -517,9 +523,9 @@ public class PassphraseCacheService extends Service {
public class CachedPassphrase {
private String primaryUserID;
private String passphrase;
private Passphrase passphrase;
public CachedPassphrase(String passphrase, String primaryUserID) {
public CachedPassphrase(Passphrase passphrase, String primaryUserID) {
setPassphrase(passphrase);
setPrimaryUserID(primaryUserID);
}
@ -528,7 +534,7 @@ public class PassphraseCacheService extends Service {
return primaryUserID;
}
public String getPassphrase() {
public Passphrase getPassphrase() {
return passphrase;
}
@ -536,7 +542,7 @@ public class PassphraseCacheService extends Service {
this.primaryUserID = primaryUserID;
}
public void setPassphrase(String passphrase) {
public void setPassphrase(Passphrase passphrase) {
this.passphrase = passphrase;
}
}

View File

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

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.KeySpinner;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
import java.lang.reflect.Method;
@ -168,8 +169,8 @@ public class CertifyKeyFragment extends LoaderFragment
@Override
public void onClick(View v) {
if (mSignMasterKeyId == Constants.key.none) {
Notify.showNotify(getActivity(), getString(R.string.select_key_to_certify),
Notify.Style.ERROR);
Notify.create(getActivity(), getString(R.string.select_key_to_certify),
Notify.Style.ERROR).show();
} else {
initiateCertifying();
}
@ -246,14 +247,14 @@ public class CertifyKeyFragment extends LoaderFragment
while (!data.isAfterLast()) {
long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID);
String userId = data.getString(INDEX_USER_ID);
String[] pieces = KeyRing.splitUserId(userId);
KeyRing.UserId pieces = KeyRing.splitUserId(userId);
// Two cases:
boolean grouped = masterKeyId == lastMasterKeyId;
boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces[0]);
boolean subGrouped = data.isFirst() || grouped && lastName.equals(pieces.name);
// Remember for next loop
lastName = pieces[0];
lastName = pieces.name;
Log.d(Constants.TAG, Long.toString(masterKeyId, 16) + (grouped ? "grouped" : "not grouped"));
@ -318,7 +319,7 @@ public class CertifyKeyFragment extends LoaderFragment
*/
private void initiateCertifying() {
// get the user's passphrase for this key (if required)
String passphrase;
Passphrase passphrase;
try {
passphrase = PassphraseCacheService.getCachedPassphrase(getActivity(), mSignMasterKeyId, mSignMasterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
@ -341,7 +342,6 @@ public class CertifyKeyFragment extends LoaderFragment
switch (requestCode) {
case REQUEST_CODE_PASSPHRASE: {
if (resultCode == Activity.RESULT_OK && data != null) {
String passphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
startCertifying();
}
return;
@ -360,8 +360,8 @@ public class CertifyKeyFragment extends LoaderFragment
// Bail out if there is not at least one user id selected
ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
if (certifyActions.isEmpty()) {
Notify.showNotify(getActivity(), "No identities selected!",
Notify.Style.ERROR);
Notify.create(getActivity(), "No identities selected!",
Notify.Style.ERROR).show();
return;
}

View File

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

View File

@ -46,16 +46,12 @@ import java.util.List;
public class CreateKeyEmailFragment extends Fragment {
public static final String ARG_NAME = "name";
public static final String ARG_EMAIL = "email";
CreateKeyActivity mCreateKeyActivity;
EmailEditText mEmailEdit;
RecyclerView mEmailsRecyclerView;
View mBackButton;
View mNextButton;
String mName;
ArrayList<EmailAdapter.ViewModel> mAdditionalEmailModels;
EmailAdapter mEmailAdapter;
@ -63,13 +59,10 @@ public class CreateKeyEmailFragment extends Fragment {
/**
* Creates new instance of this fragment
*/
public static CreateKeyEmailFragment newInstance(String name, String email) {
public static CreateKeyEmailFragment newInstance() {
CreateKeyEmailFragment frag = new CreateKeyEmailFragment();
Bundle args = new Bundle();
args.putString(ARG_NAME, name);
args.putString(ARG_EMAIL, email);
frag.setArguments(args);
return frag;
@ -85,7 +78,7 @@ public class CreateKeyEmailFragment extends Fragment {
*/
private static boolean isEditTextNotEmpty(Context context, EditText editText) {
boolean output = true;
if (editText.getText().toString().length() == 0) {
if (editText.getText().length() == 0) {
editText.setError(context.getString(R.string.create_key_empty));
editText.requestFocus();
output = false;
@ -106,31 +99,33 @@ public class CreateKeyEmailFragment extends Fragment {
mEmailsRecyclerView = (RecyclerView) view.findViewById(R.id.create_key_emails);
// initial values
mName = getArguments().getString(ARG_NAME);
String email = getArguments().getString(ARG_EMAIL);
mEmailEdit.setText(email);
mEmailEdit.setText(mCreateKeyActivity.mEmail);
// focus empty edit fields
if (email == null) {
if (mCreateKeyActivity.mEmail == null) {
mEmailEdit.requestFocus();
}
mBackButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT);
mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
}
});
mNextButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
createKeyCheck();
nextClicked();
}
});
mEmailsRecyclerView.setHasFixedSize(true);
mEmailsRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mEmailsRecyclerView.setItemAnimator(new DefaultItemAnimator());
// initial values
mAdditionalEmailModels = new ArrayList<>();
if (mCreateKeyActivity.mAdditionalEmails != null) {
setAdditionalEmails(mCreateKeyActivity.mAdditionalEmails);
}
mEmailAdapter = new EmailAdapter(mAdditionalEmailModels, new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -171,25 +166,38 @@ public class CreateKeyEmailFragment extends Fragment {
mCreateKeyActivity = (CreateKeyActivity) getActivity();
}
private void createKeyCheck() {
private void nextClicked() {
if (isEditTextNotEmpty(getActivity(), mEmailEdit)) {
// save state
mCreateKeyActivity.mEmail = mEmailEdit.getText().toString();
mCreateKeyActivity.mAdditionalEmails = getAdditionalEmails();
ArrayList<String> emails = new ArrayList<>();
for (EmailAdapter.ViewModel holder : mAdditionalEmailModels) {
emails.add(holder.toString());
}
CreateKeyPassphraseFragment frag =
CreateKeyPassphraseFragment.newInstance(
mName,
mEmailEdit.getText().toString(),
emails
);
mCreateKeyActivity.loadFragment(null, frag, FragAction.TO_RIGHT);
CreateKeyPassphraseFragment frag = CreateKeyPassphraseFragment.newInstance();
mCreateKeyActivity.loadFragment(frag, FragAction.TO_RIGHT);
}
}
private ArrayList<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> {
private List<ViewModel> mDataset;
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.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
@ -64,32 +65,15 @@ public class CreateKeyFinalFragment extends Fragment {
TextView mEditText;
View mEditButton;
public static final String ARG_NAME = "name";
public static final String ARG_EMAIL = "email";
public static final String ARG_ADDITIONAL_EMAILS = "emails";
public static final String ARG_PASSPHRASE = "passphrase";
String mName;
String mEmail;
ArrayList<String> mAdditionalEmails;
String mPassphrase;
SaveKeyringParcel mSaveKeyringParcel;
/**
* Creates new instance of this fragment
*/
public static CreateKeyFinalFragment newInstance(String name, String email,
ArrayList<String> additionalEmails,
String passphrase) {
public static CreateKeyFinalFragment newInstance() {
CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
Bundle args = new Bundle();
args.putString(ARG_NAME, name);
args.putString(ARG_EMAIL, email);
args.putStringArrayList(ARG_ADDITIONAL_EMAILS, additionalEmails);
args.putString(ARG_PASSPHRASE, passphrase);
frag.setArguments(args);
return frag;
@ -107,17 +91,11 @@ public class CreateKeyFinalFragment extends Fragment {
mEditText = (TextView) view.findViewById(R.id.create_key_edit_text);
mEditButton = view.findViewById(R.id.create_key_edit_button);
// get args
mName = getArguments().getString(ARG_NAME);
mEmail = getArguments().getString(ARG_EMAIL);
mAdditionalEmails = getArguments().getStringArrayList(ARG_ADDITIONAL_EMAILS);
mPassphrase = getArguments().getString(ARG_PASSPHRASE);
// set values
mNameEdit.setText(mName);
if (mAdditionalEmails != null && mAdditionalEmails.size() > 0) {
String emailText = mEmail + ", ";
Iterator<?> it = mAdditionalEmails.iterator();
mNameEdit.setText(mCreateKeyActivity.mName);
if (mCreateKeyActivity.mAdditionalEmails != null && mCreateKeyActivity.mAdditionalEmails.size() > 0) {
String emailText = mCreateKeyActivity.mEmail + ", ";
Iterator<?> it = mCreateKeyActivity.mAdditionalEmails.iterator();
while (it.hasNext()) {
Object next = it.next();
emailText += next;
@ -127,7 +105,7 @@ public class CreateKeyFinalFragment extends Fragment {
}
mEmailEdit.setText(emailText);
} else {
mEmailEdit.setText(mEmail);
mEmailEdit.setText(mCreateKeyActivity.mEmail);
}
mCreateButton.setOnClickListener(new View.OnClickListener() {
@ -140,7 +118,7 @@ public class CreateKeyFinalFragment extends Fragment {
mBackButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCreateKeyActivity.loadFragment(null, null, FragAction.TO_LEFT);
mCreateKeyActivity.loadFragment(null, FragAction.TO_LEFT);
}
});
@ -156,6 +134,12 @@ public class CreateKeyFinalFragment extends Fragment {
return view;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCreateKeyActivity = (CreateKeyActivity) getActivity();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
@ -186,17 +170,22 @@ public class CreateKeyFinalFragment extends Fragment {
Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L));
mSaveKeyringParcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
String userId = KeyRing.createUserId(mName, mEmail, null);
String userId = KeyRing.createUserId(
new KeyRing.UserId(mCreateKeyActivity.mName, mCreateKeyActivity.mEmail, null)
);
mSaveKeyringParcel.mAddUserIds.add(userId);
mSaveKeyringParcel.mChangePrimaryUserId = userId;
if (mAdditionalEmails != null && mAdditionalEmails.size() > 0) {
for (String email : mAdditionalEmails) {
String thisUserId = KeyRing.createUserId(mName, email, null);
if (mCreateKeyActivity.mAdditionalEmails != null
&& mCreateKeyActivity.mAdditionalEmails.size() > 0) {
for (String email : mCreateKeyActivity.mAdditionalEmails) {
String thisUserId = KeyRing.createUserId(
new KeyRing.UserId(mCreateKeyActivity.mName, email, null)
);
mSaveKeyringParcel.mAddUserIds.add(thisUserId);
}
}
mSaveKeyringParcel.mNewUnlock = mPassphrase != null
? new ChangeUnlockParcel(mPassphrase, null)
mSaveKeyringParcel.mNewUnlock = mCreateKeyActivity.mPassphrase != null
? new ChangeUnlockParcel(mCreateKeyActivity.mPassphrase, null)
: null;
}
}
@ -288,8 +277,8 @@ public class CreateKeyFinalFragment extends Fragment {
// TODO: upload operation needs a result!
// TODO: then combine these results
//if (result.getResult() == OperationResultParcel.RESULT_OK) {
//Notify.showNotify(getActivity(), R.string.key_send_success,
//Notify.Style.INFO);
//Notify.create(getActivity(), R.string.key_send_success,
//Notify.Style.OK).show();
Intent data = new Intent();
data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);

View File

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

View File

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

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

@ -139,7 +139,7 @@ public class DecryptFilesFragment extends DecryptFragment {
private void decryptAction() {
if (mInputUri == null) {
Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);
Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show();
return;
}
@ -147,7 +147,9 @@ public class DecryptFilesFragment extends DecryptFragment {
}
private String removeEncryptedAppend(String name) {
if (name.endsWith(".asc") || name.endsWith(".gpg") || name.endsWith(".pgp")) {
if (name.endsWith(Constants.FILE_EXTENSION_ASC)
|| name.endsWith(Constants.FILE_EXTENSION_PGP_MAIN)
|| name.endsWith(Constants.FILE_EXTENSION_PGP_ALTERNATE)) {
return name.substring(0, name.length() - 4);
}
return name;
@ -189,7 +191,7 @@ public class DecryptFilesFragment extends DecryptFragment {
data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal());
data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri);
data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@ -263,7 +265,7 @@ public class DecryptFilesFragment extends DecryptFragment {
data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal());
data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri);
data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@ -339,7 +341,7 @@ public class DecryptFilesFragment extends DecryptFragment {
switch (requestCode) {
case REQUEST_CODE_PASSPHRASE: {
if (resultCode == Activity.RESULT_OK && data != null) {
mPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
mPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
decryptOriginalFilename();
}
return;

View File

@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.util.Passphrase;
public abstract class DecryptFragment extends Fragment {
private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006;
@ -57,7 +58,7 @@ public abstract class DecryptFragment extends Fragment {
// State
protected String mPassphrase;
protected Passphrase mPassphrase;
protected byte[] mNfcDecryptedSessionKey;
@Override
@ -100,7 +101,7 @@ public abstract class DecryptFragment extends Fragment {
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
}
protected void startNfcDecrypt(long subKeyId, String pin, byte[] encryptedSessionKey) {
protected void startNfcDecrypt(long subKeyId, Passphrase pin, byte[] encryptedSessionKey) {
// build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(getActivity(), NfcActivity.class);
intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY);
@ -128,14 +129,14 @@ public abstract class DecryptFragment extends Fragment {
mSignatureKeyId = signatureResult.getKeyId();
String userId = signatureResult.getPrimaryUserId();
String[] userIdSplit = KeyRing.splitUserId(userId);
if (userIdSplit[0] != null) {
mSignatureName.setText(userIdSplit[0]);
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
if (userIdSplit.name != null) {
mSignatureName.setText(userIdSplit.name);
} else {
mSignatureName.setText(R.string.user_id_no_name);
}
if (userIdSplit[1] != null) {
mSignatureEmail.setText(userIdSplit[1]);
if (userIdSplit.email != null) {
mSignatureEmail.setText(userIdSplit.email);
} else {
mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), mSignatureKeyId));
}

View File

@ -161,7 +161,7 @@ public class DecryptTextActivity extends BaseActivity {
if (sharedText != null) {
loadFragment(savedInstanceState, sharedText);
} else {
Notify.showNotify(this, R.string.error_invalid_data, Notify.Style.ERROR);
Notify.create(this, R.string.error_invalid_data, Notify.Style.ERROR).show();
}
} else {
Log.e(Constants.TAG, "ACTION_SEND received non-plaintext, this should not happen in this activity!");
@ -175,7 +175,7 @@ public class DecryptTextActivity extends BaseActivity {
if (extraText != null) {
loadFragment(savedInstanceState, extraText);
} else {
Notify.showNotify(this, R.string.error_invalid_data, Notify.Style.ERROR);
Notify.create(this, R.string.error_invalid_data, Notify.Style.ERROR).show();
}
} else if (ACTION_DECRYPT_FROM_CLIPBOARD.equals(action)) {
Log.d(Constants.TAG, "ACTION_DECRYPT_FROM_CLIPBOARD");

View File

@ -132,7 +132,7 @@ public class DecryptTextFragment extends DecryptFragment {
private void copyToClipboard(String text) {
ClipboardReflection.copyToClipboard(getActivity(), text);
Notify.showNotify(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.INFO);
Notify.create(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.OK).show();
}
@Override
@ -161,7 +161,7 @@ public class DecryptTextFragment extends DecryptFragment {
// data
data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal());
data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes());
data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
data.putParcelable(KeychainIntentService.DECRYPT_PASSPHRASE, mPassphrase);
data.putByteArray(KeychainIntentService.DECRYPT_NFC_DECRYPTED_SESSION_KEY, mNfcDecryptedSessionKey);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
@ -247,7 +247,7 @@ public class DecryptTextFragment extends DecryptFragment {
case REQUEST_CODE_PASSPHRASE: {
if (resultCode == Activity.RESULT_OK && data != null) {
mPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
mPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
decryptStart();
} else {
getActivity().finish();

View File

@ -67,6 +67,7 @@ import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
public class EditKeyFragment extends LoaderFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
@ -100,7 +101,7 @@ public class EditKeyFragment extends LoaderFragment implements
private SaveKeyringParcel mSaveKeyringParcel;
private String mPrimaryUserId;
private String mCurrentPassphrase;
private Passphrase mCurrentPassphrase;
/**
* Creates new instance of this fragment
@ -267,7 +268,7 @@ public class EditKeyFragment extends LoaderFragment implements
switch (requestCode) {
case REQUEST_CODE_PASSPHRASE: {
if (resultCode == Activity.RESULT_OK && data != null) {
mCurrentPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
mCurrentPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, EditKeyFragment.this);
@ -386,7 +387,7 @@ public class EditKeyFragment extends LoaderFragment implements
// cache new returned passphrase!
mSaveKeyringParcel.mNewUnlock = new ChangeUnlockParcel(
data.getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
(Passphrase) data.getParcelable(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE),
null
);
}
@ -545,7 +546,7 @@ public class EditKeyFragment extends LoaderFragment implements
Messenger messenger = new Messenger(returnHandler);
// pre-fill out primary name
String predefinedName = KeyRing.splitUserId(mPrimaryUserId)[0];
String predefinedName = KeyRing.splitUserId(mPrimaryUserId).name;
AddUserIdDialogFragment addUserIdDialog = AddUserIdDialogFragment.newInstance(messenger,
predefinedName);
@ -576,11 +577,11 @@ public class EditKeyFragment extends LoaderFragment implements
private void returnKeyringParcel() {
if (mSaveKeyringParcel.mAddUserIds.size() == 0) {
Notify.showNotify(getActivity(), R.string.edit_key_error_add_identity, Notify.Style.ERROR);
Notify.create(getActivity(), R.string.edit_key_error_add_identity, Notify.Style.ERROR).show();
return;
}
if (mSaveKeyringParcel.mAddSubKeys.size() == 0) {
Notify.showNotify(getActivity(), R.string.edit_key_error_add_subkey, Notify.Style.ERROR);
Notify.create(getActivity(), R.string.edit_key_error_add_subkey, Notify.Style.ERROR).show();
return;
}
@ -593,7 +594,7 @@ public class EditKeyFragment extends LoaderFragment implements
getActivity().finish();
}
private void saveInDatabase(String passphrase) {
private void saveInDatabase(Passphrase passphrase) {
Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString());
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
@ -640,7 +641,7 @@ public class EditKeyFragment extends LoaderFragment implements
// fill values for this action
Bundle data = new Bundle();
data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase);
data.putParcelable(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase);
data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);

View File

@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.Date;
@ -41,7 +42,7 @@ public abstract class EncryptActivity extends BaseActivity {
public static final int REQUEST_CODE_NFC = 0x00008002;
// For NFC data
protected String mSigningKeyPassphrase = null;
protected Passphrase mSigningKeyPassphrase = null;
protected Date mNfcTimestamp = null;
protected byte[] mNfcHash = null;
@ -64,7 +65,7 @@ public abstract class EncryptActivity extends BaseActivity {
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
}
protected void startNfcSign(long keyId, String pin, byte[] hashToSign, int hashAlgo) {
protected void startNfcSign(long keyId, Passphrase pin, byte[] hashToSign, int hashAlgo) {
// build PendingIntent for Yubikey NFC operations
Intent intent = new Intent(this, NfcActivity.class);
intent.setAction(NfcActivity.ACTION_SIGN_HASH);
@ -84,7 +85,7 @@ public abstract class EncryptActivity extends BaseActivity {
switch (requestCode) {
case REQUEST_CODE_PASSPHRASE: {
if (resultCode == RESULT_OK && data != null) {
mSigningKeyPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
mSigningKeyPassphrase = data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE);
startEncrypt();
return;
}

View File

@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui;
import android.net.Uri;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.util.ArrayList;
public interface EncryptActivityInterface {
@ -29,6 +31,8 @@ public interface EncryptActivityInterface {
public boolean isUseArmor();
public boolean isUseCompression();
public boolean isEncryptFilenames();
public boolean isHiddenRecipients();
public long getSignatureKey();
public long[] getEncryptionKeys();
@ -37,7 +41,7 @@ public interface EncryptActivityInterface {
public void setEncryptionKeys(long[] encryptionKeys);
public void setEncryptionUsers(String[] encryptionUsers);
public void setPassphrase(String passphrase);
public void setPassphrase(Passphrase passphrase);
// ArrayList on purpose as only those are parcelable
public ArrayList<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.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ShareHelper;
import java.util.ArrayList;
@ -62,14 +63,18 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
private static final int MODE_SYMMETRIC = 1;
// model used by fragments
private long mEncryptionKeyIds[] = null;
private String mEncryptionUserIds[] = null;
private long mSigningKeyId = Constants.key.none;
private String mPassphrase = "";
private boolean mUseArmor = false;
private boolean mUseCompression = true;
private boolean mDeleteAfterEncrypt = false;
private boolean mShareAfterEncrypt = false;
private boolean mEncryptFilenames = true;
private boolean mHiddenRecipients = false;
private long mEncryptionKeyIds[] = null;
private String mEncryptionUserIds[] = null;
private long mSigningKeyId = Constants.key.none;
private Passphrase mPassphrase = new Passphrase();
private ArrayList<Uri> mInputUris;
private ArrayList<Uri> mOutputUris;
private String mMessage = "";
@ -88,6 +93,16 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
return mUseCompression;
}
@Override
public boolean isEncryptFilenames() {
return mEncryptFilenames;
}
@Override
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
@Override
public long getSignatureKey() {
return mSigningKeyId;
@ -122,7 +137,7 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
}
@Override
public void setPassphrase(String passphrase) {
public void setPassphrase(Passphrase passphrase) {
mPassphrase = passphrase;
}
@ -222,14 +237,15 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
} else {
data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED);
}
data.setHiddenRecipients(mHiddenRecipients);
data.setEnableAsciiArmorOutput(mUseArmor);
data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
if (isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!");
String passphrase = mPassphrase;
if (passphrase.length() == 0) {
Passphrase passphrase = mPassphrase;
if (passphrase.isEmpty()) {
passphrase = null;
}
data.setSymmetricPassphrase(passphrase);
@ -268,14 +284,14 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris);
}
sendIntent.setType("application/octet-stream");
sendIntent.setType(Constants.ENCRYPTED_FILES_MIME);
if (!isModeSymmetric() && mEncryptionUserIds != null) {
Set<String> users = new HashSet<>();
for (String user : mEncryptionUserIds) {
String[] userId = KeyRing.splitUserId(user);
if (userId[1] != null) {
users.add(userId[1]);
KeyRing.UserId userId = KeyRing.splitUserId(user);
if (userId.email != null) {
users.add(userId.email);
}
}
sendIntent.putExtra(Intent.EXTRA_EMAIL, users.toArray(new String[users.size()]));
@ -287,7 +303,8 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
// file checks
if (mInputUris.isEmpty()) {
Notify.showNotify(this, R.string.no_file_selected, Notify.Style.ERROR);
Notify.create(this, R.string.no_file_selected, Notify.Style.ERROR)
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment));
return false;
} else if (mInputUris.size() > 1 && !mShareAfterEncrypt) {
// This should be impossible...
@ -301,11 +318,13 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
// symmetric encryption checks
if (mPassphrase == null) {
Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR);
Notify.create(this, R.string.passphrases_do_not_match, Notify.Style.ERROR)
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment));
return false;
}
if (mPassphrase.isEmpty()) {
Notify.showNotify(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR);
Notify.create(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment));
return false;
}
@ -317,7 +336,8 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
// Files must be encrypted, only text can be signed-only right now
if (!gotEncryptionKeys) {
Notify.showNotify(this, R.string.select_encryption_key, Notify.Style.ERROR);
Notify.create(this, R.string.select_encryption_key, Notify.Style.ERROR)
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_file_fragment));
return false;
}
}
@ -371,6 +391,16 @@ public class EncryptFilesActivity extends EncryptActivity implements EncryptActi
notifyUpdate();
break;
}
case R.id.check_encrypt_filenames: {
mEncryptFilenames = item.isChecked();
notifyUpdate();
break;
}
// case R.id.check_hidden_recipients: {
// mHiddenRecipients = item.isChecked();
// notifyUpdate();
// break;
// }
default: {
return super.onOptionsItemSelected(item);
}

View File

@ -115,9 +115,9 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt
}
if (mEncryptInterface.getInputUris().contains(inputUri)) {
Notify.showNotify(getActivity(),
Notify.create(getActivity(),
getActivity().getString(R.string.error_file_added_already, FileHelper.getFilename(getActivity(), inputUri)),
Notify.Style.ERROR);
Notify.Style.ERROR).show(this);
return;
}
@ -137,36 +137,39 @@ public class EncryptFilesFragment extends Fragment implements EncryptActivityInt
throw new IllegalStateException();
}
Uri inputUri = mEncryptInterface.getInputUris().get(0);
String targetName =
(mEncryptInterface.isEncryptFilenames() ? "1" : FileHelper.getFilename(getActivity(), inputUri))
+ (mEncryptInterface.isUseArmor() ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
File file = new File(inputUri.getPath());
File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR;
String targetName = FileHelper.getFilename(getActivity(), inputUri) +
(mEncryptInterface.isUseArmor() ? ".asc" : ".gpg");
File targetFile = new File(parentDir, targetName);
FileHelper.saveFile(this, getString(R.string.title_encrypt_to_file),
getString(R.string.specify_file_to_encrypt_to), targetFile, REQUEST_CODE_OUTPUT);
} else {
FileHelper.saveDocument(this, "*/*", FileHelper.getFilename(getActivity(), inputUri) +
(mEncryptInterface.isUseArmor() ? ".asc" : ".gpg"), REQUEST_CODE_OUTPUT);
FileHelper.saveDocument(this, "*/*", targetName, REQUEST_CODE_OUTPUT);
}
}
private void encryptClicked(boolean share) {
if (mEncryptInterface.getInputUris().isEmpty()) {
Notify.showNotify(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR);
Notify.create(getActivity(), R.string.error_no_file_selected, Notify.Style.ERROR).show(this);
return;
}
if (share) {
mEncryptInterface.getOutputUris().clear();
int filenameCounter = 1;
for (Uri uri : mEncryptInterface.getInputUris()) {
String targetName = FileHelper.getFilename(getActivity(), uri) +
(mEncryptInterface.isUseArmor() ? ".asc" : ".gpg");
String targetName =
(mEncryptInterface.isEncryptFilenames() ? String.valueOf(filenameCounter) : FileHelper.getFilename(getActivity(), uri))
+ (mEncryptInterface.isUseArmor() ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN);
mEncryptInterface.getOutputUris().add(TemporaryStorageProvider.createFile(getActivity(), targetName));
filenameCounter++;
}
mEncryptInterface.startEncrypt(true);
} else {
if (mEncryptInterface.getInputUris().size() > 1) {
Notify.showNotify(getActivity(), R.string.error_multi_not_supported, Notify.Style.ERROR);
Notify.create(getActivity(), R.string.error_multi_not_supported, Notify.Style.ERROR).show(this);
return;
}
showOutputFileDialog();

View File

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

View File

@ -36,7 +36,7 @@ import org.sufficientlysecure.keychain.pgp.PgpConstants;
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.ShareHelper;
import java.util.ArrayList;
@ -63,16 +63,19 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
private static final int MODE_SYMMETRIC = 1;
// model used by fragments
private boolean mShareAfterEncrypt = false;
private boolean mUseCompression = true;
private boolean mHiddenRecipients = false;
private long mEncryptionKeyIds[] = null;
private String mEncryptionUserIds[] = null;
// TODO Constants.key.none? What's wrong with a null value?
private long mSigningKeyId = Constants.key.none;
private String mPassphrase = "";
private boolean mShareAfterEncrypt = false;
private Passphrase mPassphrase = new Passphrase();
private ArrayList<Uri> mInputUris;
private ArrayList<Uri> mOutputUris;
private String mMessage = "";
private boolean mUseCompression = true;
public boolean isModeSymmetric() {
return MODE_SYMMETRIC == mCurrentMode;
@ -83,11 +86,21 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
return true;
}
@Override
public boolean isEncryptFilenames() {
return false;
}
@Override
public boolean isUseCompression() {
return mUseCompression;
}
@Override
public boolean isHiddenRecipients() {
return mHiddenRecipients;
}
@Override
public long getSignatureKey() {
return mSigningKeyId;
@ -122,7 +135,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
}
@Override
public void setPassphrase(String passphrase) {
public void setPassphrase(Passphrase passphrase) {
mPassphrase.removeFromMemory();
mPassphrase = passphrase;
}
@ -184,8 +198,9 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
// Copy to clipboard
copyToClipboard(result.getResultBytes());
result.createNotify(EncryptTextActivity.this).show();
// Notify.showNotify(EncryptTextActivity.this,
// R.string.encrypt_sign_clipboard_successful, Notify.Style.INFO);
// Notify.create(EncryptTextActivity.this,
// R.string.encrypt_sign_clipboard_successful, Notify.Style.OK)
// .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
}
}
@ -202,6 +217,7 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
} else {
data.setCompressionId(CompressionAlgorithmTags.UNCOMPRESSED);
}
data.setHiddenRecipients(mHiddenRecipients);
data.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
data.setSignatureHashAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED);
@ -210,8 +226,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
if (isModeSymmetric()) {
Log.d(Constants.TAG, "Symmetric encryption enabled!");
String passphrase = mPassphrase;
if (passphrase.length() == 0) {
Passphrase passphrase = mPassphrase;
if (passphrase.isEmpty()) {
passphrase = null;
}
data.setSymmetricPassphrase(passphrase);
@ -247,15 +263,15 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
private Intent createSendIntent(byte[] resultBytes) {
Intent sendIntent;
sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("text/plain");
sendIntent.setType(Constants.ENCRYPTED_TEXT_MIME);
sendIntent.putExtra(Intent.EXTRA_TEXT, new String(resultBytes));
if (!isModeSymmetric() && mEncryptionUserIds != null) {
Set<String> users = new HashSet<>();
for (String user : mEncryptionUserIds) {
String[] userId = KeyRing.splitUserId(user);
if (userId[1] != null) {
users.add(userId[1]);
KeyRing.UserId userId = KeyRing.splitUserId(user);
if (userId.email != null) {
users.add(userId.email);
}
}
// pass trough email addresses as extra for email applications
@ -266,7 +282,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
protected boolean inputIsValid() {
if (mMessage == null) {
Notify.showNotify(this, R.string.error_message, Notify.Style.ERROR);
Notify.create(this, R.string.error_message, Notify.Style.ERROR)
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
return false;
}
@ -274,11 +291,13 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
// symmetric encryption checks
if (mPassphrase == null) {
Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR);
Notify.create(this, R.string.passphrases_do_not_match, Notify.Style.ERROR)
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
return false;
}
if (mPassphrase.isEmpty()) {
Notify.showNotify(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR);
Notify.create(this, R.string.passphrase_must_not_be_empty, Notify.Style.ERROR)
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
return false;
}
@ -289,7 +308,8 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
&& mEncryptionKeyIds.length > 0);
if (!gotEncryptionKeys && mSigningKeyId == 0) {
Notify.showNotify(this, R.string.select_encryption_or_signature_key, Notify.Style.ERROR);
Notify.create(this, R.string.select_encryption_or_signature_key, Notify.Style.ERROR)
.show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
return false;
}
}
@ -353,6 +373,11 @@ public class EncryptTextActivity extends EncryptActivity implements EncryptActiv
notifyUpdate();
break;
}
// case R.id.check_hidden_recipients: {
// mHiddenRecipients = item.isChecked();
// notifyUpdate();
// break;
// }
default: {
return super.onOptionsItemSelected(item);
}

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

@ -26,6 +26,7 @@ import android.os.Messenger;
import android.support.v4.app.Fragment;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
@ -277,7 +278,8 @@ public class ImportKeysActivity extends BaseActivity {
private boolean isFingerprintValid(String fingerprint) {
if (fingerprint == null || fingerprint.length() < 40) {
Notify.showNotify(this, R.string.import_qr_code_too_short_fingerprint, Notify.Style.ERROR);
Notify.create(this, R.string.import_qr_code_too_short_fingerprint, Notify.Style.ERROR)
.show((ViewGroup) findViewById(R.id.import_snackbar));
return false;
} else {
return true;
@ -329,7 +331,8 @@ public class ImportKeysActivity extends BaseActivity {
return;
}
result.createNotify(ImportKeysActivity.this).show();
result.createNotify(ImportKeysActivity.this)
.show((ViewGroup) findViewById(R.id.import_snackbar));
}
}
};
@ -372,7 +375,8 @@ public class ImportKeysActivity extends BaseActivity {
startService(intent);
} catch (IOException e) {
Log.e(Constants.TAG, "Problem writing cache file", e);
Notify.showNotify(this, "Problem writing cache file!", Notify.Style.ERROR);
Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR)
.show((ViewGroup) findViewById(R.id.import_snackbar));
}
} else if (ls instanceof ImportKeysListFragment.CloudLoaderState) {
ImportKeysListFragment.CloudLoaderState sls = (ImportKeysListFragment.CloudLoaderState) ls;
@ -412,7 +416,8 @@ public class ImportKeysActivity extends BaseActivity {
// start service with intent
startService(intent);
} else {
Notify.showNotify(this, R.string.error_nothing_import, Notify.Style.ERROR);
Notify.create(this, R.string.error_nothing_import, Notify.Style.ERROR)
.show((ViewGroup) findViewById(R.id.import_snackbar));
}
}

View File

@ -154,7 +154,7 @@ public class ImportKeysProxyActivity extends FragmentActivity {
String fingerprint = null;
// example: openpgp4fpr:73EE2314F65FA92EC2390D3A718C070100012282
if (uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
if (uri != null && uri.getScheme() != null && uri.getScheme().toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) {
fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
}

View File

@ -169,6 +169,22 @@ public class KeyListFragment extends LoaderFragment
mStickyList.setDrawingListUnderStickyHeader(false);
mStickyList.setFastScrollEnabled(true);
// Adds an empty footer view so that the Floating Action Button won't block content
// in last few rows.
View footer = new View(getActivity());
int spacing = (int) android.util.TypedValue.applyDimension(
android.util.TypedValue.COMPLEX_UNIT_DIP, 72, getResources().getDisplayMetrics()
);
android.widget.AbsListView.LayoutParams params = new android.widget.AbsListView.LayoutParams(
android.widget.AbsListView.LayoutParams.MATCH_PARENT,
spacing
);
footer.setLayoutParams(params);
mStickyList.addFooterView(footer, null, false);
/*
* Multi-selection
*/
@ -369,13 +385,13 @@ public class KeyListFragment extends LoaderFragment
/**
* Show dialog to delete key
*
* @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not
* @param hasSecret must contain whether the list of masterKeyIds contains a secret key or not
*/
public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) {
// Can only work on singular secret keys
if (hasSecret && masterKeyIds.length > 1) {
Notify.showNotify(getActivity(), R.string.secret_cannot_multiple,
Notify.Style.ERROR);
Notify.create(getActivity(), R.string.secret_cannot_multiple,
Notify.Style.ERROR).show();
return;
}
@ -468,28 +484,29 @@ public class KeyListFragment extends LoaderFragment
case R.id.menu_key_list_debug_read:
try {
KeychainDatabase.debugBackup(getActivity(), true);
Notify.showNotify(getActivity(), "Restored debug_backup.db", Notify.Style.INFO);
Notify.create(getActivity(), "Restored debug_backup.db", Notify.Style.OK).show();
getActivity().getContentResolver().notifyChange(KeychainContract.KeyRings.CONTENT_URI, null);
} catch (IOException e) {
Log.e(Constants.TAG, "IO Error", e);
Notify.showNotify(getActivity(), "IO Error " + e.getMessage(), Notify.Style.ERROR);
Notify.create(getActivity(), "IO Error " + e.getMessage(), Notify.Style.ERROR).show();
}
return true;
case R.id.menu_key_list_debug_write:
try {
KeychainDatabase.debugBackup(getActivity(), false);
Notify.showNotify(getActivity(), "Backup to debug_backup.db completed", Notify.Style.INFO);
Notify.create(getActivity(), "Backup to debug_backup.db completed", Notify.Style.OK).show();
} catch (IOException e) {
Log.e(Constants.TAG, "IO Error", e);
Notify.showNotify(getActivity(), "IO Error: " + e.getMessage(), Notify.Style.ERROR);
Notify.create(getActivity(), "IO Error: " + e.getMessage(), Notify.Style.ERROR).show();
}
return true;
case R.id.menu_key_list_debug_first_time:
Preferences prefs = Preferences.getPreferences(getActivity());
prefs.setFirstTime(true);
Intent intent = new Intent(getActivity(), FirstTimeActivity.class);
Intent intent = new Intent(getActivity(), CreateKeyActivity.class);
intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true);
startActivity(intent);
getActivity().finish();
return true;
@ -688,14 +705,14 @@ public class KeyListFragment extends LoaderFragment
{ // set name and stuff, common to both key types
String userId = cursor.getString(INDEX_USER_ID);
String[] userIdSplit = KeyRing.splitUserId(userId);
if (userIdSplit[0] != null) {
h.mMainUserId.setText(highlighter.highlight(userIdSplit[0]));
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
if (userIdSplit.name != null) {
h.mMainUserId.setText(highlighter.highlight(userIdSplit.name));
} else {
h.mMainUserId.setText(R.string.user_id_no_name);
}
if (userIdSplit[1] != null) {
h.mMainUserIdRest.setText(highlighter.highlight(userIdSplit[1]));
if (userIdSplit.email != null) {
h.mMainUserIdRest.setText(highlighter.highlight(userIdSplit.email));
h.mMainUserIdRest.setVisibility(View.VISIBLE);
} else {
h.mMainUserIdRest.setVisibility(View.GONE);
@ -908,5 +925,4 @@ public class KeyListFragment extends LoaderFragment
}
}

View File

@ -54,7 +54,9 @@ public class MainActivity extends MaterialNavigationDrawer implements FabContain
// if this is the first time show first time activity
Preferences prefs = Preferences.getPreferences(this);
if (prefs.isFirstTime()) {
startActivity(new Intent(this, FirstTimeActivity.class));
Intent intent = new Intent(this, CreateKeyActivity.class);
intent.putExtra(CreateKeyActivity.EXTRA_FIRST_TIME, true);
startActivity(intent);
finish();
return;
}

View File

@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
/**
@ -212,9 +213,9 @@ public class PassphraseDialogActivity extends FragmentActivity {
// the catch clause doesn't return.
try {
String mainUserId = mSecretRing.getPrimaryUserIdWithFallback();
String[] mainUserIdSplit = KeyRing.splitUserId(mainUserId);
if (mainUserIdSplit[0] != null) {
userId = mainUserIdSplit[0];
KeyRing.UserId mainUserIdSplit = KeyRing.splitUserId(mainUserId);
if (mainUserIdSplit.name != null) {
userId = mainUserIdSplit.name;
} else {
userId = getString(R.string.user_id_no_name);
}
@ -240,7 +241,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
break;
// special case: empty passphrase just returns the empty passphrase
case PASSPHRASE_EMPTY:
finishCaching("");
finishCaching(new Passphrase(""));
default:
message = "This should not happen!";
break;
@ -322,7 +323,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
positive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String passphrase = mPassphraseEditText.getText().toString();
final Passphrase passphrase = new Passphrase(mPassphraseEditText);
// Early breakout if we are dealing with a symmetric key
if (mSecretRing == null) {
@ -399,7 +400,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
});
}
private void finishCaching(String passphrase) {
private void finishCaching(Passphrase passphrase) {
// any indication this isn't needed anymore, don't do it.
if (mIsCancelled || getActivity() == null) {
return;

View File

@ -80,7 +80,7 @@ public class QrCodeViewActivity extends BaseActivity {
KeychainContract.KeyRings.FINGERPRINT, ProviderHelper.FIELD_TYPE_BLOB);
if (blob == null) {
Log.e(Constants.TAG, "key not found!");
Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
Notify.create(this, R.string.error_key_not_found, Style.ERROR).show();
ActivityCompat.finishAfterTransition(QrCodeViewActivity.this);
}
@ -102,7 +102,7 @@ public class QrCodeViewActivity extends BaseActivity {
});
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
Notify.showNotify(this, R.string.error_key_not_found, Style.ERROR);
Notify.create(this, R.string.error_key_not_found, Style.ERROR).show();
ActivityCompat.finishAfterTransition(QrCodeViewActivity.this);
}
}

View File

@ -205,7 +205,7 @@ public class SafeSlingerActivity extends BaseActivity {
activity.startService(intent);
} catch (IOException e) {
Log.e(Constants.TAG, "Problem writing cache file", e);
Notify.showNotify(activity, "Problem writing cache file!", Notify.Style.ERROR);
Notify.create(activity, "Problem writing cache file!", Notify.Style.ERROR).show();
}
} else {
// give everything else down to KeyListActivity!

View File

@ -21,18 +21,12 @@ package org.sufficientlysecure.keychain.ui;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcEvent;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@ -40,7 +34,6 @@ import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.LoaderManager;
@ -59,9 +52,7 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.getbase.floatingactionbutton.FloatingActionButton;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
@ -76,6 +67,8 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler.MessageStatus;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
@ -84,6 +77,7 @@ import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
import org.sufficientlysecure.keychain.util.ContactHelper;
import org.sufficientlysecure.keychain.util.ExportHelper;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper;
import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
@ -93,8 +87,8 @@ public class ViewKeyActivity extends BaseActivity implements
LoaderManager.LoaderCallbacks<Cursor> {
static final int REQUEST_QR_FINGERPRINT = 1;
static final int REQUEST_DELETE= 2;
static final int REQUEST_EXPORT= 3;
static final int REQUEST_DELETE = 2;
static final int REQUEST_EXPORT = 3;
ExportHelper mExportHelper;
ProviderHelper mProviderHelper;
@ -115,11 +109,7 @@ public class ViewKeyActivity extends BaseActivity implements
private CardView mQrCodeLayout;
// NFC
private NfcAdapter mNfcAdapter;
private NfcAdapter.CreateNdefMessageCallback mNdefCallback;
private NfcAdapter.OnNdefPushCompleteCallback mNdefCompleteCallback;
private byte[] mNfcKeyringBytes;
private static final int NFC_SENT = 1;
private NfcHelper mNfcHelper;
private static final int LOADER_ID_UNIFIED = 0;
@ -256,7 +246,7 @@ public class ViewKeyActivity extends BaseActivity implements
mActionNfc.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
invokeNfcBeam();
mNfcHelper.invokeNfcBeam();
}
});
@ -264,7 +254,8 @@ public class ViewKeyActivity extends BaseActivity implements
// or start new ones.
getSupportLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
initNfc(mDataUri);
mNfcHelper = new NfcHelper(this, mProviderHelper);
mNfcHelper.initNfc(mDataUri);
}
@ -291,31 +282,31 @@ public class ViewKeyActivity extends BaseActivity implements
return true;
}
case R.id.menu_key_view_export_file: {
Intent mIntent = new Intent(this,PassphraseDialogActivity.class);
long keyId=0;
try {
keyId = new ProviderHelper(this)
.getCachedPublicKeyRing(mDataUri)
.extractOrGetMasterKeyId();
} catch (PgpKeyNotFoundException e) {
e.printStackTrace();
if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) {
exportToFile(mDataUri, mExportHelper, mProviderHelper);
return true;
}
startPassphraseActivity(REQUEST_EXPORT);
} catch (PassphraseCacheService.KeyNotFoundException e) {
// This happens when the master key is stripped
exportToFile(mDataUri, mExportHelper, mProviderHelper);
}
mIntent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID,keyId);
startActivityForResult(mIntent,REQUEST_EXPORT);
return true;
}
case R.id.menu_key_view_delete: {
Intent mIntent = new Intent(this,PassphraseDialogActivity.class);
long keyId=0;
try {
keyId = new ProviderHelper(this)
.getCachedPublicKeyRing(mDataUri)
.extractOrGetMasterKeyId();
} catch (PgpKeyNotFoundException e) {
e.printStackTrace();
if (PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId, mMasterKeyId) != null) {
deleteKey();
return true;
}
startPassphraseActivity(REQUEST_DELETE);
} catch (PassphraseCacheService.KeyNotFoundException e) {
// This happens when the master key is stripped
deleteKey();
}
mIntent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID,keyId);
startActivityForResult(mIntent,REQUEST_DELETE);
return true;
}
case R.id.menu_key_view_advanced: {
@ -328,7 +319,7 @@ public class ViewKeyActivity extends BaseActivity implements
try {
updateFromKeyserver(mDataUri, mProviderHelper);
} catch (ProviderHelper.NotFoundException e) {
Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
Notify.create(this, R.string.error_key_not_found, Notify.Style.ERROR).show();
}
return true;
}
@ -364,41 +355,6 @@ public class ViewKeyActivity extends BaseActivity implements
return true;
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void invokeNfcBeam() {
// Check if device supports NFC
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
Notify.createNotify(this, R.string.no_nfc_support, Notify.LENGTH_LONG, Notify.Style.ERROR).show();
return;
}
// Check for available NFC Adapter
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter == null || !mNfcAdapter.isEnabled()) {
Notify.createNotify(this, R.string.error_nfc_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() {
@Override
public void onAction() {
Intent intentSettings = new Intent(Settings.ACTION_NFC_SETTINGS);
startActivity(intentSettings);
}
}, R.string.menu_nfc_preferences).show();
return;
}
if (!mNfcAdapter.isNdefPushEnabled()) {
Notify.createNotify(this, R.string.error_beam_needed, Notify.LENGTH_LONG, Notify.Style.ERROR, new Notify.ActionListener() {
@Override
public void onAction() {
Intent intentSettings = new Intent(Settings.ACTION_NFCSHARING_SETTINGS);
startActivity(intentSettings);
}
}, R.string.menu_beam_preferences).show();
return;
}
mNfcAdapter.invokeBeam(this);
}
private void scanQrCode() {
Intent scanQrCode = new Intent(this, ImportKeysProxyActivity.class);
@ -415,7 +371,7 @@ public class ViewKeyActivity extends BaseActivity implements
private void certifyImmediate() {
Intent intent = new Intent(this, CertifyKeyActivity.class);
intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId});
intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] {mMasterKeyId});
startCertifyIntent(intent);
}
@ -464,22 +420,32 @@ public class ViewKeyActivity extends BaseActivity implements
ActivityCompat.startActivity(this, qrCodeIntent, opts);
}
private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper)
throws ProviderHelper.NotFoundException {
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri);
HashMap<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 startPassphraseActivity(int requestCode) {
Intent intent = new Intent(this, PassphraseDialogActivity.class);
intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mMasterKeyId);
startActivityForResult(intent, requestCode);
}
private void deleteKey(Uri dataUri, ExportHelper exportHelper) {
private void exportToFile(Uri dataUri, ExportHelper exportHelper, ProviderHelper providerHelper) {
try {
Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri);
HashMap<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.create(this, R.string.error_key_not_found, Notify.Style.ERROR).show();
Log.e(Constants.TAG, "Key not found", e);
}
}
private void deleteKey() {
// Message is received after key is deleted
Handler returnHandler = new Handler() {
@Override
@ -491,7 +457,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
@ -507,32 +477,27 @@ public class ViewKeyActivity extends BaseActivity implements
String fp = data.getStringExtra(ImportKeysProxyActivity.EXTRA_FINGERPRINT);
if (fp == null) {
Notify.createNotify(this, "Error scanning fingerprint!",
Notify.create(this, "Error scanning fingerprint!",
Notify.LENGTH_LONG, Notify.Style.ERROR).show();
return;
}
if (mFingerprint.equalsIgnoreCase(fp)) {
certifyImmediate();
} else {
Notify.createNotify(this, "Fingerprints did not match!",
Notify.create(this, "Fingerprints did not match!",
Notify.LENGTH_LONG, Notify.Style.ERROR).show();
}
return;
}
if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK){
deleteKey(mDataUri, mExportHelper);
}
if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK){
try {
exportToFile(mDataUri, mExportHelper, mProviderHelper);
} catch (ProviderHelper.NotFoundException e) {
Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR);
Log.e(Constants.TAG, "Key not found", e);
}
if (requestCode == REQUEST_DELETE && resultCode == Activity.RESULT_OK) {
deleteKey();
}
if (requestCode == REQUEST_EXPORT && resultCode == Activity.RESULT_OK) {
exportToFile(mDataUri, mExportHelper, mProviderHelper);
}
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT);
@ -545,14 +510,14 @@ public class ViewKeyActivity extends BaseActivity implements
private void encrypt(Uri dataUri, boolean text) {
// If there is no encryption key, don't bother.
if (!mHasEncrypt) {
Notify.showNotify(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR);
Notify.create(this, R.string.error_no_encrypt_subkey, Notify.Style.ERROR).show();
return;
}
try {
long keyId = new ProviderHelper(this)
.getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId();
long[] encryptionKeyIds = new long[]{keyId};
long[] encryptionKeyIds = new long[] {keyId};
Intent intent;
if (text) {
intent = new Intent(this, EncryptTextActivity.class);
@ -690,98 +655,9 @@ public class ViewKeyActivity extends BaseActivity implements
loadTask.execute();
}
/**
* NFC: Initialize NFC sharing if OS and device supports it
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void initNfc(final Uri dataUri) {
// check if NFC Beam is supported (>= Android 4.1)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// Implementation for the CreateNdefMessageCallback interface
mNdefCallback = new NfcAdapter.CreateNdefMessageCallback() {
@Override
public NdefMessage createNdefMessage(NfcEvent event) {
/*
* When a device receives a push with an AAR in it, the application specified in the AAR is
* guaranteed to run. The AAR overrides the tag dispatch system. You can add it back in to
* guarantee that this activity starts when receiving a beamed message. For now, this code
* uses the tag dispatch system.
*/
return new NdefMessage(NdefRecord.createMime(Constants.NFC_MIME,
mNfcKeyringBytes), NdefRecord.createApplicationRecord(Constants.PACKAGE_NAME));
}
};
// Implementation for the OnNdefPushCompleteCallback interface
mNdefCompleteCallback = new NfcAdapter.OnNdefPushCompleteCallback() {
@Override
public void onNdefPushComplete(NfcEvent event) {
// A handler is needed to send messages to the activity when this
// callback occurs, because it happens from a binder thread
mNfcHandler.obtainMessage(NFC_SENT).sendToTarget();
}
};
// Check for available NFC Adapter
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter != null) {
/*
* Retrieve mNfcKeyringBytes here asynchronously (to not block the UI)
* and init nfc adapter afterwards.
* mNfcKeyringBytes can not be retrieved in createNdefMessage, because this process
* has no permissions to query the Uri.
*/
AsyncTask<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.
static final String[] PROJECTION = new String[]{
static final String[] PROJECTION = new String[] {
KeychainContract.KeyRings._ID,
KeychainContract.KeyRings.MASTER_KEY_ID,
KeychainContract.KeyRings.USER_ID,
@ -849,9 +725,9 @@ public class ViewKeyActivity extends BaseActivity implements
startFragment(mIsSecret, fpData);
// get name, email, and comment from USER_ID
String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
if (mainUserId[0] != null) {
mName.setText(mainUserId[0]);
KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
if (mainUserId.name != null) {
mName.setText(mainUserId.name);
} else {
mName.setText(R.string.user_id_no_name);
}
@ -892,6 +768,7 @@ public class ViewKeyActivity extends BaseActivity implements
} else if (mIsExpired) {
if (mIsSecret) {
mStatusText.setText(R.string.view_key_expired_secret);
mName.setText(mainUserId.name);
} else {
mStatusText.setText(R.string.view_key_expired);
}

View File

@ -197,9 +197,9 @@ public class ViewKeyAdvActivity extends BaseActivity implements
case LOADER_ID_UNIFIED: {
if (data.moveToFirst()) {
// get name, email, and comment from USER_ID
String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
if (mainUserId[0] != null) {
setTitle(mainUserId[0]);
KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID));
if (mainUserId.name != null) {
setTitle(mainUserId.name);
} else {
setTitle(R.string.user_id_no_name);
}

View File

@ -237,9 +237,9 @@ public class ViewKeyAdvCertsFragment extends LoaderFragment implements
TextView wSignStatus = (TextView) view.findViewById(R.id.signStatus);
String signerKeyId = KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), cursor.getLong(mIndexSignerKeyId));
String[] userId = KeyRing.splitUserId(cursor.getString(mIndexSignerUserId));
if (userId[0] != null) {
wSignerName.setText(userId[0]);
KeyRing.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexSignerUserId));
if (userId.name != null) {
wSignerName.setText(userId.name);
} else {
wSignerName.setText(R.string.user_id_no_name);
}

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.QrCodeUtils;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper;
import java.io.IOException;
@ -68,10 +69,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
private View mFingerprintClipboardButton;
private View mKeyShareButton;
private View mKeyClipboardButton;
private View mKeyNfcButton;
private ImageButton mKeySafeSlingerButton;
private View mKeyUploadButton;
ProviderHelper mProviderHelper;
NfcHelper mNfcHelper;
private static final int LOADER_ID_UNIFIED = 0;
@ -83,6 +86,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
View view = inflater.inflate(R.layout.view_key_adv_share_fragment, getContainer());
mProviderHelper = new ProviderHelper(ViewKeyAdvShareFragment.this.getActivity());
mNfcHelper = new NfcHelper(getActivity(), mProviderHelper);
mFingerprint = (TextView) view.findViewById(R.id.view_key_fingerprint);
mQrCode = (ImageView) view.findViewById(R.id.view_key_qr_code);
@ -90,6 +94,7 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
mFingerprintShareButton = view.findViewById(R.id.view_key_action_fingerprint_share);
mFingerprintClipboardButton = view.findViewById(R.id.view_key_action_fingerprint_clipboard);
mKeyShareButton = view.findViewById(R.id.view_key_action_key_share);
mKeyNfcButton = view.findViewById(R.id.view_key_action_key_nfc);
mKeyClipboardButton = view.findViewById(R.id.view_key_action_key_clipboard);
mKeySafeSlingerButton = (ImageButton) view.findViewById(R.id.view_key_action_key_safeslinger);
mKeyUploadButton = view.findViewById(R.id.view_key_action_upload);
@ -128,6 +133,14 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
share(mDataUri, mProviderHelper, false, true);
}
});
mKeyNfcButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mNfcHelper.invokeNfcBeam();
}
});
mKeySafeSlingerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -186,13 +199,13 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
} else {
message = getResources().getString(R.string.key_copied_to_clipboard);
}
Notify.showNotify(getActivity(), message, Notify.Style.OK);
Notify.create(getActivity(), message, Notify.Style.OK).show();
} else {
// Android will fail with android.os.TransactionTooLargeException if key is too big
// see http://www.lonestarprod.com/?p=34
if (content.length() >= 86389) {
Notify.showNotify(getActivity(), R.string.key_too_big_for_sharing,
Notify.Style.ERROR);
Notify.create(getActivity(), R.string.key_too_big_for_sharing,
Notify.Style.ERROR).show();
return;
}
@ -210,10 +223,10 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
}
} catch (PgpGeneralException | IOException e) {
Log.e(Constants.TAG, "error processing key!", e);
Notify.showNotify(getActivity(), R.string.error_key_processing, Notify.Style.ERROR);
Notify.create(getActivity(), R.string.error_key_processing, Notify.Style.ERROR).show();
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
Notify.showNotify(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR);
Notify.create(getActivity(), R.string.error_key_not_found, Notify.Style.ERROR).show();
}
}
@ -255,9 +268,12 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
// Prepare the loaders. Either re-connect with an existing ones,
// or start new ones.
getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this);
// Prepare the NfcHelper
mNfcHelper.initNfc(mDataUri);
}
static final String[] UNIFIED_PROJECTION = new String[]{
static final String[] UNIFIED_PROJECTION = new String[] {
KeyRings._ID, KeyRings.MASTER_KEY_ID, KeyRings.HAS_ANY_SECRET,
KeyRings.USER_ID, KeyRings.FINGERPRINT,
KeyRings.ALGORITHM, KeyRings.KEY_SIZE, KeyRings.CREATION, KeyRings.IS_EXPIRED,
@ -362,4 +378,5 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
startActivityForResult(uploadIntent, 0);
}
}

View File

@ -140,25 +140,25 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
// main user id
String userId = entry.getUserIds().get(0);
String[] userIdSplit = KeyRing.splitUserId(userId);
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
// name
if (userIdSplit[0] != null) {
if (userIdSplit.name != null) {
// show red user id if it is a secret key
if (entry.isSecretKey()) {
holder.mainUserId.setText(mActivity.getString(R.string.secret_key)
+ " " + userIdSplit[0]);
+ " " + userIdSplit.name);
} else {
holder.mainUserId.setText(highlighter.highlight(userIdSplit[0]));
holder.mainUserId.setText(highlighter.highlight(userIdSplit.name));
}
} else {
holder.mainUserId.setText(R.string.user_id_no_name);
}
// email
if (userIdSplit[1] != null) {
if (userIdSplit.email != null) {
holder.mainUserIdRest.setVisibility(View.VISIBLE);
holder.mainUserIdRest.setText(highlighter.highlight(userIdSplit[1]));
holder.mainUserIdRest.setText(highlighter.highlight(userIdSplit.email));
} else {
holder.mainUserIdRest.setVisibility(View.GONE);
}

View File

@ -33,7 +33,6 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.util.ArrayList;
@ -83,9 +82,9 @@ public class MultiUserIdsAdapter extends CursorAdapter {
{ // first one
String userId = uids.get(0);
String[] splitUserId = KeyRing.splitUserId(userId);
if (splitUserId[0] != null) {
vName.setText(splitUserId[0]);
KeyRing.UserId splitUserId = KeyRing.splitUserId(userId);
if (splitUserId.name != null) {
vName.setText(splitUserId.name);
} else {
vName.setText(R.string.user_id_no_name);
}
@ -93,9 +92,9 @@ public class MultiUserIdsAdapter extends CursorAdapter {
if (isHeader == 1) {
vHeaderId.setVisibility(View.VISIBLE);
String message;
if (splitUserId[0] != null) {
if (splitUserId.name != null) {
message = mContext.getString(R.string.section_uids_to_certify) +
splitUserId[0];
splitUserId.name;
} else {
message = mContext.getString(R.string.section_uids_to_certify) +
context.getString(R.string.user_id_no_name);
@ -108,13 +107,13 @@ public class MultiUserIdsAdapter extends CursorAdapter {
StringBuilder lines = new StringBuilder();
for (String uid : uids) {
String[] splitUserId = KeyRing.splitUserId(uid);
if (splitUserId[1] == null) {
KeyRing.UserId splitUserId = KeyRing.splitUserId(uid);
if (splitUserId.email == null) {
continue;
}
lines.append(splitUserId[1]);
if (splitUserId[2] != null) {
lines.append(" (").append(splitUserId[2]).append(")");
lines.append(splitUserId.email);
if (splitUserId.comment != null) {
lines.append(" (").append(splitUserId.comment).append(")");
}
lines.append('\n');
}

View File

@ -122,16 +122,16 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter {
ViewHolderItem h = (ViewHolderItem) view.getTag();
String userId = cursor.getString(mIndexUserId);
String[] userIdSplit = KeyRing.splitUserId(userId);
KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId);
if (userIdSplit[0] != null) {
h.mainUserId.setText(highlighter.highlight(userIdSplit[0]));
if (userIdSplit.name != null) {
h.mainUserId.setText(highlighter.highlight(userIdSplit.name));
} else {
h.mainUserId.setText(R.string.user_id_no_name);
}
if (userIdSplit[1] != null) {
if (userIdSplit.email != null) {
h.mainUserIdRest.setVisibility(View.VISIBLE);
h.mainUserIdRest.setText(highlighter.highlight(userIdSplit[1]));
h.mainUserIdRest.setText(highlighter.highlight(userIdSplit.email));
} else {
h.mainUserIdRest.setVisibility(View.GONE);
}

View File

@ -72,20 +72,20 @@ public class UserIdsAdapter extends UserAttributesAdapter {
vDeleteButton.setVisibility(View.GONE); // not used
String userId = cursor.getString(INDEX_USER_ID);
String[] splitUserId = KeyRing.splitUserId(userId);
if (splitUserId[0] != null) {
vName.setText(splitUserId[0]);
KeyRing.UserId splitUserId = KeyRing.splitUserId(userId);
if (splitUserId.name != null) {
vName.setText(splitUserId.name);
} else {
vName.setText(R.string.user_id_no_name);
}
if (splitUserId[1] != null) {
vAddress.setText(splitUserId[1]);
if (splitUserId.email != null) {
vAddress.setText(splitUserId.email);
vAddress.setVisibility(View.VISIBLE);
} else {
vAddress.setVisibility(View.GONE);
}
if (splitUserId[2] != null) {
vComment.setText(splitUserId[2]);
if (splitUserId.comment != null) {
vComment.setText(splitUserId.comment);
vComment.setVisibility(View.VISIBLE);
} else {
vComment.setVisibility(View.GONE);

View File

@ -92,20 +92,20 @@ public class UserIdsAddedAdapter extends ArrayAdapter<String> {
// save reference to model item
holder.mModel = getItem(position);
String[] splitUserId = KeyRing.splitUserId(holder.mModel);
if (splitUserId[0] != null) {
holder.vName.setText(splitUserId[0]);
KeyRing.UserId splitUserId = KeyRing.splitUserId(holder.mModel);
if (splitUserId.name != null) {
holder.vName.setText(splitUserId.name);
} else {
holder.vName.setText(R.string.user_id_no_name);
}
if (splitUserId[1] != null) {
holder.vAddress.setText(splitUserId[1]);
if (splitUserId.email != null) {
holder.vAddress.setText(splitUserId.email);
holder.vAddress.setVisibility(View.VISIBLE);
} else {
holder.vAddress.setVisibility(View.GONE);
}
if (splitUserId[2] != null) {
holder.vComment.setText(splitUserId[2]);
if (splitUserId.comment != null) {
holder.vComment.setText(splitUserId.comment);
holder.vComment.setVisibility(View.VISIBLE);
} else {
holder.vComment.setVisibility(View.GONE);

View File

@ -100,8 +100,8 @@ public class AddUserIdDialogFragment extends DialogFragment implements OnEditorA
// return new user id back to activity
Bundle data = new Bundle();
String userId = KeyRing.createUserId(mName.getText().toString(),
mEmail.getText().toString(), mComment.getText().toString());
String userId = KeyRing.createUserId(new KeyRing.UserId(mName.getText().toString(),
mEmail.getText().toString(), mComment.getText().toString()));
data.putString(MESSAGE_DATA_USER_ID, userId);
sendMessageToHandler(MESSAGE_OKAY, data);
}

View File

@ -100,9 +100,9 @@ public class DeleteKeyDialogFragment extends DialogFragment {
}
);
String name;
String[] mainUserId = KeyRing.splitUserId((String) data.get(KeyRings.USER_ID));
if (mainUserId[0] != null) {
name = mainUserId[0];
KeyRing.UserId mainUserId = KeyRing.splitUserId((String) data.get(KeyRings.USER_ID));
if (mainUserId.name != null) {
name = mainUserId.name;
} else {
name = getString(R.string.user_id_no_name);
}

View File

@ -190,7 +190,7 @@ public class FileDialogFragment extends DialogFragment {
mFile = file;
mFilename.setText(mFile.getName());
} else {
Notify.showNotify(getActivity(), R.string.no_file_selected, Notify.Style.ERROR);
Notify.create(getActivity(), R.string.no_file_selected, Notify.Style.ERROR).show();
}
}

View File

@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.widget.PassphraseEditText;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
public class SetPassphraseDialogFragment extends DialogFragment implements OnEditorActionListener {
private static final String ARG_MESSENGER = "messenger";
@ -67,12 +68,12 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
* @param messenger to communicate back after setting the passphrase
* @return
*/
public static SetPassphraseDialogFragment newInstance(Messenger messenger, String oldPassphrase, int title) {
public static SetPassphraseDialogFragment newInstance(Messenger messenger, Passphrase oldPassphrase, int title) {
SetPassphraseDialogFragment frag = new SetPassphraseDialogFragment();
Bundle args = new Bundle();
args.putInt(ARG_TITLE, title);
args.putParcelable(ARG_MESSENGER, messenger);
args.putString(ARG_OLD_PASSPHRASE, oldPassphrase);
args.putParcelable(ARG_OLD_PASSPHRASE, oldPassphrase);
frag.setArguments(args);
@ -88,7 +89,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
int title = getArguments().getInt(ARG_TITLE);
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
String oldPassphrase = getArguments().getString(ARG_OLD_PASSPHRASE);
Passphrase oldPassphrase = getArguments().getParcelable(ARG_OLD_PASSPHRASE);
CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(activity);
@ -103,7 +104,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
mNoPassphraseCheckBox = (CheckBox) view.findViewById(R.id.passphrase_no_passphrase);
if (TextUtils.isEmpty(oldPassphrase)) {
if (oldPassphrase.isEmpty()) {
mNoPassphraseCheckBox.setChecked(true);
mPassphraseEditText.setEnabled(false);
mPassphraseAgainEditText.setEnabled(false);
@ -123,12 +124,12 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
public void onClick(DialogInterface dialog, int id) {
dismiss();
String passphrase1;
Passphrase passphrase1 = new Passphrase();
if (mNoPassphraseCheckBox.isChecked()) {
passphrase1 = "";
passphrase1.setEmpty();
} else {
passphrase1 = mPassphraseEditText.getText().toString();
String passphrase2 = mPassphraseAgainEditText.getText().toString();
passphrase1 = new Passphrase(mPassphraseEditText);
Passphrase passphrase2 = new Passphrase(mPassphraseAgainEditText);
if (!passphrase1.equals(passphrase2)) {
Toast.makeText(
activity,
@ -139,7 +140,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
return;
}
if (passphrase1.equals("")) {
if (passphrase1.isEmpty()) {
Toast.makeText(
activity,
getString(R.string.error_message,
@ -152,7 +153,7 @@ public class SetPassphraseDialogFragment extends DialogFragment implements OnEdi
// return resulting data back to activity
Bundle data = new Bundle();
data.putString(MESSAGE_NEW_PASSPHRASE, passphrase1);
data.putParcelable(MESSAGE_NEW_PASSPHRASE, passphrase1);
sendMessageToHandler(MESSAGE_OKAY, data);
}

View File

@ -118,7 +118,7 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment
private void proofToClipboard() {
ClipboardReflection.copyToClipboard(getActivity(), mResourceString);
Notify.showNotify(getActivity(), R.string.linked_text_clipboard, Notify.Style.OK);
Notify.create(getActivity(), R.string.linked_text_clipboard, Notify.Style.OK).show();
}
private void saveFile(Uri uri) {
@ -127,10 +127,10 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment
new PrintWriter(getActivity().getContentResolver().openOutputStream(uri));
out.print(mResourceString);
if (out.checkError()) {
Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR);
Notify.create(getActivity(), "Error writing file!", Style.ERROR).show();
}
} catch (FileNotFoundException e) {
Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR);
Notify.create(getActivity(), "File could not be opened for writing!", Style.ERROR).show();
e.printStackTrace();
}
}

View File

@ -168,7 +168,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment {
private void startCertify() {
if (mVerifiedResource == null) {
Notify.showNotify(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR);
Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR).show();
return;
}

View File

@ -80,14 +80,14 @@ public class LinkedIdCreateGithubStep1Fragment extends Fragment {
super.onPostExecute(result);
if (result == null) {
Notify.showNotify(getActivity(),
"Connection error while checking username!", Notify.Style.ERROR);
Notify.create(getActivity(),
"Connection error while checking username!", Notify.Style.ERROR).show();
return;
}
if (!result) {
Notify.showNotify(getActivity(),
"This handle does not exist on Github!", Notify.Style.ERROR);
Notify.create(getActivity(),
"This handle does not exist on Github!", Notify.Style.ERROR).show();
return;
}

View File

@ -125,7 +125,7 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen
private void proofSave () {
String state = Environment.getExternalStorageState();
if (!Environment.MEDIA_MOUNTED.equals(state)) {
Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR);
Notify.create(getActivity(), "External storage not available!", Style.ERROR);
return;
}
@ -146,11 +146,10 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen
new PrintWriter(getActivity().getContentResolver().openOutputStream(uri));
out.print(mResourceString);
if (out.checkError()) {
Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR);
Notify.create(getActivity(), "Error writing file!", Style.ERROR).show();
}
} catch (FileNotFoundException e) {
Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR);
e.printStackTrace();
Notify.create(getActivity(), "File could not be opened for writing!", Style.ERROR).show();
}
}

View File

@ -78,13 +78,13 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment {
super.onPostExecute(result);
if (result == null) {
Notify.showNotify(getActivity(),
Notify.create(getActivity(),
"Connection error while checking username!", Notify.Style.ERROR);
return;
}
if (!result) {
Notify.showNotify(getActivity(),
Notify.create(getActivity(),
"This handle does not exist on Twitter!", Notify.Style.ERROR);
return;
}

View File

@ -55,6 +55,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.ui.widget.CertListWidget;
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
public class LinkedIdViewFragment extends Fragment implements
@ -153,7 +154,7 @@ public class LinkedIdViewFragment extends Fragment implements
} catch (IOException e) {
Log.e(Constants.TAG, "error parsing identity", e);
Notify.createNotify(getActivity(), "Error parsing identity!",
Notify.create(getActivity(), "Error parsing identity!",
Notify.LENGTH_LONG, Style.ERROR).show();
finishFragment();
}
@ -492,7 +493,7 @@ public class LinkedIdViewFragment extends Fragment implements
}
// get the user's passphrase for this key (if required)
String passphrase;
Passphrase passphrase;
long certifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId();
try {
passphrase = PassphraseCacheService.getCachedPassphrase(

View File

@ -18,7 +18,9 @@
package org.sufficientlysecure.keychain.ui.util;
import android.app.Activity;
import android.content.res.Resources;
import android.support.v4.app.Fragment;
import android.view.View;
import android.view.ViewGroup;
import com.nispok.snackbar.Snackbar;
import com.nispok.snackbar.Snackbar.SnackbarDuration;
@ -35,135 +37,58 @@ import org.sufficientlysecure.keychain.util.FabContainer;
*/
public class Notify {
public static enum Style {OK, WARN, INFO, ERROR}
public static enum Style {
OK, WARN, ERROR;
public void applyToBar(Snackbar bar) {
switch (this) {
case OK:
// bar.actionColorResource(R.color.android_green_light);
bar.lineColorResource(R.color.android_green_light);
break;
case WARN:
// bar.textColorResource(R.color.android_orange_light);
bar.lineColorResource(R.color.android_orange_light);
break;
case ERROR:
// bar.textColorResource(R.color.android_red_light);
bar.lineColorResource(R.color.android_red_light);
break;
}
}
}
public static final int LENGTH_INDEFINITE = 0;
public static final int LENGTH_LONG = 3500;
/**
* Shows a simple in-layout notification with the CharSequence given as parameter
* @param text Text to show
* @param style Notification styling
*/
public static void showNotify(final Activity activity, CharSequence text, Style style) {
Snackbar bar = getSnackbar(activity)
public static Showable create(final Activity activity, String text, int duration, Style style,
final ActionListener actionListener, int actionResId) {
final Snackbar snackbar = Snackbar.with(activity)
.type(SnackbarType.MULTI_LINE)
.text(text);
switch (style) {
case OK:
break;
case WARN:
bar.textColor(activity.getResources().getColor(R.color.android_orange_light));
break;
case ERROR:
bar.textColor(activity.getResources().getColor(R.color.android_red_light));
break;
}
SnackbarManager.show(bar);
}
public static Showable createNotify (Activity activity, int resId, int duration, Style style) {
final Snackbar bar = getSnackbar(activity)
.text(resId);
if (duration == LENGTH_INDEFINITE) {
bar.duration(SnackbarDuration.LENGTH_INDEFINITE);
snackbar.duration(SnackbarDuration.LENGTH_INDEFINITE);
} else {
bar.duration(duration);
snackbar.duration(duration);
}
switch (style) {
case OK:
bar.actionColor(activity.getResources().getColor(R.color.android_green_light));
break;
case WARN:
bar.textColor(activity.getResources().getColor(R.color.android_orange_light));
break;
case ERROR:
bar.textColor(activity.getResources().getColor(R.color.android_red_light));
break;
style.applyToBar(snackbar);
if (actionListener != null) {
snackbar.actionLabel(actionResId)
.actionListener(new ActionClickListener() {
@Override
public void onActionClicked(Snackbar snackbar) {
actionListener.onAction();
}
});
}
return new Showable () {
@Override
public void show() {
SnackbarManager.show(bar);
}
};
}
public static Showable createNotify(Activity activity, int resId, int duration, Style style,
final ActionListener listener, int resIdAction) {
return createNotify(activity, activity.getString(resId), duration, style, listener, resIdAction);
}
public static Showable createNotify(Activity activity, String msg, int duration, Style style) {
return createNotify(activity, msg, duration, style, null, 0);
}
public static Showable createNotify(Activity activity, String msg, int duration, Style style,
final ActionListener listener, int resIdAction) {
final Snackbar bar = getSnackbar(activity)
.text(msg);
if (listener != null) {
bar.actionLabel(resIdAction);
bar.actionListener(new ActionClickListener() {
@Override
public void onActionClicked(Snackbar snackbar) {
listener.onAction();
}
});
}
if (duration == LENGTH_INDEFINITE) {
bar.duration(SnackbarDuration.LENGTH_INDEFINITE);
} else {
bar.duration(duration);
}
switch (style) {
case OK:
bar.actionColor(activity.getResources().getColor(R.color.android_green_light));
break;
case WARN:
bar.textColor(activity.getResources().getColor(R.color.android_orange_light));
break;
case ERROR:
bar.textColor(activity.getResources().getColor(R.color.android_red_light));
break;
}
return new Showable () {
@Override
public void show() {
SnackbarManager.show(bar);
}
};
}
/**
* Shows a simple in-layout notification with the resource text from given id
* @param resId ResourceId of notification text
* @param style Notification styling
* @throws Resources.NotFoundException
*/
public static void showNotify(Activity activity, int resId, Style style) throws Resources.NotFoundException {
showNotify(activity, activity.getResources().getText(resId), style);
}
private static Snackbar getSnackbar(final Activity activity) {
Snackbar bar = Snackbar.with(activity)
.type(SnackbarType.MULTI_LINE)
.duration(SnackbarDuration.LENGTH_LONG);
if (activity instanceof FabContainer) {
bar.eventListener(new EventListenerAdapter() {
snackbar.eventListener(new EventListenerAdapter() {
@Override
public void onShow(Snackbar snackbar) {
((FabContainer) activity).fabMoveUp(snackbar.getHeight());
@ -175,15 +100,82 @@ public class Notify {
}
});
}
return bar;
return new Showable() {
@Override
public void show() {
SnackbarManager.show(snackbar, activity);
}
@Override
public void show(Fragment fragment) {
if (fragment != null) {
View view = fragment.getView();
if (view != null && view instanceof ViewGroup) {
SnackbarManager.show(snackbar, (ViewGroup) view);
return;
}
}
show();
}
@Override
public void show(ViewGroup viewGroup) {
if (viewGroup != null) {
SnackbarManager.show(snackbar, viewGroup);
return;
}
show();
}
};
}
public static Showable create(Activity activity, String text, int duration, Style style) {
return create(activity, text, duration, style, null, -1);
}
public static Showable create(Activity activity, String text, Style style) {
return create(activity, text, LENGTH_LONG, style);
}
public static Showable create(Activity activity, int textResId, int duration, Style style,
ActionListener actionListener, int actionResId) {
return create(activity, activity.getString(textResId), duration, style, actionListener, actionResId);
}
public static Showable create(Activity activity, int textResId, int duration, Style style) {
return create(activity, activity.getString(textResId), duration, style);
}
public static Showable create(Activity activity, int textResId, Style style) {
return create(activity, activity.getString(textResId), style);
}
public interface Showable {
/**
* Shows the notification on the bottom of the Activity.
*/
public void show();
/**
* Shows the notification on the bottom of the Fragment.
*/
public void show(Fragment fragment);
/**
* Shows the notification on the given ViewGroup.
* The viewGroup should be either a RelativeLayout or FrameLayout.
*/
public void show(ViewGroup viewGroup);
}
public interface ActionListener {
public void onAction();
}

View File

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

View File

@ -184,7 +184,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
public class EncryptionKey {
private String mUserIdFull;
private String[] mUserId;
private KeyRing.UserId mUserId;
private long mKeyId;
private boolean mHasDuplicate;
private Date mCreation;
@ -222,23 +222,23 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
}
public String getPrimary() {
if (mUserId[0] != null) {
return mUserId[0];
if (mUserId.name != null) {
return mUserId.name;
} else {
return mUserId[1];
return mUserId.email;
}
}
public String getSecondary() {
if (mUserId[1] != null) {
return mUserId[1];
if (mUserId.email != null) {
return mUserId.email;
} else {
return getCreationDate();
}
}
public String getTertiary() {
if (mUserId[0] != null) {
if (mUserId.name != null) {
return getCreationDate();
} else {
return null;

View File

@ -39,7 +39,6 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log;
import java.util.Calendar;
@ -158,9 +157,9 @@ public abstract class KeySpinner extends TintSpinner implements LoaderManager.Lo
TextView vKeyEmail = (TextView) view.findViewById(R.id.keyspinner_key_email);
TextView vDuplicate = (TextView) view.findViewById(R.id.keyspinner_duplicate);
String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId));
vKeyName.setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")"));
vKeyEmail.setText(userId[1]);
KeyRing.UserId userId = KeyRing.splitUserId(cursor.getString(mIndexUserId));
vKeyName.setText(userId.name);
vKeyEmail.setText(userId.email);
boolean duplicate = cursor.getLong(mIndexDuplicate) > 0;
if (duplicate) {

View File

@ -50,7 +50,7 @@ public class NameEditText extends AutoCompleteTextView {
}
private void init() {
removeFlag();
reenableKeyboardSuggestions();
initAdapter();
}
@ -62,10 +62,10 @@ public class NameEditText extends AutoCompleteTextView {
}
/**
* Hack to re-enable keyboard auto correction in AutoCompleteTextView.
* Hack to re-enable keyboard suggestions in AutoCompleteTextView.
* From http://stackoverflow.com/a/22512858
*/
private void removeFlag() {
private void reenableKeyboardSuggestions() {
int inputType = getInputType();
inputType &= ~EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE;
setRawInputType(inputType);

View File

@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.util;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.TargetApi;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
@ -28,7 +27,6 @@ import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.provider.ContactsContract;
import android.util.Patterns;
@ -37,7 +35,6 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import java.io.InputStream;
import java.util.ArrayList;
@ -303,10 +300,9 @@ public class ContactHelper {
return new ArrayList<>(names);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public static Uri dataUriFromContactUri(Context context, Uri contactUri) {
Cursor contactMasterKey = context.getContentResolver().query(contactUri,
new String[]{ContactsContract.Data.DATA2}, null, null, null, null);
new String[]{ContactsContract.Data.DATA2}, null, null, null);
if (contactMasterKey != null) {
if (contactMasterKey.moveToNext()) {
return KeychainContract.KeyRings.buildGenericKeyRingUri(contactMasterKey.getLong(0));
@ -447,7 +443,7 @@ public class ContactHelper {
if (cursor != null) {
while (cursor.moveToNext()) {
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID));
KeyRing.UserId userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID));
boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0;
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0;
@ -470,19 +466,19 @@ public class ContactHelper {
if (rawContactId != -1) {
deleteRawContactById(resolver, rawContactId);
}
} else if (userIdSplit[0] != null) {
} else if (userIdSplit.name != null) {
// Create a new rawcontact with corresponding key if it does not exist yet
if (rawContactId == -1) {
Log.d(Constants.TAG, "Insert new raw contact with masterKeyId " + masterKeyId);
insertContact(ops, context, masterKeyId);
writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit[0]);
writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit.name);
}
// We always update the display name (which is derived from primary user id)
// and email addresses from user id
writeContactDisplayName(ops, rawContactId, userIdSplit[0]);
writeContactDisplayName(ops, rawContactId, userIdSplit.name);
writeContactEmail(ops, resolver, rawContactId, masterKeyId);
try {
resolver.applyBatch(ContactsContract.AUTHORITY, ops);
@ -521,9 +517,9 @@ public class ContactHelper {
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0;
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
String[] userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID));
KeyRing.UserId userIdSplit = KeyRing.splitUserId(cursor.getString(INDEX_USER_ID));
if (!isExpired && !isRevoked && userIdSplit[0] != null) {
if (!isExpired && !isRevoked && userIdSplit.name != null) {
// if expired or revoked will not be removed from keysToDelete or inserted
// into main profile ("me" contact)
boolean existsInMainProfile = keysToDelete.remove(masterKeyId);
@ -534,7 +530,7 @@ public class ContactHelper {
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
insertMainProfileRawContact(ops, masterKeyId);
writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit[0]);
writeContactKey(ops, context, rawContactId, masterKeyId, userIdSplit.name);
try {
resolver.applyBatch(ContactsContract.AUTHORITY, ops);
@ -715,7 +711,6 @@ public class ContactHelper {
*
* @return raw contact id or -1 if not found
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private static long findRawContactId(ContentResolver resolver, long masterKeyId) {
long rawContactId = -1;
Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI,
@ -725,7 +720,7 @@ public class ContactHelper {
ContactsContract.RawContacts.ACCOUNT_TYPE + "=? AND " + ContactsContract.RawContacts.SOURCE_ID + "=?",
new String[]{
Constants.ACCOUNT_TYPE, Long.toString(masterKeyId)
}, null, null);
}, null);
if (raw != null) {
if (raw.moveToNext()) {
rawContactId = raw.getLong(0);
@ -776,14 +771,14 @@ public class ContactHelper {
null, null);
if (ids != null) {
while (ids.moveToNext()) {
String[] userId = KeyRing.splitUserId(ids.getString(0));
if (userId[1] != null) {
KeyRing.UserId userId = KeyRing.splitUserId(ids.getString(0));
if (userId.email != null) {
ops.add(referenceRawContact(
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI),
rawContactId)
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Email.DATA, userId[1])
.withValue(ContactsContract.CommonDataKinds.Email.DATA, userId.email)
.build());
}
}

View File

@ -47,21 +47,6 @@ public class ExportHelper {
this.mActivity = activity;
}
public void deleteKey(Uri dataUri, Handler deleteHandler) {
try {
long masterKeyId = new ProviderHelper(mActivity).getCachedPublicKeyRing(dataUri)
.extractOrGetMasterKeyId();
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(deleteHandler);
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
new long[]{ masterKeyId });
deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog");
} catch (PgpKeyNotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
}
/**
* Show dialog where to export keys
*/

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.create(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.create(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.create(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.create(activity, R.string.nfc_successful, Notify.Style.OK).show();
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

Some files were not shown because too many files have changed in this diff Show More