aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf <twolf@apache.org>2024-02-01 19:09:50 +0100
committerThomas Wolf <twolf@apache.org>2024-02-01 20:26:34 +0100
commit5eff1dd4c7497aab41a7db6b5ab6360233f0cd32 (patch)
tree71acbc5166faec40a2f1eae39e1e6d0a6468dce2
parentb079968cdd31bb305b99f860c6b1194662619921 (diff)
downloadjgit-5eff1dd4c7497aab41a7db6b5ab6360233f0cd32.tar.gz
jgit-5eff1dd4c7497aab41a7db6b5ab6360233f0cd32.zip
[gpg] Refactor the GpgSignatureVerifier
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
-rw-r--r--org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java64
-rw-r--r--org.eclipse.jgit/.settings/.api_filters8
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java71
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java30
4 files changed, 119 insertions, 54 deletions
diff --git a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
index 7161895a6b..f4fed40973 100644
--- a/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
+++ b/org.eclipse.jgit.gpg.bc/src/org/eclipse/jgit/gpg/bc/internal/BouncyCastleGpgSignatureVerifier.java
@@ -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.
diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters
index 2057347229..e15ed48e28 100644
--- a/org.eclipse.jgit/.settings/.api_filters
+++ b/org.eclipse.jgit/.settings/.api_filters
@@ -30,6 +30,14 @@
</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
index 0000000000..06a89dc535
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/AbstractGpgSignatureVerifier.java
@@ -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;
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java
index a7a39c998f..91c9bab5a4 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/GpgSignatureVerifier.java
@@ -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;