aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
diff options
context:
space:
mode:
authorVincent Breitmoser <valodim@mugenguild.com>2015-09-10 21:44:15 +0200
committerVincent Breitmoser <valodim@mugenguild.com>2015-09-10 21:44:15 +0200
commitb78954fc16e79d6910ef9b6c781faf755e89a158 (patch)
treef6e849487cfdcaf9d46f0f449c5940a34aecb754 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
parentcb067f748b0e07e7e96e5e04fc3d8e1ff74ceb45 (diff)
downloadopen-keychain-b78954fc16e79d6910ef9b6c781faf755e89a158.tar.gz
open-keychain-b78954fc16e79d6910ef9b6c781faf755e89a158.tar.bz2
open-keychain-b78954fc16e79d6910ef9b6c781faf755e89a158.zip
add support for signed-only data in the backend (#1507)
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java685
1 files changed, 368 insertions, 317 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
index dd30156f9..a538c9bd1 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
@@ -313,6 +313,27 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
return result;
}
+ private static class EncryptStreamResult {
+
+ // this is non-null iff an error occured, return directly
+ DecryptVerifyResult errorResult;
+
+ // for verification
+ PGPEncryptedData encryptedData;
+ InputStream cleartextStream;
+
+ int symmetricEncryptionAlgo = 0;
+
+ boolean skippedDisallowedKey = false;
+ boolean insecureEncryptionKey = false;
+
+ // convenience method to return with error
+ public EncryptStreamResult with(DecryptVerifyResult result) {
+ errorResult = result;
+ return this;
+ }
+
+ }
/** Decrypt and/or verify binary or ascii armored pgp data. */
@NonNull
@@ -320,42 +341,14 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
InputStream in, OutputStream out, int indent) throws IOException, PGPException {
- OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
- OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();
OperationLog log = new OperationLog();
log.add(LogType.MSG_DC, indent);
indent += 1;
- JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
- PGPEncryptedDataList enc;
- Object o = pgpF.nextObject();
-
int currentProgress = 0;
updateProgress(R.string.progress_reading_data, currentProgress, 100);
- if (o instanceof PGPEncryptedDataList) {
- enc = (PGPEncryptedDataList) o;
- } else {
- enc = (PGPEncryptedDataList) pgpF.nextObject();
- }
-
- if (enc == null) {
- log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
- }
-
- InputStream clear;
- PGPEncryptedData encryptedData;
-
- PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
- PGPPBEEncryptedData encryptedDataSymmetric = null;
- CanonicalizedSecretKey secretEncryptionKey = null;
- Iterator<?> it = enc.getEncryptedDataObjects();
- boolean asymmetricPacketFound = false;
- boolean symmetricPacketFound = false;
- boolean anyPacketFound = false;
-
// If the input stream is armored, and there is a charset specified, take a note for later
// https://tools.ietf.org/html/rfc4880#page56
String charset = null;
@@ -375,8 +368,328 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
}
}
+ OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
+ OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();
+
+ JcaPGPObjectFactory plainFact;
+ Object dataChunk;
+ EncryptStreamResult esResult = null;
+ { // resolve encrypted (symmetric and asymmetric) packets
+ JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in);
+ Object obj = pgpF.nextObject();
+
+ if (obj instanceof PGPEncryptedDataList) {
+ esResult = handleEncryptedPacket(
+ input, cryptoInput, (PGPEncryptedDataList) obj, log, indent, currentProgress);
+
+ // if there is an error, there is nothing left to do here
+ if (esResult.errorResult != null) {
+ return esResult.errorResult;
+ }
+
+ decryptionResultBuilder.setEncrypted(true);
+ if (esResult.insecureEncryptionKey) {
+ log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
+ decryptionResultBuilder.setInsecure(true);
+ }
+
+ // Check for insecure encryption algorithms!
+ if (!PgpSecurityConstants.isSecureSymmetricAlgorithm(esResult.symmetricEncryptionAlgo)) {
+ log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
+ decryptionResultBuilder.setInsecure(true);
+ }
+
+ plainFact = new JcaPGPObjectFactory(esResult.cleartextStream);
+ dataChunk = plainFact.nextObject();
+
+ } else {
+ decryptionResultBuilder.setEncrypted(false);
+
+ plainFact = pgpF;
+ dataChunk = obj;
+ }
+
+ }
+
+ log.add(LogType.MSG_DC_PREP_STREAMS, indent);
+
+ int signatureIndex = -1;
+ CanonicalizedPublicKeyRing signingRing = null;
+ CanonicalizedPublicKey signingKey = null;
+
+ log.add(LogType.MSG_DC_CLEAR, indent);
+ indent += 1;
+
+ // resolve compressed data
+ if (dataChunk instanceof PGPCompressedData) {
+ log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1);
+ currentProgress += 2;
+ updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
+
+ PGPCompressedData compressedData = (PGPCompressedData) dataChunk;
+
+ JcaPGPObjectFactory fact = new JcaPGPObjectFactory(compressedData.getDataStream());
+ dataChunk = fact.nextObject();
+ plainFact = fact;
+ }
+
+ // resolve leading signature data
+ PGPOnePassSignature signature = null;
+ if (dataChunk instanceof PGPOnePassSignatureList) {
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent + 1);
+ currentProgress += 2;
+ updateProgress(R.string.progress_processing_signature, currentProgress, 100);
+
+ PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
+
+ // NOTE: following code is similar to processSignature, but for PGPOnePassSignature
+
+ // go through all signatures
+ // and find out for which signature we have a key in our database
+ for (int i = 0; i < sigList.size(); ++i) {
+ try {
+ long sigKeyId = sigList.get(i).getKeyID();
+ signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
+ KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
+ );
+ signingKey = signingRing.getPublicKey(sigKeyId);
+ signatureIndex = i;
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.d(Constants.TAG, "key not found, trying next signature...");
+ }
+ }
+
+ if (signingKey != null) {
+ // key found in our database!
+ signature = sigList.get(signatureIndex);
+
+ signatureResultBuilder.initValid(signingRing, signingKey);
+
+ JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+ new JcaPGPContentVerifierBuilderProvider()
+ .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+ signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
+ } else {
+ // no key in our database -> return "unknown pub key" status including the first key id
+ if (!sigList.isEmpty()) {
+ signatureResultBuilder.setSignatureAvailable(true);
+ signatureResultBuilder.setKnownKey(false);
+ signatureResultBuilder.setKeyId(sigList.get(0).getKeyID());
+ }
+ }
+
+ // check for insecure signing key
+ // TODO: checks on signingRing ?
+ if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) {
+ log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
+ signatureResultBuilder.setInsecure(true);
+ }
+
+ dataChunk = plainFact.nextObject();
+ }
+
+ if (dataChunk instanceof PGPSignatureList) {
+ // skip
+ dataChunk = plainFact.nextObject();
+ }
+
+ OpenPgpMetadata metadata;
+
+ if ( ! (dataChunk instanceof PGPLiteralData)) {
+
+ log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+
+ }
+
+ log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1);
+ indent += 2;
+ currentProgress += 4;
+ updateProgress(R.string.progress_decrypting, currentProgress, 100);
+
+ PGPLiteralData literalData = (PGPLiteralData) dataChunk;
+
+ String originalFilename = literalData.getFileName();
+ String mimeType = null;
+ if (literalData.getFormat() == PGPLiteralData.TEXT
+ || literalData.getFormat() == PGPLiteralData.UTF8) {
+ mimeType = "text/plain";
+ } else {
+ // try to guess from file ending
+ String extension = MimeTypeMap.getFileExtensionFromUrl(originalFilename);
+ if (extension != null) {
+ MimeTypeMap mime = MimeTypeMap.getSingleton();
+ mimeType = mime.getMimeTypeFromExtension(extension);
+ }
+ if (mimeType == null) {
+ mimeType = "application/octet-stream";
+ }
+ }
+
+ if (!"".equals(originalFilename)) {
+ log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename);
+ }
+ log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1,
+ mimeType);
+ log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1,
+ new Date(literalData.getModificationTime().getTime()).toString());
+
+ // return here if we want to decrypt the metadata only
+ if (input.isDecryptMetadataOnly()) {
+
+ // this operation skips the entire stream to find the data length!
+ Long originalSize = literalData.findDataLength();
+
+ if (originalSize != null) {
+ log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1,
+ Long.toString(originalSize));
+ } else {
+ log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1);
+ }
+
+ metadata = new OpenPgpMetadata(
+ originalFilename,
+ mimeType,
+ literalData.getModificationTime().getTime(),
+ originalSize == null ? 0 : originalSize);
+
+ log.add(LogType.MSG_DC_OK_META_ONLY, indent);
+ DecryptVerifyResult result =
+ new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ result.setCharset(charset);
+ result.setDecryptionMetadata(metadata);
+ return result;
+ }
+
+ int endProgress;
+ if (signature != null) {
+ endProgress = 90;
+ } else if (esResult != null && esResult.encryptedData.isIntegrityProtected()) {
+ endProgress = 95;
+ } else {
+ endProgress = 100;
+ }
+ ProgressScaler progressScaler =
+ new ProgressScaler(mProgressable, currentProgress, endProgress, 100);
+
+ InputStream dataIn = literalData.getInputStream();
+
+ long alreadyWritten = 0;
+ 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 (out != null) {
+ out.write(buffer, 0, length);
+ }
+
+ // update signature buffer if signature is also present
+ if (signature != null) {
+ signature.update(buffer, 0, length);
+ }
+
+ alreadyWritten += length;
+ if (wholeSize > 0) {
+ long progress = 100 * alreadyWritten / wholeSize;
+ // stop at 100% for wrong file sizes...
+ if (progress > 100) {
+ progress = 100;
+ }
+ progressScaler.setProgress((int) progress, 100);
+ }
+ // TODO: slow annealing to fake a progress?
+ }
+
+ metadata = new OpenPgpMetadata(
+ originalFilename, mimeType, literalData.getModificationTime().getTime(), alreadyWritten);
+
+ if (signature != null) {
+ updateProgress(R.string.progress_verifying_signature, 90, 100);
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
+
+ PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
+ PGPSignature messageSignature = signatureList.get(signatureIndex);
+
+ // Verify signature
+ boolean validSignature = signature.verify(messageSignature);
+ if (validSignature) {
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
+ } else {
+ log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
+ }
+
+ // check for insecure hash algorithms
+ if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
+ log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
+ signatureResultBuilder.setInsecure(true);
+ }
+
+ signatureResultBuilder.setValidSignature(validSignature);
+ }
+
+ indent -= 1;
+
+ if (esResult != null && esResult.encryptedData.isIntegrityProtected()) {
+ updateProgress(R.string.progress_verifying_integrity, 95, 100);
+
+ if (esResult.encryptedData.verify()) {
+ log.add(LogType.MSG_DC_INTEGRITY_CHECK_OK, indent);
+ } else {
+ log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent);
+ return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ }
+ } else {
+ // If no valid signature is present:
+ // Handle missing integrity protection like failed integrity protection!
+ // The MDC packet can be stripped by an attacker!
+ Log.d(Constants.TAG, "MDC fail");
+ if (!signatureResultBuilder.isValidSignature()) {
+ log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent);
+ decryptionResultBuilder.setInsecure(true);
+ }
+ }
+
+ updateProgress(R.string.progress_done, 100, 100);
+
+ log.add(LogType.MSG_DC_OK, indent);
+
+ // Return a positive result, with metadata and verification info
+ DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+
+ result.setCachedCryptoInputParcel(cryptoInput);
+ result.setSignatureResult(signatureResultBuilder.build());
+ result.setCharset(charset);
+ result.setDecryptionResult(decryptionResultBuilder.build());
+ result.setDecryptionMetadata(metadata);
+
+ return result;
+
+ }
+
+ private EncryptStreamResult handleEncryptedPacket(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
+ PGPEncryptedDataList enc, OperationLog log, int indent, int currentProgress) throws PGPException {
+
+ // TODO is this necessary?
+ /*
+ else if (obj instanceof PGPEncryptedDataList) {
+ enc = (PGPEncryptedDataList) pgpF.nextObject();
+ }
+ */
+
+ EncryptStreamResult result = new EncryptStreamResult();
+
+ boolean asymmetricPacketFound = false;
+ boolean symmetricPacketFound = false;
+ boolean anyPacketFound = false;
+
+ PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
+ PGPPBEEncryptedData encryptedDataSymmetric = null;
+ CanonicalizedSecretKey secretEncryptionKey = null;
+
Passphrase passphrase = null;
- boolean skippedDisallowedKey = false;
+
+ Iterator<?> it = enc.getEncryptedDataObjects();
// go through all objects and find one we can decrypt
while (it.hasNext()) {
@@ -420,7 +733,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
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;
+ result.skippedDisallowedKey = true;
log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent + 1);
continue;
}
@@ -451,23 +764,23 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
log.add(LogType.MSG_DC_PASS_CACHED, indent + 1);
} catch (PassphraseCacheInterface.NoSecretKeyException e) {
log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
}
// if passphrase was not cached, return here indicating that a passphrase is missing!
if (passphrase == null) {
log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
- return new DecryptVerifyResult(log,
+ return result.with(new DecryptVerifyResult(log,
RequiredInputParcel.createRequiredDecryptPassphrase(
- secretKeyRing.getMasterKeyId(), secretEncryptionKey.getKeyId()),
- cryptoInput);
+ secretKeyRing.getMasterKeyId(), secretEncryptionKey.getKeyId()),
+ cryptoInput));
}
}
// check for insecure encryption key
if ( ! PgpSecurityConstants.isSecureKey(secretEncryptionKey)) {
log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
- decryptionResultBuilder.setInsecure(true);
+ result.insecureEncryptionKey = true;
}
// break out of while, only decrypt the first packet where we have a key
@@ -504,9 +817,9 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
if (passphrase == null) {
log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1);
- return new DecryptVerifyResult(log,
+ return result.with(new DecryptVerifyResult(log,
RequiredInputParcel.createRequiredSymmetricPassphrase(),
- cryptoInput);
+ cryptoInput));
}
} else {
@@ -533,10 +846,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
}
}
- log.add(LogType.MSG_DC_PREP_STREAMS, indent);
-
// we made sure above one of these two would be true
- int symmetricEncryptionAlgo;
if (symmetricPacketFound) {
currentProgress += 2;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
@@ -548,16 +858,16 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
passphrase.getCharArray());
try {
- clear = encryptedDataSymmetric.getDataStream(decryptorFactory);
+ result.cleartextStream = encryptedDataSymmetric.getDataStream(decryptorFactory);
} catch (PGPDataValidationException e) {
- log.add(LogType.MSG_DC_ERROR_SYM_PASSPHRASE, indent +1);
- return new DecryptVerifyResult(log,
- RequiredInputParcel.createRequiredSymmetricPassphrase(), cryptoInput);
+ log.add(LogType.MSG_DC_ERROR_SYM_PASSPHRASE, indent + 1);
+ return result.with(new DecryptVerifyResult(log,
+ RequiredInputParcel.createRequiredSymmetricPassphrase(), cryptoInput));
}
- encryptedData = encryptedDataSymmetric;
+ result.encryptedData = encryptedDataSymmetric;
- symmetricEncryptionAlgo = encryptedDataSymmetric.getSymmetricAlgorithm(decryptorFactory);
+ result.symmetricEncryptionAlgo = encryptedDataSymmetric.getSymmetricAlgorithm(decryptorFactory);
} else if (asymmetricPacketFound) {
currentProgress += 2;
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
@@ -566,11 +876,11 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
log.add(LogType.MSG_DC_UNLOCKING, indent + 1);
if (!secretEncryptionKey.unlock(passphrase)) {
log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
}
} catch (PgpGeneralException e) {
log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
}
currentProgress += 2;
@@ -585,300 +895,41 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
&& !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) {
log.add(LogType.MSG_DC_PENDING_NFC, indent + 1);
- return new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation(
+ return result.with(new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation(
secretEncryptionKey.getRing().getMasterKeyId(),
secretEncryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0]
- ),
- cryptoInput);
+ ), cryptoInput));
}
try {
- clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
+ result.cleartextStream = encryptedDataAsymmetric.getDataStream(decryptorFactory);
} catch (PGPKeyValidationException | ArrayIndexOutOfBoundsException e) {
log.add(LogType.MSG_DC_ERROR_CORRUPT_DATA, indent + 1);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
}
- symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
+ result.symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory);
+ result.encryptedData = encryptedDataAsymmetric;
cryptoInput.addCryptoData(decryptorFactory.getCachedSessionKeys());
- encryptedData = encryptedDataAsymmetric;
} else {
// there wasn't even any useful data
if (!anyPacketFound) {
log.add(LogType.MSG_DC_ERROR_NO_DATA, indent + 1);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_NO_DATA, log);
+ return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_NO_DATA, log));
}
// there was data but key wasn't allowed
- if (skippedDisallowedKey) {
+ if (result.skippedDisallowedKey) {
log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_KEY_DISALLOWED, log);
+ return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_KEY_DISALLOWED, log));
}
// no packet has been found where we have the corresponding secret key in our db
log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
+ return result.with(new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log));
}
- decryptionResultBuilder.setEncrypted(true);
-
- // Check for insecure encryption algorithms!
- if (!PgpSecurityConstants.isSecureSymmetricAlgorithm(symmetricEncryptionAlgo)) {
- log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1);
- decryptionResultBuilder.setInsecure(true);
- }
-
- JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear);
- Object dataChunk = plainFact.nextObject();
- int signatureIndex = -1;
- CanonicalizedPublicKeyRing signingRing = null;
- CanonicalizedPublicKey signingKey = null;
-
- log.add(LogType.MSG_DC_CLEAR, indent);
- indent += 1;
-
- if (dataChunk instanceof PGPCompressedData) {
- log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1);
- currentProgress += 2;
- updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
- PGPCompressedData compressedData = (PGPCompressedData) dataChunk;
-
- JcaPGPObjectFactory fact = new JcaPGPObjectFactory(compressedData.getDataStream());
- dataChunk = fact.nextObject();
- plainFact = fact;
- }
-
- PGPOnePassSignature signature = null;
- if (dataChunk instanceof PGPOnePassSignatureList) {
- log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent + 1);
- currentProgress += 2;
- updateProgress(R.string.progress_processing_signature, currentProgress, 100);
-
- PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
-
- // NOTE: following code is similar to processSignature, but for PGPOnePassSignature
-
- // go through all signatures
- // and find out for which signature we have a key in our database
- for (int i = 0; i < sigList.size(); ++i) {
- try {
- long sigKeyId = sigList.get(i).getKeyID();
- signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
- KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
- );
- signingKey = signingRing.getPublicKey(sigKeyId);
- signatureIndex = i;
- } catch (ProviderHelper.NotFoundException e) {
- Log.d(Constants.TAG, "key not found, trying next signature...");
- }
- }
-
- if (signingKey != null) {
- // key found in our database!
- signature = sigList.get(signatureIndex);
-
- signatureResultBuilder.initValid(signingRing, signingKey);
-
- JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
- new JcaPGPContentVerifierBuilderProvider()
- .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
- signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
- } else {
- // no key in our database -> return "unknown pub key" status including the first key id
- if (!sigList.isEmpty()) {
- signatureResultBuilder.setSignatureAvailable(true);
- signatureResultBuilder.setKnownKey(false);
- signatureResultBuilder.setKeyId(sigList.get(0).getKeyID());
- }
- }
-
- // check for insecure signing key
- // TODO: checks on signingRing ?
- if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) {
- log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
- signatureResultBuilder.setInsecure(true);
- }
-
- dataChunk = plainFact.nextObject();
- }
-
- if (dataChunk instanceof PGPSignatureList) {
- // skip
- dataChunk = plainFact.nextObject();
- }
-
- OpenPgpMetadata metadata;
-
- if (dataChunk instanceof PGPLiteralData) {
- log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1);
- indent += 2;
- currentProgress += 4;
- updateProgress(R.string.progress_decrypting, currentProgress, 100);
-
- PGPLiteralData literalData = (PGPLiteralData) dataChunk;
-
- String originalFilename = literalData.getFileName();
- String mimeType = null;
- if (literalData.getFormat() == PGPLiteralData.TEXT
- || literalData.getFormat() == PGPLiteralData.UTF8) {
- mimeType = "text/plain";
- } else {
- // try to guess from file ending
- String extension = MimeTypeMap.getFileExtensionFromUrl(originalFilename);
- if (extension != null) {
- MimeTypeMap mime = MimeTypeMap.getSingleton();
- mimeType = mime.getMimeTypeFromExtension(extension);
- }
- if (mimeType == null) {
- mimeType = "application/octet-stream";
- }
- }
-
- if (!"".equals(originalFilename)) {
- log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename);
- }
- log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1,
- mimeType);
- log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1,
- new Date(literalData.getModificationTime().getTime()).toString());
-
- // return here if we want to decrypt the metadata only
- if (input.isDecryptMetadataOnly()) {
-
- // this operation skips the entire stream to find the data length!
- Long originalSize = literalData.findDataLength();
-
- if (originalSize != null) {
- log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1,
- Long.toString(originalSize));
- } else {
- log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1);
- }
-
- metadata = new OpenPgpMetadata(
- originalFilename,
- mimeType,
- literalData.getModificationTime().getTime(),
- originalSize == null ? 0 : originalSize);
-
- log.add(LogType.MSG_DC_OK_META_ONLY, indent);
- DecryptVerifyResult result =
- new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
- result.setCharset(charset);
- result.setDecryptionMetadata(metadata);
- return result;
- }
-
- int endProgress;
- if (signature != null) {
- endProgress = 90;
- } else if (encryptedData.isIntegrityProtected()) {
- endProgress = 95;
- } else {
- endProgress = 100;
- }
- ProgressScaler progressScaler =
- new ProgressScaler(mProgressable, currentProgress, endProgress, 100);
-
- InputStream dataIn = literalData.getInputStream();
-
- long alreadyWritten = 0;
- 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 (out != null) {
- out.write(buffer, 0, length);
- }
-
- // update signature buffer if signature is also present
- if (signature != null) {
- signature.update(buffer, 0, length);
- }
-
- alreadyWritten += length;
- if (wholeSize > 0) {
- long progress = 100 * alreadyWritten / wholeSize;
- // stop at 100% for wrong file sizes...
- if (progress > 100) {
- progress = 100;
- }
- progressScaler.setProgress((int) progress, 100);
- }
- // TODO: slow annealing to fake a progress?
- }
-
- metadata = new OpenPgpMetadata(
- originalFilename,
- mimeType,
- literalData.getModificationTime().getTime(),
- alreadyWritten);
-
- if (signature != null) {
- updateProgress(R.string.progress_verifying_signature, 90, 100);
- log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
-
- PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject();
- PGPSignature messageSignature = signatureList.get(signatureIndex);
-
- // TODO: what about binary signatures?
-
- // Verify signature
- boolean validSignature = signature.verify(messageSignature);
- if (validSignature) {
- log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1);
- } else {
- log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1);
- }
-
- // check for insecure hash algorithms
- if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) {
- log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1);
- signatureResultBuilder.setInsecure(true);
- }
-
- signatureResultBuilder.setValidSignature(validSignature);
- }
-
- indent -= 1;
- } 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()) {
- log.add(LogType.MSG_DC_INTEGRITY_CHECK_OK, indent);
- } else {
- log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent);
- return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
- }
- } else {
- // If no valid signature is present:
- // Handle missing integrity protection like failed integrity protection!
- // The MDC packet can be stripped by an attacker!
- Log.d(Constants.TAG, "MDC fail");
- if (!signatureResultBuilder.isValidSignature()) {
- log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent);
- decryptionResultBuilder.setInsecure(true);
- }
- }
-
- updateProgress(R.string.progress_done, 100, 100);
-
- log.add(LogType.MSG_DC_OK, indent);
-
- // Return a positive result, with metadata and verification info
- DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
- result.setCachedCryptoInputParcel(cryptoInput);
- result.setSignatureResult(signatureResultBuilder.build());
- result.setCharset(charset);
- result.setDecryptionResult(decryptionResultBuilder.build());
- result.setDecryptionMetadata(metadata);
return result;
}