From b67356503511725df1b016d8a8513b6356cb450a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 11 Aug 2014 17:10:47 +0200 Subject: [PATCH] Get original filename for decryption --- .../keychain/pgp/PgpDecryptVerify.java | 23 ++++- .../service/KeychainIntentService.java | 81 ++++++++++++++-- .../keychain/ui/DecryptFileFragment.java | 95 ++++++++++++++++++- .../keychain/ui/EncryptActivity.java | 9 +- 4 files changed, 188 insertions(+), 20 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index 6ce483989..2ab93ca41 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -71,6 +71,7 @@ public class PgpDecryptVerify { private boolean mAllowSymmetricDecryption; private String mPassphrase; private Set mAllowedKeyIds; + private boolean mReturnMetadataOnly; private PgpDecryptVerify(Builder builder) { // private Constructor can only be called from Builder @@ -83,6 +84,7 @@ public class PgpDecryptVerify { this.mAllowSymmetricDecryption = builder.mAllowSymmetricDecryption; this.mPassphrase = builder.mPassphrase; this.mAllowedKeyIds = builder.mAllowedKeyIds; + this.mReturnMetadataOnly = builder.mReturnMetadataOnly; } public static class Builder { @@ -97,6 +99,7 @@ public class PgpDecryptVerify { private boolean mAllowSymmetricDecryption = true; private String mPassphrase = null; private Set mAllowedKeyIds = null; + private boolean mReturnMetadataOnly = false; public Builder(ProviderHelper providerHelper, PassphraseCache passphraseCache, InputData data, OutputStream outStream) { @@ -126,7 +129,16 @@ public class PgpDecryptVerify { * This means only ciphertexts encrypted for one of these private key can be decrypted. */ public Builder setAllowedKeyIds(Set allowedKeyIds) { - this.mAllowedKeyIds = allowedKeyIds; + mAllowedKeyIds = allowedKeyIds; + return this; + } + + /** + * If enabled, the actual decryption/verification of the content will not be executed. + * The metadata only will be decrypted and returned. + */ + public Builder setReturnMetadataOnly(boolean returnMetadataOnly) { + mReturnMetadataOnly = returnMetadataOnly; return this; } @@ -442,7 +454,7 @@ public class PgpDecryptVerify { PGPLiteralData literalData = (PGPLiteralData) dataChunk; // TODO: how to get the real original size? - // this is the encrypted size + // this is the encrypted size so if we enable compression this value is wrong! long originalSize = mData.getSize() - mData.getStreamPosition(); if (originalSize < 0) { originalSize = 0; @@ -455,6 +467,13 @@ public class PgpDecryptVerify { originalSize); result.setDecryptMetadata(metadata); + Log.d(Constants.TAG, "metadata: " + metadata); + + // return here if we want to decrypt the metadata only + if (mReturnMetadataOnly) { + return result; + } + int endProgress; if (signature != null) { endProgress = 90; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 30108d52d..680f2fb27 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -86,6 +86,8 @@ public class KeychainIntentService extends IntentService public static final String ACTION_DECRYPT_VERIFY = Constants.INTENT_PREFIX + "DECRYPT_VERIFY"; + public static final String ACTION_DECRYPT_METADATA = Constants.INTENT_PREFIX + "DECRYPT_METADATA"; + public static final String ACTION_SAVE_KEYRING = Constants.INTENT_PREFIX + "SAVE_KEYRING"; public static final String ACTION_DELETE_FILE_SECURELY = Constants.INTENT_PREFIX @@ -241,6 +243,7 @@ public class KeychainIntentService extends IntentService data.putInt(SELECTED_URI, i); InputData inputData = createEncryptInputData(data); OutputStream outStream = createCryptOutputStream(data); + String originalFilename = getOriginalFilename(data); /* Operation */ PgpSignEncrypt.Builder builder = @@ -262,7 +265,8 @@ public class KeychainIntentService extends IntentService .setSignatureHashAlgorithm( Preferences.getPreferences(this).getDefaultHashAlgorithm()) .setSignaturePassphrase( - PassphraseCacheService.getCachedPassphrase(this, signatureKeyId)); + PassphraseCacheService.getCachedPassphrase(this, signatureKeyId)) + .setOriginalFilename(originalFilename); // this assumes that the bytes are cleartext (valid for current implementation!) if (source == IO_BYTES) { @@ -308,10 +312,10 @@ public class KeychainIntentService extends IntentService KeychainIntentService.this, masterKeyId); } }, - inputData, outStream); - builder.setProgressable(this); - - builder.setAllowSymmetricDecryption(true) + inputData, outStream + ); + builder.setProgressable(this) + .setAllowSymmetricDecryption(true) .setPassphrase(passphrase); PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute(); @@ -326,6 +330,46 @@ public class KeychainIntentService extends IntentService OtherHelper.logDebugBundle(resultData, "resultData"); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + } catch (Exception e) { + sendErrorToHandler(e); + } + } else if (ACTION_DECRYPT_METADATA.equals(action)) { + try { + /* Input */ + String passphrase = data.getString(DECRYPT_PASSPHRASE); + + InputData inputData = createDecryptInputData(data); + + /* Operation */ + + Bundle resultData = new Bundle(); + + // verifyText and decrypt returning additional resultData values for the + // verification of signatures + PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( + new ProviderHelper(this), + new PgpDecryptVerify.PassphraseCache() { + @Override + public String getCachedPassphrase(long masterKeyId) { + return PassphraseCacheService.getCachedPassphrase( + KeychainIntentService.this, masterKeyId); + } + }, + inputData, null + ); + builder.setProgressable(this) + .setAllowSymmetricDecryption(true) + .setPassphrase(passphrase) + .setReturnMetadataOnly(true); + + PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute(); + + resultData.putParcelable(RESULT_DECRYPT_VERIFY_RESULT, decryptVerifyResult); + + /* Output */ + OtherHelper.logDebugBundle(resultData, "resultData"); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); } catch (Exception e) { sendErrorToHandler(e); @@ -356,7 +400,7 @@ public class KeychainIntentService extends IntentService UncachedKeyRing ring = result.getRing(); - providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100)); + providerHelper.saveSecretKeyRing(ring, new ProgressScaler(this, 60, 95, 100)); // cache new passphrase if (saveParcel.mNewPassphrase != null) { @@ -403,7 +447,7 @@ public class KeychainIntentService extends IntentService } else { // get entries from cached file FileImportCache cache = - new FileImportCache(this); + new FileImportCache(this); entries = cache.readCacheIntoList(); } @@ -576,7 +620,7 @@ public class KeychainIntentService extends IntentService CanonicalizedPublicKeyRing publicRing = providerHelper.getCanonicalizedPublicKeyRing(pubKeyId); CanonicalizedSecretKeyRing secretKeyRing = providerHelper.getCanonicalizedSecretKeyRing(masterKeyId); CanonicalizedSecretKey certificationKey = secretKeyRing.getSecretKey(); - if(!certificationKey.unlock(signaturePassphrase)) { + if (!certificationKey.unlock(signaturePassphrase)) { throw new PgpGeneralException("Error extracting key (bad passphrase?)"); } UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds); @@ -729,6 +773,27 @@ public class KeychainIntentService extends IntentService } } + private String getOriginalFilename(Bundle data) throws PgpGeneralException, FileNotFoundException { + int target = data.getInt(TARGET); + switch (target) { + case IO_BYTES: + return ""; + + case IO_URI: + Uri providerUri = data.getParcelable(ENCRYPT_INPUT_URI); + + return FileHelper.getFilename(this, providerUri); + + case IO_URIS: + providerUri = data.getParcelableArrayList(ENCRYPT_INPUT_URIS).get(data.getInt(SELECTED_URI)); + + return FileHelper.getFilename(this, providerUri); + + default: + throw new PgpGeneralException("No target choosen!"); + } + } + private OutputStream createCryptOutputStream(Bundle data) throws PgpGeneralException, FileNotFoundException { int target = data.getInt(TARGET); switch (target) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java index c33b1489a..845fbfa3b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFileFragment.java @@ -23,8 +23,10 @@ import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.os.Message; import android.os.Messenger; +import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -38,6 +40,7 @@ import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Notify; @@ -113,7 +116,8 @@ public class DecryptFileFragment extends DecryptFragment { return; } - askForOutputFilename(); +// askForOutputFilename(); + decryptOriginalFilename(null); } private String removeEncryptedAppend(String name) { @@ -123,8 +127,13 @@ public class DecryptFileFragment extends DecryptFragment { return name; } - private void askForOutputFilename() { - String targetName = removeEncryptedAppend(FileHelper.getFilename(getActivity(), mInputUri)); + private void askForOutputFilename(String originalFilename) { + String targetName; + if (!TextUtils.isEmpty(originalFilename)) { + targetName = originalFilename; + } else { + targetName = removeEncryptedAppend(FileHelper.getFilename(getActivity(), mInputUri)); + } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { File file = new File(mInputUri.getPath()); File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; @@ -136,6 +145,82 @@ public class DecryptFileFragment extends DecryptFragment { } } + private void decryptOriginalFilename(String passphrase) { + Log.d(Constants.TAG, "decryptOriginalFilename"); + + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + + // fill values for this action + Bundle data = new Bundle(); + intent.setAction(KeychainIntentService.ACTION_DECRYPT_METADATA); + + // data + Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri); + + data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URI); + data.putParcelable(KeychainIntentService.ENCRYPT_INPUT_URI, mInputUri); + + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URI); + data.putParcelable(KeychainIntentService.ENCRYPT_OUTPUT_URI, mOutputUri); + + data.putString(KeychainIntentService.DECRYPT_PASSPHRASE, passphrase); + + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after decrypting is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), + getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + // get returned data bundle + Bundle returnData = message.getData(); + + PgpDecryptVerifyResult decryptVerifyResult = + returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); + + if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) { + showPassphraseDialogForFilename(decryptVerifyResult.getKeyIdPassphraseNeeded()); + } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED == + decryptVerifyResult.getStatus()) { + showPassphraseDialogForFilename(Constants.key.symmetric); + } else { + + // go on... + askForOutputFilename(decryptVerifyResult.getDecryptMetadata().getFilename()); + } + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + } + + protected void showPassphraseDialogForFilename(long keyId) { + PassphraseDialogFragment.show(getActivity(), keyId, + new Handler() { + @Override + public void handleMessage(Message message) { + if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { + String passphrase = + message.getData().getString(PassphraseDialogFragment.MESSAGE_DATA_PASSPHRASE); + decryptOriginalFilename(passphrase); + } + } + } + ); + } + @Override protected void decryptStart(String passphrase) { Log.d(Constants.TAG, "decryptStart"); @@ -161,7 +246,7 @@ public class DecryptFileFragment extends DecryptFragment { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after encrypting is done in KeychainIntentService + // Message is received after decrypting is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { @@ -178,7 +263,7 @@ public class DecryptFileFragment extends DecryptFragment { if (PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED == decryptVerifyResult.getStatus()) { showPassphraseDialog(decryptVerifyResult.getKeyIdPassphraseNeeded()); } else if (PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED == - decryptVerifyResult.getStatus()) { + decryptVerifyResult.getStatus()) { showPassphraseDialog(Constants.key.symmetric); } else { // display signature result in activity diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index ab26d539a..a5cf4d84d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -264,12 +264,12 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn // fill values for this action Bundle data = new Bundle(); - int compressionId; if (isContentMessage()) { data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES); data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mMessage.getBytes()); - compressionId = Preferences.getPreferences(this).getDefaultMessageCompression(); + data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, + Preferences.getPreferences(this).getDefaultMessageCompression()); } else { data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_URIS); data.putParcelableArrayList(KeychainIntentService.ENCRYPT_INPUT_URIS, mInputUris); @@ -277,10 +277,10 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_URIS); data.putParcelableArrayList(KeychainIntentService.ENCRYPT_OUTPUT_URIS, mOutputUris); - compressionId = Preferences.getPreferences(this).getDefaultFileCompression(); + data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, + Preferences.getPreferences(this).getDefaultFileCompression()); } - data.putInt(KeychainIntentService.ENCRYPT_COMPRESSION_ID, compressionId); // Always use armor for messages data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, mUseArmor || isContentMessage()); @@ -429,7 +429,6 @@ public class EncryptActivity extends DrawerActivity implements EncryptActivityIn if (isModeSymmetric()) { // symmetric encryption checks - if (mPassphrase == null) { Notify.showNotify(this, R.string.passphrases_do_not_match, Notify.Style.ERROR); return false;