mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-14 21:05:09 -05:00
IMplement CryptoInputParcelCacheService
This commit is contained in:
parent
9fc001c9b9
commit
256d644d03
@ -696,6 +696,10 @@
|
|||||||
android:name=".service.PassphraseCacheService"
|
android:name=".service.PassphraseCacheService"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:process=":passphrase_cache" />
|
android:process=":passphrase_cache" />
|
||||||
|
<service
|
||||||
|
android:name=".remote.CryptoInputParcelCacheService"
|
||||||
|
android:exported="false"
|
||||||
|
android:process=":remote_api" />
|
||||||
<service
|
<service
|
||||||
android:name=".service.KeychainIntentService"
|
android:name=".service.KeychainIntentService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
}
|
@ -36,7 +36,6 @@ import org.sufficientlysecure.keychain.Constants;
|
|||||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
|
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
|
||||||
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
|
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.PgpConstants;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;
|
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.ui.ViewKeyActivity;
|
||||||
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.ParcelableCache;
|
|
||||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -68,25 +66,6 @@ import java.util.Set;
|
|||||||
|
|
||||||
public class OpenPgpService extends RemoteService {
|
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[]{
|
static final String[] EMAIL_SEARCH_PROJECTION = new String[]{
|
||||||
KeyRings._ID,
|
KeyRings._ID,
|
||||||
KeyRings.MASTER_KEY_ID,
|
KeyRings.MASTER_KEY_ID,
|
||||||
@ -283,7 +262,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
long inputLength = is.available();
|
long inputLength = is.available();
|
||||||
InputData inputData = new InputData(is, inputLength);
|
InputData inputData = new InputData(is, inputLength);
|
||||||
|
|
||||||
CryptoInputParcel inputParcel = getCryptoInputParcel(data);
|
CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
|
||||||
if (inputParcel == null) {
|
if (inputParcel == null) {
|
||||||
inputParcel = new CryptoInputParcel();
|
inputParcel = new CryptoInputParcel();
|
||||||
}
|
}
|
||||||
@ -424,7 +403,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
.setAdditionalEncryptId(signKeyId); // add sign key for encryption
|
.setAdditionalEncryptId(signKeyId); // add sign key for encryption
|
||||||
}
|
}
|
||||||
|
|
||||||
CryptoInputParcel inputParcel = getCryptoInputParcel(data);
|
CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
|
||||||
if (inputParcel == null) {
|
if (inputParcel == null) {
|
||||||
inputParcel = new CryptoInputParcel();
|
inputParcel = new CryptoInputParcel();
|
||||||
}
|
}
|
||||||
@ -513,7 +492,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
this, new ProviderHelper(getContext()), null, inputData, os
|
this, new ProviderHelper(getContext()), null, inputData, os
|
||||||
);
|
);
|
||||||
|
|
||||||
CryptoInputParcel inputParcel = getCryptoInputParcel(data);
|
CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
|
||||||
if (inputParcel == null) {
|
if (inputParcel == null) {
|
||||||
inputParcel = new CryptoInputParcel();
|
inputParcel = new CryptoInputParcel();
|
||||||
}
|
}
|
||||||
@ -768,9 +747,7 @@ public class OpenPgpService extends RemoteService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: multi-threading
|
|
||||||
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
|
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) {
|
public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) {
|
||||||
try {
|
try {
|
||||||
@ -780,30 +757,42 @@ public class OpenPgpService extends RemoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String action = data.getAction();
|
String action = data.getAction();
|
||||||
if (OpenPgpApi.ACTION_CLEARTEXT_SIGN.equals(action)) {
|
switch (action) {
|
||||||
return signImpl(data, input, output, true);
|
case OpenPgpApi.ACTION_CLEARTEXT_SIGN: {
|
||||||
} else if (OpenPgpApi.ACTION_SIGN.equals(action)) {
|
return signImpl(data, input, output, true);
|
||||||
// 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!");
|
case OpenPgpApi.ACTION_SIGN: {
|
||||||
return signImpl(data, input, output, true);
|
// DEPRECATED: same as ACTION_CLEARTEXT_SIGN
|
||||||
} else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(action)) {
|
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, false);
|
return signImpl(data, input, output, true);
|
||||||
} else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
|
}
|
||||||
return encryptAndSignImpl(data, input, output, false);
|
case OpenPgpApi.ACTION_DETACHED_SIGN: {
|
||||||
} else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
|
return signImpl(data, input, output, false);
|
||||||
return encryptAndSignImpl(data, input, output, true);
|
}
|
||||||
} else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
|
case OpenPgpApi.ACTION_ENCRYPT: {
|
||||||
return decryptAndVerifyImpl(data, input, output, false);
|
return encryptAndSignImpl(data, input, output, false);
|
||||||
} else if (OpenPgpApi.ACTION_DECRYPT_METADATA.equals(action)) {
|
}
|
||||||
return decryptAndVerifyImpl(data, input, output, true);
|
case OpenPgpApi.ACTION_SIGN_AND_ENCRYPT: {
|
||||||
} else if (OpenPgpApi.ACTION_GET_SIGN_KEY_ID.equals(action)) {
|
return encryptAndSignImpl(data, input, output, true);
|
||||||
return getSignKeyIdImpl(data);
|
}
|
||||||
} else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
|
case OpenPgpApi.ACTION_DECRYPT_VERIFY: {
|
||||||
return getKeyIdsImpl(data);
|
return decryptAndVerifyImpl(data, input, output, false);
|
||||||
} else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
|
}
|
||||||
return getKeyImpl(data);
|
case OpenPgpApi.ACTION_DECRYPT_METADATA: {
|
||||||
} else {
|
return decryptAndVerifyImpl(data, input, output, true);
|
||||||
return null;
|
}
|
||||||
|
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 {
|
} finally {
|
||||||
// always close input and output file descriptors even in error cases
|
// always close input and output file descriptors even in error cases
|
||||||
|
@ -337,11 +337,17 @@ public class PassphraseCacheService extends Service {
|
|||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
Log.d(Constants.TAG, "PassphraseCacheService.onStartCommand()");
|
Log.d(Constants.TAG, "PassphraseCacheService.onStartCommand()");
|
||||||
|
|
||||||
|
if (intent == null || intent.getAction() == null) {
|
||||||
|
updateService();
|
||||||
|
return START_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
// register broadcastreceiver
|
// register broadcastreceiver
|
||||||
registerReceiver();
|
registerReceiver();
|
||||||
|
|
||||||
if (intent != null && intent.getAction() != null) {
|
String action = intent.getAction();
|
||||||
if (ACTION_PASSPHRASE_CACHE_ADD.equals(intent.getAction())) {
|
switch (action) {
|
||||||
|
case ACTION_PASSPHRASE_CACHE_ADD: {
|
||||||
long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL);
|
long ttl = intent.getLongExtra(EXTRA_TTL, DEFAULT_TTL);
|
||||||
long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
|
long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
|
||||||
long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1);
|
long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, -1);
|
||||||
@ -365,9 +371,9 @@ public class PassphraseCacheService extends Service {
|
|||||||
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
|
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
|
||||||
am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, referenceKeyId));
|
am.set(AlarmManager.RTC_WAKEUP, triggerTime, buildIntent(this, referenceKeyId));
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
updateService();
|
}
|
||||||
} else if (ACTION_PASSPHRASE_CACHE_GET.equals(intent.getAction())) {
|
case ACTION_PASSPHRASE_CACHE_GET: {
|
||||||
long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, Constants.key.symmetric);
|
long masterKeyId = intent.getLongExtra(EXTRA_KEY_ID, Constants.key.symmetric);
|
||||||
long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, Constants.key.symmetric);
|
long subKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, Constants.key.symmetric);
|
||||||
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
|
Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
|
||||||
@ -395,7 +401,9 @@ public class PassphraseCacheService extends Service {
|
|||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.e(Constants.TAG, "PassphraseCacheService: Sending message failed", e);
|
Log.e(Constants.TAG, "PassphraseCacheService: Sending message failed", e);
|
||||||
}
|
}
|
||||||
} else if (ACTION_PASSPHRASE_CACHE_CLEAR.equals(intent.getAction())) {
|
break;
|
||||||
|
}
|
||||||
|
case ACTION_PASSPHRASE_CACHE_CLEAR: {
|
||||||
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
|
AlarmManager am = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
|
||||||
|
|
||||||
if (intent.hasExtra(EXTRA_SUBKEY_ID) && intent.hasExtra(EXTRA_KEY_ID)) {
|
if (intent.hasExtra(EXTRA_SUBKEY_ID) && intent.hasExtra(EXTRA_KEY_ID)) {
|
||||||
@ -419,13 +427,16 @@ public class PassphraseCacheService extends Service {
|
|||||||
mPassphraseCache.clear();
|
mPassphraseCache.clear();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
updateService();
|
}
|
||||||
} else {
|
default: {
|
||||||
Log.e(Constants.TAG, "PassphraseCacheService: Intent or Intent Action not supported!");
|
Log.e(Constants.TAG, "PassphraseCacheService: Intent or Intent Action not supported!");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateService();
|
||||||
|
|
||||||
return START_STICKY;
|
return START_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,13 +10,11 @@ import android.annotation.TargetApi;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpApi;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.remote.OpenPgpService;
|
import org.sufficientlysecure.keychain.remote.CryptoInputParcelCacheService;
|
||||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||||
@ -93,7 +91,7 @@ public class NfcOperationActivity extends BaseNfcActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mServiceIntent != null) {
|
if (mServiceIntent != null) {
|
||||||
OpenPgpService.cacheCryptoInputParcel(mServiceIntent, inputParcel);
|
CryptoInputParcelCacheService.addCryptoInputParcel(this, mServiceIntent, inputParcel);
|
||||||
setResult(RESULT_OK, mServiceIntent);
|
setResult(RESULT_OK, mServiceIntent);
|
||||||
} else {
|
} else {
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
|
@ -41,7 +41,6 @@ import android.widget.EditText;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.openintents.openpgp.util.OpenPgpApi;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
||||||
@ -53,11 +52,10 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
|||||||
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
import org.sufficientlysecure.keychain.remote.OpenPgpService;
|
import org.sufficientlysecure.keychain.remote.CryptoInputParcelCacheService;
|
||||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
|
|
||||||
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
|
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||||
@ -428,7 +426,7 @@ public class PassphraseDialogActivity extends FragmentActivity {
|
|||||||
|
|
||||||
CryptoInputParcel inputParcel = new CryptoInputParcel(null, passphrase);
|
CryptoInputParcel inputParcel = new CryptoInputParcel(null, passphrase);
|
||||||
if (mServiceIntent != null) {
|
if (mServiceIntent != null) {
|
||||||
OpenPgpService.cacheCryptoInputParcel(mServiceIntent, inputParcel);
|
CryptoInputParcelCacheService.addCryptoInputParcel(getActivity(), mServiceIntent, inputParcel);
|
||||||
getActivity().setResult(RESULT_OK, mServiceIntent);
|
getActivity().setResult(RESULT_OK, mServiceIntent);
|
||||||
} else {
|
} else {
|
||||||
// also return passphrase back to activity
|
// also return passphrase back to activity
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
|
|
||||||
package org.sufficientlysecure.keychain.util;
|
package org.sufficientlysecure.keychain.util;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -30,7 +28,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
* To overcome this issue this class allows to cache Parcelables, mapped by unique UUIDs,
|
* To overcome this issue this class allows to cache Parcelables, mapped by unique UUIDs,
|
||||||
* which are written to the parcel instead of the whole Parcelable.
|
* which are written to the parcel instead of the whole Parcelable.
|
||||||
*/
|
*/
|
||||||
public class ParcelableCache<E extends Object> {
|
public class ParcelableCache<E> {
|
||||||
|
|
||||||
private static final UUID NULL_UUID = new UUID(0, 0);
|
private static final UUID NULL_UUID = new UUID(0, 0);
|
||||||
|
|
||||||
@ -39,7 +37,7 @@ public class ParcelableCache<E extends Object> {
|
|||||||
* This is used such that when we become parceled, we are
|
* This is used such that when we become parceled, we are
|
||||||
* well below the 1 MB boundary that is specified.
|
* well below the 1 MB boundary that is specified.
|
||||||
*/
|
*/
|
||||||
private ConcurrentHashMap<UUID, E> dehydratedLogs = new ConcurrentHashMap<>();
|
private ConcurrentHashMap<UUID, E> objectCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dehydrate a Parcelable (such that it is available after deparcelization)
|
* Dehydrate a Parcelable (such that it is available after deparcelization)
|
||||||
@ -52,9 +50,9 @@ public class ParcelableCache<E extends Object> {
|
|||||||
if (parcelable == null) {
|
if (parcelable == null) {
|
||||||
return NULL_UUID;
|
return NULL_UUID;
|
||||||
} else {
|
} else {
|
||||||
UUID ticket = UUID.randomUUID();
|
UUID uuid = UUID.randomUUID();
|
||||||
dehydratedLogs.put(ticket, parcelable);
|
objectCache.put(uuid, parcelable);
|
||||||
return ticket;
|
return uuid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,53 +61,34 @@ public class ParcelableCache<E extends Object> {
|
|||||||
* invalidating its place in the dehydration pool.
|
* invalidating its place in the dehydration pool.
|
||||||
* This is used such that when parcelized, the Parcelable is no larger than 1 MB.
|
* This is used such that when parcelized, the Parcelable is no larger than 1 MB.
|
||||||
*
|
*
|
||||||
* @param ticket A UUID ticket that identifies the log in question.
|
* @param uuid A UUID ticket that identifies the log in question.
|
||||||
* @return An OperationLog.
|
* @return An OperationLog.
|
||||||
*/
|
*/
|
||||||
private E rehydrateParcelable(UUID ticket) {
|
private E rehydrateParcelable(UUID uuid) {
|
||||||
// UUID.equals isn't well documented; we use compareTo instead.
|
// UUID.equals isn't well documented; we use compareTo instead.
|
||||||
if (NULL_UUID.compareTo(ticket) == 0) {
|
if (NULL_UUID.compareTo(uuid) == 0) {
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
E parcelable = dehydratedLogs.get(ticket);
|
E parcelable = objectCache.get(uuid);
|
||||||
dehydratedLogs.remove(ticket);
|
objectCache.remove(uuid);
|
||||||
return parcelable;
|
return parcelable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public E readFromIntentAndGetFromCache(Intent data, String key1, String key2) {
|
|
||||||
if (!data.getExtras().containsKey(key1) || !data.getExtras().containsKey(key2)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
long mostSig = data.getLongExtra(key1, 0);
|
|
||||||
long leastSig = data.getLongExtra(key2, 0);
|
|
||||||
UUID mTicket = new UUID(mostSig, leastSig);
|
|
||||||
// fetch the dehydrated log out of storage (this removes it from the dehydration pool)
|
|
||||||
return rehydrateParcelable(mTicket);
|
|
||||||
}
|
|
||||||
|
|
||||||
public E readFromParcelAndGetFromCache(Parcel source) {
|
public E readFromParcelAndGetFromCache(Parcel source) {
|
||||||
long mostSig = source.readLong();
|
long mostSig = source.readLong();
|
||||||
long leastSig = source.readLong();
|
long leastSig = source.readLong();
|
||||||
UUID mTicket = new UUID(mostSig, leastSig);
|
UUID mTicket = new UUID(mostSig, leastSig);
|
||||||
// fetch the dehydrated log out of storage (this removes it from the dehydration pool)
|
// fetch the dehydrated parcelable out of storage (this removes it from the dehydration pool)
|
||||||
return rehydrateParcelable(mTicket);
|
return rehydrateParcelable(mTicket);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cacheAndWriteToParcel(E parcelable, Parcel dest) {
|
public void cacheAndWriteToParcel(E parcelable, Parcel dest) {
|
||||||
// Get a ticket for our log.
|
// Get a ticket for our parcelable.
|
||||||
UUID mTicket = dehydrateParcelable(parcelable);
|
UUID mTicket = dehydrateParcelable(parcelable);
|
||||||
// And write out the UUID most and least significant bits.
|
// And write out the UUID most and least significant bits.
|
||||||
dest.writeLong(mTicket.getMostSignificantBits());
|
dest.writeLong(mTicket.getMostSignificantBits());
|
||||||
dest.writeLong(mTicket.getLeastSignificantBits());
|
dest.writeLong(mTicket.getLeastSignificantBits());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cacheAndWriteToIntent(E parcelable, Intent data, String key1, String key2) {
|
|
||||||
// Get a ticket for our log.
|
|
||||||
UUID mTicket = dehydrateParcelable(parcelable);
|
|
||||||
// And write out the UUID most and least significant bits.
|
|
||||||
data.putExtra(key1, mTicket.getMostSignificantBits());
|
|
||||||
data.putExtra(key2, mTicket.getLeastSignificantBits());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user