aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2014-08-28 11:00:18 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2014-08-28 11:00:18 +0200
commitc0ebc926117d3e444c7c32bf3251880852000df6 (patch)
treeb84926b3eee5cf1cfd6d38a2c3bec23e8f0b6779 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain
parentad69e47cec58287d82978b28416db50f6c3feb77 (diff)
parentb193965e585d0d492cf2744e3c86ecc9e45b71d4 (diff)
downloadopen-keychain-c0ebc926117d3e444c7c32bf3251880852000df6.tar.gz
open-keychain-c0ebc926117d3e444c7c32bf3251880852000df6.tar.bz2
open-keychain-c0ebc926117d3e444c7c32bf3251880852000df6.zip
Merge branch 'master' into yubikey
Conflicts: OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java38
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java31
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java63
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java45
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java71
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java40
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java131
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java751
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java92
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java24
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java28
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java38
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java19
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java323
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java150
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java5
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java12
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java23
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java56
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java92
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java154
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java77
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java58
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java6
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java30
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java72
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java99
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java65
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java21
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java150
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java169
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java14
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java27
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java18
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java75
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java41
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java3
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java9
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java31
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java17
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java334
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java85
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java135
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java8
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java77
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java232
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java62
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java65
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java13
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java33
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressFixedScaler.java29
77 files changed, 3041 insertions, 1309 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 755d74ac2..9f84da815 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -35,6 +35,9 @@ public final class Constants {
public static final String PACKAGE_NAME = "org.sufficientlysecure.keychain";
+ public static final String ACCOUNT_NAME = "OpenKeychain";
+ public static final String ACCOUNT_TYPE = PACKAGE_NAME + ".account";
+
// as defined in http://tools.ietf.org/html/rfc3156, section 7
public static final String NFC_MIME = "application/pgp-keys";
@@ -68,11 +71,14 @@ public final class Constants {
public static final String KEY_SERVERS_DEFAULT_VERSION = "keyServersDefaultVersion";
public static final String WRITE_VERSION_HEADER = "writeVersionHeader";
public static final String FIRST_TIME = "firstTime";
+ public static final String CACHED_CONSOLIDATE = "cachedConsolidate";
+ public static final String CACHED_CONSOLIDATE_SECRETS = "cachedConsolidateSecrets";
+ public static final String CACHED_CONSOLIDATE_PUBLICS = "cachedConsolidatePublics";
}
public static final class Defaults {
- public static final String KEY_SERVERS = "hkps://hkps.pool.sks-keyservers.net, subkeys.pgp.net, hkps://pgp.mit.edu";
- public static final int KEY_SERVERS_VERSION = 2;
+ public static final String KEY_SERVERS = "hkps://hkps.pool.sks-keyservers.net, hkps://pgp.mit.edu";
+ public static final int KEY_SERVERS_VERSION = 3;
}
public static final class DrawerItems {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
index 9b9880533..57d74967b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java
@@ -20,15 +20,20 @@ package org.sufficientlysecure.keychain;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Application;
+import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Environment;
+import android.provider.ContactsContract;
import org.spongycastle.jce.provider.BouncyCastleProvider;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.helper.TlsHelper;
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
+import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PRNGFixes;
@@ -89,14 +94,37 @@ public class KeychainApplication extends Application {
TlsHelper.addStaticCA("pool.sks-keyservers.net", getAssets(), "sks-keyservers.netCA.cer");
TemporaryStorageProvider.cleanUp(this);
+
+ checkConsolidateRecovery();
+
+ }
+
+ public void checkConsolidateRecovery() {
+
+ // restart consolidate process if it has been interruped before
+ if (Preferences.getPreferences(this).getCachedConsolidate()) {
+ // do something which calls ProviderHelper.consolidateDatabaseStep2 with a progressable
+ Intent consolidateIntent = new Intent(this, ConsolidateDialogActivity.class);
+ consolidateIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(consolidateIntent);
+ }
+
}
public static void setupAccountAsNeeded(Context context) {
- AccountManager manager = AccountManager.get(context);
- Account[] accounts = manager.getAccountsByType(Constants.PACKAGE_NAME);
- if (accounts == null || accounts.length == 0) {
- Account dummy = new Account(context.getString(R.string.app_name), Constants.PACKAGE_NAME);
- manager.addAccountExplicitly(dummy, null, null);
+ // 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) {
+ 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!");
+ }
+ }
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java
index 8697e49f7..016d6dc14 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/ContactHelper.java
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.helper;
import android.accounts.Account;
import android.accounts.AccountManager;
+import android.annotation.TargetApi;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -234,6 +235,25 @@ public class ContactHelper {
return new ArrayList<String>(mails);
}
+ public static List<String> getContactNames(Context context) {
+ ContentResolver resolver = context.getContentResolver();
+ Cursor cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI,
+ new String[]{ContactsContract.Contacts.DISPLAY_NAME},
+ null, null, null);
+ if (cursor == null) return null;
+
+ Set<String> names = new HashSet<String>();
+ while (cursor.moveToNext()) {
+ String name = cursor.getString(0);
+ if (name != null) {
+ names.add(name);
+ }
+ }
+ cursor.close();
+ return new ArrayList<String>(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);
@@ -323,7 +343,7 @@ public class ContactHelper {
// Delete fingerprints that are no longer present in OK
for (String fingerprint : contactFingerprints) {
resolver.delete(ContactsContract.RawContacts.CONTENT_URI, ACCOUNT_TYPE_AND_SOURCE_ID_SELECTION,
- new String[]{Constants.PACKAGE_NAME, fingerprint});
+ new String[]{Constants.ACCOUNT_TYPE, fingerprint});
}
}
@@ -334,7 +354,7 @@ public class ContactHelper {
private static Set<String> getRawContactFingerprints(ContentResolver resolver) {
HashSet<String> result = new HashSet<String>();
Cursor fingerprints = resolver.query(ContactsContract.RawContacts.CONTENT_URI, SOURCE_ID_PROJECTION,
- ACCOUNT_TYPE_SELECTION, new String[]{Constants.PACKAGE_NAME}, null);
+ ACCOUNT_TYPE_SELECTION, new String[]{Constants.ACCOUNT_TYPE}, null);
if (fingerprints != null) {
while (fingerprints.moveToNext()) {
result.add(fingerprints.getString(0));
@@ -349,10 +369,11 @@ public class ContactHelper {
*
* @return raw contact id or -1 if not found
*/
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private static int findRawContactId(ContentResolver resolver, String fingerprint) {
int rawContactId = -1;
Cursor raw = resolver.query(ContactsContract.RawContacts.CONTENT_URI, ID_PROJECTION,
- ACCOUNT_TYPE_AND_SOURCE_ID_SELECTION, new String[]{Constants.PACKAGE_NAME, fingerprint}, null, null);
+ ACCOUNT_TYPE_AND_SOURCE_ID_SELECTION, new String[]{Constants.ACCOUNT_TYPE, fingerprint}, null, null);
if (raw != null) {
if (raw.moveToNext()) {
rawContactId = raw.getInt(0);
@@ -367,8 +388,8 @@ public class ContactHelper {
*/
private static void insertContact(ArrayList<ContentProviderOperation> ops, Context context, String fingerprint) {
ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
- .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, context.getString(R.string.app_name))
- .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.PACKAGE_NAME)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, Constants.ACCOUNT_NAME)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE)
.withValue(ContactsContract.RawContacts.SOURCE_ID, fingerprint)
.build());
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
index 72e88d793..5190a550e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java
@@ -25,7 +25,12 @@ import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.Constants.Pref;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.ListIterator;
import java.util.Vector;
/**
@@ -130,6 +135,36 @@ public class Preferences {
editor.commit();
}
+ public boolean getCachedConsolidate() {
+ return mSharedPreferences.getBoolean(Pref.CACHED_CONSOLIDATE, false);
+ }
+
+ public void setCachedConsolidate(boolean value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Pref.CACHED_CONSOLIDATE, value);
+ editor.commit();
+ }
+
+ public int getCachedConsolidateNumPublics() {
+ return mSharedPreferences.getInt(Pref.CACHED_CONSOLIDATE_PUBLICS, -1);
+ }
+
+ public void setCachedConsolidateNumPublics(int value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putInt(Pref.CACHED_CONSOLIDATE_PUBLICS, value);
+ editor.commit();
+ }
+
+ public int getCachedConsolidateNumSecrets() {
+ return mSharedPreferences.getInt(Pref.CACHED_CONSOLIDATE_SECRETS, -1);
+ }
+
+ public void setCachedConsolidateNumSecrets(int value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putInt(Pref.CACHED_CONSOLIDATE_SECRETS, value);
+ editor.commit();
+ }
+
public boolean isFirstTime() {
return mSharedPreferences.getBoolean(Constants.Pref.FIRST_TIME, true);
}
@@ -175,15 +210,24 @@ public class Preferences {
// migrate keyserver to hkps
if (mSharedPreferences.getInt(Constants.Pref.KEY_SERVERS_DEFAULT_VERSION, 0) !=
Constants.Defaults.KEY_SERVERS_VERSION) {
- String[] servers = getKeyServers();
- for (int i = 0; i < servers.length; i++) {
- if (servers[i].equals("pool.sks-keyservers.net")) {
- servers[i] = "hkps://hkps.pool.sks-keyservers.net";
- } else if (servers[i].equals("pgp.mit.edu")) {
- servers[i] = "hkps://pgp.mit.edu";
+ String[] serversArray = getKeyServers();
+ ArrayList<String> servers = new ArrayList<String>(Arrays.asList(serversArray));
+ ListIterator<String> it = servers.listIterator();
+ while (it.hasNext()) {
+ String server = it.next();
+ if (server.equals("pool.sks-keyservers.net")) {
+ // use HKPS!
+ it.set("hkps://hkps.pool.sks-keyservers.net");
+ } else if (server.equals("pgp.mit.edu")) {
+ // use HKPS!
+ it.set("hkps://pgp.mit.edu");
+ } else if (server.equals("subkeys.pgp.net")) {
+ // remove, because often down and no HKPS!
+ it.remove();
}
+
}
- setKeyServers(servers);
+ setKeyServers(servers.toArray(new String[servers.size()]));
mSharedPreferences.edit()
.putInt(Constants.Pref.KEY_SERVERS_DEFAULT_VERSION, Constants.Defaults.KEY_SERVERS_VERSION)
.commit();
@@ -193,6 +237,11 @@ public class Preferences {
if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_FILE_COMPRESSION, 0) == 0x21070001) {
setDefaultFileCompression(CompressionAlgorithmTags.UNCOMPRESSED);
}
+
+ // migrate away from MD5
+ if (mSharedPreferences.getInt(Constants.Pref.DEFAULT_HASH_ALGORITHM, 0) == HashAlgorithmTags.MD5) {
+ setDefaultHashAlgorithm(HashAlgorithmTags.SHA512);
+ }
}
public void setWriteVersionHeader(boolean conceal) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
index f617be62a..e30401180 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java
@@ -24,8 +24,10 @@ import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.util.Log;
-import java.io.DataOutputStream;
+import java.io.BufferedWriter;
import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
@@ -281,7 +283,7 @@ public class HkpKeyserver extends Keyserver {
entry.setBitStrength(Integer.parseInt(matcher.group(3)));
final int algorithmId = Integer.decode(matcher.group(2));
- entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId));
+ entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId, null, null));
// group 1 contains the full fingerprint (v4) or the long key id if available
// see http://bit.ly/1d4bxbk and http://bit.ly/1gD1wwr
@@ -352,25 +354,38 @@ public class HkpKeyserver extends Keyserver {
@Override
public void add(String armoredKey) throws AddKeyException {
try {
- String query = getUrlPrefix() + mHost + ":" + mPort + "/pks/add";
+ String request = "/pks/add";
String params;
try {
- params = "keytext=" + URLEncoder.encode(armoredKey, "utf8");
+ params = "keytext=" + URLEncoder.encode(armoredKey, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new AddKeyException();
}
- Log.d(Constants.TAG, "hkp keyserver add: " + query);
-
- HttpURLConnection connection = openConnection(new URL(query));
- connection.setRequestMethod("POST");
- connection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
- connection.setRequestProperty("Content-Length", Integer.toString(params.getBytes().length));
- connection.setDoOutput(true);
- DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
- wr.writeBytes(params);
- wr.flush();
- wr.close();
+ URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + request);
+
+ Log.d(Constants.TAG, "hkp keyserver add: " + url.toString());
+ Log.d(Constants.TAG, "params: " + params);
+
+ HttpURLConnection conn = openConnection(url);
+ conn.setRequestMethod("POST");
+ conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+ conn.setRequestProperty("Content-Length", Integer.toString(params.getBytes().length));
+ conn.setDoInput(true);
+ conn.setDoOutput(true);
+
+ OutputStream os = conn.getOutputStream();
+ BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
+ writer.write(params);
+ writer.flush();
+ writer.close();
+ os.close();
+
+ conn.connect();
+
+ Log.d(Constants.TAG, "response code: " + conn.getResponseCode());
+ Log.d(Constants.TAG, "answer: " + readAll(conn.getInputStream(), conn.getContentEncoding()));
} catch (IOException e) {
+ Log.e(Constants.TAG, "IOException", e);
throw new AddKeyException();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
index 30e93f957..da70f1505 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java
@@ -39,7 +39,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
private boolean mExpired;
private Date mDate; // TODO: not displayed
private String mFingerprintHex;
- private int mBitStrength;
+ private Integer mBitStrength;
+ private String mCurveOid;
private String mAlgorithm;
private boolean mSecretKey;
private String mPrimaryUserId;
@@ -162,10 +163,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
this.mFingerprintHex = fingerprintHex;
}
- public int getBitStrength() {
+ public Integer getBitStrength() {
return mBitStrength;
}
+ public String getCurveOid() {
+ return mCurveOid;
+ }
+
public void setBitStrength(int bitStrength) {
this.mBitStrength = bitStrength;
}
@@ -258,13 +263,15 @@ public class ImportKeysListEntry implements Serializable, Parcelable {
mPrimaryUserId = mUserIds.get(0);
}
- this.mKeyId = key.getKeyId();
- this.mKeyIdHex = PgpKeyHelper.convertKeyIdToHex(mKeyId);
+ mKeyId = key.getKeyId();
+ mKeyIdHex = PgpKeyHelper.convertKeyIdToHex(mKeyId);
- this.mRevoked = key.isRevoked();
- this.mFingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
- this.mBitStrength = key.getBitStrength();
+ mRevoked = key.isRevoked();
+ mFingerprintHex = PgpKeyHelper.convertFingerprintToHex(key.getFingerprint());
+ mBitStrength = key.getBitStrength();
+ mCurveOid = key.getCurveOid();
final int algorithm = key.getAlgorithm();
- this.mAlgorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm);
+ mAlgorithm = PgpKeyHelper.getAlgorithmInfo(context, algorithm, mBitStrength, mCurveOid);
}
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
index 0ca6f07fd..cbd06da90 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java
@@ -75,7 +75,7 @@ public class KeybaseKeyserver extends Keyserver {
entry.setExtraData(username);
final int algorithmId = match.getAlgorithmId();
- entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId));
+ entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId, null, null));
final int bitStrength = match.getBitStrength();
entry.setBitStrength(bitStrength);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
index 8609a7082..55e6be9b9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
index 1da66872d..8076a14f3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
index ce6498df1..7a63a7a42 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKey.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
index 972e45c2e..2c27c5c37 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedPublicKeyRing.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
@@ -60,10 +61,6 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
return mRing;
}
- public void encode(ArmoredOutputStream stream) throws IOException {
- getRing().encode(stream);
- }
-
/** Getter that returns the subkey that should be used for signing. */
CanonicalizedPublicKey getEncryptionSubKey() throws PgpGeneralException {
PGPPublicKey key = getRing().getPublicKey(getEncryptId());
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
index 877857553..c79dc45c3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKey.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
index 4f74a2336..d8b873d31 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedSecretKeyRing.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
index 35020b815..3ef4b336e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/KeyRing.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index 7d113241b..828bee848 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -523,13 +523,7 @@ public class PgpDecryptVerify {
// update signature buffer if signature is also present
if (signature != null) {
- try {
- signature.update(buffer, 0, length);
- } catch (SignatureException e) {
- Log.e(Constants.TAG, "SignatureException -> Not a valid signature!", e);
- signatureResultBuilder.validSignature(false);
- signature = null;
- }
+ signature.update(buffer, 0, length);
}
alreadyWritten += length;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
index 1cf027721..db360f810 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java
@@ -29,7 +29,6 @@ import org.sufficientlysecure.keychain.util.Log;
import java.io.File;
import java.io.IOException;
-import java.io.InputStream;
import java.io.RandomAccessFile;
import java.security.SecureRandom;
import java.util.regex.Pattern;
@@ -68,76 +67,6 @@ public class PgpHelper {
}
}
-// public static final class content {
-// public static final int unknown = 0;
-// public static final int encrypted_data = 1;
-// public static final int keys = 2;
-// }
-//
-// public static int getStreamContent(Context context, InputStream inStream) throws IOException {
-//
-// InputStream in = PGPUtil.getDecoderStream(inStream);
-// PGPObjectFactory pgpF = new PGPObjectFactory(in);
-// Object object = pgpF.nextObject();
-// while (object != null) {
-// if (object instanceof PGPPublicKeyRing || object instanceof PGPSecretKeyRing) {
-// return Id.content.keys;
-// } else if (object instanceof PGPEncryptedDataList) {
-// return Id.content.encrypted_data;
-// }
-// object = pgpF.nextObject();
-// }
-//
-// return Id.content.unknown;
-// }
-
- /**
- * Generate a random filename
- *
- * @param length
- * @return
- */
- public static String generateRandomFilename(int length) {
- SecureRandom random = new SecureRandom();
-
- byte bytes[] = new byte[length];
- random.nextBytes(bytes);
- String result = "";
- for (int i = 0; i < length; ++i) {
- int v = (bytes[i] + 256) % 64;
- if (v < 10) {
- result += (char) ('0' + v);
- } else if (v < 36) {
- result += (char) ('A' + v - 10);
- } else if (v < 62) {
- result += (char) ('a' + v - 36);
- } else if (v == 62) {
- result += '_';
- } else if (v == 63) {
- result += '.';
- }
- }
- return result;
- }
-
- /**
- * Go once through stream to get length of stream. The length is later used to display progress
- * when encrypting/decrypting
- *
- * @param in
- * @return
- * @throws IOException
- */
- public static long getLengthOfStream(InputStream in) throws IOException {
- long size = 0;
- long n = 0;
- byte dummy[] = new byte[0x10000];
- while ((n = in.read(dummy)) > 0) {
- size += n;
- }
- return size;
- }
-
/**
* Deletes file securely by overwriting it with random data before deleting it.
* <p/>
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
index f14eacda2..0bc3ac0ab 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java
@@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult;
+import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler;
@@ -43,6 +44,7 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
public class PgpImportExport {
@@ -60,10 +62,14 @@ public class PgpImportExport {
private ProviderHelper mProviderHelper;
public PgpImportExport(Context context, Progressable progressable) {
+ this(context, new ProviderHelper(context), progressable);
+ }
+
+ public PgpImportExport(Context context, ProviderHelper providerHelper, Progressable progressable) {
super();
this.mContext = context;
this.mProgressable = progressable;
- this.mProviderHelper = new ProviderHelper(context);
+ this.mProviderHelper = providerHelper;
}
public PgpImportExport(Context context,
@@ -93,7 +99,7 @@ public class PgpImportExport {
}
}
- public boolean uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) {
+ public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) throws AddKeyException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null;
try {
@@ -103,13 +109,9 @@ public class PgpImportExport {
String armoredKey = bos.toString("UTF-8");
server.add(armoredKey);
-
- return true;
} catch (IOException e) {
- return false;
- } catch (AddKeyException e) {
- // TODO: tell the user?
- return false;
+ Log.e(Constants.TAG, "IOException", e);
+ throw new AddKeyException();
} finally {
try {
if (aos != null) {
@@ -124,20 +126,23 @@ public class PgpImportExport {
/** Imports keys from given data. If keyIds is given only those are imported */
public ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries) {
+ return importKeyRings(entries.iterator(), entries.size());
+ }
+ public ImportKeyResult importKeyRings(Iterator<ParcelableKeyRing> entries, int num) {
updateProgress(R.string.progress_importing, 0, 100);
// If there aren't even any keys, do nothing here.
- if (entries == null || entries.size() == 0) {
+ if (entries == null || !entries.hasNext()) {
return new ImportKeyResult(
- ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0);
+ ImportKeyResult.RESULT_FAIL_NOTHING, mProviderHelper.getLog(), 0, 0, 0, 0);
}
- int newKeys = 0, oldKeys = 0, badKeys = 0;
+ int newKeys = 0, oldKeys = 0, badKeys = 0, secret = 0;
int position = 0;
- int progSteps = 100 / entries.size();
- for (ParcelableKeyRing entry : entries) {
+ double progSteps = 100.0 / num;
+ for (ParcelableKeyRing entry : new IterableIterator<ParcelableKeyRing>(entries)) {
try {
UncachedKeyRing key = UncachedKeyRing.decodeFromData(entry.getBytes());
@@ -157,10 +162,10 @@ public class PgpImportExport {
SaveKeyringResult result;
if (key.isSecret()) {
result = mProviderHelper.saveSecretKeyRing(key,
- new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100));
+ new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
} else {
result = mProviderHelper.savePublicKeyRing(key,
- new ProgressScaler(mProgressable, position, (position+1)*progSteps, 100));
+ new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
}
if (!result.success()) {
badKeys += 1;
@@ -168,6 +173,9 @@ public class PgpImportExport {
oldKeys += 1;
} else {
newKeys += 1;
+ if (key.isSecret()) {
+ secret += 1;
+ }
}
} catch (IOException e) {
@@ -204,7 +212,7 @@ public class PgpImportExport {
}
}
- return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys);
+ return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
index 459b80be2..05acb86df 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java
@@ -24,10 +24,16 @@ import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.nist.NISTNamedCurves;
+import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
+import org.spongycastle.bcpg.ECPublicBCPGKey;
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.util.Log;
import java.security.DigestException;
@@ -37,18 +43,14 @@ import java.util.Locale;
public class PgpKeyHelper {
- public static String getAlgorithmInfo(int algorithm) {
- return getAlgorithmInfo(null, algorithm, 0);
- }
-
- public static String getAlgorithmInfo(Context context, int algorithm) {
- return getAlgorithmInfo(context, algorithm, 0);
+ public static String getAlgorithmInfo(int algorithm, Integer keySize, String oid) {
+ return getAlgorithmInfo(null, algorithm, keySize, oid);
}
/**
* Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>
*/
- public static String getAlgorithmInfo(Context context, int algorithm, int keySize) {
+ public static String getAlgorithmInfo(Context context, int algorithm, Integer keySize, String oid) {
String algorithmStr;
switch (algorithm) {
@@ -69,10 +71,19 @@ public class PgpKeyHelper {
break;
}
- case PublicKeyAlgorithmTags.ECDSA:
+ case PublicKeyAlgorithmTags.ECDSA: {
+ if (oid == null) {
+ return "ECDSA";
+ }
+ String oidName = PgpKeyHelper.getCurveInfo(context, oid);
+ return "ECDSA (" + oidName + ")";
+ }
case PublicKeyAlgorithmTags.ECDH: {
- algorithmStr = "ECC";
- break;
+ if (oid == null) {
+ return "ECDH";
+ }
+ String oidName = PgpKeyHelper.getCurveInfo(context, oid);
+ return "ECDH (" + oidName + ")";
}
default: {
@@ -90,6 +101,106 @@ public class PgpKeyHelper {
return algorithmStr;
}
+ public static String getAlgorithmInfo(Algorithm algorithm, Integer keySize, Curve curve) {
+ return getAlgorithmInfo(null, algorithm, keySize, curve);
+ }
+
+ /**
+ * Based on <a href="http://tools.ietf.org/html/rfc2440#section-9.1">OpenPGP Message Format</a>
+ */
+ public static String getAlgorithmInfo(Context context, Algorithm algorithm, Integer keySize, Curve curve) {
+ String algorithmStr;
+
+ switch (algorithm) {
+ case RSA: {
+ algorithmStr = "RSA";
+ break;
+ }
+ case DSA: {
+ algorithmStr = "DSA";
+ break;
+ }
+
+ case ELGAMAL: {
+ algorithmStr = "ElGamal";
+ break;
+ }
+
+ case ECDSA: {
+ algorithmStr = "ECDSA";
+ if (curve != null) {
+ algorithmStr += " (" + getCurveInfo(context, curve) + ")";
+ }
+ return algorithmStr;
+ }
+ case ECDH: {
+ algorithmStr = "ECDH";
+ if (curve != null) {
+ algorithmStr += " (" + getCurveInfo(context, curve) + ")";
+ }
+ return algorithmStr;
+ }
+
+ default: {
+ if (context != null) {
+ algorithmStr = context.getResources().getString(R.string.unknown_algorithm);
+ } else {
+ algorithmStr = "unknown";
+ }
+ break;
+ }
+ }
+ if (keySize != null && keySize > 0)
+ return algorithmStr + ", " + keySize + " bit";
+ else
+ return algorithmStr;
+ }
+
+ // Return name of a curve. These are names, no need for translation
+ public static String getCurveInfo(Context context, Curve curve) {
+ switch(curve) {
+ case NIST_P256:
+ return "NIST P-256";
+ case NIST_P384:
+ return "NIST P-384";
+ case NIST_P521:
+ return "NIST P-521";
+
+ /* see SaveKeyringParcel
+ case BRAINPOOL_P256:
+ return "Brainpool P-256";
+ case BRAINPOOL_P384:
+ return "Brainpool P-384";
+ case BRAINPOOL_P512:
+ return "Brainpool P-512";
+ */
+ }
+ if (context != null) {
+ return context.getResources().getString(R.string.unknown_algorithm);
+ } else {
+ return "unknown";
+ }
+ }
+
+ public static String getCurveInfo(Context context, String oidStr) {
+ ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(oidStr);
+
+ String name;
+ name = NISTNamedCurves.getName(oid);
+ if (name != null) {
+ return name;
+ }
+ name = TeleTrusTNamedCurves.getName(oid);
+ if (name != null) {
+ return name;
+ }
+ if (context != null) {
+ return context.getResources().getString(R.string.unknown_algorithm);
+ } else {
+ return "unknown";
+ }
+ }
+
/**
* Converts fingerprint to hex
* <p/>
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index bb692555e..967a7caa9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ * 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
@@ -20,12 +20,12 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
-import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
+import org.spongycastle.bcpg.sig.Features;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.jce.spec.ElGamalParameterSpec;
-import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPException;
+import org.spongycastle.openpgp.PGPKeyFlags;
import org.spongycastle.openpgp.PGPKeyPair;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
@@ -34,7 +34,6 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
-import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
@@ -53,6 +52,8 @@ import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyAdd;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
@@ -67,6 +68,7 @@ import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.SignatureException;
+import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
@@ -84,15 +86,45 @@ import java.util.Stack;
public class PgpKeyOperation {
private Stack<Progressable> mProgress;
+ // most preferred is first
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
- SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
- SymmetricKeyAlgorithmTags.AES_128, SymmetricKeyAlgorithmTags.CAST5,
- SymmetricKeyAlgorithmTags.TRIPLE_DES};
- private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{HashAlgorithmTags.SHA1,
- HashAlgorithmTags.SHA256, HashAlgorithmTags.RIPEMD160};
+ SymmetricKeyAlgorithmTags.AES_256,
+ SymmetricKeyAlgorithmTags.AES_192,
+ SymmetricKeyAlgorithmTags.AES_128,
+ SymmetricKeyAlgorithmTags.CAST5
+ };
+ private static final int[] PREFERRED_HASH_ALGORITHMS = new int[]{
+ HashAlgorithmTags.SHA512,
+ HashAlgorithmTags.SHA384,
+ HashAlgorithmTags.SHA224,
+ HashAlgorithmTags.SHA256,
+ HashAlgorithmTags.RIPEMD160
+ };
private static final int[] PREFERRED_COMPRESSION_ALGORITHMS = new int[]{
- CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
- CompressionAlgorithmTags.ZIP};
+ CompressionAlgorithmTags.ZLIB,
+ CompressionAlgorithmTags.BZIP2,
+ CompressionAlgorithmTags.ZIP
+ };
+
+ /*
+ * Note: s2kcount is a number between 0 and 0xff that controls the
+ * number of times to iterate the password hash before use. More
+ * iterations are useful against offline attacks, as it takes more
+ * time to check each password. The actual number of iterations is
+ * rather complex, and also depends on the hash function in use.
+ * Refer to Section 3.7.1.3 in rfc4880.txt. Bigger numbers give
+ * you more iterations. As a rough rule of thumb, when using
+ * SHA256 as the hashing function, 0x10 gives you about 64
+ * iterations, 0x20 about 128, 0x30 about 256 and so on till 0xf0,
+ * or about 1 million iterations. The maximum you can go to is
+ * 0xff, or about 2 million iterations. I'll use 0xc0 as a
+ * default -- about 130,000 iterations.
+ *
+ * http://kbsriram.com/2013/01/generating-rsa-keys-with-bouncycastle.html
+ */
+ private static final int SECRET_KEY_ENCRYPTOR_HASH_ALGO = HashAlgorithmTags.SHA512;
+ private static final int SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO = SymmetricKeyAlgorithmTags.AES_256;
+ private static final int SECRET_KEY_ENCRYPTOR_S2K_COUNT = 0x60;
public PgpKeyOperation(Progressable progress) {
super();
@@ -126,31 +158,65 @@ public class PgpKeyOperation {
mProgress.peek().setProgress(message, current, 100);
}
+ private ECGenParameterSpec getEccParameterSpec(Curve curve) {
+ switch (curve) {
+ case NIST_P256: return new ECGenParameterSpec("P-256");
+ case NIST_P384: return new ECGenParameterSpec("P-384");
+ case NIST_P521: return new ECGenParameterSpec("P-521");
+
+ // @see SaveKeyringParcel
+ // case BRAINPOOL_P256: return new ECGenParameterSpec("brainpoolp256r1");
+ // case BRAINPOOL_P384: return new ECGenParameterSpec("brainpoolp384r1");
+ // case BRAINPOOL_P512: return new ECGenParameterSpec("brainpoolp512r1");
+ }
+ throw new RuntimeException("Invalid choice! (can't happen)");
+ }
+
/** Creates new secret key. */
- private PGPKeyPair createKey(int algorithmChoice, int keySize, OperationLog log, int indent) {
+ private PGPKeyPair createKey(SubkeyAdd add, OperationLog log, int indent) {
try {
- if (keySize < 512) {
- log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent);
- return null;
+ // Some safety checks
+ if (add.mAlgorithm == Algorithm.ECDH || add.mAlgorithm == Algorithm.ECDSA) {
+ if (add.mCurve == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_CURVE, indent);
+ return null;
+ }
+ } else {
+ if (add.mKeySize == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NO_KEYSIZE, indent);
+ return null;
+ }
+ if (add.mKeySize < 512) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_KEYSIZE_512, indent);
+ return null;
+ }
}
int algorithm;
KeyPairGenerator keyGen;
- switch (algorithmChoice) {
- case PublicKeyAlgorithmTags.DSA: {
+ switch (add.mAlgorithm) {
+ case DSA: {
+ if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_DSA, indent);
+ return null;
+ }
progress(R.string.progress_generating_dsa, 30);
keyGen = KeyPairGenerator.getInstance("DSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- keyGen.initialize(keySize, new SecureRandom());
+ keyGen.initialize(add.mKeySize, new SecureRandom());
algorithm = PGPPublicKey.DSA;
break;
}
- case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: {
+ case ELGAMAL: {
+ if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ELGAMAL, indent);
+ return null;
+ }
progress(R.string.progress_generating_elgamal, 30);
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- BigInteger p = Primes.getBestPrime(keySize);
+ BigInteger p = Primes.getBestPrime(add.mKeySize);
BigInteger g = new BigInteger("2");
ElGamalParameterSpec elParams = new ElGamalParameterSpec(p, g);
@@ -160,15 +226,44 @@ public class PgpKeyOperation {
break;
}
- case PublicKeyAlgorithmTags.RSA_GENERAL: {
+ case RSA: {
progress(R.string.progress_generating_rsa, 30);
keyGen = KeyPairGenerator.getInstance("RSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- keyGen.initialize(keySize, new SecureRandom());
+ keyGen.initialize(add.mKeySize, new SecureRandom());
algorithm = PGPPublicKey.RSA_GENERAL;
break;
}
+ case ECDSA: {
+ if ((add.mFlags & (PGPKeyFlags.CAN_ENCRYPT_COMMS | PGPKeyFlags.CAN_ENCRYPT_STORAGE)) > 0) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ECDSA, indent);
+ return null;
+ }
+ progress(R.string.progress_generating_ecdsa, 30);
+ ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve);
+ keyGen = KeyPairGenerator.getInstance("ECDSA", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ keyGen.initialize(ecParamSpec, new SecureRandom());
+
+ algorithm = PGPPublicKey.ECDSA;
+ break;
+ }
+
+ case ECDH: {
+ // make sure there are no sign or certify flags set
+ if ((add.mFlags & (PGPKeyFlags.CAN_SIGN | PGPKeyFlags.CAN_CERTIFY)) > 0) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_FLAGS_ECDH, indent);
+ return null;
+ }
+ progress(R.string.progress_generating_ecdh, 30);
+ ECGenParameterSpec ecParamSpec = getEccParameterSpec(add.mCurve);
+ keyGen = KeyPairGenerator.getInstance("ECDH", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ keyGen.initialize(ecParamSpec, new SecureRandom());
+
+ algorithm = PGPPublicKey.ECDH;
+ break;
+ }
+
default: {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
return null;
@@ -181,7 +276,8 @@ public class PgpKeyOperation {
} catch(NoSuchProviderException e) {
throw new RuntimeException(e);
} catch(NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_UNKNOWN_ALGO, indent);
+ return null;
} catch(InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
} catch(PGPException e) {
@@ -218,13 +314,13 @@ public class PgpKeyOperation {
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- if (add.mAlgorithm == PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT) {
- log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_MASTER_ELGAMAL, indent);
+ if (add.mExpiry == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_NULL_EXPIRY, indent);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
subProgressPush(10, 30);
- PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ PGPKeyPair keyPair = createKey(add, log, indent);
subProgressPop();
// return null if this failed (an error will already have been logged by createKey)
@@ -234,13 +330,16 @@ public class PgpKeyOperation {
progress(R.string.progress_building_master_key, 40);
- // define hashing and signing algos
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(HashAlgorithmTags.SHA1);
// Build key encrypter and decrypter based on passphrase
+ PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
+ .build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PGPEncryptedData.CAST5, sha1Calc)
+ SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+
+ // NOTE: only SHA1 is supported for key checksum calculations.
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
+ .build().get(HashAlgorithmTags.SHA1);
PGPSecretKey masterSecretKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(),
sha1Calc, true, keyEncryptor);
@@ -248,7 +347,7 @@ public class PgpKeyOperation {
masterSecretKey.getEncoded(), new JcaKeyFingerprintCalculator());
subProgressPush(50, 100);
- return internal(sKR, masterSecretKey, add.mFlags, saveParcel, "", log);
+ return internal(sKR, masterSecretKey, add.mFlags, add.mExpiry, saveParcel, "", log);
} catch (PGPException e) {
log.add(LogLevel.ERROR, LogType.MSG_CR_ERROR_INTERNAL_PGP, indent);
@@ -314,14 +413,17 @@ public class PgpKeyOperation {
// read masterKeyFlags, and use the same as before.
// since this is the master key, this contains at least CERTIFY_OTHER
- int masterKeyFlags = readKeyFlags(masterSecretKey.getPublicKey()) | KeyFlags.CERTIFY_OTHER;
+ PGPPublicKey masterPublicKey = masterSecretKey.getPublicKey();
+ int masterKeyFlags = readKeyFlags(masterPublicKey) | KeyFlags.CERTIFY_OTHER;
+ long masterKeyExpiry = masterPublicKey.getValidSeconds() == 0L ? 0L :
+ masterPublicKey.getCreationTime().getTime() / 1000 + masterPublicKey.getValidSeconds();
- return internal(sKR, masterSecretKey, masterKeyFlags, saveParcel, passphrase, log);
+ return internal(sKR, masterSecretKey, masterKeyFlags, masterKeyExpiry, saveParcel, passphrase, log);
}
private EditKeyResult internal(PGPSecretKeyRing sKR, PGPSecretKey masterSecretKey,
- int masterKeyFlags,
+ int masterKeyFlags, long masterKeyExpiry,
SaveKeyringParcel saveParcel, String passphrase,
OperationLog log) {
@@ -346,177 +448,196 @@ public class PgpKeyOperation {
}
}
- // work on master secret key
try {
- PGPPublicKey modifiedPublicKey = masterPublicKey;
+ { // work on master secret key
- // 2a. Add certificates for new user ids
- subProgressPush(15, 25);
- for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
+ PGPPublicKey modifiedPublicKey = masterPublicKey;
- progress(R.string.progress_modify_adduid, (i-1) * (100 / saveParcel.mAddUserIds.size()));
- String userId = saveParcel.mAddUserIds.get(i);
- log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent, userId);
+ // 2a. Add certificates for new user ids
+ subProgressPush(15, 25);
+ for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) {
- if (userId.equals("")) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent+1);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
- }
+ progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.mAddUserIds.size()));
+ String userId = saveParcel.mAddUserIds.get(i);
+ log.add(LogLevel.INFO, LogType.MSG_MF_UID_ADD, indent, userId);
- // this operation supersedes all previous binding and revocation certificates,
- // so remove those to retain assertions from canonicalization for later operations
- @SuppressWarnings("unchecked")
- Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
- if (it != null) {
- for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) {
- if (cert.getKeyID() != masterPublicKey.getKeyID()) {
- // foreign certificate?! error error error
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
- }
- if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
- || cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
- || cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION
- || cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION
- || cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
- modifiedPublicKey = PGPPublicKey.removeCertification(
- modifiedPublicKey, userId, cert);
- }
+ if (userId.equals("")) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_UID_ERROR_EMPTY, indent + 1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- }
- // if it's supposed to be primary, we can do that here as well
- boolean isPrimary = saveParcel.mChangePrimaryUserId != null
- && userId.equals(saveParcel.mChangePrimaryUserId);
- // generate and add new certificate
- PGPSignature cert = generateUserIdSignature(masterPrivateKey,
- masterPublicKey, userId, isPrimary, masterKeyFlags);
- modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
- }
- subProgressPop();
+ // this operation supersedes all previous binding and revocation certificates,
+ // so remove those to retain assertions from canonicalization for later operations
+ @SuppressWarnings("unchecked")
+ Iterator<PGPSignature> it = modifiedPublicKey.getSignaturesForID(userId);
+ if (it != null) {
+ for (PGPSignature cert : new IterableIterator<PGPSignature>(it)) {
+ if (cert.getKeyID() != masterPublicKey.getKeyID()) {
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+ if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION
+ || cert.getSignatureType() == PGPSignature.NO_CERTIFICATION
+ || cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION
+ || cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION
+ || cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
+ modifiedPublicKey = PGPPublicKey.removeCertification(
+ modifiedPublicKey, userId, cert);
+ }
+ }
+ }
- // 2b. Add revocations for revoked user ids
- subProgressPush(25, 40);
- for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
+ // if it's supposed to be primary, we can do that here as well
+ boolean isPrimary = saveParcel.mChangePrimaryUserId != null
+ && userId.equals(saveParcel.mChangePrimaryUserId);
+ // generate and add new certificate
+ PGPSignature cert = generateUserIdSignature(masterPrivateKey,
+ masterPublicKey, userId, isPrimary, masterKeyFlags, masterKeyExpiry);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
+ }
+ subProgressPop();
- progress(R.string.progress_modify_revokeuid, (i-1) * (100 / saveParcel.mRevokeUserIds.size()));
- String userId = saveParcel.mRevokeUserIds.get(i);
- log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId);
+ // 2b. Add revocations for revoked user ids
+ subProgressPush(25, 40);
+ for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) {
+
+ progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / saveParcel.mRevokeUserIds.size()));
+ String userId = saveParcel.mRevokeUserIds.get(i);
+ log.add(LogLevel.INFO, LogType.MSG_MF_UID_REVOKE, indent, userId);
+
+ // Make sure the user id exists (yes these are 10 LoC in Java!)
+ boolean exists = false;
+ //noinspection unchecked
+ for (String uid : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
+ if (userId.equals(uid)) {
+ exists = true;
+ break;
+ }
+ }
+ if (!exists) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_REVOKE, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
- // a duplicate revocation will be removed during canonicalization, so no need to
- // take care of that here.
- PGPSignature cert = generateRevocationSignature(masterPrivateKey,
- masterPublicKey, userId);
- modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
- }
- subProgressPop();
+ // a duplicate revocation will be removed during canonicalization, so no need to
+ // take care of that here.
+ PGPSignature cert = generateRevocationSignature(masterPrivateKey,
+ masterPublicKey, userId);
+ modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, userId, cert);
+ }
+ subProgressPop();
- // 3. If primary user id changed, generate new certificates for both old and new
- if (saveParcel.mChangePrimaryUserId != null) {
- progress(R.string.progress_modify_primaryuid, 40);
+ // 3. If primary user id changed, generate new certificates for both old and new
+ if (saveParcel.mChangePrimaryUserId != null) {
+ progress(R.string.progress_modify_primaryuid, 40);
- // keep track if we actually changed one
- boolean ok = false;
- log.add(LogLevel.INFO, LogType.MSG_MF_UID_PRIMARY, indent);
- indent += 1;
+ // keep track if we actually changed one
+ boolean ok = false;
+ log.add(LogLevel.INFO, LogType.MSG_MF_UID_PRIMARY, indent);
+ indent += 1;
- // we work on the modifiedPublicKey here, to respect new or newly revoked uids
- // noinspection unchecked
- for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
- boolean isRevoked = false;
- PGPSignature currentCert = null;
+ // we work on the modifiedPublicKey here, to respect new or newly revoked uids
// noinspection unchecked
- for (PGPSignature cert : new IterableIterator<PGPSignature>(
- modifiedPublicKey.getSignaturesForID(userId))) {
- if (cert.getKeyID() != masterPublicKey.getKeyID()) {
- // foreign certificate?! error error error
+ for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
+ boolean isRevoked = false;
+ PGPSignature currentCert = null;
+ // noinspection unchecked
+ for (PGPSignature cert : new IterableIterator<PGPSignature>(
+ modifiedPublicKey.getSignaturesForID(userId))) {
+ if (cert.getKeyID() != masterPublicKey.getKeyID()) {
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+ // we know from canonicalization that if there is any revocation here, it
+ // is valid and not superseded by a newer certification.
+ if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
+ isRevoked = true;
+ continue;
+ }
+ // we know from canonicalization that there is only one binding
+ // certification here, so we can just work with the first one.
+ if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
+ currentCert = cert;
+ }
+ }
+
+ if (currentCert == null) {
+ // no certificate found?! error error error
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- // we know from canonicalization that if there is any revocation here, it
- // is valid and not superseded by a newer certification.
- if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
- isRevoked = true;
+
+ // we definitely should not update certifications of revoked keys, so just leave it.
+ if (isRevoked) {
+ // revoked user ids cannot be primary!
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
continue;
}
- // we know from canonicalization that there is only one binding
- // certification here, so we can just work with the first one.
- if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION ||
- cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION ||
- cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION ||
- cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
- currentCert = cert;
- }
- }
- if (currentCert == null) {
- // no certificate found?! error error error
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
- }
-
- // we definitely should not update certifications of revoked keys, so just leave it.
- if (isRevoked) {
- // revoked user ids cannot be primary!
- if (userId.equals(saveParcel.mChangePrimaryUserId)) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_REVOKED_PRIMARY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ // if this is~ the/a primary user id
+ if (currentCert.getHashedSubPackets() != null
+ && currentCert.getHashedSubPackets().isPrimaryUserID()) {
+ // if it's the one we want, just leave it as is
+ if (userId.equals(saveParcel.mChangePrimaryUserId)) {
+ ok = true;
+ continue;
+ }
+ // otherwise, generate new non-primary certification
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);
+ modifiedPublicKey = PGPPublicKey.removeCertification(
+ modifiedPublicKey, userId, currentCert);
+ PGPSignature newCert = generateUserIdSignature(
+ masterPrivateKey, masterPublicKey, userId, false,
+ masterKeyFlags, masterKeyExpiry);
+ modifiedPublicKey = PGPPublicKey.addCertification(
+ modifiedPublicKey, userId, newCert);
+ continue;
}
- continue;
- }
- // if this is~ the/a primary user id
- if (currentCert.getHashedSubPackets() != null
- && currentCert.getHashedSubPackets().isPrimaryUserID()) {
- // if it's the one we want, just leave it as is
+ // if we are here, this is not currently a primary user id
+
+ // if it should be
if (userId.equals(saveParcel.mChangePrimaryUserId)) {
+ // add shiny new primary user id certificate
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_NEW, indent);
+ modifiedPublicKey = PGPPublicKey.removeCertification(
+ modifiedPublicKey, userId, currentCert);
+ PGPSignature newCert = generateUserIdSignature(
+ masterPrivateKey, masterPublicKey, userId, true,
+ masterKeyFlags, masterKeyExpiry);
+ modifiedPublicKey = PGPPublicKey.addCertification(
+ modifiedPublicKey, userId, newCert);
ok = true;
- continue;
}
- // otherwise, generate new non-primary certification
- log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_REPLACE_OLD, indent);
- modifiedPublicKey = PGPPublicKey.removeCertification(
- modifiedPublicKey, userId, currentCert);
- PGPSignature newCert = generateUserIdSignature(
- masterPrivateKey, masterPublicKey, userId, false, masterKeyFlags);
- modifiedPublicKey = PGPPublicKey.addCertification(
- modifiedPublicKey, userId, newCert);
- continue;
- }
- // if we are here, this is not currently a primary user id
-
- // if it should be
- if (userId.equals(saveParcel.mChangePrimaryUserId)) {
- // add shiny new primary user id certificate
- log.add(LogLevel.DEBUG, LogType.MSG_MF_PRIMARY_NEW, indent);
- modifiedPublicKey = PGPPublicKey.removeCertification(
- modifiedPublicKey, userId, currentCert);
- PGPSignature newCert = generateUserIdSignature(
- masterPrivateKey, masterPublicKey, userId, true, masterKeyFlags);
- modifiedPublicKey = PGPPublicKey.addCertification(
- modifiedPublicKey, userId, newCert);
- ok = true;
+ // user id is not primary and is not supposed to be - nothing to do here.
+
}
- // user id is not primary and is not supposed to be - nothing to do here.
+ indent -= 1;
+ if (!ok) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
}
- indent -= 1;
-
- if (!ok) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NOEXIST_PRIMARY, indent);
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ // Update the secret key ring
+ if (modifiedPublicKey != masterPublicKey) {
+ masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey);
+ masterPublicKey = modifiedPublicKey;
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
}
- }
- // Update the secret key ring
- if (modifiedPublicKey != masterPublicKey) {
- masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, modifiedPublicKey);
- masterPublicKey = modifiedPublicKey;
- sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
}
// 4a. For each subkey change, generate new subkey binding certificate
@@ -528,27 +649,47 @@ public class PgpKeyOperation {
log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_CHANGE,
indent, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
- // TODO allow changes in master key? this implies generating new user id certs...
- if (change.mKeyId == masterPublicKey.getKeyID()) {
- Log.e(Constants.TAG, "changing the master key not supported");
- return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
- }
-
PGPSecretKey sKey = sKR.getSecretKey(change.mKeyId);
if (sKey == null) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
- PGPPublicKey pKey = sKey.getPublicKey();
// expiry must not be in the past
- if (change.mExpiry != null && new Date(change.mExpiry*1000).before(new Date())) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY,
+ if (change.mExpiry != null && change.mExpiry != 0 &&
+ new Date(change.mExpiry*1000).before(new Date())) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PAST_EXPIRY,
indent + 1, PgpKeyHelper.convertKeyIdToHex(change.mKeyId));
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
+ // if this is the master key, update uid certificates instead
+ if (change.mKeyId == masterPublicKey.getKeyID()) {
+ int flags = change.mFlags == null ? masterKeyFlags : change.mFlags;
+ long expiry = change.mExpiry == null ? masterKeyExpiry : change.mExpiry;
+
+ if ((flags & KeyFlags.CERTIFY_OTHER) != KeyFlags.CERTIFY_OTHER) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NO_CERTIFY, indent + 1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ PGPPublicKey pKey =
+ updateMasterCertificates(masterPrivateKey, masterPublicKey,
+ flags, expiry, indent, log);
+ if (pKey == null) {
+ // error log entry has already been added by updateMasterCertificates itself
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+ masterSecretKey = PGPSecretKey.replacePublicKey(masterSecretKey, pKey);
+ masterPublicKey = pKey;
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, masterSecretKey);
+ continue;
+ }
+
+ // otherwise, continue working on the public key
+ PGPPublicKey pKey = sKey.getPublicKey();
+
// keep old flags, or replace with new ones
int flags = change.mFlags == null ? readKeyFlags(pKey) : change.mFlags;
long expiry;
@@ -565,7 +706,7 @@ public class PgpKeyOperation {
//noinspection unchecked
for (PGPSignature sig : new IterableIterator<PGPSignature>(pKey.getSignatures())) {
// special case: if there is a revocation, don't use expiry from before
- if (change.mExpiry == null
+ if ( (change.mExpiry == null || change.mExpiry == 0L)
&& sig.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) {
expiry = 0;
}
@@ -591,7 +732,7 @@ public class PgpKeyOperation {
PGPSecretKey sKey = sKR.getSecretKey(revocation);
if (sKey == null) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_MISSING,
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SUBKEY_MISSING,
indent+1, PgpKeyHelper.convertKeyIdToHex(revocation));
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
@@ -611,10 +752,16 @@ public class PgpKeyOperation {
progress(R.string.progress_modify_subkeyadd, (i-1) * (100 / saveParcel.mAddSubKeys.size()));
SaveKeyringParcel.SubkeyAdd add = saveParcel.mAddSubKeys.get(i);
- log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent);
+ log.add(LogLevel.INFO, LogType.MSG_MF_SUBKEY_NEW, indent,
+ PgpKeyHelper.getAlgorithmInfo(add.mAlgorithm, add.mKeySize, add.mCurve) );
+
+ if (add.mExpiry == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_NULL_EXPIRY, indent +1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
- if (add.mExpiry != null && new Date(add.mExpiry*1000).before(new Date())) {
- log.add(LogLevel.ERROR, LogType.MSG_MF_SUBKEY_PAST_EXPIRY, indent +1);
+ if (add.mExpiry > 0L && new Date(add.mExpiry*1000).before(new Date())) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PAST_EXPIRY, indent +1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
@@ -623,9 +770,10 @@ public class PgpKeyOperation {
(i-1) * (100 / saveParcel.mAddSubKeys.size()),
i * (100 / saveParcel.mAddSubKeys.size())
);
- PGPKeyPair keyPair = createKey(add.mAlgorithm, add.mKeysize, log, indent);
+ PGPKeyPair keyPair = createKey(add, log, indent);
subProgressPop();
- if(keyPair == null) {
+ if (keyPair == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent +1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
@@ -633,21 +781,21 @@ public class PgpKeyOperation {
PGPPublicKey pKey = keyPair.getPublicKey();
PGPSignature cert = generateSubkeyBindingSignature(
masterPublicKey, masterPrivateKey, keyPair.getPrivateKey(), pKey,
- add.mFlags, add.mExpiry == null ? 0 : add.mExpiry);
+ add.mFlags, add.mExpiry);
pKey = PGPPublicKey.addSubkeyBindingCertification(pKey, cert);
PGPSecretKey sKey; {
- // define hashing and signing algos
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
- .build().get(HashAlgorithmTags.SHA1);
-
// Build key encrypter and decrypter based on passphrase
+ PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder()
+ .build().get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(
- PGPEncryptedData.CAST5, sha1Calc)
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey,
- sha1Calc, false, keyEncryptor);
+ // NOTE: only SHA1 is supported for key checksum calculations.
+ PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder()
+ .build().get(HashAlgorithmTags.SHA1);
+ sKey = new PGPSecretKey(keyPair.getPrivateKey(), pKey, sha1Calc, false, keyEncryptor);
}
log.add(LogLevel.DEBUG, LogType.MSG_MF_SUBKEY_NEW_ID,
@@ -662,21 +810,67 @@ public class PgpKeyOperation {
if (saveParcel.mNewPassphrase != null) {
progress(R.string.progress_modify_passphrase, 90);
log.add(LogLevel.INFO, LogType.MSG_MF_PASSPHRASE, indent);
- PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
- .get(HashAlgorithmTags.SHA1);
+ indent += 1;
+
+ PGPDigestCalculator encryptorHashCalc = new JcaPGPDigestCalculatorProviderBuilder().build()
+ .get(SECRET_KEY_ENCRYPTOR_HASH_ALGO);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
// Build key encryptor based on new passphrase
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
- PGPEncryptedData.CAST5, sha1Calc)
+ SECRET_KEY_ENCRYPTOR_SYMMETRIC_ALGO, encryptorHashCalc, SECRET_KEY_ENCRYPTOR_S2K_COUNT)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
saveParcel.mNewPassphrase.toCharArray());
- sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
+ // noinspection unchecked
+ for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PASSPHRASE_KEY, indent,
+ PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()));
+
+ boolean ok = false;
+
+ try {
+ // try to set new passphrase
+ sKey = PGPSecretKey.copyWithNewPassword(sKey, keyDecryptor, keyEncryptorNew);
+ ok = true;
+ } catch (PGPException e) {
+
+ // if this is the master key, error!
+ if (sKey.getKeyID() == masterPublicKey.getKeyID()) {
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PASSPHRASE_MASTER, indent+1);
+ return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
+ }
+
+ // being in here means decrypt failed, likely due to a bad passphrase try
+ // again with an empty passphrase, maybe we can salvage this
+ try {
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_PASSPHRASE_EMPTY_RETRY, indent+1);
+ PBESecretKeyDecryptor emptyDecryptor =
+ new JcePBESecretKeyDecryptorBuilder().setProvider(
+ Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
+ sKey = PGPSecretKey.copyWithNewPassword(sKey, emptyDecryptor, keyEncryptorNew);
+ ok = true;
+ } catch (PGPException e2) {
+ // non-fatal but not ok, handled below
+ }
+ }
+
+ if (!ok) {
+ // for a subkey, it's merely a warning
+ log.add(LogLevel.WARN, LogType.MSG_MF_PASSPHRASE_FAIL, indent+1,
+ PgpKeyHelper.convertKeyIdToHex(sKey.getKeyID()));
+ continue;
+ }
+
+ sKR = PGPSecretKeyRing.insertSecretKey(sKR, sKey);
+
+ }
+
+ indent -= 1;
}
- // This one must only be thrown by
} catch (IOException e) {
+ Log.e(Constants.TAG, "encountered IOException while modifying key", e);
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_ENCODE, indent+1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (PGPException e) {
@@ -684,6 +878,7 @@ public class PgpKeyOperation {
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_PGP, indent+1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
} catch (SignatureException e) {
+ Log.e(Constants.TAG, "encountered SignatureException while modifying key", e);
log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_SIG, indent+1);
return new EditKeyResult(EditKeyResult.RESULT_ERROR, log, null);
}
@@ -694,21 +889,121 @@ public class PgpKeyOperation {
}
+ /** Update all (non-revoked) uid signatures with new flags and expiry time. */
+ private static PGPPublicKey updateMasterCertificates(
+ PGPPrivateKey masterPrivateKey, PGPPublicKey masterPublicKey,
+ int flags, long expiry, int indent, OperationLog log)
+ throws PGPException, IOException, SignatureException {
+
+ // keep track if we actually changed one
+ boolean ok = false;
+ log.add(LogLevel.DEBUG, LogType.MSG_MF_MASTER, indent);
+ indent += 1;
+
+ PGPPublicKey modifiedPublicKey = masterPublicKey;
+
+ // we work on the modifiedPublicKey here, to respect new or newly revoked uids
+ // noinspection unchecked
+ for (String userId : new IterableIterator<String>(modifiedPublicKey.getUserIDs())) {
+ boolean isRevoked = false;
+ PGPSignature currentCert = null;
+ // noinspection unchecked
+ for (PGPSignature cert : new IterableIterator<PGPSignature>(
+ modifiedPublicKey.getSignaturesForID(userId))) {
+ if (cert.getKeyID() != masterPublicKey.getKeyID()) {
+ // foreign certificate?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return null;
+ }
+ // we know from canonicalization that if there is any revocation here, it
+ // is valid and not superseded by a newer certification.
+ if (cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION) {
+ isRevoked = true;
+ continue;
+ }
+ // we know from canonicalization that there is only one binding
+ // certification here, so we can just work with the first one.
+ if (cert.getSignatureType() == PGPSignature.NO_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.CASUAL_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.POSITIVE_CERTIFICATION ||
+ cert.getSignatureType() == PGPSignature.DEFAULT_CERTIFICATION) {
+ currentCert = cert;
+ }
+ }
+
+ if (currentCert == null) {
+ // no certificate found?! error error error
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_INTEGRITY, indent);
+ return null;
+ }
+
+ // we definitely should not update certifications of revoked keys, so just leave it.
+ if (isRevoked) {
+ continue;
+ }
+
+ // add shiny new user id certificate
+ boolean isPrimary = currentCert.getHashedSubPackets() != null &&
+ currentCert.getHashedSubPackets().isPrimaryUserID();
+ modifiedPublicKey = PGPPublicKey.removeCertification(
+ modifiedPublicKey, userId, currentCert);
+ PGPSignature newCert = generateUserIdSignature(
+ masterPrivateKey, masterPublicKey, userId, isPrimary, flags, expiry);
+ modifiedPublicKey = PGPPublicKey.addCertification(
+ modifiedPublicKey, userId, newCert);
+ ok = true;
+
+ }
+
+ if (!ok) {
+ // might happen, theoretically, if there is a key with no uid..
+ log.add(LogLevel.ERROR, LogType.MSG_MF_ERROR_MASTER_NONE, indent);
+ return null;
+ }
+
+ return modifiedPublicKey;
+
+ }
+
private static PGPSignature generateUserIdSignature(
- PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary, int flags)
+ PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary,
+ int flags, long expiry)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PGPUtil.SHA1)
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
- PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
- subHashedPacketsGen.setSignatureCreationTime(false, new Date());
- subHashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
- subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
- subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
- subHashedPacketsGen.setPrimaryUserID(false, primary);
- subHashedPacketsGen.setKeyFlags(false, flags);
- sGen.setHashedSubpackets(subHashedPacketsGen.generate());
+
+ PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator();
+ {
+ /*
+ * From RFC about critical subpackets:
+ * If a subpacket is encountered that is
+ * marked critical but is unknown to the evaluating software, the
+ * evaluator SHOULD consider the signature to be in error.
+ * An evaluator may "recognize" a subpacket, but not implement it. The
+ * purpose of the critical bit is to allow the signer to tell an
+ * evaluator that it would prefer a new, unknown feature to generate an
+ * error than be ignored.
+ */
+ /* non-critical subpackets: */
+ hashedPacketsGen.setPreferredSymmetricAlgorithms(false, PREFERRED_SYMMETRIC_ALGORITHMS);
+ hashedPacketsGen.setPreferredHashAlgorithms(false, PREFERRED_HASH_ALGORITHMS);
+ hashedPacketsGen.setPreferredCompressionAlgorithms(false, PREFERRED_COMPRESSION_ALGORITHMS);
+ hashedPacketsGen.setPrimaryUserID(false, primary);
+
+ /* critical subpackets: we consider those important for a modern pgp implementation */
+ hashedPacketsGen.setSignatureCreationTime(true, new Date());
+ // Request that senders add the MDC to the message (authenticate unsigned messages)
+ hashedPacketsGen.setFeature(true, Features.FEATURE_MODIFICATION_DETECTION);
+ hashedPacketsGen.setKeyFlags(true, flags);
+ if (expiry > 0) {
+ hashedPacketsGen.setKeyExpirationTime(
+ true, expiry - pKey.getCreationTime().getTime() / 1000);
+ }
+ }
+
+ sGen.setHashedSubpackets(hashedPacketsGen.generate());
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
}
@@ -717,11 +1012,11 @@ public class PgpKeyOperation {
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PGPUtil.SHA1)
+ masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
- subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ subHashedPacketsGen.setSignatureCreationTime(true, new Date());
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
return sGen.generateCertification(userId, pKey);
@@ -731,11 +1026,11 @@ public class PgpKeyOperation {
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey, PGPPublicKey pKey)
throws IOException, PGPException, SignatureException {
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PGPUtil.SHA1)
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
- subHashedPacketsGen.setSignatureCreationTime(false, new Date());
+ subHashedPacketsGen.setSignatureCreationTime(true, new Date());
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
// Generate key revocation or subkey revocation, depending on master/subkey-ness
if (masterPublicKey.getKeyID() == pKey.getKeyID()) {
@@ -765,38 +1060,38 @@ public class PgpKeyOperation {
throws IOException, PGPException, SignatureException {
// date for signing
- Date todayDate = new Date();
+ Date creationTime = new Date();
+
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
// If this key can sign, we need a primary key binding signature
if ((flags & KeyFlags.SIGN_DATA) > 0) {
// cross-certify signing keys
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
- subHashedPacketsGen.setSignatureCreationTime(false, todayDate);
+ subHashedPacketsGen.setSignatureCreationTime(false, creationTime);
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PGPUtil.SHA1)
+ pKey.getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
PGPSignature certification = sGen.generateCertification(masterPublicKey, pKey);
- unhashedPacketsGen.setEmbeddedSignature(false, certification);
+ unhashedPacketsGen.setEmbeddedSignature(true, certification);
}
PGPSignatureSubpacketGenerator hashedPacketsGen;
{
hashedPacketsGen = new PGPSignatureSubpacketGenerator();
- hashedPacketsGen.setSignatureCreationTime(false, todayDate);
- hashedPacketsGen.setKeyFlags(false, flags);
- }
-
- if (expiry > 0) {
- long creationTime = pKey.getCreationTime().getTime() / 1000;
- hashedPacketsGen.setKeyExpirationTime(false, expiry - creationTime);
+ hashedPacketsGen.setSignatureCreationTime(true, creationTime);
+ hashedPacketsGen.setKeyFlags(true, flags);
+ if (expiry > 0) {
+ hashedPacketsGen.setKeyExpirationTime(true,
+ expiry - pKey.getCreationTime().getTime() / 1000);
+ }
}
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
- pKey.getAlgorithm(), PGPUtil.SHA1)
+ masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA512)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 3fe535f65..2e4eafe41 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -70,7 +70,7 @@ public class PgpSignEncrypt {
private long mSignatureMasterKeyId;
private int mSignatureHashAlgorithm;
private String mSignaturePassphrase;
- private boolean mEncryptToSigner;
+ private long mAdditionalEncryptId;
private boolean mCleartextInput;
private String mOriginalFilename;
@@ -103,7 +103,7 @@ public class PgpSignEncrypt {
this.mSignatureMasterKeyId = builder.mSignatureMasterKeyId;
this.mSignatureHashAlgorithm = builder.mSignatureHashAlgorithm;
this.mSignaturePassphrase = builder.mSignaturePassphrase;
- this.mEncryptToSigner = builder.mEncryptToSigner;
+ this.mAdditionalEncryptId = builder.mAdditionalEncryptId;
this.mCleartextInput = builder.mCleartextInput;
this.mNfcSignedHash = builder.mNfcSignedHash;
this.mNfcCreationTimestamp = builder.mNfcCreationTimestamp;
@@ -127,7 +127,7 @@ public class PgpSignEncrypt {
private long mSignatureMasterKeyId = Constants.key.none;
private int mSignatureHashAlgorithm = 0;
private String mSignaturePassphrase = null;
- private boolean mEncryptToSigner = false;
+ private long mAdditionalEncryptId = Constants.key.none;
private boolean mCleartextInput = false;
private String mOriginalFilename = "";
private byte[] mNfcSignedHash = null;
@@ -175,7 +175,7 @@ public class PgpSignEncrypt {
}
public Builder setSignatureMasterKeyId(long signatureMasterKeyId) {
- this.mSignatureMasterKeyId = signatureMasterKeyId;
+ mSignatureMasterKeyId = signatureMasterKeyId;
return this;
}
@@ -192,11 +192,11 @@ public class PgpSignEncrypt {
/**
* Also encrypt with the signing keyring
*
- * @param encryptToSigner
+ * @param additionalEncryptId
* @return
*/
- public Builder setEncryptToSigner(boolean encryptToSigner) {
- mEncryptToSigner = encryptToSigner;
+ public Builder setAdditionalEncryptId(long additionalEncryptId) {
+ mAdditionalEncryptId = additionalEncryptId;
return this;
}
@@ -288,10 +288,10 @@ public class PgpSignEncrypt {
+ "\nenableCompression:" + enableCompression
+ "\nenableAsciiArmorOutput:" + mEnableAsciiArmorOutput);
- // add signature key id to encryption ids (self-encrypt)
- if (enableEncryption && enableSignature && mEncryptToSigner) {
+ // add additional key id to encryption ids (mostly to do self-encryption)
+ if (enableEncryption && mAdditionalEncryptId != Constants.key.none) {
mEncryptionMasterKeyIds = Arrays.copyOf(mEncryptionMasterKeyIds, mEncryptionMasterKeyIds.length + 1);
- mEncryptionMasterKeyIds[mEncryptionMasterKeyIds.length - 1] = mSignatureMasterKeyId;
+ mEncryptionMasterKeyIds[mEncryptionMasterKeyIds.length - 1] = mAdditionalEncryptId;
}
ArmoredOutputStream armorOut = null;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
index 73a51942d..f00383e0f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
@@ -44,6 +45,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.Iterator;
@@ -55,7 +58,8 @@ import java.util.TreeSet;
* This class and its relatives UncachedPublicKey and UncachedSecretKey are
* used to move around pgp key rings in non crypto related (UI, mostly) code.
* It should be used for simple inspection only until it saved in the database,
- * all actual crypto operations should work with WrappedKeyRings exclusively.
+ * all actual crypto operations should work with CanonicalizedKeyRings
+ * exclusively.
*
* This class is also special in that it can hold either the PGPPublicKeyRing
* or PGPSecretKeyRing derivate of the PGPKeyRing class, since these are
@@ -118,6 +122,10 @@ public class UncachedKeyRing {
return mRing.getPublicKey().getFingerprint();
}
+ public int getVersion() {
+ return mRing.getPublicKey().getVersion();
+ }
+
public static UncachedKeyRing decodeFromData(byte[] data)
throws PgpGeneralException, IOException {
@@ -211,8 +219,7 @@ public class UncachedKeyRing {
aos.close();
}
- /** "Canonicalizes" a public key, removing inconsistencies in the process. This variant can be
- * applied to public keyrings only.
+ /** "Canonicalizes" a public key, removing inconsistencies in the process.
*
* More specifically:
* - Remove all non-verifying self-certificates
@@ -229,9 +236,9 @@ public class UncachedKeyRing {
* - If the key is a secret key, remove all certificates by foreign keys
* - If no valid user id remains, log an error and return null
*
- * This operation writes an OperationLog which can be used as part of a OperationResultParcel.
+ * This operation writes an OperationLog which can be used as part of an OperationResultParcel.
*
- * @return A canonicalized key, or null on fatal error
+ * @return A canonicalized key, or null on fatal error (log will include a message in this case)
*
*/
@SuppressWarnings("ConstantConditions")
@@ -241,6 +248,12 @@ public class UncachedKeyRing {
indent, PgpKeyHelper.convertKeyIdToHex(getMasterKeyId()));
indent += 1;
+ // do not accept v3 keys
+ if (getVersion() <= 3) {
+ log.add(LogLevel.ERROR, LogType.MSG_KC_V3_KEY, indent);
+ return null;
+ }
+
final Date now = new Date();
int redundantCerts = 0, badCerts = 0;
@@ -259,13 +272,12 @@ public class UncachedKeyRing {
for (PGPSignature zert : new IterableIterator<PGPSignature>(masterKey.getKeySignatures())) {
int type = zert.getSignatureType();
- // Disregard certifications on user ids, we will deal with those later
+ // These should most definitely not be here...
if (type == PGPSignature.NO_CERTIFICATION
|| type == PGPSignature.DEFAULT_CERTIFICATION
|| type == PGPSignature.CASUAL_CERTIFICATION
|| type == PGPSignature.POSITIVE_CERTIFICATION
|| type == PGPSignature.CERTIFICATION_REVOCATION) {
- // These should not be here...
log.add(LogLevel.WARN, LogType.MSG_KC_REVOKE_BAD_TYPE_UID, indent);
modified = PGPPublicKey.removeCertification(modified, zert);
badCerts += 1;
@@ -328,7 +340,17 @@ public class UncachedKeyRing {
}
}
+ ArrayList<String> processedUserIds = new ArrayList<String>();
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
+ // check for duplicate user ids
+ if (processedUserIds.contains(userId)) {
+ log.add(LogLevel.WARN, LogType.MSG_KC_UID_DUP,
+ indent, userId);
+ // strip out the first found user id with this name
+ modified = PGPPublicKey.removeCertification(modified, userId);
+ }
+ processedUserIds.add(userId);
+
PGPSignature selfCert = null;
revocation = null;
@@ -405,13 +427,13 @@ public class UncachedKeyRing {
if (selfCert == null) {
selfCert = zert;
} else if (selfCert.getCreationTime().before(cert.getCreationTime())) {
- log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP,
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_CERT_DUP,
indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, selfCert);
redundantCerts += 1;
selfCert = zert;
} else {
- log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_DUP,
+ log.add(LogLevel.DEBUG, LogType.MSG_KC_UID_CERT_DUP,
indent, userId);
modified = PGPPublicKey.removeCertification(modified, userId, zert);
redundantCerts += 1;
@@ -474,6 +496,10 @@ public class UncachedKeyRing {
// Replace modified key in the keyring
ring = replacePublicKey(ring, modified);
+ if (ring == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent);
+ return null;
+ }
indent -= 1;
}
@@ -580,8 +606,8 @@ public class UncachedKeyRing {
}
- // if we already have a cert, and this one is not newer: skip it
- if (selfCert != null && selfCert.getCreationTime().before(cert.getCreationTime())) {
+ // if we already have a cert, and this one is older: skip it
+ if (selfCert != null && cert.getCreationTime().before(selfCert.getCreationTime())) {
log.add(LogLevel.DEBUG, LogType.MSG_KC_SUB_DUP, indent);
redundantCerts += 1;
continue;
@@ -641,6 +667,10 @@ public class UncachedKeyRing {
}
// replace pubkey in keyring
ring = replacePublicKey(ring, modified);
+ if (ring == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent);
+ return null;
+ }
indent -= 1;
}
@@ -681,8 +711,9 @@ public class UncachedKeyRing {
long masterKeyId = other.getMasterKeyId();
- if (getMasterKeyId() != masterKeyId) {
- log.add(LogLevel.ERROR, LogType.MSG_MG_HETEROGENEOUS, indent);
+ if (getMasterKeyId() != masterKeyId
+ || !Arrays.equals(getFingerprint(), other.getFingerprint())) {
+ log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_HETEROGENEOUS, indent);
return null;
}
@@ -729,6 +760,10 @@ public class UncachedKeyRing {
} else {
// otherwise, just insert the public key
result = replacePublicKey(result, key);
+ if (result == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent);
+ return null;
+ }
}
continue;
}
@@ -757,6 +792,10 @@ public class UncachedKeyRing {
if (!key.isMasterKey()) {
if (modified != resultKey) {
result = replacePublicKey(result, modified);
+ if (result == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent);
+ return null;
+ }
}
continue;
}
@@ -781,6 +820,10 @@ public class UncachedKeyRing {
// If anything changed, save the updated (sub)key
if (modified != resultKey) {
result = replacePublicKey(result, modified);
+ if (result == null) {
+ log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_SECRET_DUMMY, indent);
+ return null;
+ }
}
}
@@ -795,7 +838,7 @@ public class UncachedKeyRing {
return new UncachedKeyRing(result);
} catch (IOException e) {
- log.add(LogLevel.ERROR, LogType.MSG_MG_FATAL_ENCODE, indent);
+ log.add(LogLevel.ERROR, LogType.MSG_MG_ERROR_ENCODE, indent);
return null;
}
@@ -826,16 +869,19 @@ public class UncachedKeyRing {
*/
private static PGPKeyRing replacePublicKey(PGPKeyRing ring, PGPPublicKey key) {
if (ring instanceof PGPPublicKeyRing) {
- return PGPPublicKeyRing.insertPublicKey((PGPPublicKeyRing) ring, key);
- }
- PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring;
- PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID());
- // TODO generate secret key with S2K dummy, if none exists! for now, just die.
- if (sKey == null) {
- throw new RuntimeException("dummy secret key generation not yet implemented");
+ PGPPublicKeyRing pubRing = (PGPPublicKeyRing) ring;
+ return PGPPublicKeyRing.insertPublicKey(pubRing, key);
+ } else {
+ PGPSecretKeyRing secRing = (PGPSecretKeyRing) ring;
+ PGPSecretKey sKey = secRing.getSecretKey(key.getKeyID());
+ // TODO generate secret key with S2K dummy, if none exists!
+ if (sKey == null) {
+ Log.e(Constants.TAG, "dummy secret key generation not yet implemented");
+ return null;
+ }
+ sKey = PGPSecretKey.replacePublicKey(sKey, key);
+ return PGPSecretKeyRing.insertSecretKey(secRing, sKey);
}
- sKey = PGPSecretKey.replacePublicKey(sKey, key);
- return PGPSecretKeyRing.insertSecretKey(secRing, sKey);
}
/** This method removes a subkey in a keyring.
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
index 341ca6d04..c7a8bb1d0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
@@ -17,6 +18,10 @@
package org.sufficientlysecure.keychain.pgp;
+import org.spongycastle.asn1.ASN1ObjectIdentifier;
+import org.spongycastle.asn1.nist.NISTNamedCurves;
+import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
+import org.spongycastle.bcpg.ECPublicBCPGKey;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature;
@@ -93,10 +98,23 @@ public class UncachedPublicKey {
return mPublicKey.getAlgorithm();
}
- public int getBitStrength() {
+ public Integer getBitStrength() {
+ if (isEC()) {
+ return null;
+ }
return mPublicKey.getBitStrength();
}
+ public String getCurveOid() {
+ if ( ! isEC()) {
+ return null;
+ }
+ if ( ! (mPublicKey.getPublicKeyPacket().getKey() instanceof ECPublicBCPGKey)) {
+ return null;
+ }
+ return ((ECPublicBCPGKey) mPublicKey.getPublicKeyPacket().getKey()).getCurveOID().getId();
+ }
+
/** Returns the primary user id, as indicated by the public key's self certificates.
*
* This is an expensive operation, since potentially a lot of certificates (and revocations)
@@ -185,6 +203,10 @@ public class UncachedPublicKey {
return getAlgorithm() == PGPPublicKey.DSA;
}
+ public boolean isEC() {
+ return getAlgorithm() == PGPPublicKey.ECDH || getAlgorithm() == PGPPublicKey.ECDSA;
+ }
+
@SuppressWarnings("unchecked")
// TODO make this safe
public int getKeyUsage() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java
index 8dc28c2b3..4dfd93289 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedSecretKey.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
index ebd110dc5..f24259ba7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
@@ -95,9 +96,6 @@ public class WrappedSignature {
} catch (PGPException e) {
// no matter
Log.e(Constants.TAG, "exception reading embedded signatures", e);
- } catch (IOException e) {
- // no matter
- Log.e(Constants.TAG, "exception reading embedded signatures", e);
}
return sigs;
}
@@ -149,27 +147,17 @@ public class WrappedSignature {
}
}
- public void update(byte[] data, int offset, int length) throws PgpGeneralException {
- try {
- mSig.update(data, offset, length);
- } catch(SignatureException e) {
- throw new PgpGeneralException(e);
- }
+ public void update(byte[] data, int offset, int length) {
+ mSig.update(data, offset, length);
}
- public void update(byte data) throws PgpGeneralException {
- try {
- mSig.update(data);
- } catch(SignatureException e) {
- throw new PgpGeneralException(e);
- }
+ public void update(byte data) {
+ mSig.update(data);
}
public boolean verify() throws PgpGeneralException {
try {
return mSig.verify();
- } catch(SignatureException e) {
- throw new PgpGeneralException(e);
} catch(PGPException e) {
throw new PgpGeneralException(e);
}
@@ -178,8 +166,6 @@ public class WrappedSignature {
boolean verifySignature(PGPPublicKey key) throws PgpGeneralException {
try {
return mSig.verifyCertification(key);
- } catch (SignatureException e) {
- throw new PgpGeneralException("Sign!", e);
} catch (PGPException e) {
throw new PgpGeneralException("Error!", e);
}
@@ -188,8 +174,6 @@ public class WrappedSignature {
boolean verifySignature(PGPPublicKey masterKey, PGPPublicKey subKey) throws PgpGeneralException {
try {
return mSig.verifyCertification(masterKey, subKey);
- } catch (SignatureException e) {
- throw new PgpGeneralException("Sign!", e);
} catch (PGPException e) {
throw new PgpGeneralException("Error!", e);
}
@@ -198,8 +182,6 @@ public class WrappedSignature {
boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
try {
return mSig.verifyCertification(uid, key);
- } catch (SignatureException e) {
- throw new PgpGeneralException("Error!", e);
} catch (PGPException e) {
throw new PgpGeneralException("Error!", e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
index 3700b4c34..aa0d86bb3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/exception/PgpGeneralMsgIdException.java
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ * 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
index e076fd9cc..21f0dddf6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/CachedPublicKeyRing.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index dd59f8603..4f6e878b4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012-2014 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
@@ -38,6 +39,7 @@ public class KeychainContract {
String FINGERPRINT = "fingerprint";
String KEY_SIZE = "key_size";
+ String KEY_CURVE_OID = "key_curve_oid";
String CAN_SIGN = "can_sign";
String CAN_ENCRYPT = "can_encrypt";
String CAN_CERTIFY = "can_certify";
@@ -111,6 +113,7 @@ public class KeychainContract {
public static final String HAS_ANY_SECRET = "has_any_secret";
public static final String HAS_ENCRYPT = "has_encrypt";
public static final String HAS_SIGN = "has_sign";
+ public static final String HAS_CERTIFY = "has_certify";
public static final String PUBKEY_DATA = "pubkey_data";
public static final String PRIVKEY_DATA = "privkey_data";
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
index 3a859f505..0bb43d47f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012-2014 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
@@ -51,7 +52,7 @@ import java.io.IOException;
*/
public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "openkeychain.db";
- private static final int DATABASE_VERSION = 2;
+ private static final int DATABASE_VERSION = 3;
static Boolean apgHack = false;
public interface Tables {
@@ -85,6 +86,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ KeysColumns.KEY_ID + " INTEGER, "
+ KeysColumns.KEY_SIZE + " INTEGER, "
+ + KeysColumns.KEY_CURVE_OID + " TEXT, "
+ KeysColumns.ALGORITHM + " INTEGER, "
+ KeysColumns.FINGERPRINT + " BLOB, "
@@ -201,13 +203,20 @@ public class KeychainDatabase extends SQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion == 1) {
- // add has_secret for all who are upgrading from a beta version
- try {
- db.execSQL("ALTER TABLE keys ADD COLUMN has_secret BOOLEAN");
- } catch (Exception e) {
- // never mind, the column probably already existed
- }
+ // add has_secret for all who are upgrading from a beta version
+ switch (oldVersion) {
+ case 1:
+ try {
+ db.execSQL("ALTER TABLE keys ADD COLUMN has_secret BOOLEAN");
+ } catch(Exception e){
+ // never mind, the column probably already existed
+ }
+ case 2:
+ try {
+ db.execSQL("ALTER TABLE keys ADD COLUMN " + KeysColumns.KEY_CURVE_OID + " TEXT");
+ } catch(Exception e){
+ // never mind, the column probably already existed
+ }
}
}
@@ -227,7 +236,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
if (db.equals("apg.db")) {
hasApgDb = true;
} else if (db.equals("apg_old.db")) {
- Log.d(Constants.TAG, "Found apg_old.db");
+ Log.d(Constants.TAG, "Found apg_old.db, delete it!");
+ context.getDatabasePath("apg_old.db").delete();
}
}
}
@@ -310,9 +320,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
}
}
- // Move to a different file (but don't delete, just to be safe)
- Log.d(Constants.TAG, "All done - moving apg.db to apg_old.db");
- context.getDatabasePath("apg.db").renameTo(context.getDatabasePath("apg_old.db"));
+ // delete old database
+ context.getDatabasePath("apg.db").delete();
}
private static void copy(File in, File out) throws IOException {
@@ -349,4 +358,9 @@ public class KeychainDatabase extends SQLiteOpenHelper {
copy(in, out);
}
+ // DANGEROUS, use in test code ONLY!
+ public void clearDatabase() {
+ getWritableDatabase().execSQL("delete from " + Tables.KEY_RINGS_PUBLIC);
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index c914cb5b7..f6df4a3eb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ * 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
@@ -245,6 +246,7 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
projectionMap.put(KeyRings.KEY_ID, Tables.KEYS + "." + Keys.KEY_ID);
projectionMap.put(KeyRings.KEY_SIZE, Tables.KEYS + "." + Keys.KEY_SIZE);
+ projectionMap.put(KeyRings.KEY_CURVE_OID, Tables.KEYS + "." + Keys.KEY_CURVE_OID);
projectionMap.put(KeyRings.IS_REVOKED, Tables.KEYS + "." + Keys.IS_REVOKED);
projectionMap.put(KeyRings.CAN_CERTIFY, Tables.KEYS + "." + Keys.CAN_CERTIFY);
projectionMap.put(KeyRings.CAN_ENCRYPT, Tables.KEYS + "." + Keys.CAN_ENCRYPT);
@@ -271,6 +273,8 @@ public class KeychainProvider extends ContentProvider {
"kE." + Keys.KEY_ID + " AS " + KeyRings.HAS_ENCRYPT);
projectionMap.put(KeyRings.HAS_SIGN,
"kS." + Keys.KEY_ID + " AS " + KeyRings.HAS_SIGN);
+ projectionMap.put(KeyRings.HAS_CERTIFY,
+ "kC." + Keys.KEY_ID + " AS " + KeyRings.HAS_CERTIFY);
projectionMap.put(KeyRings.IS_EXPIRED,
"(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS + "." + Keys.EXPIRY
+ " < " + new Date().getTime() / 1000 + ") AS " + KeyRings.IS_EXPIRED);
@@ -324,6 +328,15 @@ public class KeychainProvider extends ContentProvider {
+ " AND ( kS." + Keys.EXPIRY + " IS NULL OR kS." + Keys.EXPIRY
+ " >= " + new Date().getTime() / 1000 + " )"
+ ")" : "")
+ + (plist.contains(KeyRings.HAS_CERTIFY) ?
+ " LEFT JOIN " + Tables.KEYS + " AS kC ON ("
+ +"kC." + Keys.MASTER_KEY_ID
+ + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ + " AND kC." + Keys.IS_REVOKED + " = 0"
+ + " AND kC." + Keys.CAN_CERTIFY + " = 1"
+ + " AND ( kC." + Keys.EXPIRY + " IS NULL OR kC." + Keys.EXPIRY
+ + " >= " + new Date().getTime() / 1000 + " )"
+ + ")" : "")
);
qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
// in case there are multiple verifying certificates
@@ -400,6 +413,7 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(Keys.RANK, Tables.KEYS + "." + Keys.RANK);
projectionMap.put(Keys.KEY_ID, Keys.KEY_ID);
projectionMap.put(Keys.KEY_SIZE, Keys.KEY_SIZE);
+ projectionMap.put(Keys.KEY_CURVE_OID, Keys.KEY_CURVE_OID);
projectionMap.put(Keys.IS_REVOKED, Keys.IS_REVOKED);
projectionMap.put(Keys.CAN_CERTIFY, Keys.CAN_CERTIFY);
projectionMap.put(Keys.CAN_ENCRYPT, Keys.CAN_ENCRYPT);
@@ -674,6 +688,11 @@ public class KeychainProvider extends ContentProvider {
final int match = mUriMatcher.match(uri);
switch (match) {
+ // dangerous
+ case KEY_RINGS_UNIFIED: {
+ count = db.delete(Tables.KEY_RINGS_PUBLIC, null, null);
+ break;
+ }
case KEY_RING_PUBLIC: {
@SuppressWarnings("ConstantConditions") // ensured by uriMatcher above
String selection = KeyRings.MASTER_KEY_ID + " = " + uri.getPathSegments().get(1);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
index a13bb9c98..bb095c340 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012-2014 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
@@ -28,12 +29,16 @@ import android.os.RemoteException;
import android.support.v4.util.LongSparseArray;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.Preferences;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.NullProgressable;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
+import org.sufficientlysecure.keychain.pgp.PgpImportExport;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
@@ -51,9 +56,12 @@ import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogLevel;
import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType;
import org.sufficientlysecure.keychain.service.OperationResultParcel.OperationLog;
+import org.sufficientlysecure.keychain.service.OperationResults.ConsolidateResult;
import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult;
+import org.sufficientlysecure.keychain.util.FileImportCache;
import org.sufficientlysecure.keychain.util.IterableIterator;
import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ProgressFixedScaler;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -63,6 +71,7 @@ import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
@@ -86,6 +95,10 @@ public class ProviderHelper {
this(context, new OperationLog(), 0);
}
+ public ProviderHelper(Context context, OperationLog log) {
+ this(context, log, 0);
+ }
+
public ProviderHelper(Context context, OperationLog log, int indent) {
mContext = context;
mContentResolver = context.getContentResolver();
@@ -93,14 +106,6 @@ public class ProviderHelper {
mIndent = indent;
}
- public void resetLog() {
- if(mLog != null) {
- // Start a new log (leaving the old one intact)
- mLog = new OperationLog();
- mIndent = 0;
- }
- }
-
public OperationLog getLog() {
return mLog;
}
@@ -322,6 +327,7 @@ public class ProviderHelper {
values.put(Keys.KEY_ID, key.getKeyId());
values.put(Keys.KEY_SIZE, key.getBitStrength());
+ values.put(Keys.KEY_CURVE_OID, key.getCurveOid());
values.put(Keys.ALGORITHM, key.getAlgorithm());
values.put(Keys.FINGERPRINT, key.getFingerprint());
@@ -644,7 +650,7 @@ public class ProviderHelper {
if (publicRing.isSecret()) {
log(LogLevel.ERROR, LogType.MSG_IP_BAD_TYPE_SECRET);
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
CanonicalizedPublicKeyRing canPublicRing;
@@ -658,20 +664,20 @@ public class ProviderHelper {
// If this is null, there is an error in the log so we can just return
if (publicRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
// Canonicalize this keyring, to assert a number of assumptions made about it.
canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
if (canPublicRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
// Early breakout if nothing changed
if (Arrays.hashCode(publicRing.getEncoded())
== Arrays.hashCode(oldPublicRing.getEncoded())) {
log(LogLevel.OK, LogType.MSG_IP_SUCCESS_IDENTICAL);
- return new SaveKeyringResult(SaveKeyringResult.UPDATED, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.UPDATED, mLog, null);
}
} catch (NotFoundException e) {
// Not an issue, just means we are dealing with a new keyring.
@@ -679,7 +685,7 @@ public class ProviderHelper {
// Canonicalize this keyring, to assert a number of assumptions made about it.
canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
if (canPublicRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
}
@@ -692,12 +698,12 @@ public class ProviderHelper {
// Merge data from new public ring into secret one
secretRing = secretRing.merge(publicRing, mLog, mIndent);
if (secretRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
// This has always been a secret key ring, this is a safe cast
canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent);
if (canSecretRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
} catch (NotFoundException e) {
@@ -716,11 +722,11 @@ public class ProviderHelper {
}
}
- return new SaveKeyringResult(result, mLog);
+ return new SaveKeyringResult(result, mLog, canSecretRing);
} catch (IOException e) {
log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC);
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
} finally {
mIndent -= 1;
}
@@ -736,7 +742,7 @@ public class ProviderHelper {
if ( ! secretRing.isSecret()) {
log(LogLevel.ERROR, LogType.MSG_IS_BAD_TYPE_PUBLIC);
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
CanonicalizedSecretKeyRing canSecretRing;
@@ -750,14 +756,14 @@ public class ProviderHelper {
// If this is null, there is an error in the log so we can just return
if (secretRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
// Canonicalize this keyring, to assert a number of assumptions made about it.
// This is a safe cast, because we made sure this is a secret ring above
canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent);
if (canSecretRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
// Early breakout if nothing changed
@@ -765,7 +771,7 @@ public class ProviderHelper {
== Arrays.hashCode(oldSecretRing.getEncoded())) {
log(LogLevel.OK, LogType.MSG_IS_SUCCESS_IDENTICAL,
PgpKeyHelper.convertKeyIdToHex(masterKeyId) );
- return new SaveKeyringResult(SaveKeyringResult.UPDATED, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.UPDATED, mLog, null);
}
} catch (NotFoundException e) {
// Not an issue, just means we are dealing with a new keyring
@@ -774,7 +780,7 @@ public class ProviderHelper {
// This is a safe cast, because we made sure this is a secret ring above
canSecretRing = (CanonicalizedSecretKeyRing) secretRing.canonicalize(mLog, mIndent);
if (canSecretRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
}
@@ -787,7 +793,7 @@ public class ProviderHelper {
// Merge data from new secret ring into public one
publicRing = oldPublicRing.merge(secretRing, mLog, mIndent);
if (publicRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
} catch (NotFoundException e) {
@@ -797,30 +803,289 @@ public class ProviderHelper {
CanonicalizedPublicKeyRing canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
if (canPublicRing == null) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
int result;
result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, true);
if ((result & SaveKeyringResult.RESULT_ERROR) == SaveKeyringResult.RESULT_ERROR) {
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
}
progress.setProgress(LogType.MSG_IP_REINSERT_SECRET.getMsgId(), 90, 100);
result = saveCanonicalizedSecretKeyRing(canSecretRing);
- return new SaveKeyringResult(result, mLog);
+ return new SaveKeyringResult(result, mLog, canSecretRing);
} catch (IOException e) {
log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC);
- return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog);
+ return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
} finally {
mIndent -= 1;
}
}
+ public ConsolidateResult consolidateDatabaseStep1(Progressable progress) {
+
+ // 1a. fetch all secret keyrings into a cache file
+ log(LogLevel.START, LogType.MSG_CON);
+ mIndent += 1;
+
+ progress.setProgress(R.string.progress_con_saving, 0, 100);
+
+ try {
+
+ log(LogLevel.DEBUG, LogType.MSG_CON_SAVE_SECRET);
+ mIndent += 1;
+
+ final Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[]{
+ KeyRings.PRIVKEY_DATA, KeyRings.FINGERPRINT, KeyRings.HAS_ANY_SECRET
+ }, KeyRings.HAS_ANY_SECRET + " = 1", null, null);
+
+ if (cursor == null || !cursor.moveToFirst()) {
+ log(LogLevel.ERROR, LogType.MSG_CON_ERROR_DB);
+ return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog);
+ }
+
+ Preferences.getPreferences(mContext).setCachedConsolidateNumSecrets(cursor.getCount());
+
+ FileImportCache<ParcelableKeyRing> cache =
+ new FileImportCache<ParcelableKeyRing>(mContext, "consolidate_secret.pcl");
+ cache.writeCache(new Iterator<ParcelableKeyRing>() {
+ ParcelableKeyRing ring;
+
+ @Override
+ public boolean hasNext() {
+ if (ring != null) {
+ return true;
+ }
+ if (cursor.isAfterLast()) {
+ return false;
+ }
+ ring = new ParcelableKeyRing(cursor.getBlob(0),
+ PgpKeyHelper.convertFingerprintToHex(cursor.getBlob(1)));
+ cursor.moveToNext();
+ return true;
+ }
+
+ @Override
+ public ParcelableKeyRing next() {
+ try {
+ return ring;
+ } finally {
+ ring = null;
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ });
+
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "error saving secret", e);
+ log(LogLevel.ERROR, LogType.MSG_CON_ERROR_IO_SECRET);
+ return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog);
+ } finally {
+ mIndent -= 1;
+ }
+
+ progress.setProgress(R.string.progress_con_saving, 3, 100);
+
+ // 1b. fetch all public keyrings into a cache file
+ try {
+
+ log(LogLevel.DEBUG, LogType.MSG_CON_SAVE_PUBLIC);
+ mIndent += 1;
+
+ final Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[]{
+ KeyRings.PUBKEY_DATA, KeyRings.FINGERPRINT
+ }, null, null, null);
+
+ if (cursor == null || !cursor.moveToFirst()) {
+ log(LogLevel.ERROR, LogType.MSG_CON_ERROR_DB);
+ return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog);
+ }
+
+ Preferences.getPreferences(mContext).setCachedConsolidateNumPublics(cursor.getCount());
+
+ FileImportCache<ParcelableKeyRing> cache =
+ new FileImportCache<ParcelableKeyRing>(mContext, "consolidate_public.pcl");
+ cache.writeCache(new Iterator<ParcelableKeyRing>() {
+ ParcelableKeyRing ring;
+
+ @Override
+ public boolean hasNext() {
+ if (ring != null) {
+ return true;
+ }
+ if (cursor.isAfterLast()) {
+ return false;
+ }
+ ring = new ParcelableKeyRing(cursor.getBlob(0),
+ PgpKeyHelper.convertFingerprintToHex(cursor.getBlob(1)));
+ cursor.moveToNext();
+ return true;
+ }
+
+ @Override
+ public ParcelableKeyRing next() {
+ try {
+ return ring;
+ } finally {
+ ring = null;
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ });
+
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "error saving public", e);
+ log(LogLevel.ERROR, LogType.MSG_CON_ERROR_IO_PUBLIC);
+ return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog);
+ } finally {
+ mIndent -= 1;
+ }
+
+ log(LogLevel.INFO, LogType.MSG_CON_CRITICAL_IN);
+ Preferences.getPreferences(mContext).setCachedConsolidate(true);
+
+ return consolidateDatabaseStep2(progress, false);
+ }
+
+ public ConsolidateResult consolidateDatabaseStep2(Progressable progress) {
+ return consolidateDatabaseStep2(progress, true);
+ }
+
+ private static boolean mConsolidateCritical = false;
+
+ private ConsolidateResult consolidateDatabaseStep2(Progressable progress, boolean recovery) {
+
+ synchronized (ProviderHelper.class) {
+ if (mConsolidateCritical) {
+ log(LogLevel.ERROR, LogType.MSG_CON_ERROR_CONCURRENT);
+ return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog);
+ }
+ mConsolidateCritical = true;
+ }
+
+ try {
+ Preferences prefs = Preferences.getPreferences(mContext);
+
+ // Set flag that we have a cached consolidation here
+ int numSecrets = prefs.getCachedConsolidateNumSecrets();
+ int numPublics = prefs.getCachedConsolidateNumPublics();
+
+ if (recovery) {
+ if (numSecrets >= 0 && numPublics >= 0) {
+ log(LogLevel.START, LogType.MSG_CON_RECOVER, numSecrets, numPublics);
+ } else {
+ log(LogLevel.START, LogType.MSG_CON_RECOVER_UNKNOWN);
+ }
+ mIndent += 1;
+ }
+
+ if (!prefs.getCachedConsolidate()) {
+ log(LogLevel.ERROR, LogType.MSG_CON_ERROR_BAD_STATE);
+ return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog);
+ }
+
+ // 2. wipe database (IT'S DANGEROUS)
+ log(LogLevel.DEBUG, LogType.MSG_CON_DB_CLEAR);
+ mContentResolver.delete(KeyRings.buildUnifiedKeyRingsUri(), null, null);
+
+ FileImportCache<ParcelableKeyRing> cacheSecret =
+ new FileImportCache<ParcelableKeyRing>(mContext, "consolidate_secret.pcl");
+ FileImportCache<ParcelableKeyRing> cachePublic =
+ new FileImportCache<ParcelableKeyRing>(mContext, "consolidate_public.pcl");
+
+ // 3. Re-Import secret keyrings from cache
+ if (numSecrets > 0) try {
+ log(LogLevel.DEBUG, LogType.MSG_CON_REIMPORT_SECRET, numSecrets);
+ mIndent += 1;
+
+ new PgpImportExport(mContext, this,
+ new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport))
+ .importKeyRings(cacheSecret.readCache(false), numSecrets);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "error importing secret", e);
+ log(LogLevel.ERROR, LogType.MSG_CON_ERROR_SECRET);
+ return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog);
+ } finally {
+ mIndent -= 1;
+ }
+ else {
+ log(LogLevel.DEBUG, LogType.MSG_CON_REIMPORT_SECRET_SKIP);
+ }
+
+ // 4. Re-Import public keyrings from cache
+ if (numPublics > 0) try {
+ log(LogLevel.DEBUG, LogType.MSG_CON_REIMPORT_PUBLIC, numPublics);
+ mIndent += 1;
+
+ new PgpImportExport(mContext, this,
+ new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport))
+ .importKeyRings(cachePublic.readCache(false), numPublics);
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "error importing public", e);
+ log(LogLevel.ERROR, LogType.MSG_CON_ERROR_PUBLIC);
+ return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog);
+ } finally {
+ mIndent -= 1;
+ }
+ else {
+ log(LogLevel.DEBUG, LogType.MSG_CON_REIMPORT_PUBLIC_SKIP);
+ }
+
+ log(LogLevel.INFO, LogType.MSG_CON_CRITICAL_OUT);
+ Preferences.getPreferences(mContext).setCachedConsolidate(false);
+
+ // 5. Delete caches
+ try {
+ log(LogLevel.DEBUG, LogType.MSG_CON_DELETE_SECRET);
+ mIndent += 1;
+ cacheSecret.delete();
+ } catch (IOException e) {
+ // doesn't /really/ matter
+ Log.e(Constants.TAG, "IOException during delete of secret cache", e);
+ log(LogLevel.WARN, LogType.MSG_CON_WARN_DELETE_SECRET);
+ } finally {
+ mIndent -= 1;
+ }
+
+ try {
+ log(LogLevel.DEBUG, LogType.MSG_CON_DELETE_PUBLIC);
+ mIndent += 1;
+ cachePublic.delete();
+ } catch (IOException e) {
+ // doesn't /really/ matter
+ Log.e(Constants.TAG, "IOException during deletion of public cache", e);
+ log(LogLevel.WARN, LogType.MSG_CON_WARN_DELETE_PUBLIC);
+ } finally {
+ mIndent -= 1;
+ }
+
+ progress.setProgress(100, 100);
+ log(LogLevel.OK, LogType.MSG_CON_SUCCESS);
+ mIndent -= 1;
+
+ return new ConsolidateResult(ConsolidateResult.RESULT_OK, mLog);
+
+ } finally {
+ mConsolidateCritical = false;
+ }
+
+ }
+
/**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/
@@ -863,9 +1128,7 @@ public class ProviderHelper {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
String version = PgpHelper.getVersionForHeader(mContext);
- if (version != null) {
- keyRing.encodeArmored(bos, version);
- }
+ keyRing.encodeArmored(bos, version);
String armoredKey = bos.toString("UTF-8");
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
index 87c0cc0a6..a65d222da 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 44d37b926..20dfac36d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -23,6 +23,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.text.TextUtils;
import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpMetadata;
@@ -38,6 +39,7 @@ import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -56,11 +58,16 @@ import java.util.Set;
public class OpenPgpService extends RemoteService {
- static final String[] KEYRING_PROJECTION =
- new String[]{
- KeyRings._ID,
- KeyRings.MASTER_KEY_ID,
- };
+ static final String[] EMAIL_SEARCH_PROJECTION = new String[]{
+ KeyRings._ID,
+ KeyRings.MASTER_KEY_ID,
+ KeyRings.IS_EXPIRED,
+ KeyRings.IS_REVOKED,
+ };
+
+ // do not pre-select revoked or expired keys
+ static final String EMAIL_SEARCH_WHERE = KeychainContract.KeyRings.IS_REVOKED + " = 0 AND "
+ + KeychainContract.KeyRings.IS_EXPIRED + " = 0";
/**
* Search database for key ids based on emails.
@@ -69,52 +76,61 @@ public class OpenPgpService extends RemoteService {
* @return
*/
private Intent getKeyIdsFromEmails(Intent data, String[] encryptionUserIds) {
- // find key ids to given emails in database
- ArrayList<Long> keyIds = new ArrayList<Long>();
-
+ boolean noUserIdsCheck = (encryptionUserIds == null || encryptionUserIds.length == 0);
boolean missingUserIdsCheck = false;
boolean duplicateUserIdsCheck = false;
+
+ ArrayList<Long> keyIds = new ArrayList<Long>();
ArrayList<String> missingUserIds = new ArrayList<String>();
ArrayList<String> duplicateUserIds = new ArrayList<String>();
-
- for (String email : encryptionUserIds) {
- Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email);
- Cursor cursor = getContentResolver().query(uri, KEYRING_PROJECTION, null, null, null);
- try {
- if (cursor != null && cursor.moveToFirst()) {
- long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID));
- keyIds.add(id);
- } else {
- missingUserIdsCheck = true;
- missingUserIds.add(email);
- Log.d(Constants.TAG, "user id missing");
- }
- if (cursor != null && cursor.moveToNext()) {
- duplicateUserIdsCheck = true;
- duplicateUserIds.add(email);
- Log.d(Constants.TAG, "more than one user id with the same email");
- }
- } finally {
- if (cursor != null) {
- cursor.close();
+ if (!noUserIdsCheck) {
+ for (String email : encryptionUserIds) {
+ // try to find the key for this specific email
+ Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email);
+ Cursor cursor = getContentResolver().query(uri, EMAIL_SEARCH_PROJECTION, EMAIL_SEARCH_WHERE, null, null);
+ try {
+ // result should be one entry containing the key id
+ if (cursor != null && cursor.moveToFirst()) {
+ long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID));
+ keyIds.add(id);
+ } else {
+ missingUserIdsCheck = true;
+ missingUserIds.add(email);
+ Log.d(Constants.TAG, "user id missing");
+ }
+ // another entry for this email -> too keys with the same email inside user id
+ if (cursor != null && cursor.moveToNext()) {
+ duplicateUserIdsCheck = true;
+ duplicateUserIds.add(email);
+
+ // also pre-select
+ long id = cursor.getLong(cursor.getColumnIndex(KeyRings.MASTER_KEY_ID));
+ keyIds.add(id);
+ Log.d(Constants.TAG, "more than one user id with the same email");
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
}
}
}
- // convert to long[]
+ // convert ArrayList<Long> to long[]
long[] keyIdsArray = new long[keyIds.size()];
for (int i = 0; i < keyIdsArray.length; i++) {
keyIdsArray[i] = keyIds.get(i);
}
- // allow the user to verify pub key selection
- if (missingUserIdsCheck || duplicateUserIdsCheck) {
- // build PendingIntent
+ if (noUserIdsCheck || missingUserIdsCheck || duplicateUserIdsCheck) {
+ // allow the user to verify pub key selection
+
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS);
intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
+ intent.putExtra(RemoteServiceActivity.EXTRA_NO_USER_IDS_CHECK, noUserIdsCheck);
intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
- intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, duplicateUserIds);
+ intent.putExtra(RemoteServiceActivity.EXTRA_DUPLICATE_USER_IDS, duplicateUserIds);
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
@@ -126,16 +142,18 @@ public class OpenPgpService extends RemoteService {
result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
return result;
- }
+ } else {
+ // everything was easy, we have exactly one key for every email
- if (keyIdsArray.length == 0) {
- return null;
- }
+ if (keyIdsArray.length == 0) {
+ Log.e(Constants.TAG, "keyIdsArray.length == 0, should never happen!");
+ }
- Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_KEY_IDS, keyIdsArray);
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
- return result;
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_KEY_IDS, keyIdsArray);
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
+ return result;
+ }
}
private Intent getNfcIntent(Intent data, byte[] hashToSign, int hashAlgo) {
@@ -191,7 +209,7 @@ public class OpenPgpService extends RemoteService {
} catch (PassphraseCacheService.KeyNotFoundException e) {
// secret key that is set for this account is deleted?
// show account config again!
- return getCreateAccountIntent(data, data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME));
+ return getCreateAccountIntent(data, getAccountName(data));
}
}
if (passphrase == null) {
@@ -270,10 +288,9 @@ public class OpenPgpService extends RemoteService {
originalFilename = "";
}
- long[] keyIds;
- if (data.hasExtra(OpenPgpApi.EXTRA_KEY_IDS)) {
- keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS);
- } else if (data.hasExtra(OpenPgpApi.EXTRA_USER_IDS)) {
+ // first try to get key ids from non-ambiguous key id extra
+ long[] keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS);
+ if (keyIds == null) {
// get key ids based on given user ids
String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS);
// give params through to activity...
@@ -285,20 +302,8 @@ public class OpenPgpService extends RemoteService {
// if not success -> result contains a PendingIntent for user interaction
return result;
}
- } else {
- Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_ERROR,
- new OpenPgpError(OpenPgpError.GENERIC_ERROR,
- "Missing parameter user_ids or key_ids!")
- );
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR);
- return result;
}
- // add own key for encryption
- keyIds = Arrays.copyOf(keyIds, keyIds.length + 1);
- keyIds[keyIds.length - 1] = accSettings.getKeyId();
-
// build InputData and write into OutputStream
// Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
@@ -315,7 +320,8 @@ public class OpenPgpService extends RemoteService {
.setCompressionId(accSettings.getCompression())
.setSymmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm())
.setEncryptionMasterKeyIds(keyIds)
- .setOriginalFilename(originalFilename);
+ .setOriginalFilename(originalFilename)
+ .setAdditionalEncryptId(accSettings.getKeyId()); // add acc key for encryption
if (sign) {
String passphrase;
@@ -334,9 +340,6 @@ public class OpenPgpService extends RemoteService {
builder.setSignatureHashAlgorithm(accSettings.getHashAlgorithm())
.setSignatureMasterKeyId(accSettings.getKeyId())
.setSignaturePassphrase(passphrase);
- } else {
- // encrypt only
- builder.setSignatureMasterKeyId(Constants.key.none);
}
try {
@@ -448,7 +451,7 @@ public class OpenPgpService extends RemoteService {
// If signature is unknown we return an _additional_ PendingIntent
// to retrieve the missing key
Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE);
intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, signatureResult.getKeyId());
intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data);
@@ -514,7 +517,7 @@ public class OpenPgpService extends RemoteService {
// If keys are not in db we return an additional PendingIntent
// to retrieve the missing key
Intent intent = new Intent(getBaseContext(), ImportKeysActivity.class);
- intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN);
+ intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE);
intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, masterKeyId);
intent.putExtra(ImportKeysActivity.EXTRA_PENDING_INTENT_DATA, data);
@@ -596,6 +599,16 @@ public class OpenPgpService extends RemoteService {
return null;
}
+ private String getAccountName(Intent data) {
+ String accName = data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME);
+ // if no account name is given use name "default"
+ if (TextUtils.isEmpty(accName)) {
+ accName = "default";
+ }
+ Log.d(Constants.TAG, "accName: " + accName);
+ return accName;
+ }
+
// TODO: multi-threading
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
@@ -606,12 +619,7 @@ public class OpenPgpService extends RemoteService {
return errorResult;
}
- String accName;
- if (data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME) != null) {
- accName = data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME);
- } else {
- accName = "default";
- }
+ String accName = getAccountName(data);
final AccountSettings accSettings = getAccSettings(accName);
if (accSettings == null) {
return getCreateAccountIntent(data, accName);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
index f70324e2c..e71b52ccd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java
@@ -27,6 +27,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.Binder;
+import android.text.TextUtils;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpApi;
@@ -160,7 +161,7 @@ public abstract class RemoteService extends Service {
*/
protected AccountSettings getAccSettings(String accountName) {
String currentPkg = getCurrentCallingPackage();
- Log.d(Constants.TAG, "accountName: " + accountName);
+ Log.d(Constants.TAG, "getAccSettings accountName: "+ accountName);
Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName);
@@ -171,7 +172,7 @@ public abstract class RemoteService extends Service {
protected Intent getCreateAccountIntent(Intent data, String accountName) {
String packageName = getCurrentCallingPackage();
- Log.d(Constants.TAG, "accountName: " + accountName);
+ Log.d(Constants.TAG, "getCreateAccountIntent accountName: " + accountName);
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_CREATE_ACCOUNT);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
index 666252353..67ad0822d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
@@ -30,6 +30,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ActionBarHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.util.Log;
public class AccountSettingsActivity extends ActionBarActivity {
@@ -106,4 +107,15 @@ public class AccountSettingsActivity extends ActionBarActivity {
finish();
}
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // if a result has been returned, display a notify
+ if (data != null && data.hasExtra(OperationResultParcel.EXTRA_RESULT)) {
+ OperationResultParcel result = data.getParcelableExtra(OperationResultParcel.EXTRA_RESULT);
+ result.createNotify(this).show();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
index 8468f5eca..2cab23e51 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java
@@ -36,6 +36,8 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AccountSettings;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment;
import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter;
@@ -177,24 +179,19 @@ public class AccountSettingsFragment extends Fragment implements
switch (requestCode) {
case REQUEST_CODE_CREATE_KEY: {
if (resultCode == Activity.RESULT_OK) {
- // select newly created key
- try {
- long masterKeyId = new ProviderHelper(getActivity())
- .getCachedPublicKeyRing(data.getData())
- .extractOrGetMasterKeyId();
- mSelectKeyFragment.selectKey(masterKeyId);
- } catch (PgpGeneralException e) {
- Log.e(Constants.TAG, "key not found!", e);
+ if (data != null && data.hasExtra(OperationResultParcel.EXTRA_RESULT)) {
+ OperationResults.SaveKeyringResult result = data.getParcelableExtra(OperationResultParcel.EXTRA_RESULT);
+ mSelectKeyFragment.selectKey(result.mRingMasterKeyId);
+ } else {
+ Log.e(Constants.TAG, "missing result!");
}
}
break;
}
-
- default:
- super.onActivityResult(requestCode, resultCode, data);
-
- break;
}
+
+ // execute activity's onActivityResult to show log notify
+ super.onActivityResult(requestCode, resultCode, data);
}
/**
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
index 48c76d561..4b27e115b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
@@ -18,11 +18,20 @@
package org.sufficientlysecure.keychain.remote.ui;
import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.SpannedString;
+import android.text.TextUtils;
+import android.text.style.BulletSpan;
+import android.text.style.StyleSpan;
import android.view.View;
import android.widget.TextView;
@@ -39,7 +48,6 @@ import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
-import java.security.Provider;
import java.util.ArrayList;
public class RemoteServiceActivity extends ActionBarActivity {
@@ -68,7 +76,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
// select pub keys action
public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids";
public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids";
- public static final String EXTRA_DUBLICATE_USER_IDS = "dublicate_user_ids";
+ public static final String EXTRA_DUPLICATE_USER_IDS = "dublicate_user_ids";
+ public static final String EXTRA_NO_USER_IDS_CHECK = "no_user_ids";
// error message
public static final String EXTRA_ERROR_MESSAGE = "error_message";
@@ -229,32 +238,41 @@ public class RemoteServiceActivity extends ActionBarActivity {
} else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
+ boolean noUserIdsCheck = intent.getBooleanExtra(EXTRA_NO_USER_IDS_CHECK, true);
ArrayList<String> missingUserIds = intent
.getStringArrayListExtra(EXTRA_MISSING_USER_IDS);
ArrayList<String> dublicateUserIds = intent
- .getStringArrayListExtra(EXTRA_DUBLICATE_USER_IDS);
+ .getStringArrayListExtra(EXTRA_DUPLICATE_USER_IDS);
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+ final SpannableString textIntro = new SpannableString(
+ noUserIdsCheck ? getString(R.string.api_select_pub_keys_text_no_user_ids)
+ : getString(R.string.api_select_pub_keys_text)
+ );
+ textIntro.setSpan(new StyleSpan(Typeface.BOLD), 0, textIntro.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append(textIntro);
- // TODO: do this with spannable instead of HTML to prevent parsing failures with weird user ids
- String text = "<b>" + getString(R.string.api_select_pub_keys_text) + "</b>";
- text += "<br/><br/>";
if (missingUserIds != null && missingUserIds.size() > 0) {
- text += getString(R.string.api_select_pub_keys_missing_text);
- text += "<br/>";
- text += "<ul>";
+ ssb.append("\n\n");
+ ssb.append(getString(R.string.api_select_pub_keys_missing_text));
+ ssb.append("\n");
for (String userId : missingUserIds) {
- text += "<li>" + userId + "</li>";
+ SpannableString ss = new SpannableString(userId + "\n");
+ ss.setSpan(new BulletSpan(15, Color.BLACK), 0, ss.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append(ss);
}
- text += "</ul>";
- text += "<br/>";
}
if (dublicateUserIds != null && dublicateUserIds.size() > 0) {
- text += getString(R.string.api_select_pub_keys_dublicates_text);
- text += "<br/>";
- text += "<ul>";
+ ssb.append("\n\n");
+ ssb.append(getString(R.string.api_select_pub_keys_dublicates_text));
+ ssb.append("\n");
for (String userId : dublicateUserIds) {
- text += "<li>" + userId + "</li>";
+ SpannableString ss = new SpannableString(userId + "\n");
+ ss.setSpan(new BulletSpan(15, Color.BLACK), 0, ss.length(),
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append(ss);
}
- text += "</ul>";
}
// Inflate a "Done"/"Cancel" custom action bar view
@@ -284,8 +302,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
setContentView(R.layout.api_remote_select_pub_keys);
// set text on view
- HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text);
- textView.setHtmlFromString(text, true);
+ TextView textView = (TextView) findViewById(R.id.api_select_pub_keys_text);
+ textView.setText(ssb, TextView.BufferType.SPANNABLE);
/* Load select pub keys fragment */
// Check that the activity is using the layout version with
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
index 43ed2dad0..fbe914b78 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
@@ -21,6 +21,8 @@ import android.accounts.Account;
import android.app.Service;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.os.Bundle;
@@ -29,9 +31,11 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
+import android.provider.ContactsContract;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.KeychainApplication;
+import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.ContactHelper;
import org.sufficientlysecure.keychain.helper.EmailKeyHelper;
import org.sufficientlysecure.keychain.util.Log;
@@ -42,7 +46,7 @@ public class ContactSyncAdapterService extends Service {
private class ContactSyncAdapter extends AbstractThreadedSyncAdapter {
- private final AtomicBoolean importDone = new AtomicBoolean(false);
+// private final AtomicBoolean importDone = new AtomicBoolean(false);
public ContactSyncAdapter() {
super(ContactSyncAdapterService.this, true);
@@ -51,47 +55,59 @@ public class ContactSyncAdapterService extends Service {
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
final SyncResult syncResult) {
- importDone.set(false);
- KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this);
- EmailKeyHelper.importContacts(getContext(), new Messenger(new Handler(Looper.getMainLooper(),
- new Handler.Callback() {
- @Override
- public boolean handleMessage(Message msg) {
- Bundle data = msg.getData();
- switch (msg.arg1) {
- case KeychainIntentServiceHandler.MESSAGE_OKAY:
- Log.d(Constants.TAG, "Syncing... Done.");
- synchronized (importDone) {
- importDone.set(true);
- importDone.notifyAll();
- }
- return true;
- case KeychainIntentServiceHandler.MESSAGE_UPDATE_PROGRESS:
- if (data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS) &&
- data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)) {
- Log.d(Constants.TAG, "Syncing... Progress: " +
- data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS) + "/" +
- data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS_MAX));
- return false;
- }
- default:
- Log.d(Constants.TAG, "Syncing... " + msg.toString());
- return false;
- }
- }
- })));
- synchronized (importDone) {
- try {
- if (!importDone.get()) importDone.wait();
- } catch (InterruptedException e) {
- Log.w(Constants.TAG, e);
- return;
- }
- }
+ Log.d(Constants.TAG, "Performing a sync!");
+ // TODO: Import is currently disabled for 2.8, until we implement proper origin management
+// importDone.set(false);
+// KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this);
+// EmailKeyHelper.importContacts(getContext(), new Messenger(new Handler(Looper.getMainLooper(),
+// new Handler.Callback() {
+// @Override
+// public boolean handleMessage(Message msg) {
+// Bundle data = msg.getData();
+// switch (msg.arg1) {
+// case KeychainIntentServiceHandler.MESSAGE_OKAY:
+// Log.d(Constants.TAG, "Syncing... Done.");
+// synchronized (importDone) {
+// importDone.set(true);
+// importDone.notifyAll();
+// }
+// return true;
+// case KeychainIntentServiceHandler.MESSAGE_UPDATE_PROGRESS:
+// if (data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS) &&
+// data.containsKey(KeychainIntentServiceHandler.DATA_PROGRESS_MAX)) {
+// Log.d(Constants.TAG, "Syncing... Progress: " +
+// data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS) + "/" +
+// data.getInt(KeychainIntentServiceHandler.DATA_PROGRESS_MAX));
+// return false;
+// }
+// default:
+// Log.d(Constants.TAG, "Syncing... " + msg.toString());
+// return false;
+// }
+// }
+// })));
+// synchronized (importDone) {
+// try {
+// if (!importDone.get()) importDone.wait();
+// } catch (InterruptedException e) {
+// Log.w(Constants.TAG, e);
+// return;
+// }
+// }
ContactHelper.writeKeysToContacts(ContactSyncAdapterService.this);
}
}
+ public static void requestSync() {
+ Bundle extras = new Bundle();
+ // no need to wait for internet connection!
+ extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+ ContentResolver.requestSync(
+ new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE),
+ ContactsContract.AUTHORITY,
+ extras);
+ }
+
@Override
public IBinder onBind(Intent intent) {
return new ContactSyncAdapter().getSyncAdapterBinder();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
index d6c470e11..021e6bc07 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012-2014 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
@@ -49,11 +50,14 @@ import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
+import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.OperationResults.ConsolidateResult;
import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
+import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResult;
import org.sufficientlysecure.keychain.util.FileImportCache;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@@ -102,6 +106,10 @@ public class KeychainIntentService extends IntentService
public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
+ public static final String ACTION_DELETE = Constants.INTENT_PREFIX + "DELETE";
+
+ public static final String ACTION_CONSOLIDATE = Constants.INTENT_PREFIX + "CONSOLIDATE";
+
/* keys for data bundle */
// encrypt, decrypt, import export
@@ -139,8 +147,13 @@ public class KeychainIntentService extends IntentService
// delete file securely
public static final String DELETE_FILE = "deleteFile";
+ // delete keyring(s)
+ public static final String DELETE_KEY_LIST = "delete_list";
+ public static final String DELETE_IS_SECRET = "delete_is_secret";
+
// import key
public static final String IMPORT_KEY_LIST = "import_key_list";
+ public static final String IMPORT_KEY_FILE = "import_key_file";
// export key
public static final String EXPORT_OUTPUT_STREAM = "export_output_stream";
@@ -162,6 +175,10 @@ public class KeychainIntentService extends IntentService
public static final String CERTIFY_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
public static final String CERTIFY_KEY_UIDS = "sign_key_uids";
+ // consolidate
+ public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
+
+
/*
* possible data keys as result send over messenger
*/
@@ -176,8 +193,6 @@ public class KeychainIntentService extends IntentService
// export
public static final String RESULT_EXPORT = "exported";
- public static final String RESULT_IMPORT = "result";
-
Messenger mMessenger;
private boolean mIsCanceled;
@@ -246,27 +261,31 @@ public class KeychainIntentService extends IntentService
String originalFilename = getOriginalFilename(data);
/* Operation */
- PgpSignEncrypt.Builder builder =
- new PgpSignEncrypt.Builder(
- new ProviderHelper(this),
- inputData, outStream);
- builder.setProgressable(this);
-
- builder.setEnableAsciiArmorOutput(useAsciiArmor)
+ PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
+ new ProviderHelper(this),
+ inputData, outStream
+ );
+ builder.setProgressable(this)
+ .setEnableAsciiArmorOutput(useAsciiArmor)
.setVersionHeader(PgpHelper.getVersionForHeader(this))
.setCompressionId(compressionId)
.setSymmetricEncryptionAlgorithm(
Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
.setEncryptionMasterKeyIds(encryptionKeyIds)
.setSymmetricPassphrase(symmetricPassphrase)
- .setSignatureMasterKeyId(signatureKeyId)
- .setEncryptToSigner(true)
- .setSignatureHashAlgorithm(
- Preferences.getPreferences(this).getDefaultHashAlgorithm())
- .setSignaturePassphrase(
- PassphraseCacheService.getCachedPassphrase(this, signatureKeyId))
.setOriginalFilename(originalFilename);
+ try {
+ builder.setSignatureMasterKeyId(signatureKeyId)
+ .setSignaturePassphrase(
+ PassphraseCacheService.getCachedPassphrase(this, signatureKeyId))
+ .setSignatureHashAlgorithm(
+ Preferences.getPreferences(this).getDefaultHashAlgorithm())
+ .setAdditionalEncryptId(signatureKeyId);
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ // encrypt-only
+ }
+
// this assumes that the bytes are cleartext (valid for current implementation!)
if (source == IO_BYTES) {
builder.setCleartextInput(true);
@@ -391,23 +410,41 @@ public class KeychainIntentService extends IntentService
}
/* Operation */
- ProviderHelper providerHelper = new ProviderHelper(this);
PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 10, 60, 100));
- EditKeyResult result;
+ EditKeyResult modifyResult;
if (saveParcel.mMasterKeyId != null) {
String passphrase = data.getString(SAVE_KEYRING_PASSPHRASE);
CanonicalizedSecretKeyRing secRing =
- providerHelper.getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId);
+ new ProviderHelper(this).getCanonicalizedSecretKeyRing(saveParcel.mMasterKeyId);
- result = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase);
+ modifyResult = keyOperations.modifySecretKeyRing(secRing, saveParcel, passphrase);
} else {
- result = keyOperations.createSecretKeyRing(saveParcel);
+ modifyResult = keyOperations.createSecretKeyRing(saveParcel);
}
- UncachedKeyRing ring = result.getRing();
+ // If the edit operation didn't succeed, exit here
+ if (!modifyResult.success()) {
+ // always return SaveKeyringResult, so create one out of the EditKeyResult
+ SaveKeyringResult saveResult = new SaveKeyringResult(
+ SaveKeyringResult.RESULT_ERROR,
+ modifyResult.getLog(),
+ null);
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult);
+ return;
+ }
- providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100));
+ UncachedKeyRing ring = modifyResult.getRing();
+
+ // Save the keyring. The ProviderHelper is initialized with the previous log
+ SaveKeyringResult saveResult = new ProviderHelper(this, modifyResult.getLog())
+ .saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100));
+
+ // If the edit operation didn't succeed, exit here
+ if (!saveResult.success()) {
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult);
+ return;
+ }
// cache new passphrase
if (saveParcel.mNewPassphrase != null) {
@@ -417,8 +454,11 @@ public class KeychainIntentService extends IntentService
setProgress(R.string.progress_done, 100, 100);
+ // make sure new data is synced into contacts
+ ContactSyncAdapterService.requestSync();
+
/* Output */
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, saveResult);
} catch (Exception e) {
sendErrorToHandler(e);
}
@@ -454,17 +494,21 @@ public class KeychainIntentService extends IntentService
} else {
// get entries from cached file
FileImportCache<ParcelableKeyRing> cache =
- new FileImportCache<ParcelableKeyRing>(this);
+ new FileImportCache<ParcelableKeyRing>(this, "key_import.pcl");
entries = cache.readCacheIntoList();
}
- PgpImportExport pgpImportExport = new PgpImportExport(this, this);
+ ProviderHelper providerHelper = new ProviderHelper(this);
+ PgpImportExport pgpImportExport = new PgpImportExport(this, providerHelper, this);
ImportKeyResult result = pgpImportExport.importKeyRings(entries);
- Bundle resultData = new Bundle();
- resultData.putParcelable(RESULT_IMPORT, result);
+ if (result.mSecret > 0) {
+ providerHelper.consolidateDatabaseStep1(this);
+ }
+ // make sure new data is synced into contacts
+ ContactSyncAdapterService.requestSync();
- sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
} catch (Exception e) {
sendErrorToHandler(e);
}
@@ -549,8 +593,9 @@ public class KeychainIntentService extends IntentService
CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
- boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring);
- if (!uploaded) {
+ try {
+ pgpImportExport.uploadKeyRingToServer(server, keyring);
+ } catch (Keyserver.AddKeyException e) {
throw new PgpGeneralException("Unable to export key to selected server");
}
@@ -639,7 +684,56 @@ public class KeychainIntentService extends IntentService
} catch (Exception e) {
sendErrorToHandler(e);
}
+
+ } else if (ACTION_DELETE.equals(action)) {
+
+ try {
+
+ long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
+ boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
+
+ if (masterKeyIds.length == 0) {
+ throw new PgpGeneralException("List of keys to delete is empty");
+ }
+
+ if (isSecret && masterKeyIds.length > 1) {
+ throw new PgpGeneralException("Secret keys can only be deleted individually!");
+ }
+
+ boolean success = false;
+ for (long masterKeyId : masterKeyIds) {
+ int count = getContentResolver().delete(
+ KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null
+ );
+ success |= count > 0;
+ }
+
+ if (isSecret && success) {
+ ConsolidateResult result =
+ new ProviderHelper(this).consolidateDatabaseStep1(this);
+ }
+
+ if (success) {
+ // make sure new data is synced into contacts
+ ContactSyncAdapterService.requestSync();
+
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
+ }
+
+ } catch (Exception e) {
+ sendErrorToHandler(e);
+ }
+
+ } else if (ACTION_CONSOLIDATE.equals(action)) {
+ ConsolidateResult result;
+ if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) {
+ result = new ProviderHelper(this).consolidateDatabaseStep2(this);
+ } else {
+ result = new ProviderHelper(this).consolidateDatabaseStep1(this);
+ }
+ sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result);
}
+
}
private void sendErrorToHandler(Exception e) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
index d7d98fd68..142bf65cc 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
@@ -155,10 +156,8 @@ public class OperationResultParcel implements Parcelable {
if ((resultType & OperationResultParcel.RESULT_ERROR) == 0) {
if (getLog().containsWarnings()) {
- duration = 0;
color = Style.ORANGE;
} else {
- duration = SuperToast.Duration.LONG;
color = Style.GREEN;
}
@@ -167,7 +166,6 @@ public class OperationResultParcel implements Parcelable {
} else {
- duration = 0;
color = Style.RED;
str = "operation failed";
@@ -180,8 +178,8 @@ public class OperationResultParcel implements Parcelable {
button ? SuperToast.Type.BUTTON : SuperToast.Type.STANDARD,
Style.getStyle(color, SuperToast.Animations.POPUP));
toast.setText(str);
- toast.setDuration(duration);
- toast.setIndeterminate(duration == 0);
+ toast.setDuration(SuperToast.Duration.EXTRA_LONG);
+ toast.setIndeterminate(false);
toast.setSwipeToDismiss(true);
// If we have a log and it's non-empty, show a View Log button
if (button) {
@@ -289,6 +287,7 @@ public class OperationResultParcel implements Parcelable {
MSG_IS_SUCCESS (R.string.msg_is_success),
// keyring canonicalization
+ MSG_KC_V3_KEY (R.string.msg_kc_v3_key),
MSG_KC_PUBLIC (R.string.msg_kc_public),
MSG_KC_SECRET (R.string.msg_kc_secret),
MSG_KC_FATAL_NO_UID (R.string.msg_kc_fatal_no_uid),
@@ -324,6 +323,7 @@ public class OperationResultParcel implements Parcelable {
MSG_KC_UID_BAD_TIME (R.string.msg_kc_uid_bad_time),
MSG_KC_UID_BAD_TYPE (R.string.msg_kc_uid_bad_type),
MSG_KC_UID_BAD (R.string.msg_kc_uid_bad),
+ MSG_KC_UID_CERT_DUP (R.string.msg_kc_uid_cert_dup),
MSG_KC_UID_DUP (R.string.msg_kc_uid_dup),
MSG_KC_UID_FOREIGN (R.string.msg_kc_uid_foreign),
MSG_KC_UID_NO_CERT (R.string.msg_kc_uid_no_cert),
@@ -333,10 +333,11 @@ public class OperationResultParcel implements Parcelable {
// keyring consolidation
+ MSG_MG_ERROR_SECRET_DUMMY(R.string.msg_mg_error_secret_dummy),
+ MSG_MG_ERROR_ENCODE(R.string.msg_mg_error_encode),
+ MSG_MG_ERROR_HETEROGENEOUS(R.string.msg_mg_error_heterogeneous),
MSG_MG_PUBLIC (R.string.msg_mg_public),
MSG_MG_SECRET (R.string.msg_mg_secret),
- MSG_MG_FATAL_ENCODE (R.string.msg_mg_fatal_encode),
- MSG_MG_HETEROGENEOUS (R.string.msg_mg_heterogeneous),
MSG_MG_NEW_SUBKEY (R.string.msg_mg_new_subkey),
MSG_MG_FOUND_NEW (R.string.msg_mg_found_new),
MSG_MG_UNCHANGED (R.string.msg_mg_unchanged),
@@ -346,10 +347,16 @@ public class OperationResultParcel implements Parcelable {
MSG_CR_ERROR_NO_MASTER (R.string.msg_cr_error_no_master),
MSG_CR_ERROR_NO_USER_ID (R.string.msg_cr_error_no_user_id),
MSG_CR_ERROR_NO_CERTIFY (R.string.msg_cr_error_no_certify),
+ MSG_CR_ERROR_NULL_EXPIRY(R.string.msg_cr_error_null_expiry),
MSG_CR_ERROR_KEYSIZE_512 (R.string.msg_cr_error_keysize_512),
+ MSG_CR_ERROR_NO_KEYSIZE (R.string.msg_cr_error_no_keysize),
+ MSG_CR_ERROR_NO_CURVE (R.string.msg_cr_error_no_curve),
MSG_CR_ERROR_UNKNOWN_ALGO (R.string.msg_cr_error_unknown_algo),
MSG_CR_ERROR_INTERNAL_PGP (R.string.msg_cr_error_internal_pgp),
- MSG_CR_ERROR_MASTER_ELGAMAL (R.string.msg_cr_error_master_elgamal),
+ MSG_CR_ERROR_FLAGS_DSA (R.string.msg_cr_error_flags_dsa),
+ MSG_CR_ERROR_FLAGS_ELGAMAL (R.string.msg_cr_error_flags_elgamal),
+ MSG_CR_ERROR_FLAGS_ECDSA (R.string.msg_cr_error_flags_ecdsa),
+ MSG_CR_ERROR_FLAGS_ECDH (R.string.msg_cr_error_flags_ecdh),
// secret key modify
MSG_MF (R.string.msg_mr),
@@ -357,18 +364,27 @@ public class OperationResultParcel implements Parcelable {
MSG_MF_ERROR_FINGERPRINT (R.string.msg_mf_error_fingerprint),
MSG_MF_ERROR_KEYID (R.string.msg_mf_error_keyid),
MSG_MF_ERROR_INTEGRITY (R.string.msg_mf_error_integrity),
+ MSG_MF_ERROR_MASTER_NONE(R.string.msg_mf_error_master_none),
+ MSG_MF_ERROR_NO_CERTIFY (R.string.msg_cr_error_no_certify),
MSG_MF_ERROR_NOEXIST_PRIMARY (R.string.msg_mf_error_noexist_primary),
- MSG_MF_ERROR_REVOKED_PRIMARY (R.string.msg_mf_error_revoked_primary),
+ MSG_MF_ERROR_NOEXIST_REVOKE (R.string.msg_mf_error_noexist_revoke),
+ MSG_MF_ERROR_NULL_EXPIRY (R.string.msg_mf_error_null_expiry),
+ MSG_MF_ERROR_PASSPHRASE_MASTER(R.string.msg_mf_error_passphrase_master),
+ MSG_MF_ERROR_PAST_EXPIRY(R.string.msg_mf_error_past_expiry),
MSG_MF_ERROR_PGP (R.string.msg_mf_error_pgp),
+ MSG_MF_ERROR_REVOKED_PRIMARY (R.string.msg_mf_error_revoked_primary),
MSG_MF_ERROR_SIG (R.string.msg_mf_error_sig),
+ MSG_MF_ERROR_SUBKEY_MISSING(R.string.msg_mf_error_subkey_missing),
+ MSG_MF_MASTER (R.string.msg_mf_master),
MSG_MF_PASSPHRASE (R.string.msg_mf_passphrase),
+ MSG_MF_PASSPHRASE_KEY (R.string.msg_mf_passphrase_key),
+ MSG_MF_PASSPHRASE_EMPTY_RETRY (R.string.msg_mf_passphrase_empty_retry),
+ MSG_MF_PASSPHRASE_FAIL (R.string.msg_mf_passphrase_fail),
MSG_MF_PRIMARY_REPLACE_OLD (R.string.msg_mf_primary_replace_old),
MSG_MF_PRIMARY_NEW (R.string.msg_mf_primary_new),
MSG_MF_SUBKEY_CHANGE (R.string.msg_mf_subkey_change),
- MSG_MF_SUBKEY_MISSING (R.string.msg_mf_subkey_missing),
MSG_MF_SUBKEY_NEW_ID (R.string.msg_mf_subkey_new_id),
MSG_MF_SUBKEY_NEW (R.string.msg_mf_subkey_new),
- MSG_MF_SUBKEY_PAST_EXPIRY (R.string.msg_mf_subkey_past_expiry),
MSG_MF_SUBKEY_REVOKE (R.string.msg_mf_subkey_revoke),
MSG_MF_SUCCESS (R.string.msg_mf_success),
MSG_MF_UID_ADD (R.string.msg_mf_uid_add),
@@ -377,6 +393,32 @@ public class OperationResultParcel implements Parcelable {
MSG_MF_UID_ERROR_EMPTY (R.string.msg_mf_uid_error_empty),
MSG_MF_UNLOCK_ERROR (R.string.msg_mf_unlock_error),
MSG_MF_UNLOCK (R.string.msg_mf_unlock),
+
+ // consolidate
+ MSG_CON_CRITICAL_IN (R.string.msg_con_critical_in),
+ MSG_CON_CRITICAL_OUT (R.string.msg_con_critical_out),
+ MSG_CON_DB_CLEAR (R.string.msg_con_db_clear),
+ MSG_CON_DELETE_PUBLIC (R.string.msg_con_delete_public),
+ MSG_CON_DELETE_SECRET (R.string.msg_con_delete_secret),
+ MSG_CON_ERROR_BAD_STATE (R.string.msg_con_error_bad_state),
+ MSG_CON_ERROR_CONCURRENT(R.string.msg_con_error_concurrent),
+ MSG_CON_ERROR_DB (R.string.msg_con_error_db),
+ MSG_CON_ERROR_IO_PUBLIC (R.string.msg_con_error_io_public),
+ MSG_CON_ERROR_IO_SECRET (R.string.msg_con_error_io_secret),
+ MSG_CON_ERROR_PUBLIC (R.string.msg_con_error_public),
+ MSG_CON_ERROR_SECRET (R.string.msg_con_error_secret),
+ MSG_CON_RECOVER (R.plurals.msg_con_recover),
+ MSG_CON_RECOVER_UNKNOWN (R.string.msg_con_recover_unknown),
+ MSG_CON_REIMPORT_PUBLIC (R.plurals.msg_con_reimport_public),
+ MSG_CON_REIMPORT_PUBLIC_SKIP (R.string.msg_con_reimport_public_skip),
+ MSG_CON_REIMPORT_SECRET (R.plurals.msg_con_reimport_secret),
+ MSG_CON_REIMPORT_SECRET_SKIP (R.string.msg_con_reimport_secret_skip),
+ MSG_CON (R.string.msg_con),
+ MSG_CON_SAVE_PUBLIC (R.string.msg_con_save_public),
+ MSG_CON_SAVE_SECRET (R.string.msg_con_save_secret),
+ MSG_CON_SUCCESS (R.string.msg_con_success),
+ MSG_CON_WARN_DELETE_PUBLIC (R.string.msg_con_warn_delete_public),
+ MSG_CON_WARN_DELETE_SECRET (R.string.msg_con_warn_delete_secret),
;
private final int mMsgId;
@@ -406,7 +448,9 @@ public class OperationResultParcel implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mResult);
- dest.writeTypedList(mLog.toList());
+ if (mLog != null) {
+ dest.writeTypedList(mLog.toList());
+ }
}
public static final Creator<OperationResultParcel> CREATOR = new Creator<OperationResultParcel>() {
@@ -432,6 +476,15 @@ public class OperationResultParcel implements Parcelable {
mParcels.add(new OperationResultParcel.LogEntryParcel(level, type, indent, (Object[]) null));
}
+ public boolean containsType(LogType type) {
+ for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {
+ if (entry.mType == type) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public boolean containsWarnings() {
for(LogEntryParcel entry : new IterableIterator<LogEntryParcel>(mParcels.iterator())) {
if (entry.mLevel == LogLevel.WARN || entry.mLevel == LogLevel.ERROR) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
index 543b83edb..f3d0b9e9b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResults.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
@@ -28,7 +29,10 @@ import com.github.johnpersano.supertoasts.SuperToast;
import com.github.johnpersano.supertoasts.util.OnClickWrapper;
import com.github.johnpersano.supertoasts.util.Style;
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
@@ -37,7 +41,7 @@ public abstract class OperationResults {
public static class ImportKeyResult extends OperationResultParcel {
- public final int mNewKeys, mUpdatedKeys, mBadKeys;
+ public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret;
// At least one new key
public static final int RESULT_OK_NEWKEYS = 2;
@@ -49,18 +53,21 @@ public abstract class OperationResults {
public static final int RESULT_WITH_WARNINGS = 16;
// No keys to import...
- public static final int RESULT_FAIL_NOTHING = 32 +1;
+ public static final int RESULT_FAIL_NOTHING = 32 + 1;
public boolean isOkBoth() {
return (mResult & (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED))
== (RESULT_OK_NEWKEYS | RESULT_OK_UPDATED);
}
+
public boolean isOkNew() {
return (mResult & RESULT_OK_NEWKEYS) == RESULT_OK_NEWKEYS;
}
+
public boolean isOkUpdated() {
return (mResult & RESULT_OK_UPDATED) == RESULT_OK_UPDATED;
}
+
public boolean isFailNothing() {
return (mResult & RESULT_FAIL_NOTHING) == RESULT_FAIL_NOTHING;
}
@@ -70,14 +77,16 @@ public abstract class OperationResults {
mNewKeys = source.readInt();
mUpdatedKeys = source.readInt();
mBadKeys = source.readInt();
+ mSecret = source.readInt();
}
public ImportKeyResult(int result, OperationLog log,
- int newKeys, int updatedKeys, int badKeys) {
+ int newKeys, int updatedKeys, int badKeys, int secret) {
super(result, log);
mNewKeys = newKeys;
mUpdatedKeys = updatedKeys;
mBadKeys = badKeys;
+ mSecret = secret;
}
@Override
@@ -86,6 +95,7 @@ public abstract class OperationResults {
dest.writeInt(mNewKeys);
dest.writeInt(mUpdatedKeys);
dest.writeInt(mBadKeys);
+ dest.writeInt(mSecret);
}
public static Creator<ImportKeyResult> CREATOR = new Creator<ImportKeyResult>() {
@@ -124,7 +134,7 @@ public abstract class OperationResults {
if (this.isOkBoth()) {
str = activity.getResources().getQuantityString(
R.plurals.import_keys_added_and_updated_1, mNewKeys, mNewKeys);
- str += " "+ activity.getResources().getQuantityString(
+ str += " " + activity.getResources().getQuantityString(
R.plurals.import_keys_added_and_updated_2, mUpdatedKeys, mUpdatedKeys, withWarnings);
} else if (isOkUpdated()) {
str = activity.getResources().getQuantityString(
@@ -185,13 +195,13 @@ public abstract class OperationResults {
public static class EditKeyResult extends OperationResultParcel {
private transient UncachedKeyRing mRing;
- public final Long mRingMasterKeyId;
+ public final long mRingMasterKeyId;
public EditKeyResult(int result, OperationLog log,
- UncachedKeyRing ring) {
+ UncachedKeyRing ring) {
super(result, log);
mRing = ring;
- mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : null;
+ mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : Constants.key.none;
}
public UncachedKeyRing getRing() {
@@ -224,8 +234,12 @@ public abstract class OperationResults {
public static class SaveKeyringResult extends OperationResultParcel {
- public SaveKeyringResult(int result, OperationLog log) {
+ public final long mRingMasterKeyId;
+
+ public SaveKeyringResult(int result, OperationLog log,
+ CanonicalizedKeyRing ring) {
super(result, log);
+ mRingMasterKeyId = ring != null ? ring.getMasterKeyId() : Constants.key.none;
}
// Some old key was updated
@@ -240,6 +254,34 @@ public abstract class OperationResults {
return (mResult & UPDATED) == UPDATED;
}
+ public SaveKeyringResult(Parcel source) {
+ super(source);
+ mRingMasterKeyId = source.readLong();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeLong(mRingMasterKeyId);
+ }
+
+ public static Creator<SaveKeyringResult> CREATOR = new Creator<SaveKeyringResult>() {
+ public SaveKeyringResult createFromParcel(final Parcel source) {
+ return new SaveKeyringResult(source);
+ }
+
+ public SaveKeyringResult[] newArray(final int size) {
+ return new SaveKeyringResult[size];
+ }
+ };
+ }
+
+ public static class ConsolidateResult extends OperationResultParcel {
+
+ public ConsolidateResult(int result, OperationLog log) {
+ super(result, log);
+ }
+
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index 8cd9876eb..1b357bd65 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -78,7 +78,7 @@ public class PassphraseCacheService extends Service {
private static final int NOTIFICATION_ID = 1;
private static final int MSG_PASSPHRASE_CACHE_GET_OKAY = 1;
- private static final int MSG_PASSPHRASE_CACHE_GET_KEY_NO_FOUND = 2;
+ private static final int MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND = 2;
private BroadcastReceiver mIntentReceiver;
@@ -170,7 +170,7 @@ public class PassphraseCacheService extends Service {
switch (returnMessage.what) {
case MSG_PASSPHRASE_CACHE_GET_OKAY:
return returnMessage.getData().getString(EXTRA_PASSPHRASE);
- case MSG_PASSPHRASE_CACHE_GET_KEY_NO_FOUND:
+ case MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND:
throw new KeyNotFoundException();
default:
throw new KeyNotFoundException("should not happen!");
@@ -322,7 +322,7 @@ public class PassphraseCacheService extends Service {
msg.setData(bundle);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "PassphraseCacheService: Passphrase for unknown key was requested!");
- msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NO_FOUND;
+ msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND;
}
try {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index 490a8e738..996ce6a5a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
@@ -79,14 +80,16 @@ public class SaveKeyringParcel implements Parcelable {
// performance gain for using Parcelable here would probably be negligible,
// use Serializable instead.
public static class SubkeyAdd implements Serializable {
- public int mAlgorithm;
- public int mKeysize;
+ public Algorithm mAlgorithm;
+ public Integer mKeySize;
+ public Curve mCurve;
public int mFlags;
public Long mExpiry;
- public SubkeyAdd(int algorithm, int keysize, int flags, Long expiry) {
+ public SubkeyAdd(Algorithm algorithm, Integer keySize, Curve curve, int flags, Long expiry) {
mAlgorithm = algorithm;
- mKeysize = keysize;
+ mKeySize = keySize;
+ mCurve = curve;
mFlags = flags;
mExpiry = expiry;
}
@@ -94,7 +97,8 @@ public class SaveKeyringParcel implements Parcelable {
@Override
public String toString() {
String out = "mAlgorithm: " + mAlgorithm + ", ";
- out += "mKeysize: " + mKeysize + ", ";
+ out += "mKeySize: " + mKeySize + ", ";
+ out += "mCurve: " + mCurve + ", ";
out += "mFlags: " + mFlags;
out += "mExpiry: " + mExpiry;
@@ -213,4 +217,20 @@ public class SaveKeyringParcel implements Parcelable {
return out;
}
+
+ // All supported algorithms
+ public enum Algorithm {
+ RSA, DSA, ELGAMAL, ECDSA, ECDH
+ }
+
+ // All curves defined in the standard
+ // http://www.bouncycastle.org/wiki/pages/viewpage.action?pageId=362269
+ public enum Curve {
+ NIST_P256, NIST_P384, NIST_P521,
+
+ // these are supported by gpg, but they are not in rfc6637 and not supported by BouncyCastle yet
+ // (adding support would be trivial though -> JcaPGPKeyConverter.java:190)
+ // BRAINPOOL_P256, BRAINPOOL_P384, BRAINPOOL_P512
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
index c1986825c..8b99d474d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
* Copyright (C) 2011 Senecaso
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -40,6 +41,7 @@ import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.ListView;
+import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
@@ -53,9 +55,12 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
+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.Notify;
@@ -64,18 +69,18 @@ import java.util.ArrayList;
/**
* Signs the specified public key with the specified secret master key
*/
-public class CertifyKeyActivity extends ActionBarActivity implements
- SelectSecretKeyLayoutFragment.SelectSecretKeyCallback, LoaderManager.LoaderCallbacks<Cursor> {
+public class CertifyKeyActivity extends ActionBarActivity implements LoaderManager.LoaderCallbacks<Cursor> {
private View mCertifyButton;
private ImageView mActionCertifyImage;
private CheckBox mUploadKeyCheckbox;
private Spinner mSelectKeyserverSpinner;
+ private ScrollView mScrollView;
- private SelectSecretKeyLayoutFragment mSelectKeyFragment;
+ private CertifyKeySpinner mCertifyKeySpinner;
private Uri mDataUri;
- private long mPubKeyId = 0;
- private long mMasterKeyId = 0;
+ private long mPubKeyId = Constants.key.none;
+ private long mMasterKeyId = Constants.key.none;
private ListView mUserIds;
private UserIdsAdapter mUserIdsAdapter;
@@ -89,20 +94,24 @@ public class CertifyKeyActivity extends ActionBarActivity implements
setContentView(R.layout.certify_key_activity);
- mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getSupportFragmentManager()
- .findFragmentById(R.id.sign_key_select_key_fragment);
+ mCertifyKeySpinner = (CertifyKeySpinner) findViewById(R.id.certify_key_spinner);
mSelectKeyserverSpinner = (Spinner) findViewById(R.id.upload_key_keyserver);
mUploadKeyCheckbox = (CheckBox) findViewById(R.id.sign_key_upload_checkbox);
mCertifyButton = findViewById(R.id.certify_key_certify_button);
mActionCertifyImage = (ImageView) findViewById(R.id.certify_key_action_certify_image);
mUserIds = (ListView) findViewById(R.id.view_key_user_ids);
+ mScrollView = (ScrollView) findViewById(R.id.certify_scroll_view);
// make certify image gray, like action icons
mActionCertifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light),
PorterDuff.Mode.SRC_IN);
- mSelectKeyFragment.setCallback(this);
- mSelectKeyFragment.setFilterCertify(true);
+ mCertifyKeySpinner.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() {
+ @Override
+ public void onKeyChanged(long masterKeyId) {
+ mMasterKeyId = masterKeyId;
+ }
+ });
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this)
@@ -135,9 +144,9 @@ public class CertifyKeyActivity extends ActionBarActivity implements
public void onClick(View v) {
if (mPubKeyId != 0) {
if (mMasterKeyId == 0) {
- mSelectKeyFragment.setError(getString(R.string.select_key_to_certify));
Notify.showNotify(CertifyKeyActivity.this, getString(R.string.select_key_to_certify),
Notify.Style.ERROR);
+ scrollUp();
} else {
initiateCertifying();
}
@@ -162,6 +171,14 @@ public class CertifyKeyActivity extends ActionBarActivity implements
getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
}
+ private void scrollUp() {
+ mScrollView.post(new Runnable() {
+ public void run() {
+ mScrollView.fullScroll(ScrollView.FOCUS_UP);
+ }
+ });
+ }
+
static final String USER_IDS_SELECTION = UserIds.IS_REVOKED + " = 0";
static final String[] KEYRING_PROJECTION =
@@ -199,6 +216,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements
if (data.moveToFirst()) {
// TODO: put findViewById in onCreate!
mPubKeyId = data.getLong(INDEX_MASTER_KEY_ID);
+ mCertifyKeySpinner.setHiddenMasterKeyId(mPubKeyId);
String keyIdStr = PgpKeyHelper.convertKeyIdToHex(mPubKeyId);
((TextView) findViewById(R.id.key_id)).setText(keyIdStr);
@@ -213,6 +231,9 @@ public class CertifyKeyActivity extends ActionBarActivity implements
break;
case LOADER_ID_USER_IDS:
mUserIdsAdapter.swapCursor(data);
+ // when some user ids are pre-checked, the focus is requested and the scroll view goes
+ // down. This fixes it.
+ scrollUp();
break;
}
}
@@ -292,8 +313,14 @@ public class CertifyKeyActivity extends ActionBarActivity implements
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- Notify.showNotify(CertifyKeyActivity.this, R.string.key_certify_success,
- Notify.Style.INFO);
+// Notify.showNotify(CertifyKeyActivity.this, R.string.key_certify_success,
+// Notify.Style.INFO);
+
+ OperationResultParcel result = new OperationResultParcel(OperationResultParcel.RESULT_OK, null);
+ Intent intent = new Intent();
+ intent.putExtra(OperationResultParcel.EXTRA_RESULT, result);
+ CertifyKeyActivity.this.setResult(RESULT_OK, intent);
+ CertifyKeyActivity.this.finish();
// check if we need to send the key to the server or not
if (mUploadKeyCheckbox.isChecked()) {
@@ -345,13 +372,14 @@ public class CertifyKeyActivity extends ActionBarActivity implements
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- Intent intent = new Intent();
- intent.putExtra(OperationResultParcel.EXTRA_RESULT, message.getData());
- Notify.showNotify(CertifyKeyActivity.this, R.string.key_send_success,
- Notify.Style.INFO);
+ //Notify.showNotify(CertifyKeyActivity.this, R.string.key_send_success,
+ //Notify.Style.INFO);
- setResult(RESULT_OK);
- finish();
+ OperationResultParcel result = new OperationResultParcel(OperationResultParcel.RESULT_OK, null);
+ Intent intent = new Intent();
+ intent.putExtra(OperationResultParcel.EXTRA_RESULT, result);
+ CertifyKeyActivity.this.setResult(RESULT_OK, intent);
+ CertifyKeyActivity.this.finish();
}
}
};
@@ -367,14 +395,6 @@ public class CertifyKeyActivity extends ActionBarActivity implements
startService(intent);
}
- /**
- * callback from select key fragment
- */
- @Override
- public void onKeySelected(long secretKeyId) {
- mMasterKeyId = secretKeyId;
- }
-
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
new file mode 100644
index 000000000..edc05e28d
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java
@@ -0,0 +1,99 @@
+/*
+ * 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.ProgressDialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.support.v4.app.FragmentActivity;
+
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+
+/**
+ * We can not directly create a dialog on the application context.
+ * This activity encapsulates a DialogFragment to emulate a dialog.
+ */
+public class ConsolidateDialogActivity extends FragmentActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // this activity itself has no content view (see manifest)
+
+ consolidateRecovery();
+
+ }
+
+ private void consolidateRecovery() {
+ // Message is received after importing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
+ this,
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ /* don't care about the results (for now?)
+
+ // get returned data bundle
+ Bundle returnData = message.getData();
+ if (returnData == null) {
+ return;
+ }
+ final ConsolidateResult result =
+ returnData.getParcelable(KeychainIntentService.RESULT_CONSOLIDATE);
+ if (result == null) {
+ return;
+ }
+ result.createNotify(ConsolidateDialogActivity.this).show();
+ */
+
+ ConsolidateDialogActivity.this.finish();
+ }
+ }
+ };
+
+ // Send all information needed to service to import key in other thread
+ Intent intent = new Intent(this, KeychainIntentService.class);
+ intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+ data.putBoolean(KeychainIntentService.CONSOLIDATE_RECOVERY, true);
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(this);
+
+ // start service with intent
+ startService(intent);
+
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
index 773be816a..69f4af04b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java
@@ -33,6 +33,7 @@ import android.widget.TextView;
import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.KeyFlags;
+import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.KeyRing;
@@ -42,6 +43,8 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.service.OperationResults;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
+import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
public class CreateKeyFinalFragment extends Fragment {
@@ -125,10 +128,9 @@ public class CreateKeyFinalFragment extends Fragment {
Intent intent = new Intent(getActivity(), KeychainIntentService.class);
intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING);
- // Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
getActivity(),
- getString(R.string.progress_importing),
+ getString(R.string.progress_building_key),
ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
// handle messages by standard KeychainIntentServiceHandler first
@@ -140,26 +142,21 @@ public class CreateKeyFinalFragment extends Fragment {
if (returnData == null) {
return;
}
- final OperationResults.EditKeyResult result =
+ final OperationResults.SaveKeyringResult result =
returnData.getParcelable(OperationResultParcel.EXTRA_RESULT);
if (result == null) {
+ Log.e(Constants.TAG, "result == null");
return;
}
- if (result.getResult() == OperationResultParcel.RESULT_OK) {
- if (mUploadCheckbox.isChecked()) {
- // result will be displayed after upload
- uploadKey(result);
- } else {
- // TODO: return result
- result.createNotify(getActivity());
-
- getActivity().setResult(Activity.RESULT_OK);
- getActivity().finish();
- }
+ if (mUploadCheckbox.isChecked()) {
+ // result will be displayed after upload
+ uploadKey(result);
} else {
- // display result on error without finishing activity
- result.createNotify(getActivity());
+ Intent data = new Intent();
+ data.putExtra(OperationResultParcel.EXTRA_RESULT, result);
+ getActivity().setResult(Activity.RESULT_OK, data);
+ getActivity().finish();
}
}
}
@@ -169,9 +166,12 @@ public class CreateKeyFinalFragment extends Fragment {
Bundle data = new Bundle();
SaveKeyringParcel parcel = new SaveKeyringParcel();
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.CERTIFY_OTHER, null));
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.SIGN_DATA, null));
- parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(PublicKeyAlgorithmTags.RSA_GENERAL, 4096, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, null));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.RSA, 4096, null, KeyFlags.CERTIFY_OTHER, 0L));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.RSA, 4096, null, KeyFlags.SIGN_DATA, 0L));
+ parcel.mAddSubKeys.add(new SaveKeyringParcel.SubkeyAdd(
+ Algorithm.RSA, 4096, null, KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, 0L));
String userId = KeyRing.createUserId(mName, mEmail, null);
parcel.mAddUserIds.add(userId);
parcel.mChangePrimaryUserId = userId;
@@ -191,7 +191,7 @@ public class CreateKeyFinalFragment extends Fragment {
getActivity().startService(intent);
}
- private void uploadKey(final OperationResults.EditKeyResult editKeyResult) {
+ private void uploadKey(final OperationResults.SaveKeyringResult saveKeyResult) {
// Send all information needed to service to upload key in other thread
final Intent intent = new Intent(getActivity(), KeychainIntentService.class);
@@ -199,7 +199,7 @@ public class CreateKeyFinalFragment extends Fragment {
// set data uri as path to keyring
Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
- editKeyResult.mRingMasterKeyId);
+ saveKeyResult.mRingMasterKeyId);
intent.setData(blobUri);
// fill values for this action
@@ -211,7 +211,6 @@ public class CreateKeyFinalFragment extends Fragment {
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
- // Message is received after uploading is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(),
getString(R.string.progress_uploading), ProgressDialog.STYLE_HORIZONTAL) {
public void handleMessage(Message message) {
@@ -219,20 +218,16 @@ public class CreateKeyFinalFragment extends Fragment {
super.handleMessage(message);
if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
- // TODO: not supported by upload?
-// if (result.getResult() == OperationResultParcel.RESULT_OK) {
- // TODO: return result
- editKeyResult.createNotify(getActivity());
-
- Notify.showNotify(getActivity(), R.string.key_send_success,
- Notify.Style.INFO);
-
- getActivity().setResult(Activity.RESULT_OK);
+ // 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);
+
+ Intent data = new Intent();
+ data.putExtra(OperationResultParcel.EXTRA_RESULT, saveKeyResult);
+ getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
-// } else {
-// // display result on error without finishing activity
-// editKeyResult.createNotify(getActivity());
-// }
}
}
};
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index 03074bb6a..28692f638 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -48,8 +48,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
-import org.sufficientlysecure.keychain.service.OperationResults;
-import org.sufficientlysecure.keychain.service.OperationResults.EditKeyResult;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.adapter.SubkeysAdapter;
@@ -340,7 +339,8 @@ public class EditKeyFragment extends LoaderFragment implements
} else {
mSaveKeyringParcel.mRevokeUserIds.add(userId);
// not possible to revoke and change to primary user id
- if (mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) {
+ if (mSaveKeyringParcel.mChangePrimaryUserId != null
+ && mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)) {
mSaveKeyringParcel.mChangePrimaryUserId = null;
}
}
@@ -407,10 +407,10 @@ public class EditKeyFragment extends LoaderFragment implements
@Override
public void handleMessage(Message message) {
switch (message.what) {
- case EditSubkeyExpiryDialogFragment.MESSAGE_NEW_EXPIRY_DATE:
- Long expiry = (Long) message.getData().
- getSerializable(EditSubkeyExpiryDialogFragment.MESSAGE_DATA_EXPIRY_DATE);
- mSaveKeyringParcel.getOrCreateSubkeyChange(keyId).mExpiry = expiry;
+ case EditSubkeyExpiryDialogFragment.MESSAGE_NEW_EXPIRY:
+ mSaveKeyringParcel.getOrCreateSubkeyChange(keyId).mExpiry =
+ (Long) message.getData().getSerializable(
+ EditSubkeyExpiryDialogFragment.MESSAGE_DATA_EXPIRY);
break;
}
getLoaderManager().getLoader(LOADER_ID_SUBKEYS).forceLoad();
@@ -504,7 +504,6 @@ public class EditKeyFragment extends LoaderFragment implements
private void save(String passphrase) {
Log.d(Constants.TAG, "mSaveKeyringParcel:\n" + mSaveKeyringParcel.toString());
- // Message is received after importing is done in KeychainIntentService
KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
getActivity(),
getString(R.string.progress_saving),
@@ -520,8 +519,8 @@ public class EditKeyFragment extends LoaderFragment implements
if (returnData == null) {
return;
}
- final OperationResults.EditKeyResult result =
- returnData.getParcelable(EditKeyResult.EXTRA_RESULT);
+ final OperationResultParcel result =
+ returnData.getParcelable(OperationResultParcel.EXTRA_RESULT);
if (result == null) {
return;
}
@@ -534,7 +533,7 @@ public class EditKeyFragment extends LoaderFragment implements
// if good -> finish, return result to showkey and display there!
Intent intent = new Intent();
- intent.putExtra(EditKeyResult.EXTRA_RESULT, result);
+ intent.putExtra(OperationResultParcel.EXTRA_RESULT, result);
getActivity().setResult(EditKeyActivity.RESULT_OK, intent);
getActivity().finish();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
index 7e08f6b7c..4cd694d48 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java
@@ -73,16 +73,13 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
public static final String EXTRA_ENCRYPTION_KEY_IDS = "encryption_key_ids";
// view
- ViewPager mViewPagerMode;
- //PagerTabStrip mPagerTabStripMode;
- PagerTabStripAdapter mTabsAdapterMode;
- ViewPager mViewPagerContent;
- PagerTabStrip mPagerTabStripContent;
- PagerTabStripAdapter mTabsAdapterContent;
+ private int mCurrentMode = PAGER_MODE_ASYMMETRIC;
+ private ViewPager mViewPagerContent;
+ private PagerTabStrip mPagerTabStripContent;
+ private PagerTabStripAdapter mTabsAdapterContent;
// tabs
- int mSwitchToMode = PAGER_MODE_ASYMMETRIC;
- int mSwitchToContent = PAGER_CONTENT_MESSAGE;
+ private int mSwitchToContent = PAGER_CONTENT_MESSAGE;
private static final int PAGER_MODE_ASYMMETRIC = 0;
private static final int PAGER_MODE_SYMMETRIC = 1;
@@ -102,7 +99,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
private String mMessage = "";
public boolean isModeSymmetric() {
- return PAGER_MODE_SYMMETRIC == mViewPagerMode.getCurrentItem();
+ return PAGER_MODE_SYMMETRIC == mCurrentMode;
}
public boolean isContentMessage() {
@@ -312,59 +309,60 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
String title = isContentMessage() ? getString(R.string.title_share_message)
: getString(R.string.title_share_file);
- // fallback on Android 2.3, otherwise we would get weird results
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- return Intent.createChooser(prototype, title);
- }
-
- // prevent recursion aka Inception :P
- String[] blacklist = new String[]{Constants.PACKAGE_NAME + ".ui.EncryptActivity"};
-
- List<LabeledIntent> targetedShareIntents = new ArrayList<LabeledIntent>();
-
- List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(prototype, 0);
- List<ResolveInfo> resInfoListFiltered = new ArrayList<ResolveInfo>();
- if (!resInfoList.isEmpty()) {
- for (ResolveInfo resolveInfo : resInfoList) {
- // do not add blacklisted ones
- if (resolveInfo.activityInfo == null || Arrays.asList(blacklist).contains(resolveInfo.activityInfo.name))
- continue;
-
- resInfoListFiltered.add(resolveInfo);
- }
-
- if (!resInfoListFiltered.isEmpty()) {
- // sorting for nice readability
- Collections.sort(resInfoListFiltered, new Comparator<ResolveInfo>() {
- @Override
- public int compare(ResolveInfo first, ResolveInfo second) {
- String firstName = first.loadLabel(getPackageManager()).toString();
- String secondName = second.loadLabel(getPackageManager()).toString();
- return firstName.compareToIgnoreCase(secondName);
- }
- });
-
- // create the custom intent list
- for (ResolveInfo resolveInfo : resInfoListFiltered) {
- Intent targetedShareIntent = (Intent) prototype.clone();
- targetedShareIntent.setPackage(resolveInfo.activityInfo.packageName);
- targetedShareIntent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
-
- LabeledIntent lIntent = new LabeledIntent(targetedShareIntent,
- resolveInfo.activityInfo.packageName,
- resolveInfo.loadLabel(getPackageManager()),
- resolveInfo.activityInfo.icon);
- targetedShareIntents.add(lIntent);
- }
-
- // Create chooser with only one Intent in it
- Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title);
- // append all other Intents
- chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{}));
- return chooserIntent;
- }
-
- }
+ // Disabled, produced an empty list on Huawei U8860 with Android Version 4.0.3
+// // fallback on Android 2.3, otherwise we would get weird results
+// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+// return Intent.createChooser(prototype, title);
+// }
+//
+// // prevent recursion aka Inception :P
+// String[] blacklist = new String[]{Constants.PACKAGE_NAME + ".ui.EncryptActivity"};
+//
+// List<LabeledIntent> targetedShareIntents = new ArrayList<LabeledIntent>();
+//
+// List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(prototype, 0);
+// List<ResolveInfo> resInfoListFiltered = new ArrayList<ResolveInfo>();
+// if (!resInfoList.isEmpty()) {
+// for (ResolveInfo resolveInfo : resInfoList) {
+// // do not add blacklisted ones
+// if (resolveInfo.activityInfo == null || Arrays.asList(blacklist).contains(resolveInfo.activityInfo.name))
+// continue;
+//
+// resInfoListFiltered.add(resolveInfo);
+// }
+//
+// if (!resInfoListFiltered.isEmpty()) {
+// // sorting for nice readability
+// Collections.sort(resInfoListFiltered, new Comparator<ResolveInfo>() {
+// @Override
+// public int compare(ResolveInfo first, ResolveInfo second) {
+// String firstName = first.loadLabel(getPackageManager()).toString();
+// String secondName = second.loadLabel(getPackageManager()).toString();
+// return firstName.compareToIgnoreCase(secondName);
+// }
+// });
+//
+// // create the custom intent list
+// for (ResolveInfo resolveInfo : resInfoListFiltered) {
+// Intent targetedShareIntent = (Intent) prototype.clone();
+// targetedShareIntent.setPackage(resolveInfo.activityInfo.packageName);
+// targetedShareIntent.setClassName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
+//
+// LabeledIntent lIntent = new LabeledIntent(targetedShareIntent,
+// resolveInfo.activityInfo.packageName,
+// resolveInfo.loadLabel(getPackageManager()),
+// resolveInfo.activityInfo.icon);
+// targetedShareIntents.add(lIntent);
+// }
+//
+// // Create chooser with only one Intent in it
+// Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), title);
+// // append all other Intents
+// chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[]{}));
+// return chooserIntent;
+// }
+//
+// }
// fallback to Android's default chooser
return Intent.createChooser(prototype, title);
@@ -385,7 +383,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
sendIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
sendIntent.putExtra(Intent.EXTRA_STREAM, mOutputUris);
}
- sendIntent.setType("application/pgp-encrypted");
+ sendIntent.setType("application/octet-stream");
}
if (!isModeSymmetric() && mEncryptionUserIds != null) {
Set<String> users = new HashSet<String>();
@@ -474,13 +472,9 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
}
private void initView() {
- mViewPagerMode = (ViewPager) findViewById(R.id.encrypt_pager_mode);
- //mPagerTabStripMode = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_mode);
mViewPagerContent = (ViewPager) findViewById(R.id.encrypt_pager_content);
mPagerTabStripContent = (PagerTabStrip) findViewById(R.id.encrypt_pager_tab_strip_content);
- mTabsAdapterMode = new PagerTabStripAdapter(this);
- mViewPagerMode.setAdapter(mTabsAdapterMode);
mTabsAdapterContent = new PagerTabStripAdapter(this);
mViewPagerContent.setAdapter(mTabsAdapterContent);
}
@@ -502,10 +496,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
// Handle intent actions
handleActions(getIntent());
-
- mTabsAdapterMode.addTab(EncryptAsymmetricFragment.class, null, getString(R.string.label_asymmetric));
- mTabsAdapterMode.addTab(EncryptSymmetricFragment.class, null, getString(R.string.label_symmetric));
- mViewPagerMode.setCurrentItem(mSwitchToMode);
+ updateModeFragment();
mTabsAdapterContent.addTab(EncryptMessageFragment.class, null, getString(R.string.label_message));
mTabsAdapterContent.addTab(EncryptFileFragment.class, null, getString(R.string.label_files));
@@ -521,6 +512,16 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
return super.onCreateOptionsMenu(menu);
}
+ private void updateModeFragment() {
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.encrypt_pager_mode,
+ mCurrentMode == PAGER_MODE_SYMMETRIC
+ ? new EncryptSymmetricFragment()
+ : new EncryptAsymmetricFragment())
+ .commitAllowingStateLoss();
+ getSupportFragmentManager().executePendingTransactions();
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.isCheckable()) {
@@ -528,9 +529,8 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
}
switch (item.getItemId()) {
case R.id.check_use_symmetric:
- mSwitchToMode = item.isChecked() ? PAGER_MODE_SYMMETRIC : PAGER_MODE_ASYMMETRIC;
-
- mViewPagerMode.setCurrentItem(mSwitchToMode);
+ mCurrentMode = item.isChecked() ? PAGER_MODE_SYMMETRIC : PAGER_MODE_ASYMMETRIC;
+ updateModeFragment();
notifyUpdate();
break;
case R.id.check_use_armor:
@@ -604,7 +604,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
mEncryptionKeyIds = extras.getLongArray(EXTRA_ENCRYPTION_KEY_IDS);
// preselect keys given by intent
- mSwitchToMode = PAGER_MODE_ASYMMETRIC;
+ mCurrentMode = PAGER_MODE_ASYMMETRIC;
/**
* Main Actions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
index a402b6f68..a508472d6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java
@@ -18,35 +18,22 @@
package org.sufficientlysecure.keychain.ui;
import android.app.Activity;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
-import android.support.v4.app.LoaderManager;
-import android.support.v4.content.CursorLoader;
-import android.support.v4.content.Loader;
-import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.BaseAdapter;
-import android.widget.Spinner;
-import android.widget.SpinnerAdapter;
-import android.widget.TextView;
import com.tokenautocomplete.TokenCompleteTextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView;
+import org.sufficientlysecure.keychain.ui.widget.KeySpinner;
import org.sufficientlysecure.keychain.util.Log;
import java.util.ArrayList;
@@ -54,22 +41,20 @@ import java.util.Iterator;
import java.util.List;
public class EncryptAsymmetricFragment extends Fragment implements EncryptActivityInterface.UpdateListener {
- public static final String ARG_SIGNATURE_KEY_ID = "signature_key_id";
- public static final String ARG_ENCRYPTION_KEY_IDS = "encryption_key_ids";
-
ProviderHelper mProviderHelper;
// view
- private Spinner mSign;
+ private KeySpinner mSign;
private EncryptKeyCompletionView mEncryptKeyView;
- private SelectSignKeyCursorAdapter mSignAdapter = new SelectSignKeyCursorAdapter();
// model
private EncryptActivityInterface mEncryptInterface;
@Override
public void onNotifyUpdate() {
-
+ if (mSign != null) {
+ mSign.setSelectedKeyId(mEncryptInterface.getSignatureKey());
+ }
}
@Override
@@ -101,17 +86,11 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.encrypt_asymmetric_fragment, container, false);
- mSign = (Spinner) view.findViewById(R.id.sign);
- mSign.setAdapter(mSignAdapter);
- mSign.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- setSignatureKeyId(parent.getAdapter().getItemId(position));
- }
-
+ mSign = (KeySpinner) view.findViewById(R.id.sign);
+ mSign.setOnKeyChangedListener(new KeySpinner.OnKeyChangedListener() {
@Override
- public void onNothingSelected(AdapterView<?> parent) {
- setSignatureKeyId(Constants.key.none);
+ public void onKeyChanged(long masterKeyId) {
+ setSignatureKeyId(masterKeyId);
}
});
mEncryptKeyView = (EncryptKeyCompletionView) view.findViewById(R.id.recipient_list);
@@ -128,42 +107,6 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
// preselect keys given
preselectKeys();
- getLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks<Cursor>() {
- @Override
- public Loader<Cursor> onCreateLoader(int id, Bundle args) {
- // This is called when a new Loader needs to be created. This
- // sample only has one Loader, so we don't care about the ID.
- Uri baseUri = KeyRings.buildUnifiedKeyRingsUri();
-
- // These are the rows that we will retrieve.
- String[] projection = new String[]{
- KeyRings._ID,
- KeyRings.MASTER_KEY_ID,
- KeyRings.KEY_ID,
- KeyRings.USER_ID,
- KeyRings.IS_EXPIRED,
- KeyRings.HAS_SIGN,
- KeyRings.HAS_ANY_SECRET
- };
-
- String where = KeyRings.HAS_ANY_SECRET + " = 1 AND " + KeyRings.HAS_SIGN + " NOT NULL AND "
- + KeyRings.IS_REVOKED + " = 0 AND " + KeyRings.IS_EXPIRED + " = 0";
-
- // Now create and return a CursorLoader that will take care of
- // creating a Cursor for the data being displayed.
- return new CursorLoader(getActivity(), baseUri, projection, where, null, null);
- }
-
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- mSignAdapter.swapCursor(data);
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {
- mSignAdapter.swapCursor(null);
- }
- });
mEncryptKeyView.setTokenListener(new TokenCompleteTextView.TokenListener() {
@Override
public void onTokenAdded(Object token) {
@@ -194,6 +137,7 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
KeyRings.buildUnifiedKeyRingUri(signatureKey));
if(keyring.hasAnySecret()) {
setSignatureKeyId(keyring.getMasterKeyId());
+ mSign.setSelectedKeyId(mEncryptInterface.getSignatureKey());
}
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
@@ -211,6 +155,8 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
Log.e(Constants.TAG, "key not found!", e);
}
}
+ // This is to work-around a rendering bug in TokenCompleteTextView
+ mEncryptKeyView.requestFocus();
updateEncryptionKeys();
}
}
@@ -233,95 +179,4 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi
setEncryptionKeyIds(keyIdsArr);
setEncryptionUserIds(userIds.toArray(new String[userIds.size()]));
}
-
- private class SelectSignKeyCursorAdapter extends BaseAdapter implements SpinnerAdapter {
- private CursorAdapter inner;
- private int mIndexUserId;
- private int mIndexKeyId;
- private int mIndexMasterKeyId;
-
- public SelectSignKeyCursorAdapter() {
- inner = new CursorAdapter(null, null, 0) {
- @Override
- public View newView(Context context, Cursor cursor, ViewGroup parent) {
- return getActivity().getLayoutInflater().inflate(R.layout.encrypt_asymmetric_signkey, null);
- }
-
- @Override
- public void bindView(View view, Context context, Cursor cursor) {
- String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId));
- ((TextView) view.findViewById(android.R.id.title)).setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")"));
- ((TextView) view.findViewById(android.R.id.text1)).setText(userId[1]);
- ((TextView) view.findViewById(android.R.id.text2)).setText(PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId)));
- }
-
- @Override
- public long getItemId(int position) {
- mCursor.moveToPosition(position);
- return mCursor.getLong(mIndexMasterKeyId);
- }
- };
- }
-
- public Cursor swapCursor(Cursor newCursor) {
- if (newCursor == null) return inner.swapCursor(null);
-
- mIndexKeyId = newCursor.getColumnIndex(KeyRings.KEY_ID);
- mIndexUserId = newCursor.getColumnIndex(KeyRings.USER_ID);
- mIndexMasterKeyId = newCursor.getColumnIndex(KeyRings.MASTER_KEY_ID);
- if (newCursor.moveToFirst()) {
- do {
- if (newCursor.getLong(mIndexMasterKeyId) == mEncryptInterface.getSignatureKey()) {
- mSign.setSelection(newCursor.getPosition() + 1);
- }
- } while (newCursor.moveToNext());
- }
- return inner.swapCursor(newCursor);
- }
-
- @Override
- public int getCount() {
- return inner.getCount() + 1;
- }
-
- @Override
- public Object getItem(int position) {
- if (position == 0) return null;
- return inner.getItem(position - 1);
- }
-
- @Override
- public long getItemId(int position) {
- if (position == 0) return Constants.key.none;
- return inner.getItemId(position - 1);
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View v = getDropDownView(position, convertView, parent);
- v.findViewById(android.R.id.text1).setVisibility(View.GONE);
- return v;
- }
-
- @Override
- public View getDropDownView(int position, View convertView, ViewGroup parent) {
- View v;
- if (position == 0) {
- if (convertView == null) {
- v = inner.newView(null, null, parent);
- } else {
- v = convertView;
- }
- ((TextView) v.findViewById(android.R.id.title)).setText("None");
- v.findViewById(android.R.id.text1).setVisibility(View.GONE);
- v.findViewById(android.R.id.text2).setVisibility(View.GONE);
- } else {
- v = inner.getView(position - 1, convertView, parent);
- v.findViewById(android.R.id.text1).setVisibility(View.VISIBLE);
- v.findViewById(android.R.id.text2).setVisibility(View.VISIBLE);
- }
- return v;
- }
- }
-
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
index 5f3f170a1..7b608a0a2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java
@@ -51,7 +51,7 @@ public class FirstTimeActivity extends ActionBarActivity {
mSkipSetup.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- finishSetup();
+ finishSetup(null);
}
});
@@ -80,18 +80,22 @@ public class FirstTimeActivity extends ActionBarActivity {
if (requestCode == REQUEST_CODE_CREATE_OR_IMPORT_KEY) {
if (resultCode == RESULT_OK) {
- finishSetup();
+ finishSetup(data);
}
} else {
Log.e(Constants.TAG, "No valid request code!");
}
}
- private void finishSetup() {
+ private void finishSetup(Intent srcData) {
Preferences prefs = Preferences.getPreferences(this);
prefs.setFirstTime(false);
- Intent intent = new Intent(FirstTimeActivity.this, KeyListActivity.class);
- startActivity(intent);
+ Intent intent = new Intent(this, KeyListActivity.class);
+ // give intent through to display notify
+ if (srcData != null) {
+ intent.putExtras(srcData);
+ }
+ startActivityForResult(intent, 0);
finish();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
index 255290de3..96fa11363 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java
@@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.service.OperationResults.ImportKeyResult;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;
@@ -64,7 +65,7 @@ public class ImportKeysActivity extends ActionBarActivity {
+ "IMPORT_KEY_FROM_KEYSERVER";
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT =
Constants.INTENT_PREFIX + "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN_RESULT";
- public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN = Constants.INTENT_PREFIX
+ public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_KEY_SERVER_AND_RETURN";
public static final String ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN = Constants.INTENT_PREFIX
+ "IMPORT_KEY_FROM_FILE_AND_RETURN";
@@ -87,7 +88,7 @@ public class ImportKeysActivity extends ActionBarActivity {
public static final String EXTRA_KEY_ID = "key_id";
public static final String EXTRA_FINGERPRINT = "fingerprint";
- // only used by ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN when used from OpenPgpService
+ // only used by ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE when used from OpenPgpService
public static final String EXTRA_PENDING_INTENT_DATA = "data";
private Intent mPendingIntentData;
@@ -170,7 +171,7 @@ public class ImportKeysActivity extends ActionBarActivity {
startListFragment(savedInstanceState, importData, null, null);
}
} else if (ACTION_IMPORT_KEY_FROM_KEYSERVER.equals(action)
- || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(action)
+ || ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(action)
|| ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(action)) {
// only used for OpenPgpService
@@ -287,7 +288,9 @@ public class ImportKeysActivity extends ActionBarActivity {
@Override
public void onPageSelected(int position) {
// cancel loader and clear list
- mListFragment.destroyLoader();
+ if (mListFragment != null) {
+ mListFragment.destroyLoader();
+ }
}
@Override
@@ -456,28 +459,25 @@ public class ImportKeysActivity extends ActionBarActivity {
return;
}
final ImportKeyResult result =
- returnData.getParcelable(KeychainIntentService.RESULT_IMPORT);
+ returnData.getParcelable(OperationResultParcel.EXTRA_RESULT);
if (result == null) {
+ Log.e(Constants.TAG, "result == null");
return;
}
- if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())) {
+ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())
+ || ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(getIntent().getAction())) {
Intent intent = new Intent();
intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
ImportKeysActivity.this.setResult(RESULT_OK, intent);
ImportKeysActivity.this.finish();
return;
}
- if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) {
+ if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(getIntent().getAction())) {
ImportKeysActivity.this.setResult(RESULT_OK, mPendingIntentData);
ImportKeysActivity.this.finish();
return;
}
- if (ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(getIntent().getAction())) {
- ImportKeysActivity.this.setResult(RESULT_OK);
- ImportKeysActivity.this.finish();
- return;
- }
result.createNotify(ImportKeysActivity.this).show();
}
@@ -503,7 +503,8 @@ public class ImportKeysActivity extends ActionBarActivity {
// to prevent Java Binder problems on heavy imports
// read FileImportCache for more info.
try {
- FileImportCache<ParcelableKeyRing> cache = new FileImportCache<ParcelableKeyRing>(this);
+ FileImportCache<ParcelableKeyRing> cache =
+ new FileImportCache<ParcelableKeyRing>(this, "key_import.pcl");
cache.writeCache(selectedEntries);
intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
index d339bc132..88caebc32 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java
@@ -29,15 +29,19 @@ import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.helper.ContactHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.util.Log;
+import java.util.List;
+
public class ImportKeysServerFragment extends Fragment {
public static final String ARG_QUERY = "query";
public static final String ARG_KEYSERVER = "keyserver";
@@ -46,7 +50,7 @@ public class ImportKeysServerFragment extends Fragment {
private ImportKeysActivity mImportActivity;
private View mSearchButton;
- private EditText mQueryEditText;
+ private AutoCompleteTextView mQueryEditText;
private View mConfigButton;
private View mConfigLayout;
private Spinner mServerSpinner;
@@ -75,7 +79,7 @@ public class ImportKeysServerFragment extends Fragment {
View view = inflater.inflate(R.layout.import_keys_server_fragment, container, false);
mSearchButton = view.findViewById(R.id.import_server_search);
- mQueryEditText = (EditText) view.findViewById(R.id.import_server_query);
+ mQueryEditText = (AutoCompleteTextView) view.findViewById(R.id.import_server_query);
mConfigButton = view.findViewById(R.id.import_server_config_button);
mConfigLayout = view.findViewById(R.id.import_server_config);
mServerSpinner = (Spinner) view.findViewById(R.id.import_server_spinner);
@@ -93,6 +97,16 @@ public class ImportKeysServerFragment extends Fragment {
mSearchButton.setEnabled(false);
}
+ List<String> namesAndEmails = ContactHelper.getContactNames(getActivity());
+ namesAndEmails.addAll(ContactHelper.getContactMails(getActivity()));
+ mQueryEditText.setThreshold(3);
+ mQueryEditText.setAdapter(
+ new ArrayAdapter<String>
+ (getActivity(), android.R.layout.simple_spinner_dropdown_item,
+ namesAndEmails
+ )
+ );
+
mSearchButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
index 7a6e78a7d..e2ad241e2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2012-2014 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
@@ -17,8 +18,11 @@
package org.sufficientlysecure.keychain.ui;
+import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
import android.view.Menu;
import android.view.MenuItem;
@@ -28,6 +32,10 @@ import org.sufficientlysecure.keychain.helper.ExportHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
+import org.sufficientlysecure.keychain.service.OperationResultParcel;
+import org.sufficientlysecure.keychain.service.OperationResults.ConsolidateResult;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Notify;
@@ -63,6 +71,7 @@ public class KeyListActivity extends DrawerActivity {
getMenuInflater().inflate(R.menu.key_list, menu);
if (Constants.DEBUG) {
+ menu.findItem(R.id.menu_key_list_debug_cons).setVisible(true);
menu.findItem(R.id.menu_key_list_debug_read).setVisible(true);
menu.findItem(R.id.menu_key_list_debug_write).setVisible(true);
menu.findItem(R.id.menu_key_list_debug_first_time).setVisible(true);
@@ -92,6 +101,10 @@ public class KeyListActivity extends DrawerActivity {
mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true);
return true;
+ case R.id.menu_key_list_debug_cons:
+ consolidate();
+ return true;
+
case R.id.menu_key_list_debug_read:
try {
KeychainDatabase.debugBackup(this, true);
@@ -133,7 +146,67 @@ public class KeyListActivity extends DrawerActivity {
private void createKey() {
Intent intent = new Intent(this, CreateKeyActivity.class);
- startActivity(intent);
+ startActivityForResult(intent, 0);
+ }
+
+ private void consolidate() {
+ // Message is received after importing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
+ this,
+ getString(R.string.progress_importing),
+ ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ // get returned data bundle
+ Bundle returnData = message.getData();
+ if (returnData == null) {
+ return;
+ }
+ final ConsolidateResult result =
+ returnData.getParcelable(OperationResultParcel.EXTRA_RESULT);
+ if (result == null) {
+ return;
+ }
+
+ result.createNotify(KeyListActivity.this).show();
+ }
+ }
+ };
+
+ // Send all information needed to service to import key in other thread
+ Intent intent = new Intent(this, KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_CONSOLIDATE);
+
+ // fill values for this action
+ Bundle data = new Bundle();
+
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(this);
+
+ // start service with intent
+ startService(intent);
+
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // if a result has been returned, display a notify
+ if (data != null && data.hasExtra(OperationResultParcel.EXTRA_RESULT)) {
+ OperationResultParcel result = data.getParcelableExtra(OperationResultParcel.EXTRA_RESULT);
+ result.createNotify(this).show();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index 3c97b1128..b4676f9b7 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013-2014 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
@@ -79,14 +80,15 @@ public class KeyListFragment extends LoaderFragment
private KeyListAdapter mAdapter;
private StickyListHeadersListView mStickyList;
+ // saves the mode object for multiselect, needed for reset at some point
+ private ActionMode mActionMode = null;
+
private String mQuery;
private SearchView mSearchView;
// empty list layout
private Button mButtonEmptyCreate;
private Button mButtonEmptyImport;
- public static final int REQUEST_CODE_CREATE_OR_IMPORT_KEY = 0x00007012;
-
/**
* Load custom layout with StickyListView from library
*/
@@ -105,7 +107,7 @@ public class KeyListFragment extends LoaderFragment
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), CreateKeyActivity.class);
- startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
+ startActivityForResult(intent, 0);
}
});
mButtonEmptyImport = (Button) view.findViewById(R.id.key_list_empty_button_import);
@@ -115,7 +117,7 @@ public class KeyListFragment extends LoaderFragment
public void onClick(View v) {
Intent intent = new Intent(getActivity(), ImportKeysActivity.class);
intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN);
- startActivityForResult(intent, REQUEST_CODE_CREATE_OR_IMPORT_KEY);
+ startActivityForResult(intent, 0);
}
});
@@ -148,6 +150,7 @@ public class KeyListFragment extends LoaderFragment
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
android.view.MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.key_list_multi, menu);
+ mActionMode = mode;
return true;
}
@@ -193,6 +196,7 @@ public class KeyListFragment extends LoaderFragment
@Override
public void onDestroyActionMode(ActionMode mode) {
+ mActionMode = null;
mAdapter.clearSelection();
}
@@ -288,6 +292,13 @@ public class KeyListFragment extends LoaderFragment
// this view is made visible if no data is available
mStickyList.setEmptyView(getActivity().findViewById(R.id.key_list_empty));
+ // end action mode, if any
+ if (mActionMode != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mActionMode.finish();
+ }
+ }
+
// The list should now be shown.
if (isResumed()) {
setContentShown(true);
@@ -330,12 +341,12 @@ public class KeyListFragment extends LoaderFragment
* Show dialog to delete key
*
* @param masterKeyIds
- * @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
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void showDeleteKeyDialog(final ActionMode mode, long[] masterKeyIds, boolean hasSecret) {
// Can only work on singular secret keys
- if(hasSecret && masterKeyIds.length > 1) {
+ if (hasSecret && masterKeyIds.length > 1) {
Notify.showNotify(getActivity(), R.string.secret_cannot_multiple,
Notify.Style.ERROR);
return;
@@ -365,6 +376,7 @@ public class KeyListFragment extends LoaderFragment
public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
// Get the searchview
MenuItem searchItem = menu.findItem(R.id.menu_key_list_search);
+
mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);
// Execute this when searching
@@ -383,7 +395,6 @@ public class KeyListFragment extends LoaderFragment
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
mQuery = null;
- mSearchView.setQuery("", true);
getLoaderManager().restartLoader(0, null, KeyListFragment.this);
return true;
}
@@ -399,11 +410,18 @@ public class KeyListFragment extends LoaderFragment
@Override
public boolean onQueryTextChange(String s) {
+ Log.d(Constants.TAG, "onQueryTextChange s:" + s);
// Called when the action bar search text has changed. Update
// the search filter, and restart the loader to do a new query
// with this filter.
- mQuery = !TextUtils.isEmpty(s) ? s : null;
- getLoaderManager().restartLoader(0, null, this);
+ // If the nav drawer is opened, onQueryTextChange("") is executed.
+ // This hack prevents restarting the loader.
+ // TODO: better way to fix this?
+ String tmp = (mQuery == null) ? "" : mQuery;
+ if (!s.equals(tmp)) {
+ mQuery = s;
+ getLoaderManager().restartLoader(0, null, this);
+ }
return true;
}
@@ -479,7 +497,7 @@ public class KeyListFragment extends LoaderFragment
boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0;
boolean isExpired = !cursor.isNull(INDEX_EXPIRY)
- && new Date(cursor.getLong(INDEX_EXPIRY)*1000).before(new Date());
+ && new Date(cursor.getLong(INDEX_EXPIRY) * 1000).before(new Date());
boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0;
// Note: order is important!
@@ -521,6 +539,7 @@ public class KeyListFragment extends LoaderFragment
return mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0;
}
+
public long getMasterKeyId(int id) {
if (!mCursor.moveToPosition(id)) {
throw new IllegalStateException("couldn't move cursor to position " + id);
@@ -625,7 +644,7 @@ public class KeyListFragment extends LoaderFragment
public boolean isAnySecretSelected() {
for (int pos : mSelection.keySet()) {
- if(mAdapter.isSecretAvailable(pos))
+ if (mAdapter.isSecretAvailable(pos))
return true;
}
return false;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java
index b8386b144..248b8976a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
index 0e948bf7f..e524c3870 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
index de3236d35..64c1e16be 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -317,10 +317,10 @@ public class PreferencesActivity extends PreferenceActivity {
private static void initializeHashAlgorithm
(final IntegerListPreference mHashAlgorithm, int[] valueIds, String[] entries, String[] values) {
- valueIds = new int[]{HashAlgorithmTags.MD5, HashAlgorithmTags.RIPEMD160,
+ valueIds = new int[]{HashAlgorithmTags.RIPEMD160,
HashAlgorithmTags.SHA1, HashAlgorithmTags.SHA224, HashAlgorithmTags.SHA256,
HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA512,};
- entries = new String[]{"MD5", "RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384",
+ entries = new String[]{"RIPEMD-160", "SHA-1", "SHA-224", "SHA-256", "SHA-384",
"SHA-512",};
values = new String[valueIds.length];
for (int i = 0; i < values.length; ++i) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
index 341e11d1d..92f38a44c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewCertActivity.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013-2014 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
@@ -166,7 +167,7 @@ public class ViewCertActivity extends ActionBarActivity
mStatus.setTextColor(getResources().getColor(R.color.black));
}
- String algorithmStr = PgpKeyHelper.getAlgorithmInfo(this, sig.getKeyAlgorithm(), 0);
+ String algorithmStr = PgpKeyHelper.getAlgorithmInfo(this, sig.getKeyAlgorithm(), null, null);
mAlgorithm.setText(algorithmStr);
mRowReason.setVisibility(View.GONE);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
index 28f7b8bf5..2c0881ea4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java
@@ -54,6 +54,8 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.service.OperationResultParcel;
import org.sufficientlysecure.keychain.ui.adapter.PagerTabStripAdapter;
import org.sufficientlysecure.keychain.ui.widget.SlidingTabLayout;
@@ -303,8 +305,10 @@ public class ViewKeyActivity extends ActionBarActivity implements
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
- setResult(RESULT_CANCELED);
- finish();
+ if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
}
};
@@ -313,6 +317,7 @@ public class ViewKeyActivity extends ActionBarActivity implements
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // if a result has been returned, display a notify
if (data != null && data.hasExtra(OperationResultParcel.EXTRA_RESULT)) {
OperationResultParcel result = data.getParcelableExtra(OperationResultParcel.EXTRA_RESULT);
result.createNotify(this).show();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
index 5a55b0dad..786537b25 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyCertsFragment.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
index 08243f06b..c37cb014e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 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
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
index 1f809cc51..1fed58721 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java
@@ -155,8 +155,8 @@ public class ImportKeysAdapter extends ArrayAdapter<ImportKeysListEntry> {
// don't show full fingerprint on key import
holder.fingerprint.setVisibility(View.GONE);
- if (entry.getBitStrength() != 0 && entry.getAlgorithm() != null) {
- holder.algorithm.setText("" + entry.getBitStrength() + "/" + entry.getAlgorithm());
+ if (entry.getAlgorithm() != null) {
+ holder.algorithm.setText(entry.getAlgorithm());
holder.algorithm.setVisibility(View.VISIBLE);
} else {
holder.algorithm.setVisibility(View.INVISIBLE);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
index d457e75bd..489cbcefb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAdapter.java
@@ -35,7 +35,9 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import java.util.Calendar;
import java.util.Date;
+import java.util.TimeZone;
public class SubkeysAdapter extends CursorAdapter {
private LayoutInflater mInflater;
@@ -50,6 +52,7 @@ public class SubkeysAdapter extends CursorAdapter {
Keys.RANK,
Keys.ALGORITHM,
Keys.KEY_SIZE,
+ Keys.KEY_CURVE_OID,
Keys.HAS_SECRET,
Keys.CAN_CERTIFY,
Keys.CAN_ENCRYPT,
@@ -64,14 +67,15 @@ public class SubkeysAdapter extends CursorAdapter {
private static final int INDEX_RANK = 2;
private static final int INDEX_ALGORITHM = 3;
private static final int INDEX_KEY_SIZE = 4;
- private static final int INDEX_HAS_SECRET = 5;
- private static final int INDEX_CAN_CERTIFY = 6;
- private static final int INDEX_CAN_ENCRYPT = 7;
- private static final int INDEX_CAN_SIGN = 8;
- private static final int INDEX_IS_REVOKED = 9;
- private static final int INDEX_CREATION = 10;
- private static final int INDEX_EXPIRY = 11;
- private static final int INDEX_FINGERPRINT = 12;
+ private static final int INDEX_KEY_CURVE_OID = 5;
+ private static final int INDEX_HAS_SECRET = 6;
+ private static final int INDEX_CAN_CERTIFY = 7;
+ private static final int INDEX_CAN_ENCRYPT = 8;
+ private static final int INDEX_CAN_SIGN = 9;
+ private static final int INDEX_IS_REVOKED = 10;
+ private static final int INDEX_CREATION = 11;
+ private static final int INDEX_EXPIRY = 12;
+ private static final int INDEX_FINGERPRINT = 13;
public SubkeysAdapter(Context context, Cursor c, int flags,
SaveKeyringParcel saveKeyringParcel) {
@@ -139,7 +143,8 @@ public class SubkeysAdapter extends CursorAdapter {
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
context,
cursor.getInt(INDEX_ALGORITHM),
- cursor.getInt(INDEX_KEY_SIZE)
+ cursor.getInt(INDEX_KEY_SIZE),
+ cursor.getString(INDEX_KEY_CURVE_OID)
);
vKeyId.setText(keyIdStr);
@@ -183,7 +188,7 @@ public class SubkeysAdapter extends CursorAdapter {
SaveKeyringParcel.SubkeyChange subkeyChange = mSaveKeyringParcel.getSubkeyChange(keyId);
if (subkeyChange != null) {
- if (subkeyChange.mExpiry == null) {
+ if (subkeyChange.mExpiry == null || subkeyChange.mExpiry == 0L) {
expiryDate = null;
} else {
expiryDate = new Date(subkeyChange.mExpiry * 1000);
@@ -198,9 +203,13 @@ public class SubkeysAdapter extends CursorAdapter {
boolean isExpired;
if (expiryDate != null) {
isExpired = expiryDate.before(new Date());
+ Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ expiryCal.setTime(expiryDate);
+ // convert from UTC to time zone of device
+ expiryCal.setTimeZone(TimeZone.getDefault());
vKeyExpiry.setText(context.getString(R.string.label_expiry) + ": "
- + DateFormat.getDateFormat(context).format(expiryDate));
+ + DateFormat.getDateFormat(context).format(expiryCal.getTime()));
} else {
isExpired = false;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
index be2e17c63..d50318fb4 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SubkeysAddedAdapter.java
@@ -33,21 +33,19 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import java.util.Calendar;
import java.util.Date;
import java.util.List;
+import java.util.TimeZone;
public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAdd> {
private LayoutInflater mInflater;
private Activity mActivity;
- // hold a private reference to the underlying data List
- private List<SaveKeyringParcel.SubkeyAdd> mData;
-
public SubkeysAddedAdapter(Activity activity, List<SaveKeyringParcel.SubkeyAdd> data) {
super(activity, -1, data);
mActivity = activity;
mInflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mData = data;
}
static class ViewHolder {
@@ -101,16 +99,21 @@ public class SubkeysAddedAdapter extends ArrayAdapter<SaveKeyringParcel.SubkeyAd
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(
mActivity,
holder.mModel.mAlgorithm,
- holder.mModel.mKeysize
+ holder.mModel.mKeySize,
+ holder.mModel.mCurve
);
holder.vKeyId.setText(R.string.edit_key_new_subkey);
holder.vKeyDetails.setText(algorithmStr);
- if (holder.mModel.mExpiry != null) {
+ if (holder.mModel.mExpiry != 0L) {
Date expiryDate = new Date(holder.mModel.mExpiry * 1000);
+ Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ expiryCal.setTime(expiryDate);
+ // convert from UTC to time zone of device
+ expiryCal.setTimeZone(TimeZone.getDefault());
holder.vKeyExpiry.setText(getContext().getString(R.string.label_expiry) + ": "
- + DateFormat.getDateFormat(getContext()).format(expiryDate));
+ + DateFormat.getDateFormat(getContext()).format(expiryCal.getTime()));
} else {
holder.vKeyExpiry.setText(getContext().getString(R.string.label_expiry) + ": "
+ getContext().getString(R.string.none));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
index cb31978e9..b4119a5eb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddSubkeyDialogFragment.java
@@ -20,19 +20,18 @@ package org.sufficientlysecure.keychain.ui.dialog;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
-import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.text.Editable;
import android.text.TextWatcher;
-import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.DatePicker;
@@ -40,17 +39,18 @@ import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TableRow;
import android.widget.TextView;
+import android.widget.Toast;
-import org.spongycastle.bcpg.PublicKeyAlgorithmTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
+import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve;
import org.sufficientlysecure.keychain.util.Choice;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
-import java.util.Date;
import java.util.TimeZone;
public class AddSubkeyDialogFragment extends DialogFragment {
@@ -67,7 +67,10 @@ public class AddSubkeyDialogFragment extends DialogFragment {
private TableRow mExpiryRow;
private DatePicker mExpiryDatePicker;
private Spinner mAlgorithmSpinner;
+ private View mKeySizeRow;
private Spinner mKeySizeSpinner;
+ private View mCurveRow;
+ private Spinner mCurveSpinner;
private TextView mCustomKeyTextView;
private EditText mCustomKeyEditText;
private TextView mCustomKeyInfoTextView;
@@ -76,6 +79,8 @@ public class AddSubkeyDialogFragment extends DialogFragment {
private CheckBox mFlagEncrypt;
private CheckBox mFlagAuthenticate;
+ private boolean mWillBeMasterKey;
+
public void setOnAlgorithmSelectedListener(OnAlgorithmSelectedListener listener) {
mAlgorithmSelectedListener = listener;
}
@@ -96,7 +101,7 @@ public class AddSubkeyDialogFragment extends DialogFragment {
final FragmentActivity context = getActivity();
final LayoutInflater mInflater;
- final boolean willBeMasterKey = getArguments().getBoolean(ARG_WILL_BE_MASTER_KEY);
+ mWillBeMasterKey = getArguments().getBoolean(ARG_WILL_BE_MASTER_KEY);
mInflater = context.getLayoutInflater();
CustomAlertDialogBuilder dialog = new CustomAlertDialogBuilder(context);
@@ -110,6 +115,9 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mExpiryDatePicker = (DatePicker) view.findViewById(R.id.add_subkey_expiry_date_picker);
mAlgorithmSpinner = (Spinner) view.findViewById(R.id.add_subkey_algorithm);
mKeySizeSpinner = (Spinner) view.findViewById(R.id.add_subkey_size);
+ mCurveSpinner = (Spinner) view.findViewById(R.id.add_subkey_curve);
+ mKeySizeRow = view.findViewById(R.id.add_subkey_row_size);
+ mCurveRow = view.findViewById(R.id.add_subkey_row_curve);
mCustomKeyTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_label);
mCustomKeyEditText = (EditText) view.findViewById(R.id.add_subkey_custom_key_size_input);
mCustomKeyInfoTextView = (TextView) view.findViewById(R.id.add_subkey_custom_key_size_info);
@@ -130,27 +138,36 @@ public class AddSubkeyDialogFragment extends DialogFragment {
});
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
- mExpiryDatePicker.setMinDate(new Date().getTime() + DateUtils.DAY_IN_MILLIS);
+ // date picker works based on default time zone
+ Calendar minDateCal = Calendar.getInstance(TimeZone.getDefault());
+ minDateCal.add(Calendar.DAY_OF_YEAR, 1); // at least one day after creation (today)
+ mExpiryDatePicker.setMinDate(minDateCal.getTime().getTime());
}
- ArrayList<Choice> choices = new ArrayList<Choice>();
- choices.add(new Choice(PublicKeyAlgorithmTags.DSA, getResources().getString(
- R.string.dsa)));
- if (!willBeMasterKey) {
- choices.add(new Choice(PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT, getResources().getString(
- R.string.elgamal)));
- }
- choices.add(new Choice(PublicKeyAlgorithmTags.RSA_GENERAL, getResources().getString(
- R.string.rsa)));
- ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(context,
- android.R.layout.simple_spinner_item, choices);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mAlgorithmSpinner.setAdapter(adapter);
- // make RSA the default
- for (int i = 0; i < choices.size(); ++i) {
- if (choices.get(i).getId() == PublicKeyAlgorithmTags.RSA_GENERAL) {
- mAlgorithmSpinner.setSelection(i);
- break;
+ {
+ ArrayList<Choice<Algorithm>> choices = new ArrayList<Choice<Algorithm>>();
+ choices.add(new Choice<Algorithm>(Algorithm.DSA, getResources().getString(
+ R.string.dsa)));
+ if (!mWillBeMasterKey) {
+ choices.add(new Choice<Algorithm>(Algorithm.ELGAMAL, getResources().getString(
+ R.string.elgamal)));
+ }
+ choices.add(new Choice<Algorithm>(Algorithm.RSA, getResources().getString(
+ R.string.rsa)));
+ choices.add(new Choice<Algorithm>(Algorithm.ECDSA, getResources().getString(
+ R.string.ecdsa)));
+ choices.add(new Choice<Algorithm>(Algorithm.ECDH, getResources().getString(
+ R.string.ecdh)));
+ ArrayAdapter<Choice<Algorithm>> adapter = new ArrayAdapter<Choice<Algorithm>>(context,
+ android.R.layout.simple_spinner_item, choices);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ mAlgorithmSpinner.setAdapter(adapter);
+ // make RSA the default
+ for (int i = 0; i < choices.size(); ++i) {
+ if (choices.get(i).getId() == Algorithm.RSA) {
+ mAlgorithmSpinner.setSelection(i);
+ break;
+ }
}
}
@@ -161,58 +178,42 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mKeySizeSpinner.setAdapter(keySizeAdapter);
mKeySizeSpinner.setSelection(1); // Default to 4096 for the key length
-
- dialog.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface di, int id) {
- di.dismiss();
- Choice newKeyAlgorithmChoice = (Choice) mAlgorithmSpinner.getSelectedItem();
- int newKeySize = getProperKeyLength(newKeyAlgorithmChoice.getId(), getSelectedKeyLength());
-
- int flags = 0;
- if (mFlagCertify.isChecked()) {
- flags |= KeyFlags.CERTIFY_OTHER;
- }
- if (mFlagSign.isChecked()) {
- flags |= KeyFlags.SIGN_DATA;
- }
- if (mFlagEncrypt.isChecked()) {
- flags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
- }
- if (mFlagAuthenticate.isChecked()) {
- flags |= KeyFlags.AUTHENTICATION;
- }
-
- Long expiry;
- if (mNoExpiryCheckBox.isChecked()) {
- expiry = null;
- } else {
- Calendar selectedCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- //noinspection ResourceType
- selectedCal.set(mExpiryDatePicker.getYear(),
- mExpiryDatePicker.getMonth(), mExpiryDatePicker.getDayOfMonth());
- expiry = selectedCal.getTime().getTime() / 1000;
- }
-
- SaveKeyringParcel.SubkeyAdd newSubkey = new SaveKeyringParcel.SubkeyAdd(
- newKeyAlgorithmChoice.getId(),
- newKeySize,
- flags,
- expiry
- );
- mAlgorithmSelectedListener.onAlgorithmSelected(newSubkey);
- }
+ {
+ ArrayList<Choice<Curve>> choices = new ArrayList<Choice<Curve>>();
+
+ choices.add(new Choice<Curve>(Curve.NIST_P256, getResources().getString(
+ R.string.key_curve_nist_p256)));
+ choices.add(new Choice<Curve>(Curve.NIST_P384, getResources().getString(
+ R.string.key_curve_nist_p384)));
+ choices.add(new Choice<Curve>(Curve.NIST_P521, getResources().getString(
+ R.string.key_curve_nist_p521)));
+
+ /* @see SaveKeyringParcel
+ choices.add(new Choice<Curve>(Curve.BRAINPOOL_P256, getResources().getString(
+ R.string.key_curve_bp_p256)));
+ choices.add(new Choice<Curve>(Curve.BRAINPOOL_P384, getResources().getString(
+ R.string.key_curve_bp_p384)));
+ choices.add(new Choice<Curve>(Curve.BRAINPOOL_P512, getResources().getString(
+ R.string.key_curve_bp_p512)));
+ */
+
+ ArrayAdapter<Choice<Curve>> adapter = new ArrayAdapter<Choice<Curve>>(context,
+ android.R.layout.simple_spinner_item, choices);
+ mCurveSpinner.setAdapter(adapter);
+ // make NIST P-256 the default
+ for (int i = 0; i < choices.size(); ++i) {
+ if (choices.get(i).getId() == Curve.NIST_P256) {
+ mCurveSpinner.setSelection(i);
+ break;
}
- );
+ }
+ }
dialog.setCancelable(true);
- dialog.setNegativeButton(android.R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface di, int id) {
- di.dismiss();
- }
- }
- );
+
+ // onClickListener are set in onStart() to override default dismiss behaviour
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.setNegativeButton(android.R.string.cancel, null);
final AlertDialog alertDialog = dialog.show();
@@ -246,7 +247,7 @@ public class AddSubkeyDialogFragment extends DialogFragment {
mAlgorithmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- setKeyLengthSpinnerValuesForAlgorithm(((Choice) parent.getSelectedItem()).getId());
+ updateUiForAlgorithm(((Choice<Algorithm>) parent.getSelectedItem()).getId());
setCustomKeyVisibility();
setOkButtonAvailability(alertDialog);
@@ -260,6 +261,79 @@ public class AddSubkeyDialogFragment extends DialogFragment {
return alertDialog;
}
+ @Override
+ public void onStart() {
+ super.onStart(); //super.onStart() is where dialog.show() is actually called on the underlying dialog, so we have to do it after this point
+ AlertDialog d = (AlertDialog) getDialog();
+ if (d != null) {
+ Button positiveButton = d.getButton(Dialog.BUTTON_POSITIVE);
+ Button negativeButton = d.getButton(Dialog.BUTTON_NEGATIVE);
+ positiveButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!mFlagCertify.isChecked() && !mFlagSign.isChecked()
+ && !mFlagEncrypt.isChecked() && !mFlagAuthenticate.isChecked()) {
+ Toast.makeText(getActivity(), R.string.edit_key_select_flag, Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ Algorithm algorithm = ((Choice<Algorithm>) mAlgorithmSpinner.getSelectedItem()).getId();
+ Curve curve = null;
+ Integer keySize = null;
+ // For EC keys, add a curve
+ if (algorithm == Algorithm.ECDH || algorithm == Algorithm.ECDSA) {
+ curve = ((Choice<Curve>) mCurveSpinner.getSelectedItem()).getId();
+ // Otherwise, get a keysize
+ } else {
+ keySize = getProperKeyLength(algorithm, getSelectedKeyLength());
+ }
+
+ int flags = 0;
+ if (mFlagCertify.isChecked()) {
+ flags |= KeyFlags.CERTIFY_OTHER;
+ }
+ if (mFlagSign.isChecked()) {
+ flags |= KeyFlags.SIGN_DATA;
+ }
+ if (mFlagEncrypt.isChecked()) {
+ flags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE;
+ }
+ if (mFlagAuthenticate.isChecked()) {
+ flags |= KeyFlags.AUTHENTICATION;
+ }
+
+ long expiry;
+ if (mNoExpiryCheckBox.isChecked()) {
+ expiry = 0L;
+ } else {
+ Calendar selectedCal = Calendar.getInstance(TimeZone.getDefault());
+ //noinspection ResourceType
+ selectedCal.set(mExpiryDatePicker.getYear(),
+ mExpiryDatePicker.getMonth(), mExpiryDatePicker.getDayOfMonth());
+ // date picker uses default time zone, we need to convert to UTC
+ selectedCal.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ expiry = selectedCal.getTime().getTime() / 1000;
+ }
+
+ SaveKeyringParcel.SubkeyAdd newSubkey = new SaveKeyringParcel.SubkeyAdd(
+ algorithm, keySize, curve, flags, expiry
+ );
+ mAlgorithmSelectedListener.onAlgorithmSelected(newSubkey);
+
+ // finally, dismiss the dialogue
+ dismiss();
+ }
+ });
+ negativeButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ }
+ });
+ }
+ }
+
private int getSelectedKeyLength() {
final String selectedItemString = (String) mKeySizeSpinner.getSelectedItem();
final String customLengthString = getResources().getString(R.string.key_size_custom);
@@ -290,16 +364,16 @@ public class AddSubkeyDialogFragment extends DialogFragment {
* @return correct key length, according to SpongyCastle specification. Returns <code>-1</code>, if key length is
* inappropriate.
*/
- private int getProperKeyLength(int algorithmId, int currentKeyLength) {
+ private int getProperKeyLength(Algorithm algorithm, int currentKeyLength) {
final int[] elGamalSupportedLengths = {1536, 2048, 3072, 4096, 8192};
int properKeyLength = -1;
- switch (algorithmId) {
- case PublicKeyAlgorithmTags.RSA_GENERAL:
+ switch (algorithm) {
+ case RSA:
if (currentKeyLength > 1024 && currentKeyLength <= 16384) {
properKeyLength = currentKeyLength + ((8 - (currentKeyLength % 8)) % 8);
}
break;
- case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
+ case ELGAMAL:
int[] elGammalKeyDiff = new int[elGamalSupportedLengths.length];
for (int i = 0; i < elGamalSupportedLengths.length; i++) {
elGammalKeyDiff[i] = Math.abs(elGamalSupportedLengths[i] - currentKeyLength);
@@ -314,7 +388,7 @@ public class AddSubkeyDialogFragment extends DialogFragment {
}
properKeyLength = elGamalSupportedLengths[minimalIndex];
break;
- case PublicKeyAlgorithmTags.DSA:
+ case DSA:
if (currentKeyLength >= 512 && currentKeyLength <= 1024) {
properKeyLength = currentKeyLength + ((64 - (currentKeyLength % 64)) % 64);
}
@@ -324,10 +398,10 @@ public class AddSubkeyDialogFragment extends DialogFragment {
}
private void setOkButtonAvailability(AlertDialog alertDialog) {
- final Choice selectedAlgorithm = (Choice) mAlgorithmSpinner.getSelectedItem();
- final int selectedKeySize = getSelectedKeyLength(); //Integer.parseInt((String) mKeySizeSpinner.getSelectedItem());
- final int properKeyLength = getProperKeyLength(selectedAlgorithm.getId(), selectedKeySize);
- alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(properKeyLength > 0);
+ Algorithm algorithm = ((Choice<Algorithm>) mAlgorithmSpinner.getSelectedItem()).getId();
+ boolean enabled = algorithm == Algorithm.ECDSA || algorithm == Algorithm.ECDH
+ || getProperKeyLength(algorithm, getSelectedKeyLength()) > 0;
+ alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled);
}
private void setCustomKeyVisibility() {
@@ -343,38 +417,104 @@ public class AddSubkeyDialogFragment extends DialogFragment {
// hide keyboard after setting visibility to gone
if (visibility == View.GONE) {
InputMethodManager imm = (InputMethodManager)
- getActivity().getSystemService(getActivity().INPUT_METHOD_SERVICE);
+ getActivity().getSystemService(FragmentActivity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mCustomKeyEditText.getWindowToken(), 0);
}
}
- private void setKeyLengthSpinnerValuesForAlgorithm(int algorithmId) {
+ private void updateUiForAlgorithm(Algorithm algorithm) {
final ArrayAdapter<CharSequence> keySizeAdapter = (ArrayAdapter<CharSequence>) mKeySizeSpinner.getAdapter();
- final Object selectedItem = mKeySizeSpinner.getSelectedItem();
keySizeAdapter.clear();
- switch (algorithmId) {
- case PublicKeyAlgorithmTags.RSA_GENERAL:
+ switch (algorithm) {
+ case RSA:
replaceArrayAdapterContent(keySizeAdapter, R.array.rsa_key_size_spinner_values);
+ mKeySizeSpinner.setSelection(1);
+ mKeySizeRow.setVisibility(View.VISIBLE);
+ mCurveRow.setVisibility(View.GONE);
mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_rsa));
+ // allowed flags:
+ mFlagSign.setEnabled(true);
+ mFlagEncrypt.setEnabled(true);
+ mFlagAuthenticate.setEnabled(true);
+
+ if (mWillBeMasterKey) {
+ mFlagCertify.setEnabled(true);
+
+ mFlagCertify.setChecked(true);
+ mFlagSign.setChecked(false);
+ mFlagEncrypt.setChecked(false);
+ } else {
+ mFlagCertify.setEnabled(false);
+
+ mFlagCertify.setChecked(false);
+ mFlagSign.setChecked(true);
+ mFlagEncrypt.setChecked(true);
+ }
+ mFlagAuthenticate.setChecked(false);
break;
- case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT:
+ case ELGAMAL:
replaceArrayAdapterContent(keySizeAdapter, R.array.elgamal_key_size_spinner_values);
+ mKeySizeSpinner.setSelection(3);
+ mKeySizeRow.setVisibility(View.VISIBLE);
+ mCurveRow.setVisibility(View.GONE);
mCustomKeyInfoTextView.setText(""); // ElGamal does not support custom key length
+ // allowed flags:
+ mFlagCertify.setChecked(false);
+ mFlagCertify.setEnabled(false);
+ mFlagSign.setChecked(false);
+ mFlagSign.setEnabled(false);
+ mFlagEncrypt.setChecked(true);
+ mFlagEncrypt.setEnabled(true);
+ mFlagAuthenticate.setChecked(false);
+ mFlagAuthenticate.setEnabled(false);
break;
- case PublicKeyAlgorithmTags.DSA:
+ case DSA:
replaceArrayAdapterContent(keySizeAdapter, R.array.dsa_key_size_spinner_values);
+ mKeySizeSpinner.setSelection(2);
+ mKeySizeRow.setVisibility(View.VISIBLE);
+ mCurveRow.setVisibility(View.GONE);
mCustomKeyInfoTextView.setText(getResources().getString(R.string.key_size_custom_info_dsa));
+ // allowed flags:
+ mFlagCertify.setChecked(false);
+ mFlagCertify.setEnabled(false);
+ mFlagSign.setChecked(true);
+ mFlagSign.setEnabled(true);
+ mFlagEncrypt.setChecked(false);
+ mFlagEncrypt.setEnabled(false);
+ mFlagAuthenticate.setChecked(false);
+ mFlagAuthenticate.setEnabled(false);
+ break;
+ case ECDSA:
+ mKeySizeRow.setVisibility(View.GONE);
+ mCurveRow.setVisibility(View.VISIBLE);
+ mCustomKeyInfoTextView.setText("");
+ // allowed flags:
+ mFlagCertify.setEnabled(mWillBeMasterKey);
+ mFlagCertify.setChecked(mWillBeMasterKey);
+ mFlagSign.setEnabled(true);
+ mFlagSign.setChecked(!mWillBeMasterKey);
+ mFlagEncrypt.setEnabled(false);
+ mFlagEncrypt.setChecked(false);
+ mFlagAuthenticate.setEnabled(true);
+ mFlagAuthenticate.setChecked(false);
+ break;
+ case ECDH:
+ mKeySizeRow.setVisibility(View.GONE);
+ mCurveRow.setVisibility(View.VISIBLE);
+ mCustomKeyInfoTextView.setText("");
+ // allowed flags:
+ mFlagCertify.setChecked(false);
+ mFlagCertify.setEnabled(false);
+ mFlagSign.setChecked(false);
+ mFlagSign.setEnabled(false);
+ mFlagEncrypt.setChecked(true);
+ mFlagEncrypt.setEnabled(true);
+ mFlagAuthenticate.setChecked(false);
+ mFlagAuthenticate.setEnabled(false);
break;
}
keySizeAdapter.notifyDataSetChanged();
- // when switching algorithm, try to select same key length as before
- for (int i = 0; i < keySizeAdapter.getCount(); i++) {
- if (selectedItem.equals(keySizeAdapter.getItem(i))) {
- mKeySizeSpinner.setSelection(i);
- break;
- }
- }
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
index 4927a4d24..d0c9cea5b 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java
@@ -18,7 +18,9 @@
package org.sufficientlysecure.keychain.ui.dialog;
import android.app.Dialog;
+import android.app.ProgressDialog;
import android.content.DialogInterface;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Message;
import android.os.Messenger;
@@ -31,9 +33,10 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler;
import org.sufficientlysecure.keychain.util.Log;
import java.util.HashMap;
@@ -48,8 +51,6 @@ public class DeleteKeyDialogFragment extends DialogFragment {
private TextView mMainMessage;
private View mInflateView;
- private Messenger mMessenger;
-
/**
* Creates new instance of this delete file dialog fragment
*/
@@ -68,7 +69,7 @@ public class DeleteKeyDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final FragmentActivity activity = getActivity();
- mMessenger = getArguments().getParcelable(ARG_MESSENGER);
+ final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER);
final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
@@ -83,6 +84,8 @@ public class DeleteKeyDialogFragment extends DialogFragment {
builder.setTitle(R.string.warning);
+ final boolean hasSecret;
+
// If only a single key has been selected
if (masterKeyIds.length == 1) {
long masterKeyId = masterKeyIds[0];
@@ -98,7 +101,7 @@ public class DeleteKeyDialogFragment extends DialogFragment {
}
);
String userId = (String) data.get(KeyRings.USER_ID);
- boolean hasSecret = ((Long) data.get(KeyRings.HAS_ANY_SECRET)) == 1;
+ hasSecret = ((Long) data.get(KeyRings.HAS_ANY_SECRET)) == 1;
// Set message depending on which key it is.
mMainMessage.setText(getString(
@@ -107,12 +110,12 @@ public class DeleteKeyDialogFragment extends DialogFragment {
userId
));
} catch (ProviderHelper.NotFoundException e) {
- sendMessageToHandler(MESSAGE_ERROR, null);
dismiss();
return null;
}
} else {
mMainMessage.setText(R.string.key_deletion_confirmation_multi);
+ hasSecret = false;
}
builder.setIcon(R.drawable.ic_dialog_alert_holo_light);
@@ -120,18 +123,45 @@ public class DeleteKeyDialogFragment extends DialogFragment {
@Override
public void onClick(DialogInterface dialog, int which) {
- boolean success = false;
- for (long masterKeyId : masterKeyIds) {
- int count = activity.getContentResolver().delete(
- KeyRingData.buildPublicKeyRingUri(masterKeyId), null, null
- );
- success = count > 0;
- }
- if (success) {
- sendMessageToHandler(MESSAGE_OKAY, null);
- } else {
- sendMessageToHandler(MESSAGE_ERROR, null);
- }
+ // Send all information needed to service to import key in other thread
+ Intent intent = new Intent(getActivity(), KeychainIntentService.class);
+
+ intent.setAction(KeychainIntentService.ACTION_DELETE);
+
+ // Message is received after importing is done in KeychainIntentService
+ KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(
+ getActivity(),
+ getString(R.string.progress_deleting),
+ ProgressDialog.STYLE_HORIZONTAL) {
+ public void handleMessage(Message message) {
+ // handle messages by standard KeychainIntentServiceHandler first
+ super.handleMessage(message);
+ try {
+ Message msg = Message.obtain();
+ msg.copyFrom(message);
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "messenger error", e);
+ }
+ }
+ };
+
+ // fill values for this action
+ Bundle data = new Bundle();
+ data.putLongArray(KeychainIntentService.DELETE_KEY_LIST, masterKeyIds);
+ data.putBoolean(KeychainIntentService.DELETE_IS_SECRET, hasSecret);
+ intent.putExtra(KeychainIntentService.EXTRA_DATA, data);
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(saveHandler);
+ intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger);
+
+ // show progress dialog
+ saveHandler.showProgressDialog(getActivity());
+
+ // start service with intent
+ getActivity().startService(intent);
+
dismiss();
}
});
@@ -146,23 +176,4 @@ public class DeleteKeyDialogFragment extends DialogFragment {
return builder.show();
}
- /**
- * Send message back to handler which is initialized in a activity
- *
- * @param what Message integer you want to send
- */
- private void sendMessageToHandler(Integer what, Bundle data) {
- Message msg = Message.obtain();
- msg.what = what;
- if (data != null) {
- msg.setData(data);
- }
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
- } catch (NullPointerException e) {
- Log.w(Constants.TAG, "Messenger is null!", e);
- }
- }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java
index aa63f9944..f46e253c0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/EditSubkeyExpiryDialogFragment.java
@@ -25,9 +25,10 @@ import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.DialogFragment;
-import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
import android.widget.DatePicker;
import org.sufficientlysecure.keychain.Constants;
@@ -40,17 +41,14 @@ import java.util.TimeZone;
public class EditSubkeyExpiryDialogFragment extends DialogFragment {
private static final String ARG_MESSENGER = "messenger";
- private static final String ARG_CREATION_DATE = "creation_date";
- private static final String ARG_EXPIRY_DATE = "expiry_date";
+ private static final String ARG_CREATION = "creation";
+ private static final String ARG_EXPIRY = "expiry";
- public static final int MESSAGE_NEW_EXPIRY_DATE = 1;
+ public static final int MESSAGE_NEW_EXPIRY = 1;
public static final int MESSAGE_CANCEL = 2;
- public static final String MESSAGE_DATA_EXPIRY_DATE = "expiry_date";
+ public static final String MESSAGE_DATA_EXPIRY = "expiry";
private Messenger mMessenger;
- private Calendar mExpiryCal;
-
- private DatePicker mDatePicker;
/**
* Creates new instance of this dialog fragment
@@ -60,8 +58,8 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment {
EditSubkeyExpiryDialogFragment frag = new EditSubkeyExpiryDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger);
- args.putSerializable(ARG_CREATION_DATE, creationDate);
- args.putSerializable(ARG_EXPIRY_DATE, expiryDate);
+ args.putSerializable(ARG_CREATION, creationDate);
+ args.putSerializable(ARG_EXPIRY, expiryDate);
frag.setArguments(args);
@@ -75,15 +73,17 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
- Date creationDate = new Date(getArguments().getLong(ARG_CREATION_DATE) * 1000);
- Date expiryDate = new Date(getArguments().getLong(ARG_EXPIRY_DATE) * 1000);
+ long creation = getArguments().getLong(ARG_CREATION);
+ long expiry = getArguments().getLong(ARG_EXPIRY);
Calendar creationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- creationCal.setTime(creationDate);
- mExpiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- mExpiryCal.setTime(expiryDate);
+ creationCal.setTime(new Date(creation * 1000));
+ final Calendar expiryCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ expiryCal.setTime(new Date(expiry * 1000));
- Log.d(Constants.TAG, "onCreateDialog");
+ // date picker works with default time zone, we need to convert from UTC to default timezone
+ creationCal.setTimeZone(TimeZone.getDefault());
+ expiryCal.setTimeZone(TimeZone.getDefault());
// Explicitly not using DatePickerDialog here!
// DatePickerDialog is difficult to customize and has many problems (see old git versions)
@@ -95,19 +95,64 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment {
View view = inflater.inflate(R.layout.edit_subkey_expiry_dialog, null);
alert.setView(view);
- mDatePicker = (DatePicker) view.findViewById(R.id.edit_subkey_expiry_date_picker);
+ final CheckBox noExpiry = (CheckBox) view.findViewById(R.id.edit_subkey_expiry_no_expiry);
+ final DatePicker datePicker = (DatePicker) view.findViewById(R.id.edit_subkey_expiry_date_picker);
+
+ noExpiry.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (isChecked) {
+ datePicker.setVisibility(View.GONE);
+ } else {
+ datePicker.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+
+ // init date picker with default selected date
+ if (expiry == 0L) {
+ noExpiry.setChecked(true);
+ datePicker.setVisibility(View.GONE);
+
+ Calendar todayCal = Calendar.getInstance(TimeZone.getDefault());
+ if (creationCal.after(todayCal)) {
+ // Note: This is just for the rare cases where creation is _after_ today
+
+ // set it to creation date +1 day (don't set it to creationCal, it would break crash
+ // datePicker.setMinDate() execution with IllegalArgumentException
+ Calendar creationCalPlusOne = (Calendar) creationCal.clone();
+ creationCalPlusOne.add(Calendar.DAY_OF_YEAR, 1);
+ datePicker.init(
+ creationCalPlusOne.get(Calendar.YEAR),
+ creationCalPlusOne.get(Calendar.MONTH),
+ creationCalPlusOne.get(Calendar.DAY_OF_MONTH),
+ null
+ );
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
- // will crash with IllegalArgumentException if we set a min date
- // that is not before expiry
- if (creationCal.before(mExpiryCal)) {
- mDatePicker.setMinDate(creationCal.getTime().getTime()
- + DateUtils.DAY_IN_MILLIS);
} else {
- // when creation date isn't available
- mDatePicker.setMinDate(mExpiryCal.getTime().getTime()
- + DateUtils.DAY_IN_MILLIS);
+ // normally, just init with today
+ datePicker.init(
+ todayCal.get(Calendar.YEAR),
+ todayCal.get(Calendar.MONTH),
+ todayCal.get(Calendar.DAY_OF_MONTH),
+ null
+ );
}
+ } else {
+ noExpiry.setChecked(false);
+ datePicker.setVisibility(View.VISIBLE);
+
+ // set date picker to current expiry
+ datePicker.init(
+ expiryCal.get(Calendar.YEAR),
+ expiryCal.get(Calendar.MONTH),
+ expiryCal.get(Calendar.DAY_OF_MONTH),
+ null
+ );
+ }
+
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+ datePicker.setMinDate(creationCal.getTime().getTime());
}
alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@@ -115,34 +160,28 @@ public class EditSubkeyExpiryDialogFragment extends DialogFragment {
public void onClick(DialogInterface dialog, int id) {
dismiss();
- Calendar selectedCal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
- //noinspection ResourceType
- selectedCal.set(mDatePicker.getYear(), mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
+ long expiry;
+ if (noExpiry.isChecked()) {
+ expiry = 0L;
+ } else {
+ Calendar selectedCal = Calendar.getInstance(TimeZone.getDefault());
+ //noinspection ResourceType
+ selectedCal.set(datePicker.getYear(), datePicker.getMonth(), datePicker.getDayOfMonth());
+ // date picker uses default time zone, we need to convert to UTC
+ selectedCal.setTimeZone(TimeZone.getTimeZone("UTC"));
- if (mExpiryCal != null) {
long numDays = (selectedCal.getTimeInMillis() / 86400000)
- - (mExpiryCal.getTimeInMillis() / 86400000);
- if (numDays > 0) {
- Bundle data = new Bundle();
- data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime().getTime() / 1000);
- sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
+ - (expiryCal.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ Log.e(Constants.TAG, "Should not happen! Expiry num of days <= 0!");
+ throw new RuntimeException();
}
- } else {
- Bundle data = new Bundle();
- data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, selectedCal.getTime().getTime() / 1000);
- sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
+ expiry = selectedCal.getTime().getTime() / 1000;
}
- }
- });
-
- alert.setNeutralButton(R.string.btn_no_date, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- dismiss();
Bundle data = new Bundle();
- data.putSerializable(MESSAGE_DATA_EXPIRY_DATE, null);
- sendMessageToHandler(MESSAGE_NEW_EXPIRY_DATE, data);
+ data.putSerializable(MESSAGE_DATA_EXPIRY, expiry);
+ sendMessageToHandler(MESSAGE_NEW_EXPIRY, data);
}
});
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java
index 093a04aff..0324dd3b9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java
@@ -25,6 +25,7 @@ import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnKeyListener;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
+import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import org.sufficientlysecure.keychain.R;
@@ -113,7 +114,12 @@ public class ProgressDialogFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Activity activity = getActivity();
- ProgressDialog dialog = new ProgressDialog(activity);
+ // if the progress dialog is displayed from the application class, design is missing
+ // hack to get holo design (which is not automatically applied due to activity's Theme.NoDisplay
+ ContextThemeWrapper context = new ContextThemeWrapper(activity,
+ R.style.Theme_AppCompat_Light);
+
+ ProgressDialog dialog = new ProgressDialog(context);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
new file mode 100644
index 000000000..e3c9804bb
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java
@@ -0,0 +1,77 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.util.AttributeSet;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.KeychainDatabase;
+
+public class CertifyKeySpinner extends KeySpinner {
+ private long mHiddenMasterKeyId = Constants.key.none;
+
+ public CertifyKeySpinner(Context context) {
+ super(context);
+ }
+
+ public CertifyKeySpinner(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CertifyKeySpinner(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setHiddenMasterKeyId(long hiddenMasterKeyId) {
+ this.mHiddenMasterKeyId = hiddenMasterKeyId;
+ reload();
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader() {
+ // This is called when a new Loader needs to be created. This
+ // sample only has one Loader, so we don't care about the ID.
+ Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri();
+
+ // These are the rows that we will retrieve.
+ String[] projection = new String[]{
+ KeychainContract.KeyRings._ID,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.KEY_ID,
+ KeychainContract.KeyRings.USER_ID,
+ KeychainContract.KeyRings.IS_EXPIRED,
+ KeychainContract.KeyRings.HAS_CERTIFY,
+ KeychainContract.KeyRings.HAS_ANY_SECRET
+ };
+
+ String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND "
+ + KeychainContract.KeyRings.HAS_CERTIFY + " NOT NULL AND "
+ + KeychainContract.KeyRings.IS_REVOKED + " = 0 AND "
+ + KeychainContract.KeyRings.IS_EXPIRED + " = 0 AND " + KeychainDatabase.Tables.KEYS + "."
+ + KeychainContract.KeyRings.MASTER_KEY_ID + " != " + mHiddenMasterKeyId;
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(getContext(), baseUri, projection, where, null, null);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
index 20b9570bb..ceb3f665f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java
@@ -111,7 +111,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (getContext() instanceof FragmentActivity) {
- ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
+ ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(hashCode(), null, new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// These are the rows that we will retrieve.
@@ -143,6 +143,8 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView {
swapCursor(null);
}
});
+ } else {
+ Log.e(Constants.TAG, "EncryptKeyCompletionView must be attached to a FragmentActivity, this is " + getContext().getClass());
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
index 31e01a7fb..b456b61ab 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java
@@ -24,7 +24,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
-import android.widget.ImageButton;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -38,9 +38,7 @@ import org.sufficientlysecure.keychain.R;
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:foldedLabel="@string/TEXT_TO_DISPLAY_WHEN_FOLDED"
- custom:unFoldedLabel="@string/TEXT_TO_DISPLAY_WHEN_UNFOLDED"
- custom:foldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_FOLDED"
- custom:unFoldedIcon="ICON_NAME_FROM_FontAwesomeText_TO_USE_WHEN_UNFOLDED">
+ custom:unFoldedLabel="@string/TEXT_TO_DISPLAY_WHEN_UNFOLDED">
<include layout="@layout/ELEMENTS_TO_BE_FOLDED"/>
@@ -49,7 +47,7 @@ import org.sufficientlysecure.keychain.R;
*/
public class FoldableLinearLayout extends LinearLayout {
- private ImageButton mFoldableIcon;
+ private ImageView mFoldableIcon;
private boolean mFolded;
private boolean mHasMigrated = false;
private Integer mShortAnimationDuration = null;
@@ -139,7 +137,7 @@ public class FoldableLinearLayout extends LinearLayout {
}
private void initialiseInnerViews() {
- mFoldableIcon = (ImageButton) mFoldableLayout.findViewById(R.id.foldableIcon);
+ mFoldableIcon = (ImageView) mFoldableLayout.findViewById(R.id.foldableIcon);
mFoldableIcon.setImageResource(R.drawable.ic_action_expand);
mFoldableTextView = (TextView) mFoldableLayout.findViewById(R.id.foldableText);
mFoldableTextView.setText(mFoldedLabel);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
new file mode 100644
index 000000000..3a31fc9f4
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java
@@ -0,0 +1,232 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+import android.support.v4.widget.CursorAdapter;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.pgp.KeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.util.Log;
+
+public abstract class KeySpinner extends Spinner {
+ public interface OnKeyChangedListener {
+ public void onKeyChanged(long masterKeyId);
+ }
+
+ private long mSelectedKeyId;
+ private SelectKeyAdapter mAdapter = new SelectKeyAdapter();
+ private OnKeyChangedListener mListener;
+
+ public KeySpinner(Context context) {
+ super(context);
+ initView();
+ }
+
+ public KeySpinner(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initView();
+ }
+
+ public KeySpinner(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ initView();
+ }
+
+ private void initView() {
+ setAdapter(mAdapter);
+ super.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ if (mListener != null) {
+ mListener.onKeyChanged(id);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ if (mListener != null) {
+ mListener.onKeyChanged(Constants.key.none);
+ }
+ }
+ });
+ }
+
+ public abstract Loader<Cursor> onCreateLoader();
+
+ @Override
+ public void setOnItemSelectedListener(OnItemSelectedListener listener) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void setOnKeyChangedListener(OnKeyChangedListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ reload();
+ }
+
+ public void reload() {
+ if (getContext() instanceof FragmentActivity) {
+ ((FragmentActivity) getContext()).getSupportLoaderManager().restartLoader(hashCode(), null, new LoaderManager.LoaderCallbacks<Cursor>() {
+ @Override
+ public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+ return KeySpinner.this.onCreateLoader();
+ }
+
+ @Override
+ public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+ mAdapter.swapCursor(data);
+ }
+
+ @Override
+ public void onLoaderReset(Loader<Cursor> loader) {
+ mAdapter.swapCursor(null);
+ }
+ });
+ } else {
+ Log.e(Constants.TAG, "KeySpinner must be attached to FragmentActivity, this is " + getContext().getClass());
+ }
+ }
+
+ public long getSelectedKeyId() {
+ return mSelectedKeyId;
+ }
+
+ public void setSelectedKeyId(long selectedKeyId) {
+ this.mSelectedKeyId = selectedKeyId;
+ }
+
+ private class SelectKeyAdapter extends BaseAdapter implements SpinnerAdapter {
+ private CursorAdapter inner;
+ private int mIndexUserId;
+ private int mIndexKeyId;
+ private int mIndexMasterKeyId;
+
+ public SelectKeyAdapter() {
+ inner = new CursorAdapter(null, null, 0) {
+ @Override
+ public View newView(Context context, Cursor cursor, ViewGroup parent) {
+ return View.inflate(getContext(), R.layout.keyspinner_key, null);
+ }
+
+ @Override
+ public void bindView(View view, Context context, Cursor cursor) {
+ String[] userId = KeyRing.splitUserId(cursor.getString(mIndexUserId));
+ ((TextView) view.findViewById(android.R.id.title)).setText(userId[2] == null ? userId[0] : (userId[0] + " (" + userId[2] + ")"));
+ ((TextView) view.findViewById(android.R.id.text1)).setText(userId[1]);
+ ((TextView) view.findViewById(android.R.id.text2)).setText(PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId)));
+ }
+
+ @Override
+ public long getItemId(int position) {
+ try {
+ return ((Cursor) getItem(position)).getLong(mIndexMasterKeyId);
+ } catch (Exception e) {
+ // This can happen on concurrent modification :(
+ return Constants.key.none;
+ }
+ }
+ };
+ }
+
+ public Cursor swapCursor(Cursor newCursor) {
+ if (newCursor == null) return inner.swapCursor(null);
+
+ mIndexKeyId = newCursor.getColumnIndex(KeychainContract.KeyRings.KEY_ID);
+ mIndexUserId = newCursor.getColumnIndex(KeychainContract.KeyRings.USER_ID);
+ mIndexMasterKeyId = newCursor.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID);
+ if (newCursor.moveToFirst()) {
+ do {
+ if (newCursor.getLong(mIndexMasterKeyId) == mSelectedKeyId) {
+ setSelection(newCursor.getPosition() + 1);
+ }
+ } while (newCursor.moveToNext());
+ }
+ return inner.swapCursor(newCursor);
+ }
+
+ @Override
+ public int getCount() {
+ return inner.getCount() + 1;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ if (position == 0) return null;
+ return inner.getItem(position - 1);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ if (position == 0) return Constants.key.none;
+ return inner.getItemId(position - 1);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ try {
+ View v = getDropDownView(position, convertView, parent);
+ v.findViewById(android.R.id.text1).setVisibility(View.GONE);
+ return v;
+ } catch (NullPointerException e) {
+ // This is for the preview...
+ return View.inflate(getContext(), android.R.layout.simple_list_item_1, null);
+ }
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ View v;
+ if (position == 0) {
+ if (convertView == null) {
+ v = inner.newView(null, null, parent);
+ } else {
+ v = convertView;
+ }
+ ((TextView) v.findViewById(android.R.id.title)).setText("None");
+ v.findViewById(android.R.id.text1).setVisibility(View.GONE);
+ v.findViewById(android.R.id.text2).setVisibility(View.GONE);
+ } else {
+ v = inner.getView(position - 1, convertView, parent);
+ v.findViewById(android.R.id.text1).setVisibility(View.VISIBLE);
+ v.findViewById(android.R.id.text2).setVisibility(View.VISIBLE);
+ }
+ return v;
+ }
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java
deleted file mode 100644
index a48d2a026..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/NoSwipeWrapContentViewPager.java
+++ /dev/null
@@ -1,62 +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.widget;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-
-public class NoSwipeWrapContentViewPager extends android.support.v4.view.ViewPager {
- public NoSwipeWrapContentViewPager(Context context) {
- super(context);
- }
-
- public NoSwipeWrapContentViewPager(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
- int height;
- View child = getChildAt(getCurrentItem());
- if (child != null) {
- child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- height = child.getMeasuredHeight();
- } else {
- height = 0;
- }
-
- heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
-
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent arg0) {
- // Never allow swiping to switch between pages
- return false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // Never allow swiping to switch between pages
- return false;
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
new file mode 100644
index 000000000..a1ed2c065
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SignKeySpinner.java
@@ -0,0 +1,65 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.support.v4.content.CursorLoader;
+import android.support.v4.content.Loader;
+import android.util.AttributeSet;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+
+public class SignKeySpinner extends KeySpinner {
+ public SignKeySpinner(Context context) {
+ super(context);
+ }
+
+ public SignKeySpinner(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public SignKeySpinner(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public Loader<Cursor> onCreateLoader() {
+ // This is called when a new Loader needs to be created. This
+ // sample only has one Loader, so we don't care about the ID.
+ Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri();
+
+ // These are the rows that we will retrieve.
+ String[] projection = new String[]{
+ KeychainContract.KeyRings._ID,
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.KEY_ID,
+ KeychainContract.KeyRings.USER_ID,
+ KeychainContract.KeyRings.IS_EXPIRED,
+ KeychainContract.KeyRings.HAS_SIGN,
+ KeychainContract.KeyRings.HAS_ANY_SECRET
+ };
+
+ String where = KeychainContract.KeyRings.HAS_ANY_SECRET + " = 1 AND " + KeychainContract.KeyRings.HAS_SIGN + " NOT NULL AND "
+ + KeychainContract.KeyRings.IS_REVOKED + " = 0 AND " + KeychainContract.KeyRings.IS_EXPIRED + " = 0";
+
+ // Now create and return a CursorLoader that will take care of
+ // creating a Cursor for the data being displayed.
+ return new CursorLoader(getContext(), baseUri, projection, where, null, null);
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
index 9acc7a73b..99db634ac 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/AlgorithmNames.java
@@ -50,7 +50,6 @@ public class AlgorithmNames {
mEncryptionNames.put(PGPEncryptedData.TRIPLE_DES, "Triple DES");
mEncryptionNames.put(PGPEncryptedData.IDEA, "IDEA");
- mHashNames.put(HashAlgorithmTags.MD5, "MD5");
mHashNames.put(HashAlgorithmTags.RIPEMD160, "RIPEMD-160");
mHashNames.put(HashAlgorithmTags.SHA1, "SHA-1");
mHashNames.put(HashAlgorithmTags.SHA224, "SHA-224");
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
index 70c7d80fe..48f10d4b9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Choice.java
@@ -17,21 +17,16 @@
package org.sufficientlysecure.keychain.util;
-public class Choice {
+public class Choice <E> {
private String mName;
- private int mId;
+ private E mId;
- public Choice() {
- mId = -1;
- mName = "";
- }
-
- public Choice(int id, String name) {
+ public Choice(E id, String name) {
mId = id;
mName = name;
}
- public int getId() {
+ public E getId() {
return mId;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java
index 5a4bf5311..09275fc95 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java
@@ -46,10 +46,11 @@ public class FileImportCache<E extends Parcelable> {
private Context mContext;
- private static final String FILENAME = "key_import.pcl";
+ private final String mFilename;
- public FileImportCache(Context context) {
- this.mContext = context;
+ public FileImportCache(Context context, String filename) {
+ mContext = context;
+ mFilename = filename;
}
public void writeCache(ArrayList<E> selectedEntries) throws IOException {
@@ -64,7 +65,7 @@ public class FileImportCache<E extends Parcelable> {
throw new IOException("cache dir is null!");
}
- File tempFile = new File(mContext.getCacheDir(), FILENAME);
+ File tempFile = new File(mContext.getCacheDir(), mFilename);
DataOutputStream oos = new DataOutputStream(new FileOutputStream(tempFile));
@@ -91,6 +92,10 @@ public class FileImportCache<E extends Parcelable> {
}
public Iterator<E> readCache() throws IOException {
+ return readCache(true);
+ }
+
+ public Iterator<E> readCache(final boolean deleteAfterRead) throws IOException {
File cacheDir = mContext.getCacheDir();
if (cacheDir == null) {
@@ -98,7 +103,7 @@ public class FileImportCache<E extends Parcelable> {
throw new IOException("cache dir is null!");
}
- final File tempFile = new File(cacheDir, FILENAME);
+ final File tempFile = new File(cacheDir, mFilename);
final DataInputStream ois = new DataInputStream(new FileInputStream(tempFile));
return new Iterator<E>() {
@@ -165,7 +170,10 @@ public class FileImportCache<E extends Parcelable> {
if (!closed) {
try {
ois.close();
- tempFile.delete();
+ if (deleteAfterRead) {
+ //noinspection ResultOfMethodCallIgnored
+ tempFile.delete();
+ }
} catch (IOException e) {
// nvm
}
@@ -176,4 +184,17 @@ public class FileImportCache<E extends Parcelable> {
};
}
+
+ public boolean delete() throws IOException {
+
+ File cacheDir = mContext.getCacheDir();
+ if (cacheDir == null) {
+ // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU
+ throw new IOException("cache dir is null!");
+ }
+
+ final File tempFile = new File(cacheDir, mFilename);
+ return tempFile.delete();
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressFixedScaler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressFixedScaler.java
new file mode 100644
index 000000000..4bb4ca5de
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ProgressFixedScaler.java
@@ -0,0 +1,29 @@
+package org.sufficientlysecure.keychain.util;
+
+import org.sufficientlysecure.keychain.pgp.Progressable;
+
+/** This is a simple variant of ProgressScaler which shows a fixed progress message, ignoring
+ * the provided ones.
+ */
+public class ProgressFixedScaler extends ProgressScaler {
+
+ final int mResId;
+
+ public ProgressFixedScaler(Progressable wrapped, int from, int to, int max, int resId) {
+ super(wrapped, from, to, max);
+ mResId = resId;
+ }
+
+ public void setProgress(int resourceId, int progress, int max) {
+ if (mWrapped != null) {
+ mWrapped.setProgress(mResId, mFrom + progress * (mTo - mFrom) / max, mMax);
+ }
+ }
+
+ public void setProgress(String message, int progress, int max) {
+ if (mWrapped != null) {
+ mWrapped.setProgress(mResId, mFrom + progress * (mTo - mFrom) / max, mMax);
+ }
+ }
+
+}