From fe8db664a86a35b18ec62ddaaa82cf7aafff1e4d Mon Sep 17 00:00:00 2001
From: Vincent Breitmoser <valodim@mugenguild.com>
Date: Thu, 8 Oct 2015 13:53:58 +0200
Subject: pgpdecryptverify: refactor signature verification state into
 SignatureChecker subclass

---
 .../keychain/pgp/PgpDecryptVerifyOperation.java    | 275 +++++++++++----------
 OpenKeychain/src/main/res/values/strings.xml       |   1 -
 2 files changed, 141 insertions(+), 135 deletions(-)

(limited to 'OpenKeychain/src')

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 4f3f323a5..d613b08eb 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java
@@ -207,22 +207,18 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
             updateProgress(R.string.progress_decompressing_data, 80, 100);
         }
 
-        // all we want to see is a OnePassSignatureList followed by LiteralData
-        if (!(o instanceof PGPOnePassSignatureList)) {
+        SignatureChecker signatureChecker = new SignatureChecker();
+        if ( ! signatureChecker.initializeOnePassSignature(o, log, indent)) {
             log.add(LogType.MSG_VL_ERROR_MISSING_SIGLIST, indent);
             return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
         }
-        PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) o;
 
-        SignatureData signatureData = findAvailableSignature(sigList);
-
-        if (signatureData == null) {
+        if ( ! signatureChecker.isInitialized()) {
             log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent);
-            Log.d(Constants.TAG, "Failed to find signature with available key in signed-literal message");
             return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
         }
 
-        String fingerprint = KeyFormattingUtils.convertFingerprintToHex(signatureData.signingKey.getFingerprint());
+        String fingerprint = KeyFormattingUtils.convertFingerprintToHex(signatureChecker.getSigningFingerprint());
         if (!(input.getRequiredSignerFingerprint().equals(fingerprint))) {
             log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent);
             Log.d(Constants.TAG, "Fingerprint mismatch; wanted " + input.getRequiredSignerFingerprint() +
@@ -230,16 +226,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
             return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
         }
 
-        OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
-
-        PGPOnePassSignature signature = sigList.get(signatureData.signatureIndex);
-        signatureResultBuilder.initValid(signatureData.signingKey);
-
-        JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
-                new JcaPGPContentVerifierBuilderProvider()
-                        .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-        signature.init(contentVerifierBuilderProvider, signatureData.signingKey.getPublicKey());
-
         o = pgpF.nextObject();
 
         if (!(o instanceof PGPLiteralData)) {
@@ -258,38 +244,18 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
         byte[] buffer = new byte[1 << 16];
         while ((length = dataIn.read(buffer)) > 0) {
             out.write(buffer, 0, length);
-            signature.update(buffer, 0, length);
+            signatureChecker.updateSignatureData(buffer, 0, length);
         }
 
         updateProgress(R.string.progress_verifying_signature, 95, 100);
         log.add(LogType.MSG_VL_CLEAR_SIGNATURE_CHECK, indent + 1);
 
         o = pgpF.nextObject();
-        if ( ! (o instanceof PGPSignatureList) ) {
-            log.add(LogType.MSG_VL_ERROR_NO_SIGNATURE, indent);
-            return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
-        }
-        PGPSignatureList signatureList = (PGPSignatureList) o;
-        if (signatureList.size() <= signatureData.signatureIndex) {
-            log.add(LogType.MSG_VL_ERROR_NO_SIGNATURE, indent);
+        if ( ! signatureChecker.verifySignature(o, log, indent) ) {
             return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
         }
 
-        // PGPOnePassSignature and PGPSignature packets are "bracketed",
-        // so we need to take the last-minus-index'th element here
-        PGPSignature messageSignature = signatureList.get(signatureList.size() -1 -signatureData.signatureIndex);
-
-        // Verify signature and check binding signatures
-        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);
-        }
-
-        signatureResultBuilder.setValidSignature(validSignature);
-
-        OpenPgpSignatureResult signatureResult = signatureResultBuilder.build();
+        OpenPgpSignatureResult signatureResult = signatureChecker.getSignatureResult();
 
         if (signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_CONFIRMED
                 && signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_UNCONFIRMED) {
@@ -364,7 +330,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
             }
         }
 
-        OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
         OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder();
 
         JcaPGPObjectFactory plainFact;
@@ -411,8 +376,6 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
 
         log.add(LogType.MSG_DC_PREP_STREAMS, indent);
 
-        SignatureData signatureData = null;
-
         log.add(LogType.MSG_DC_CLEAR, indent);
         indent += 1;
 
@@ -429,41 +392,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
             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;
-            signatureData = findAvailableSignature(sigList);
-
-            if (signatureData != null) {
-                // key found in our database!
-                signature = sigList.get(signatureData.signatureIndex);
-                signatureResultBuilder.initValid(signatureData.signingKey);
-
-                JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
-                        new JcaPGPContentVerifierBuilderProvider()
-                                .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
-                signature.init(contentVerifierBuilderProvider, signatureData.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 (signatureData != null && ! PgpSecurityConstants.isSecureKey(signatureData.signingKey)) {
-                log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
-                signatureResultBuilder.setInsecure(true);
-            }
-
+        SignatureChecker signatureChecker = new SignatureChecker();
+        if (signatureChecker.initializeOnePassSignature(dataChunk, log, indent +1)) {
             dataChunk = plainFact.nextObject();
         }
 
@@ -544,16 +474,8 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
             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);
+                new ProgressScaler(mProgressable, currentProgress, 95, 100);
 
         InputStream dataIn = literalData.getInputStream();
 
@@ -568,9 +490,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
             }
 
             // update signature buffer if signature is also present
-            if (signature != null) {
-                signature.update(buffer, 0, length);
-            }
+            signatureChecker.updateSignatureData(buffer, 0, length);
 
             alreadyWritten += length;
             if (wholeSize > 0) {
@@ -587,40 +507,17 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
         metadata = new OpenPgpMetadata(
                 originalFilename, mimeType, literalData.getModificationTime().getTime(), alreadyWritten, charset);
 
-        if (signatureData != null) {
-            updateProgress(R.string.progress_verifying_signature, 90, 100);
-            log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
+        if (signatureChecker.isInitialized()) {
 
+            log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent);
             Object o = plainFact.nextObject();
-            if ( ! (o instanceof PGPSignatureList) ) {
-                log.add(LogType.MSG_DC_ERROR_NO_SIGNATURE, indent);
-                return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
-            }
-            PGPSignatureList signatureList = (PGPSignatureList) o;
-            if (signatureList.size() <= signatureData.signatureIndex) {
-                log.add(LogType.MSG_DC_ERROR_NO_SIGNATURE, indent);
-                return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
-            }
-
-            // PGPOnePassSignature and PGPSignature packets are "bracketed",
-            // so we need to take the last-minus-index'th element here
-            PGPSignature messageSignature = signatureList.get(signatureList.size() -1 - signatureData.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);
-            }
+            boolean signatureCheckOk = signatureChecker.verifySignature(o, log, 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);
+            if (!signatureCheckOk) {
+                return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
             }
 
-            signatureResultBuilder.setValidSignature(validSignature);
         }
 
         indent -= 1;
@@ -635,7 +532,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
                     log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent);
                     return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
                 }
-            } else if (signature == null) {
+            } else if ( ! signatureChecker.isInitialized() ) {
                 // If no signature is present, we *require* an MDC!
                 // Handle missing integrity protection like failed integrity protection!
                 // The MDC packet can be stripped by an attacker!
@@ -652,7 +549,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
         DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
 
         result.setCachedCryptoInputParcel(cryptoInput);
-        result.setSignatureResult(signatureResultBuilder.build());
+        result.setSignatureResult(signatureChecker.getSignatureResult());
         result.setDecryptionResult(decryptionResultBuilder.build());
         result.setDecryptionMetadata(metadata);
 
@@ -1279,21 +1176,131 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
 
     }
 
-    public SignatureData findAvailableSignature(PGPOnePassSignatureList sigList) {
-        // go through all signatures (should be just one), make sure we have
-        //  the key and it matches the one we’re looking for
-        for (int i = 0; i < sigList.size(); ++i) {
-            try {
-                long sigKeyId = sigList.get(i).getKeyID();
-                CanonicalizedPublicKeyRing signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
-                        KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
-                );
-                return new SignatureData(signingRing.getPublicKey(sigKeyId), i);
-            } catch (ProviderHelper.NotFoundException e) {
-                Log.d(Constants.TAG, "key not found, trying next signature...");
+    private class SignatureChecker {
+
+        OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
+
+        private CanonicalizedPublicKey signingKey;
+        // we use the signatureIndex instead of the signature itself here for two reasons:
+        // 1) the signature may be either of type PGPSignature or PGPOnePassSignature (which have no common ancestor)
+        // 2) in case of the latter, we need the signatureIndex to know which PGPSignature to use later on
+        private int signatureIndex;
+
+        PGPOnePassSignature signature;
+
+        boolean initializeOnePassSignature(Object dataChunk, OperationLog log, int indent) throws PGPException {
+
+            if ( ! (dataChunk instanceof PGPOnePassSignatureList) ) {
+                return false;
+            }
+
+            // resolve leading signature data
+            log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent +1);
+
+            PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk;
+            findAvailableSignature(sigList);
+
+            if (signingKey != null) {
+
+                // key found in our database!
+                signature = sigList.get(signatureIndex);
+                signatureResultBuilder.initValid(signingKey);
+
+                JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
+                        new JcaPGPContentVerifierBuilderProvider()
+                                .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
+                signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey());
+
+                // TODO: checks on signingRing ?
+                if ( ! PgpSecurityConstants.isSecureKey(signingKey)) {
+                    log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1);
+                    signatureResultBuilder.setInsecure(true);
+                }
+
+            } else if (!sigList.isEmpty()) {
+
+                signatureResultBuilder.setSignatureAvailable(true);
+                signatureResultBuilder.setKnownKey(false);
+                signatureResultBuilder.setKeyId(sigList.get(0).getKeyID());
+
+            }
+
+            return true;
+
+        }
+
+        public boolean isInitialized() {
+            return signingKey != null;
+        }
+
+        private void findAvailableSignature(PGPOnePassSignatureList sigList) {
+            // go through all signatures (should be just one), make sure we have
+            //  the key and it matches the one we’re looking for
+            for (int i = 0; i < sigList.size(); ++i) {
+                try {
+                    long sigKeyId = sigList.get(i).getKeyID();
+                    CanonicalizedPublicKeyRing signingRing = mProviderHelper.getCanonicalizedPublicKeyRing(
+                            KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId)
+                    );
+                    signatureIndex = i;
+                    signingKey = signingRing.getPublicKey(sigKeyId);
+                    return;
+                } catch (ProviderHelper.NotFoundException e) {
+                    Log.d(Constants.TAG, "key not found, trying next signature...");
+                }
             }
         }
-        return null;
+
+        public void updateSignatureData(byte[] buf, int off, int len) {
+            if (signature != null) {
+                signature.update(buf, off, len);
+            }
+        }
+
+        boolean verifySignature(Object o, OperationLog log, int indent) throws PGPException {
+
+            if ( ! (o instanceof PGPSignatureList) ) {
+                log.add(LogType.MSG_DC_ERROR_NO_SIGNATURE, indent);
+                return false;
+            }
+            PGPSignatureList signatureList = (PGPSignatureList) o;
+            if (signatureList.size() <= signatureIndex) {
+                log.add(LogType.MSG_DC_ERROR_NO_SIGNATURE, indent);
+                return false;
+            }
+
+            // PGPOnePassSignature and PGPSignature packets are "bracketed",
+            // so we need to take the last-minus-index'th element here
+            PGPSignature messageSignature = signatureList.get(signatureList.size() -1 - 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);
+
+            return true;
+
+        }
+
+        public byte[] getSigningFingerprint() {
+            return signingKey.getFingerprint();
+        }
+
+        public OpenPgpSignatureResult getSignatureResult() {
+            return signatureResultBuilder.build();
+        }
+
     }
 
     public SignatureData findAvailableSignature(PGPSignatureList sigList) {
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index 7ed79e7d7..c3fabaa69 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -436,7 +436,6 @@
     <string name="progress_decrypting">"decrypting data…"</string>
     <string name="progress_preparing_signature">"preparing signature…"</string>
     <string name="progress_generating_signature">"generating signature…"</string>
-    <string name="progress_processing_signature">"processing signature…"</string>
     <string name="progress_verifying_signature">"verifying signature…"</string>
     <string name="progress_signing">"signing…"</string>
     <string name="progress_certifying">"certifying…"</string>
-- 
cgit v1.2.3