From 0d8370be1d7ae25baa053e681e8739e43120c2cd Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 30 May 2015 13:25:47 +0200 Subject: [PATCH] rewrite PgpDecryptVerify input, introduce PgpDecryptVerifyInputParcel --- .../results/DecryptVerifyResult.java | 31 ++- .../keychain/pgp/PgpDecryptVerify.java | 217 +++++++----------- .../pgp/PgpDecryptVerifyInputParcel.java | 162 +++++++++++++ .../keychain/provider/ProviderHelper.java | 4 +- .../keychain/remote/OpenPgpService.java | 55 +++-- .../service/KeychainIntentService.java | 184 ++------------- .../keychain/ui/DecryptFilesFragment.java | 12 +- .../keychain/ui/DecryptTextFragment.java | 11 +- .../ui/EncryptModeAsymmetricFragment.java | 3 + .../keychain/ui/PassphraseDialogActivity.java | 2 - 10 files changed, 331 insertions(+), 350 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java index 7680107f8..ac571390a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java @@ -36,6 +36,23 @@ public class DecryptVerifyResult extends InputPendingResult { // https://tools.ietf.org/html/rfc4880#page56 String mCharset; + byte[] mOutputBytes; + + public DecryptVerifyResult(int result, OperationLog log) { + super(result, log); + } + + public DecryptVerifyResult(OperationLog log, RequiredInputParcel requiredInput) { + super(log, requiredInput); + } + + public DecryptVerifyResult(Parcel source) { + super(source); + mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader()); + mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader()); + } + + public boolean isKeysDisallowed () { return (mResult & RESULT_KEY_DISALLOWED) == RESULT_KEY_DISALLOWED; } @@ -64,18 +81,12 @@ public class DecryptVerifyResult extends InputPendingResult { mCharset = charset; } - public DecryptVerifyResult(int result, OperationLog log) { - super(result, log); + public void setOutputBytes(byte[] outputBytes) { + mOutputBytes = outputBytes; } - public DecryptVerifyResult(OperationLog log, RequiredInputParcel requiredInput) { - super(log, requiredInput); - } - - public DecryptVerifyResult(Parcel source) { - super(source); - mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader()); - mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader()); + public byte[] getOutputBytes() { + return mOutputBytes; } public int describeContents() { 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 c5303fc9e..4382c9fae 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -58,6 +58,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Passphrase; @@ -66,6 +67,7 @@ import org.sufficientlysecure.keychain.util.ProgressScaler; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -73,147 +75,90 @@ import java.net.URLConnection; import java.security.SignatureException; import java.util.Date; import java.util.Iterator; -import java.util.Set; /** * This class uses a Builder pattern! */ public class PgpDecryptVerify extends BaseOperation { - private InputData mData; - private OutputStream mOutStream; - - private boolean mAllowSymmetricDecryption; - private Set mAllowedKeyIds; - private boolean mDecryptMetadataOnly; - private byte[] mDetachedSignature; - private String mRequiredSignerFingerprint; - private boolean mSignedLiteralData; - - protected PgpDecryptVerify(Builder builder) { - super(builder.mContext, builder.mProviderHelper, builder.mProgressable); - - // private Constructor can only be called from Builder - this.mData = builder.mData; - this.mOutStream = builder.mOutStream; - - this.mAllowSymmetricDecryption = builder.mAllowSymmetricDecryption; - this.mAllowedKeyIds = builder.mAllowedKeyIds; - this.mDecryptMetadataOnly = builder.mDecryptMetadataOnly; - this.mDetachedSignature = builder.mDetachedSignature; - this.mSignedLiteralData = builder.mSignedLiteralData; - this.mRequiredSignerFingerprint = builder.mRequiredSignerFingerprint; - } - - public static class Builder { - // mandatory parameter - private Context mContext; - private ProviderHelper mProviderHelper; - private InputData mData; - - // optional - private OutputStream mOutStream = null; - private Progressable mProgressable = null; - private boolean mAllowSymmetricDecryption = true; - private Set mAllowedKeyIds = null; - private boolean mDecryptMetadataOnly = false; - private byte[] mDetachedSignature = null; - private String mRequiredSignerFingerprint = null; - private boolean mSignedLiteralData = false; - - public Builder(Context context, ProviderHelper providerHelper, - Progressable progressable, - InputData data, OutputStream outStream) { - mContext = context; - mProviderHelper = providerHelper; - mProgressable = progressable; - mData = data; - mOutStream = outStream; - } - - /** - * This is used when verifying signed literals to check that they are signed with - * the required key - */ - public Builder setRequiredSignerFingerprint(String fingerprint) { - mRequiredSignerFingerprint = fingerprint; - return this; - } - - /** - * This is to force a mode where the message is just the signature key id and - * then a literal data packet; used in Keybase.io proofs - */ - public Builder setSignedLiteralData(boolean signedLiteralData) { - mSignedLiteralData = signedLiteralData; - return this; - } - - public Builder setAllowSymmetricDecryption(boolean allowSymmetricDecryption) { - mAllowSymmetricDecryption = allowSymmetricDecryption; - return this; - } - - /** - * Allow these key ids alone for decryption. - * This means only ciphertexts encrypted for one of these private key can be decrypted. - */ - public Builder setAllowedKeyIds(Set 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 setDecryptMetadataOnly(boolean decryptMetadataOnly) { - mDecryptMetadataOnly = decryptMetadataOnly; - return this; - } - - /** - * If detachedSignature != null, it will be used exclusively to verify the signature - */ - public Builder setDetachedSignature(byte[] detachedSignature) { - mDetachedSignature = detachedSignature; - return this; - } - - public PgpDecryptVerify build() { - return new PgpDecryptVerify(this); - } + public PgpDecryptVerify(Context context, ProviderHelper providerHelper, Progressable progressable) { + super(context, providerHelper, progressable); } /** * Decrypts and/or verifies data based on parameters of class */ - public DecryptVerifyResult execute(CryptoInputParcel cryptoInput) { + public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput) { + InputData inputData; + OutputStream outputStream; + + if (input.getInputBytes() != null) { + byte[] inputBytes = input.getInputBytes(); + inputData = new InputData(new ByteArrayInputStream(inputBytes), inputBytes.length); + } else { + try { + InputStream inputStream = mContext.getContentResolver().openInputStream(input.getInputUri()); + long inputSize = FileHelper.getFileSize(mContext, input.getInputUri(), 0); + inputData = new InputData(inputStream, inputSize); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + } + + if (input.getOutputUri() == null) { + outputStream = new ByteArrayOutputStream(); + } else { + try { + outputStream = mContext.getContentResolver().openOutputStream(input.getOutputUri()); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } + } + + DecryptVerifyResult result = executeInternal(input, cryptoInput, inputData, outputStream); + if (outputStream instanceof ByteArrayOutputStream) { + byte[] outputData = ((ByteArrayOutputStream) outputStream).toByteArray(); + result.setOutputBytes(outputData); + } + + return result; + + } + + public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, + InputData inputData, OutputStream outputStream) { + return executeInternal(input, cryptoInput, inputData, outputStream); + } + + private DecryptVerifyResult executeInternal(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, + InputData inputData, OutputStream outputStream) { try { - if (mDetachedSignature != null) { + if (input.getDetachedSignature() != null) { Log.d(Constants.TAG, "Detached signature present, verifying with this signature only"); - return verifyDetachedSignature(mData.getInputStream(), 0); + return verifyDetachedSignature(input, inputData, outputStream, 0); } else { // automatically works with PGP ascii armor and PGP binary - InputStream in = PGPUtil.getDecoderStream(mData.getInputStream()); + InputStream in = PGPUtil.getDecoderStream(inputData.getInputStream()); if (in instanceof ArmoredInputStream) { ArmoredInputStream aIn = (ArmoredInputStream) in; // it is ascii armored Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine()); - if (mSignedLiteralData) { - return verifySignedLiteralData(aIn, 0); + if (input.isSignedLiteralData()) { + return verifySignedLiteralData(input, aIn, outputStream, 0); } else if (aIn.isClearText()) { // a cleartext signature, verify it with the other method return verifyCleartextSignature(aIn, 0); } else { // else: ascii armored encryption! go on... - return decryptVerify(cryptoInput, in, 0); + return decryptVerify(input, cryptoInput, in, outputStream, 0); } } else { - return decryptVerify(cryptoInput, in, 0); + return decryptVerify(input, cryptoInput, in, outputStream, 0); } } } catch (PGPException e) { @@ -232,7 +177,8 @@ public class PgpDecryptVerify extends BaseOperation { /** * Verify Keybase.io style signed literal data */ - private DecryptVerifyResult verifySignedLiteralData(InputStream in, int indent) + private DecryptVerifyResult verifySignedLiteralData( + PgpDecryptVerifyInputParcel input, InputStream in, OutputStream out, int indent) throws IOException, PGPException { OperationLog log = new OperationLog(); log.add(LogType.MSG_VL, indent); @@ -283,9 +229,9 @@ public class PgpDecryptVerify extends BaseOperation { } String fingerprint = KeyFormattingUtils.convertFingerprintToHex(signingRing.getFingerprint()); - if (!(mRequiredSignerFingerprint.equals(fingerprint))) { + if (!(input.getRequiredSignerFingerprint().equals(fingerprint))) { log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent); - Log.d(Constants.TAG, "Fingerprint mismatch; wanted " + mRequiredSignerFingerprint + + Log.d(Constants.TAG, "Fingerprint mismatch; wanted " + input.getRequiredSignerFingerprint() + " got " + fingerprint + "!"); return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } @@ -317,7 +263,7 @@ public class PgpDecryptVerify extends BaseOperation { int length; byte[] buffer = new byte[1 << 16]; while ((length = dataIn.read(buffer)) > 0) { - mOutStream.write(buffer, 0, length); + out.write(buffer, 0, length); signature.update(buffer, 0, length); } @@ -363,8 +309,9 @@ public class PgpDecryptVerify extends BaseOperation { /** * Decrypt and/or verifies binary or ascii armored pgp */ - private DecryptVerifyResult decryptVerify(CryptoInputParcel cryptoInput, - InputStream in, int indent) throws IOException, PGPException { + private DecryptVerifyResult decryptVerify( + PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, + InputStream in, OutputStream out, int indent) throws IOException, PGPException { OperationLog log = new OperationLog(); @@ -455,13 +402,13 @@ public class PgpDecryptVerify extends BaseOperation { } // allow only specific keys for decryption? - if (mAllowedKeyIds != null) { + if (input.getAllowedKeyIds() != null) { long masterKeyId = secretKeyRing.getMasterKeyId(); Log.d(Constants.TAG, "encData.getKeyID(): " + subKeyId); - Log.d(Constants.TAG, "mAllowedKeyIds: " + mAllowedKeyIds); + Log.d(Constants.TAG, "mAllowedKeyIds: " + input.getAllowedKeyIds()); Log.d(Constants.TAG, "masterKeyId: " + masterKeyId); - if (!mAllowedKeyIds.contains(masterKeyId)) { + if (!input.getAllowedKeyIds().contains(masterKeyId)) { // this key is in our db, but NOT allowed! // continue with the next packet in the while loop skippedDisallowedKey = true; @@ -515,7 +462,7 @@ public class PgpDecryptVerify extends BaseOperation { log.add(LogType.MSG_DC_SYM, indent); - if (!mAllowSymmetricDecryption) { + if (!input.isAllowSymmetricDecryption()) { log.add(LogType.MSG_DC_SYM_SKIP, indent + 1); continue; } @@ -764,7 +711,7 @@ public class PgpDecryptVerify extends BaseOperation { } // return here if we want to decrypt the metadata only - if (mDecryptMetadataOnly) { + if (input.isDecryptMetadataOnly()) { log.add(LogType.MSG_DC_OK_META_ONLY, indent); DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); @@ -787,13 +734,13 @@ public class PgpDecryptVerify extends BaseOperation { InputStream dataIn = literalData.getInputStream(); long alreadyWritten = 0; - long wholeSize = mData.getSize() - mData.getStreamPosition(); + long wholeSize = 0; // TODO inputData.getSize() - inputData.getStreamPosition(); int length; byte[] buffer = new byte[1 << 16]; while ((length = dataIn.read(buffer)) > 0) { - Log.d(Constants.TAG, "read bytes: " + length); - if (mOutStream != null) { - mOutStream.write(buffer, 0, length); + // Log.d(Constants.TAG, "read bytes: " + length); + if (out != null) { + out.write(buffer, 0, length); } // update signature buffer if signature is also present @@ -919,8 +866,8 @@ public class PgpDecryptVerify extends BaseOperation { out.close(); byte[] clearText = out.toByteArray(); - if (mOutStream != null) { - mOutStream.write(clearText); + if (out != null) { + out.write(clearText); } updateProgress(R.string.progress_processing_signature, 60, 100); @@ -987,7 +934,8 @@ public class PgpDecryptVerify extends BaseOperation { return result; } - private DecryptVerifyResult verifyDetachedSignature(InputStream in, int indent) + private DecryptVerifyResult verifyDetachedSignature( + PgpDecryptVerifyInputParcel input, InputData inputData, OutputStream out, int indent) throws IOException, PGPException { OperationLog log = new OperationLog(); @@ -997,7 +945,7 @@ public class PgpDecryptVerify extends BaseOperation { signatureResultBuilder.setSignatureOnly(true); updateProgress(R.string.progress_processing_signature, 0, 100); - InputStream detachedSigIn = new ByteArrayInputStream(mDetachedSignature); + InputStream detachedSigIn = new ByteArrayInputStream(input.getDetachedSignature()); detachedSigIn = PGPUtil.getDecoderStream(detachedSigIn); JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(detachedSigIn); @@ -1022,12 +970,13 @@ public class PgpDecryptVerify extends BaseOperation { ProgressScaler progressScaler = new ProgressScaler(mProgressable, 60, 90, 100); long alreadyWritten = 0; - long wholeSize = mData.getSize() - mData.getStreamPosition(); + long wholeSize = inputData.getSize() - inputData.getStreamPosition(); int length; byte[] buffer = new byte[1 << 16]; + InputStream in = inputData.getInputStream(); while ((length = in.read(buffer)) > 0) { - if (mOutStream != null) { - mOutStream.write(buffer, 0, length); + if (out != null) { + out.write(buffer, 0, length); } // update signature buffer if signature is also present diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java new file mode 100644 index 000000000..a6d65688c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyInputParcel.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2015 Dominik Schürmann + * Copyright (C) 2014 Vincent Breitmoser + * + * 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 . + */ + +package org.sufficientlysecure.keychain.pgp; + +import java.util.HashSet; + +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +public class PgpDecryptVerifyInputParcel implements Parcelable { + + private Uri mInputUri; + private Uri mOutputUri; + private byte[] mInputBytes; + + private boolean mAllowSymmetricDecryption; + private HashSet mAllowedKeyIds; + private boolean mDecryptMetadataOnly; + private byte[] mDetachedSignature; + private String mRequiredSignerFingerprint; + private boolean mSignedLiteralData; + + public PgpDecryptVerifyInputParcel() { + } + + public PgpDecryptVerifyInputParcel(Uri inputUri, Uri outputUri) { + mInputUri = inputUri; + mOutputUri = outputUri; + } + + public PgpDecryptVerifyInputParcel(byte[] inputBytes) { + mInputBytes = inputBytes; + } + + PgpDecryptVerifyInputParcel(Parcel source) { + // we do all of those here, so the PgpSignEncryptInput class doesn't have to be parcelable + mInputUri = source.readParcelable(getClass().getClassLoader()); + mOutputUri = source.readParcelable(getClass().getClassLoader()); + mInputBytes = source.createByteArray(); + + mAllowSymmetricDecryption = source.readInt() != 0; + mAllowedKeyIds = (HashSet) source.readSerializable(); + mDecryptMetadataOnly = source.readInt() != 0; + mDetachedSignature = source.createByteArray(); + mRequiredSignerFingerprint = source.readString(); + mSignedLiteralData = source.readInt() != 0; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelable(mInputUri, 0); + dest.writeParcelable(mOutputUri, 0); + dest.writeByteArray(mInputBytes); + + dest.writeInt(mAllowSymmetricDecryption ? 1 : 0); + dest.writeSerializable(mAllowedKeyIds); + dest.writeInt(mDecryptMetadataOnly ? 1 : 0); + dest.writeByteArray(mDetachedSignature); + dest.writeString(mRequiredSignerFingerprint); + dest.writeInt(mSignedLiteralData ? 1 : 0); + } + + byte[] getInputBytes() { + return mInputBytes; + } + + Uri getInputUri() { + return mInputUri; + } + + Uri getOutputUri() { + return mOutputUri; + } + + boolean isAllowSymmetricDecryption() { + return mAllowSymmetricDecryption; + } + + public PgpDecryptVerifyInputParcel setAllowSymmetricDecryption(boolean allowSymmetricDecryption) { + mAllowSymmetricDecryption = allowSymmetricDecryption; + return this; + } + + HashSet getAllowedKeyIds() { + return mAllowedKeyIds; + } + + public PgpDecryptVerifyInputParcel setAllowedKeyIds(HashSet allowedKeyIds) { + mAllowedKeyIds = allowedKeyIds; + return this; + } + + boolean isDecryptMetadataOnly() { + return mDecryptMetadataOnly; + } + + public PgpDecryptVerifyInputParcel setDecryptMetadataOnly(boolean decryptMetadataOnly) { + mDecryptMetadataOnly = decryptMetadataOnly; + return this; + } + + byte[] getDetachedSignature() { + return mDetachedSignature; + } + + public PgpDecryptVerifyInputParcel setDetachedSignature(byte[] detachedSignature) { + mDetachedSignature = detachedSignature; + return this; + } + + String getRequiredSignerFingerprint() { + return mRequiredSignerFingerprint; + } + + public PgpDecryptVerifyInputParcel setRequiredSignerFingerprint(String requiredSignerFingerprint) { + mRequiredSignerFingerprint = requiredSignerFingerprint; + return this; + } + + boolean isSignedLiteralData() { + return mSignedLiteralData; + } + + public PgpDecryptVerifyInputParcel setSignedLiteralData(boolean signedLiteralData) { + mSignedLiteralData = signedLiteralData; + return this; + } + + public static final Creator CREATOR = new Creator() { + public PgpDecryptVerifyInputParcel createFromParcel(final Parcel source) { + return new PgpDecryptVerifyInputParcel(source); + } + + public PgpDecryptVerifyInputParcel[] newArray(final int size) { + return new PgpDecryptVerifyInputParcel[size]; + } + }; + +} + diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index a4ee7fc36..bf7014853 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -1514,8 +1514,8 @@ public class ProviderHelper { return keyIds; } - public Set getAllowedKeyIdsForApp(Uri uri) { - Set keyIds = new HashSet<>(); + public HashSet getAllowedKeyIdsForApp(Uri uri) { + HashSet keyIds = new HashSet<>(); Cursor cursor = mContentResolver.query(uri, null, null, null, null); try { 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 19ad3f610..bef0519a3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEnt import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; import org.sufficientlysecure.keychain.pgp.PgpConstants; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; @@ -63,7 +64,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; -import java.util.Set; +import java.util.HashSet; public class OpenPgpService extends RemoteService { @@ -488,23 +489,23 @@ public class OpenPgpService extends RemoteService { } } - private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input, + private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor inputDescriptor, ParcelFileDescriptor output, boolean decryptMetadataOnly) { - InputStream is = null; - OutputStream os = null; + InputStream inputStream = null; + OutputStream outputStream = null; try { // Get Input- and OutputStream from ParcelFileDescriptor - is = new ParcelFileDescriptor.AutoCloseInputStream(input); + inputStream = new ParcelFileDescriptor.AutoCloseInputStream(inputDescriptor); // output is optional, e.g., for verifying detached signatures if (decryptMetadataOnly || output == null) { - os = null; + outputStream = null; } else { - os = new ParcelFileDescriptor.AutoCloseOutputStream(output); + outputStream = new ParcelFileDescriptor.AutoCloseOutputStream(output); } String currentPkg = getCurrentCallingPackage(); - Set allowedKeyIds = mProviderHelper.getAllowedKeyIdsForApp( + HashSet allowedKeyIds = mProviderHelper.getAllowedKeyIdsForApp( KeychainContract.ApiAllowedKeys.buildBaseUri(currentPkg)); if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) < 7) { @@ -512,33 +513,31 @@ public class OpenPgpService extends RemoteService { ApiAccounts.buildBaseUri(currentPkg))); } - long inputLength = is.available(); - InputData inputData = new InputData(is, inputLength); - - PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( - this, new ProviderHelper(getContext()), null, inputData, os - ); - - CryptoInputParcel inputParcel = CryptoInputParcelCacheService.getCryptoInputParcel(this, data); - if (inputParcel == null) { - inputParcel = new CryptoInputParcel(); + CryptoInputParcel cryptoInput = CryptoInputParcelCacheService.getCryptoInputParcel(this, data); + if (cryptoInput == null) { + cryptoInput = new CryptoInputParcel(); } // override passphrase in input parcel if given by API call if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { - inputParcel = new CryptoInputParcel(inputParcel.getSignatureTime(), + cryptoInput = new CryptoInputParcel(cryptoInput.getSignatureTime(), new Passphrase(data.getCharArrayExtra(OpenPgpApi.EXTRA_PASSPHRASE))); } byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE); + PgpDecryptVerify op = new PgpDecryptVerify(this, mProviderHelper, null); + + long inputLength = inputStream.available(); + // allow only private keys associated with accounts of this app // no support for symmetric encryption - builder.setAllowSymmetricDecryption(false) - .setAllowedKeyIds(allowedKeyIds) - .setDecryptMetadataOnly(decryptMetadataOnly) - .setDetachedSignature(detachedSignature); + PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel() + .setAllowSymmetricDecryption(false) + .setAllowedKeyIds(allowedKeyIds) + .setDecryptMetadataOnly(decryptMetadataOnly) + .setDetachedSignature(detachedSignature); - DecryptVerifyResult pgpResult = builder.build().execute(inputParcel); + DecryptVerifyResult pgpResult = op.execute(input, cryptoInput, inputStream, outputStream); if (pgpResult.isPending()) { // prepare and return PendingIntent to be executed by client @@ -624,16 +623,16 @@ public class OpenPgpService extends RemoteService { result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; } finally { - if (is != null) { + if (inputStream != null) { try { - is.close(); + inputStream.close(); } catch (IOException e) { Log.e(Constants.TAG, "IOException when closing InputStream", e); } } - if (os != null) { + if (outputStream != null) { try { - os.close(); + outputStream.close(); } catch (IOException e) { Log.e(Constants.TAG, "IOException when closing OutputStream", e); } 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 71e149672..05336cc31 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -54,6 +54,7 @@ import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult; import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -61,17 +62,11 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus; -import org.sufficientlysecure.keychain.util.FileHelper; -import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableFileCache; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -122,34 +117,13 @@ public class KeychainIntentService extends IntentService implements Progressable /* keys for data bundle */ - // encrypt, decrypt, import export - public static final String TARGET = "target"; - public static final String SOURCE = "source"; - - // possible targets: - public static enum IOType { - UNKNOWN, - BYTES, - URI; - - private static final IOType[] values = values(); - - public static IOType fromInt(int n) { - if (n < 0 || n >= values.length) { - return UNKNOWN; - } else { - return values[n]; - } - } - } - // encrypt public static final String ENCRYPT_DECRYPT_INPUT_URI = "input_uri"; public static final String ENCRYPT_DECRYPT_OUTPUT_URI = "output_uri"; public static final String SIGN_ENCRYPT_PARCEL = "sign_encrypt_parcel"; // decrypt/verify - public static final String DECRYPT_CIPHERTEXT_BYTES = "ciphertext_bytes"; + public static final String DECRYPT_VERIFY_PARCEL = "decrypt_verify_parcel"; // keybase proof public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint"; @@ -189,14 +163,6 @@ public class KeychainIntentService extends IntentService implements Progressable // consolidate public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery"; - - /* - * possible data keys as result send over messenger - */ - - // decrypt/verify - public static final String RESULT_DECRYPTED_BYTES = "decrypted_data"; - Messenger mMessenger; // this attribute can possibly merged with the one above? not sure... @@ -280,26 +246,16 @@ public class KeychainIntentService extends IntentService implements Progressable } case ACTION_DECRYPT_METADATA: { - try { - /* Input */ - CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT); + /* Input */ + CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT); + PgpDecryptVerifyInputParcel input = data.getParcelable(DECRYPT_VERIFY_PARCEL); - InputData inputData = createDecryptInputData(data); + // verifyText and decrypt returning additional resultData values for the + // verification of signatures + PgpDecryptVerify op = new PgpDecryptVerify(this, new ProviderHelper(this), this); + DecryptVerifyResult decryptVerifyResult = op.execute(input, cryptoInput); - // verifyText and decrypt returning additional resultData values for the - // verification of signatures - PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( - this, new ProviderHelper(this), this, inputData, null - ); - builder.setAllowSymmetricDecryption(true) - .setDecryptMetadataOnly(true); - - DecryptVerifyResult decryptVerifyResult = builder.build().execute(cryptoInput); - - sendMessageToHandler(MessageStatus.OKAY, decryptVerifyResult); - } catch (Exception e) { - sendErrorToHandler(e); - } + sendMessageToHandler(MessageStatus.OKAY, decryptVerifyResult); break; } @@ -356,22 +312,13 @@ public class KeychainIntentService extends IntentService implements Progressable } } - // kind of awkward, but this whole class wants to pull bytes out of “data” - data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal()); - data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, messageBytes); + PgpDecryptVerify op = new PgpDecryptVerify(this, new ProviderHelper(this), this); - InputData inputData = createDecryptInputData(data); - OutputStream outStream = createCryptOutputStream(data); + PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes) + .setSignedLiteralData(true) + .setRequiredSignerFingerprint(requiredFingerprint); - PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( - this, new ProviderHelper(this), this, - inputData, outStream - ); - builder.setSignedLiteralData(true).setRequiredSignerFingerprint(requiredFingerprint); - - DecryptVerifyResult decryptVerifyResult = builder.build().execute( - new CryptoInputParcel()); - outStream.close(); + DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel()); if (!decryptVerifyResult.success()) { OperationLog log = decryptVerifyResult.getLog(); @@ -383,7 +330,7 @@ public class KeychainIntentService extends IntentService implements Progressable return; } - if (!prover.validate(outStream.toString())) { + if (!prover.validate(new String(decryptVerifyResult.getOutputBytes()))) { sendProofError(getString(R.string.keybase_message_payload_mismatch)); return; } @@ -404,40 +351,16 @@ public class KeychainIntentService extends IntentService implements Progressable } case ACTION_DECRYPT_VERIFY: { - try { - /* Input */ - CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT); + /* Input */ + CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT); + PgpDecryptVerifyInputParcel input = data.getParcelable(DECRYPT_VERIFY_PARCEL); - InputData inputData = createDecryptInputData(data); - OutputStream outStream = createCryptOutputStream(data); + /* Operation */ + PgpDecryptVerify op = new PgpDecryptVerify(this, new ProviderHelper(this), this); + DecryptVerifyResult decryptVerifyResult = op.execute(input, cryptoInput); - /* Operation */ - Bundle resultData = new Bundle(); - - // verifyText and decrypt returning additional resultData values for the - // verification of signatures - PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder( - this, new ProviderHelper(this), this, - inputData, outStream - ); - builder.setAllowSymmetricDecryption(true); - - DecryptVerifyResult decryptVerifyResult = builder.build().execute(cryptoInput); - - outStream.close(); - - resultData.putParcelable(DecryptVerifyResult.EXTRA_RESULT, decryptVerifyResult); - - /* Output */ - finalizeDecryptOutputStream(data, resultData, outStream); - Log.logDebugBundle(resultData, "resultData"); - - sendMessageToHandler(MessageStatus.OKAY, resultData); - - } catch (IOException | PgpGeneralException e) { - // TODO get rid of this! - sendErrorToHandler(e); - } + /* Output */ + sendMessageToHandler(MessageStatus.OKAY, decryptVerifyResult); break; } @@ -676,65 +599,6 @@ public class KeychainIntentService extends IntentService implements Progressable sendMessageToHandler(MessageStatus.PREVENT_CANCEL); } - private InputData createDecryptInputData(Bundle data) throws IOException, PgpGeneralException { - return createCryptInputData(data, DECRYPT_CIPHERTEXT_BYTES); - } - - private InputData createCryptInputData(Bundle data, String bytesName) throws PgpGeneralException, IOException { - int source = data.get(SOURCE) != null ? data.getInt(SOURCE) : data.getInt(TARGET); - IOType type = IOType.fromInt(source); - switch (type) { - case BYTES: /* encrypting bytes directly */ - byte[] bytes = data.getByteArray(bytesName); - return new InputData(new ByteArrayInputStream(bytes), bytes.length); - - case URI: /* encrypting content uri */ - Uri providerUri = data.getParcelable(ENCRYPT_DECRYPT_INPUT_URI); - - // InputStream - return new InputData(getContentResolver().openInputStream(providerUri), FileHelper.getFileSize(this, providerUri, 0)); - - default: - throw new PgpGeneralException("No target chosen!"); - } - } - - private OutputStream createCryptOutputStream(Bundle data) throws PgpGeneralException, FileNotFoundException { - int target = data.getInt(TARGET); - IOType type = IOType.fromInt(target); - switch (type) { - case BYTES: - return new ByteArrayOutputStream(); - - case URI: - Uri providerUri = data.getParcelable(ENCRYPT_DECRYPT_OUTPUT_URI); - - return getContentResolver().openOutputStream(providerUri); - - default: - throw new PgpGeneralException("No target chosen!"); - } - } - - private void finalizeDecryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream) { - finalizeCryptOutputStream(data, resultData, outStream, RESULT_DECRYPTED_BYTES); - } - - private void finalizeCryptOutputStream(Bundle data, Bundle resultData, OutputStream outStream, String bytesName) { - int target = data.getInt(TARGET); - IOType type = IOType.fromInt(target); - switch (type) { - case BYTES: - byte output[] = ((ByteArrayOutputStream) outStream).toByteArray(); - resultData.putByteArray(bytesName, output); - break; - case URI: - // nothing, output was written, just send okay and verification bundle - - break; - } - } - @Override public int onStartCommand(Intent intent, int flags, int startId) { if (ACTION_CANCEL.equals(intent.getAction())) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java index 479b8fd93..c0b293b99 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java @@ -36,8 +36,8 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentService.IOType; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.dialog.DeleteFileDialogFragment; @@ -202,16 +202,14 @@ public class DecryptFilesFragment extends DecryptFragment { intent.setAction(mCurrentCryptoOperation); // data - data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); Log.d(Constants.TAG, "mInputUri=" + mInputUri + ", mOutputUri=" + mOutputUri); - data.putInt(KeychainIntentService.SOURCE, IOType.URI.ordinal()); - data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_INPUT_URI, mInputUri); - - data.putInt(KeychainIntentService.TARGET, IOType.URI.ordinal()); - data.putParcelable(KeychainIntentService.ENCRYPT_DECRYPT_OUTPUT_URI, mOutputUri); + PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(mInputUri, mOutputUri) + .setAllowSymmetricDecryption(true) + .setDecryptMetadataOnly(true); + data.putParcelable(KeychainIntentService.DECRYPT_VERIFY_PARCEL, input); data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java index a666c2357..93397c806 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java @@ -34,8 +34,8 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentService.IOType; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; @@ -168,10 +168,8 @@ public class DecryptTextFragment extends DecryptFragment { intent.setAction(KeychainIntentService.ACTION_DECRYPT_VERIFY); - // data - data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); - data.putInt(KeychainIntentService.TARGET, IOType.BYTES.ordinal()); - data.putByteArray(KeychainIntentService.DECRYPT_CIPHERTEXT_BYTES, mCiphertext.getBytes()); + PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(mCiphertext.getBytes()); + data.putParcelable(KeychainIntentService.DECRYPT_VERIFY_PARCEL, input); data.putParcelable(KeychainIntentService.EXTRA_CRYPTO_INPUT, cryptoInput); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -199,8 +197,7 @@ public class DecryptTextFragment extends DecryptFragment { returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT); if (pgpResult.success()) { - byte[] decryptedMessage = returnData - .getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES); + byte[] decryptedMessage = pgpResult.getOutputBytes(); String displayMessage; if (pgpResult.getCharset() != null) { try { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java index 9c7e494d1..1a23dda93 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptModeAsymmetricFragment.java @@ -120,6 +120,9 @@ public class EncryptModeAsymmetricFragment extends EncryptModeFragment { // preselect keys given, from state or arguments if (savedInstanceState == null) { Long signatureKeyId = getArguments().getLong(ARG_SINGATURE_KEY_ID); + if (signatureKeyId == Constants.key.none) { + signatureKeyId = null; + } long[] encryptionKeyIds = getArguments().getLongArray(ARG_ENCRYPTION_KEY_IDS); preselectKeys(signatureKeyId, encryptionKeyIds); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index 4e926c0fe..4ec4e2c84 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -163,9 +163,7 @@ public class PassphraseDialogActivity extends FragmentActivity { Bundle args = new Bundle(); args.putLong(EXTRA_SUBKEY_ID, keyId); args.putParcelable(EXTRA_SERVICE_INTENT, serviceIntent); - frag.setArguments(args); - frag.show(context.getSupportFragmentManager(), "passphraseDialog"); } });