master merge

This commit is contained in:
Ashley Hughes
2014-02-22 10:27:03 +00:00
258 changed files with 10073 additions and 5926 deletions

View File

@@ -43,10 +43,11 @@ import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpImportExport;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.PgpOperation;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.DataStream;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
@@ -95,7 +96,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
public static final String ACTION_DOWNLOAD_AND_IMPORT_KEYS = Constants.INTENT_PREFIX + "QUERY_KEYRING";
public static final String ACTION_SIGN_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
public static final String ACTION_CERTIFY_KEYRING = Constants.INTENT_PREFIX + "SIGN_KEYRING";
/* keys for data bundle */
@@ -119,11 +120,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial
public static final String ENCRYPT_PROVIDER_URI = "provider_uri";
// decrypt/verify
public static final String DECRYPT_SIGNED_ONLY = "signed_only";
public static final String DECRYPT_RETURN_BYTES = "return_binary";
public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes";
public static final String DECRYPT_ASSUME_SYMMETRIC = "assume_symmetric";
public static final String DECRYPT_LOOKUP_UNKNOWN_KEY = "lookup_unknownKey";
// save keyring
public static final String SAVE_KEYRING_NEW_PASSPHRASE = "new_passphrase";
@@ -166,8 +165,8 @@ public class KeychainIntentService extends IntentService implements ProgressDial
public static final String DOWNLOAD_KEY_LIST = "query_key_id";
// sign key
public static final String SIGN_KEY_MASTER_KEY_ID = "sign_key_master_key_id";
public static final String SIGN_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
public static final String CERTIFY_KEY_MASTER_KEY_ID = "sign_key_master_key_id";
public static final String CERTIFY_KEY_PUB_KEY_ID = "sign_key_pub_key_id";
/*
* possible data keys as result send over messenger
@@ -189,10 +188,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial
public static final String RESULT_SIGNATURE = "signature";
public static final String RESULT_SIGNATURE_KEY_ID = "signature_key_id";
public static final String RESULT_SIGNATURE_USER_ID = "signature_user_id";
public static final String RESULT_CLEARTEXT_SIGNATURE_ONLY = "signature_only";
public static final String RESULT_SIGNATURE_SUCCESS = "signature_success";
public static final String RESULT_SIGNATURE_UNKNOWN = "signature_unknown";
public static final String RESULT_SIGNATURE_LOOKUP_KEY = "lookup_key";
// import
public static final String RESULT_IMPORT_ADDED = "added";
@@ -241,7 +240,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial
String action = intent.getAction();
// execute action from extra bundle
// executeServiceMethod action from extra bundle
if (ACTION_ENCRYPT_SIGN.equals(action)) {
try {
/* Input */
@@ -322,27 +321,41 @@ public class KeychainIntentService extends IntentService implements ProgressDial
}
/* Operation */
PgpOperation operation = new PgpOperation(this, this, inputData, outStream);
PgpSignEncrypt.Builder builder =
new PgpSignEncrypt.Builder(this, inputData, outStream);
builder.progress(this);
if (generateSignature) {
Log.d(Constants.TAG, "generating signature...");
operation.generateSignature(useAsciiArmor, false, secretKeyId,
PassphraseCacheService.getCachedPassphrase(this, secretKeyId),
Preferences.getPreferences(this).getDefaultHashAlgorithm(), Preferences
.getPreferences(this).getForceV3Signatures());
builder.enableAsciiArmorOutput(useAsciiArmor)
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
.signatureKeyId(secretKeyId)
.signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
.signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
builder.build().generateSignature();
} else if (signOnly) {
Log.d(Constants.TAG, "sign only...");
operation.signText(secretKeyId, PassphraseCacheService.getCachedPassphrase(
this, secretKeyId), Preferences.getPreferences(this)
.getDefaultHashAlgorithm(), Preferences.getPreferences(this)
.getForceV3Signatures());
builder.enableAsciiArmorOutput(useAsciiArmor)
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
.signatureKeyId(secretKeyId)
.signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
.signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
builder.build().execute();
} else {
Log.d(Constants.TAG, "encrypt...");
operation.signAndEncrypt(useAsciiArmor, compressionId, encryptionKeyIds,
encryptionPassphrase, Preferences.getPreferences(this)
.getDefaultEncryptionAlgorithm(), secretKeyId, Preferences
.getPreferences(this).getDefaultHashAlgorithm(), Preferences
.getPreferences(this).getForceV3Signatures(),
PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
builder.enableAsciiArmorOutput(useAsciiArmor)
.compressionId(compressionId)
.symmetricEncryptionAlgorithm(Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
.signatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
.encryptionKeyIds(encryptionKeyIds)
.encryptionPassphrase(encryptionPassphrase)
.signatureKeyId(secretKeyId)
.signatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
.signaturePassphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
builder.build().execute();
}
outStream.close();
@@ -395,12 +408,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial
long secretKeyId = data.getLong(ENCRYPT_SECRET_KEY_ID);
byte[] bytes = data.getByteArray(DECRYPT_CIPHERTEXT_BYTES);
boolean signedOnly = data.getBoolean(DECRYPT_SIGNED_ONLY);
boolean returnBytes = data.getBoolean(DECRYPT_RETURN_BYTES);
boolean assumeSymmetricEncryption = data.getBoolean(DECRYPT_ASSUME_SYMMETRIC);
boolean lookupUnknownKey = data.getBoolean(DECRYPT_LOOKUP_UNKNOWN_KEY);
InputStream inStream = null;
long inLength = -1;
InputData inputData = null;
@@ -474,14 +484,13 @@ public class KeychainIntentService extends IntentService implements ProgressDial
// verifyText and decrypt returning additional resultData values for the
// verification of signatures
PgpOperation operation = new PgpOperation(this, this, inputData, outStream);
if (signedOnly) {
resultData = operation.verifyText(lookupUnknownKey);
} else {
resultData = operation.decryptAndVerify(
PassphraseCacheService.getCachedPassphrase(this, secretKeyId),
assumeSymmetricEncryption);
}
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream);
builder.progress(this);
builder.assumeSymmetric(assumeSymmetricEncryption)
.passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId));
resultData = builder.build().execute();
outStream.close();
@@ -785,19 +794,19 @@ public class KeychainIntentService extends IntentService implements ProgressDial
} catch (Exception e) {
sendErrorToHandler(e);
}
} else if (ACTION_SIGN_KEYRING.equals(action)) {
} else if (ACTION_CERTIFY_KEYRING.equals(action)) {
try {
/* Input */
long masterKeyId = data.getLong(SIGN_KEY_MASTER_KEY_ID);
long pubKeyId = data.getLong(SIGN_KEY_PUB_KEY_ID);
long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID);
long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID);
/* Operation */
String signaturePassPhrase = PassphraseCacheService.getCachedPassphrase(this,
masterKeyId);
PgpKeyOperation keyOperation = new PgpKeyOperation(this, this);
PGPPublicKeyRing signedPubKeyRing = keyOperation.signKey(masterKeyId, pubKeyId,
PGPPublicKeyRing signedPubKeyRing = keyOperation.certifyKey(masterKeyId, pubKeyId,
signaturePassPhrase);
// store the signed key in our local cache

View File

@@ -25,6 +25,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
public class KeychainIntentServiceHandler extends Handler {
@@ -60,7 +61,14 @@ public class KeychainIntentServiceHandler extends Handler {
}
public void showProgressDialog(FragmentActivity activity) {
mProgressDialogFragment.show(activity.getSupportFragmentManager(), "progressDialog");
// TODO: This is a hack!, see http://stackoverflow.com/questions/10114324/show-dialogfragment-from-onactivityresult
final FragmentManager manager = activity.getSupportFragmentManager();
Handler handler = new Handler();
handler.post(new Runnable() {
public void run() {
mProgressDialogFragment.show(manager, "progressDialog");
}
});
}
@Override

View File

@@ -1,10 +0,0 @@
package org.sufficientlysecure.keychain.service.exception;
public class NoUserIdsException extends Exception {
private static final long serialVersionUID = 7009311527126696207L;
public NoUserIdsException(String message) {
super(message);
}
}

View File

@@ -1,10 +0,0 @@
package org.sufficientlysecure.keychain.service.exception;
public class UserInteractionRequiredException extends Exception {
private static final long serialVersionUID = -60128148603511936L;
public UserInteractionRequiredException(String message) {
super(message);
}
}

View File

@@ -1,10 +0,0 @@
package org.sufficientlysecure.keychain.service.exception;
public class WrongPassphraseException extends Exception {
private static final long serialVersionUID = -5309689232853485740L;
public WrongPassphraseException(String message) {
super(message);
}
}

View File

@@ -109,6 +109,15 @@ public class AppSettingsFragment extends Fragment implements
return view;
}
/**
* Set error String on key selection
*
* @param error
*/
public void setErrorOnSelectKeyFragment(String error) {
mSelectKeyFragment.setError(error);
}
private void initView(View view) {
mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById(
R.id.api_app_settings_select_key_fragment);
@@ -182,7 +191,7 @@ public class AppSettingsFragment extends Fragment implements
// TODO: Better: collapse/expand animation
// final Animation animation2 = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f,
// Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f,
// Animation.RELATIVE_TO_SELF, 0.0f);
// Animation.RELATIVE_TO_SELF, 0.0f);u
// animation2.setDuration(150);
mAdvancedSettingsButton.setOnClickListener(new OnClickListener() {

View File

@@ -1,122 +0,0 @@
/*
* Copyright (C) 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.remote;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.security.cert.X509Certificate;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openssl.PEMWriter;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.PgpToX509;
import org.sufficientlysecure.keychain.util.Log;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class ExtendedApiService extends RemoteService {
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private void selfSignedX509CertSafe(String subjAltNameURI, IExtendedApiCallback callback,
AppSettings appSettings) {
// TODO: for pgp keyrings with password
CallbackHandler pgpPwdCallbackHandler = new PgpToX509.PredefinedPasswordCallbackHandler("");
try {
long keyId = appSettings.getKeyId();
PGPSecretKey pgpSecretKey = PgpKeyHelper.getSigningKey(this, keyId);
PasswordCallback pgpSecKeyPasswordCallBack = new PasswordCallback("pgp passphrase?",
false);
pgpPwdCallbackHandler.handle(new Callback[] { pgpSecKeyPasswordCallBack });
PGPPrivateKey pgpPrivKey = pgpSecretKey.extractPrivateKey(
pgpSecKeyPasswordCallBack.getPassword(), Constants.BOUNCY_CASTLE_PROVIDER_NAME);
pgpSecKeyPasswordCallBack.clearPassword();
X509Certificate selfSignedCert = PgpToX509.createSelfSignedCert(pgpSecretKey,
pgpPrivKey, subjAltNameURI);
// Write x509cert and privKey into files
// FileOutputStream fosCert = context.openFileOutput(CERT_FILENAME,
// Context.MODE_PRIVATE);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
PEMWriter pemWriterCert = new PEMWriter(new PrintWriter(outStream));
pemWriterCert.writeObject(selfSignedCert);
pemWriterCert.close();
byte[] outputBytes = outStream.toByteArray();
callback.onSuccess(outputBytes);
} catch (Exception e) {
Log.e(Constants.TAG, "ExtendedApiService", e);
try {
callback.onError(e.getMessage());
} catch (RemoteException e1) {
Log.e(Constants.TAG, "ExtendedApiService", e);
}
}
// TODO: no private key at the moment! Don't give it to others
// PrivateKey privKey = pgpPrivKey.getKey();
// FileOutputStream fosKey = context.openFileOutput(PRIV_KEY_FILENAME,
// Context.MODE_PRIVATE);
// PEMWriter pemWriterKey = new PEMWriter(new PrintWriter(fosKey));
// pemWriterKey.writeObject(privKey);
// pemWriterKey.close();
}
private final IExtendedApiService.Stub mBinder = new IExtendedApiService.Stub() {
@Override
public void encrypt(byte[] inputBytes, String passphrase, IExtendedApiCallback callback)
throws RemoteException {
// TODO : implement
}
@Override
public void selfSignedX509Cert(final String subjAltNameURI,
final IExtendedApiCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
selfSignedX509CertSafe(subjAltNameURI, callback, settings);
}
};
checkAndEnqueue(r);
}
};
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2013-2014 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
@@ -17,108 +17,46 @@
package org.sufficientlysecure.keychain.service.remote;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.regex.Matcher;
import org.openintents.openpgp.IOpenPgpCallback;
import org.openintents.openpgp.IOpenPgpKeyIdsCallback;
import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpData;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.spongycastle.util.Arrays;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpOperation;
import org.sufficientlysecure.keychain.pgp.exception.NoAsymmetricEncryptionException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.service.exception.NoUserIdsException;
import org.sufficientlysecure.keychain.service.exception.UserInteractionRequiredException;
import org.sufficientlysecure.keychain.service.exception.WrongPassphraseException;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import android.app.PendingIntent;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.ParcelFileDescriptor;
import org.openintents.openpgp.IOpenPgpService;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.OpenPgpSignatureResult;
import org.openintents.openpgp.util.OpenPgpConstants;
import org.spongycastle.util.Arrays;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
public class OpenPgpService extends RemoteService {
private String getCachedPassphrase(long keyId, boolean allowUserInteraction)
throws UserInteractionRequiredException {
String passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), keyId);
private static final int PRIVATE_REQUEST_CODE_PASSPHRASE = 551;
private static final int PRIVATE_REQUEST_CODE_USER_IDS = 552;
if (passphrase == null) {
if (!allowUserInteraction) {
throw new UserInteractionRequiredException(
"Passphrase not found in cache, please enter your passphrase!");
}
Log.d(Constants.TAG, "No passphrase! Activity required!");
// start passphrase dialog
PassphraseActivityCallback callback = new PassphraseActivityCallback();
Bundle extras = new Bundle();
extras.putLong(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE, callback,
extras);
if (callback.isSuccess()) {
Log.d(Constants.TAG, "New passphrase entered!");
// get again after it was entered
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), keyId);
} else {
Log.d(Constants.TAG, "Passphrase dialog canceled!");
return null;
}
}
return passphrase;
}
public class PassphraseActivityCallback extends UserInputCallback {
private boolean success = false;
public boolean isSuccess() {
return success;
}
@Override
public void handleUserInput(Message msg) {
if (msg.arg1 == OKAY) {
success = true;
} else {
success = false;
}
}
};
/**
* Search database for key ids based on emails.
*
*
* @param encryptionUserIds
* @return
*/
private long[] getKeyIdsFromEmails(String[] encryptionUserIds, boolean allowUserInteraction)
throws UserInteractionRequiredException {
private Bundle getKeyIdsFromEmails(Bundle params, String[] encryptionUserIds) {
// find key ids to given emails in database
ArrayList<Long> keyIds = new ArrayList<Long>();
@@ -152,96 +90,129 @@ public class OpenPgpService extends RemoteService {
}
// allow the user to verify pub key selection
if (allowUserInteraction && (missingUserIdsCheck || dublicateUserIdsCheck)) {
SelectPubKeysActivityCallback callback = new SelectPubKeysActivityCallback();
if (missingUserIdsCheck || dublicateUserIdsCheck) {
// build PendingIntent for passphrase input
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS);
intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, dublicateUserIds);
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
Bundle extras = new Bundle();
extras.putLongArray(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
extras.putStringArrayList(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
extras.putStringArrayList(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS,
dublicateUserIds);
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_USER_IDS, intent, 0);
pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS, callback,
extras);
// return PendingIntent to be executed by client
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
if (callback.isSuccess()) {
Log.d(Constants.TAG, "New selection of pub keys!");
keyIdsArray = callback.getPubKeyIds();
} else {
Log.d(Constants.TAG, "Pub key selection canceled!");
return null;
}
}
// if no user interaction is allow throw exceptions on duplicate or missing pub keys
if (!allowUserInteraction) {
if (missingUserIdsCheck)
throw new UserInteractionRequiredException(
"Pub keys for these user ids are missing:" + missingUserIds.toString());
if (dublicateUserIdsCheck)
throw new UserInteractionRequiredException(
"More than one pub key with these user ids exist:"
+ dublicateUserIds.toString());
return result;
}
if (keyIdsArray.length == 0) {
return null;
}
return keyIdsArray;
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
result.putLongArray(OpenPgpConstants.PARAMS_KEY_IDS, keyIdsArray);
return result;
}
public class SelectPubKeysActivityCallback extends UserInputCallback {
public static final String PUB_KEY_IDS = "pub_key_ids";
private Bundle getPassphraseBundleIntent(Bundle params, long keyId) {
// build PendingIntent for passphrase input
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE);
intent.putExtra(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId);
// pass params through to activity that it can be returned again later to repeat pgp operation
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_PASSPHRASE, intent, 0);
private boolean success = false;
private long[] pubKeyIds;
// return PendingIntent to be executed by client
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
public boolean isSuccess() {
return success;
}
return result;
}
public long[] getPubKeyIds() {
return pubKeyIds;
}
private Bundle signImpl(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output,
AppSettings appSettings) {
try {
boolean asciiArmor = params.getBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
@Override
public void handleUserInput(Message msg) {
if (msg.arg1 == OKAY) {
success = true;
pubKeyIds = msg.getData().getLongArray(PUB_KEY_IDS);
// get passphrase from cache, if key has "no" passphrase, this returns an empty String
String passphrase;
if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE);
} else {
success = false;
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
}
}
};
private synchronized void getKeyIdsSafe(String[] userIds, boolean allowUserInteraction,
IOpenPgpKeyIdsCallback callback, AppSettings appSettings) {
try {
long[] keyIds = getKeyIdsFromEmails(userIds, allowUserInteraction);
if (keyIds == null) {
throw new NoUserIdsException("No user ids!");
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
return passphraseBundle;
}
callback.onSuccess(keyIds);
} catch (UserInteractionRequiredException e) {
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
} catch (NoUserIdsException e) {
callbackOpenPgpError(callback, OpenPgpError.NO_USER_IDS, e.getMessage());
// Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
try {
long inputLength = is.available();
InputData inputData = new InputData(is, inputLength);
// sign-only
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
builder.enableAsciiArmorOutput(asciiArmor)
.signatureHashAlgorithm(appSettings.getHashAlgorithm())
.signatureForceV3(false)
.signatureKeyId(appSettings.getKeyId())
.signaturePassphrase(passphrase);
builder.build().execute();
} finally {
is.close();
os.close();
}
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
return result;
} catch (Exception e) {
callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
return result;
}
}
private synchronized void encryptAndSignSafe(OpenPgpData inputData,
final OpenPgpData outputData, long[] keyIds, boolean allowUserInteraction,
IOpenPgpCallback callback, AppSettings appSettings, boolean sign) {
private Bundle encryptAndSignImpl(Bundle params, ParcelFileDescriptor input,
ParcelFileDescriptor output, AppSettings appSettings,
boolean sign) {
try {
// TODO: other options of OpenPgpData!
byte[] inputBytes = getInput(inputData);
boolean asciiArmor = false;
if (outputData.getType() == OpenPgpData.TYPE_STRING) {
asciiArmor = true;
boolean asciiArmor = params.getBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true);
long[] keyIds;
if (params.containsKey(OpenPgpConstants.PARAMS_KEY_IDS)) {
keyIds = params.getLongArray(OpenPgpConstants.PARAMS_KEY_IDS);
} else if (params.containsKey(OpenPgpConstants.PARAMS_USER_IDS)) {
// get key ids based on given user ids
String[] userIds = params.getStringArray(OpenPgpConstants.PARAMS_USER_IDS);
// give params through to activity...
Bundle result = getKeyIdsFromEmails(params, userIds);
if (result.getInt(OpenPgpConstants.RESULT_CODE, 0) == OpenPgpConstants.RESULT_CODE_SUCCESS) {
keyIds = result.getLongArray(OpenPgpConstants.PARAMS_KEY_IDS);
} else {
// if not success -> result contains a PendingIntent for user interaction
return result;
}
} else {
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, "Missing parameter user_ids or key_ids!"));
return result;
}
// add own key for encryption
@@ -249,351 +220,339 @@ public class OpenPgpService extends RemoteService {
keyIds[keyIds.length - 1] = appSettings.getKeyId();
// build InputData and write into OutputStream
InputStream inputStream = new ByteArrayInputStream(inputBytes);
long inputLength = inputBytes.length;
InputData inputDt = new InputData(inputStream, inputLength);
// Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
try {
long inputLength = is.available();
InputData inputData = new InputData(is, inputLength);
OutputStream outputStream = new ByteArrayOutputStream();
PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os);
builder.enableAsciiArmorOutput(asciiArmor)
.compressionId(appSettings.getCompression())
.symmetricEncryptionAlgorithm(appSettings.getEncryptionAlgorithm())
.encryptionKeyIds(keyIds);
PgpOperation operation = new PgpOperation(getContext(), null, inputDt, outputStream);
if (sign) {
String passphrase = getCachedPassphrase(appSettings.getKeyId(),
allowUserInteraction);
if (passphrase == null) {
throw new WrongPassphraseException("No or wrong passphrase!");
}
if (sign) {
String passphrase;
if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE);
} else {
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(),
appSettings.getKeyId());
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
return passphraseBundle;
}
operation.signAndEncrypt(asciiArmor, appSettings.getCompression(), keyIds, null,
appSettings.getEncryptionAlgorithm(), appSettings.getKeyId(),
appSettings.getHashAlgorithm(), true, passphrase);
} else {
operation.signAndEncrypt(asciiArmor, appSettings.getCompression(), keyIds, null,
appSettings.getEncryptionAlgorithm(), Id.key.none,
appSettings.getHashAlgorithm(), true, null);
}
outputStream.close();
byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
OpenPgpData output = null;
if (asciiArmor) {
output = new OpenPgpData(new String(outputBytes));
} else {
output = new OpenPgpData(outputBytes);
}
// return over handler on client side
callback.onSuccess(output, null);
} catch (UserInteractionRequiredException e) {
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
} catch (WrongPassphraseException e) {
callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage());
} catch (Exception e) {
callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
}
}
// TODO: asciiArmor?!
private void signSafe(byte[] inputBytes, boolean allowUserInteraction,
IOpenPgpCallback callback, AppSettings appSettings) {
try {
// build InputData and write into OutputStream
InputStream inputStream = new ByteArrayInputStream(inputBytes);
long inputLength = inputBytes.length;
InputData inputData = new InputData(inputStream, inputLength);
OutputStream outputStream = new ByteArrayOutputStream();
String passphrase = getCachedPassphrase(appSettings.getKeyId(), allowUserInteraction);
if (passphrase == null) {
throw new WrongPassphraseException("No or wrong passphrase!");
}
PgpOperation operation = new PgpOperation(getContext(), null, inputData, outputStream);
operation.signText(appSettings.getKeyId(), passphrase, appSettings.getHashAlgorithm(),
Preferences.getPreferences(this).getForceV3Signatures());
outputStream.close();
byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
OpenPgpData output = new OpenPgpData(new String(outputBytes));
// return over handler on client side
callback.onSuccess(output, null);
} catch (UserInteractionRequiredException e) {
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
} catch (WrongPassphraseException e) {
callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage());
} catch (Exception e) {
callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
}
}
private synchronized void decryptAndVerifySafe(byte[] inputBytes, boolean allowUserInteraction,
IOpenPgpCallback callback, AppSettings appSettings) {
try {
// TODO: this is not really needed
// checked if it is text with BEGIN and END tags
String message = new String(inputBytes);
Log.d(Constants.TAG, "in: " + message);
boolean signedOnly = false;
Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(message);
if (matcher.matches()) {
Log.d(Constants.TAG, "PGP_MESSAGE matched");
message = matcher.group(1);
// replace non breakable spaces
message = message.replaceAll("\\xa0", " ");
// overwrite inputBytes
inputBytes = message.getBytes();
} else {
matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(message);
if (matcher.matches()) {
signedOnly = true;
Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
message = matcher.group(1);
// replace non breakable spaces
message = message.replaceAll("\\xa0", " ");
// overwrite inputBytes
inputBytes = message.getBytes();
// sign and encrypt
builder.signatureHashAlgorithm(appSettings.getHashAlgorithm())
.signatureForceV3(false)
.signatureKeyId(appSettings.getKeyId())
.signaturePassphrase(passphrase);
} else {
Log.d(Constants.TAG, "Nothing matched! Binary?");
}
}
// END TODO
Log.d(Constants.TAG, "in: " + new String(inputBytes));
// TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
// app, Fix this?
String passphrase = null;
if (!signedOnly) {
// BEGIN Get key
// TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it
// better!
InputStream inputStream2 = new ByteArrayInputStream(inputBytes);
// TODO: duplicates functions from DecryptActivity!
long secretKeyId;
try {
if (inputStream2.markSupported()) {
// should probably set this to the max size of two
// pgpF objects, if it even needs to be anything other
// than 0.
inputStream2.mark(200);
}
secretKeyId = PgpHelper.getDecryptionKeyId(this, inputStream2);
if (secretKeyId == Id.key.none) {
throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
}
} catch (NoAsymmetricEncryptionException e) {
if (inputStream2.markSupported()) {
inputStream2.reset();
}
secretKeyId = Id.key.symmetric;
if (!PgpOperation.hasSymmetricEncryption(this, inputStream2)) {
throw new PgpGeneralException(
getString(R.string.error_no_known_encryption_found));
}
// we do not support symmetric decryption from the API!
throw new Exception("Symmetric decryption is not supported!");
}
Log.d(Constants.TAG, "secretKeyId " + secretKeyId);
passphrase = getCachedPassphrase(secretKeyId, allowUserInteraction);
if (passphrase == null) {
throw new WrongPassphraseException("No or wrong passphrase!");
// encrypt only
builder.signatureKeyId(Id.key.none);
}
// execute PGP operation!
builder.build().execute();
} finally {
is.close();
os.close();
}
// build InputData and write into OutputStream
InputStream inputStream = new ByteArrayInputStream(inputBytes);
long inputLength = inputBytes.length;
InputData inputData = new InputData(inputStream, inputLength);
OutputStream outputStream = new ByteArrayOutputStream();
Bundle outputBundle;
PgpOperation operation = new PgpOperation(getContext(), null, inputData, outputStream);
if (signedOnly) {
// TODO: download missing keys from keyserver?
outputBundle = operation.verifyText(false);
} else {
outputBundle = operation.decryptAndVerify(passphrase, false);
}
outputStream.close();
byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
// get signature informations from bundle
boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE);
OpenPgpSignatureResult sigResult = null;
if (signature) {
long signatureKeyId = outputBundle
.getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID);
String signatureUserId = outputBundle
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
boolean signatureSuccess = outputBundle
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS);
boolean signatureUnknown = outputBundle
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN);
int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR;
if (signatureSuccess) {
signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_TRUSTED;
} else if (signatureUnknown) {
signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
}
sigResult = new OpenPgpSignatureResult(signatureStatus, signatureUserId,
signedOnly, signatureKeyId);
}
OpenPgpData output = new OpenPgpData(new String(outputBytes));
// return over handler on client side
callback.onSuccess(output, sigResult);
} catch (UserInteractionRequiredException e) {
callbackOpenPgpError(callback, OpenPgpError.USER_INTERACTION_REQUIRED, e.getMessage());
} catch (WrongPassphraseException e) {
callbackOpenPgpError(callback, OpenPgpError.NO_OR_WRONG_PASSPHRASE, e.getMessage());
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
return result;
} catch (Exception e) {
callbackOpenPgpError(callback, OpenPgpError.GENERIC_ERROR, e.getMessage());
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
return result;
}
}
private Bundle decryptAndVerifyImpl(Bundle params, ParcelFileDescriptor input,
ParcelFileDescriptor output, AppSettings appSettings) {
try {
// Get Input- and OutputStream from ParcelFileDescriptor
InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input);
OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output);
OpenPgpSignatureResult sigResult = null;
try {
// PGPUtil.getDecoderStream(is)
// TODOs API 2.0:
// implement verify-only!
// fix the mess: http://stackoverflow.com/questions/148130/how-do-i-peek-at-the-first-two-bytes-in-an-inputstream
// should we allow to decrypt everything under every key id or only the one set?
// TODO: instead of trying to get the passphrase before
// pause stream when passphrase is missing and then resume
// TODO: this is not really needed
// checked if it is text with BEGIN and END tags
// String message = new String(inputBytes);
// Log.d(Constants.TAG, "in: " + message);
// boolean signedOnly = false;
// Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(message);
// if (matcher.matches()) {
// Log.d(Constants.TAG, "PGP_MESSAGE matched");
// message = matcher.group(1);
// // replace non breakable spaces
// message = message.replaceAll("\\xa0", " ");
//
// // overwrite inputBytes
// inputBytes = message.getBytes();
// } else {
// matcher = PgpHelper.PGP_SIGNED_MESSAGE.matcher(message);
// if (matcher.matches()) {
// signedOnly = true;
// Log.d(Constants.TAG, "PGP_SIGNED_MESSAGE matched");
// message = matcher.group(1);
// // replace non breakable spaces
// message = message.replaceAll("\\xa0", " ");
//
// // overwrite inputBytes
// inputBytes = message.getBytes();
// } else {
// Log.d(Constants.TAG, "Nothing matched! Binary?");
// }
// }
// END TODO
// Log.d(Constants.TAG, "in: " + new String(inputBytes));
// TODO: This allows to decrypt messages with ALL secret keys, not only the one for the
// app, Fix this?
// String passphrase = null;
// if (!signedOnly) {
// // BEGIN Get key
// // TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it
// // better!
// InputStream inputStream2 = new ByteArrayInputStream(inputBytes);
//
// // TODO: duplicates functions from DecryptActivity!
// long secretKeyId;
// try {
// if (inputStream2.markSupported()) {
// // should probably set this to the max size of two
// // pgpF objects, if it even needs to be anything other
// // than 0.
// inputStream2.mark(200);
// }
// secretKeyId = PgpHelper.getDecryptionKeyId(this, inputStream2);
// if (secretKeyId == Id.key.none) {
// throw new PgpGeneralException(getString(R.string.error_no_secret_key_found));
// }
// } catch (NoAsymmetricEncryptionException e) {
// if (inputStream2.markSupported()) {
// inputStream2.reset();
// }
// secretKeyId = Id.key.symmetric;
// if (!PgpDecryptVerify.hasSymmetricEncryption(this, inputStream2)) {
// throw new PgpGeneralException(
// getString(R.string.error_no_known_encryption_found));
// }
// // we do not support symmetric decryption from the API!
// throw new Exception("Symmetric decryption is not supported!");
// }
//
// Log.d(Constants.TAG, "secretKeyId " + secretKeyId);
// NOTE: currently this only gets the passphrase for the key set for this client
String passphrase;
if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) {
passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE);
} else {
passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId());
}
if (passphrase == null) {
// get PendingIntent for passphrase input, add it to given params and return to client
Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId());
return passphraseBundle;
}
// }
// build InputData and write into OutputStream
long inputLength = is.available();
InputData inputData = new InputData(is, inputLength);
Bundle outputBundle;
PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os);
// if (signedOnly) {
// outputBundle = builder.build().verifyText();
// } else {
builder.assumeSymmetric(false)
.passphrase(passphrase);
// Do we want to do this: instead of trying to get the passphrase before
// pause stream when passphrase is missing and then resume???
// TODO: this also decrypts with other secret keys without passphrase!!!
outputBundle = builder.build().execute();
// }
// outputStream.close();
// byte[] outputBytes = ((ByteArrayOutputStream) outputStream).toByteArray();
// get signature informations from bundle
boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE, false);
if (signature) {
long signatureKeyId = outputBundle
.getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, 0);
String signatureUserId = outputBundle
.getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID);
boolean signatureSuccess = outputBundle
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false);
boolean signatureUnknown = outputBundle
.getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, false);
boolean signatureOnly = outputBundle
.getBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false);
int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR;
if (signatureSuccess) {
signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED;
} else if (signatureUnknown) {
signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY;
}
sigResult = new OpenPgpSignatureResult(signatureStatus, signatureUserId,
signatureOnly, signatureKeyId);
}
} finally {
is.close();
os.close();
}
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS);
result.putParcelable(OpenPgpConstants.RESULT_SIGNATURE, sigResult);
return result;
} catch (Exception e) {
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
return result;
}
}
private Bundle getKeyIdsImpl(Bundle params) {
// get key ids based on given user ids
String[] userIds = params.getStringArray(OpenPgpConstants.PARAMS_USER_IDS);
Bundle result = getKeyIdsFromEmails(params, userIds);
return result;
}
/**
* Returns error to IOpenPgpCallback
*
* @param callback
* @param errorId
* @param message
* Check requirements:
* - params != null
* - has supported API version
* - is allowed to call the service (access has been granted)
*
* @param params
* @return null if everything is okay, or a Bundle with an error/PendingIntent
*/
private void callbackOpenPgpError(IOpenPgpCallback callback, int errorId, String message) {
try {
callback.onError(new OpenPgpError(0, message));
} catch (Exception t) {
Log.e(Constants.TAG,
"Exception while returning OpenPgpError to client via callback.onError()", t);
}
}
private void callbackOpenPgpError(IOpenPgpKeyIdsCallback callback, int errorId, String message) {
try {
callback.onError(new OpenPgpError(0, message));
} catch (Exception t) {
Log.e(Constants.TAG,
"Exception while returning OpenPgpError to client via callback.onError()", t);
private Bundle checkRequirements(Bundle params) {
// params Bundle is required!
if (params == null) {
Bundle result = new Bundle();
OpenPgpError error = new OpenPgpError(OpenPgpError.GENERIC_ERROR, "params Bundle required!");
result.putParcelable(OpenPgpConstants.RESULT_ERRORS, error);
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
return result;
}
// version code is required and needs to correspond to version code of service!
if (params.getInt(OpenPgpConstants.PARAMS_API_VERSION) != OpenPgpConstants.API_VERSION) {
Bundle result = new Bundle();
OpenPgpError error = new OpenPgpError(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!");
result.putParcelable(OpenPgpConstants.RESULT_ERRORS, error);
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
return result;
}
// check if caller is allowed to access openpgp keychain
Bundle result = isAllowed(params);
if (result != null) {
return result;
}
return null;
}
// TODO: multi-threading
private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() {
@Override
public void encrypt(final OpenPgpData input, final OpenPgpData output, final long[] keyIds,
final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
public Bundle sign(Bundle params, final ParcelFileDescriptor input, final ParcelFileDescriptor output) {
final AppSettings appSettings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
encryptAndSignSafe(input, output, keyIds, true, callback, settings, false);
}
};
Bundle errorResult = checkRequirements(params);
if (errorResult != null) {
return errorResult;
}
checkAndEnqueue(r);
return signImpl(params, input, output, appSettings);
}
@Override
public void signAndEncrypt(final OpenPgpData input, final OpenPgpData output,
final long[] keyIds, final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
public Bundle encrypt(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
final AppSettings appSettings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
encryptAndSignSafe(input, output, keyIds, true, callback, settings, true);
}
};
Bundle errorResult = checkRequirements(params);
if (errorResult != null) {
return errorResult;
}
checkAndEnqueue(r);
return encryptAndSignImpl(params, input, output, appSettings, false);
}
@Override
public void sign(final OpenPgpData input, final OpenPgpData output,
final IOpenPgpCallback callback) throws RemoteException {
final AppSettings settings = getAppSettings();
public Bundle signAndEncrypt(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
final AppSettings appSettings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
signSafe(getInput(input), true, callback, settings);
}
};
Bundle errorResult = checkRequirements(params);
if (errorResult != null) {
return errorResult;
}
checkAndEnqueue(r);
return encryptAndSignImpl(params, input, output, appSettings, true);
}
@Override
public void decryptAndVerify(final OpenPgpData input, final OpenPgpData output,
final IOpenPgpCallback callback) throws RemoteException {
public Bundle decryptAndVerify(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) {
final AppSettings appSettings = getAppSettings();
final AppSettings settings = getAppSettings();
Bundle errorResult = checkRequirements(params);
if (errorResult != null) {
return errorResult;
}
Runnable r = new Runnable() {
@Override
public void run() {
decryptAndVerifySafe(getInput(input), true, callback, settings);
}
};
checkAndEnqueue(r);
return decryptAndVerifyImpl(params, input, output, appSettings);
}
@Override
public void getKeyIds(final String[] userIds, final boolean allowUserInteraction,
final IOpenPgpKeyIdsCallback callback) throws RemoteException {
public Bundle getKeyIds(Bundle params) {
Bundle errorResult = checkRequirements(params);
if (errorResult != null) {
return errorResult;
}
final AppSettings settings = getAppSettings();
Runnable r = new Runnable() {
@Override
public void run() {
getKeyIdsSafe(userIds, allowUserInteraction, callback, settings);
}
};
checkAndEnqueue(r);
return getKeyIdsImpl(params);
}
};
private static byte[] getInput(OpenPgpData data) {
// TODO: support Uri and ParcelFileDescriptor
byte[] inBytes = null;
switch (data.getType()) {
case OpenPgpData.TYPE_STRING:
inBytes = data.getString().getBytes();
break;
case OpenPgpData.TYPE_BYTE_ARRAY:
inBytes = data.getBytes();
break;
default:
Log.e(Constants.TAG, "Uri and ParcelFileDescriptor not supported right now!");
break;
}
return inBytes;
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2013-2014 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
@@ -19,17 +19,16 @@ package org.sufficientlysecure.keychain.service.remote;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.openintents.openpgp.OpenPgpError;
import org.openintents.openpgp.util.OpenPgpConstants;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.exception.WrongPackageSignatureException;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.PausableThreadPoolExecutor;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -50,66 +49,19 @@ import android.os.Messenger;
public abstract class RemoteService extends Service {
Context mContext;
private final ArrayBlockingQueue<Runnable> mPoolQueue = new ArrayBlockingQueue<Runnable>(100);
// TODO: Are these parameters okay?
private PausableThreadPoolExecutor mThreadPool = new PausableThreadPoolExecutor(2, 4, 10,
TimeUnit.SECONDS, mPoolQueue);
private static final int PRIVATE_REQUEST_CODE_REGISTER = 651;
private static final int PRIVATE_REQUEST_CODE_ERROR = 652;
private final Object userInputLock = new Object();
/**
* Override handleUserInput() to handle OKAY (1) and CANCEL (0). After handling the waiting
* threads will be notified and the queue resumed
*/
protected class UserInputCallback extends BaseCallback {
public void handleUserInput(Message msg) {
}
@Override
public boolean handleMessage(Message msg) {
handleUserInput(msg);
// resume
synchronized (userInputLock) {
userInputLock.notifyAll();
}
mThreadPool.resume();
return true;
}
}
/**
* Extends Handler.Callback with OKAY (1), CANCEL (0) variables
*/
private class BaseCallback implements Handler.Callback {
public static final int OKAY = 1;
public static final int CANCEL = 0;
@Override
public boolean handleMessage(Message msg) {
return false;
}
}
public Context getContext() {
return mContext;
}
/**
* Should be used from Stub implementations of AIDL interfaces to enqueue a runnable for
* execution
*
* @param r
*/
protected void checkAndEnqueue(Runnable r) {
protected Bundle isAllowed(Bundle params) {
try {
if (isCallerAllowed(false)) {
mThreadPool.execute(r);
Log.d(Constants.TAG, "Enqueued runnable…");
return null;
} else {
String[] callingPackages = getPackageManager().getPackagesForUid(
Binder.getCallingUid());
@@ -121,32 +73,46 @@ public abstract class RemoteService extends Service {
packageSignature = getPackageSignature(packageName);
} catch (NameNotFoundException e) {
Log.e(Constants.TAG, "Should not happen, returning!", e);
return;
// return error
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR);
result.putParcelable(OpenPgpConstants.RESULT_ERRORS,
new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage()));
return result;
}
Log.e(Constants.TAG,
"Not allowed to use service! Starting activity for registration!");
Bundle extras = new Bundle();
extras.putString(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
extras.putByteArray(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
RegisterActivityCallback callback = new RegisterActivityCallback();
Log.e(Constants.TAG, "Not allowed to use service! return PendingIntent for registration!");
pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_REGISTER, callback,
extras);
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_REGISTER);
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName);
intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature);
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
if (callback.isAllowed()) {
mThreadPool.execute(r);
Log.d(Constants.TAG, "Enqueued runnable…");
} else {
Log.d(Constants.TAG, "User disallowed app!");
}
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_REGISTER, intent, 0);
// return PendingIntent to be executed by client
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
return result;
}
} catch (WrongPackageSignatureException e) {
Log.e(Constants.TAG, e.getMessage());
Log.e(Constants.TAG, "wrong signature!", e);
Bundle extras = new Bundle();
extras.putString(RemoteServiceActivity.EXTRA_ERROR_MESSAGE,
getString(R.string.api_error_wrong_signature));
pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_ERROR_MESSAGE, null, extras);
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE);
intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, getString(R.string.api_error_wrong_signature));
intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_ERROR, intent, 0);
// return PendingIntent to be executed by client
Bundle result = new Bundle();
result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED);
result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi);
return result;
}
}
@@ -160,41 +126,9 @@ public abstract class RemoteService extends Service {
return packageSignature;
}
/**
* Locks current thread and pauses execution of runnables and starts activity for user input
*
* @param action
* @param messenger
* @param extras
*/
protected void pauseAndStartUserInteraction(String action, BaseCallback callback, Bundle extras) {
synchronized (userInputLock) {
mThreadPool.pause();
Log.d(Constants.TAG, "starting activity...");
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(action);
Messenger messenger = new Messenger(new Handler(getMainLooper(), callback));
extras.putParcelable(RemoteServiceActivity.EXTRA_MESSENGER, messenger);
intent.putExtras(extras);
startActivity(intent);
// lock current thread for user input
try {
userInputLock.wait();
} catch (InterruptedException e) {
Log.e(Constants.TAG, "CryptoService", e);
}
}
}
/**
* Retrieves AppSettings from database for the application calling this remote service
*
*
* @return
*/
protected AppSettings getAppSettings() {
@@ -215,66 +149,11 @@ public abstract class RemoteService extends Service {
return null;
}
class RegisterActivityCallback extends BaseCallback {
public static final String PACKAGE_NAME = "package_name";
private boolean allowed = false;
private String packageName;
public boolean isAllowed() {
return allowed;
}
public String getPackageName() {
return packageName;
}
@Override
public boolean handleMessage(Message msg) {
if (msg.arg1 == OKAY) {
allowed = true;
packageName = msg.getData().getString(PACKAGE_NAME);
// resume threads
try {
if (isPackageAllowed(packageName)) {
synchronized (userInputLock) {
userInputLock.notifyAll();
}
mThreadPool.resume();
} else {
// Should not happen!
Log.e(Constants.TAG, "Should not happen! Emergency shutdown!");
mThreadPool.shutdownNow();
}
} catch (WrongPackageSignatureException e) {
Log.e(Constants.TAG, e.getMessage());
Bundle extras = new Bundle();
extras.putString(RemoteServiceActivity.EXTRA_ERROR_MESSAGE,
getString(R.string.api_error_wrong_signature));
pauseAndStartUserInteraction(RemoteServiceActivity.ACTION_ERROR_MESSAGE, null,
extras);
}
} else {
allowed = false;
synchronized (userInputLock) {
userInputLock.notifyAll();
}
mThreadPool.resume();
}
return true;
}
}
/**
* Checks if process that binds to this service (i.e. the package name corresponding to the
* process) is in the list of allowed package names.
*
* @param allowOnlySelf
* allow only Keychain app itself
*
* @param allowOnlySelf allow only Keychain app itself
* @return true if process is allowed to use this service
* @throws WrongPackageSignatureException
*/
@@ -308,7 +187,7 @@ public abstract class RemoteService extends Service {
/**
* Checks if packageName is a registered app for the API. Does not return true for own package!
*
*
* @param packageName
* @return
* @throws WrongPackageSignatureException

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2013-2014 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
@@ -17,8 +17,15 @@
package org.sufficientlysecure.keychain.service.remote;
import java.util.ArrayList;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import org.openintents.openpgp.util.OpenPgpConstants;
import org.sufficientlysecure.htmltextview.HtmlTextView;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
@@ -30,15 +37,7 @@ import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment;
import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
public class RemoteServiceActivity extends ActionBarActivity {
@@ -64,17 +63,11 @@ public class RemoteServiceActivity extends ActionBarActivity {
// error message
public static final String EXTRA_ERROR_MESSAGE = "error_message";
private Messenger mMessenger;
// register view
private AppSettingsFragment mSettingsFragment;
// select pub keys view
private SelectPublicKeyFragment mSelectFragment;
// has the user clicked one of the buttons
// or do we need to handle the callback in onStop()
private boolean finishHandled;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -82,36 +75,12 @@ public class RemoteServiceActivity extends ActionBarActivity {
handleActions(getIntent(), savedInstanceState);
}
@Override
protected void onStop() {
super.onStop();
if (!finishHandled) {
Message msg = Message.obtain();
msg.arg1 = RemoteService.RegisterActivityCallback.CANCEL;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
}
}
protected void handleActions(Intent intent, Bundle savedInstanceState) {
finishHandled = false;
String action = intent.getAction();
Bundle extras = intent.getExtras();
final Bundle extras = intent.getExtras();
if (extras == null) {
extras = new Bundle();
}
mMessenger = extras.getParcelable(EXTRA_MESSENGER);
/**
* com.android.crypto actions
*/
if (ACTION_REGISTER.equals(action)) {
final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE);
@@ -125,44 +94,27 @@ public class RemoteServiceActivity extends ActionBarActivity {
// user needs to select a key!
if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) {
Toast.makeText(RemoteServiceActivity.this,
R.string.api_register_error_select_key, Toast.LENGTH_LONG)
.show();
mSettingsFragment.setErrorOnSelectKeyFragment(
getString(R.string.api_register_error_select_key));
} else {
ProviderHelper.insertApiApp(RemoteServiceActivity.this,
mSettingsFragment.getAppSettings());
Message msg = Message.obtain();
msg.arg1 = RemoteService.RegisterActivityCallback.OKAY;
Bundle data = new Bundle();
data.putString(RemoteService.RegisterActivityCallback.PACKAGE_NAME,
packageName);
msg.setData(data);
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
// give params through for new service call
Bundle oldParams = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
finishHandled = true;
finish();
Intent finishIntent = new Intent();
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, oldParams);
RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
RemoteServiceActivity.this.finish();
}
}
}, R.string.api_register_disallow, new View.OnClickListener() {
@Override
public void onClick(View v) {
// Disallow
Message msg = Message.obtain();
msg.arg1 = RemoteService.RegisterActivityCallback.CANCEL;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
finishHandled = true;
finish();
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
RemoteServiceActivity.this.finish();
}
}
);
@@ -176,8 +128,9 @@ public class RemoteServiceActivity extends ActionBarActivity {
mSettingsFragment.setAppSettings(settings);
} else if (ACTION_CACHE_PASSPHRASE.equals(action)) {
long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID);
Bundle oldParams = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
showPassphraseDialog(secretKeyId);
showPassphraseDialog(oldParams, secretKeyId);
} else if (ACTION_SELECT_PUB_KEYS.equals(action)) {
long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS);
ArrayList<String> missingUserIds = intent
@@ -185,8 +138,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
ArrayList<String> dublicateUserIds = intent
.getStringArrayListExtra(EXTRA_DUBLICATE_USER_IDS);
String text = new String();
text += "<b>" + getString(R.string.api_select_pub_keys_text) + "</b>";
// TODO: do this with spannable instead of HTML to prevent parsing failures with weird user ids
String text = "<b>" + getString(R.string.api_select_pub_keys_text) + "</b>";
text += "<br/><br/>";
if (missingUserIds != null && missingUserIds.size() > 0) {
text += getString(R.string.api_select_pub_keys_missing_text);
@@ -213,40 +166,22 @@ public class RemoteServiceActivity extends ActionBarActivity {
new View.OnClickListener() {
@Override
public void onClick(View v) {
// ok
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.OKAY;
Bundle data = new Bundle();
data.putLongArray(
OpenPgpService.SelectPubKeysActivityCallback.PUB_KEY_IDS,
// add key ids to params Bundle for new request
Bundle params = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS);
params.putLongArray(OpenPgpConstants.PARAMS_KEY_IDS,
mSelectFragment.getSelectedMasterKeyIds());
msg.setData(data);
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
finishHandled = true;
finish();
Intent finishIntent = new Intent();
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
RemoteServiceActivity.this.finish();
}
}, R.string.btn_do_not_save, new View.OnClickListener() {
@Override
public void onClick(View v) {
// cancel
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.SelectPubKeysActivityCallback.CANCEL;
;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
finishHandled = true;
finish();
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
RemoteServiceActivity.this.finish();
}
}
);
@@ -279,8 +214,7 @@ public class RemoteServiceActivity extends ActionBarActivity {
} else if (ACTION_ERROR_MESSAGE.equals(action)) {
String errorMessage = intent.getStringExtra(EXTRA_ERROR_MESSAGE);
String text = new String();
text += "<font color=\"red\">" + errorMessage + "</font>";
String text = "<font color=\"red\">" + errorMessage + "</font>";
// Inflate a "Done" custom action bar view
ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_okay,
@@ -288,7 +222,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
@Override
public void onClick(View v) {
finish();
RemoteServiceActivity.this.setResult(RESULT_OK);
RemoteServiceActivity.this.finish();
}
});
@@ -298,7 +233,8 @@ public class RemoteServiceActivity extends ActionBarActivity {
HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text);
textView.setHtmlFromString(text);
} else {
Log.e(Constants.TAG, "Wrong action!");
Log.e(Constants.TAG, "Action does not exist!");
setResult(RESULT_CANCELED);
finish();
}
}
@@ -308,31 +244,21 @@ public class RemoteServiceActivity extends ActionBarActivity {
* encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks
* for a symmetric passphrase
*/
private void showPassphraseDialog(long secretKeyId) {
private void showPassphraseDialog(final Bundle params, long secretKeyId) {
// Message is received after passphrase is cached
Handler returnHandler = new Handler() {
@Override
public void handleMessage(Message message) {
if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) {
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.PassphraseActivityCallback.OKAY;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
// return given params again, for calling the service method again
Intent finishIntent = new Intent();
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent);
} else {
Message msg = Message.obtain();
msg.arg1 = OpenPgpService.PassphraseActivityCallback.CANCEL;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
Log.e(Constants.TAG, "CryptoServiceActivity", e);
}
RemoteServiceActivity.this.setResult(RESULT_CANCELED);
}
finishHandled = true;
finish();
RemoteServiceActivity.this.finish();
}
};
@@ -345,9 +271,12 @@ public class RemoteServiceActivity extends ActionBarActivity {
passphraseDialog.show(getSupportFragmentManager(), "passphraseDialog");
} catch (PgpGeneralException e) {
Log.d(Constants.TAG, "No passphrase for this secret key, encrypt directly!");
// send message to handler to start encryption directly
returnHandler.sendEmptyMessage(PassphraseDialogFragment.MESSAGE_OKAY);
Log.d(Constants.TAG, "No passphrase for this secret key, do pgp operation directly!");
// return given params again, for calling the service method again
Intent finishIntent = new Intent();
finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params);
setResult(RESULT_OK, finishIntent);
finish();
}
}
}

View File

@@ -1,4 +1,4 @@
package org.sufficientlysecure.keychain.service.exception;
package org.sufficientlysecure.keychain.service.remote;
public class WrongPackageSignatureException extends Exception {