aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote
diff options
context:
space:
mode:
authorDominik Schürmann <dominik@dominikschuermann.de>2015-04-13 23:29:35 +0200
committerDominik Schürmann <dominik@dominikschuermann.de>2015-04-13 23:29:35 +0200
commit256d644d03f989132e9db01e15f75aaee2f76157 (patch)
treeb7c1f562c1c9e6feffb71d21bb49e14a42f8107e /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote
parent9fc001c9b98e54f37eb8254a215f9d4c1e1c95ca (diff)
downloadopen-keychain-256d644d03f989132e9db01e15f75aaee2f76157.tar.gz
open-keychain-256d644d03f989132e9db01e15f75aaee2f76157.tar.bz2
open-keychain-256d644d03f989132e9db01e15f75aaee2f76157.zip
IMplement CryptoInputParcelCacheService
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java246
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java89
2 files changed, 285 insertions, 50 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java
new file mode 100644
index 000000000..e3e39417a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.remote;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+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 org.openintents.openpgp.util.OpenPgpApi;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+public class CryptoInputParcelCacheService extends Service {
+
+ public static final String ACTION_ADD = Constants.INTENT_PREFIX + "ADD";
+ public static final String ACTION_GET = Constants.INTENT_PREFIX + "GET";
+
+ public static final String EXTRA_CRYPTO_INPUT_PARCEL = "crypto_input_parcel";
+ public static final String EXTRA_UUID1 = "uuid1";
+ public static final String EXTRA_UUID2 = "uuid2";
+ public static final String EXTRA_MESSENGER = "messenger";
+
+ private static final int MSG_GET_OKAY = 1;
+ private static final int MSG_GET_NOT_FOUND = 2;
+
+ Context mContext;
+
+ private static final UUID NULL_UUID = new UUID(0, 0);
+
+ private ConcurrentHashMap<UUID, CryptoInputParcel> mCache = new ConcurrentHashMap<>();
+
+ public static class InputParcelNotFound extends Exception {
+ public InputParcelNotFound() {
+ }
+
+ public InputParcelNotFound(String name) {
+ super(name);
+ }
+ }
+
+ public static void addCryptoInputParcel(Context context, Intent data, CryptoInputParcel inputParcel) {
+ UUID mTicket = addCryptoInputParcel(context, inputParcel);
+ // And write out the UUID most and least significant bits.
+ data.putExtra(OpenPgpApi.EXTRA_CALL_UUID1, mTicket.getMostSignificantBits());
+ data.putExtra(OpenPgpApi.EXTRA_CALL_UUID2, mTicket.getLeastSignificantBits());
+ }
+
+ public static CryptoInputParcel getCryptoInputParcel(Context context, Intent data) {
+ if (!data.getExtras().containsKey(OpenPgpApi.EXTRA_CALL_UUID1)
+ || !data.getExtras().containsKey(OpenPgpApi.EXTRA_CALL_UUID2)) {
+ return null;
+ }
+ long mostSig = data.getLongExtra(OpenPgpApi.EXTRA_CALL_UUID1, 0);
+ long leastSig = data.getLongExtra(OpenPgpApi.EXTRA_CALL_UUID2, 0);
+ UUID uuid = new UUID(mostSig, leastSig);
+ try {
+ return getCryptoInputParcel(context, uuid);
+ } catch (InputParcelNotFound inputParcelNotFound) {
+ return null;
+ }
+ }
+
+ private static UUID addCryptoInputParcel(Context context, CryptoInputParcel inputParcel) {
+ UUID uuid = UUID.randomUUID();
+
+ Intent intent = new Intent(context, CryptoInputParcelCacheService.class);
+ intent.setAction(ACTION_ADD);
+ intent.putExtra(EXTRA_CRYPTO_INPUT_PARCEL, inputParcel);
+ intent.putExtra(EXTRA_UUID1, uuid.getMostSignificantBits());
+ intent.putExtra(EXTRA_UUID2, uuid.getLeastSignificantBits());
+ context.startService(intent);
+ return uuid;
+ }
+
+ private static CryptoInputParcel getCryptoInputParcel(Context context, UUID uuid) throws InputParcelNotFound {
+ Intent intent = new Intent(context, CryptoInputParcelCacheService.class);
+ intent.setAction(ACTION_GET);
+
+ final Object mutex = new Object();
+ final Message returnMessage = Message.obtain();
+
+ HandlerThread handlerThread = new HandlerThread("getParcelableThread");
+ handlerThread.start();
+ Handler returnHandler = new Handler(handlerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message message) {
+ // copy over result to handle after mutex.wait
+ returnMessage.what = message.what;
+ returnMessage.copyFrom(message);
+ synchronized (mutex) {
+ mutex.notify();
+ }
+ // quit handlerThread
+ getLooper().quit();
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+ intent.putExtra(EXTRA_UUID1, uuid.getMostSignificantBits());
+ intent.putExtra(EXTRA_UUID2, uuid.getLeastSignificantBits());
+ intent.putExtra(EXTRA_MESSENGER, messenger);
+ // send intent to this service
+ context.startService(intent);
+
+ // Wait on mutex until parcelable 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
+ }
+ }
+
+ switch (returnMessage.what) {
+ case MSG_GET_OKAY:
+ Bundle returnData = returnMessage.getData();
+ returnData.setClassLoader(context.getClassLoader());
+ return returnData.getParcelable(EXTRA_CRYPTO_INPUT_PARCEL);
+ case MSG_GET_NOT_FOUND:
+ throw new InputParcelNotFound();
+ default:
+ Log.e(Constants.TAG, "timeout!");
+ throw new InputParcelNotFound("should not happen!");
+ }
+ }
+
+ /**
+ * Executed when service is started by intent
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+
+ if (intent == null || intent.getAction() == null) {
+ return START_NOT_STICKY;
+ }
+
+ String action = intent.getAction();
+ switch (action) {
+ case ACTION_ADD: {
+ long uuid1 = intent.getLongExtra(EXTRA_UUID1, 0);
+ long uuid2 = intent.getLongExtra(EXTRA_UUID2, 0);
+ UUID uuid = new UUID(uuid1, uuid2);
+ CryptoInputParcel inputParcel = intent.getParcelableExtra(EXTRA_CRYPTO_INPUT_PARCEL);
+ mCache.put(uuid, inputParcel);
+
+ break;
+ }
+ case ACTION_GET: {
+ long uuid1 = intent.getLongExtra(EXTRA_UUID1, 0);
+ long uuid2 = intent.getLongExtra(EXTRA_UUID2, 0);
+ UUID uuid = new UUID(uuid1, uuid2);
+ Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
+
+ Message msg = Message.obtain();
+ // UUID.equals isn't well documented; we use compareTo instead.
+ if (NULL_UUID.compareTo(uuid) == 0) {
+ msg.what = MSG_GET_NOT_FOUND;
+ } else {
+ CryptoInputParcel inputParcel = mCache.get(uuid);
+ mCache.remove(uuid);
+ msg.what = MSG_GET_OKAY;
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EXTRA_CRYPTO_INPUT_PARCEL, inputParcel);
+ msg.setData(bundle);
+ }
+
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "CryptoInputParcelCacheService: Sending message failed", e);
+ }
+ break;
+ }
+ default: {
+ Log.e(Constants.TAG, "CryptoInputParcelCacheService: Intent or Intent Action not supported!");
+ break;
+ }
+ }
+
+ if (mCache.size() <= 0) {
+ // stop whole service if cache is empty
+ Log.d(Constants.TAG, "CryptoInputParcelCacheService: No passphrases remaining in memory, stopping service!");
+ stopSelf();
+ }
+
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mContext = this;
+ Log.d(Constants.TAG, "CryptoInputParcelCacheService, onCreate()");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.d(Constants.TAG, "CryptoInputParcelCacheService, onDestroy()");
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ public class CryptoInputParcelCacheServiceBinder extends Binder {
+ public CryptoInputParcelCacheService getService() {
+ return CryptoInputParcelCacheService.this;
+ }
+ }
+
+ private final IBinder mBinder = new CryptoInputParcelCacheServiceBinder();
+
+} \ No newline at end of file
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 39f7f815c..71843cd7f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -36,7 +36,6 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
-import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
import org.sufficientlysecure.keychain.pgp.PgpConstants;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;
@@ -57,7 +56,6 @@ import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.ParcelableCache;
import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.IOException;
@@ -68,25 +66,6 @@ import java.util.Set;
public class OpenPgpService extends RemoteService {
- /**
- * Instead of parceling the CryptoInputParcel, they are cached on our side to prevent
- * leakage of passphrases, symmetric keys, an yubikey related pass-through values
- */
- private static ParcelableCache<CryptoInputParcel> inputParcelCache;
- static {
- inputParcelCache = new ParcelableCache<>();
- }
-
- public static void cacheCryptoInputParcel(Intent data, CryptoInputParcel inputParcel) {
- inputParcelCache.cacheAndWriteToIntent(inputParcel, data,
- OpenPgpApi.EXTRA_CALL_UUID1, OpenPgpApi.EXTRA_CALL_UUID2);
- }
-
- public static CryptoInputParcel getCryptoInputParcel(Intent data) {
- return inputParcelCache.readFromIntentAndGetFromCache(data,
- OpenPgpApi.EXTRA_CALL_UUID1, OpenPgpApi.EXTRA_CALL_UUID2);
- }
-
static final String[] EMAIL_SEARCH_PROJECTION = new String[]{
KeyRings._ID,
KeyRings.MASTER_KEY_ID,
@@ -283,7 +262,7 @@ public class OpenPgpService extends RemoteService {
long inputLength = is.available();
InputData inputData = new InputData(is, inputLength);
- CryptoInputParcel inputParcel = getCryptoInputParcel(data);
+ CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) {
inputParcel = new CryptoInputParcel();
}
@@ -424,7 +403,7 @@ public class OpenPgpService extends RemoteService {
.setAdditionalEncryptId(signKeyId); // add sign key for encryption
}
- CryptoInputParcel inputParcel = getCryptoInputParcel(data);
+ CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) {
inputParcel = new CryptoInputParcel();
}
@@ -513,7 +492,7 @@ public class OpenPgpService extends RemoteService {
this, new ProviderHelper(getContext()), null, inputData, os
);
- CryptoInputParcel inputParcel = getCryptoInputParcel(data);
+ CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
if (inputParcel == null) {
inputParcel = new CryptoInputParcel();
}
@@ -768,9 +747,7 @@ public class OpenPgpService extends RemoteService {
return null;
}
- // TODO: multi-threading
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
-
@Override
public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) {
try {
@@ -780,30 +757,42 @@ public class OpenPgpService extends RemoteService {
}
String action = data.getAction();
- if (OpenPgpApi.ACTION_CLEARTEXT_SIGN.equals(action)) {
- return signImpl(data, input, output, true);
- } else if (OpenPgpApi.ACTION_SIGN.equals(action)) {
- // DEPRECATED: same as ACTION_CLEARTEXT_SIGN
- Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!");
- return signImpl(data, input, output, true);
- } else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(action)) {
- return signImpl(data, input, output, false);
- } else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, false);
- } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, true);
- } else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
- return decryptAndVerifyImpl(data, input, output, false);
- } else if (OpenPgpApi.ACTION_DECRYPT_METADATA.equals(action)) {
- return decryptAndVerifyImpl(data, input, output, true);
- } else if (OpenPgpApi.ACTION_GET_SIGN_KEY_ID.equals(action)) {
- return getSignKeyIdImpl(data);
- } else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
- return getKeyIdsImpl(data);
- } else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
- return getKeyImpl(data);
- } else {
- return null;
+ switch (action) {
+ case OpenPgpApi.ACTION_CLEARTEXT_SIGN: {
+ return signImpl(data, input, output, true);
+ }
+ case OpenPgpApi.ACTION_SIGN: {
+ // DEPRECATED: same as ACTION_CLEARTEXT_SIGN
+ Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!");
+ return signImpl(data, input, output, true);
+ }
+ case OpenPgpApi.ACTION_DETACHED_SIGN: {
+ return signImpl(data, input, output, false);
+ }
+ case OpenPgpApi.ACTION_ENCRYPT: {
+ return encryptAndSignImpl(data, input, output, false);
+ }
+ case OpenPgpApi.ACTION_SIGN_AND_ENCRYPT: {
+ return encryptAndSignImpl(data, input, output, true);
+ }
+ case OpenPgpApi.ACTION_DECRYPT_VERIFY: {
+ return decryptAndVerifyImpl(data, input, output, false);
+ }
+ case OpenPgpApi.ACTION_DECRYPT_METADATA: {
+ return decryptAndVerifyImpl(data, input, output, true);
+ }
+ case OpenPgpApi.ACTION_GET_SIGN_KEY_ID: {
+ return getSignKeyIdImpl(data);
+ }
+ case OpenPgpApi.ACTION_GET_KEY_IDS: {
+ return getKeyIdsImpl(data);
+ }
+ case OpenPgpApi.ACTION_GET_KEY: {
+ return getKeyImpl(data);
+ }
+ default: {
+ return null;
+ }
}
} finally {
// always close input and output file descriptors even in error cases