Better error handling for passphrase cache if key is missing

This commit is contained in:
Dominik Schürmann 2014-08-13 16:37:28 +02:00
parent e4c8674792
commit 38da2af0e8
7 changed files with 115 additions and 71 deletions

View File

@ -46,6 +46,7 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
@ -163,7 +164,8 @@ public class PgpDecryptVerify {
} }
public interface PassphraseCache { public interface PassphraseCache {
public String getCachedPassphrase(long masterKeyId); public String getCachedPassphrase(long masterKeyId)
throws NoSecretKeyException;
} }
public static class InvalidDataException extends Exception { public static class InvalidDataException extends Exception {

View File

@ -168,8 +168,7 @@ public class OpenPgpService extends RemoteService {
} }
if (passphrase == null) { if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client // get PendingIntent for passphrase input, add it to given params and return to client
Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId()); return getPassphraseBundleIntent(data, accSettings.getKeyId());
return passphraseBundle;
} }
// Get Input- and OutputStream from ParcelFileDescriptor // Get Input- and OutputStream from ParcelFileDescriptor
@ -289,8 +288,7 @@ public class OpenPgpService extends RemoteService {
} }
if (passphrase == null) { if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client // get PendingIntent for passphrase input, add it to given params and return to client
Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId()); return getPassphraseBundleIntent(data, accSettings.getKeyId());
return passphraseBundle;
} }
// sign and encrypt // sign and encrypt
@ -358,9 +356,13 @@ public class OpenPgpService extends RemoteService {
new ProviderHelper(this), new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() { new PgpDecryptVerify.PassphraseCache() {
@Override @Override
public String getCachedPassphrase(long masterKeyId) { public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException {
try {
return PassphraseCacheService.getCachedPassphrase( return PassphraseCacheService.getCachedPassphrase(
OpenPgpService.this, masterKeyId); OpenPgpService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
throw new PgpDecryptVerify.NoSecretKeyException();
}
} }
}, },
inputData, os inputData, os

View File

@ -307,9 +307,13 @@ public class KeychainIntentService extends IntentService
new ProviderHelper(this), new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() { new PgpDecryptVerify.PassphraseCache() {
@Override @Override
public String getCachedPassphrase(long masterKeyId) { public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException {
try {
return PassphraseCacheService.getCachedPassphrase( return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId); KeychainIntentService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
throw new PgpDecryptVerify.NoSecretKeyException();
}
} }
}, },
inputData, outStream inputData, outStream
@ -351,9 +355,13 @@ public class KeychainIntentService extends IntentService
new ProviderHelper(this), new ProviderHelper(this),
new PgpDecryptVerify.PassphraseCache() { new PgpDecryptVerify.PassphraseCache() {
@Override @Override
public String getCachedPassphrase(long masterKeyId) { public String getCachedPassphrase(long masterKeyId) throws PgpDecryptVerify.NoSecretKeyException {
try {
return PassphraseCacheService.getCachedPassphrase( return PassphraseCacheService.getCachedPassphrase(
KeychainIntentService.this, masterKeyId); KeychainIntentService.this, masterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
throw new PgpDecryptVerify.NoSecretKeyException();
}
} }
}, },
inputData, null inputData, null

View File

@ -76,12 +76,24 @@ public class PassphraseCacheService extends Service {
private static final int NOTIFICATION_ID = 1; 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 BroadcastReceiver mIntentReceiver; private BroadcastReceiver mIntentReceiver;
private LongSparseArray<CachedPassphrase> mPassphraseCache = new LongSparseArray<CachedPassphrase>(); private LongSparseArray<CachedPassphrase> mPassphraseCache = new LongSparseArray<CachedPassphrase>();
Context mContext; Context mContext;
public static class KeyNotFoundException extends Exception {
public KeyNotFoundException() {
}
public KeyNotFoundException(String name) {
super(name);
}
}
/** /**
* This caches a new passphrase in memory by sending a new command to the service. An android * 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 * service is only run once. Thus, when the service is already started, new commands just add
@ -114,24 +126,23 @@ public class PassphraseCacheService extends Service {
* @param keyId * @param keyId
* @return passphrase or null (if no passphrase is cached for this keyId) * @return passphrase or null (if no passphrase is cached for this keyId)
*/ */
public static String getCachedPassphrase(Context context, long keyId) { public static String getCachedPassphrase(Context context, long keyId) throws KeyNotFoundException {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphrase() get masterKeyId for " + keyId); Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphrase() get masterKeyId for " + keyId);
Intent intent = new Intent(context, PassphraseCacheService.class); Intent intent = new Intent(context, PassphraseCacheService.class);
intent.setAction(ACTION_PASSPHRASE_CACHE_GET); intent.setAction(ACTION_PASSPHRASE_CACHE_GET);
final Object mutex = new Object(); final Object mutex = new Object();
final Bundle returnBundle = new Bundle(); final Message returnMessage = Message.obtain();
HandlerThread handlerThread = new HandlerThread("getPassphraseThread"); HandlerThread handlerThread = new HandlerThread("getPassphraseThread");
handlerThread.start(); handlerThread.start();
Handler returnHandler = new Handler(handlerThread.getLooper()) { Handler returnHandler = new Handler(handlerThread.getLooper()) {
@Override @Override
public void handleMessage(Message message) { public void handleMessage(Message message) {
if (message.obj != null) { // copy over result to handle after mutex.wait
String passphrase = ((Bundle) message.obj).getString(EXTRA_PASSPHRASE); returnMessage.what = message.what;
returnBundle.putString(EXTRA_PASSPHRASE, passphrase); returnMessage.copyFrom(message);
}
synchronized (mutex) { synchronized (mutex) {
mutex.notify(); mutex.notify();
} }
@ -155,10 +166,13 @@ public class PassphraseCacheService extends Service {
} }
} }
if (returnBundle.containsKey(EXTRA_PASSPHRASE)) { switch (returnMessage.what) {
return returnBundle.getString(EXTRA_PASSPHRASE); case MSG_PASSPHRASE_CACHE_GET_OKAY:
} else { return returnMessage.getData().getString(EXTRA_PASSPHRASE);
return null; case MSG_PASSPHRASE_CACHE_GET_KEY_NO_FOUND:
throw new KeyNotFoundException();
default:
throw new KeyNotFoundException("should not happen!");
} }
} }
@ -168,7 +182,7 @@ public class PassphraseCacheService extends Service {
* @param keyId * @param keyId
* @return * @return
*/ */
private String getCachedPassphraseImpl(long keyId) { private String getCachedPassphraseImpl(long keyId) throws ProviderHelper.NotFoundException {
// passphrase for symmetric encryption? // passphrase for symmetric encryption?
if (keyId == Constants.key.symmetric) { if (keyId == Constants.key.symmetric) {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption"); Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption");
@ -181,7 +195,6 @@ public class PassphraseCacheService extends Service {
} }
// try to get master key id which is used as an identifier for cached passphrases // try to get master key id which is used as an identifier for cached passphrases
try {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId " + keyId); Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for masterKeyId " + keyId);
CanonicalizedSecretKeyRing key = new ProviderHelper(this).getCanonicalizedSecretKeyRing( CanonicalizedSecretKeyRing key = new ProviderHelper(this).getCanonicalizedSecretKeyRing(
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId)); KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId));
@ -209,11 +222,6 @@ public class PassphraseCacheService extends Service {
Log.d(Constants.TAG, "PassphraseCacheService: Cache passphrase again when getting it!"); Log.d(Constants.TAG, "PassphraseCacheService: Cache passphrase again when getting it!");
addCachedPassphrase(this, keyId, cachedPassphrase.getPassphrase(), cachedPassphrase.getPrimaryUserID()); addCachedPassphrase(this, keyId, cachedPassphrase.getPassphrase(), cachedPassphrase.getPrimaryUserID());
return cachedPassphrase.getPassphrase(); return cachedPassphrase.getPassphrase();
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "PassphraseCacheService: Passphrase for unknown key was requested!");
return null;
}
} }
/** /**
@ -295,12 +303,19 @@ public class PassphraseCacheService extends Service {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1); long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER); Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
String passphrase = getCachedPassphraseImpl(keyId);
Message msg = Message.obtain(); Message msg = Message.obtain();
try {
String passphrase = getCachedPassphraseImpl(keyId);
msg.what = MSG_PASSPHRASE_CACHE_GET_OKAY;
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putString(EXTRA_PASSPHRASE, passphrase); bundle.putString(EXTRA_PASSPHRASE, passphrase);
msg.obj = bundle; 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;
}
try { try {
messenger.send(msg); messenger.send(msg);
} catch (RemoteException e) { } catch (RemoteException e) {

View File

@ -231,7 +231,14 @@ public class CertifyKeyActivity extends ActionBarActivity implements
*/ */
private void initiateCertifying() { private void initiateCertifying() {
// get the user's passphrase for this key (if required) // get the user's passphrase for this key (if required)
String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId); String passphrase = null;
try {
passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
Log.e(Constants.TAG, "Key not found!", e);
finish();
return;
}
if (passphrase == null) { if (passphrase == null) {
PassphraseDialogFragment.show(this, mMasterKeyId, PassphraseDialogFragment.show(this, mMasterKeyId,
new Handler() { new Handler() {

View File

@ -475,8 +475,14 @@ public class EditKeyFragment extends LoaderFragment implements
} }
private void cachePassphraseForEdit() { private void cachePassphraseForEdit() {
try {
mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(getActivity(), mCurrentPassphrase = PassphraseCacheService.getCachedPassphrase(getActivity(),
mSaveKeyringParcel.mMasterKeyId); mSaveKeyringParcel.mMasterKeyId);
} catch (PassphraseCacheService.KeyNotFoundException e) {
Log.e(Constants.TAG, "Key not found!", e);
getActivity().finish();
return;
}
if (mCurrentPassphrase == null) { if (mCurrentPassphrase == null) {
PassphraseDialogFragment.show(getActivity(), mSaveKeyringParcel.mMasterKeyId, PassphraseDialogFragment.show(getActivity(), mSaveKeyringParcel.mMasterKeyId,
new Handler() { new Handler() {

View File

@ -450,6 +450,7 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
return false; return false;
} }
try {
if (mSigningKeyId != 0 && PassphraseCacheService.getCachedPassphrase(this, mSigningKeyId) == null) { if (mSigningKeyId != 0 && PassphraseCacheService.getCachedPassphrase(this, mSigningKeyId) == null) {
PassphraseDialogFragment.show(this, mSigningKeyId, PassphraseDialogFragment.show(this, mSigningKeyId,
new Handler() { new Handler() {
@ -465,6 +466,9 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn
return false; return false;
} }
} catch (PassphraseCacheService.KeyNotFoundException e) {
Log.e(Constants.TAG, "Key not found!", e);
}
} }
return true; return true;
} }