aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2014-09-23 15:56:48 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2014-09-23 15:56:48 +0200
commit070017b12febe8012cd1381fcff8addfd82211f6 (patch)
tree414a84ffb539e022830cb7f08f247bfd9d8aad3f
parent675e8e2015000ae406758436628a5c9f67212e8b (diff)
downloadopen-keychain-070017b12febe8012cd1381fcff8addfd82211f6.tar.gz
open-keychain-070017b12febe8012cd1381fcff8addfd82211f6.tar.bz2
open-keychain-070017b12febe8012cd1381fcff8addfd82211f6.zip
passphrasecache: cache by master key, introduce preference for cache by subkey
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java1
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java70
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java67
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java163
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java11
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java20
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java7
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java10
-rw-r--r--OpenKeychain/src/main/res/values/strings.xml1
-rw-r--r--OpenKeychain/src/main/res/xml/adv_preferences.xml4
13 files changed, 208 insertions, 154 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
index 51fa1795d..f8f0d2ac3 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java
@@ -72,6 +72,7 @@ public final class Constants {
public static final String DEFAULT_MESSAGE_COMPRESSION = "defaultMessageCompression";
public static final String DEFAULT_FILE_COMPRESSION = "defaultFileCompression";
public static final String PASSPHRASE_CACHE_TTL = "passphraseCacheTtl";
+ public static final String PASSPHRASE_CACHE_SUBS = "passphraseCacheSubs";
public static final String LANGUAGE = "language";
public static final String KEY_SERVERS = "keyServers";
public static final String PREF_DEFAULT_VERSION = "keyServersDefaultVersion";
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java
index 433c4db00..ae1b94a34 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PassphraseCacheInterface.java
@@ -6,6 +6,8 @@ public interface PassphraseCacheInterface {
}
}
- public String getCachedPassphrase(long masterKeyId) throws NoSecretKeyException;
+ public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException;
+
+ public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException;
}
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 55354cdec..1b4ad1fc1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -34,9 +34,11 @@ import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.nfc.NfcActivity;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
+import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface.NoSecretKeyException;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
@@ -74,6 +76,28 @@ public class OpenPgpService extends RemoteService {
static final String EMAIL_SEARCH_WHERE = Tables.KEYS + "." + KeychainContract.KeyRings.IS_REVOKED
+ " = 0 AND " + KeychainContract.KeyRings.IS_EXPIRED + " = 0";
+ private PassphraseCacheInterface passphraseCacheInterface = new PassphraseCacheInterface() {
+ @Override
+ public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException {
+ try {
+ long masterKeyId = new ProviderHelper(getContext()).getMasterKeyId(subKeyId);
+ return getCachedPassphrase(masterKeyId, subKeyId);
+ } catch (NotFoundException e) {
+ throw new PassphraseCacheInterface.NoSecretKeyException();
+ }
+ }
+
+ @Override
+ public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException {
+ try {
+ return PassphraseCacheService.getCachedPassphrase(
+ getContext(), masterKeyId, subKeyId);
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ throw new PassphraseCacheInterface.NoSecretKeyException();
+ }
+ }
+ };
+
/**
* Search database for key ids based on emails.
*
@@ -232,7 +256,8 @@ public class OpenPgpService extends RemoteService {
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
} else {
try {
- passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), accSettings.getKeyId());
+ passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
+ accSettings.getKeyId(), accSettings.getKeyId());
} catch (PassphraseCacheService.KeyNotFoundException e) {
// secret key that is set for this account is deleted?
// show account config again!
@@ -269,22 +294,11 @@ public class OpenPgpService extends RemoteService {
// Find the appropriate subkey to sign with
CachedPublicKeyRing signingRing =
new ProviderHelper(this).getCachedPublicKeyRing(accSettings.getKeyId());
- long sigSubKeyId = signingRing.getSignId();
+ final long sigSubKeyId = signingRing.getSignId();
// sign-only
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
- new ProviderHelper(getContext()),
- new PassphraseCacheInterface() {
- @Override
- public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
- try {
- return PassphraseCacheService.getCachedPassphrase(
- OpenPgpService.this, masterKeyId);
- } catch (PassphraseCacheService.KeyNotFoundException e) {
- throw new PassphraseCacheInterface.NoSecretKeyException();
- }
- }
- },
+ new ProviderHelper(getContext()), passphraseCacheInterface,
inputData, os
);
builder.setEnableAsciiArmorOutput(asciiArmor)
@@ -376,18 +390,7 @@ public class OpenPgpService extends RemoteService {
InputData inputData = new InputData(is, inputLength);
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
- new ProviderHelper(getContext()),
- new PassphraseCacheInterface() {
- @Override
- public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
- try {
- return PassphraseCacheService.getCachedPassphrase(
- OpenPgpService.this, masterKeyId);
- } catch (PassphraseCacheService.KeyNotFoundException e) {
- throw new PassphraseCacheInterface.NoSecretKeyException();
- }
- }
- },
+ new ProviderHelper(getContext()), passphraseCacheInterface,
inputData, os
);
builder.setEnableAsciiArmorOutput(asciiArmor)
@@ -404,7 +407,7 @@ public class OpenPgpService extends RemoteService {
passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE);
} else {
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
- accSettings.getKeyId());
+ accSettings.getKeyId(), accSettings.getKeyId());
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
@@ -496,18 +499,7 @@ public class OpenPgpService extends RemoteService {
InputData inputData = new InputData(is, inputLength);
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- new ProviderHelper(this),
- new PassphraseCacheInterface() {
- @Override
- public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
- try {
- return PassphraseCacheService.getCachedPassphrase(
- OpenPgpService.this, masterKeyId);
- } catch (PassphraseCacheService.KeyNotFoundException e) {
- throw new PassphraseCacheInterface.NoSecretKeyException();
- }
- }
- },
+ new ProviderHelper(getContext()), passphraseCacheInterface,
inputData, os
);
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 94ea20328..6d23c36b9 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
@@ -29,6 +29,7 @@ import android.os.RemoteException;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
@@ -84,7 +85,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
* data from the activities or other apps, queues these intents, executes them, and stops itself
* after doing them.
*/
-public class KeychainIntentService extends IntentService implements Progressable {
+public class KeychainIntentService extends IntentService implements Progressable, PassphraseCacheInterface {
/* extras that can be given by intent */
public static final String EXTRA_MESSENGER = "messenger";
@@ -268,19 +269,7 @@ public class KeychainIntentService extends IntentService implements Progressable
/* Operation */
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(
- new ProviderHelper(this),
- new PassphraseCacheInterface() {
- @Override
- public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
- try {
- return PassphraseCacheService.getCachedPassphrase(
- KeychainIntentService.this, masterKeyId);
- } catch (PassphraseCacheService.KeyNotFoundException e) {
- throw new PassphraseCacheInterface.NoSecretKeyException();
- }
- }
- },
- inputData, outStream
+ new ProviderHelper(this), this, inputData, outStream
);
builder.setProgressable(this)
.setEnableAsciiArmorOutput(useAsciiArmor)
@@ -348,18 +337,7 @@ public class KeychainIntentService extends IntentService implements Progressable
// verifyText and decrypt returning additional resultData values for the
// verification of signatures
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- new ProviderHelper(this),
- new PassphraseCacheInterface() {
- @Override
- public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
- try {
- return PassphraseCacheService.getCachedPassphrase(
- KeychainIntentService.this, masterKeyId);
- } catch (PassphraseCacheService.KeyNotFoundException e) {
- throw new PassphraseCacheInterface.NoSecretKeyException();
- }
- }
- },
+ new ProviderHelper(this), this,
inputData, outStream
);
builder.setProgressable(this)
@@ -397,18 +375,7 @@ public class KeychainIntentService extends IntentService implements Progressable
// verification of signatures
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
new ProviderHelper(this),
- new PassphraseCacheInterface() {
- @Override
- public String getCachedPassphrase(long masterKeyId) throws PassphraseCacheInterface.NoSecretKeyException {
- try {
- return PassphraseCacheService.getCachedPassphrase(
- KeychainIntentService.this, masterKeyId);
- } catch (PassphraseCacheService.KeyNotFoundException e) {
- throw new PassphraseCacheInterface.NoSecretKeyException();
- }
- }
- },
- inputData, null
+ this, inputData, null
);
builder.setProgressable(this)
.setAllowSymmetricDecryption(true)
@@ -489,7 +456,7 @@ public class KeychainIntentService extends IntentService implements Progressable
// cache new passphrase
if (saveParcel.mNewPassphrase != null) {
- PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(),
+ PassphraseCacheService.addCachedPassphrase(this, ring.getMasterKeyId(), ring.getMasterKeyId(),
saveParcel.mNewPassphrase, ring.getPublicKey().getPrimaryUserIdWithFallback());
}
@@ -715,7 +682,7 @@ public class KeychainIntentService extends IntentService implements Progressable
/* Operation */
String signaturePassphrase = PassphraseCacheService.getCachedPassphrase(this,
- masterKeyId);
+ masterKeyId, masterKeyId);
if (signaturePassphrase == null) {
throw new PgpGeneralException("Unable to obtain passphrase");
}
@@ -899,6 +866,26 @@ public class KeychainIntentService extends IntentService implements Progressable
}
@Override
+ public String getCachedPassphrase(long subKeyId) throws NoSecretKeyException {
+ try {
+ long masterKeyId = new ProviderHelper(this).getMasterKeyId(subKeyId);
+ return getCachedPassphrase(masterKeyId, subKeyId);
+ } catch (NotFoundException e) {
+ throw new PassphraseCacheInterface.NoSecretKeyException();
+ }
+ }
+
+ @Override
+ public String getCachedPassphrase(long masterKeyId, long subKeyId) throws NoSecretKeyException {
+ try {
+ return PassphraseCacheService.getCachedPassphrase(
+ KeychainIntentService.this, masterKeyId, subKeyId);
+ } catch (PassphraseCacheService.KeyNotFoundException e) {
+ throw new PassphraseCacheInterface.NoSecretKeyException();
+ }
+ }
+
+ @Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_CANCEL.equals(intent.getAction())) {
mActionCanceled.set(true);
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 949ce0f0b..9638fb7bf 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -53,14 +53,26 @@ import java.util.Date;
* passphrase cache. Use the static methods addCachedPassphrase and getCachedPassphrase for
* convenience.
*
- * Design decisions:
- * - Cache passphrases based on master key ids, but try to unlock before using the subkey id
- * (to be compatible with stripped keys)
- * - Cache based on master key id so that there is not need to enter a passphrase twice for sign and
- * decrypt (if these are two different subkeys)
- * - Assume that all passphrases cached here are valid passphrases
- * - Do not handle if a keyring contains subkeys with different passphrases. This is not considered
- * supported and has not been seen in other OpenPGP implementations
+ * The passphrase cache service always works with both a master key id and a subkey id. The master
+ * key id is always used to retrieve relevant info from the database, while the subkey id is used
+ * to determine the type behavior (regular passphrase, empty passphrase, stripped key,
+ * divert-to-card) for the specific key requested.
+ *
+ * Caching behavior for subkeys depends on the cacheSubs preference:
+ *
+ * - If cacheSubs is NOT set, passphrases will be cached and retrieved by master key id. The
+ * checks for special subkeys will still be done, but otherwise it is assumed that all subkeys
+ * from the same master key will use the same passphrase. This can lead to bad passphrase
+ * errors if two subkeys are encrypted differently. This is the default behavior.
+ *
+ * - If cacheSubs IS set, passphrases will be cached per subkey id. This means that if a keyring
+ * has two subkeys for different purposes, passphrases will be cached independently and the
+ * user will be asked for a passphrase once per subkey even if it is the same one. This mode
+ * of operation is more precise, since we can assume that all passphrases returned from cache
+ * will be correct without fail. Since keyrings with differently encrypted subkeys are a very
+ * rare occurrence, and caching by keyring is what the user expects in the vast majority of
+ * cases, this is not the default behavior.
+ *
*/
public class PassphraseCacheService extends Service {
@@ -76,6 +88,7 @@ public class PassphraseCacheService extends Service {
public static final String EXTRA_TTL = "ttl";
public static final String EXTRA_KEY_ID = "key_id";
+ public static final String EXTRA_SUBKEY_ID = "subkey_id";
public static final String EXTRA_PASSPHRASE = "passphrase";
public static final String EXTRA_MESSENGER = "messenger";
public static final String EXTRA_USER_ID = "user_id";
@@ -107,21 +120,19 @@ public class PassphraseCacheService extends Service {
* This caches a new passphrase in memory by sending a new command to the service. An android
* service is only run once. Thus, when the service is already started, new commands just add
* new events to the alarm manager for new passphrases to let them timeout in the future.
- *
- * @param context
- * @param keyId
- * @param passphrase
*/
- public static void addCachedPassphrase(Context context, long keyId, String passphrase,
+ public static void addCachedPassphrase(Context context, long masterKeyId, long subKeyId,
+ String passphrase,
String primaryUserId) {
- Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + keyId);
+ Log.d(Constants.TAG, "PassphraseCacheService.cacheNewPassphrase() for " + masterKeyId);
Intent intent = new Intent(context, PassphraseCacheService.class);
intent.setAction(ACTION_PASSPHRASE_CACHE_ADD);
intent.putExtra(EXTRA_TTL, Preferences.getPreferences(context).getPassphraseCacheTtl());
intent.putExtra(EXTRA_PASSPHRASE, passphrase);
- intent.putExtra(EXTRA_KEY_ID, keyId);
+ intent.putExtra(EXTRA_KEY_ID, masterKeyId);
+ intent.putExtra(EXTRA_SUBKEY_ID, subKeyId);
intent.putExtra(EXTRA_USER_ID, primaryUserId);
context.startService(intent);
@@ -130,13 +141,11 @@ public class PassphraseCacheService extends Service {
/**
* Gets a cached passphrase from memory by sending an intent to the service. This method is
* designed to wait until the service returns the passphrase.
- *
- * @param context
- * @param keyId
+
* @return passphrase or null (if no passphrase is cached for this keyId)
*/
- public static String getCachedPassphrase(Context context, long keyId) throws KeyNotFoundException {
- Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphrase() get masterKeyId for " + keyId);
+ public static String getCachedPassphrase(Context context, long masterKeyId, long subKeyId) throws KeyNotFoundException {
+ Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphrase() get masterKeyId for " + masterKeyId);
Intent intent = new Intent(context, PassphraseCacheService.class);
intent.setAction(ACTION_PASSPHRASE_CACHE_GET);
@@ -162,16 +171,20 @@ public class PassphraseCacheService extends Service {
// Create a new Messenger for the communication back
Messenger messenger = new Messenger(returnHandler);
- intent.putExtra(EXTRA_KEY_ID, keyId);
+ intent.putExtra(EXTRA_KEY_ID, masterKeyId);
+ intent.putExtra(EXTRA_SUBKEY_ID, subKeyId);
intent.putExtra(EXTRA_MESSENGER, messenger);
// send intent to this service
context.startService(intent);
- // Wait on mutex until passphrase is returned to handlerThread
+ // Wait on mutex until passphrase is returned to handlerThread. Note that this local
+ // variable is used in the handler closure above, so it does make sense here!
+ // noinspection SynchronizationOnLocalVariableOrMethodParameter
synchronized (mutex) {
try {
mutex.wait(3000);
} catch (InterruptedException e) {
+ // don't care
}
}
@@ -187,34 +200,31 @@ public class PassphraseCacheService extends Service {
/**
* Internal implementation to get cached passphrase.
- *
- * @param subKeyId
- * @return
*/
- private String getCachedPassphraseImpl(long subKeyId) throws ProviderHelper.NotFoundException {
+ private String getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException {
// passphrase for symmetric encryption?
- if (subKeyId == Constants.key.symmetric) {
+ if (masterKeyId == Constants.key.symmetric) {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption");
String cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase();
if (cachedPassphrase == null) {
return null;
}
- addCachedPassphrase(this, Constants.key.symmetric, cachedPassphrase, getString(R.string.passp_cache_notif_pwd));
+ addCachedPassphrase(this, Constants.key.symmetric, Constants.key.symmetric,
+ cachedPassphrase, getString(R.string.passp_cache_notif_pwd));
return cachedPassphrase;
}
// on "none" key, just do nothing
- if(subKeyId == Constants.key.none) {
+ if(masterKeyId == Constants.key.none) {
return null;
}
// try to get master key id which is used as an identifier for cached passphrases
- Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId " + subKeyId);
- // find a master key id for our key
- long masterKeyId = new ProviderHelper(this).getMasterKeyId(subKeyId);
- CachedPublicKeyRing keyRing = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId);
+ Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId "
+ + masterKeyId + ", subKeyId " + subKeyId);
// get the type of key (from the database)
+ CachedPublicKeyRing keyRing = new ProviderHelper(this).getCachedPublicKeyRing(masterKeyId);
SecretKeyType keyType = keyRing.getSecretKeyType(subKeyId);
switch (keyType) {
@@ -237,14 +247,33 @@ public class PassphraseCacheService extends Service {
// get cached passphrase
CachedPassphrase cachedPassphrase = mPassphraseCache.get(subKeyId);
if (cachedPassphrase == null) {
- Log.d(Constants.TAG, "PassphraseCacheService: Passphrase not (yet) cached, returning null");
- // not really an error, just means the passphrase is not cached but not empty either
- return null;
+
+ // If we cache strictly by subkey, exit early
+ if (Preferences.getPreferences(mContext).getPassphraseCacheSubs()) {
+ Log.d(Constants.TAG, "PassphraseCacheService: specific subkey passphrase not (yet) cached, returning null");
+ // not really an error, just means the passphrase is not cached but not empty either
+ return null;
+ }
+
+ if (subKeyId == masterKeyId) {
+ Log.d(Constants.TAG, "PassphraseCacheService: masterkey passphrase not (yet) cached, returning null");
+ // not really an error, just means the passphrase is not cached but not empty either
+ return null;
+ }
+
+ cachedPassphrase = mPassphraseCache.get(masterKeyId);
+ // If we cache strictly by subkey, exit early
+ if (cachedPassphrase == null) {
+ Log.d(Constants.TAG, "PassphraseCacheService: keyring passphrase not (yet) cached, returning null");
+ // not really an error, just means the passphrase is not cached but not empty either
+ return null;
+ }
+
}
// set it again to reset the cache life cycle
Log.d(Constants.TAG, "PassphraseCacheService: Cache passphrase again when getting it!");
- addCachedPassphrase(this, subKeyId, cachedPassphrase.getPassphrase(), cachedPassphrase.getPrimaryUserID());
+ addCachedPassphrase(this, masterKeyId, subKeyId, cachedPassphrase.getPassphrase(), cachedPassphrase.getPrimaryUserID());
return cachedPassphrase.getPassphrase();
}
@@ -277,10 +306,6 @@ public class PassphraseCacheService extends Service {
/**
* Build pending intent that is executed by alarm manager to time out a specific passphrase
- *
- * @param context
- * @param keyId
- * @return
*/
private static PendingIntent buildIntent(Context context, long keyId) {
Intent intent = new Intent(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE);
@@ -302,38 +327,57 @@ public class PassphraseCacheService extends Service {
if (intent != null && intent.getAction() != null) {
if (ACTION_PASSPHRASE_CACHE_ADD.equals(intent.getAction())) {
long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL);
- long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
+ long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
+ long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1);
String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE);
String primaryUserID = intent.getStringExtra(EXTRA_USER_ID);
Log.d(Constants.TAG,
- "PassphraseCacheService: Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with keyId: "
- + keyId + ", ttl: " + ttl + ", usrId: " + primaryUserID
+ "PassphraseCacheService: Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with masterkeyId: "
+ + masterKeyId + ", subKeyId: " + subKeyId + ", ttl: " + ttl + ", usrId: " + primaryUserID
);
- // add keyId, passphrase and primary user id to memory
- mPassphraseCache.put(keyId, new CachedPassphrase(passphrase, primaryUserID));
-
- if (ttl > 0) {
- // register new alarm with keyId for this passphrase
- long triggerTime = new Date().getTime() + (ttl * 1000);
- AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
- am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, keyId));
+ // if we don't cache by specific subkey id, or the requested subkey is the master key,
+ // just add master key id to the cache
+ if (subKeyId == masterKeyId || !Preferences.getPreferences(mContext).getPassphraseCacheSubs()) {
+ mPassphraseCache.put(masterKeyId, new CachedPassphrase(passphrase, primaryUserID));
+ if (ttl > 0) {
+ // register new alarm with keyId for this passphrase
+ long triggerTime = new Date().getTime() + (ttl * 1000);
+ AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
+ am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, masterKeyId));
+ }
+ } else {
+ // otherwise, add this specific subkey to the cache
+ mPassphraseCache.put(subKeyId, new CachedPassphrase(passphrase, primaryUserID));
+ if (ttl > 0) {
+ // register new alarm with keyId for this passphrase
+ long triggerTime = new Date().getTime() + (ttl * 1000);
+ AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
+ am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, subKeyId));
+ }
}
updateService();
} else if (ACTION_PASSPHRASE_CACHE_GET.equals(intent.getAction())) {
- long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
+ long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, Constants.key.symmetric);
+ long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, Constants.key.symmetric);
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
Message msg = Message.obtain();
try {
- String passphrase = getCachedPassphraseImpl(keyId);
- msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY;
- Bundle bundle = new Bundle();
- bundle.putString(EXTRA_PASSPHRASE, passphrase);
- msg.setData(bundle);
+ // If only one of these is symmetric, error out!
+ if (masterKeyId == Constants.key.symmetric ^ subKeyId == Constants.key.symmetric) {
+ Log.e(Constants.TAG, "PassphraseCacheService: Bad request, missing masterKeyId or subKeyId!");
+ msg.what = MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND;
+ } else {
+ String passphrase = getCachedPassphraseImpl(masterKeyId, subKeyId);
+ msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY;
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_PASSPHRASE, passphrase);
+ 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_NOT_FOUND;
@@ -365,9 +409,6 @@ public class PassphraseCacheService extends Service {
/**
* Called when one specific passphrase for keyId timed out
- *
- * @param context
- * @param keyId
*/
private void timeout(Context context, long keyId) {
// remove passphrase corresponding to keyId from memory
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
index 70276bba3..438b56eb0 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java
@@ -271,7 +271,7 @@ public class CertifyKeyFragment extends LoaderFragment
// get the user's passphrase for this key (if required)
String passphrase;
try {
- passphrase = PassphraseCacheService.getCachedPassphrase(mActivity, mMasterKeyId);
+ passphrase = PassphraseCacheService.getCachedPassphrase(mActivity, mMasterKeyId, mMasterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
Log.e(Constants.TAG, "Key not found!", e);
mActivity.finish();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
index e7d5d3869..a7db73a0a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java
@@ -236,7 +236,7 @@ public class EditKeyFragment extends LoaderFragment implements
try {
mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(getActivity(),
- mSaveKeyringParcel.mMasterKeyId);
+ mSaveKeyringParcel.mMasterKeyId, mSaveKeyringParcel.mMasterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
finishWithError(LogType.MSG_EK_ERROR_NOT_FOUND);
return;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
index ddd5fb5f0..decd1757f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java
@@ -64,7 +64,6 @@ public class PassphraseDialogActivity extends FragmentActivity {
// special extra for OpenPgpService
public static final String EXTRA_DATA = "data";
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -251,8 +250,9 @@ public class PassphraseDialogActivity extends FragmentActivity {
// Early breakout if we are dealing with a symmetric key
if (mSecretRing == null) {
- PassphraseCacheService.addCachedPassphrase(getActivity(), Constants.key.symmetric,
- passphrase, getString(R.string.passp_cache_notif_pwd));
+ PassphraseCacheService.addCachedPassphrase(getActivity(),
+ Constants.key.symmetric, Constants.key.symmetric, passphrase,
+ getString(R.string.passp_cache_notif_pwd));
finishCaching(passphrase);
return;
@@ -309,8 +309,9 @@ public class PassphraseDialogActivity extends FragmentActivity {
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
try {
- PassphraseCacheService.addCachedPassphrase(getActivity(), mSubKeyId,
- passphrase, mSecretRing.getPrimaryUserIdWithFallback());
+ PassphraseCacheService.addCachedPassphrase(getActivity(),
+ mSecretRing.getMasterKeyId(), mSubKeyId, passphrase,
+ mSecretRing.getPrimaryUserIdWithFallback());
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "adding of a passphrase failed", e);
}
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 fb419dd60..3ffc182fb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesActivity.java
@@ -82,7 +82,7 @@ public class PreferencesActivity extends PreferenceActivity {
} else if (action != null && action.equals(ACTION_PREFS_ADV)) {
addPreferencesFromResource(R.xml.adv_preferences);
- initializePassPassphraceCacheTtl(
+ initializePassphraseCacheTtl(
(IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
initializeEncryptionAlgorithm(
@@ -228,7 +228,10 @@ public class PreferencesActivity extends PreferenceActivity {
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.adv_preferences);
- initializePassPassphraceCacheTtl(
+ initializePassphraseCacheSubs(
+ (CheckBoxPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_SUBS));
+
+ initializePassphraseCacheTtl(
(IntegerListPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_TTL));
initializeEncryptionAlgorithm(
@@ -281,7 +284,18 @@ public class PreferencesActivity extends PreferenceActivity {
|| super.isValidFragment(fragmentName);
}
- private static void initializePassPassphraceCacheTtl(final IntegerListPreference mPassphraseCacheTtl) {
+ private static void initializePassphraseCacheSubs(final CheckBoxPreference mPassphraseCacheSubs) {
+ mPassphraseCacheSubs.setChecked(sPreferences.getPassphraseCacheSubs());
+ mPassphraseCacheSubs.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ mPassphraseCacheSubs.setChecked((Boolean) newValue);
+ sPreferences.setPassphraseCacheSubs((Boolean) newValue);
+ return false;
+ }
+ });
+ }
+
+ private static void initializePassphraseCacheTtl(final IntegerListPreference mPassphraseCacheTtl) {
mPassphraseCacheTtl.setValue("" + sPreferences.getPassphraseCacheTtl());
mPassphraseCacheTtl.setSummary(mPassphraseCacheTtl.getEntry());
mPassphraseCacheTtl
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
index 6739e8374..a89d4be1c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/PassphraseDialogFragment.java
@@ -242,7 +242,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
// Early breakout if we are dealing with a symmetric key
if (mSecretRing == null) {
PassphraseCacheService.addCachedPassphrase(getActivity(), Constants.key.symmetric,
- passphrase, getString(R.string.passp_cache_notif_pwd));
+ Constants.key.symmetric, passphrase, getString(R.string.passp_cache_notif_pwd));
// also return passphrase back to activity
Bundle data = new Bundle();
data.putString(MESSAGE_DATA_PASSPHRASE, passphrase);
@@ -301,8 +301,9 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
Log.d(Constants.TAG, "Everything okay! Caching entered passphrase");
try {
- PassphraseCacheService.addCachedPassphrase(getActivity(), mSubKeyId,
- passphrase, mSecretRing.getPrimaryUserIdWithFallback());
+ PassphraseCacheService.addCachedPassphrase(getActivity(),
+ mSecretRing.getMasterKeyId(), mSubKeyId, passphrase,
+ mSecretRing.getPrimaryUserIdWithFallback());
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "adding of a passphrase failed", e);
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
index 35570ef6e..d4cd9b385 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java
@@ -93,6 +93,16 @@ public class Preferences {
editor.commit();
}
+ public boolean getPassphraseCacheSubs() {
+ return mSharedPreferences.getBoolean(Pref.PASSPHRASE_CACHE_SUBS, false);
+ }
+
+ public void setPassphraseCacheSubs(boolean value) {
+ SharedPreferences.Editor editor = mSharedPreferences.edit();
+ editor.putBoolean(Pref.PASSPHRASE_CACHE_SUBS, value);
+ editor.commit();
+ }
+
public int getDefaultEncryptionAlgorithm() {
return mSharedPreferences.getInt(Constants.Pref.DEFAULT_ENCRYPTION_ALGORITHM,
PGPEncryptedData.AES_256);
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index ea55a8932..01b461316 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -111,6 +111,7 @@
<string name="label_hash_algorithm">"Hash algorithm"</string>
<string name="label_symmetric">"Encrypt with passphrase"</string>
<string name="label_passphrase_cache_ttl">"Passphrase cache"</string>
+ <string name="label_passphrase_cache_subs">"Cache passphrases by subkey"</string>
<string name="label_message_compression">"Message compression"</string>
<string name="label_file_compression">"File compression"</string>
<string name="label_keyservers">"Keyservers"</string>
diff --git a/OpenKeychain/src/main/res/xml/adv_preferences.xml b/OpenKeychain/src/main/res/xml/adv_preferences.xml
index 250f21e2a..92e0f2ffc 100644
--- a/OpenKeychain/src/main/res/xml/adv_preferences.xml
+++ b/OpenKeychain/src/main/res/xml/adv_preferences.xml
@@ -6,6 +6,10 @@
android:key="passphraseCacheTtl"
android:persistent="false"
android:title="@string/label_passphrase_cache_ttl" />
+ <CheckBoxPreference
+ android:key="passphraseCacheSubs"
+ android:persistent="false"
+ android:title="@string/label_passphrase_cache_subs" />
<org.sufficientlysecure.keychain.ui.widget.IntegerListPreference
android:key="defaultEncryptionAlgorithm"
android:persistent="false"