diff options
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp')
2 files changed, 85 insertions, 210 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 b2b633ed7..d7107d695 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -48,6 +48,8 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.service.results.OperationResultParcel.OperationLog; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressScaler; @@ -68,7 +70,6 @@ import java.util.Set; */ public class PgpDecryptVerify { private ProviderHelper mProviderHelper; - private PassphraseCache mPassphraseCache; private InputData mData; private OutputStream mOutStream; @@ -82,7 +83,6 @@ public class PgpDecryptVerify { private PgpDecryptVerify(Builder builder) { // private Constructor can only be called from Builder this.mProviderHelper = builder.mProviderHelper; - this.mPassphraseCache = builder.mPassphraseCache; this.mData = builder.mData; this.mOutStream = builder.mOutStream; @@ -97,7 +97,6 @@ public class PgpDecryptVerify { public static class Builder { // mandatory parameter private ProviderHelper mProviderHelper; - private PassphraseCache mPassphraseCache; private InputData mData; private OutputStream mOutStream; @@ -109,10 +108,8 @@ public class PgpDecryptVerify { private boolean mDecryptMetadataOnly = false; private byte[] mDecryptedSessionKey = null; - public Builder(ProviderHelper providerHelper, PassphraseCache passphraseCache, - InputData data, OutputStream outStream) { + public Builder(ProviderHelper providerHelper, InputData data, OutputStream outStream) { this.mProviderHelper = providerHelper; - this.mPassphraseCache = passphraseCache; this.mData = data; this.mOutStream = outStream; } @@ -172,78 +169,44 @@ public class PgpDecryptVerify { } } - public interface PassphraseCache { - public String getCachedPassphrase(long masterKeyId) - throws NoSecretKeyException; - } - - public static class InvalidDataException extends Exception { - public InvalidDataException() { - } - } - - public static class KeyExtractionException extends Exception { - public KeyExtractionException() { - } - } - - public static class WrongPassphraseException extends Exception { - public WrongPassphraseException() { - } - } - - public static class NoSecretKeyException extends Exception { - public NoSecretKeyException() { - } - } - - public static class IntegrityCheckFailedException extends Exception { - public IntegrityCheckFailedException() { - } - } - - public static class NeedNfcDataException extends Exception { - public byte[] mEncryptedSessionKey; - public String mPassphrase; - - public NeedNfcDataException(byte[] encryptedSessionKey, String passphrase) { - mEncryptedSessionKey = encryptedSessionKey; - mPassphrase = passphrase; - } - } - /** * Decrypts and/or verifies data based on parameters of class */ - public PgpDecryptVerifyResult execute() - throws IOException, PGPException, SignatureException, - WrongPassphraseException, NoSecretKeyException, KeyExtractionException, - InvalidDataException, IntegrityCheckFailedException, NeedNfcDataException { - // automatically works with ascii armor input and binary - InputStream in = PGPUtil.getDecoderStream(mData.getInputStream()); - if (in instanceof ArmoredInputStream) { - ArmoredInputStream aIn = (ArmoredInputStream) in; - // it is ascii armored - Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine()); - - if (aIn.isClearText()) { - // a cleartext signature, verify it with the other method - return verifyCleartextSignature(aIn); + public DecryptVerifyResult execute() { + try { + // automatically works with ascii armor input and binary + InputStream in = PGPUtil.getDecoderStream(mData.getInputStream()); + + if (in instanceof ArmoredInputStream) { + ArmoredInputStream aIn = (ArmoredInputStream) in; + // it is ascii armored + Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine()); + + if (aIn.isClearText()) { + // a cleartext signature, verify it with the other method + return verifyCleartextSignature(aIn); + } + // else: ascii armored encryption! go on... } - // else: ascii armored encryption! go on... - } - return decryptVerify(in); + return decryptVerify(in); + } catch (PGPException e) { + OperationLog log = new OperationLog(); + // log.add(LogLevel.ERROR, LogType.MSG_DC_ERROR_PGP_EXCEPTION); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } catch (IOException e) { + OperationLog log = new OperationLog(); + // log.add(LogLevel.ERROR, LogType.MSG_DC_ERROR_IO); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } } /** * Decrypt and/or verifies binary or ascii armored pgp */ - private PgpDecryptVerifyResult decryptVerify(InputStream in) - throws IOException, PGPException, SignatureException, - WrongPassphraseException, KeyExtractionException, NoSecretKeyException, - InvalidDataException, IntegrityCheckFailedException, NeedNfcDataException { - PgpDecryptVerifyResult result = new PgpDecryptVerifyResult(); + private DecryptVerifyResult decryptVerify(InputStream in) throws IOException, PGPException { + + OperationLog log = new OperationLog(); PGPObjectFactory pgpF = new PGPObjectFactory(in, new JcaKeyFingerprintCalculator()); PGPEncryptedDataList enc; @@ -259,7 +222,8 @@ public class PgpDecryptVerify { } if (enc == null) { - throw new InvalidDataException(); + // log.add(LogLevel.ERROR, LogType.MSG_DC_ERROR_INVALID_DATA) + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } InputStream clear; @@ -322,18 +286,12 @@ public class PgpDecryptVerify { encryptedDataAsymmetric = encData; - // if no passphrase was explicitly set try to get it from the cache service + // if passphrase was not cached, return here indicating that a passphrase is missing! if (mPassphrase == null) { - // returns "" if key has no passphrase - mPassphrase = mPassphraseCache.getCachedPassphrase(subKeyId); - - // if passphrase was not cached, return here - // indicating that a passphrase is missing! - if (mPassphrase == null) { - result.setKeyIdPassphraseNeeded(subKeyId); - result.setStatus(PgpDecryptVerifyResult.KEY_PASSHRASE_NEEDED); - return result; - } + DecryptVerifyResult result = + new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_ASYM_PASSPHRASE, log); + result.setKeyIdPassphraseNeeded(subKeyId); + return result; } // break out of while, only decrypt the first packet where we have a key @@ -351,8 +309,7 @@ public class PgpDecryptVerify { // if no passphrase is given, return here // indicating that a passphrase is missing! if (mPassphrase == null) { - result.setStatus(PgpDecryptVerifyResult.SYMMETRIC_PASSHRASE_NEEDED); - return result; + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_SYM_PASSPHRASE, log); } // break out of while, only decrypt the first packet @@ -379,10 +336,12 @@ public class PgpDecryptVerify { updateProgress(R.string.progress_extracting_key, currentProgress, 100); try { if (!secretEncryptionKey.unlock(mPassphrase)) { - throw new WrongPassphraseException(); + // log.add(LogLevel.ERROR, LogType.MSG_DC_ERROR_BAD_PASSPHRASE); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } } catch (PgpGeneralException e) { - throw new KeyExtractionException(); + // log.add(LogLevel.ERROR, LogType.MSG_DC_ERROR_EXTRACT_KEY); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } currentProgress += 2; @@ -393,12 +352,19 @@ public class PgpDecryptVerify { = secretEncryptionKey.getDecryptorFactory(mDecryptedSessionKey); clear = encryptedDataAsymmetric.getDataStream(decryptorFactory); } catch (NfcSyncPublicKeyDataDecryptorFactoryBuilder.NfcInteractionNeeded e) { - throw new NeedNfcDataException(e.encryptedSessionKey, mPassphrase); + // log.add(LogLevel.ERROR, LogType.MSG_DC_ERROR_NFC_INTERACTION); + + DecryptVerifyResult result = + new DecryptVerifyResult(DecryptVerifyResult.RESULT_PENDING_NFC, log); + result.setNfcEncryptedSessionKey(e.encryptedSessionKey); + // TODO save passphrase here? + return result; } encryptedData = encryptedDataAsymmetric; } else { // no packet has been found where we have the corresponding secret key in our db - throw new NoSecretKeyException(); + // log.add(LogLevel.ERROR, LogType.MSG_DC_MISSING_SECRET_KEY); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } PGPObjectFactory plainFact = new PGPObjectFactory(clear, new JcaKeyFingerprintCalculator()); @@ -468,6 +434,8 @@ public class PgpDecryptVerify { dataChunk = plainFact.nextObject(); } + OpenPgpMetadata metadata; + if (dataChunk instanceof PGPLiteralData) { currentProgress += 4; updateProgress(R.string.progress_decrypting, currentProgress, 100); @@ -503,17 +471,17 @@ public class PgpDecryptVerify { } } - OpenPgpMetadata metadata = new OpenPgpMetadata( + metadata = new OpenPgpMetadata( originalFilename, mimeType, literalData.getModificationTime().getTime(), originalSize); - result.setDecryptMetadata(metadata); - - Log.d(Constants.TAG, "metadata: " + metadata); // return here if we want to decrypt the metadata only if (mDecryptMetadataOnly) { + DecryptVerifyResult result = + new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + result.setDecryptMetadata(metadata); return result; } @@ -569,18 +537,19 @@ public class PgpDecryptVerify { boolean validSignature = signature.verify(messageSignature); signatureResultBuilder.setValidSignature(validSignature); } + } else { + // If there is no literalData, we don't have any metadata + metadata = null; } if (encryptedData.isIntegrityProtected()) { updateProgress(R.string.progress_verifying_integrity, 95, 100); if (encryptedData.verify()) { - // passed - Log.d(Constants.TAG, "Integrity verification: success!"); + // log.add(LogLevel.INFO, LogType.MSG_DC_INTEGRITY_CHECK_OK) } else { - // failed - Log.d(Constants.TAG, "Integrity verification: failed!"); - throw new IntegrityCheckFailedException(); + // log.add(LogLevel.ERROR, LogType.MSG_DC_ERROR_INTEGRITY_CHECK) + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } } else { // no integrity check @@ -590,14 +559,20 @@ public class PgpDecryptVerify { // Handle missing integrity protection like failed integrity protection! // The MDC packet can be stripped by an attacker! if (!signatureResultBuilder.isValidSignature()) { - throw new IntegrityCheckFailedException(); + // log.add(LogLevel.ERROR, LogType.MSG_DC_ERROR_INTEGRITY_CHECK) + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } } updateProgress(R.string.progress_done, 100, 100); + // Return a positive result, with metadata and verification info + DecryptVerifyResult result = + new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + result.setDecryptMetadata(metadata); result.setSignatureResult(signatureResultBuilder.build()); return result; + } /** @@ -607,9 +582,11 @@ public class PgpDecryptVerify { * The method is heavily based on * pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java */ - private PgpDecryptVerifyResult verifyCleartextSignature(ArmoredInputStream aIn) - throws IOException, PGPException, SignatureException, InvalidDataException { - PgpDecryptVerifyResult result = new PgpDecryptVerifyResult(); + private DecryptVerifyResult verifyCleartextSignature(ArmoredInputStream aIn) + throws IOException, PGPException { + + OperationLog log = new OperationLog(); + OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); // cleartext signatures are never encrypted ;) signatureResultBuilder.setSignatureOnly(true); @@ -643,7 +620,8 @@ public class PgpDecryptVerify { PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); if (sigList == null) { - throw new InvalidDataException(); + // log.add(LogLevel.ERROR, LogType.MSG_DC_ERROR_INVALID_DATA) + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); } CanonicalizedPublicKeyRing signingRing = null; @@ -686,7 +664,7 @@ public class PgpDecryptVerify { } } - if (signature != null) { + if (signature != null) try { updateProgress(R.string.progress_verifying_signature, 90, 100); InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText)); @@ -708,13 +686,16 @@ public class PgpDecryptVerify { // Verify signature and check binding signatures boolean validSignature = signature.verify(); - signatureResultBuilder.setValidSignature(validSignature); - } - result.setSignatureResult(signatureResultBuilder.build()); + } catch (SignatureException e) { + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } updateProgress(R.string.progress_done, 100, 100); + + DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + result.setSignatureResult(signatureResultBuilder.build()); return result; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java deleted file mode 100644 index 506f48c52..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 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 - * 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.pgp; - -import android.os.Parcel; -import android.os.Parcelable; - -import org.openintents.openpgp.OpenPgpMetadata; -import org.openintents.openpgp.OpenPgpSignatureResult; - -public class PgpDecryptVerifyResult implements Parcelable { - public static final int SUCCESS = 1; - public static final int KEY_PASSHRASE_NEEDED = 2; - public static final int SYMMETRIC_PASSHRASE_NEEDED = 3; - - int mStatus; - long mKeyIdPassphraseNeeded; - - OpenPgpSignatureResult mSignatureResult; - OpenPgpMetadata mDecryptMetadata; - - public int getStatus() { - return mStatus; - } - - public void setStatus(int status) { - mStatus = status; - } - - public long getKeyIdPassphraseNeeded() { - return mKeyIdPassphraseNeeded; - } - - public void setKeyIdPassphraseNeeded(long keyIdPassphraseNeeded) { - mKeyIdPassphraseNeeded = keyIdPassphraseNeeded; - } - - public OpenPgpSignatureResult getSignatureResult() { - return mSignatureResult; - } - - public void setSignatureResult(OpenPgpSignatureResult signatureResult) { - mSignatureResult = signatureResult; - } - - public OpenPgpMetadata getDecryptMetadata() { - return mDecryptMetadata; - } - - public void setDecryptMetadata(OpenPgpMetadata decryptMetadata) { - mDecryptMetadata = decryptMetadata; - } - - public PgpDecryptVerifyResult() { - - } - - public PgpDecryptVerifyResult(PgpDecryptVerifyResult b) { - this.mStatus = b.mStatus; - this.mKeyIdPassphraseNeeded = b.mKeyIdPassphraseNeeded; - this.mSignatureResult = b.mSignatureResult; - this.mDecryptMetadata = b.mDecryptMetadata; - } - - - public int describeContents() { - return 0; - } - - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mStatus); - dest.writeLong(mKeyIdPassphraseNeeded); - dest.writeParcelable(mSignatureResult, 0); - dest.writeParcelable(mDecryptMetadata, 0); - } - - public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() { - public PgpDecryptVerifyResult createFromParcel(final Parcel source) { - PgpDecryptVerifyResult vr = new PgpDecryptVerifyResult(); - vr.mStatus = source.readInt(); - vr.mKeyIdPassphraseNeeded = source.readLong(); - vr.mSignatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader()); - vr.mDecryptMetadata = source.readParcelable(OpenPgpMetadata.class.getClassLoader()); - return vr; - } - - public PgpDecryptVerifyResult[] newArray(final int size) { - return new PgpDecryptVerifyResult[size]; - } - }; -} |