From 559c68cb012b971bf506d12aaf0a0f0504c9780b Mon Sep 17 00:00:00 2001 From: David Turner Date: Thu, 14 Jun 2018 20:05:38 -0400 Subject: [PATCH] Parse signature of GPG-signed commits In order to support GPG-signed commits, add some methods which will allow GPG signatures to be parsed out of RevCommit objects. Later, we can add code to verify the signatures. Change-Id: Ifcf6b3ac79115c15d3ec4b4eaed07315534d09ac Signed-off-by: David Turner Signed-off-by: Matthias Sohn --- .../jgit/revwalk/RevCommitParseTest.java | 32 +++++++++++ .../eclipse/jgit/util/RawParseUtilsTest.java | 43 ++++++++++++++ .../org/eclipse/jgit/revwalk/RevCommit.java | 29 ++++++++++ .../org/eclipse/jgit/util/RawParseUtils.java | 56 +++++++++++++++++++ 4 files changed, 160 insertions(+) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java index d55fb4467b..b814984935 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevCommitParseTest.java @@ -486,4 +486,36 @@ public class RevCommitParseTest extends RepositoryTestCase { private static ObjectId id(String str) { return ObjectId.fromString(str); } + + @Test + public void testParse_gpgSig() throws Exception { + String commit = "tree e3a1035abd2b319bb01e57d69b0ba6cab289297e\n" + + "parent 54e895b87c0768d2317a2b17062e3ad9f76a8105\n" + + "committer A U Thor + * This method parses and returns the raw content of the gpgsig lines. This + * method is fairly expensive and produces a new byte[] instance on each + * invocation. Callers should invoke this method only if they are certain + * they will need, and should cache the return value for as long as + * necessary to use all information from it. + *

+ * RevFilter implementations should try to use + * {@link org.eclipse.jgit.util.RawParseUtils} to scan the + * {@link #getRawBuffer()} instead, as this will allow faster evaluation of + * commits. + * + * @return contents of the gpg signature; null if the commit was not signed. + * @since 5.1 + */ + public final @Nullable byte[] getRawGpgSignature() { + final byte[] raw = buffer; + final byte[] header = {'g', 'p', 'g', 's', 'i', 'g'}; + final int start = RawParseUtils.headerStart(header, raw, 0); + if (start < 0) { + return null; + } + final int end = RawParseUtils.headerEnd(raw, start); + return Arrays.copyOfRange(raw, start, end); + } + /** * Parse the author identity from the raw buffer. *

diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java index c22c8de5d1..28f406a49e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/util/RawParseUtils.java @@ -548,6 +548,62 @@ public final class RawParseUtils { return ptr; } + /** + * Locate the end of the header. Note that headers may be + * more than one line long. + * @param b + * buffer to scan. + * @param ptr + * position within buffer to start looking for the end-of-header. + * @return new position just after the header. This is either + * b.length, or the index of the header's terminating newline. + * @since 5.1 + */ + public static final int headerEnd(final byte[] b, int ptr) { + final int sz = b.length; + while (ptr < sz) { + final byte c = b[ptr++]; + if (c == '\n' && (ptr == sz || b[ptr] != ' ')) { + return ptr - 1; + } + } + return ptr - 1; + } + + /** + * Find the start of the contents of a given header. + * + * @param b + * buffer to scan. + * @param headerName + * header to search for + * @param ptr + * position within buffer to start looking for header at. + * @return new position at the start of the header's contents, -1 for + * not found + * @since 5.1 + */ + public static final int headerStart(byte[] headerName, byte[] b, int ptr) { + // Start by advancing to just past a LF or buffer start + if (ptr != 0) { + ptr = nextLF(b, ptr - 1); + } + while (ptr < b.length - (headerName.length + 1)) { + boolean found = true; + for (int i = 0; i < headerName.length; i++) { + if (headerName[i] != b[ptr++]) { + found = false; + break; + } + } + if (found && b[ptr++] == ' ') { + return ptr; + } + ptr = nextLF(b, ptr); + } + return -1; + } + /** * Locate the first position before a given character. * -- 2.39.5