--- /dev/null
+/*
+ * Copyright (C) 2015, Google Inc.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.transport;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+
+import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
+import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.BaseReceivePack.ReceiveConfig;
+import org.junit.Test;
+
+/** Test for push certificate parsing. */
+public class PushCertificateParserTest {
+ @Test
+ public void parseCertFromPktLine() throws Exception {
+ // Example push certificate generated by C git 2.2.0.
+ String input = "001ccertificate version 0.1\n"
+ + "0041pusher Dave Borowitz <dborowitz@google.com> 1433954361 -0700\n"
+ + "0024pushee git://localhost/repo.git\n"
+ + "002anonce 1433954361-bde756572d665bba81d8\n"
+ + "0005\n"
+ + "00680000000000000000000000000000000000000000"
+ + " 6c2b981a177396fb47345b7df3e4d3f854c6bea7"
+ + " refs/heads/master\n"
+ + "0022-----BEGIN PGP SIGNATURE-----\n"
+ + "0016Version: GnuPG v1\n"
+ + "0005\n"
+ + "0045iQEcBAABAgAGBQJVeGg5AAoJEPfTicJkUdPkUggH/RKAeI9/i/LduuiqrL/SSdIa\n"
+ + "00459tYaSqJKLbXz63M/AW4Sp+4u+dVCQvnAt/a35CVEnpZz6hN4Kn/tiswOWVJf4CO7\n"
+ + "0045htNubGs5ZMwvD6sLYqKAnrM3WxV/2TbbjzjZW6Jkidz3jz/WRT4SmjGYiEO7aA+V\n"
+ + "00454ZdIS9f7sW5VsHHYlNThCA7vH8Uu48bUovFXyQlPTX0pToSgrWV3JnTxDNxfn3iG\n"
+ + "0045IL0zTY/qwVCdXgFownLcs6J050xrrBWIKqfcWr3u4D2aCLyR0v+S/KArr7ulZygY\n"
+ + "0045+SOklImn8TAZiNxhWtA6ens66IiammUkZYFv7SSzoPLFZT4dC84SmGPWgf94NoQ=\n"
+ + "000a=XFeC\n"
+ + "0020-----END PGP SIGNATURE-----\n"
+ + "0012push-cert-end\n";
+
+ PacketLineIn pckIn = newPacketLineIn(input);
+ Config cfg = new Config();
+ cfg.setString("receive", null, "certnonceseed", "sekret");
+ Repository db = new InMemoryRepository(
+ new DfsRepositoryDescription("repo"));
+
+ PushCertificateParser parser = new PushCertificateParser(
+ db, new ReceiveConfig(cfg));
+ parser.receiveHeader(pckIn, false);
+ parser.addCommand(pckIn.readStringRaw());
+ assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readStringRaw());
+ parser.receiveSignature(pckIn);
+
+ PushCertificate cert = parser.build();
+ assertEquals("0.1", cert.getVersion());
+ assertEquals("Dave Borowitz", cert.getPusherIdent().getName());
+ assertEquals("dborowitz@google.com",
+ cert.getPusherIdent().getEmailAddress());
+ assertEquals(1433954361000L, cert.getPusherIdent().getWhen().getTime());
+ assertEquals(-7 * 60, cert.getPusherIdent().getTimeZoneOffset());
+ assertEquals("git://localhost/repo.git", cert.getPushee());
+ assertEquals("1433954361-bde756572d665bba81d8", cert.getNonce());
+
+ assertNotEquals(cert.getNonce(), parser.getAdvertiseNonce());
+ assertEquals(PushCertificate.NonceStatus.BAD, cert.getNonceStatus());
+
+ assertEquals(1, cert.getCommands().size());
+ ReceiveCommand cmd = cert.getCommands().get(0);
+ assertEquals("refs/heads/master", cmd.getRefName());
+ assertEquals(ObjectId.zeroId(), cmd.getOldId());
+ assertEquals("6c2b981a177396fb47345b7df3e4d3f854c6bea7",
+ cmd.getNewId().name());
+
+ assertEquals(concatPacketLines(input, 0, 6), cert.toText());
+
+ String signature = concatPacketLines(input, 7, 16);
+ assertFalse(signature.contains(PushCertificateParser.BEGIN_SIGNATURE));
+ assertFalse(signature.contains(PushCertificateParser.END_SIGNATURE));
+ assertEquals(signature, cert.getSignature());
+ }
+
+ @Test
+ public void testConcatPacketLines() throws Exception {
+ String input = "000bline 1\n000bline 2\n000bline 3\n";
+ assertEquals("line 1\n", concatPacketLines(input, 0, 1));
+ assertEquals("line 1\nline 2\n", concatPacketLines(input, 0, 2));
+ assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 3));
+ assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 4));
+ }
+
+ private static String concatPacketLines(String input, int begin, int end)
+ throws IOException {
+ StringBuilder result = new StringBuilder();
+ int i = 0;
+ PacketLineIn pckIn = newPacketLineIn(input);
+ while (i < end) {
+ String line;
+ try {
+ line = pckIn.readStringRaw();
+ } catch (EOFException e) {
+ break;
+ }
+ if (++i > begin) {
+ result.append(line);
+ }
+ }
+ return result.toString();
+ }
+
+ private static PacketLineIn newPacketLineIn(String input) {
+ return new PacketLineIn(new ByteArrayInputStream(Constants.encode(input)));
+ }
+}
errorEncodingFromFile=Error encoding from file {0}
errorInBase64CodeReadingStream=Error in Base64 code reading stream.
errorInPackedRefs=error in packed-refs
-errorInvalidPushCert=error: invalid protocol: {0}
errorInvalidProtocolWantedOldNewRef=error: invalid protocol: wanted 'old new ref'
errorListing=Error listing {0}
errorOccurredDuringUnpackingOnTheRemoteEnd=error occurred during unpacking on the remote end: {0}
pullOnRepoWithoutHEADCurrentlyNotSupported=Pull on repository without HEAD currently not supported
pullTaskName=Pull
pushCancelled=push cancelled
+pushCertificateInvalidField=Push certificate has missing or invalid value for {0}
+pushCertificateInvalidFieldValue=Push certificate has missing or invalid value for {0}: {1}
+pushCertificateInvalidHeader=Push certificate has invalid header format
+pushCertificateInvalidSignature=Push certificate has invalid signature format
pushIsNotSupportedForBundleTransport=Push is not supported for bundle transport
pushNotPermitted=push not permitted
rawLogMessageDoesNotParseAsLogEntry=Raw log message does not parse as log entry
/***/ public String errorEncodingFromFile;
/***/ public String errorInBase64CodeReadingStream;
/***/ public String errorInPackedRefs;
- /***/ public String errorInvalidPushCert;
/***/ public String errorInvalidProtocolWantedOldNewRef;
/***/ public String errorListing;
/***/ public String errorOccurredDuringUnpackingOnTheRemoteEnd;
/***/ public String pullOnRepoWithoutHEADCurrentlyNotSupported;
/***/ public String pullTaskName;
/***/ public String pushCancelled;
+ /***/ public String pushCertificateInvalidField;
+ /***/ public String pushCertificateInvalidFieldValue;
+ /***/ public String pushCertificateInvalidHeader;
+ /***/ public String pushCertificateInvalidSignature;
/***/ public String pushIsNotSupportedForBundleTransport;
/***/ public String pushNotPermitted;
/***/ public String rawLogMessageDoesNotParseAsLogEntry;
/** The size of the received pack, including index size */
private Long packSize;
- PushCertificateParser pushCertificateParser;
+ private PushCertificateParser pushCertificateParser;
/**
- * @return the push certificate used to verify the pushers identity.
+ * Get the push certificate used to verify the pusher's identity.
+ * <p>
+ * Only valid after commands are read from the wire.
+ *
+ * @return the parsed certificate, or null if push certificates are disabled.
+ * @throws IOException if the certificate was present but invalid.
+ * @since 4.1
*/
- PushCertificate getPushCertificate() {
- return pushCertificateParser;
+ public PushCertificate getPushCertificate() throws IOException {
+ return pushCertificateParser.build();
}
/**
adv.advertiseCapability(CAPABILITY_REPORT_STATUS);
if (allowQuiet)
adv.advertiseCapability(CAPABILITY_QUIET);
- if (pushCertificateParser.enabled())
- adv.advertiseCapability(
- pushCertificateParser.getAdvertiseNonce());
+ String nonce = pushCertificateParser.getAdvertiseNonce();
+ if (nonce != null) {
+ adv.advertiseCapability(nonce);
+ }
if (db.getRefDatabase().performsAtomicTransactions())
adv.advertiseCapability(CAPABILITY_ATOMIC);
if (allowOfsDelta)
!isBiDirectionalPipe());
}
- if (line.equals("-----BEGIN PGP SIGNATURE-----\n")) //$NON-NLS-1$
+ if (line.equals(PushCertificateParser.BEGIN_SIGNATURE)) {
pushCertificateParser.receiveSignature(pckIn);
-
- if (pushCertificateParser.enabled()) {
- // Must use raw line with optional newline so signed payload can be
- // reconstructed.
- pushCertificateParser.addCommand(rawLine);
}
if (line.length() < 83) {
cmd.setRef(refs.get(cmd.getRefName()));
}
commands.add(cmd);
+ if (pushCertificateParser.enabled()) {
+ // Must use raw line with optional newline so signed payload can be
+ // reconstructed.
+ pushCertificateParser.addCommand(cmd, rawLine);
+ }
}
}
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.PushCertificateParser.NONCE;
+import static org.eclipse.jgit.transport.PushCertificateParser.PUSHEE;
+import static org.eclipse.jgit.transport.PushCertificateParser.PUSHER;
+import static org.eclipse.jgit.transport.PushCertificateParser.VERSION;
+
+import java.text.MessageFormat;
+import java.util.List;
+
+import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.PersonIdent;
+
/**
* The required information to verify the push.
+ * <p>
+ * A valid certificate will not return null from any getter methods; callers may
+ * assume that any null value indicates a missing or invalid certificate.
*
* @since 4.0
*/
public class PushCertificate {
- /** The tuple "name <email>" as presented in the push certificate. */
- String pusher;
-
- /** The remote URL the signed push goes to. */
- String pushee;
-
- /** What we think about the returned signed nonce. */
- NonceStatus nonceStatus;
-
/** Verification result of the nonce returned during push. */
public enum NonceStatus {
/** Nonce was not expected, yet client sent one anyway. */
SLOP
}
- String commandList;
- String signature;
+ private final String version;
+ private final PersonIdent pusher;
+ private final String pushee;
+ private final String nonce;
+ private final NonceStatus nonceStatus;
+ private final List<ReceiveCommand> commands;
+ private final String rawCommands;
+ private final String signature;
+
+ PushCertificate(String version, PersonIdent pusher, String pushee,
+ String nonce, NonceStatus nonceStatus, List<ReceiveCommand> commands,
+ String rawCommands, String signature) {
+ if (version == null || version.isEmpty()) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField, VERSION));
+ }
+ if (pusher == null) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField, PUSHER));
+ }
+ if (pushee == null || pushee.isEmpty()) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField, PUSHEE));
+ }
+ if (nonce == null || nonce.isEmpty()) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField, NONCE));
+ }
+ if (nonceStatus == null) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField,
+ "nonce status")); //$NON-NLS-1$
+ }
+ if (commands == null || commands.isEmpty()
+ || rawCommands == null || rawCommands.isEmpty()) {
+ throw new IllegalArgumentException(MessageFormat.format(
+ JGitText.get().pushCertificateInvalidField,
+ "command")); //$NON-NLS-1$
+ }
+ if (signature == null || signature.isEmpty()) {
+ throw new IllegalArgumentException(
+ JGitText.get().pushCertificateInvalidSignature);
+ }
+ this.version = version;
+ this.pusher = pusher;
+ this.pushee = pushee;
+ this.nonce = nonce;
+ this.nonceStatus = nonceStatus;
+ this.commands = commands;
+ this.rawCommands = rawCommands;
+ this.signature = signature;
+ }
/**
- * @return the signature, consisting of the lines received between the lines
- * '----BEGIN GPG SIGNATURE-----\n' and the '----END GPG
- * SIGNATURE-----\n'
+ * @return the certificate version string.
+ * @since 4.1
*/
- public String getSignature() {
- return signature;
+ public String getVersion() {
+ return version;
}
/**
- * @return the list of commands as one string to be feed into the signature
- * verifier.
+ * @return the identity of the pusher who signed the cert, as a string.
+ * @since 4.0
*/
- public String getCommandList() {
- return commandList;
+ public String getPusher() {
+ return pusher.toExternalString();
}
/**
- * @return the tuple "name <email>" as presented by the client in the
- * push certificate.
+ * @return identity of the pusher who signed the cert.
+ * @since 4.1
*/
- public String getPusher() {
+ public PersonIdent getPusherIdent() {
return pusher;
}
- /** @return URL of the repository the push was originally sent to. */
+ /**
+ * @return URL of the repository the push was originally sent to.
+ * @since 4.0
+ */
public String getPushee() {
return pushee;
}
- /** @return verification status of the nonce embedded in the certificate. */
+ /**
+ * @return the raw nonce value that was presented by the pusher.
+ * @since 4.0
+ */
+ public String getNonce() {
+ return nonce;
+ }
+
+ /**
+ * @return verification status of the nonce embedded in the certificate.
+ * @since 4.0
+ */
public NonceStatus getNonceStatus() {
return nonceStatus;
}
+
+ /**
+ * @return the list of commands as one string to be feed into the signature
+ * verifier.
+ * @since 4.1
+ */
+ public List<ReceiveCommand> getCommands() {
+ return commands;
+ }
+
+ /**
+ * @return the raw signature, consisting of the lines received between the
+ * lines {@code "----BEGIN GPG SIGNATURE-----\n"} and
+ * {@code "----END GPG SIGNATURE-----\n}", exclusive
+ * @since 4.0
+ */
+ public String getSignature() {
+ return signature;
+ }
+
+ /** @return text payload of the certificate for the signature verifier. */
+ public String toText() {
+ return new StringBuilder()
+ .append(VERSION).append(' ').append(version).append('\n')
+ .append(PUSHER).append(' ').append(pusher.toExternalString())
+ .append('\n')
+ .append(PUSHEE).append(' ').append(pushee).append('\n')
+ .append(NONCE).append(' ').append(nonce).append('\n')
+ .append('\n')
+ .append(rawCommands)
+ .toString();
+ }
}
*/
package org.eclipse.jgit.transport;
+import static org.eclipse.jgit.transport.BaseReceivePack.chomp;
+import static org.eclipse.jgit.transport.BaseReceivePack.parseCommand;
import static org.eclipse.jgit.transport.GitProtocolConstants.CAPABILITY_PUSH_CERT;
import java.io.EOFException;
import java.io.IOException;
import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.internal.JGitText;
+import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.BaseReceivePack.ReceiveConfig;
+import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
+import org.eclipse.jgit.util.RawParseUtils;
/**
- * Parser for Push certificates
+ * Parser for signed push certificates.
*
* @since 4.0
*/
-public class PushCertificateParser extends PushCertificate {
+public class PushCertificateParser {
+ static final String BEGIN_SIGNATURE =
+ "-----BEGIN PGP SIGNATURE-----\n"; //$NON-NLS-1$
+ static final String END_SIGNATURE =
+ "-----END PGP SIGNATURE-----\n"; //$NON-NLS-1$
- private static final String VERSION = "version "; //$NON-NLS-1$
+ static final String VERSION = "certificate version"; //$NON-NLS-1$
- private static final String PUSHER = "pusher"; //$NON-NLS-1$
+ static final String PUSHER = "pusher"; //$NON-NLS-1$
- private static final String PUSHEE = "pushee"; //$NON-NLS-1$
+ static final String PUSHEE = "pushee"; //$NON-NLS-1$
- private static final String NONCE = "nonce"; //$NON-NLS-1$
+ static final String NONCE = "nonce"; //$NON-NLS-1$
- /** The individual certificate which is presented to the client */
+ private static final String VERSION_0_1 = "0.1"; //$NON-NLS-1$
+
+ private static final String END_CERT = "push-cert-end\n"; //$NON-NLS-1$
+
+ private String version;
+ private PersonIdent pusher;
+ private String pushee;
+
+ /** The nonce that was sent to the client. */
private String sentNonce;
/**
- * The nonce the pusher signed. This may vary from pushCertNonce See
- * git-core documentation for reasons.
+ * The nonce the pusher signed.
+ * <p>
+ * This may vary from {@link #sentNonce}; see git-core documentation for
+ * reasons.
*/
private String receivedNonce;
+ private NonceStatus nonceStatus;
+ private String signature;
+
+ /** Database we write the push certificate into. */
+ private final Repository db;
+
/**
* The maximum time difference which is acceptable between advertised nonce
* and received signed nonce.
*/
- private int nonceSlopLimit;
+ private final int nonceSlopLimit;
- NonceGenerator nonceGenerator;
-
- /**
- * used to build up commandlist
- */
- StringBuilder commandlistBuilder;
-
- /** Database we write the push certificate into. */
- private Repository db;
+ private final NonceGenerator nonceGenerator;
+ private final List<ReceiveCommand> commands;
+ private final StringBuilder rawCommands;
PushCertificateParser(Repository into, ReceiveConfig cfg) {
nonceSlopLimit = cfg.certNonceSlopLimit;
? new HMACSHA1NonceGenerator(cfg.certNonceSeed)
: null;
db = into;
+ commands = new ArrayList<>();
+ rawCommands = new StringBuilder();
+ }
+
+ /**
+ * @return the parsed certificate, or null if push certificates are disabled.
+ * @throws IOException
+ * if the push certificate has missing or invalid fields.
+ * @since 4.1
+ */
+ public PushCertificate build() throws IOException {
+ if (nonceGenerator == null) {
+ return null;
+ }
+ try {
+ return new PushCertificate(version, pusher, pushee, receivedNonce,
+ nonceStatus, Collections.unmodifiableList(commands),
+ rawCommands.toString(), signature);
+ } catch (IllegalArgumentException e) {
+ throw new IOException(e.getMessage(), e);
+ }
}
/**
* @return if the server is configured to use signed pushes.
+ * @since 4.0
*/
public boolean enabled() {
return nonceGenerator != null;
/**
* @return the whole string for the nonce to be included into the capability
- * advertisement.
+ * advertisement, or null if push certificates are disabled.
+ * @since 4.0
*/
public String getAdvertiseNonce() {
- sentNonce = nonceGenerator.createNonce(db,
- TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
- return CAPABILITY_PUSH_CERT + "=" + sentNonce; //$NON-NLS-1$
+ String nonce = sentNonce();
+ if (nonce == null) {
+ return null;
+ }
+ return CAPABILITY_PUSH_CERT + '=' + nonce;
}
- private String parseNextLine(PacketLineIn pckIn, String startingWith)
+ private String sentNonce() {
+ if (sentNonce == null && nonceGenerator != null) {
+ sentNonce = nonceGenerator.createNonce(db,
+ TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
+ }
+ return sentNonce;
+ }
+
+ private static String parseHeader(PacketLineIn pckIn, String header)
throws IOException {
String s = pckIn.readString();
- if (!s.startsWith(startingWith))
+ if (s.length() <= header.length()
+ || !s.startsWith(header)
+ || s.charAt(header.length()) != ' ') {
throw new IOException(MessageFormat.format(
- JGitText.get().errorInvalidPushCert,
- "expected " + startingWith)); //$NON-NLS-1$
- return s.substring(startingWith.length());
+ JGitText.get().pushCertificateInvalidHeader, header));
+ }
+ return s.substring(header.length() + 1);
}
/**
* Receive a list of commands from the input encapsulated in a push
- * certificate. This method doesn't parse the first line "push-cert \NUL
- * <capabilities>", but assumes the first line including the
+ * certificate.
+ * <p>
+ * This method doesn't parse the first line {@code "push-cert \NUL
+ * <capabilities>"}, but assumes the first line including the
* capabilities has already been handled by the caller.
*
* @param pckIn
* @throws IOException
* if the certificate from the client is badly malformed or the
* client disconnects before sending the entire certificate.
+ * @since 4.0
*/
public void receiveHeader(PacketLineIn pckIn, boolean stateless)
throws IOException {
try {
- String version = parseNextLine(pckIn, VERSION);
- if (!version.equals("0.1")) { //$NON-NLS-1$
+ version = parseHeader(pckIn, VERSION);
+ if (!version.equals(VERSION_0_1)) {
throw new IOException(MessageFormat.format(
- JGitText.get().errorInvalidPushCert,
- "version not supported")); //$NON-NLS-1$
+ JGitText.get().pushCertificateInvalidFieldValue, VERSION, version));
}
- pusher = parseNextLine(pckIn, PUSHER);
- pushee = parseNextLine(pckIn, PUSHEE);
- receivedNonce = parseNextLine(pckIn, NONCE);
- // an empty line
- if (!pckIn.readString().isEmpty()) {
+ String pusherStr = parseHeader(pckIn, PUSHER);
+ pusher = RawParseUtils.parsePersonIdent(pusherStr);
+ if (pusher == null) {
throw new IOException(MessageFormat.format(
- JGitText.get().errorInvalidPushCert,
- "expected empty line after header")); //$NON-NLS-1$
+ JGitText.get().pushCertificateInvalidFieldValue,
+ PUSHER, pusherStr));
+ }
+ pushee = parseHeader(pckIn, PUSHEE);
+ receivedNonce = parseHeader(pckIn, NONCE);
+ // An empty line.
+ if (!pckIn.readString().isEmpty()) {
+ throw new IOException(
+ JGitText.get().pushCertificateInvalidHeader);
}
} catch (EOFException eof) {
- throw new IOException(MessageFormat.format(
- JGitText.get().errorInvalidPushCert,
- "broken push certificate header")); //$NON-NLS-1$
+ throw new IOException(
+ JGitText.get().pushCertificateInvalidHeader, eof);
}
- nonceStatus = nonceGenerator.verify(receivedNonce, sentNonce, db,
- stateless, nonceSlopLimit);
+ nonceStatus = nonceGenerator != null
+ ? nonceGenerator.verify(
+ receivedNonce, sentNonce(), db, stateless, nonceSlopLimit)
+ : NonceStatus.UNSOLICITED;
}
/**
- * Reads the gpg signature. This method assumes the line "-----BEGIN PGP
- * SIGNATURE-----\n" has already been parsed and continues parsing until an
- * "-----END PGP SIGNATURE-----\n" is found.
+ * Read the PGP signature.
+ * <p>
+ * This method assumes the line
+ * {@code "-----BEGIN PGP SIGNATURE-----\n"} has already been parsed,
+ * and continues parsing until an {@code "-----END PGP SIGNATURE-----\n"} is
+ * found, followed by {@code "push-cert-end\n"}.
*
* @param pckIn
* where we read the signature from.
* @throws IOException
+ * if the signature is invalid.
+ * @since 4.0
*/
public void receiveSignature(PacketLineIn pckIn) throws IOException {
try {
StringBuilder sig = new StringBuilder();
- String line = pckIn.readStringRaw();
- while (!line.equals("-----END PGP SIGNATURE-----\n")) //$NON-NLS-1$
+ String line;
+ while (!(line = pckIn.readStringRaw()).equals(END_SIGNATURE)) {
sig.append(line);
+ }
signature = sig.toString();
- commandList = commandlistBuilder.toString();
+ if (!pckIn.readStringRaw().equals(END_CERT)) {
+ throw new IOException(JGitText.get().pushCertificateInvalidSignature);
+ }
} catch (EOFException eof) {
- throw new IOException(MessageFormat.format(
- JGitText.get().errorInvalidPushCert,
- "broken push certificate signature")); //$NON-NLS-1$
+ throw new IOException(
+ JGitText.get().pushCertificateInvalidSignature, eof);
}
}
/**
+ * Add a command to the signature.
+ *
+ * @param cmd
+ * the command.
+ * @param rawLine
+ * the exact line read from the wire that produced this
+ * command, including trailing newline if present.
+ * @since 4.1
+ */
+ public void addCommand(ReceiveCommand cmd, String rawLine) {
+ commands.add(cmd);
+ rawCommands.append(rawLine);
+ }
+
+ /**
+ * Add a command to the signature.
+ *
* @param rawLine
+ * the exact line read from the wire that produced this
+ * command, including trailing newline if present.
+ * @since 4.0
*/
public void addCommand(String rawLine) {
- commandlistBuilder.append(rawLine);
+ commands.add(parseCommand(chomp(rawLine)));
+ rawCommands.append(rawLine);
}
}