diff options
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; |