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.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * Copyright (C) 2015, Google Inc. and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.transport;
  11. import static org.eclipse.jgit.transport.PushCertificateParser.NONCE;
  12. import static org.eclipse.jgit.transport.PushCertificateParser.PUSHEE;
  13. import static org.eclipse.jgit.transport.PushCertificateParser.PUSHER;
  14. import static org.eclipse.jgit.transport.PushCertificateParser.VERSION;
  15. import java.text.MessageFormat;
  16. import java.util.List;
  17. import java.util.Objects;
  18. import org.eclipse.jgit.internal.JGitText;
  19. /**
  20. * The required information to verify the push.
  21. * <p>
  22. * A valid certificate will not return null from any getter methods; callers may
  23. * assume that any null value indicates a missing or invalid certificate.
  24. *
  25. * @since 4.0
  26. */
  27. public class PushCertificate {
  28. /** Verification result of the nonce returned during push. */
  29. public enum NonceStatus {
  30. /** Nonce was not expected, yet client sent one anyway. */
  31. UNSOLICITED,
  32. /** Nonce is invalid and did not match server's expectations. */
  33. BAD,
  34. /** Nonce is required, but was not sent by client. */
  35. MISSING,
  36. /**
  37. * Received nonce matches sent nonce, or is valid within the accepted slop
  38. * window.
  39. */
  40. OK,
  41. /** Received nonce is valid, but outside the accepted slop window. */
  42. SLOP
  43. }
  44. private final String version;
  45. private final PushCertificateIdent pusher;
  46. private final String pushee;
  47. private final String nonce;
  48. private final NonceStatus nonceStatus;
  49. private final List<ReceiveCommand> commands;
  50. private final String signature;
  51. PushCertificate(String version, PushCertificateIdent pusher, String pushee,
  52. String nonce, NonceStatus nonceStatus, List<ReceiveCommand> commands,
  53. String signature) {
  54. if (version == null || version.isEmpty()) {
  55. throw new IllegalArgumentException(MessageFormat.format(
  56. JGitText.get().pushCertificateInvalidField, VERSION));
  57. }
  58. if (pusher == null) {
  59. throw new IllegalArgumentException(MessageFormat.format(
  60. JGitText.get().pushCertificateInvalidField, PUSHER));
  61. }
  62. if (nonce == null || nonce.isEmpty()) {
  63. throw new IllegalArgumentException(MessageFormat.format(
  64. JGitText.get().pushCertificateInvalidField, NONCE));
  65. }
  66. if (nonceStatus == null) {
  67. throw new IllegalArgumentException(MessageFormat.format(
  68. JGitText.get().pushCertificateInvalidField,
  69. "nonce status")); //$NON-NLS-1$
  70. }
  71. if (commands == null || commands.isEmpty()) {
  72. throw new IllegalArgumentException(MessageFormat.format(
  73. JGitText.get().pushCertificateInvalidField,
  74. "command")); //$NON-NLS-1$
  75. }
  76. if (signature == null || signature.isEmpty()) {
  77. throw new IllegalArgumentException(
  78. JGitText.get().pushCertificateInvalidSignature);
  79. }
  80. if (!signature.startsWith(PushCertificateParser.BEGIN_SIGNATURE)
  81. || !signature.endsWith(PushCertificateParser.END_SIGNATURE + '\n')) {
  82. throw new IllegalArgumentException(
  83. JGitText.get().pushCertificateInvalidSignature);
  84. }
  85. this.version = version;
  86. this.pusher = pusher;
  87. this.pushee = pushee;
  88. this.nonce = nonce;
  89. this.nonceStatus = nonceStatus;
  90. this.commands = commands;
  91. this.signature = signature;
  92. }
  93. /**
  94. * Get the certificate version string.
  95. *
  96. * @return the certificate version string.
  97. * @since 4.1
  98. */
  99. public String getVersion() {
  100. return version;
  101. }
  102. /**
  103. * Get the raw line that signed the cert, as a string.
  104. *
  105. * @return the raw line that signed the cert, as a string.
  106. * @since 4.0
  107. */
  108. public String getPusher() {
  109. return pusher.getRaw();
  110. }
  111. /**
  112. * Get identity of the pusher who signed the cert.
  113. *
  114. * @return identity of the pusher who signed the cert.
  115. * @since 4.1
  116. */
  117. public PushCertificateIdent getPusherIdent() {
  118. return pusher;
  119. }
  120. /**
  121. * Get URL of the repository the push was originally sent to.
  122. *
  123. * @return URL of the repository the push was originally sent to.
  124. * @since 4.0
  125. */
  126. public String getPushee() {
  127. return pushee;
  128. }
  129. /**
  130. * Get the raw nonce value that was presented by the pusher.
  131. *
  132. * @return the raw nonce value that was presented by the pusher.
  133. * @since 4.1
  134. */
  135. public String getNonce() {
  136. return nonce;
  137. }
  138. /**
  139. * Get verification status of the nonce embedded in the certificate.
  140. *
  141. * @return verification status of the nonce embedded in the certificate.
  142. * @since 4.0
  143. */
  144. public NonceStatus getNonceStatus() {
  145. return nonceStatus;
  146. }
  147. /**
  148. * Get the list of commands as one string to be feed into the signature
  149. * verifier.
  150. *
  151. * @return the list of commands as one string to be feed into the signature
  152. * verifier.
  153. * @since 4.1
  154. */
  155. public List<ReceiveCommand> getCommands() {
  156. return commands;
  157. }
  158. /**
  159. * Get the raw signature
  160. *
  161. * @return the raw signature, consisting of the lines received between the
  162. * lines {@code "----BEGIN GPG SIGNATURE-----\n"} and
  163. * {@code "----END GPG SIGNATURE-----\n}", inclusive.
  164. * @since 4.0
  165. */
  166. public String getSignature() {
  167. return signature;
  168. }
  169. /**
  170. * Get text payload of the certificate for the signature verifier.
  171. *
  172. * @return text payload of the certificate for the signature verifier.
  173. * @since 4.1
  174. */
  175. public String toText() {
  176. return toStringBuilder().toString();
  177. }
  178. /**
  179. * Get original text payload plus signature
  180. *
  181. * @return original text payload plus signature; the final output will be
  182. * valid as input to
  183. * {@link org.eclipse.jgit.transport.PushCertificateParser#fromString(String)}.
  184. * @since 4.1
  185. */
  186. public String toTextWithSignature() {
  187. return toStringBuilder().append(signature).toString();
  188. }
  189. private StringBuilder toStringBuilder() {
  190. StringBuilder sb = new StringBuilder()
  191. .append(VERSION).append(' ').append(version).append('\n')
  192. .append(PUSHER).append(' ').append(getPusher())
  193. .append('\n');
  194. if (pushee != null) {
  195. sb.append(PUSHEE).append(' ').append(pushee).append('\n');
  196. }
  197. sb.append(NONCE).append(' ').append(nonce).append('\n')
  198. .append('\n');
  199. for (ReceiveCommand cmd : commands) {
  200. sb.append(cmd.getOldId().name())
  201. .append(' ').append(cmd.getNewId().name())
  202. .append(' ').append(cmd.getRefName()).append('\n');
  203. }
  204. return sb;
  205. }
  206. /** {@inheritDoc} */
  207. @Override
  208. public int hashCode() {
  209. return signature.hashCode();
  210. }
  211. /** {@inheritDoc} */
  212. @Override
  213. public boolean equals(Object o) {
  214. if (!(o instanceof PushCertificate)) {
  215. return false;
  216. }
  217. PushCertificate p = (PushCertificate) o;
  218. return version.equals(p.version)
  219. && pusher.equals(p.pusher)
  220. && Objects.equals(pushee, p.pushee)
  221. && nonceStatus == p.nonceStatus
  222. && signature.equals(p.signature)
  223. && commandsEqual(this, p);
  224. }
  225. private static boolean commandsEqual(PushCertificate c1, PushCertificate c2) {
  226. if (c1.commands.size() != c2.commands.size()) {
  227. return false;
  228. }
  229. for (int i = 0; i < c1.commands.size(); i++) {
  230. ReceiveCommand cmd1 = c1.commands.get(i);
  231. ReceiveCommand cmd2 = c2.commands.get(i);
  232. if (!cmd1.getOldId().equals(cmd2.getOldId())
  233. || !cmd1.getNewId().equals(cmd2.getNewId())
  234. || !cmd1.getRefName().equals(cmd2.getRefName())) {
  235. return false;
  236. }
  237. }
  238. return true;
  239. }
  240. /** {@inheritDoc} */
  241. @Override
  242. public String toString() {
  243. return getClass().getSimpleName() + '['
  244. + toTextWithSignature() + ']';
  245. }
  246. }