aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2015-04-24 14:18:01 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2015-04-24 14:18:01 +0200
commitb4aec3114d9911cf9aef0d14ee697e5131b2853f (patch)
tree7237de5955ec34d8849737b2f9229bfbb37d0c45 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote
parentc2163460971cc01e65d7dfd18eec491f01ebc744 (diff)
parentbaac30508d24dcda6135bf8ae338c99d8c3b8ad8 (diff)
downloadopen-keychain-b4aec3114d9911cf9aef0d14ee697e5131b2853f.tar.gz
open-keychain-b4aec3114d9911cf9aef0d14ee697e5131b2853f.tar.bz2
open-keychain-b4aec3114d9911cf9aef0d14ee697e5131b2853f.zip
Merge branch 'development' into linked-identities
Conflicts: Graphics/update-drawables.sh OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/operations/CertifyOperationTest.java OpenKeychain/build.gradle OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java246
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java355
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java4
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java2
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java2
6 files changed, 409 insertions, 202 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java
new file mode 100644
index 000000000..e3e39417a
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/CryptoInputParcelCacheService.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.remote;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+
+import org.openintents.openpgp.util.OpenPgpApi;
+import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.util.Log;
+
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+
+public class CryptoInputParcelCacheService extends Service {
+
+ public static final String ACTION_ADD = Constants.INTENT_PREFIX + "ADD";
+ public static final String ACTION_GET = Constants.INTENT_PREFIX + "GET";
+
+ public static final String EXTRA_CRYPTO_INPUT_PARCEL = "crypto_input_parcel";
+ public static final String EXTRA_UUID1 = "uuid1";
+ public static final String EXTRA_UUID2 = "uuid2";
+ public static final String EXTRA_MESSENGER = "messenger";
+
+ private static final int MSG_GET_OKAY = 1;
+ private static final int MSG_GET_NOT_FOUND = 2;
+
+ Context mContext;
+
+ private static final UUID NULL_UUID = new UUID(0, 0);
+
+ private ConcurrentHashMap<UUID, CryptoInputParcel> mCache = new ConcurrentHashMap<>();
+
+ public static class InputParcelNotFound extends Exception {
+ public InputParcelNotFound() {
+ }
+
+ public InputParcelNotFound(String name) {
+ super(name);
+ }
+ }
+
+ public static void addCryptoInputParcel(Context context, Intent data, CryptoInputParcel inputParcel) {
+ UUID mTicket = addCryptoInputParcel(context, inputParcel);
+ // And write out the UUID most and least significant bits.
+ data.putExtra(OpenPgpApi.EXTRA_CALL_UUID1, mTicket.getMostSignificantBits());
+ data.putExtra(OpenPgpApi.EXTRA_CALL_UUID2, mTicket.getLeastSignificantBits());
+ }
+
+ public static CryptoInputParcel getCryptoInputParcel(Context context, Intent data) {
+ if (!data.getExtras().containsKey(OpenPgpApi.EXTRA_CALL_UUID1)
+ || !data.getExtras().containsKey(OpenPgpApi.EXTRA_CALL_UUID2)) {
+ return null;
+ }
+ long mostSig = data.getLongExtra(OpenPgpApi.EXTRA_CALL_UUID1, 0);
+ long leastSig = data.getLongExtra(OpenPgpApi.EXTRA_CALL_UUID2, 0);
+ UUID uuid = new UUID(mostSig, leastSig);
+ try {
+ return getCryptoInputParcel(context, uuid);
+ } catch (InputParcelNotFound inputParcelNotFound) {
+ return null;
+ }
+ }
+
+ private static UUID addCryptoInputParcel(Context context, CryptoInputParcel inputParcel) {
+ UUID uuid = UUID.randomUUID();
+
+ Intent intent = new Intent(context, CryptoInputParcelCacheService.class);
+ intent.setAction(ACTION_ADD);
+ intent.putExtra(EXTRA_CRYPTO_INPUT_PARCEL, inputParcel);
+ intent.putExtra(EXTRA_UUID1, uuid.getMostSignificantBits());
+ intent.putExtra(EXTRA_UUID2, uuid.getLeastSignificantBits());
+ context.startService(intent);
+ return uuid;
+ }
+
+ private static CryptoInputParcel getCryptoInputParcel(Context context, UUID uuid) throws InputParcelNotFound {
+ Intent intent = new Intent(context, CryptoInputParcelCacheService.class);
+ intent.setAction(ACTION_GET);
+
+ final Object mutex = new Object();
+ final Message returnMessage = Message.obtain();
+
+ HandlerThread handlerThread = new HandlerThread("getParcelableThread");
+ handlerThread.start();
+ Handler returnHandler = new Handler(handlerThread.getLooper()) {
+ @Override
+ public void handleMessage(Message message) {
+ // copy over result to handle after mutex.wait
+ returnMessage.what = message.what;
+ returnMessage.copyFrom(message);
+ synchronized (mutex) {
+ mutex.notify();
+ }
+ // quit handlerThread
+ getLooper().quit();
+ }
+ };
+
+ // Create a new Messenger for the communication back
+ Messenger messenger = new Messenger(returnHandler);
+ intent.putExtra(EXTRA_UUID1, uuid.getMostSignificantBits());
+ intent.putExtra(EXTRA_UUID2, uuid.getLeastSignificantBits());
+ intent.putExtra(EXTRA_MESSENGER, messenger);
+ // send intent to this service
+ context.startService(intent);
+
+ // Wait on mutex until parcelable is returned to handlerThread. Note that this local
+ // variable is used in the handler closure above, so it does make sense here!
+ // noinspection SynchronizationOnLocalVariableOrMethodParameter
+ synchronized (mutex) {
+ try {
+ mutex.wait(3000);
+ } catch (InterruptedException e) {
+ // don't care
+ }
+ }
+
+ switch (returnMessage.what) {
+ case MSG_GET_OKAY:
+ Bundle returnData = returnMessage.getData();
+ returnData.setClassLoader(context.getClassLoader());
+ return returnData.getParcelable(EXTRA_CRYPTO_INPUT_PARCEL);
+ case MSG_GET_NOT_FOUND:
+ throw new InputParcelNotFound();
+ default:
+ Log.e(Constants.TAG, "timeout!");
+ throw new InputParcelNotFound("should not happen!");
+ }
+ }
+
+ /**
+ * Executed when service is started by intent
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+
+ if (intent == null || intent.getAction() == null) {
+ return START_NOT_STICKY;
+ }
+
+ String action = intent.getAction();
+ switch (action) {
+ case ACTION_ADD: {
+ long uuid1 = intent.getLongExtra(EXTRA_UUID1, 0);
+ long uuid2 = intent.getLongExtra(EXTRA_UUID2, 0);
+ UUID uuid = new UUID(uuid1, uuid2);
+ CryptoInputParcel inputParcel = intent.getParcelableExtra(EXTRA_CRYPTO_INPUT_PARCEL);
+ mCache.put(uuid, inputParcel);
+
+ break;
+ }
+ case ACTION_GET: {
+ long uuid1 = intent.getLongExtra(EXTRA_UUID1, 0);
+ long uuid2 = intent.getLongExtra(EXTRA_UUID2, 0);
+ UUID uuid = new UUID(uuid1, uuid2);
+ Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER);
+
+ Message msg = Message.obtain();
+ // UUID.equals isn't well documented; we use compareTo instead.
+ if (NULL_UUID.compareTo(uuid) == 0) {
+ msg.what = MSG_GET_NOT_FOUND;
+ } else {
+ CryptoInputParcel inputParcel = mCache.get(uuid);
+ mCache.remove(uuid);
+ msg.what = MSG_GET_OKAY;
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EXTRA_CRYPTO_INPUT_PARCEL, inputParcel);
+ msg.setData(bundle);
+ }
+
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ Log.e(Constants.TAG, "CryptoInputParcelCacheService: Sending message failed", e);
+ }
+ break;
+ }
+ default: {
+ Log.e(Constants.TAG, "CryptoInputParcelCacheService: Intent or Intent Action not supported!");
+ break;
+ }
+ }
+
+ if (mCache.size() <= 0) {
+ // stop whole service if cache is empty
+ Log.d(Constants.TAG, "CryptoInputParcelCacheService: No passphrases remaining in memory, stopping service!");
+ stopSelf();
+ }
+
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mContext = this;
+ Log.d(Constants.TAG, "CryptoInputParcelCacheService, onCreate()");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.d(Constants.TAG, "CryptoInputParcelCacheService, onDestroy()");
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ public class CryptoInputParcelCacheServiceBinder extends Binder {
+ public CryptoInputParcelCacheService getService() {
+ return CryptoInputParcelCacheService.this;
+ }
+ }
+
+ private final IBinder mBinder = new CryptoInputParcelCacheServiceBinder();
+
+} \ No newline at end of file
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index bd2866985..c51edf59c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -18,6 +18,7 @@
package org.sufficientlysecure.keychain.remote;
import android.app.PendingIntent;
+import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
@@ -31,16 +32,15 @@ import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpApi;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
-import org.spongycastle.util.encoders.Hex;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel;
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
import org.sufficientlysecure.keychain.pgp.PgpConstants;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
-import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInput;
+import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel;
import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
+import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@@ -48,8 +48,10 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity;
import org.sufficientlysecure.keychain.remote.ui.SelectSignKeyIdActivity;
+import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
+import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.ui.ImportKeysActivity;
-import org.sufficientlysecure.keychain.ui.NfcActivity;
+import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
import org.sufficientlysecure.keychain.ui.ViewKeyActivity;
import org.sufficientlysecure.keychain.util.InputData;
@@ -60,7 +62,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Date;
import java.util.Set;
public class OpenPgpService extends RemoteService {
@@ -78,9 +79,6 @@ public class OpenPgpService extends RemoteService {
/**
* Search database for key ids based on emails.
- *
- * @param encryptionUserIds
- * @return
*/
private Intent returnKeyIdsFromEmails(Intent data, String[] encryptionUserIds) {
boolean noUserIdsCheck = (encryptionUserIds == null || encryptionUserIds.length == 0);
@@ -163,52 +161,35 @@ public class OpenPgpService extends RemoteService {
}
}
- private Intent returnPassphraseIntent(Intent data, long keyId) {
- // build PendingIntent for passphrase input
- Intent intent = new Intent(getBaseContext(), PassphraseDialogActivity.class);
- intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, keyId);
- // pass params through to activity that it can be returned again later to repeat pgp operation
- intent.putExtra(PassphraseDialogActivity.EXTRA_DATA, data);
- PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
- intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
-
- // return PendingIntent to be executed by client
- Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_INTENT, pi);
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
- return result;
- }
+ private static PendingIntent getRequiredInputPendingIntent(Context context,
+ Intent data, RequiredInputParcel requiredInput) {
+
+ switch (requiredInput.mType) {
+ case NFC_DECRYPT:
+ case NFC_SIGN: {
+ // build PendingIntent for YubiKey NFC operations
+ Intent intent = new Intent(context, NfcOperationActivity.class);
+ // pass params through to activity that it can be returned again later to repeat pgp operation
+ intent.putExtra(NfcOperationActivity.EXTRA_SERVICE_INTENT, data);
+ intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
+ return PendingIntent.getActivity(context, 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ }
- private PendingIntent getNfcSignPendingIntent(Intent data, long keyId, Passphrase pin, byte[] hashToSign, int hashAlgo) {
- // build PendingIntent for Yubikey NFC operations
- Intent intent = new Intent(getBaseContext(), NfcActivity.class);
- intent.setAction(NfcActivity.ACTION_SIGN_HASH);
- // pass params through to activity that it can be returned again later to repeat pgp operation
- intent.putExtra(NfcActivity.EXTRA_DATA, data);
- intent.putExtra(NfcActivity.EXTRA_PIN, pin);
- intent.putExtra(NfcActivity.EXTRA_KEY_ID, keyId);
-
- intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign);
- intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo);
- return PendingIntent.getActivity(getBaseContext(), 0,
- intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
- }
+ case PASSPHRASE: {
+ // build PendingIntent for Passphrase request
+ Intent intent = new Intent(context, PassphraseDialogActivity.class);
+ // pass params through to activity that it can be returned again later to repeat pgp operation
+ intent.putExtra(PassphraseDialogActivity.EXTRA_SERVICE_INTENT, data);
+ intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
+ return PendingIntent.getActivity(context, 0, intent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ }
- private PendingIntent getNfcDecryptPendingIntent(Intent data, long subKeyId, Passphrase pin, byte[] encryptedSessionKey) {
- // build PendingIntent for Yubikey NFC operations
- Intent intent = new Intent(getBaseContext(), NfcActivity.class);
- intent.setAction(NfcActivity.ACTION_DECRYPT_SESSION_KEY);
- // pass params through to activity that it can be returned again later to repeat pgp operation
- intent.putExtra(NfcActivity.EXTRA_DATA, data);
- intent.putExtra(NfcActivity.EXTRA_PIN, pin);
- intent.putExtra(NfcActivity.EXTRA_KEY_ID, subKeyId);
+ default:
+ throw new AssertionError("Unhandled required input type!");
+ }
- intent.putExtra(NfcActivity.EXTRA_NFC_ENC_SESSION_KEY, encryptedSessionKey);
- return PendingIntent.getActivity(getBaseContext(), 0,
- intent,
- PendingIntent.FLAG_CANCEL_CURRENT);
}
private PendingIntent getKeyserverPendingIntent(Intent data, long masterKeyId) {
@@ -240,17 +221,13 @@ public class OpenPgpService extends RemoteService {
try {
boolean asciiArmor = cleartextSign || data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true);
- Passphrase passphrase = null;
- if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) {
- passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
- }
-
- byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
- if (nfcSignedHash != null) {
- Log.d(Constants.TAG, "nfcSignedHash:" + Hex.toHexString(nfcSignedHash));
- } else {
- Log.d(Constants.TAG, "nfcSignedHash: null");
- }
+ // sign-only
+ PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel()
+ .setEnableAsciiArmorOutput(asciiArmor)
+ .setCleartextSignature(cleartextSign)
+ .setDetachedSignature(!cleartextSign)
+ .setVersionHeader(null)
+ .setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED);
Intent signKeyIdIntent = getSignKeyMasterId(data);
// NOTE: Fallback to return account settings (Old API)
@@ -258,17 +235,21 @@ public class OpenPgpService extends RemoteService {
== OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED) {
return signKeyIdIntent;
}
+
long signKeyId = signKeyIdIntent.getLongExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, Constants.key.none);
if (signKeyId == Constants.key.none) {
- Log.e(Constants.TAG, "No signing key given!");
- }
+ throw new Exception("No signing key given");
+ } else {
+ pseInput.setSignatureMasterKeyId(signKeyId);
- // carefully: only set if timestamp exists
- Date nfcCreationDate = null;
- long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);
- Log.d(Constants.TAG, "nfcCreationTimestamp: " + nfcCreationTimestamp);
- if (nfcCreationTimestamp != -1) {
- nfcCreationDate = new Date(nfcCreationTimestamp);
+ // get first usable subkey capable of signing
+ try {
+ long signSubKeyId = mProviderHelper.getCachedPublicKeyRing(
+ pseInput.getSignatureMasterKeyId()).getSecretSignId();
+ pseInput.setSignatureSubKeyId(signSubKeyId);
+ } catch (PgpKeyNotFoundException e) {
+ throw new Exception("signing subkey not found!", e);
+ }
}
// Get Input- and OutputStream from ParcelFileDescriptor
@@ -281,42 +262,31 @@ public class OpenPgpService extends RemoteService {
long inputLength = is.available();
InputData inputData = new InputData(is, inputLength);
- // sign-only
- PgpSignEncryptInput pseInput = new PgpSignEncryptInput()
- .setSignaturePassphrase(passphrase)
- .setEnableAsciiArmorOutput(asciiArmor)
- .setCleartextSignature(cleartextSign)
- .setDetachedSignature(!cleartextSign)
- .setVersionHeader(null)
- .setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
- .setSignatureMasterKeyId(signKeyId)
- .setNfcState(nfcSignedHash, nfcCreationDate);
+ CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
+ if (inputParcel == null) {
+ inputParcel = new CryptoInputParcel();
+ }
+ // override passphrase in input parcel if given by API call
+ if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
+ inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
+ new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
+ }
// execute PGP operation!
PgpSignEncryptOperation pse = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
- PgpSignEncryptResult pgpResult = pse.execute(pseInput, inputData, os);
+ PgpSignEncryptResult pgpResult = pse.execute(pseInput, inputParcel, inputData, os);
if (pgpResult.isPending()) {
- if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
- PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) {
- return returnPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
- } else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) ==
- PgpSignEncryptResult.RESULT_PENDING_NFC) {
- // return PendingIntent to execute NFC activity
- // pass through the signature creation timestamp to be used again on second execution
- // of PgpSignEncrypt when we have the signed hash!
- data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
-
- // return PendingIntent to be executed by client
- Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_INTENT,
- getNfcSignPendingIntent(data, pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo()));
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
- return result;
- } else {
- throw new PgpGeneralException(
- "Encountered unhandled type of pending action not supported by API!");
- }
+
+ RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
+ PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data, requiredInput);
+
+ // return PendingIntent to be executed by client
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_INTENT, pIntent);
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ return result;
+
} else if (pgpResult.success()) {
Intent result = new Intent();
if (pgpResult.getDetachedSignature() != null && !cleartextSign) {
@@ -372,11 +342,6 @@ public class OpenPgpService extends RemoteService {
compressionId = CompressionAlgorithmTags.UNCOMPRESSED;
}
- Passphrase passphrase = null;
- if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) {
- passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
- }
-
// first try to get key ids from non-ambiguous key id extra
long[] keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS);
if (keyIds == null) {
@@ -401,9 +366,8 @@ public class OpenPgpService extends RemoteService {
long inputLength = is.available();
InputData inputData = new InputData(is, inputLength, originalFilename);
- PgpSignEncryptInput pseInput = new PgpSignEncryptInput();
- pseInput.setSignaturePassphrase(passphrase)
- .setEnableAsciiArmorOutput(asciiArmor)
+ PgpSignEncryptInputParcel pseInput = new PgpSignEncryptInputParcel();
+ pseInput.setEnableAsciiArmorOutput(asciiArmor)
.setVersionHeader(null)
.setCompressionId(compressionId)
.setSymmetricEncryptionAlgorithm(PgpConstants.OpenKeychainSymmetricKeyAlgorithmTags.USE_PREFERRED)
@@ -420,49 +384,49 @@ public class OpenPgpService extends RemoteService {
}
long signKeyId = signKeyIdIntent.getLongExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, Constants.key.none);
if (signKeyId == Constants.key.none) {
- Log.e(Constants.TAG, "No signing key given!");
- }
+ throw new Exception("No signing key given");
+ } else {
+ pseInput.setSignatureMasterKeyId(signKeyId);
- byte[] nfcSignedHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH);
- // carefully: only set if timestamp exists
- Date nfcCreationDate = null;
- long nfcCreationTimestamp = data.getLongExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, -1);
- if (nfcCreationTimestamp != -1) {
- nfcCreationDate = new Date(nfcCreationTimestamp);
+ // get first usable subkey capable of signing
+ try {
+ long signSubKeyId = mProviderHelper.getCachedPublicKeyRing(
+ pseInput.getSignatureMasterKeyId()).getSecretSignId();
+ pseInput.setSignatureSubKeyId(signSubKeyId);
+ } catch (PgpKeyNotFoundException e) {
+ throw new Exception("signing subkey not found!", e);
+ }
}
// sign and encrypt
pseInput.setSignatureHashAlgorithm(PgpConstants.OpenKeychainHashAlgorithmTags.USE_PREFERRED)
- .setSignatureMasterKeyId(signKeyId)
- .setNfcState(nfcSignedHash, nfcCreationDate)
.setAdditionalEncryptId(signKeyId); // add sign key for encryption
}
+ CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
+ if (inputParcel == null) {
+ inputParcel = new CryptoInputParcel();
+ }
+ // override passphrase in input parcel if given by API call
+ if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
+ inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
+ new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
+ }
+
PgpSignEncryptOperation op = new PgpSignEncryptOperation(this, new ProviderHelper(getContext()), null);
// execute PGP operation!
- PgpSignEncryptResult pgpResult = op.execute(pseInput, inputData, os);
+ PgpSignEncryptResult pgpResult = op.execute(pseInput, inputParcel, inputData, os);
if (pgpResult.isPending()) {
- if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) ==
- PgpSignEncryptResult.RESULT_PENDING_PASSPHRASE) {
- return returnPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
- } else if ((pgpResult.getResult() & PgpSignEncryptResult.RESULT_PENDING_NFC) ==
- PgpSignEncryptResult.RESULT_PENDING_NFC) {
- // return PendingIntent to execute NFC activity
- // pass through the signature creation timestamp to be used again on second execution
- // of PgpSignEncrypt when we have the signed hash!
- data.putExtra(OpenPgpApi.EXTRA_NFC_SIG_CREATION_TIMESTAMP, pgpResult.getNfcTimestamp().getTime());
- // return PendingIntent to be executed by client
- Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_INTENT,
- getNfcSignPendingIntent(data, pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo()));
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
- return result;
- } else {
- throw new PgpGeneralException(
- "Encountered unhandled type of pending action not supported by API!");
- }
+ RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
+ PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data, requiredInput);
+
+ // return PendingIntent to be executed by client
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_INTENT, pIntent);
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ return result;
} else if (pgpResult.success()) {
Intent result = new Intent();
result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS);
@@ -511,11 +475,6 @@ public class OpenPgpService extends RemoteService {
os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
}
- Passphrase passphrase = null;
- if (data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE) != null) {
- passphrase = new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE));
- }
-
String currentPkg = getCurrentCallingPackage();
Set<Long> allowedKeyIds;
if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 7) {
@@ -533,42 +492,37 @@ public class OpenPgpService extends RemoteService {
this, new ProviderHelper(getContext()), null, inputData, os
);
- byte[] nfcDecryptedSessionKey = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_DECRYPTED_SESSION_KEY);
+ CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data);
+ if (inputParcel == null) {
+ inputParcel = new CryptoInputParcel();
+ }
+ // override passphrase in input parcel if given by API call
+ if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) {
+ inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(),
+ new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE)));
+ }
byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE);
// allow only private keys associated with accounts of this app
// no support for symmetric encryption
- builder.setPassphrase(passphrase)
- .setAllowSymmetricDecryption(false)
+ builder.setAllowSymmetricDecryption(false)
.setAllowedKeyIds(allowedKeyIds)
.setDecryptMetadataOnly(decryptMetadataOnly)
- .setNfcState(nfcDecryptedSessionKey)
.setDetachedSignature(detachedSignature);
- DecryptVerifyResult pgpResult = builder.build().execute();
+ DecryptVerifyResult pgpResult = builder.build().execute(inputParcel);
if (pgpResult.isPending()) {
- if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) ==
- DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE) {
- return returnPassphraseIntent(data, pgpResult.getKeyIdPassphraseNeeded());
- } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) ==
- DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE) {
- throw new PgpGeneralException(
- "Decryption of symmetric content not supported by API!");
- } else if ((pgpResult.getResult() & DecryptVerifyResult.RESULT_PENDING_NFC) ==
- DecryptVerifyResult.RESULT_PENDING_NFC) {
-
- // return PendingIntent to be executed by client
- Intent result = new Intent();
- result.putExtra(OpenPgpApi.RESULT_INTENT,
- getNfcDecryptPendingIntent(data, pgpResult.getNfcSubKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcEncryptedSessionKey()));
- result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
- return result;
- } else {
- throw new PgpGeneralException(
- "Encountered unhandled type of pending action not supported by API!");
- }
+ // prepare and return PendingIntent to be executed by client
+ RequiredInputParcel requiredInput = pgpResult.getRequiredInputParcel();
+ PendingIntent pIntent = getRequiredInputPendingIntent(getBaseContext(), data, requiredInput);
+
+ Intent result = new Intent();
+ result.putExtra(OpenPgpApi.RESULT_INTENT, pIntent);
+ result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED);
+ return result;
+
} else if (pgpResult.success()) {
Intent result = new Intent();
@@ -754,7 +708,6 @@ public class OpenPgpService extends RemoteService {
* - has supported API version
* - is allowed to call the service (access has been granted)
*
- * @param data
* @return null if everything is okay, or a Bundle with an error/PendingIntent
*/
private Intent checkRequirements(Intent data) {
@@ -794,9 +747,7 @@ public class OpenPgpService extends RemoteService {
return null;
}
- // TODO: multi-threading
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
-
@Override
public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) {
try {
@@ -806,30 +757,42 @@ public class OpenPgpService extends RemoteService {
}
String action = data.getAction();
- if (OpenPgpApi.ACTION_CLEARTEXT_SIGN.equals(action)) {
- return signImpl(data, input, output, true);
- } else if (OpenPgpApi.ACTION_SIGN.equals(action)) {
- // DEPRECATED: same as ACTION_CLEARTEXT_SIGN
- Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!");
- return signImpl(data, input, output, true);
- } else if (OpenPgpApi.ACTION_DETACHED_SIGN.equals(action)) {
- return signImpl(data, input, output, false);
- } else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, false);
- } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) {
- return encryptAndSignImpl(data, input, output, true);
- } else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) {
- return decryptAndVerifyImpl(data, input, output, false);
- } else if (OpenPgpApi.ACTION_DECRYPT_METADATA.equals(action)) {
- return decryptAndVerifyImpl(data, input, output, true);
- } else if (OpenPgpApi.ACTION_GET_SIGN_KEY_ID.equals(action)) {
- return getSignKeyIdImpl(data);
- } else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) {
- return getKeyIdsImpl(data);
- } else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) {
- return getKeyImpl(data);
- } else {
- return null;
+ switch (action) {
+ case OpenPgpApi.ACTION_CLEARTEXT_SIGN: {
+ return signImpl(data, input, output, true);
+ }
+ case OpenPgpApi.ACTION_SIGN: {
+ // DEPRECATED: same as ACTION_CLEARTEXT_SIGN
+ Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!");
+ return signImpl(data, input, output, true);
+ }
+ case OpenPgpApi.ACTION_DETACHED_SIGN: {
+ return signImpl(data, input, output, false);
+ }
+ case OpenPgpApi.ACTION_ENCRYPT: {
+ return encryptAndSignImpl(data, input, output, false);
+ }
+ case OpenPgpApi.ACTION_SIGN_AND_ENCRYPT: {
+ return encryptAndSignImpl(data, input, output, true);
+ }
+ case OpenPgpApi.ACTION_DECRYPT_VERIFY: {
+ return decryptAndVerifyImpl(data, input, output, false);
+ }
+ case OpenPgpApi.ACTION_DECRYPT_METADATA: {
+ return decryptAndVerifyImpl(data, input, output, true);
+ }
+ case OpenPgpApi.ACTION_GET_SIGN_KEY_ID: {
+ return getSignKeyIdImpl(data);
+ }
+ case OpenPgpApi.ACTION_GET_KEY_IDS: {
+ return getKeyIdsImpl(data);
+ }
+ case OpenPgpApi.ACTION_GET_KEY: {
+ return getKeyImpl(data);
+ }
+ default: {
+ return null;
+ }
}
} finally {
// always close input and output file descriptors even in error cases
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
index 507d4dea5..e19757d65 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java
@@ -31,7 +31,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AccountSettings;
-import org.sufficientlysecure.keychain.ui.BaseActivity;
+import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.util.Log;
public class AccountSettingsActivity extends BaseActivity {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
index 407480c98..2b71d6dc1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java
@@ -40,9 +40,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AppSettings;
-import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
-import org.sufficientlysecure.keychain.ui.BaseActivity;
-import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
+import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.dialog.AdvancedAppSettingsDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
index e8c3e4511..f312c0d44 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java
@@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.remote.AppSettings;
-import org.sufficientlysecure.keychain.ui.BaseActivity;
+import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java
index 98a44466d..cb9f46f7f 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java
@@ -29,7 +29,7 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing;
-import org.sufficientlysecure.keychain.ui.BaseActivity;
+import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import org.sufficientlysecure.keychain.util.Log;