passphrasecache: cache by master key, introduce preference for cache by subkey

This commit is contained in:
Vincent Breitmoser 2014-09-23 15:56:48 +02:00
parent 675e8e2015
commit 070017b12f
13 changed files with 208 additions and 154 deletions

View File

@ -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";

View File

@ -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;
}

View File

@ -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
);

View File

@ -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");
}
@ -898,6 +865,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())) {

View File

@ -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

View File

@ -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();

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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>

View File

@ -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"