]> source.dussan.org Git - jgit.git/commitdiff
[gpg] Refactor the GpgSignatureVerifier 27/1176327/1
authorThomas Wolf <twolf@apache.org>
Thu, 1 Feb 2024 18:09:50 +0000 (19:09 +0100)
committerThomas Wolf <twolf@apache.org>
Thu, 1 Feb 2024 19:26:34 +0000 (20:26 +0100)
Add a new method verify(GpgConfig, byte[], byte[]) and deprecate the
existing verify(byte[], byte[]). Some implementations of the interface
may need the GpgConfig.

Factor out extracting the raw armored signature from commits or tags
into an abstract AbstractGpgSignatureVerifier class so that different
implementations don't have to re-implement that bit. Call the new verify
method, passing along the GpgConfig.

This makes the GPG interfaces more versatile and facilitates
implementing an alternate GpgSignatureVerifier.

Change-Id: I9cf093caa9fdebede801d665f2591cd9b275e1fd

org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
org.eclipse.jgit/.settings/.api_filters
org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java [new file with mode: 0644]
org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java

index 7161895a6be4933a29f55c67cf942114a860a18b..f4fed409733c5b093c42ecbbdfe8d8c11b527b05 100644 (file)
@@ -15,7 +15,6 @@ import java.io.InputStream;
 import java.security.Security;
 import java.text.MessageFormat;
 import java.time.Instant;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.Locale;
@@ -33,21 +32,18 @@ import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory;
 import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
 import org.bouncycastle.util.encoders.Hex;
 import org.eclipse.jgit.annotations.NonNull;
-import org.eclipse.jgit.annotations.Nullable;
 import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.lib.AbstractGpgSignatureVerifier;
 import org.eclipse.jgit.lib.GpgConfig;
 import org.eclipse.jgit.lib.GpgSignatureVerifier;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevObject;
-import org.eclipse.jgit.revwalk.RevTag;
 import org.eclipse.jgit.util.LRUMap;
-import org.eclipse.jgit.util.RawParseUtils;
 import org.eclipse.jgit.util.StringUtils;
 
 /**
  * A {@link GpgSignatureVerifier} to verify GPG signatures using BouncyCastle.
  */
-public class BouncyCastleGpgSignatureVerifier implements GpgSignatureVerifier {
+public class BouncyCastleGpgSignatureVerifier
+               extends AbstractGpgSignatureVerifier {
 
        private static void registerBouncyCastleProviderIfNecessary() {
                if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
@@ -77,50 +73,6 @@ public class BouncyCastleGpgSignatureVerifier implements GpgSignatureVerifier {
                return "bc"; //$NON-NLS-1$
        }
 
-       @Override
-       @Nullable
-       public SignatureVerification verifySignature(@NonNull RevObject object,
-                       @NonNull GpgConfig config) throws IOException {
-               if (object instanceof RevCommit) {
-                       RevCommit commit = (RevCommit) object;
-                       byte[] signatureData = commit.getRawGpgSignature();
-                       if (signatureData == null) {
-                               return null;
-                       }
-                       byte[] raw = commit.getRawBuffer();
-                       // Now remove the GPG signature
-                       byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
-                       int start = RawParseUtils.headerStart(header, raw, 0);
-                       if (start < 0) {
-                               return null;
-                       }
-                       int end = RawParseUtils.headerEnd(raw, start);
-                       // start is at the beginning of the header's content
-                       start -= header.length + 1;
-                       // end is on the terminating LF; we need to skip that, too
-                       if (end < raw.length) {
-                               end++;
-                       }
-                       byte[] data = new byte[raw.length - (end - start)];
-                       System.arraycopy(raw, 0, data, 0, start);
-                       System.arraycopy(raw, end, data, start, raw.length - end);
-                       return verify(data, signatureData);
-               } else if (object instanceof RevTag) {
-                       RevTag tag = (RevTag) object;
-                       byte[] signatureData = tag.getRawGpgSignature();
-                       if (signatureData == null) {
-                               return null;
-                       }
-                       byte[] raw = tag.getRawBuffer();
-                       // The signature is just tacked onto the end of the message, which
-                       // is last in the buffer.
-                       byte[] data = Arrays.copyOfRange(raw, 0,
-                                       raw.length - signatureData.length);
-                       return verify(data, signatureData);
-               }
-               return null;
-       }
-
        static PGPSignature parseSignature(InputStream in)
                        throws IOException, PGPException {
                try (InputStream sigIn = PGPUtil.getDecoderStream(in)) {
@@ -138,7 +90,8 @@ public class BouncyCastleGpgSignatureVerifier implements GpgSignatureVerifier {
        }
 
        @Override
-       public SignatureVerification verify(byte[] data, byte[] signatureData)
+       public SignatureVerification verify(@NonNull GpgConfig config, byte[] data,
+                       byte[] signatureData)
                        throws IOException {
                PGPSignature signature = null;
                String fingerprint = null;
@@ -280,6 +233,13 @@ public class BouncyCastleGpgSignatureVerifier implements GpgSignatureVerifier {
                                verified, expired, trust, null);
        }
 
+       @Override
+       public SignatureVerification verify(byte[] data, byte[] signatureData)
+                       throws IOException {
+               throw new UnsupportedOperationException(
+                               "Call verify(GpgConfig, byte[], byte[]) instead."); //$NON-NLS-1$
+       }
+
        private TrustLevel parseGpgTrustPacket(byte[] packet) {
                if (packet == null || packet.length < 6) {
                        // A GPG trust packet has at least 6 bytes.
index 205734722997321aebc58979c404f8e6c5c4f37d..e15ed48e28defbda8e121085eff638e3d64e0521 100644 (file)
             </message_arguments>
         </filter>
     </resource>
+    <resource path="src/org/eclipse/jgit/lib/GpgSignatureVerifier.java" type="org.eclipse.jgit.lib.GpgSignatureVerifier">
+        <filter id="404000815">
+            <message_arguments>
+                <message_argument value="org.eclipse.jgit.lib.GpgSignatureVerifier"/>
+                <message_argument value="verify(GpgConfig, byte[], byte[])"/>
+            </message_arguments>
+        </filter>
+    </resource>
     <resource path="src/org/eclipse/jgit/storage/pack/PackConfig.java" type="org.eclipse.jgit.storage.pack.PackConfig">
         <filter id="336658481">
             <message_arguments>
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java
new file mode 100644 (file)
index 0000000..06a89dc
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024, Thomas Wolf <twolf@apache.org> and others
+*
+* This program and the accompanying materials are made available under the
+* terms of the Eclipse Distribution License v. 1.0 which is available at
+* https://www.eclipse.org/org/documents/edl-v10.php.
+*
+* SPDX-License-Identifier: BSD-3-Clause
+*/
+package org.eclipse.jgit.lib;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevObject;
+import org.eclipse.jgit.revwalk.RevTag;
+import org.eclipse.jgit.util.RawParseUtils;
+
+/**
+ * Provides a base implementation of
+ * {@link GpgSignatureVerifier#verifySignature(RevObject, GpgConfig)}.
+ *
+ * @since 6.9
+ */
+public abstract class AbstractGpgSignatureVerifier
+               implements GpgSignatureVerifier {
+
+       @Override
+       public SignatureVerification verifySignature(RevObject object,
+                       GpgConfig config) throws IOException {
+               if (object instanceof RevCommit) {
+                       RevCommit commit = (RevCommit) object;
+                       byte[] signatureData = commit.getRawGpgSignature();
+                       if (signatureData == null) {
+                               return null;
+                       }
+                       byte[] raw = commit.getRawBuffer();
+                       // Now remove the GPG signature
+                       byte[] header = { 'g', 'p', 'g', 's', 'i', 'g' };
+                       int start = RawParseUtils.headerStart(header, raw, 0);
+                       if (start < 0) {
+                               return null;
+                       }
+                       int end = RawParseUtils.nextLfSkippingSplitLines(raw, start);
+                       // start is at the beginning of the header's content
+                       start -= header.length + 1;
+                       // end is on the terminating LF; we need to skip that, too
+                       if (end < raw.length) {
+                               end++;
+                       }
+                       byte[] data = new byte[raw.length - (end - start)];
+                       System.arraycopy(raw, 0, data, 0, start);
+                       System.arraycopy(raw, end, data, start, raw.length - end);
+                       return verify(config, data, signatureData);
+               } else if (object instanceof RevTag) {
+                       RevTag tag = (RevTag) object;
+                       byte[] signatureData = tag.getRawGpgSignature();
+                       if (signatureData == null) {
+                               return null;
+                       }
+                       byte[] raw = tag.getRawBuffer();
+                       // The signature is just tacked onto the end of the message, which
+                       // is last in the buffer.
+                       byte[] data = Arrays.copyOfRange(raw, 0,
+                                       raw.length - signatureData.length);
+                       return verify(config, data, signatureData);
+               }
+               return null;
+       }
+}
index a7a39c998fbb4e621f5d3672c7bcb7f0562d048c..91c9bab5a42900276be5332481bbaf0474bab845 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021, Thomas Wolf <thomas.wolf@paranor.ch> and others
+ * Copyright (C) 2021, 2024 Thomas Wolf <twolf@apache.org> and others
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Distribution License v. 1.0 which is available at
@@ -18,7 +18,8 @@ import org.eclipse.jgit.api.errors.JGitInternalException;
 import org.eclipse.jgit.revwalk.RevObject;
 
 /**
- * A {@code GpgVerifier} can verify GPG signatures on git commits and tags.
+ * A {@code GpgSignatureVerifier} can verify GPG signatures on git commits and
+ * tags.
  *
  * @since 5.11
  */
@@ -42,6 +43,28 @@ public interface GpgSignatureVerifier {
        SignatureVerification verifySignature(@NonNull RevObject object,
                        @NonNull GpgConfig config) throws IOException;
 
+       /**
+        * Verifies a given signature for given data.
+        *
+        * @param config
+        *            the {@link GpgConfig}
+        * @param data
+        *            the signature is for
+        * @param signatureData
+        *            the ASCII-armored signature
+        * @return a {@link SignatureVerification} describing the outcome
+        * @throws IOException
+        *             if the signature cannot be parsed
+        * @throws JGitInternalException
+        *             if signature verification fails
+        * @since 6.9
+        */
+       default SignatureVerification verify(@NonNull GpgConfig config, byte[] data,
+                       byte[] signatureData) throws IOException {
+               // Default implementation for backwards compatibility; override as
+               // appropriate
+               return verify(data, signatureData);
+       }
 
        /**
         * Verifies a given signature for given data.
@@ -55,7 +78,10 @@ public interface GpgSignatureVerifier {
         *             if the signature cannot be parsed
         * @throws JGitInternalException
         *             if signature verification fails
+        * @deprecated since 6.9, use {@link #verify(GpgConfig, byte[], byte[])}
+        *             instead
         */
+       @Deprecated
        public SignatureVerification verify(byte[] data, byte[] signatureData)
                        throws IOException;