Process safe implementation of PassphraseCacheService, First test for crypto provider
This commit is contained in:
@@ -56,7 +56,6 @@ import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
|
||||
|
||||
/**
|
||||
* This Service contains all important long lasting operations for APG. It receives Intents with
|
||||
* data from the activities or other apps, queues these intents, executes them, and stops itself
|
||||
@@ -541,11 +540,12 @@ public class KeychainIntentService extends IntentService implements ProgressDial
|
||||
|
||||
/* Operation */
|
||||
if (!canSign) {
|
||||
PgpMain.changeSecretKeyPassphrase(this, ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId),
|
||||
oldPassPhrase, newPassPhrase, this);
|
||||
PgpMain.changeSecretKeyPassphrase(this,
|
||||
ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId),
|
||||
oldPassPhrase, newPassPhrase, this);
|
||||
} else {
|
||||
PgpMain.buildSecretKey(this, userIds, keys, keysUsages, masterKeyId, oldPassPhrase,
|
||||
newPassPhrase, this);
|
||||
PgpMain.buildSecretKey(this, userIds, keys, keysUsages, masterKeyId,
|
||||
oldPassPhrase, newPassPhrase, this);
|
||||
}
|
||||
PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,27 +39,39 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
public class PassphraseCacheService extends Service {
|
||||
public static final String TAG = Constants.TAG + ": PassphraseCacheService";
|
||||
|
||||
public static final String ACTION_PASSPHRASE_CACHE_ADD = Constants.INTENT_PREFIX
|
||||
+ "PASSPHRASE_CACHE_ADD";
|
||||
public static final String ACTION_PASSPHRASE_CACHE_GET = Constants.INTENT_PREFIX
|
||||
+ "PASSPHRASE_CACHE_GET";
|
||||
|
||||
public static final String BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE = Constants.INTENT_PREFIX
|
||||
+ "PASSPHRASE_CACHE_SERVICE";
|
||||
+ "PASSPHRASE_CACHE_BROADCAST";
|
||||
|
||||
public static final String EXTRA_TTL = "ttl";
|
||||
public static final String EXTRA_KEY_ID = "keyId";
|
||||
public static final String EXTRA_PASSPHRASE = "passphrase";
|
||||
public static final String EXTRA_MESSENGER = "messenger";
|
||||
|
||||
private static final int REQUEST_ID = 0;
|
||||
private static final long DEFAULT_TTL = 15;
|
||||
|
||||
private BroadcastReceiver mIntentReceiver;
|
||||
|
||||
// This is static to be easily retrieved by getCachedPassphrase() without the need of callback
|
||||
// functions
|
||||
private static HashMap<Long, String> mPassphraseCache = new HashMap<Long, String>();
|
||||
private HashMap<Long, String> mPassphraseCache = new HashMap<Long, String>();
|
||||
|
||||
Context mContext;
|
||||
|
||||
/**
|
||||
* This caches a new passphrase by sending a new command to the service. An android service is
|
||||
@@ -74,6 +86,7 @@ public class PassphraseCacheService extends Service {
|
||||
Log.d(TAG, "cacheNewPassphrase() for " + keyId);
|
||||
|
||||
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);
|
||||
@@ -82,17 +95,65 @@ public class PassphraseCacheService extends Service {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a cached passphrase from memory
|
||||
* Gets a cached passphrase from memory, blocking method
|
||||
*
|
||||
* @param context
|
||||
* @param keyId
|
||||
* @return
|
||||
*/
|
||||
public static String getCachedPassphrase(Context context, long keyId) {
|
||||
Log.d(TAG, "getCachedPassphrase() get masterKeyId for " + keyId);
|
||||
Intent intent = new Intent(context, PassphraseCacheService.class);
|
||||
intent.setAction(ACTION_PASSPHRASE_CACHE_GET);
|
||||
|
||||
final Object mutex = new Object();
|
||||
final Bundle returnBundle = new Bundle();
|
||||
|
||||
HandlerThread handlerThread = new HandlerThread("getPassphrase");
|
||||
handlerThread.start();
|
||||
Handler returnHandler = new Handler(handlerThread.getLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
if (message.obj != null) {
|
||||
String passphrase = ((Bundle) message.obj).getString(EXTRA_PASSPHRASE);
|
||||
returnBundle.putString(EXTRA_PASSPHRASE, passphrase);
|
||||
}
|
||||
synchronized (mutex) {
|
||||
mutex.notify();
|
||||
}
|
||||
getLooper().quit();
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(returnHandler);
|
||||
intent.putExtra(EXTRA_KEY_ID, keyId);
|
||||
intent.putExtra(EXTRA_MESSENGER, messenger);
|
||||
// send intent to this service
|
||||
context.startService(intent);
|
||||
|
||||
// Wait on mutex until passphrase is returned to handlerThread
|
||||
synchronized (mutex) {
|
||||
try {
|
||||
mutex.wait(3000);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (returnBundle.containsKey(EXTRA_PASSPHRASE)) {
|
||||
return returnBundle.getString(EXTRA_PASSPHRASE);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getCachedPassphraseImpl(long keyId) {
|
||||
Log.d(TAG, "getCachedPassphraseImpl() get masterKeyId for " + keyId);
|
||||
|
||||
// try to get master key id which is used as an identifier for cached passphrases
|
||||
long masterKeyId = keyId;
|
||||
if (masterKeyId != Id.key.symmetric) {
|
||||
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, keyId);
|
||||
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(this, keyId);
|
||||
if (keyRing == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -102,22 +163,24 @@ public class PassphraseCacheService extends Service {
|
||||
}
|
||||
masterKeyId = masterKey.getKeyID();
|
||||
}
|
||||
Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId" + masterKeyId);
|
||||
|
||||
// get cached passphrase
|
||||
String cachedPassphrase = mPassphraseCache.get(masterKeyId);
|
||||
if (cachedPassphrase == null) {
|
||||
// TODO: fix!
|
||||
// check if secret key has a passphrase
|
||||
if (!hasPassphrase(context, masterKeyId)) {
|
||||
// cache empty passphrase
|
||||
addCachedPassphrase(context, masterKeyId, "");
|
||||
return "";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
// if (!hasPassphrase(context, masterKeyId)) {
|
||||
// // cache empty passphrase
|
||||
// addCachedPassphrase(context, masterKeyId, "");
|
||||
// return "";
|
||||
// } else {
|
||||
return null;
|
||||
// }
|
||||
}
|
||||
// set it again to reset the cache life cycle
|
||||
Log.d(TAG, "Cache passphrase again when getting it!");
|
||||
addCachedPassphrase(context, masterKeyId, cachedPassphrase);
|
||||
addCachedPassphrase(this, masterKeyId, cachedPassphrase);
|
||||
|
||||
return cachedPassphrase;
|
||||
}
|
||||
@@ -196,11 +259,6 @@ public class PassphraseCacheService extends Service {
|
||||
return sender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Log.d(TAG, "onCreate()");
|
||||
}
|
||||
|
||||
/**
|
||||
* Executed when service is started by intent
|
||||
*/
|
||||
@@ -211,20 +269,41 @@ public class PassphraseCacheService extends Service {
|
||||
// register broadcastreceiver
|
||||
registerReceiver();
|
||||
|
||||
if (intent != null) {
|
||||
long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL);
|
||||
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
|
||||
String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE);
|
||||
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);
|
||||
String passphrase = intent.getStringExtra(EXTRA_PASSPHRASE);
|
||||
|
||||
Log.d(TAG, "Received intent in onStartCommand() with keyId: " + keyId + ", ttl: " + ttl);
|
||||
Log.d(TAG,
|
||||
"Received ACTION_PASSPHRASE_CACHE_ADD intent in onStartCommand() with keyId: "
|
||||
+ keyId + ", ttl: " + ttl);
|
||||
|
||||
// add keyId and passphrase to memory
|
||||
mPassphraseCache.put(keyId, passphrase);
|
||||
// add keyId and passphrase to memory
|
||||
mPassphraseCache.put(keyId, passphrase);
|
||||
|
||||
// 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));
|
||||
// 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));
|
||||
} else if (ACTION_PASSPHRASE_CACHE_GET.equals(intent.getAction())) {
|
||||
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
|
||||
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
|
||||
|
||||
String passphrase = getCachedPassphraseImpl(keyId);
|
||||
|
||||
Message msg = Message.obtain();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(EXTRA_PASSPHRASE, passphrase);
|
||||
msg.obj = bundle;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(Constants.TAG, "Sending message failed", e);
|
||||
}
|
||||
} else {
|
||||
Log.e(Constants.TAG, "Intent or Intent Action not supported!");
|
||||
}
|
||||
}
|
||||
|
||||
return START_STICKY;
|
||||
@@ -240,7 +319,7 @@ public class PassphraseCacheService extends Service {
|
||||
// remove passphrase corresponding to keyId from memory
|
||||
mPassphraseCache.remove(keyId);
|
||||
|
||||
Log.d(TAG, "Timeout of " + keyId + ", removed from memory!");
|
||||
Log.d(TAG, "Timeout of keyId " + keyId + ", removed from memory!");
|
||||
|
||||
// stop whole service if no cached passphrases remaining
|
||||
if (mPassphraseCache.isEmpty()) {
|
||||
@@ -249,13 +328,26 @@ public class PassphraseCacheService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
mContext = this;
|
||||
Log.d(Constants.TAG, "PassphraseCacheService, onCreate()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
Log.d(TAG, "onDestroy()");
|
||||
super.onDestroy();
|
||||
Log.d(Constants.TAG, "PassphraseCacheService, onDestroy()");
|
||||
|
||||
unregisterReceiver(mIntentReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
public class PassphraseCacheBinder extends Binder {
|
||||
public PassphraseCacheService getService() {
|
||||
return PassphraseCacheService.this;
|
||||
@@ -264,9 +356,4 @@ public class PassphraseCacheService extends Service {
|
||||
|
||||
private final IBinder mBinder = new PassphraseCacheBinder();
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user