You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PushCertificate.java 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /*
  2. * Copyright (C) 2015, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.transport;
  44. import static org.eclipse.jgit.transport.PushCertificateParser.NONCE;
  45. import static org.eclipse.jgit.transport.PushCertificateParser.PUSHEE;
  46. import static org.eclipse.jgit.transport.PushCertificateParser.PUSHER;
  47. import static org.eclipse.jgit.transport.PushCertificateParser.VERSION;
  48. import java.text.MessageFormat;
  49. import java.util.List;
  50. import java.util.Objects;
  51. import org.eclipse.jgit.internal.JGitText;
  52. /**
  53. * The required information to verify the push.
  54. * <p>
  55. * A valid certificate will not return null from any getter methods; callers may
  56. * assume that any null value indicates a missing or invalid certificate.
  57. *
  58. * @since 4.0
  59. */
  60. public class PushCertificate {
  61. /** Verification result of the nonce returned during push. */
  62. public enum NonceStatus {
  63. /** Nonce was not expected, yet client sent one anyway. */
  64. UNSOLICITED,
  65. /** Nonce is invalid and did not match server's expectations. */
  66. BAD,
  67. /** Nonce is required, but was not sent by client. */
  68. MISSING,
  69. /**
  70. * Received nonce matches sent nonce, or is valid within the accepted slop
  71. * window.
  72. */
  73. OK,
  74. /** Received nonce is valid, but outside the accepted slop window. */
  75. SLOP
  76. }
  77. private final String version;
  78. private final PushCertificateIdent pusher;
  79. private final String pushee;
  80. private final String nonce;
  81. private final NonceStatus nonceStatus;
  82. private final List<ReceiveCommand> commands;
  83. private final String signature;
  84. PushCertificate(String version, PushCertificateIdent pusher, String pushee,
  85. String nonce, NonceStatus nonceStatus, List<ReceiveCommand> commands,
  86. String signature) {
  87. if (version == null || version.isEmpty()) {
  88. throw new IllegalArgumentException(MessageFormat.format(
  89. JGitText.get().pushCertificateInvalidField, VERSION));
  90. }
  91. if (pusher == null) {
  92. throw new IllegalArgumentException(MessageFormat.format(
  93. JGitText.get().pushCertificateInvalidField, PUSHER));
  94. }
  95. if (nonce == null || nonce.isEmpty()) {
  96. throw new IllegalArgumentException(MessageFormat.format(
  97. JGitText.get().pushCertificateInvalidField, NONCE));
  98. }
  99. if (nonceStatus == null) {
  100. throw new IllegalArgumentException(MessageFormat.format(
  101. JGitText.get().pushCertificateInvalidField,
  102. "nonce status")); //$NON-NLS-1$
  103. }
  104. if (commands == null || commands.isEmpty()) {
  105. throw new IllegalArgumentException(MessageFormat.format(
  106. JGitText.get().pushCertificateInvalidField,
  107. "command")); //$NON-NLS-1$
  108. }
  109. if (signature == null || signature.isEmpty()) {
  110. throw new IllegalArgumentException(
  111. JGitText.get().pushCertificateInvalidSignature);
  112. }
  113. if (!signature.startsWith(PushCertificateParser.BEGIN_SIGNATURE)
  114. || !signature.endsWith(PushCertificateParser.END_SIGNATURE + '\n')) {
  115. throw new IllegalArgumentException(
  116. JGitText.get().pushCertificateInvalidSignature);
  117. }
  118. this.version = version;
  119. this.pusher = pusher;
  120. this.pushee = pushee;
  121. this.nonce = nonce;
  122. this.nonceStatus = nonceStatus;
  123. this.commands = commands;
  124. this.signature = signature;
  125. }
  126. /**
  127. * @return the certificate version string.
  128. * @since 4.1
  129. */
  130. public String getVersion() {
  131. return version;
  132. }
  133. /**
  134. * @return the raw line that signed the cert, as a string.
  135. * @since 4.0
  136. */
  137. public String getPusher() {
  138. return pusher.getRaw();
  139. }
  140. /**
  141. * @return identity of the pusher who signed the cert.
  142. * @since 4.1
  143. */
  144. public PushCertificateIdent getPusherIdent() {
  145. return pusher;
  146. }
  147. /**
  148. * @return URL of the repository the push was originally sent to.
  149. * @since 4.0
  150. */
  151. public String getPushee() {
  152. return pushee;
  153. }
  154. /**
  155. * @return the raw nonce value that was presented by the pusher.
  156. * @since 4.1
  157. */
  158. public String getNonce() {
  159. return nonce;
  160. }
  161. /**
  162. * @return verification status of the nonce embedded in the certificate.
  163. * @since 4.0
  164. */
  165. public NonceStatus getNonceStatus() {
  166. return nonceStatus;
  167. }
  168. /**
  169. * @return the list of commands as one string to be feed into the signature
  170. * verifier.
  171. * @since 4.1
  172. */
  173. public List<ReceiveCommand> getCommands() {
  174. return commands;
  175. }
  176. /**
  177. * @return the raw signature, consisting of the lines received between the
  178. * lines {@code "----BEGIN GPG SIGNATURE-----\n"} and
  179. * {@code "----END GPG SIGNATURE-----\n}", inclusive.
  180. * @since 4.0
  181. */
  182. public String getSignature() {
  183. return signature;
  184. }
  185. /**
  186. * @return text payload of the certificate for the signature verifier.
  187. * @since 4.1
  188. */
  189. public String toText() {
  190. StringBuilder sb = new StringBuilder()
  191. .append(VERSION).append(' ').append(version).append('\n')
  192. .append(PUSHER).append(' ').append(getPusher())
  193. .append('\n')
  194. .append(PUSHEE).append(' ').append(pushee).append('\n')
  195. .append(NONCE).append(' ').append(nonce).append('\n')
  196. .append('\n');
  197. for (ReceiveCommand cmd : commands) {
  198. sb.append(cmd.getOldId().name())
  199. .append(' ').append(cmd.getNewId().name())
  200. .append(' ').append(cmd.getRefName()).append('\n');
  201. }
  202. return sb.toString();
  203. }
  204. @Override
  205. public int hashCode() {
  206. return signature.hashCode();
  207. }
  208. @Override
  209. public boolean equals(Object o) {
  210. if (!(o instanceof PushCertificate)) {
  211. return false;
  212. }
  213. PushCertificate p = (PushCertificate) o;
  214. return version.equals(p.version)
  215. && pusher.equals(p.pusher)
  216. && Objects.equals(pushee, p.pushee)
  217. && nonceStatus == p.nonceStatus
  218. && signature.equals(p.signature)
  219. && commandsEqual(this, p);
  220. }
  221. private static boolean commandsEqual(PushCertificate c1, PushCertificate c2) {
  222. if (c1.commands.size() != c2.commands.size()) {
  223. return false;
  224. }
  225. for (int i = 0; i < c1.commands.size(); i++) {
  226. ReceiveCommand cmd1 = c1.commands.get(i);
  227. ReceiveCommand cmd2 = c2.commands.get(i);
  228. if (!cmd1.getOldId().equals(cmd2.getOldId())
  229. || !cmd1.getNewId().equals(cmd2.getNewId())
  230. || !cmd1.getRefName().equals(cmd2.getRefName())) {
  231. return false;
  232. }
  233. }
  234. return true;
  235. }
  236. @Override
  237. public String toString() {
  238. return getClass().getSimpleName() + '['
  239. + toText() + signature + ']';
  240. }
  241. }