aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java10
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CloudImportService.java384
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java58
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java63
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java111
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java74
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java62
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java752
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java221
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java516
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java159
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java66
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java67
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java129
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java83
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java131
19 files changed, 1645 insertions, 1311 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
index a7571a7ac..3cdbca633 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java
@@ -22,14 +22,13 @@ import android.os.Parcel;
import android.os.Parcelable;
import java.io.Serializable;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import java.util.Map;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
/**
@@ -44,6 +43,8 @@ public class CertifyActionsParcel implements Parcelable {
public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
+ public String keyServerUri;
+
public CertifyActionsParcel(long masterKeyId) {
mMasterKeyId = masterKeyId;
mLevel = CertifyLevel.DEFAULT;
@@ -53,6 +54,7 @@ public class CertifyActionsParcel implements Parcelable {
mMasterKeyId = source.readLong();
// just like parcelables, this is meant for ad-hoc IPC only and is NOT portable!
mLevel = CertifyLevel.values()[source.readInt()];
+ keyServerUri = source.readString();
mCertifyActions = (ArrayList<CertifyAction>) source.readSerializable();
}
@@ -65,6 +67,7 @@ public class CertifyActionsParcel implements Parcelable {
public void writeToParcel(Parcel destination, int flags) {
destination.writeLong(mMasterKeyId);
destination.writeInt(mLevel.ordinal());
+ destination.writeString(keyServerUri);
destination.writeSerializable(mCertifyActions);
}
@@ -86,8 +89,7 @@ public class CertifyActionsParcel implements Parcelable {
final public ArrayList<String> mUserIds;
final public ArrayList<WrappedUserAttribute> mUserAttributes;
- public CertifyAction(long masterKeyId, List<String> userIds,
- List<WrappedUserAttribute> attributes) {
+ public CertifyAction(long masterKeyId, List<String> userIds, List<WrappedUserAttribute> attributes) {
mMasterKeyId = masterKeyId;
mUserIds = userIds == null ? null : new ArrayList<>(userIds);
mUserAttributes = attributes == null ? null : new ArrayList<>(attributes);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CloudImportService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CloudImportService.java
deleted file mode 100644
index 249586f6d..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CloudImportService.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright (C) 2012-2013 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.service;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.ImportExportOperation;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * When this service is started it will initiate a multi-threaded key import and when done it will
- * shut itself down.
- */
-public class CloudImportService extends Service implements Progressable {
-
- // required as extras from intent
- public static final String EXTRA_MESSENGER = "messenger";
- public static final String EXTRA_DATA = "data";
-
- // required by data bundle
- public static final String IMPORT_KEY_LIST = "import_key_list";
- public static final String IMPORT_KEY_SERVER = "import_key_server";
-
- // indicates a request to cancel the import
- public static final String ACTION_CANCEL = Constants.INTENT_PREFIX + "CANCEL";
-
- // tells the spawned threads whether the user has requested a cancel
- private static AtomicBoolean mActionCancelled = new AtomicBoolean(false);
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- /**
- * Used to accumulate the results of individual key imports
- */
- private class KeyImportAccumulator {
- private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
- private int mTotalKeys;
- private int mImportedKeys = 0;
- private Progressable mImportProgressable;
- ArrayList<Long> mImportedMasterKeyIds = new ArrayList<Long>();
- private int mBadKeys = 0;
- private int mNewKeys = 0;
- private int mUpdatedKeys = 0;
- private int mSecret = 0;
- private int mResultType = 0;
-
- public KeyImportAccumulator(int totalKeys) {
- mTotalKeys = totalKeys;
- // ignore updates from ImportExportOperation for now
- mImportProgressable = new Progressable() {
- @Override
- public void setProgress(String message, int current, int total) {
-
- }
-
- @Override
- public void setProgress(int resourceId, int current, int total) {
-
- }
-
- @Override
- public void setProgress(int current, int total) {
-
- }
-
- @Override
- public void setPreventCancel() {
-
- }
- };
- }
-
- public Progressable getImportProgressable() {
- return mImportProgressable;
- }
-
- public int getTotalKeys() {
- return mTotalKeys;
- }
-
- public int getImportedKeys() {
- return mImportedKeys;
- }
-
- public synchronized void accumulateKeyImport(ImportKeyResult result) {
- mImportedKeys++;
- mImportLog.addAll(result.getLog().toList());//accumulates log
- mBadKeys += result.mBadKeys;
- mNewKeys += result.mNewKeys;
- mUpdatedKeys += result.mUpdatedKeys;
- mSecret += result.mSecret;
-
- long[] masterKeyIds = result.getImportedMasterKeyIds();
- for (long masterKeyId : masterKeyIds) {
- mImportedMasterKeyIds.add(masterKeyId);
- }
-
- // if any key import has been cancelled, set result type to cancelled
- // resultType is added to in getConsolidatedKayImport to account for remaining factors
- mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
- }
-
- /**
- * returns accumulated result of all imports so far
- */
- public ImportKeyResult getConsolidatedImportKeyResult() {
-
- // adding required information to mResultType
- // special case,no keys requested for import
- if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) {
- mResultType = ImportKeyResult.RESULT_FAIL_NOTHING;
- } else {
- if (mNewKeys > 0) {
- mResultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
- }
- if (mUpdatedKeys > 0) {
- mResultType |= ImportKeyResult.RESULT_OK_UPDATED;
- }
- if (mBadKeys > 0) {
- mResultType |= ImportKeyResult.RESULT_WITH_ERRORS;
- if (mNewKeys == 0 && mUpdatedKeys == 0) {
- mResultType |= ImportKeyResult.RESULT_ERROR;
- }
- }
- if (mImportLog.containsWarnings()) {
- mResultType |= ImportKeyResult.RESULT_WARNINGS;
- }
- }
-
- long masterKeyIds[] = new long[mImportedMasterKeyIds.size()];
- for (int i = 0; i < masterKeyIds.length; i++) {
- masterKeyIds[i] = mImportedMasterKeyIds.get(i);
- }
-
- return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys,
- mSecret, masterKeyIds);
- }
-
- public boolean isImportFinished() {
- return mTotalKeys == mImportedKeys;
- }
- }
-
- private KeyImportAccumulator mKeyImportAccumulator;
-
- Messenger mMessenger;
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
-
- if (ACTION_CANCEL.equals(intent.getAction())) {
- mActionCancelled.set(true);
- return Service.START_NOT_STICKY;
- }
-
- mActionCancelled.set(false);//we haven't been cancelled, yet
-
- Bundle extras = intent.getExtras();
-
- mMessenger = (Messenger) extras.get(EXTRA_MESSENGER);
-
- Bundle data = extras.getBundle(EXTRA_DATA);
-
- final String keyServer = data.getString(IMPORT_KEY_SERVER);
- // keyList being null (in case key list to be reaad from cache) is checked by importKeys
- final ArrayList<ParcelableKeyRing> keyList = data.getParcelableArrayList(IMPORT_KEY_LIST);
-
- // Adding keys to the ThreadPoolExecutor takes time, we don't want to block the main thread
- Thread baseImportThread = new Thread(new Runnable() {
-
- @Override
- public void run() {
- importKeys(keyList, keyServer);
- }
- });
- baseImportThread.start();
- return Service.START_NOT_STICKY;
- }
-
- public void importKeys(ArrayList<ParcelableKeyRing> keyList, final String keyServer) {
- ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<>(this, "key_import.pcl");
- int totKeys = 0;
- Iterator<ParcelableKeyRing> keyListIterator = null;
- // either keyList or cache must be null, no guarantees otherwise
- if (keyList == null) {//export from cache, copied from ImportExportOperation.importKeyRings
-
- try {
- ParcelableFileCache.IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
- keyListIterator = it;
- totKeys = it.getSize();
- } catch (IOException e) {
-
- // Special treatment here, we need a lot
- OperationResult.OperationLog log = new OperationResult.OperationLog();
- log.add(OperationResult.LogType.MSG_IMPORT, 0, 0);
- log.add(OperationResult.LogType.MSG_IMPORT_ERROR_IO, 0, 0);
-
- keyImportFailed(new ImportKeyResult(ImportKeyResult.RESULT_ERROR, log));
- }
- } else {
- keyListIterator = keyList.iterator();
- totKeys = keyList.size();
- }
-
-
- if (keyListIterator != null) {
- mKeyImportAccumulator = new KeyImportAccumulator(totKeys);
- setProgress(0, totKeys);
-
- final int maxThreads = 200;
- ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads,
- 30L, TimeUnit.SECONDS,
- new SynchronousQueue<Runnable>());
-
- while (keyListIterator.hasNext()) {
-
- final ParcelableKeyRing pkRing = keyListIterator.next();
-
- Runnable importOperationRunnable = new Runnable() {
-
- @Override
- public void run() {
- ImportKeyResult result = null;
- try {
- ImportExportOperation importExportOperation = new ImportExportOperation(
- CloudImportService.this,
- new ProviderHelper(CloudImportService.this),
- mKeyImportAccumulator.getImportProgressable(),
- mActionCancelled);
-
- ArrayList<ParcelableKeyRing> list = new ArrayList<>();
- list.add(pkRing);
- result = importExportOperation.importKeyRings(list,
- keyServer);
- } finally {
- // in the off-chance that importKeyRings does something to crash the
- // thread before it can call singleKeyRingImportCompleted, our imported
- // key count will go wrong. This will cause the service to never die,
- // and the progress dialog to stay displayed. The finally block was
- // originally meant to ensure singleKeyRingImportCompleted was called,
- // and checks for null were to be introduced, but in such a scenario,
- // knowing an uncaught error exists in importKeyRings is more important.
-
- // if a null gets passed, something wrong is happening. We want a crash.
-
- singleKeyRingImportCompleted(result);
- }
- }
- };
-
- importExecutor.execute(importOperationRunnable);
- }
- }
- }
-
- private synchronized void singleKeyRingImportCompleted(ImportKeyResult result) {
- // increase imported key count and accumulate log and bad, new etc. key counts from result
- mKeyImportAccumulator.accumulateKeyImport(result);
-
- setProgress(mKeyImportAccumulator.getImportedKeys(), mKeyImportAccumulator.getTotalKeys());
-
- if (mKeyImportAccumulator.isImportFinished()) {
- ContactSyncAdapterService.requestSync();
-
- sendMessageToHandler(ServiceProgressHandler.MessageStatus.OKAY,
- mKeyImportAccumulator.getConsolidatedImportKeyResult());
-
- stopSelf();//we're done here
- }
- }
-
- private void keyImportFailed(ImportKeyResult result) {
- sendMessageToHandler(ServiceProgressHandler.MessageStatus.OKAY, result);
- }
-
- private void sendMessageToHandler(ServiceProgressHandler.MessageStatus status, Integer arg2, Bundle data) {
-
- Message msg = Message.obtain();
- assert msg != null;
- msg.arg1 = status.ordinal();
- if (arg2 != null) {
- msg.arg2 = arg2;
- }
- if (data != null) {
- msg.setData(data);
- }
-
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
- } catch (NullPointerException e) {
- Log.w(Constants.TAG, "Messenger is null!", e);
- }
- }
-
- private void sendMessageToHandler(ServiceProgressHandler.MessageStatus status, OperationResult data) {
- Bundle bundle = new Bundle();
- bundle.putParcelable(OperationResult.EXTRA_RESULT, data);
- sendMessageToHandler(status, null, bundle);
- }
-
- private void sendMessageToHandler(ServiceProgressHandler.MessageStatus status, Bundle data) {
- sendMessageToHandler(status, null, data);
- }
-
- private void sendMessageToHandler(ServiceProgressHandler.MessageStatus status) {
- sendMessageToHandler(status, null, null);
- }
-
- /**
- * Set progress of ProgressDialog by sending message to handler on UI thread
- */
- @Override
- public synchronized void setProgress(String message, int progress, int max) {
- Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
- + max);
-
- Bundle data = new Bundle();
- if (message != null) {
- data.putString(ServiceProgressHandler.DATA_MESSAGE, message);
- }
- data.putInt(ServiceProgressHandler.DATA_PROGRESS, progress);
- data.putInt(ServiceProgressHandler.DATA_PROGRESS_MAX, max);
-
- sendMessageToHandler(ServiceProgressHandler.MessageStatus.UPDATE_PROGRESS, null, data);
- }
-
- @Override
- public synchronized void setProgress(int resourceId, int progress, int max) {
- setProgress(getString(resourceId), progress, max);
- }
-
- @Override
- public synchronized void setProgress(int progress, int max) {
- setProgress(null, progress, max);
- }
-
- @Override
- public synchronized void setPreventCancel() {
- sendMessageToHandler(ServiceProgressHandler.MessageStatus.PREVENT_CANCEL);
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java
new file mode 100644
index 000000000..15d109814
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class ConsolidateInputParcel implements Parcelable {
+
+ public boolean mConsolidateRecovery;
+
+ public ConsolidateInputParcel(boolean consolidateRecovery) {
+ mConsolidateRecovery = consolidateRecovery;
+ }
+
+ protected ConsolidateInputParcel(Parcel in) {
+ mConsolidateRecovery = in.readByte() != 0x00;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mConsolidateRecovery ? 0x01 : 0x00));
+ }
+
+ public static final Parcelable.Creator<ConsolidateInputParcel> CREATOR = new Parcelable.Creator<ConsolidateInputParcel>() {
+ @Override
+ public ConsolidateInputParcel createFromParcel(Parcel in) {
+ return new ConsolidateInputParcel(in);
+ }
+
+ @Override
+ public ConsolidateInputParcel[] newArray(int size) {
+ return new ConsolidateInputParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
index 7688b9252..b36d23775 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java
@@ -45,7 +45,7 @@ public class ContactSyncAdapterService extends Service {
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
final SyncResult syncResult) {
- Log.d(Constants.TAG, "Performing a sync!");
+ Log.d(Constants.TAG, "Performing a contact sync!");
// TODO: Import is currently disabled for 2.8, until we implement proper origin management
// importDone.set(false);
// KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this);
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java
new file mode 100644
index 000000000..b412a6e2b
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class DeleteKeyringParcel implements Parcelable {
+
+ public long[] mMasterKeyIds;
+ public boolean mIsSecret;
+
+ public DeleteKeyringParcel(long[] masterKeyIds, boolean isSecret) {
+ mMasterKeyIds = masterKeyIds;
+ mIsSecret = isSecret;
+ }
+
+ protected DeleteKeyringParcel(Parcel in) {
+ mIsSecret = in.readByte() != 0x00;
+ mMasterKeyIds = in.createLongArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mIsSecret ? 0x01 : 0x00));
+ dest.writeLongArray(mMasterKeyIds);
+ }
+
+ public static final Parcelable.Creator<DeleteKeyringParcel> CREATOR = new Parcelable.Creator<DeleteKeyringParcel>() {
+ @Override
+ public DeleteKeyringParcel createFromParcel(Parcel in) {
+ return new DeleteKeyringParcel(in);
+ }
+
+ @Override
+ public DeleteKeyringParcel[] newArray(int size) {
+ return new DeleteKeyringParcel[size];
+ }
+ };
+}
+
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java
index 41ff6d02b..18bc3cc9d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DummyAccountService.java
@@ -83,7 +83,7 @@ public class DummyAccountService extends Service {
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType,
String[] requiredFeatures, Bundle options) throws NetworkErrorException {
response.onResult(new Bundle());
- toaster.toast(R.string.info_no_manual_account_creation);
+ toaster.toast(R.string.account_no_manual_account_creation);
Log.d(Constants.TAG, "DummyAccountService.addAccount");
return null;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java
new file mode 100644
index 000000000..24c002bbd
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * 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.service;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
+
+public class ExportKeyringParcel implements Parcelable {
+ public String mKeyserver;
+ public Uri mCanonicalizedPublicKeyringUri;
+ public UncachedKeyRing mUncachedKeyRing;
+
+ public boolean mExportSecret;
+ public long mMasterKeyIds[];
+ public String mOutputFile;
+ public Uri mOutputUri;
+ public ExportType mExportType;
+
+ public enum ExportType {
+ UPLOAD_KEYSERVER,
+ EXPORT_FILE,
+ EXPORT_URI
+ }
+
+ public ExportKeyringParcel(String keyserver, Uri keyringUri) {
+ mExportType = ExportType.UPLOAD_KEYSERVER;
+ mKeyserver = keyserver;
+ mCanonicalizedPublicKeyringUri = keyringUri;
+ }
+
+ public ExportKeyringParcel(String keyserver, UncachedKeyRing uncachedKeyRing) {
+ mExportType = ExportType.UPLOAD_KEYSERVER;
+ mKeyserver = keyserver;
+ mUncachedKeyRing = uncachedKeyRing;
+ }
+
+ public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, String outputFile) {
+ mExportType = ExportType.EXPORT_FILE;
+ mMasterKeyIds = masterKeyIds;
+ mExportSecret = exportSecret;
+ mOutputFile = outputFile;
+ }
+
+ @SuppressWarnings("unused") // TODO: is it used?
+ public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
+ mExportType = ExportType.EXPORT_URI;
+ mMasterKeyIds = masterKeyIds;
+ mExportSecret = exportSecret;
+ mOutputUri = outputUri;
+ }
+
+ protected ExportKeyringParcel(Parcel in) {
+ mKeyserver = in.readString();
+ mCanonicalizedPublicKeyringUri = (Uri) in.readValue(Uri.class.getClassLoader());
+ mUncachedKeyRing = (UncachedKeyRing) in.readValue(UncachedKeyRing.class.getClassLoader());
+ mExportSecret = in.readByte() != 0x00;
+ mOutputFile = in.readString();
+ mOutputUri = (Uri) in.readValue(Uri.class.getClassLoader());
+ mExportType = (ExportType) in.readValue(ExportType.class.getClassLoader());
+ mMasterKeyIds = in.createLongArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mKeyserver);
+ dest.writeValue(mCanonicalizedPublicKeyringUri);
+ dest.writeValue(mUncachedKeyRing);
+ dest.writeByte((byte) (mExportSecret ? 0x01 : 0x00));
+ dest.writeString(mOutputFile);
+ dest.writeValue(mOutputUri);
+ dest.writeValue(mExportType);
+ dest.writeLongArray(mMasterKeyIds);
+ }
+
+ public static final Parcelable.Creator<ExportKeyringParcel> CREATOR = new Parcelable.Creator<ExportKeyringParcel>() {
+ @Override
+ public ExportKeyringParcel createFromParcel(Parcel in) {
+ return new ExportKeyringParcel(in);
+ }
+
+ @Override
+ public ExportKeyringParcel[] newArray(int size) {
+ return new ExportKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java
new file mode 100644
index 000000000..a41dd71cb
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+
+import java.util.ArrayList;
+
+public class ImportKeyringParcel implements Parcelable {
+ // if null, keys are expected to be read from a cache file in ImportExportOperations
+ public ArrayList<ParcelableKeyRing> mKeyList;
+ public String mKeyserver; // must be set if keys are to be imported from a keyserver
+
+ public ImportKeyringParcel (ArrayList<ParcelableKeyRing> keyList, String keyserver) {
+ mKeyList = keyList;
+ mKeyserver = keyserver;
+ }
+
+ protected ImportKeyringParcel(Parcel in) {
+ if (in.readByte() == 0x01) {
+ mKeyList = new ArrayList<>();
+ in.readList(mKeyList, ParcelableKeyRing.class.getClassLoader());
+ } else {
+ mKeyList = null;
+ }
+ mKeyserver = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mKeyList == null) {
+ dest.writeByte((byte) (0x00));
+ } else {
+ dest.writeByte((byte) (0x01));
+ dest.writeList(mKeyList);
+ }
+ dest.writeString(mKeyserver);
+ }
+
+ public static final Parcelable.Creator<ImportKeyringParcel> CREATOR = new Parcelable.Creator<ImportKeyringParcel>() {
+ @Override
+ public ImportKeyringParcel createFromParcel(Parcel in) {
+ return new ImportKeyringParcel(in);
+ }
+
+ @Override
+ public ImportKeyringParcel[] newArray(int size) {
+ return new ImportKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java
new file mode 100644
index 000000000..1872191af
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class KeybaseVerificationParcel implements Parcelable {
+
+ public String mKeybaseProof;
+ public String mRequiredFingerprint;
+
+ public KeybaseVerificationParcel(String keybaseProof, String requiredFingerprint) {
+ mKeybaseProof = keybaseProof;
+ mRequiredFingerprint = requiredFingerprint;
+ }
+
+ protected KeybaseVerificationParcel(Parcel in) {
+ mKeybaseProof = in.readString();
+ mRequiredFingerprint = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mKeybaseProof);
+ dest.writeString(mRequiredFingerprint);
+ }
+
+ public static final Parcelable.Creator<KeybaseVerificationParcel> CREATOR = new Parcelable.Creator<KeybaseVerificationParcel>() {
+ @Override
+ public KeybaseVerificationParcel createFromParcel(Parcel in) {
+ return new KeybaseVerificationParcel(in);
+ }
+
+ @Override
+ public KeybaseVerificationParcel[] newArray(int size) {
+ return new KeybaseVerificationParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
deleted file mode 100644
index 63ea6285c..000000000
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java
+++ /dev/null
@@ -1,752 +0,0 @@
-/*
- * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
- * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
- *
- * 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.service;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-
-import com.textuality.keybase.lib.Proof;
-import com.textuality.keybase.lib.prover.Prover;
-
-import org.json.JSONObject;
-import org.spongycastle.openpgp.PGPUtil;
-import org.sufficientlysecure.keychain.Constants;
-import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver;
-import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.CertifyOperation;
-import org.sufficientlysecure.keychain.operations.DeleteOperation;
-import org.sufficientlysecure.keychain.operations.EditKeyOperation;
-import org.sufficientlysecure.keychain.operations.ImportExportOperation;
-import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
-import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
-import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
-import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
-import org.sufficientlysecure.keychain.operations.results.DeleteResult;
-import org.sufficientlysecure.keychain.operations.results.ExportResult;
-import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
-import org.sufficientlysecure.keychain.util.FileHelper;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
-import org.sufficientlysecure.keychain.util.Preferences;
-import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
-import org.sufficientlysecure.keychain.keyimport.Keyserver;
-import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
-import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult;
-import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
-import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
-import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
-import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
-import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.pgp.Progressable;
-import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
-import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
-import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
-import org.sufficientlysecure.keychain.util.FileHelper;
-import org.sufficientlysecure.keychain.util.InputData;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.ParcelableFileCache;
-import org.sufficientlysecure.keychain.util.Passphrase;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import de.measite.minidns.Client;
-import de.measite.minidns.DNSMessage;
-import de.measite.minidns.Question;
-import de.measite.minidns.Record;
-import de.measite.minidns.record.Data;
-import de.measite.minidns.record.TXT;
-
-/**
- * This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
- * 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 {
-
- /* extras that can be given by intent */
- public static final String EXTRA_MESSENGER = "messenger";
- public static final String EXTRA_DATA = "data";
-
- /* possible actions */
- public static final String ACTION_SIGN_ENCRYPT = Constants.INTENT_PREFIX + "SIGN_ENCRYPT";
-
- public static final String ACTION_DECRYPT_VERIFY = Constants.INTENT_PREFIX + "DECRYPT_VERIFY";
-
- public static final String ACTION_VERIFY_KEYBASE_PROOF = Constants.INTENT_PREFIX + "VERIFY_KEYBASE_PROOF";
-
- public static final String ACTION_DECRYPT_METADATA = Constants.INTENT_PREFIX + "DECRYPT_METADATA";
-
- public static final String ACTION_EDIT_KEYRING = Constants.INTENT_PREFIX + "EDIT_KEYRING";
-
- public static final String ACTION_PROMOTE_KEYRING = Constants.INTENT_PREFIX + "PROMOTE_KEYRING";
-
- public static final String ACTION_IMPORT_KEYRING = Constants.INTENT_PREFIX + "IMPORT_KEYRING";
- public static final String ACTION_EXPORT_KEYRING = Constants.INTENT_PREFIX + "EXPORT_KEYRING";
-
- public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
-
- public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
-
- public static final String ACTION_DELETE = Constants.INTENT_PREFIX + "DELETE";
-
- public static final String ACTION_CONSOLIDATE = Constants.INTENT_PREFIX + "CONSOLIDATE";
-
- public static final String ACTION_CANCEL = Constants.INTENT_PREFIX + "CANCEL";
-
- /* keys for data bundle */
-
- // encrypt, decrypt, import export
- public static final String TARGET = "target";
- public static final String SOURCE = "source";
-
- // possible targets:
- public static enum IOType {
- UNKNOWN,
- BYTES,
- URI;
-
- private static final IOType[] values = values();
-
- public static IOType fromInt(int n) {
- if (n < 0 || n >= values.length) {
- return UNKNOWN;
- } else {
- return values[n];
- }
- }
- }
-
- // encrypt
- public static final String ENCRYPT_DECRYPT_INPUT_URI = "input_uri";
- public static final String ENCRYPT_DECRYPT_OUTPUT_URI = "output_uri";
- public static final String SIGN_ENCRYPT_PARCEL = "sign_encrypt_parcel";
-
- // decrypt/verify
- public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes";
-
- // keybase proof
- public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint";
- public static final String KEYBASE_PROOF = "keybase_proof";
-
- // save keyring
- public static final String EDIT_KEYRING_PARCEL = "save_parcel";
- public static final String EDIT_KEYRING_PASSPHRASE = "passphrase";
- public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
-
- // delete keyring(s)
- public static final String DELETE_KEY_LIST = "delete_list";
- public static final String DELETE_IS_SECRET = "delete_is_secret";
-
- // import key
- public static final String IMPORT_KEY_LIST = "import_key_list";
- public static final String IMPORT_KEY_SERVER = "import_key_server";
-
- // export key
- public static final String EXPORT_FILENAME = "export_filename";
- public static final String EXPORT_URI = "export_uri";
- public static final String EXPORT_SECRET = "export_secret";
- public static final String EXPORT_ALL = "export_all";
- public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
-
- // upload key
- public static final String UPLOAD_KEY_SERVER = "upload_key_server";
-
- // certify key
- public static final String CERTIFY_PARCEL = "certify_parcel";
-
- // promote key
- public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id";
- public static final String PROMOTE_CARD_AID = "promote_card_aid";
-
- // consolidate
- public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
-
-
- /*
- * possible data keys as result send over messenger
- */
-
- // decrypt/verify
- public static final String RESULT_DECRYPTED_BYTES = "decrypted_data";
-
- Messenger mMessenger;
-
- // this attribute can possibly merged with the one above? not sure...
- private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
-
- public KeychainIntentService() {
- super("KeychainIntentService");
- }
-
- /**
- * The IntentService calls this method from the default worker thread with the intent that
- * started the service. When this method returns, IntentService stops the service, as
- * appropriate.
- */
- @Override
- protected void onHandleIntent(Intent intent) {
-
- // We have not been cancelled! (yet)
- mActionCanceled.set(false);
-
- Bundle extras = intent.getExtras();
- if (extras == null) {
- Log.e(Constants.TAG, "Extras bundle is null!");
- return;
- }
-
- if (!(extras.containsKey(EXTRA_MESSENGER) || extras.containsKey(EXTRA_DATA) || (intent
- .getAction() == null))) {
- Log.e(Constants.TAG,
- "Extra bundle must contain a messenger, a data bundle, and an action!");
- return;
- }
-
- Uri dataUri = intent.getData();
-
- mMessenger = (Messenger) extras.get(EXTRA_MESSENGER);
- Bundle data = extras.getBundle(EXTRA_DATA);
- if (data == null) {
- Log.e(Constants.TAG, "data extra is null!");
- return;
- }
-
- Log.logDebugBundle(data, "EXTRA_DATA");
-
- ProviderHelper providerHelper = new ProviderHelper(this);
-
- String action = intent.getAction();
-
- // executeServiceMethod action from extra bundle
- switch (action) {
- case ACTION_CERTIFY_KEYRING: {
-
- // Input
- CertifyActionsParcel parcel = data.getParcelable(CERTIFY_PARCEL);
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
- String keyServerUri = data.getString(UPLOAD_KEY_SERVER);
-
- // Operation
- CertifyOperation op = new CertifyOperation(this, providerHelper, this, mActionCanceled);
- CertifyResult result = op.certify(parcel, cryptoInput, keyServerUri);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_CONSOLIDATE: {
-
- // Operation
- ConsolidateResult result;
- if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) {
- result = new ProviderHelper(this).consolidateDatabaseStep2(this);
- } else {
- result = new ProviderHelper(this).consolidateDatabaseStep1(this);
- }
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_DECRYPT_METADATA: {
-
- try {
- /* Input */
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
-
- InputData inputData = createDecryptInputData(data);
-
- // verifyText and decrypt returning additional resultData values for the
- // verification of signatures
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(this), this, inputData, null
- );
- builder.setAllowSymmetricDecryption(true)
- .setDecryptMetadataOnly(true);
-
- DecryptVerifyResult decryptVerifyResult = builder.build().execute(cryptoInput);
-
- sendMessageToHandler(MessageStatus.OKAY, decryptVerifyResult);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
-
- break;
- }
- case ACTION_VERIFY_KEYBASE_PROOF: {
-
- try {
- Proof proof = new Proof(new JSONObject(data.getString(KEYBASE_PROOF)));
- setProgress(R.string.keybase_message_fetching_data, 0, 100);
-
- Prover prover = Prover.findProverFor(proof);
-
- if (prover == null) {
- sendProofError(getString(R.string.keybase_no_prover_found) + ": " + proof.getPrettyName());
- return;
- }
-
- if (!prover.fetchProofData()) {
- sendProofError(prover.getLog(), getString(R.string.keybase_problem_fetching_evidence));
- return;
- }
- String requiredFingerprint = data.getString(KEYBASE_REQUIRED_FINGERPRINT);
- if (!prover.checkFingerprint(requiredFingerprint)) {
- sendProofError(getString(R.string.keybase_key_mismatch));
- return;
- }
-
- String domain = prover.dnsTxtCheckRequired();
- if (domain != null) {
- DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
- if (dnsQuery == null) {
- sendProofError(prover.getLog(), getString(R.string.keybase_dns_query_failure));
- return;
- }
- Record[] records = dnsQuery.getAnswers();
- List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
- for (Record r : records) {
- Data d = r.getPayload();
- if (d instanceof TXT) {
- extents.add(((TXT) d).getExtents());
- }
- }
- if (!prover.checkDnsTxt(extents)) {
- sendProofError(prover.getLog(), null);
- return;
- }
- }
-
- byte[] messageBytes = prover.getPgpMessage().getBytes();
- if (prover.rawMessageCheckRequired()) {
- InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream(messageBytes));
- if (!prover.checkRawMessageBytes(messageByteStream)) {
- sendProofError(prover.getLog(), null);
- return;
- }
- }
-
- // kind of awkward, but this whole class wants to pull bytes out of “data”
- data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal());
- data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, messageBytes);
-
- InputData inputData = createDecryptInputData(data);
- OutputStream outStream = createCryptOutputStream(data);
-
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(this), this,
- inputData, outStream
- );
- builder.setSignedLiteralData(true).setRequiredSignerFingerprint(requiredFingerprint);
-
- DecryptVerifyResult decryptVerifyResult = builder.build().execute(
- new CryptoInputParcel());
- outStream.close();
-
- if (!decryptVerifyResult.success()) {
- OperationLog log = decryptVerifyResult.getLog();
- OperationResult.LogEntryParcel lastEntry = null;
- for (OperationResult.LogEntryParcel entry : log) {
- lastEntry = entry;
- }
- sendProofError(getString(lastEntry.mType.getMsgId()));
- return;
- }
-
- if (!prover.validate(outStream.toString())) {
- sendProofError(getString(R.string.keybase_message_payload_mismatch));
- return;
- }
-
- Bundle resultData = new Bundle();
- resultData.putString(ServiceProgressHandler.DATA_MESSAGE, "OK");
-
- // these help the handler construct a useful human-readable message
- resultData.putString(ServiceProgressHandler.KEYBASE_PROOF_URL, prover.getProofUrl());
- resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_URL, prover.getPresenceUrl());
- resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL, prover.getPresenceLabel());
- sendMessageToHandler(MessageStatus.OKAY, resultData);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
-
- break;
- }
- case ACTION_DECRYPT_VERIFY: {
-
- try {
- /* Input */
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
-
- InputData inputData = createDecryptInputData(data);
- OutputStream outStream = createCryptOutputStream(data);
-
- /* Operation */
- Bundle resultData = new Bundle();
-
- // verifyText and decrypt returning additional resultData values for the
- // verification of signatures
- PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(
- this, new ProviderHelper(this), this,
- inputData, outStream
- );
- builder.setAllowSymmetricDecryption(true);
-
- DecryptVerifyResult decryptVerifyResult = builder.build().execute(cryptoInput);
-
- outStream.close();
-
- resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult);
-
- /* Output */
- finalizeDecryptOutputStream(data, resultData, outStream);
- Log.logDebugBundle(resultData, "resultData");
-
- sendMessageToHandler(MessageStatus.OKAY, resultData);
-
- } catch (IOException | PgpGeneralException e) {
- // TODO get rid of this!
- sendErrorToHandler(e);
- }
-
- break;
- }
- case ACTION_DELETE: {
-
- // Input
- long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
- boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
-
- // Operation
- DeleteOperation op = new DeleteOperation(this, new ProviderHelper(this), this);
- DeleteResult result = op.execute(masterKeyIds, isSecret);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_EDIT_KEYRING: {
-
- // Input
- SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
-
- // Operation
- EditKeyOperation op = new EditKeyOperation(this, providerHelper, this, mActionCanceled);
- OperationResult result = op.execute(saveParcel, cryptoInput);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_PROMOTE_KEYRING: {
-
- // Input
- long keyRingId = data.getLong(PROMOTE_MASTER_KEY_ID);
- byte[] cardAid = data.getByteArray(PROMOTE_CARD_AID);
-
- // Operation
- PromoteKeyOperation op = new PromoteKeyOperation(this, providerHelper, this, mActionCanceled);
- PromoteKeyResult result = op.execute(keyRingId, cardAid);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_EXPORT_KEYRING: {
-
- // Input
- boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
- String outputFile = data.getString(EXPORT_FILENAME);
- Uri outputUri = data.getParcelable(EXPORT_URI);
-
- boolean exportAll = data.getBoolean(EXPORT_ALL);
- long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
-
- // Operation
- ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this);
- ExportResult result;
- if (outputFile != null) {
- result = importExportOperation.exportToFile(masterKeyIds, exportSecret, outputFile);
- } else {
- result = importExportOperation.exportToUri(masterKeyIds, exportSecret, outputUri);
- }
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_IMPORT_KEYRING: {
-
- // Input
- String keyServer = data.getString(IMPORT_KEY_SERVER);
- ArrayList<ParcelableKeyRing> list = data.getParcelableArrayList(IMPORT_KEY_LIST);
- ParcelableFileCache<ParcelableKeyRing> cache =
- new ParcelableFileCache<>(this, "key_import.pcl");
-
- // Operation
- ImportExportOperation importExportOperation = new ImportExportOperation(
- this, providerHelper, this, mActionCanceled);
- // Either list or cache must be null, no guarantees otherwise.
- ImportKeyResult result = list != null
- ? importExportOperation.importKeyRings(list, keyServer)
- : importExportOperation.importKeyRings(cache, keyServer);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
-
- }
- case ACTION_SIGN_ENCRYPT: {
-
- // Input
- SignEncryptParcel inputParcel = data.getParcelable(SIGN_ENCRYPT_PARCEL);
- CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
-
- // Operation
- SignEncryptOperation op = new SignEncryptOperation(
- this, new ProviderHelper(this), this, mActionCanceled);
- SignEncryptResult result = op.execute(inputParcel, cryptoInput);
-
- // Result
- sendMessageToHandler(MessageStatus.OKAY, result);
-
- break;
- }
- case ACTION_UPLOAD_KEYRING: {
- try {
-
- /* Input */
- String keyServer = data.getString(UPLOAD_KEY_SERVER);
- // and dataUri!
-
- /* Operation */
- HkpKeyserver server = new HkpKeyserver(keyServer);
-
- CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
- ImportExportOperation importExportOperation = new ImportExportOperation(this, new ProviderHelper(this), this);
-
- try {
- importExportOperation.uploadKeyRingToServer(server, keyring);
- } catch (Keyserver.AddKeyException e) {
- throw new PgpGeneralException("Unable to export key to selected server");
- }
-
- sendMessageToHandler(MessageStatus.OKAY);
- } catch (Exception e) {
- sendErrorToHandler(e);
- }
- break;
- }
- }
- }
-
- private void sendProofError(List<String> log, String label) {
- String msg = null;
- label = (label == null) ? "" : label + ": ";
- for (String m : log) {
- Log.e(Constants.TAG, label + m);
- msg = m;
- }
- sendProofError(label + msg);
- }
-
- private void sendProofError(String msg) {
- Bundle bundle = new Bundle();
- bundle.putString(ServiceProgressHandler.DATA_ERROR, msg);
- sendMessageToHandler(MessageStatus.OKAY, bundle);
- }
-
- private void sendErrorToHandler(Exception e) {
- // TODO: Implement a better exception handling here
- // contextualize the exception, if necessary
- String message;
- if (e instanceof PgpGeneralMsgIdException) {
- e = ((PgpGeneralMsgIdException) e).getContextualized(this);
- message = e.getMessage();
- } else {
- message = e.getMessage();
- }
- Log.d(Constants.TAG, "KeychainIntentService Exception: ", e);
-
- Bundle data = new Bundle();
- data.putString(ServiceProgressHandler.DATA_ERROR, message);
- sendMessageToHandler(MessageStatus.EXCEPTION, null, data);
- }
-
- private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
-
- Message msg = Message.obtain();
- assert msg != null;
- msg.arg1 = status.ordinal();
- if (arg2 != null) {
- msg.arg2 = arg2;
- }
- if (data != null) {
- msg.setData(data);
- }
-
- try {
- mMessenger.send(msg);
- } catch (RemoteException e) {
- Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
- } catch (NullPointerException e) {
- Log.w(Constants.TAG, "Messenger is null!", e);
- }
- }
-
- private void sendMessageToHandler(MessageStatus status, OperationResult data) {
- Bundle bundle = new Bundle();
- bundle.putParcelable(OperationResult.EXTRA_RESULT, data);
- sendMessageToHandler(status, null, bundle);
- }
-
- private void sendMessageToHandler(MessageStatus status, Bundle data) {
- sendMessageToHandler(status, null, data);
- }
-
- private void sendMessageToHandler(MessageStatus status) {
- sendMessageToHandler(status, null, null);
- }
-
- /**
- * Set progress of ProgressDialog by sending message to handler on UI thread
- */
- public void setProgress(String message, int progress, int max) {
- Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
- + max);
-
- Bundle data = new Bundle();
- if (message != null) {
- data.putString(ServiceProgressHandler.DATA_MESSAGE, message);
- }
- data.putInt(ServiceProgressHandler.DATA_PROGRESS, progress);
- data.putInt(ServiceProgressHandler.DATA_PROGRESS_MAX, max);
-
- sendMessageToHandler(MessageStatus.UPDATE_PROGRESS, null, data);
- }
-
- public void setProgress(int resourceId, int progress, int max) {
- setProgress(getString(resourceId), progress, max);
- }
-
- public void setProgress(int progress, int max) {
- setProgress(null, progress, max);
- }
-
- @Override
- public void setPreventCancel() {
- sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
- }
-
- private InputData createDecryptInputData(Bundle data) throws IOException, PgpGeneralException {
- return createCryptInputData(data, DECRYPT_CIPHERTEXT_BYTES);
- }
-
- private InputData createCryptInputData(Bundle data, String bytesName) throws PgpGeneralException, IOException {
- int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET);
- IOType type = IOType.fromInt(source);
- switch (type) {
- case BYTES: /* encrypting bytes directly */
- byte[] bytes = data.getByteArray(bytesName);
- return new InputData(new ByteArrayInputStream(bytes), bytes.length);
-
- case URI: /* encrypting content uri */
- Uri providerUri = data.getParcelable(ENCRYPT_DECRYPT_INPUT_URI);
-
- // InputStream
- return new InputData(getContentResolver().openInputStream(providerUri), FileHelper.getFileSize(this, providerUri, 0));
-
- default:
- throw new PgpGeneralException("No target chosen!");
- }
- }
-
- private OutputStream createCryptOutputStream(Bundle data) throws PgpGeneralException, FileNotFoundException {
- int target = data.getInt(TARGET);
- IOType type = IOType.fromInt(target);
- switch (type) {
- case BYTES:
- return new ByteArrayOutputStream();
-
- case URI:
- Uri providerUri = data.getParcelable(ENCRYPT_DECRYPT_OUTPUT_URI);
-
- return getContentResolver().openOutputStream(providerUri);
-
- default:
- throw new PgpGeneralException("No target chosen!");
- }
- }
-
- private void finalizeDecryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream) {
- finalizeCryptOutputStream(data, resultData, outStream, RESULT_DECRYPTED_BYTES);
- }
-
- private void finalizeCryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream, String bytesName) {
- int target = data.getInt(TARGET);
- IOType type = IOType.fromInt(target);
- switch (type) {
- case BYTES:
- byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
- resultData.putByteArray(bytesName, output);
- break;
- case URI:
- // nothing, output was written, just send okay and verification bundle
-
- break;
- }
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (ACTION_CANCEL.equals(intent.getAction())) {
- mActionCanceled.set(true);
- return START_NOT_STICKY;
- }
- return super.onStartCommand(intent, flags, startId);
- }
-}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
new file mode 100644
index 000000000..eff27f112
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ *
+ * 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.service;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.operations.BaseOperation;
+import org.sufficientlysecure.keychain.operations.CertifyOperation;
+import org.sufficientlysecure.keychain.operations.ConsolidateOperation;
+import org.sufficientlysecure.keychain.operations.DeleteOperation;
+import org.sufficientlysecure.keychain.operations.EditKeyOperation;
+import org.sufficientlysecure.keychain.operations.ExportOperation;
+import org.sufficientlysecure.keychain.operations.ImportOperation;
+import org.sufficientlysecure.keychain.operations.KeybaseVerificationOperation;
+import org.sufficientlysecure.keychain.operations.PromoteKeyOperation;
+import org.sufficientlysecure.keychain.operations.RevokeOperation;
+import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation;
+import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
+import org.sufficientlysecure.keychain.pgp.Progressable;
+import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
+ * data from the activities or other apps, executes them, and stops itself after doing them.
+ */
+public class KeychainService extends Service implements Progressable {
+
+ // messenger for communication (hack)
+ public static final String EXTRA_MESSENGER = "messenger";
+
+ // extras for operation
+ public static final String EXTRA_OPERATION_INPUT = "op_input";
+ public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
+
+ public static final String ACTION_CANCEL = "action_cancel";
+
+ // this attribute can possibly merged with the one above? not sure...
+ private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
+
+ ThreadLocal<Messenger> mMessenger = new ThreadLocal<>();
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ /**
+ * This is run on the main thread, we need to spawn a runnable which runs on another thread for the actual operation
+ */
+ @Override
+ public int onStartCommand(final Intent intent, int flags, int startId) {
+
+ if (intent.getAction() != null && intent.getAction().equals(ACTION_CANCEL)) {
+ mActionCanceled.set(true);
+ return START_NOT_STICKY;
+ }
+
+ Runnable actionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // We have not been cancelled! (yet)
+ mActionCanceled.set(false);
+
+ Bundle extras = intent.getExtras();
+
+ // Set messenger for communication (for this particular thread)
+ mMessenger.set(extras.<Messenger>getParcelable(EXTRA_MESSENGER));
+
+ // Input
+ Parcelable inputParcel = extras.getParcelable(EXTRA_OPERATION_INPUT);
+ CryptoInputParcel cryptoInput = extras.getParcelable(EXTRA_CRYPTO_INPUT);
+
+ // Operation
+ BaseOperation op;
+
+ // just for brevity
+ KeychainService outerThis = KeychainService.this;
+ if (inputParcel instanceof SignEncryptParcel) {
+ op = new SignEncryptOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis, mActionCanceled);
+ } else if (inputParcel instanceof PgpDecryptVerifyInputParcel) {
+ op = new PgpDecryptVerifyOperation(outerThis, new ProviderHelper(outerThis), outerThis);
+ } else if (inputParcel instanceof SaveKeyringParcel) {
+ op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof RevokeKeyringParcel) {
+ op = new RevokeOperation(outerThis, new ProviderHelper(outerThis), outerThis);
+ } else if (inputParcel instanceof CertifyActionsParcel) {
+ op = new CertifyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof DeleteKeyringParcel) {
+ op = new DeleteOperation(outerThis, new ProviderHelper(outerThis), outerThis);
+ } else if (inputParcel instanceof PromoteKeyringParcel) {
+ op = new PromoteKeyOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis, mActionCanceled);
+ } else if (inputParcel instanceof ImportKeyringParcel) {
+ op = new ImportOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof ExportKeyringParcel) {
+ op = new ExportOperation(outerThis, new ProviderHelper(outerThis), outerThis,
+ mActionCanceled);
+ } else if (inputParcel instanceof ConsolidateInputParcel) {
+ op = new ConsolidateOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis);
+ } else if (inputParcel instanceof KeybaseVerificationParcel) {
+ op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis),
+ outerThis);
+ } else {
+ throw new AssertionError("Unrecognized input parcel in KeychainService!");
+ }
+
+ @SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
+ OperationResult result = op.execute(inputParcel, cryptoInput);
+ sendMessageToHandler(MessageStatus.OKAY, result);
+
+ }
+ };
+
+ Thread actionThread = new Thread(actionRunnable);
+ actionThread.start();
+
+ return START_NOT_STICKY;
+ }
+
+ private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
+
+ Message msg = Message.obtain();
+ assert msg != null;
+ msg.arg1 = status.ordinal();
+ if (arg2 != null) {
+ msg.arg2 = arg2;
+ }
+ if (data != null) {
+ msg.setData(data);
+ }
+
+ try {
+ mMessenger.get().send(msg);
+ } catch (RemoteException e) {
+ Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
+ } catch (NullPointerException e) {
+ Log.w(Constants.TAG, "Messenger is null!", e);
+ }
+ }
+
+ private void sendMessageToHandler(MessageStatus status, OperationResult data) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(OperationResult.EXTRA_RESULT, data);
+ sendMessageToHandler(status, null, bundle);
+ }
+
+ private void sendMessageToHandler(MessageStatus status) {
+ sendMessageToHandler(status, null, null);
+ }
+
+ /**
+ * Set progress of ProgressDialog by sending message to handler on UI thread
+ */
+ @Override
+ public void setProgress(String message, int progress, int max) {
+ Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
+ + max);
+
+ Bundle data = new Bundle();
+ if (message != null) {
+ data.putString(ServiceProgressHandler.DATA_MESSAGE, message);
+ }
+ data.putInt(ServiceProgressHandler.DATA_PROGRESS, progress);
+ data.putInt(ServiceProgressHandler.DATA_PROGRESS_MAX, max);
+
+ sendMessageToHandler(MessageStatus.UPDATE_PROGRESS, null, data);
+ }
+
+ @Override
+ public void setProgress(int resourceId, int progress, int max) {
+ setProgress(getString(resourceId), progress, max);
+ }
+
+ @Override
+ public void setProgress(int progress, int max) {
+ setProgress(null, progress, max);
+ }
+
+ @Override
+ public void setPreventCancel() {
+ sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
+ }
+
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
new file mode 100644
index 000000000..3243df1a8
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java
@@ -0,0 +1,516 @@
+package org.sufficientlysecure.keychain.service;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SyncResult;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.support.v4.app.NotificationCompat;
+import android.widget.Toast;
+
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.R;
+import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.operations.ImportOperation;
+import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.operations.results.OperationResult;
+import org.sufficientlysecure.keychain.provider.KeychainContract;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.ui.OrbotRequiredDialogActivity;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
+import org.sufficientlysecure.keychain.util.Preferences;
+import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
+
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class KeyserverSyncAdapterService extends Service {
+
+ // how often a sync should be initiated, in s
+ public static final long SYNC_INTERVAL =
+ Constants.DEBUG_KEYSERVER_SYNC
+ ? TimeUnit.MINUTES.toSeconds(2) : TimeUnit.DAYS.toSeconds(3);
+ // time since last update after which a key should be updated again, in s
+ public static final long KEY_UPDATE_LIMIT =
+ Constants.DEBUG_KEYSERVER_SYNC ? 1 : TimeUnit.DAYS.toSeconds(7);
+ // time by which a sync is postponed in case of a
+ public static final long SYNC_POSTPONE_TIME =
+ Constants.DEBUG_KEYSERVER_SYNC ? 30 * 1000 : TimeUnit.MINUTES.toMillis(5);
+ // Time taken by Orbot before a new circuit is created
+ public static final int ORBOT_CIRCUIT_TIMEOUT = (int) TimeUnit.MINUTES.toMillis(10);
+
+
+ private static final String ACTION_IGNORE_TOR = "ignore_tor";
+ private static final String ACTION_UPDATE_ALL = "update_all";
+ private static final String ACTION_SYNC_NOW = "sync_now";
+ private static final String ACTION_DISMISS_NOTIFICATION = "cancel_sync";
+ private static final String ACTION_START_ORBOT = "start_orbot";
+ private static final String ACTION_CANCEL = "cancel";
+
+ private AtomicBoolean mCancelled = new AtomicBoolean(false);
+
+ @Override
+ public int onStartCommand(final Intent intent, int flags, final int startId) {
+ switch (intent.getAction()) {
+ case ACTION_CANCEL: {
+ mCancelled.set(true);
+ break;
+ }
+ // the reason for the separation betweyeen SYNC_NOW and UPDATE_ALL is so that starting
+ // the sync directly from the notification is possible while the screen is on with
+ // UPDATE_ALL, but a postponed sync is only started if screen is off
+ case ACTION_SYNC_NOW: {
+ // this checks for screen on/off before sync, and postpones the sync if on
+ ContentResolver.requestSync(
+ new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE),
+ Constants.PROVIDER_AUTHORITY,
+ new Bundle()
+ );
+ break;
+ }
+ case ACTION_UPDATE_ALL: {
+ // does not check for screen on/off
+ asyncKeyUpdate(this, new CryptoInputParcel());
+ break;
+ }
+ case ACTION_IGNORE_TOR: {
+ NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT);
+ asyncKeyUpdate(this, new CryptoInputParcel(ParcelableProxy.getForNoProxy()));
+ break;
+ }
+ case ACTION_START_ORBOT: {
+ NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT);
+ Intent startOrbot = new Intent(this, OrbotRequiredDialogActivity.class);
+ startOrbot.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_START_ORBOT, true);
+ Messenger messenger = new Messenger(
+ new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case OrbotRequiredDialogActivity.MESSAGE_ORBOT_STARTED: {
+ asyncKeyUpdate(KeyserverSyncAdapterService.this,
+ new CryptoInputParcel());
+ break;
+ }
+ case OrbotRequiredDialogActivity.MESSAGE_ORBOT_IGNORE: {
+ asyncKeyUpdate(KeyserverSyncAdapterService.this,
+ new CryptoInputParcel(
+ ParcelableProxy.getForNoProxy()));
+ break;
+ }
+ case OrbotRequiredDialogActivity.MESSAGE_DIALOG_CANCEL: {
+ // just stop service
+ stopSelf();
+ break;
+ }
+ }
+ }
+ }
+ );
+ startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_MESSENGER, messenger);
+ startActivity(startOrbot);
+ break;
+ }
+ case ACTION_DISMISS_NOTIFICATION: {
+ NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT);
+ stopSelf(startId);
+ break;
+ }
+ }
+ return START_NOT_STICKY;
+ }
+
+ private class KeyserverSyncAdapter extends AbstractThreadedSyncAdapter {
+
+ public KeyserverSyncAdapter() {
+ super(KeyserverSyncAdapterService.this, true);
+ }
+
+ @Override
+ public void onPerformSync(Account account, Bundle extras, String authority,
+ ContentProviderClient provider, SyncResult syncResult) {
+ Log.d(Constants.TAG, "Performing a keyserver sync!");
+
+ PowerManager pm = (PowerManager) KeyserverSyncAdapterService.this
+ .getSystemService(Context.POWER_SERVICE);
+ @SuppressWarnings("deprecation") // our min is API 15, deprecated only in 20
+ boolean isScreenOn = pm.isScreenOn();
+
+ if (!isScreenOn) {
+ Intent serviceIntent = new Intent(KeyserverSyncAdapterService.this,
+ KeyserverSyncAdapterService.class);
+ serviceIntent.setAction(ACTION_UPDATE_ALL);
+ startService(serviceIntent);
+ } else {
+ postponeSync();
+ }
+ }
+
+ @Override
+ public void onSyncCanceled() {
+ super.onSyncCanceled();
+ cancelUpdates(KeyserverSyncAdapterService.this);
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new KeyserverSyncAdapter().getSyncAdapterBinder();
+ }
+
+ private void handleUpdateResult(ImportKeyResult result) {
+ if (result.isPending()) {
+ // result is pending due to Orbot not being started
+ // try to start it silently, if disabled show notifications
+ new OrbotHelper.SilentStartManager() {
+ @Override
+ protected void onOrbotStarted() {
+ // retry the update
+ asyncKeyUpdate(KeyserverSyncAdapterService.this,
+ new CryptoInputParcel());
+ }
+
+ @Override
+ protected void onSilentStartDisabled() {
+ // show notification
+ NotificationManager manager =
+ (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ manager.notify(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT,
+ getOrbotNoification(KeyserverSyncAdapterService.this));
+ }
+ }.startOrbotAndListen(this, false);
+ } else if (isUpdateCancelled()) {
+ Log.d(Constants.TAG, "Keyserver sync cancelled, postponing by" + SYNC_POSTPONE_TIME
+ + "ms");
+ postponeSync();
+ } else {
+ Log.d(Constants.TAG, "Keyserver sync completed: Updated: " + result.mUpdatedKeys
+ + " Failed: " + result.mBadKeys);
+ stopSelf();
+ }
+ }
+
+ private void postponeSync() {
+ AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
+ Intent serviceIntent = new Intent(this, KeyserverSyncAdapterService.class);
+ serviceIntent.setAction(ACTION_SYNC_NOW);
+ PendingIntent pi = PendingIntent.getService(this, 0, serviceIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ alarmManager.set(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + SYNC_POSTPONE_TIME,
+ pi
+ );
+ }
+
+ private void asyncKeyUpdate(final Context context,
+ final CryptoInputParcel cryptoInputParcel) {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ ImportKeyResult result = updateKeysFromKeyserver(context, cryptoInputParcel);
+ handleUpdateResult(result);
+ }
+ }).start();
+ }
+
+ private synchronized ImportKeyResult updateKeysFromKeyserver(final Context context,
+ final CryptoInputParcel cryptoInputParcel) {
+ mCancelled.set(false);
+
+ ArrayList<ParcelableKeyRing> keyList = getKeysToUpdate(context);
+
+ if (isUpdateCancelled()) { // if we've already been cancelled
+ return new ImportKeyResult(OperationResult.RESULT_CANCELLED,
+ new OperationResult.OperationLog());
+ }
+
+ if (cryptoInputParcel.getParcelableProxy() == null) {
+ // no explicit proxy, retrieve from preferences. Check if we should do a staggered sync
+ if (Preferences.getPreferences(context).getProxyPrefs().torEnabled) {
+ return staggeredUpdate(context, keyList, cryptoInputParcel);
+ } else {
+ return directUpdate(context, keyList, cryptoInputParcel);
+ }
+ } else {
+ return directUpdate(context, keyList, cryptoInputParcel);
+ }
+ }
+
+ private ImportKeyResult directUpdate(Context context, ArrayList<ParcelableKeyRing> keyList,
+ CryptoInputParcel cryptoInputParcel) {
+ Log.d(Constants.TAG, "Starting normal update");
+ ImportOperation importOp = new ImportOperation(context, new ProviderHelper(context), null);
+ return importOp.execute(
+ new ImportKeyringParcel(keyList,
+ Preferences.getPreferences(context).getPreferredKeyserver()),
+ cryptoInputParcel
+ );
+ }
+
+
+ /**
+ * will perform a staggered update of user's keys using delays to ensure new Tor circuits, as
+ * performed by parcimonie. Relevant issue and method at:
+ * https://github.com/open-keychain/open-keychain/issues/1337
+ *
+ * @return result of the sync
+ */
+ private ImportKeyResult staggeredUpdate(Context context, ArrayList<ParcelableKeyRing> keyList,
+ CryptoInputParcel cryptoInputParcel) {
+ Log.d(Constants.TAG, "Starting staggered update");
+ // final int WEEK_IN_SECONDS = (int) TimeUnit.DAYS.toSeconds(7);
+ final int WEEK_IN_SECONDS = 0;
+ ImportOperation.KeyImportAccumulator accumulator
+ = new ImportOperation.KeyImportAccumulator(keyList.size(), null);
+ for (ParcelableKeyRing keyRing : keyList) {
+ int waitTime;
+ int staggeredTime = new Random().nextInt(1 + 2 * (WEEK_IN_SECONDS / keyList.size()));
+ if (staggeredTime >= ORBOT_CIRCUIT_TIMEOUT) {
+ waitTime = staggeredTime;
+ } else {
+ waitTime = ORBOT_CIRCUIT_TIMEOUT + new Random().nextInt(ORBOT_CIRCUIT_TIMEOUT);
+ }
+ Log.d(Constants.TAG, "Updating key with fingerprint " + keyRing.mExpectedFingerprint +
+ " with a wait time of " + waitTime + "s");
+ try {
+ Thread.sleep(waitTime * 1000);
+ } catch (InterruptedException e) {
+ Log.e(Constants.TAG, "Exception during sleep between key updates", e);
+ // skip this one
+ continue;
+ }
+ ArrayList<ParcelableKeyRing> keyWrapper = new ArrayList<>();
+ keyWrapper.add(keyRing);
+ if (isUpdateCancelled()) {
+ return new ImportKeyResult(ImportKeyResult.RESULT_CANCELLED,
+ new OperationResult.OperationLog());
+ }
+ ImportKeyResult result =
+ new ImportOperation(context, new ProviderHelper(context), null, mCancelled)
+ .execute(
+ new ImportKeyringParcel(
+ keyWrapper,
+ Preferences.getPreferences(context)
+ .getPreferredKeyserver()
+ ),
+ cryptoInputParcel
+ );
+ if (result.isPending()) {
+ return result;
+ }
+ accumulator.accumulateKeyImport(result);
+ }
+ return accumulator.getConsolidatedResult();
+ }
+
+ /**
+ * 1. Get keys which have been updated recently and therefore do not need to
+ * be updated now
+ * 2. Get list of all keys and filter out ones that don't need to be updated
+ * 3. Return keys to be updated
+ *
+ * @return list of keys that require update
+ */
+ private ArrayList<ParcelableKeyRing> getKeysToUpdate(Context context) {
+
+ // 1. Get keys which have been updated recently and don't need to updated now
+ final int INDEX_UPDATED_KEYS_MASTER_KEY_ID = 0;
+ final int INDEX_LAST_UPDATED = 1;
+
+ // all time in seconds not milliseconds
+ final long CURRENT_TIME = GregorianCalendar.getInstance().getTimeInMillis() / 1000;
+ Cursor updatedKeysCursor = context.getContentResolver().query(
+ KeychainContract.UpdatedKeys.CONTENT_URI,
+ new String[]{
+ KeychainContract.UpdatedKeys.MASTER_KEY_ID,
+ KeychainContract.UpdatedKeys.LAST_UPDATED
+ },
+ "? - " + KeychainContract.UpdatedKeys.LAST_UPDATED + " < " + KEY_UPDATE_LIMIT,
+ new String[]{"" + CURRENT_TIME},
+ null
+ );
+
+ ArrayList<Long> ignoreMasterKeyIds = new ArrayList<>();
+ while (updatedKeysCursor.moveToNext()) {
+ long masterKeyId = updatedKeysCursor.getLong(INDEX_UPDATED_KEYS_MASTER_KEY_ID);
+ Log.d(Constants.TAG, "Keyserver sync: Ignoring {" + masterKeyId + "} last updated at {"
+ + updatedKeysCursor.getLong(INDEX_LAST_UPDATED) + "}s");
+ ignoreMasterKeyIds.add(masterKeyId);
+ }
+ updatedKeysCursor.close();
+
+ // 2. Make a list of public keys which should be updated
+ final int INDEX_MASTER_KEY_ID = 0;
+ final int INDEX_FINGERPRINT = 1;
+ Cursor keyCursor = context.getContentResolver().query(
+ KeychainContract.KeyRings.buildUnifiedKeyRingsUri(),
+ new String[]{
+ KeychainContract.KeyRings.MASTER_KEY_ID,
+ KeychainContract.KeyRings.FINGERPRINT
+ },
+ null,
+ null,
+ null
+ );
+
+ if (keyCursor == null) {
+ return new ArrayList<>();
+ }
+
+ ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
+ while (keyCursor.moveToNext()) {
+ long keyId = keyCursor.getLong(INDEX_MASTER_KEY_ID);
+ if (ignoreMasterKeyIds.contains(keyId)) {
+ continue;
+ }
+ Log.d(Constants.TAG, "Keyserver sync: Updating {" + keyId + "}");
+ String fingerprint = KeyFormattingUtils
+ .convertFingerprintToHex(keyCursor.getBlob(INDEX_FINGERPRINT));
+ String hexKeyId = KeyFormattingUtils
+ .convertKeyIdToHex(keyId);
+ // we aren't updating from keybase as of now
+ keyList.add(new ParcelableKeyRing(fingerprint, hexKeyId, null));
+ }
+ keyCursor.close();
+
+ return keyList;
+ }
+
+ private boolean isUpdateCancelled() {
+ return mCancelled.get();
+ }
+
+ /**
+ * will cancel an update already in progress. We send an Intent to cancel it instead of simply
+ * modifying a static variable sync the service is running in a process that is different from
+ * the default application process where the UI code runs.
+ *
+ * @param context used to send an Intent to the service requesting cancellation.
+ */
+ public static void cancelUpdates(Context context) {
+ Intent intent = new Intent(context, KeyserverSyncAdapterService.class);
+ intent.setAction(ACTION_CANCEL);
+ context.startService(intent);
+ }
+
+ private Notification getOrbotNoification(Context context) {
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+ builder.setSmallIcon(R.drawable.ic_stat_notify_24dp)
+ .setLargeIcon(getBitmap(R.drawable.ic_launcher, context))
+ .setContentTitle(context.getString(R.string.keyserver_sync_orbot_notif_title))
+ .setContentText(context.getString(R.string.keyserver_sync_orbot_notif_msg))
+ .setAutoCancel(true);
+
+ // In case the user decides to not use tor
+ Intent ignoreTorIntent = new Intent(context, KeyserverSyncAdapterService.class);
+ ignoreTorIntent.setAction(ACTION_IGNORE_TOR);
+ PendingIntent ignoreTorPi = PendingIntent.getService(
+ context,
+ 0, // security not issue since we're giving this pending intent to Notification Manager
+ ignoreTorIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT
+ );
+
+ builder.addAction(R.drawable.ic_stat_tor_off,
+ context.getString(R.string.keyserver_sync_orbot_notif_ignore),
+ ignoreTorPi);
+
+ Intent startOrbotIntent = new Intent(context, KeyserverSyncAdapterService.class);
+ startOrbotIntent.setAction(ACTION_START_ORBOT);
+ PendingIntent startOrbotPi = PendingIntent.getService(
+ context,
+ 0, // security not issue since we're giving this pending intent to Notification Manager
+ startOrbotIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT
+ );
+
+ builder.addAction(R.drawable.ic_stat_tor,
+ context.getString(R.string.keyserver_sync_orbot_notif_start),
+ startOrbotPi
+ );
+ builder.setContentIntent(startOrbotPi);
+
+ return builder.build();
+ }
+
+ public static void enableKeyserverSync(Context context) {
+ try {
+ AccountManager manager = AccountManager.get(context);
+ Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE);
+
+ Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
+ if (accounts.length == 0) {
+ if (!manager.addAccountExplicitly(account, null, null)) {
+ Log.e(Constants.TAG, "Adding account failed!");
+ }
+ }
+ // for keyserver sync
+ ContentResolver.setIsSyncable(account, Constants.PROVIDER_AUTHORITY, 1);
+ ContentResolver.setSyncAutomatically(account, Constants.PROVIDER_AUTHORITY,
+ true);
+ ContentResolver.addPeriodicSync(
+ account,
+ Constants.PROVIDER_AUTHORITY,
+ new Bundle(),
+ SYNC_INTERVAL
+ );
+ } catch (SecurityException e) {
+ Log.e(Constants.TAG, "SecurityException when adding the account", e);
+ Toast.makeText(context, R.string.reinstall_openkeychain, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android
+ private Bitmap getBitmap(int resId, Context context) {
+ int mLargeIconWidth = (int) context.getResources().getDimension(
+ android.R.dimen.notification_large_icon_width);
+ int mLargeIconHeight = (int) context.getResources().getDimension(
+ android.R.dimen.notification_large_icon_height);
+ Drawable d;
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ // noinspection deprecation (can't help it at this api level)
+ d = context.getResources().getDrawable(resId);
+ } else {
+ d = context.getDrawable(resId);
+ }
+ if (d == null) {
+ return null;
+ }
+ Bitmap b = Bitmap.createBitmap(mLargeIconWidth, mLargeIconHeight, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ d.setBounds(0, 0, mLargeIconWidth, mLargeIconHeight);
+ d.draw(c);
+ return b;
+ }
+}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
index 78137170d..be269c66d 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java
@@ -17,6 +17,9 @@
package org.sufficientlysecure.keychain.service;
+
+import java.util.Date;
+
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
@@ -25,8 +28,13 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
import android.os.Binder;
import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
@@ -46,8 +54,6 @@ import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences;
-import java.util.Date;
-
/**
* This service runs in its own process, but is available to all other processes as the main
* passphrase cache. Use the static methods addCachedPassphrase and getCachedPassphrase for
@@ -95,8 +101,6 @@ public class PassphraseCacheService extends Service {
private static final long DEFAULT_TTL = 15;
- 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_NOT_FOUND = 2;
@@ -149,6 +153,14 @@ public class PassphraseCacheService extends Service {
context.startService(intent);
}
+ public static void clearCachedPassphrases(Context context) {
+ Log.d(Constants.TAG, "PassphraseCacheService.clearCachedPassphrase()");
+
+ Intent intent = new Intent(context, PassphraseCacheService.class);
+ intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
+
+ context.startService(intent);
+ }
/**
* Gets a cached passphrase from memory by sending an intent to the service. This method is
@@ -218,21 +230,21 @@ public class PassphraseCacheService extends Service {
* Internal implementation to get cached passphrase.
*/
private Passphrase getCachedPassphraseImpl(long masterKeyId, long subKeyId) throws ProviderHelper.NotFoundException {
+ // on "none" key, just do nothing
+ if (masterKeyId == Constants.key.none) {
+ return null;
+ }
+
// passphrase for symmetric encryption?
if (masterKeyId == Constants.key.symmetric) {
Log.d(Constants.TAG, "PassphraseCacheService.getCachedPassphraseImpl() for symmetric encryption");
- Passphrase cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric).getPassphrase();
+ CachedPassphrase cachedPassphrase = mPassphraseCache.get(Constants.key.symmetric);
if (cachedPassphrase == null) {
return null;
}
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 (masterKeyId == Constants.key.none) {
- return null;
+ cachedPassphrase.getPassphrase(), getString(R.string.passp_cache_notif_pwd));
+ return cachedPassphrase.getPassphrase();
}
// try to get master key id which is used as an identifier for cached passphrases
@@ -309,7 +321,7 @@ public class PassphraseCacheService extends Service {
if (action.equals(BROADCAST_ACTION_PASSPHRASE_CACHE_SERVICE)) {
long keyId = intent.getLongExtra(EXTRA_KEY_ID, -1);
- timeout(context, keyId);
+ timeout(keyId);
}
}
};
@@ -411,9 +423,9 @@ public class PassphraseCacheService extends Service {
long referenceKeyId;
if (Preferences.getPreferences(mContext).getPassphraseCacheSubs()) {
- referenceKeyId = intent.getLongExtra(EXTRA_KEY_ID, 0L);
- } else {
referenceKeyId = intent.getLongExtra(EXTRA_SUBKEY_ID, 0L);
+ } else {
+ referenceKeyId = intent.getLongExtra(EXTRA_KEY_ID, 0L);
}
// Stop specific ttl alarm and
am.cancel(buildIntent(this, referenceKeyId));
@@ -444,12 +456,17 @@ public class PassphraseCacheService extends Service {
/**
* Called when one specific passphrase for keyId timed out
*/
- private void timeout(Context context, long keyId) {
+ private void timeout(long keyId) {
+
CachedPassphrase cPass = mPassphraseCache.get(keyId);
- // clean internal char[] from memory!
- cPass.getPassphrase().removeFromMemory();
- // remove passphrase object
- mPassphraseCache.remove(keyId);
+ if (cPass != null) {
+ if (cPass.getPassphrase() != null) {
+ // clean internal char[] from memory!
+ cPass.getPassphrase().removeFromMemory();
+ }
+ // remove passphrase object
+ mPassphraseCache.remove(keyId);
+ }
Log.d(Constants.TAG, "PassphraseCacheService Timeout of keyId " + keyId + ", removed from memory!");
@@ -458,7 +475,7 @@ public class PassphraseCacheService extends Service {
private void updateService() {
if (mPassphraseCache.size() > 0) {
- startForeground(NOTIFICATION_ID, getNotification());
+ startForeground(Constants.Notification.PASSPHRASE_CACHE, getNotification());
} else {
// stop whole service if no cached passphrases remaining
Log.d(Constants.TAG, "PassphraseCacheService: No passphrases remaining in memory, stopping service!");
@@ -466,59 +483,67 @@ public class PassphraseCacheService extends Service {
}
}
+ // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android
+ private static Bitmap getBitmap(int resId, Context context) {
+ int mLargeIconWidth = (int) context.getResources().getDimension(
+ android.R.dimen.notification_large_icon_width);
+ int mLargeIconHeight = (int) context.getResources().getDimension(
+ android.R.dimen.notification_large_icon_height);
+ Drawable d;
+ if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
+ // noinspection deprecation (can't help it at this api level)
+ d = context.getResources().getDrawable(resId);
+ } else {
+ d = context.getDrawable(resId);
+ }
+ if (d == null) {
+ return null;
+ }
+ Bitmap b = Bitmap.createBitmap(mLargeIconWidth, mLargeIconHeight, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ d.setBounds(0, 0, mLargeIconWidth, mLargeIconHeight);
+ d.draw(c);
+ return b;
+ }
+
private Notification getNotification() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
+ builder.setSmallIcon(R.drawable.ic_stat_notify_24dp)
+ .setLargeIcon(getBitmap(R.drawable.ic_launcher, getBaseContext()))
+ .setContentTitle(getResources().getQuantityString(R.plurals.passp_cache_notif_n_keys,
+ mPassphraseCache.size(), mPassphraseCache.size()))
+ .setContentText(getString(R.string.passp_cache_notif_click_to_clear));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- builder.setSmallIcon(R.drawable.ic_launcher)
- .setContentTitle(getString(R.string.app_name))
- .setContentText(String.format(getString(R.string.passp_cache_notif_n_keys),
- mPassphraseCache.size()));
+ NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
- NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
+ inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys));
- inboxStyle.setBigContentTitle(getString(R.string.passp_cache_notif_keys));
+ // Moves events into the big view
+ for (int i = 0; i < mPassphraseCache.size(); i++) {
+ inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID());
+ }
- // Moves events into the big view
- for (int i = 0; i < mPassphraseCache.size(); i++) {
- inboxStyle.addLine(mPassphraseCache.valueAt(i).getPrimaryUserID());
- }
+ // Moves the big view style object into the notification object.
+ builder.setStyle(inboxStyle);
- // Moves the big view style object into the notification object.
- builder.setStyle(inboxStyle);
-
- // Add purging action
- Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
- intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
- builder.addAction(
- R.drawable.abc_ic_clear_mtrl_alpha,
- getString(R.string.passp_cache_notif_clear),
- PendingIntent.getService(
- getApplicationContext(),
- 0,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT
- )
- );
- } else {
- // Fallback, since expandable notifications weren't available back then
- builder.setSmallIcon(R.drawable.ic_launcher)
- .setContentTitle(String.format(getString(R.string.passp_cache_notif_n_keys),
- mPassphraseCache.size()))
- .setContentText(getString(R.string.passp_cache_notif_click_to_clear));
-
- Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
- intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
-
- builder.setContentIntent(
- PendingIntent.getService(
- getApplicationContext(),
- 0,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT
- )
- );
- }
+ Intent intent = new Intent(getApplicationContext(), PassphraseCacheService.class);
+ intent.setAction(ACTION_PASSPHRASE_CACHE_CLEAR);
+ PendingIntent clearCachePi = PendingIntent.getService(
+ getApplicationContext(),
+ 0,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT
+ );
+
+ // Add cache clear PI to normal touch
+ builder.setContentIntent(clearCachePi);
+
+ // Add clear PI action below text
+ builder.addAction(
+ R.drawable.abc_ic_clear_mtrl_alpha,
+ getString(R.string.passp_cache_notif_clear),
+ clearCachePi
+ );
return builder.build();
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java
new file mode 100644
index 000000000..d268c3694
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class PromoteKeyringParcel implements Parcelable {
+
+ public long mKeyRingId;
+ public byte[] mCardAid;
+ public long[] mSubKeyIds;
+
+ public PromoteKeyringParcel(long keyRingId, byte[] cardAid, long[] subKeyIds) {
+ mKeyRingId = keyRingId;
+ mCardAid = cardAid;
+ mSubKeyIds = subKeyIds;
+ }
+
+ protected PromoteKeyringParcel(Parcel in) {
+ mKeyRingId = in.readLong();
+ mCardAid = in.createByteArray();
+ mSubKeyIds = in.createLongArray();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mKeyRingId);
+ dest.writeByteArray(mCardAid);
+ dest.writeLongArray(mSubKeyIds);
+ }
+
+ public static final Parcelable.Creator<PromoteKeyringParcel> CREATOR = new Parcelable.Creator<PromoteKeyringParcel>() {
+ @Override
+ public PromoteKeyringParcel createFromParcel(Parcel in) {
+ return new PromoteKeyringParcel(in);
+ }
+
+ @Override
+ public PromoteKeyringParcel[] newArray(int size) {
+ return new PromoteKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java
new file mode 100644
index 000000000..02e8fda46
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013-2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
+ * Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
+ *
+ * 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.service;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class RevokeKeyringParcel implements Parcelable {
+
+ final public long mMasterKeyId;
+ final public boolean mUpload;
+ final public String mKeyserver;
+
+ public RevokeKeyringParcel(long masterKeyId, boolean upload, String keyserver) {
+ mMasterKeyId = masterKeyId;
+ mUpload = upload;
+ mKeyserver = keyserver;
+ }
+
+ protected RevokeKeyringParcel(Parcel in) {
+ mMasterKeyId = in.readLong();
+ mUpload = in.readByte() != 0x00;
+ mKeyserver = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mMasterKeyId);
+ dest.writeByte((byte) (mUpload ? 0x01 : 0x00));
+ dest.writeString(mKeyserver);
+ }
+
+ public static final Parcelable.Creator<RevokeKeyringParcel> CREATOR = new Parcelable.Creator<RevokeKeyringParcel>() {
+ @Override
+ public RevokeKeyringParcel createFromParcel(Parcel in) {
+ return new RevokeKeyringParcel(in);
+ }
+
+ @Override
+ public RevokeKeyringParcel[] newArray(int size) {
+ return new RevokeKeyringParcel[size];
+ }
+ };
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
index 2e0524141..6959fca56 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java
@@ -61,6 +61,15 @@ public class SaveKeyringParcel implements Parcelable {
public ArrayList<String> mRevokeUserIds;
public ArrayList<Long> mRevokeSubKeys;
+ // if these are non-null, PINs will be changed on the card
+ public Passphrase mCardPin;
+ public Passphrase mCardAdminPin;
+
+ // private because they have to be set together with setUpdateOptions
+ private boolean mUpload;
+ private boolean mUploadAtomic;
+ private String mKeyserver;
+
public SaveKeyringParcel() {
reset();
}
@@ -80,6 +89,29 @@ public class SaveKeyringParcel implements Parcelable {
mChangeSubKeys = new ArrayList<>();
mRevokeUserIds = new ArrayList<>();
mRevokeSubKeys = new ArrayList<>();
+ mCardPin = null;
+ mCardAdminPin = null;
+ mUpload = false;
+ mUploadAtomic = false;
+ mKeyserver = null;
+ }
+
+ public void setUpdateOptions(boolean upload, boolean uploadAtomic, String keysever) {
+ mUpload = upload;
+ mUploadAtomic = uploadAtomic;
+ mKeyserver = keysever;
+ }
+
+ public boolean isUpload() {
+ return mUpload;
+ }
+
+ public boolean isUploadAtomic() {
+ return mUploadAtomic;
+ }
+
+ public String getUploadKeyserver() {
+ return mKeyserver;
}
public boolean isEmpty() {
@@ -95,7 +127,8 @@ public class SaveKeyringParcel implements Parcelable {
}
for (SubkeyChange change : mChangeSubKeys) {
- if (change.mRecertify || change.mFlags != null || change.mExpiry != null) {
+ if (change.mRecertify || change.mFlags != null || change.mExpiry != null
+ || change.mMoveKeyToCard) {
return false;
}
}
@@ -142,6 +175,8 @@ public class SaveKeyringParcel implements Parcelable {
public boolean mRecertify;
// if this flag is true, the subkey should be changed to a stripped key
public boolean mDummyStrip;
+ // if this flag is true, the subkey should be moved to a card
+ public boolean mMoveKeyToCard;
// if this is non-null, the subkey will be changed to a divert-to-card
// key for the given serial number
public byte[] mDummyDivert;
@@ -161,16 +196,16 @@ public class SaveKeyringParcel implements Parcelable {
mExpiry = expiry;
}
- public SubkeyChange(long keyId, boolean dummyStrip, byte[] dummyDivert) {
+ public SubkeyChange(long keyId, boolean dummyStrip, boolean moveKeyToCard) {
this(keyId, null, null);
// these flags are mutually exclusive!
- if (dummyStrip && dummyDivert != null) {
+ if (dummyStrip && moveKeyToCard) {
throw new AssertionError(
- "cannot set strip and divert flags at the same time - this is a bug!");
+ "cannot set strip and keytocard flags at the same time - this is a bug!");
}
mDummyStrip = dummyStrip;
- mDummyDivert = dummyDivert;
+ mMoveKeyToCard = moveKeyToCard;
}
@Override
@@ -179,6 +214,7 @@ public class SaveKeyringParcel implements Parcelable {
out += "mFlags: " + mFlags + ", ";
out += "mExpiry: " + mExpiry + ", ";
out += "mDummyStrip: " + mDummyStrip + ", ";
+ out += "mMoveKeyToCard: " + mMoveKeyToCard + ", ";
out += "mDummyDivert: [" + (mDummyDivert == null ? 0 : mDummyDivert.length) + " bytes]";
return out;
@@ -206,6 +242,7 @@ public class SaveKeyringParcel implements Parcelable {
}
}
+ @SuppressWarnings("unchecked") // we verify the reads against writes in writeToParcel
public SaveKeyringParcel(Parcel source) {
mMasterKeyId = source.readInt() != 0 ? source.readLong() : null;
mFingerprint = source.createByteArray();
@@ -221,6 +258,13 @@ public class SaveKeyringParcel implements Parcelable {
mRevokeUserIds = source.createStringArrayList();
mRevokeSubKeys = (ArrayList<Long>) source.readSerializable();
+
+ mCardPin = source.readParcelable(Passphrase.class.getClassLoader());
+ mCardAdminPin = source.readParcelable(Passphrase.class.getClassLoader());
+
+ mUpload = source.readByte() != 0;
+ mUploadAtomic = source.readByte() != 0;
+ mKeyserver = source.readString();
}
@Override
@@ -232,7 +276,7 @@ public class SaveKeyringParcel implements Parcelable {
destination.writeByteArray(mFingerprint);
// yes, null values are ok for parcelables
- destination.writeParcelable(mNewUnlock, 0);
+ destination.writeParcelable(mNewUnlock, flags);
destination.writeStringList(mAddUserIds);
destination.writeSerializable(mAddUserAttribute);
@@ -243,6 +287,13 @@ public class SaveKeyringParcel implements Parcelable {
destination.writeStringList(mRevokeUserIds);
destination.writeSerializable(mRevokeSubKeys);
+
+ destination.writeParcelable(mCardPin, flags);
+ destination.writeParcelable(mCardAdminPin, flags);
+
+ destination.writeByte((byte) (mUpload ? 1 : 0));
+ destination.writeByte((byte) (mUploadAtomic ? 1 : 0));
+ destination.writeString(mKeyserver);
}
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {
@@ -270,7 +321,9 @@ public class SaveKeyringParcel implements Parcelable {
out += "mChangeSubKeys: " + mChangeSubKeys + "\n";
out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n";
out += "mRevokeUserIds: " + mRevokeUserIds + "\n";
- out += "mRevokeSubKeys: " + mRevokeSubKeys;
+ out += "mRevokeSubKeys: " + mRevokeSubKeys + "\n";
+ out += "mCardPin: " + mCardPin + "\n";
+ out += "mCardAdminPin: " + mCardAdminPin;
return out;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
index 430d8a49b..d294e5057 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java
@@ -17,8 +17,8 @@
package org.sufficientlysecure.keychain.service;
-import android.app.Activity;
-import android.content.Intent;
+
+import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -27,7 +27,6 @@ import android.support.v4.app.FragmentManager;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.operations.results.CertifyResult;
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
@@ -35,7 +34,7 @@ import org.sufficientlysecure.keychain.util.Log;
public class ServiceProgressHandler extends Handler {
// possible messages sent from this service to handler on ui
- public static enum MessageStatus{
+ public enum MessageStatus {
UNKNOWN,
OKAY,
EXCEPTION,
@@ -44,9 +43,8 @@ public class ServiceProgressHandler extends Handler {
private static final MessageStatus[] values = values();
- public static MessageStatus fromInt(int n)
- {
- if(n < 0 || n >= values.length) {
+ public static MessageStatus fromInt(int n) {
+ if (n < 0 || n >= values.length) {
return UNKNOWN;
} else {
return values[n];
@@ -66,74 +64,51 @@ public class ServiceProgressHandler extends Handler {
public static final String KEYBASE_PRESENCE_URL = "keybase_presence_url";
public static final String KEYBASE_PRESENCE_LABEL = "keybase_presence_label";
- Activity mActivity;
- ProgressDialogFragment mProgressDialogFragment;
+ public static final String TAG_PROGRESS_DIALOG = "progressDialog";
- public ServiceProgressHandler(Activity activity) {
- this.mActivity = activity;
- }
+ FragmentActivity mActivity;
- public ServiceProgressHandler(Activity activity,
- ProgressDialogFragment progressDialogFragment) {
- this.mActivity = activity;
- this.mProgressDialogFragment = progressDialogFragment;
+ public ServiceProgressHandler(FragmentActivity activity) {
+ mActivity = activity;
}
- public ServiceProgressHandler(Activity activity,
- String progressDialogMessage,
- int progressDialogStyle,
- ProgressDialogFragment.ServiceType serviceType) {
- this(activity, progressDialogMessage, progressDialogStyle, false, serviceType);
+ public void showProgressDialog() {
+ showProgressDialog("", ProgressDialog.STYLE_SPINNER, false);
}
- public ServiceProgressHandler(Activity activity,
- String progressDialogMessage,
- int progressDialogStyle,
- boolean cancelable,
- ProgressDialogFragment.ServiceType serviceType) {
- this.mActivity = activity;
- this.mProgressDialogFragment = ProgressDialogFragment.newInstance(
+ public void showProgressDialog(
+ String progressDialogMessage, int progressDialogStyle, boolean cancelable) {
+
+ final ProgressDialogFragment frag = ProgressDialogFragment.newInstance(
progressDialogMessage,
progressDialogStyle,
- cancelable,
- serviceType);
- }
-
- public void showProgressDialog(FragmentActivity activity) {
- if (mProgressDialogFragment == null) {
- return;
- }
+ cancelable);
// TODO: This is a hack!, see
// http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
- final FragmentManager manager = activity.getSupportFragmentManager();
+ final FragmentManager manager = mActivity.getSupportFragmentManager();
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
- mProgressDialogFragment.show(manager, "progressDialog");
+ frag.show(manager, TAG_PROGRESS_DIALOG);
}
});
+
}
@Override
public void handleMessage(Message message) {
Bundle data = message.getData();
- if (mProgressDialogFragment == null) {
- // Log.e(Constants.TAG,
- // "Progress has not been updated because mProgressDialogFragment was null!");
- return;
- }
-
MessageStatus status = MessageStatus.fromInt(message.arg1);
switch (status) {
case OKAY:
- mProgressDialogFragment.dismissAllowingStateLoss();
+ dismissAllowingStateLoss();
break;
case EXCEPTION:
- mProgressDialogFragment.dismissAllowingStateLoss();
+ dismissAllowingStateLoss();
// show error from service
if (data.containsKey(DATA_ERROR)) {
@@ -147,23 +122,25 @@ public class ServiceProgressHandler extends Handler {
case UPDATE_PROGRESS:
if (data.containsKey(DATA_PROGRESS) && data.containsKey(DATA_PROGRESS_MAX)) {
+ String msg = null;
+ int progress = data.getInt(DATA_PROGRESS);
+ int max = data.getInt(DATA_PROGRESS_MAX);
+
// update progress from service
if (data.containsKey(DATA_MESSAGE)) {
- mProgressDialogFragment.setProgress(data.getString(DATA_MESSAGE),
- data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
+ msg = data.getString(DATA_MESSAGE);
} else if (data.containsKey(DATA_MESSAGE_ID)) {
- mProgressDialogFragment.setProgress(data.getInt(DATA_MESSAGE_ID),
- data.getInt(DATA_PROGRESS), data.getInt(DATA_PROGRESS_MAX));
- } else {
- mProgressDialogFragment.setProgress(data.getInt(DATA_PROGRESS),
- data.getInt(DATA_PROGRESS_MAX));
+ msg = mActivity.getString(data.getInt(DATA_MESSAGE_ID));
}
+
+ onSetProgress(msg, progress, max);
+
}
break;
case PREVENT_CANCEL:
- mProgressDialogFragment.setPreventCancel(true);
+ setPreventCancel(true);
break;
default:
@@ -171,4 +148,48 @@ public class ServiceProgressHandler extends Handler {
break;
}
}
+
+ private void setPreventCancel(boolean preventCancel) {
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) mActivity.getSupportFragmentManager()
+ .findFragmentByTag("progressDialog");
+
+ if (progressDialogFragment == null) {
+ return;
+ }
+
+ progressDialogFragment.setPreventCancel(preventCancel);
+ }
+
+ protected void dismissAllowingStateLoss() {
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) mActivity.getSupportFragmentManager()
+ .findFragmentByTag("progressDialog");
+
+ if (progressDialogFragment == null) {
+ return;
+ }
+
+ progressDialogFragment.dismissAllowingStateLoss();
+ }
+
+
+ protected void onSetProgress(String msg, int progress, int max) {
+
+ ProgressDialogFragment progressDialogFragment =
+ (ProgressDialogFragment) mActivity.getSupportFragmentManager()
+ .findFragmentByTag("progressDialog");
+
+ if (progressDialogFragment == null) {
+ return;
+ }
+
+ if (msg != null) {
+ progressDialogFragment.setProgress(msg, progress, max);
+ } else {
+ progressDialogFragment.setProgress(progress, max);
+ }
+
+ }
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
index 3d1ccaca1..0d8569fe6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/CryptoInputParcel.java
@@ -17,52 +17,87 @@
package org.sufficientlysecure.keychain.service.input;
-import java.nio.ByteBuffer;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
import android.os.Parcel;
import android.os.Parcelable;
+import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Passphrase;
+import java.nio.ByteBuffer;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* This is a base class for the input of crypto operations.
*/
public class CryptoInputParcel implements Parcelable {
- final Date mSignatureTime;
- final Passphrase mPassphrase;
+ private Date mSignatureTime;
+ private boolean mHasSignature;
+
+ public Passphrase mPassphrase;
+ // used to supply an explicit proxy to operations that require it
+ // this is not final so it can be added to an existing CryptoInputParcel
+ // (e.g) CertifyOperation with upload might require both passphrase and orbot to be enabled
+ private ParcelableProxy mParcelableProxy;
+
+ // specifies whether passphrases should be cached
+ public boolean mCachePassphrase = true;
// this map contains both decrypted session keys and signed hashes to be
// used in the crypto operation described by this parcel.
private HashMap<ByteBuffer, byte[]> mCryptoData = new HashMap<>();
public CryptoInputParcel() {
- mSignatureTime = new Date();
+ mSignatureTime = null;
mPassphrase = null;
+ mCachePassphrase = true;
}
public CryptoInputParcel(Date signatureTime, Passphrase passphrase) {
+ mHasSignature = true;
mSignatureTime = signatureTime == null ? new Date() : signatureTime;
mPassphrase = passphrase;
+ mCachePassphrase = true;
}
public CryptoInputParcel(Passphrase passphrase) {
- mSignatureTime = new Date();
mPassphrase = passphrase;
+ mCachePassphrase = true;
}
public CryptoInputParcel(Date signatureTime) {
+ mHasSignature = true;
mSignatureTime = signatureTime == null ? new Date() : signatureTime;
mPassphrase = null;
+ mCachePassphrase = true;
+ }
+
+ public CryptoInputParcel(ParcelableProxy parcelableProxy) {
+ this();
+ mParcelableProxy = parcelableProxy;
+ }
+
+ public CryptoInputParcel(Date signatureTime, boolean cachePassphrase) {
+ mHasSignature = true;
+ mSignatureTime = signatureTime == null ? new Date() : signatureTime;
+ mPassphrase = null;
+ mCachePassphrase = cachePassphrase;
+ }
+
+ public CryptoInputParcel(boolean cachePassphrase) {
+ mCachePassphrase = cachePassphrase;
}
protected CryptoInputParcel(Parcel source) {
- mSignatureTime = new Date(source.readLong());
+ mHasSignature = source.readByte() != 0;
+ if (mHasSignature) {
+ mSignatureTime = new Date(source.readLong());
+ }
mPassphrase = source.readParcelable(getClass().getClassLoader());
+ mParcelableProxy = source.readParcelable(getClass().getClassLoader());
+ mCachePassphrase = source.readByte() != 0;
{
int count = source.readInt();
@@ -83,8 +118,13 @@ public class CryptoInputParcel implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(mSignatureTime.getTime());
+ dest.writeByte((byte) (mHasSignature ? 1 : 0));
+ if (mHasSignature) {
+ dest.writeLong(mSignatureTime.getTime());
+ }
dest.writeParcelable(mPassphrase, 0);
+ dest.writeParcelable(mParcelableProxy, 0);
+ dest.writeByte((byte) (mCachePassphrase ? 1 : 0));
dest.writeInt(mCryptoData.size());
for (HashMap.Entry<ByteBuffer, byte[]> entry : mCryptoData.entrySet()) {
@@ -93,12 +133,28 @@ public class CryptoInputParcel implements Parcelable {
}
}
+ public void addParcelableProxy(ParcelableProxy parcelableProxy) {
+ mParcelableProxy = parcelableProxy;
+ }
+
+ public void addSignatureTime(Date signatureTime) {
+ mSignatureTime = signatureTime;
+ }
+
public void addCryptoData(byte[] hash, byte[] signedHash) {
mCryptoData.put(ByteBuffer.wrap(hash), signedHash);
}
+ public void addCryptoData(Map<ByteBuffer, byte[]> cachedSessionKeys) {
+ mCryptoData.putAll(cachedSessionKeys);
+ }
+
+ public ParcelableProxy getParcelableProxy() {
+ return mParcelableProxy;
+ }
+
public Map<ByteBuffer, byte[]> getCryptoData() {
- return Collections.unmodifiableMap(mCryptoData);
+ return mCryptoData;
}
public Date getSignatureTime() {
@@ -138,4 +194,5 @@ public class CryptoInputParcel implements Parcelable {
b.append("}");
return b.toString();
}
+
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
index 535c1e735..e4dac3227 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/input/RequiredInputParcel.java
@@ -1,35 +1,37 @@
package org.sufficientlysecure.keychain.service.input;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-
import android.os.Parcel;
import android.os.Parcelable;
-import org.sufficientlysecure.keychain.Constants.key;
+import org.sufficientlysecure.keychain.util.Passphrase;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
public class RequiredInputParcel implements Parcelable {
public enum RequiredInputType {
- PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT
+ PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_MOVE_KEY_TO_CARD, ENABLE_ORBOT,
+ UPLOAD_FAIL_RETRY
}
public Date mSignatureTime;
public final RequiredInputType mType;
- public final byte[][] mInputHashes;
+ public final byte[][] mInputData;
public final int[] mSignAlgos;
private Long mMasterKeyId;
private Long mSubKeyId;
- private RequiredInputParcel(RequiredInputType type, byte[][] inputHashes,
+ private RequiredInputParcel(RequiredInputType type, byte[][] inputData,
int[] signAlgos, Date signatureTime, Long masterKeyId, Long subKeyId) {
mType = type;
- mInputHashes = inputHashes;
+ mInputData = inputData;
mSignAlgos = signAlgos;
mSignatureTime = signatureTime;
mMasterKeyId = masterKeyId;
@@ -39,25 +41,25 @@ public class RequiredInputParcel implements Parcelable {
public RequiredInputParcel(Parcel source) {
mType = RequiredInputType.values()[source.readInt()];
- // 0 = none, 1 = both, 2 = only hashes (decrypt)
- int hashTypes = source.readInt();
- if (hashTypes != 0) {
+ // 0 = none, 1 = signAlgos + inputData, 2 = only inputData (decrypt)
+ int inputDataType = source.readInt();
+ if (inputDataType != 0) {
int count = source.readInt();
- mInputHashes = new byte[count][];
- if (hashTypes == 1) {
+ mInputData = new byte[count][];
+ if (inputDataType == 1) {
mSignAlgos = new int[count];
for (int i = 0; i < count; i++) {
- mInputHashes[i] = source.createByteArray();
+ mInputData[i] = source.createByteArray();
mSignAlgos[i] = source.readInt();
}
} else {
mSignAlgos = null;
for (int i = 0; i < count; i++) {
- mInputHashes[i] = source.createByteArray();
+ mInputData[i] = source.createByteArray();
}
}
} else {
- mInputHashes = null;
+ mInputData = null;
mSignAlgos = null;
}
@@ -75,16 +77,27 @@ public class RequiredInputParcel implements Parcelable {
return mSubKeyId;
}
+ public static RequiredInputParcel createRetryUploadOperation() {
+ return new RequiredInputParcel(RequiredInputType.UPLOAD_FAIL_RETRY,
+ null, null, null, 0L, 0L);
+ }
+
+ public static RequiredInputParcel createOrbotRequiredOperation() {
+ return new RequiredInputParcel(RequiredInputType.ENABLE_ORBOT, null, null, null, 0L, 0L);
+ }
+
public static RequiredInputParcel createNfcSignOperation(
+ long masterKeyId, long subKeyId,
byte[] inputHash, int signAlgo, Date signatureTime) {
return new RequiredInputParcel(RequiredInputType.NFC_SIGN,
new byte[][] { inputHash }, new int[] { signAlgo },
- signatureTime, null, null);
+ signatureTime, masterKeyId, subKeyId);
}
- public static RequiredInputParcel createNfcDecryptOperation(byte[] inputHash, long subKeyId) {
+ public static RequiredInputParcel createNfcDecryptOperation(
+ long masterKeyId, long subKeyId, byte[] encryptedSessionKey) {
return new RequiredInputParcel(RequiredInputType.NFC_DECRYPT,
- new byte[][] { inputHash }, null, null, null, subKeyId);
+ new byte[][] { encryptedSessionKey }, null, null, masterKeyId, subKeyId);
}
public static RequiredInputParcel createRequiredSignPassphrase(
@@ -118,11 +131,11 @@ public class RequiredInputParcel implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType.ordinal());
- if (mInputHashes != null) {
+ if (mInputData != null) {
dest.writeInt(mSignAlgos != null ? 1 : 2);
- dest.writeInt(mInputHashes.length);
- for (int i = 0; i < mInputHashes.length; i++) {
- dest.writeByteArray(mInputHashes[i]);
+ dest.writeInt(mInputData.length);
+ for (int i = 0; i < mInputData.length; i++) {
+ dest.writeByteArray(mInputData[i]);
if (mSignAlgos != null) {
dest.writeInt(mSignAlgos[i]);
}
@@ -165,10 +178,10 @@ public class RequiredInputParcel implements Parcelable {
Date mSignatureTime;
ArrayList<Integer> mSignAlgos = new ArrayList<>();
ArrayList<byte[]> mInputHashes = new ArrayList<>();
- Long mMasterKeyId;
- Long mSubKeyId;
+ long mMasterKeyId;
+ long mSubKeyId;
- public NfcSignOperationsBuilder(Date signatureTime, Long masterKeyId, Long subKeyId) {
+ public NfcSignOperationsBuilder(Date signatureTime, long masterKeyId, long subKeyId) {
mSignatureTime = signatureTime;
mMasterKeyId = masterKeyId;
mSubKeyId = subKeyId;
@@ -199,7 +212,7 @@ public class RequiredInputParcel implements Parcelable {
throw new AssertionError("operation types must match, this is a progrmming error!");
}
- Collections.addAll(mInputHashes, input.mInputHashes);
+ Collections.addAll(mInputHashes, input.mInputData);
for (int signAlgo : input.mSignAlgos) {
mSignAlgos.add(signAlgo);
}
@@ -211,4 +224,66 @@ public class RequiredInputParcel implements Parcelable {
}
+ public static class NfcKeyToCardOperationsBuilder {
+ ArrayList<byte[]> mSubkeysToExport = new ArrayList<>();
+ Long mMasterKeyId;
+ byte[] mPin;
+ byte[] mAdminPin;
+
+ public NfcKeyToCardOperationsBuilder(Long masterKeyId) {
+ mMasterKeyId = masterKeyId;
+ }
+
+ public RequiredInputParcel build() {
+ byte[][] inputData = new byte[mSubkeysToExport.size() + 2][];
+
+ // encode all subkeys into inputData
+ byte[][] subkeyData = new byte[mSubkeysToExport.size()][];
+ mSubkeysToExport.toArray(subkeyData);
+
+ // first two are PINs
+ inputData[0] = mPin;
+ inputData[1] = mAdminPin;
+ // then subkeys
+ System.arraycopy(subkeyData, 0, inputData, 2, subkeyData.length);
+
+ ByteBuffer buf = ByteBuffer.wrap(mSubkeysToExport.get(0));
+
+ // We need to pass in a subkey here...
+ return new RequiredInputParcel(RequiredInputType.NFC_MOVE_KEY_TO_CARD,
+ inputData, null, null, mMasterKeyId, buf.getLong());
+ }
+
+ public void addSubkey(long subkeyId) {
+ byte[] subKeyId = new byte[8];
+ ByteBuffer buf = ByteBuffer.wrap(subKeyId);
+ buf.putLong(subkeyId).rewind();
+ mSubkeysToExport.add(subKeyId);
+ }
+
+ public void setPin(Passphrase pin) {
+ mPin = pin.toStringUnsafe().getBytes();
+ }
+
+ public void setAdminPin(Passphrase adminPin) {
+ mAdminPin = adminPin.toStringUnsafe().getBytes();
+ }
+
+ public void addAll(RequiredInputParcel input) {
+ if (!mMasterKeyId.equals(input.mMasterKeyId)) {
+ throw new AssertionError("Master keys must match, this is a programming error!");
+ }
+ if (input.mType != RequiredInputType.NFC_MOVE_KEY_TO_CARD) {
+ throw new AssertionError("Operation types must match, this is a programming error!");
+ }
+
+ Collections.addAll(mSubkeysToExport, input.mInputData);
+ }
+
+ public boolean isEmpty() {
+ return mSubkeysToExport.isEmpty();
+ }
+
+ }
+
}