diff options
author | Thomas Wolf <twolf@apache.org> | 2024-02-01 19:09:50 +0100 |
---|---|---|
committer | Thomas Wolf <twolf@apache.org> | 2024-02-01 20:26:34 +0100 |
commit | 5eff1dd4c7497aab41a7db6b5ab6360233f0cd32 (patch) | |
tree | 71acbc5166faec40a2f1eae39e1e6d0a6468dce2 | |
parent | b079968cdd31bb305b99f860c6b1194662619921 (diff) | |
download | jgit-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
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; |