1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 package org.eclipse.jgit.transport;
44
45 import static org.junit.Assert.assertEquals;
46 import static org.junit.Assert.assertFalse;
47 import static org.junit.Assert.assertNotEquals;
48 import static org.junit.Assert.assertNotNull;
49 import static org.junit.Assert.assertNull;
50 import static org.junit.Assert.assertTrue;
51 import static org.junit.Assert.fail;
52
53 import java.io.ByteArrayInputStream;
54 import java.io.EOFException;
55 import java.io.IOException;
56 import java.io.InputStreamReader;
57 import java.io.Reader;
58 import java.io.StringReader;
59
60 import org.eclipse.jgit.errors.PackProtocolException;
61 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
62 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
63 import org.eclipse.jgit.lib.Config;
64 import org.eclipse.jgit.lib.Constants;
65 import org.eclipse.jgit.lib.ObjectId;
66 import org.eclipse.jgit.lib.Repository;
67 import org.eclipse.jgit.transport.PushCertificate.NonceStatus;
68 import org.junit.Before;
69 import org.junit.Test;
70
71
72 public class PushCertificateParserTest {
73
74 private static final String INPUT = "001ccertificate version 0.1\n"
75 + "0041pusher Dave Borowitz <dborowitz@google.com> 1433954361 -0700\n"
76 + "0024pushee git://localhost/repo.git\n"
77 + "002anonce 1433954361-bde756572d665bba81d8\n"
78 + "0005\n"
79 + "00680000000000000000000000000000000000000000"
80 + " 6c2b981a177396fb47345b7df3e4d3f854c6bea7"
81 + " refs/heads/master\n"
82 + "0022-----BEGIN PGP SIGNATURE-----\n"
83 + "0016Version: GnuPG v1\n"
84 + "0005\n"
85 + "0045iQEcBAABAgAGBQJVeGg5AAoJEPfTicJkUdPkUggH/RKAeI9/i/LduuiqrL/SSdIa\n"
86 + "00459tYaSqJKLbXz63M/AW4Sp+4u+dVCQvnAt/a35CVEnpZz6hN4Kn/tiswOWVJf4CO7\n"
87 + "0045htNubGs5ZMwvD6sLYqKAnrM3WxV/2TbbjzjZW6Jkidz3jz/WRT4SmjGYiEO7aA+V\n"
88 + "00454ZdIS9f7sW5VsHHYlNThCA7vH8Uu48bUovFXyQlPTX0pToSgrWV3JnTxDNxfn3iG\n"
89 + "0045IL0zTY/qwVCdXgFownLcs6J050xrrBWIKqfcWr3u4D2aCLyR0v+S/KArr7ulZygY\n"
90 + "0045+SOklImn8TAZiNxhWtA6ens66IiammUkZYFv7SSzoPLFZT4dC84SmGPWgf94NoQ=\n"
91 + "000a=XFeC\n"
92 + "0020-----END PGP SIGNATURE-----\n"
93 + "0012push-cert-end\n";
94
95
96
97
98 private static final String INPUT_NO_NEWLINES = "001bcertificate version 0.1"
99 + "0040pusher Dave Borowitz <dborowitz@google.com> 1433954361 -0700"
100 + "0023pushee git://localhost/repo.git"
101 + "0029nonce 1433954361-bde756572d665bba81d8"
102 + "0004"
103 + "00670000000000000000000000000000000000000000"
104 + " 6c2b981a177396fb47345b7df3e4d3f854c6bea7"
105 + " refs/heads/master"
106 + "0021-----BEGIN PGP SIGNATURE-----"
107 + "0015Version: GnuPG v1"
108 + "0004"
109 + "0044iQEcBAABAgAGBQJVeGg5AAoJEPfTicJkUdPkUggH/RKAeI9/i/LduuiqrL/SSdIa"
110 + "00449tYaSqJKLbXz63M/AW4Sp+4u+dVCQvnAt/a35CVEnpZz6hN4Kn/tiswOWVJf4CO7"
111 + "0044htNubGs5ZMwvD6sLYqKAnrM3WxV/2TbbjzjZW6Jkidz3jz/WRT4SmjGYiEO7aA+V"
112 + "00444ZdIS9f7sW5VsHHYlNThCA7vH8Uu48bUovFXyQlPTX0pToSgrWV3JnTxDNxfn3iG"
113 + "0044IL0zTY/qwVCdXgFownLcs6J050xrrBWIKqfcWr3u4D2aCLyR0v+S/KArr7ulZygY"
114 + "0044+SOklImn8TAZiNxhWtA6ens66IiammUkZYFv7SSzoPLFZT4dC84SmGPWgf94NoQ="
115 + "0009=XFeC"
116 + "001f-----END PGP SIGNATURE-----"
117 + "0011push-cert-end";
118
119 private Repository db;
120
121 @Before
122 public void setUp() {
123 db = new InMemoryRepository(new DfsRepositoryDescription("repo"));
124 }
125
126 private static SignedPushConfig newEnabledConfig() {
127 Config cfg = new Config();
128 cfg.setString("receive", null, "certnonceseed", "sekret");
129 return SignedPushConfig.KEY.parse(cfg);
130 }
131
132 private static SignedPushConfig newDisabledConfig() {
133 return SignedPushConfig.KEY.parse(new Config());
134 }
135
136 @Test
137 public void noCert() throws Exception {
138 PushCertificateParser parser =
139 new PushCertificateParser(db, newEnabledConfig());
140 assertTrue(parser.enabled());
141 assertNull(parser.build());
142
143 ObjectId oldId = ObjectId.zeroId();
144 ObjectId newId =
145 ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
146 String line = oldId.name() + " " + newId.name() + " refs/heads/master";
147 ReceiveCommand cmd = BaseReceivePack.parseCommand(line);
148
149 parser.addCommand(cmd);
150 parser.addCommand(line);
151 assertNull(parser.build());
152 }
153
154 @Test
155 public void disabled() throws Exception {
156 PacketLineIn pckIn = newPacketLineIn(INPUT);
157 PushCertificateParser parser =
158 new PushCertificateParser(db, newDisabledConfig());
159 assertFalse(parser.enabled());
160 assertNull(parser.build());
161
162 parser.receiveHeader(pckIn, false);
163 parser.addCommand(pckIn.readString());
164 assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
165 parser.receiveSignature(pckIn);
166 assertNull(parser.build());
167 }
168
169 @Test
170 public void disabledParserStillRequiresCorrectSyntax() throws Exception {
171 PacketLineIn pckIn = newPacketLineIn("001ccertificate version XYZ\n");
172 PushCertificateParser parser =
173 new PushCertificateParser(db, newDisabledConfig());
174 assertFalse(parser.enabled());
175 try {
176 parser.receiveHeader(pckIn, false);
177 fail("Expected PackProtocolException");
178 } catch (PackProtocolException e) {
179 assertEquals(
180 "Push certificate has missing or invalid value for certificate"
181 + " version: XYZ",
182 e.getMessage());
183 }
184 assertNull(parser.build());
185 }
186
187 @Test
188 public void parseCertFromPktLine() throws Exception {
189 PacketLineIn pckIn = newPacketLineIn(INPUT);
190 PushCertificateParser parser =
191 new PushCertificateParser(db, newEnabledConfig());
192 parser.receiveHeader(pckIn, false);
193 parser.addCommand(pckIn.readString());
194 assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
195 parser.receiveSignature(pckIn);
196
197 PushCertificate cert = parser.build();
198 assertEquals("0.1", cert.getVersion());
199 assertEquals("Dave Borowitz", cert.getPusherIdent().getName());
200 assertEquals("dborowitz@google.com",
201 cert.getPusherIdent().getEmailAddress());
202 assertEquals(1433954361000L, cert.getPusherIdent().getWhen().getTime());
203 assertEquals(-7 * 60, cert.getPusherIdent().getTimeZoneOffset());
204 assertEquals("git://localhost/repo.git", cert.getPushee());
205 assertEquals("1433954361-bde756572d665bba81d8", cert.getNonce());
206
207 assertNotEquals(cert.getNonce(), parser.getAdvertiseNonce());
208 assertEquals(PushCertificate.NonceStatus.BAD, cert.getNonceStatus());
209
210 assertEquals(1, cert.getCommands().size());
211 ReceiveCommand cmd = cert.getCommands().get(0);
212 assertEquals("refs/heads/master", cmd.getRefName());
213 assertEquals(ObjectId.zeroId(), cmd.getOldId());
214 assertEquals("6c2b981a177396fb47345b7df3e4d3f854c6bea7",
215 cmd.getNewId().name());
216
217 assertEquals(concatPacketLines(INPUT, 0, 6), cert.toText());
218 assertEquals(concatPacketLines(INPUT, 0, 17), cert.toTextWithSignature());
219
220 String signature = concatPacketLines(INPUT, 6, 17);
221 assertTrue(signature.startsWith(PushCertificateParser.BEGIN_SIGNATURE));
222 assertTrue(signature.endsWith(PushCertificateParser.END_SIGNATURE + "\n"));
223 assertEquals(signature, cert.getSignature());
224 }
225
226 @Test
227 public void parseCertFromPktLineNoNewlines() throws Exception {
228 PacketLineIn pckIn = newPacketLineIn(INPUT_NO_NEWLINES);
229 PushCertificateParser parser =
230 new PushCertificateParser(db, newEnabledConfig());
231 parser.receiveHeader(pckIn, false);
232 parser.addCommand(pckIn.readString());
233 assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
234 parser.receiveSignature(pckIn);
235
236 PushCertificate cert = parser.build();
237 assertEquals("0.1", cert.getVersion());
238 assertEquals("Dave Borowitz", cert.getPusherIdent().getName());
239 assertEquals("dborowitz@google.com",
240 cert.getPusherIdent().getEmailAddress());
241 assertEquals(1433954361000L, cert.getPusherIdent().getWhen().getTime());
242 assertEquals(-7 * 60, cert.getPusherIdent().getTimeZoneOffset());
243 assertEquals("git://localhost/repo.git", cert.getPushee());
244 assertEquals("1433954361-bde756572d665bba81d8", cert.getNonce());
245
246 assertNotEquals(cert.getNonce(), parser.getAdvertiseNonce());
247 assertEquals(PushCertificate.NonceStatus.BAD, cert.getNonceStatus());
248
249 assertEquals(1, cert.getCommands().size());
250 ReceiveCommand cmd = cert.getCommands().get(0);
251 assertEquals("refs/heads/master", cmd.getRefName());
252 assertEquals(ObjectId.zeroId(), cmd.getOldId());
253 assertEquals("6c2b981a177396fb47345b7df3e4d3f854c6bea7",
254 cmd.getNewId().name());
255
256
257 assertEquals(concatPacketLines(INPUT, 0, 6), cert.toText());
258
259 String signature = concatPacketLines(INPUT, 6, 17);
260 assertTrue(signature.startsWith(PushCertificateParser.BEGIN_SIGNATURE));
261 assertTrue(signature.endsWith(PushCertificateParser.END_SIGNATURE + "\n"));
262 assertEquals(signature, cert.getSignature());
263 }
264
265 @Test
266 public void testConcatPacketLines() throws Exception {
267 String input = "000bline 1\n000bline 2\n000bline 3\n";
268 assertEquals("line 1\n", concatPacketLines(input, 0, 1));
269 assertEquals("line 1\nline 2\n", concatPacketLines(input, 0, 2));
270 assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 3));
271 assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 4));
272 }
273
274 @Test
275 public void testConcatPacketLinesInsertsNewlines() throws Exception {
276 String input = "000bline 1\n000aline 2000bline 3\n";
277 assertEquals("line 1\n", concatPacketLines(input, 0, 1));
278 assertEquals("line 1\nline 2\n", concatPacketLines(input, 0, 2));
279 assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 3));
280 assertEquals("line 2\nline 3\n", concatPacketLines(input, 1, 4));
281 }
282
283 @Test
284 public void testParseReader() throws Exception {
285 Reader reader = new StringReader(concatPacketLines(INPUT, 0, 18));
286 PushCertificate streamCert = PushCertificateParser.fromReader(reader);
287
288 PacketLineIn pckIn = newPacketLineIn(INPUT);
289 PushCertificateParser pckParser =
290 new PushCertificateParser(db, newEnabledConfig());
291 pckParser.receiveHeader(pckIn, false);
292 pckParser.addCommand(pckIn.readString());
293 assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
294 pckParser.receiveSignature(pckIn);
295 PushCertificate pckCert = pckParser.build();
296
297
298
299 assertEquals(NonceStatus.UNSOLICITED, streamCert.getNonceStatus());
300
301 assertEquals(pckCert.getVersion(), streamCert.getVersion());
302 assertEquals(pckCert.getPusherIdent().getName(),
303 streamCert.getPusherIdent().getName());
304 assertEquals(pckCert.getPusherIdent().getEmailAddress(),
305 streamCert.getPusherIdent().getEmailAddress());
306 assertEquals(pckCert.getPusherIdent().getWhen().getTime(),
307 streamCert.getPusherIdent().getWhen().getTime());
308 assertEquals(pckCert.getPusherIdent().getTimeZoneOffset(),
309 streamCert.getPusherIdent().getTimeZoneOffset());
310 assertEquals(pckCert.getPushee(), streamCert.getPushee());
311 assertEquals(pckCert.getNonce(), streamCert.getNonce());
312 assertEquals(pckCert.getSignature(), streamCert.getSignature());
313 assertEquals(pckCert.toText(), streamCert.toText());
314
315 assertEquals(pckCert.getCommands().size(), streamCert.getCommands().size());
316 ReceiveCommand pckCmd = pckCert.getCommands().get(0);
317 ReceiveCommand streamCmd = streamCert.getCommands().get(0);
318 assertEquals(pckCmd.getRefName(), streamCmd.getRefName());
319 assertEquals(pckCmd.getOldId(), streamCmd.getOldId());
320 assertEquals(pckCmd.getNewId().name(), streamCmd.getNewId().name());
321 }
322
323 @Test
324 public void testParseString() throws Exception {
325 String str = concatPacketLines(INPUT, 0, 18);
326 assertEquals(
327 PushCertificateParser.fromReader(new StringReader(str)),
328 PushCertificateParser.fromString(str));
329 }
330
331 @Test
332 public void testParseMultipleFromStream() throws Exception {
333 String input = concatPacketLines(INPUT, 0, 17);
334 assertFalse(input.contains(PushCertificateParser.END_CERT));
335 input += input;
336 Reader reader = new InputStreamReader(
337 new ByteArrayInputStream(Constants.encode(input)));
338
339 assertNotNull(PushCertificateParser.fromReader(reader));
340 assertNotNull(PushCertificateParser.fromReader(reader));
341 assertEquals(-1, reader.read());
342 assertNull(PushCertificateParser.fromReader(reader));
343 }
344
345 @Test
346 public void testMissingPusheeField() throws Exception {
347
348
349 String input = INPUT.replace("0024pushee git://localhost/repo.git\n", "");
350 assertFalse(input.contains(PushCertificateParser.PUSHEE));
351
352 PacketLineIn pckIn = newPacketLineIn(input);
353 PushCertificateParser parser =
354 new PushCertificateParser(db, newEnabledConfig());
355 parser.receiveHeader(pckIn, false);
356 parser.addCommand(pckIn.readString());
357 assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString());
358 parser.receiveSignature(pckIn);
359
360 PushCertificate cert = parser.build();
361 assertEquals("0.1", cert.getVersion());
362 assertNull(cert.getPushee());
363 assertFalse(cert.toText().contains(PushCertificateParser.PUSHEE));
364 }
365
366 private static String concatPacketLines(String input, int begin, int end)
367 throws IOException {
368 StringBuilder result = new StringBuilder();
369 int i = 0;
370 PacketLineIn pckIn = newPacketLineIn(input);
371 while (i < end) {
372 String line;
373 try {
374 line = pckIn.readString();
375 } catch (EOFException e) {
376 break;
377 }
378 if (++i > begin) {
379 result.append(line).append('\n');
380 }
381 }
382 return result.toString();
383 }
384
385 private static PacketLineIn newPacketLineIn(String input) {
386 return new PacketLineIn(new ByteArrayInputStream(Constants.encode(input)));
387 }
388 }