| /* | |
| * 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 java.nio.charset.StandardCharsets.UTF_8; | |
| import static org.junit.Assert.assertEquals; | |
| import static org.junit.Assert.assertFalse; | |
| import static org.junit.Assert.assertNotEquals; | |
| import static org.junit.Assert.assertNotNull; | |
| import static org.junit.Assert.assertNull; | |
| import static org.junit.Assert.assertTrue; | |
| import static org.junit.Assert.fail; | |
| import java.io.ByteArrayInputStream; | |
| import java.io.EOFException; | |
| import java.io.IOException; | |
| import java.io.InputStreamReader; | |
| import java.io.Reader; | |
| import java.io.StringReader; | |
| import java.util.List; | |
| import org.eclipse.jgit.errors.PackProtocolException; | |
| 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.PushCertificate.NonceStatus; | |
| import org.junit.Before; | |
| import org.junit.Test; | |
| /** Test for push certificate parsing. */ | |
| public class PushCertificateParserTest { | |
| // Example push certificate generated by C git 2.2.0. | |
| private static final 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"; | |
| // Same push certificate, with all trailing newlines stripped. | |
| // (Note that the canonical signed payload is the same, so the same signature | |
| // is still valid.) | |
| private static final String INPUT_NO_NEWLINES = "001bcertificate version 0.1" | |
| + "0040pusher Dave Borowitz <dborowitz@google.com> 1433954361 -0700" | |
| + "0023pushee git://localhost/repo.git" | |
| + "0029nonce 1433954361-bde756572d665bba81d8" | |
| + "0004" | |
| + "00670000000000000000000000000000000000000000" | |
| + " 6c2b981a177396fb47345b7df3e4d3f854c6bea7" | |
| + " refs/heads/master" | |
| + "0021-----BEGIN PGP SIGNATURE-----" | |
| + "0015Version: GnuPG v1" | |
| + "0004" | |
| + "0044iQEcBAABAgAGBQJVeGg5AAoJEPfTicJkUdPkUggH/RKAeI9/i/LduuiqrL/SSdIa" | |
| + "00449tYaSqJKLbXz63M/AW4Sp+4u+dVCQvnAt/a35CVEnpZz6hN4Kn/tiswOWVJf4CO7" | |
| + "0044htNubGs5ZMwvD6sLYqKAnrM3WxV/2TbbjzjZW6Jkidz3jz/WRT4SmjGYiEO7aA+V" | |
| + "00444ZdIS9f7sW5VsHHYlNThCA7vH8Uu48bUovFXyQlPTX0pToSgrWV3JnTxDNxfn3iG" | |
| + "0044IL0zTY/qwVCdXgFownLcs6J050xrrBWIKqfcWr3u4D2aCLyR0v+S/KArr7ulZygY" | |
| + "0044+SOklImn8TAZiNxhWtA6ens66IiammUkZYFv7SSzoPLFZT4dC84SmGPWgf94NoQ=" | |
| + "0009=XFeC" | |
| + "001f-----END PGP SIGNATURE-----" | |
| + "0011push-cert-end"; | |
| private Repository db; | |
| @Before | |
| public void setUp() { | |
| db = new InMemoryRepository(new DfsRepositoryDescription("repo")); | |
| } | |
| private static SignedPushConfig newEnabledConfig() { | |
| Config cfg = new Config(); | |
| cfg.setString("receive", null, "certnonceseed", "sekret"); | |
| return SignedPushConfig.KEY.parse(cfg); | |
| } | |
| private static SignedPushConfig newDisabledConfig() { | |
| return SignedPushConfig.KEY.parse(new Config()); | |
| } | |
| @Test | |
| public void noCert() throws Exception { | |
| PushCertificateParser parser = | |
| new PushCertificateParser(db, newEnabledConfig()); | |
| assertTrue(parser.enabled()); | |
| assertNull(parser.build()); | |
| ObjectId oldId = ObjectId.zeroId(); | |
| ObjectId newId = | |
| ObjectId.fromString("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"); | |
| String line = oldId.name() + " " + newId.name() + " refs/heads/master"; | |
| ReceiveCommand cmd = ReceivePack.parseCommand(line); | |
| parser.addCommand(cmd); | |
| parser.addCommand(line); | |
| assertNull(parser.build()); | |
| } | |
| @Test | |
| public void disabled() throws Exception { | |
| PacketLineIn pckIn = newPacketLineIn(INPUT); | |
| PushCertificateParser parser = | |
| new PushCertificateParser(db, newDisabledConfig()); | |
| assertFalse(parser.enabled()); | |
| assertNull(parser.build()); | |
| parser.receiveHeader(pckIn, false); | |
| parser.addCommand(pckIn.readString()); | |
| assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString()); | |
| parser.receiveSignature(pckIn); | |
| assertNull(parser.build()); | |
| } | |
| @Test | |
| public void disabledParserStillRequiresCorrectSyntax() throws Exception { | |
| PacketLineIn pckIn = newPacketLineIn("001ccertificate version XYZ\n"); | |
| PushCertificateParser parser = | |
| new PushCertificateParser(db, newDisabledConfig()); | |
| assertFalse(parser.enabled()); | |
| try { | |
| parser.receiveHeader(pckIn, false); | |
| fail("Expected PackProtocolException"); | |
| } catch (PackProtocolException e) { | |
| assertEquals( | |
| "Push certificate has missing or invalid value for certificate" | |
| + " version: XYZ", | |
| e.getMessage()); | |
| } | |
| assertNull(parser.build()); | |
| } | |
| @Test | |
| public void parseCertFromPktLine() throws Exception { | |
| PacketLineIn pckIn = newPacketLineIn(INPUT); | |
| PushCertificateParser parser = | |
| new PushCertificateParser(db, newEnabledConfig()); | |
| parser.receiveHeader(pckIn, false); | |
| parser.addCommand(pckIn.readString()); | |
| assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString()); | |
| 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()); | |
| assertEquals(concatPacketLines(INPUT, 0, 17), cert.toTextWithSignature()); | |
| String signature = concatPacketLines(INPUT, 6, 17); | |
| assertTrue(signature.startsWith(PushCertificateParser.BEGIN_SIGNATURE)); | |
| assertTrue(signature.endsWith(PushCertificateParser.END_SIGNATURE + "\n")); | |
| assertEquals(signature, cert.getSignature()); | |
| } | |
| @Test | |
| public void parseCertFromPktLineNoNewlines() throws Exception { | |
| PacketLineIn pckIn = newPacketLineIn(INPUT_NO_NEWLINES); | |
| PushCertificateParser parser = | |
| new PushCertificateParser(db, newEnabledConfig()); | |
| parser.receiveHeader(pckIn, false); | |
| parser.addCommand(pckIn.readString()); | |
| assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString()); | |
| 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()); | |
| // Canonical signed payload has reinserted newlines. | |
| assertEquals(concatPacketLines(INPUT, 0, 6), cert.toText()); | |
| String signature = concatPacketLines(INPUT, 6, 17); | |
| assertTrue(signature.startsWith(PushCertificateParser.BEGIN_SIGNATURE)); | |
| assertTrue(signature.endsWith(PushCertificateParser.END_SIGNATURE + "\n")); | |
| 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)); | |
| } | |
| @Test | |
| public void testConcatPacketLinesInsertsNewlines() throws Exception { | |
| String input = "000bline 1\n000aline 2000bline 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)); | |
| } | |
| @Test | |
| public void testParseReader() throws Exception { | |
| Reader reader = new StringReader(concatPacketLines(INPUT, 0, 18)); | |
| PushCertificate streamCert = PushCertificateParser.fromReader(reader); | |
| PacketLineIn pckIn = newPacketLineIn(INPUT); | |
| PushCertificateParser pckParser = | |
| new PushCertificateParser(db, newEnabledConfig()); | |
| pckParser.receiveHeader(pckIn, false); | |
| pckParser.addCommand(pckIn.readString()); | |
| assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString()); | |
| pckParser.receiveSignature(pckIn); | |
| PushCertificate pckCert = pckParser.build(); | |
| // Nonce status is unsolicited since this was not parsed in the context of | |
| // the wire protocol; as a result, certs are not actually equal. | |
| assertEquals(NonceStatus.UNSOLICITED, streamCert.getNonceStatus()); | |
| assertEquals(pckCert.getVersion(), streamCert.getVersion()); | |
| assertEquals(pckCert.getPusherIdent().getName(), | |
| streamCert.getPusherIdent().getName()); | |
| assertEquals(pckCert.getPusherIdent().getEmailAddress(), | |
| streamCert.getPusherIdent().getEmailAddress()); | |
| assertEquals(pckCert.getPusherIdent().getWhen().getTime(), | |
| streamCert.getPusherIdent().getWhen().getTime()); | |
| assertEquals(pckCert.getPusherIdent().getTimeZoneOffset(), | |
| streamCert.getPusherIdent().getTimeZoneOffset()); | |
| assertEquals(pckCert.getPushee(), streamCert.getPushee()); | |
| assertEquals(pckCert.getNonce(), streamCert.getNonce()); | |
| assertEquals(pckCert.getSignature(), streamCert.getSignature()); | |
| assertEquals(pckCert.toText(), streamCert.toText()); | |
| assertEquals(pckCert.getCommands().size(), streamCert.getCommands().size()); | |
| ReceiveCommand pckCmd = pckCert.getCommands().get(0); | |
| ReceiveCommand streamCmd = streamCert.getCommands().get(0); | |
| assertEquals(pckCmd.getRefName(), streamCmd.getRefName()); | |
| assertEquals(pckCmd.getOldId(), streamCmd.getOldId()); | |
| assertEquals(pckCmd.getNewId().name(), streamCmd.getNewId().name()); | |
| } | |
| @Test | |
| public void testParseString() throws Exception { | |
| String str = concatPacketLines(INPUT, 0, 18); | |
| assertEquals( | |
| PushCertificateParser.fromReader(new StringReader(str)), | |
| PushCertificateParser.fromString(str)); | |
| } | |
| @Test | |
| public void testParseMultipleFromStream() throws Exception { | |
| String input = concatPacketLines(INPUT, 0, 17); | |
| assertFalse(input.contains(PushCertificateParser.END_CERT)); | |
| input += input; | |
| Reader reader = new InputStreamReader( | |
| new ByteArrayInputStream(Constants.encode(input)), UTF_8); | |
| assertNotNull(PushCertificateParser.fromReader(reader)); | |
| assertNotNull(PushCertificateParser.fromReader(reader)); | |
| assertEquals(-1, reader.read()); | |
| assertNull(PushCertificateParser.fromReader(reader)); | |
| } | |
| @Test | |
| public void testMissingPusheeField() throws Exception { | |
| // Omit pushee line from existing cert. (This means the signature would not | |
| // match, but we're not verifying it here.) | |
| String input = INPUT.replace("0024pushee git://localhost/repo.git\n", ""); | |
| assertFalse(input.contains(PushCertificateParser.PUSHEE)); | |
| PacketLineIn pckIn = newPacketLineIn(input); | |
| PushCertificateParser parser = | |
| new PushCertificateParser(db, newEnabledConfig()); | |
| parser.receiveHeader(pckIn, false); | |
| parser.addCommand(pckIn.readString()); | |
| assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString()); | |
| parser.receiveSignature(pckIn); | |
| PushCertificate cert = parser.build(); | |
| assertEquals("0.1", cert.getVersion()); | |
| assertNull(cert.getPushee()); | |
| assertFalse(cert.toText().contains(PushCertificateParser.PUSHEE)); | |
| } | |
| @Test | |
| public void testWithPushOptions() throws Exception { | |
| // Same cert as INPUT, but with push-option lines added after nonce | |
| String inputWithOptions = "001ccertificate version 0.1\n" | |
| + "0041pusher Dave Borowitz <dborowitz@google.com> 1433954361 -0700\n" | |
| + "0024pushee git://localhost/repo.git\n" | |
| + "002anonce 1433954361-bde756572d665bba81d8\n" | |
| + "0024push-option label=Code-Review+2\n" | |
| + "0021push-option label=Verified+1\n" | |
| + "0017push-option submit\n" | |
| + "0020push-option hashtag=bug fix\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(inputWithOptions); | |
| PushCertificateParser parser = | |
| new PushCertificateParser(db, newEnabledConfig()); | |
| parser.receiveHeader(pckIn, false); | |
| parser.addCommand(pckIn.readString()); | |
| assertEquals(PushCertificateParser.BEGIN_SIGNATURE, pckIn.readString()); | |
| parser.receiveSignature(pckIn); | |
| PushCertificate cert = parser.build(); | |
| assertNotNull(cert); | |
| assertEquals("0.1", cert.getVersion()); | |
| assertEquals("Dave Borowitz", cert.getPusherIdent().getName()); | |
| // Verify push options were parsed and are available on the certificate | |
| List<String> pushOptions = cert.getPushOptions(); | |
| assertEquals(4, pushOptions.size()); | |
| assertEquals("label=Code-Review+2", pushOptions.get(0)); | |
| assertEquals("label=Verified+1", pushOptions.get(1)); | |
| assertEquals("submit", pushOptions.get(2)); | |
| assertEquals("hashtag=bug fix", pushOptions.get(3)); | |
| // Verify that toText() includes push options (critical for signature verification) | |
| String text = cert.toText(); | |
| assertTrue("toText() must include push-option lines", | |
| text.contains("push-option label=Code-Review+2")); | |
| assertTrue("toText() must include push-option lines", | |
| text.contains("push-option label=Verified+1")); | |
| assertTrue("toText() must include push-option lines", | |
| text.contains("push-option submit")); | |
| assertTrue("toText() must include push-option lines", | |
| text.contains("push-option hashtag=bug fix")); | |
| // Verify push options appear after nonce and before empty line | |
| int nonceIndex = text.indexOf("nonce"); | |
| int pushOptionIndex = text.indexOf("push-option"); | |
| int emptyLineIndex = text.indexOf("\n\n"); | |
| assertTrue("push-option must appear after nonce", | |
| pushOptionIndex > nonceIndex); | |
| assertTrue("push-option must appear before empty line separator", | |
| pushOptionIndex < emptyLineIndex); | |
| } | |
| 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.readString(); | |
| } catch (EOFException e) { | |
| break; | |
| } | |
| if (++i > begin) { | |
| result.append(line).append('\n'); | |
| } | |
| } | |
| return result.toString(); | |
| } | |
| private static PacketLineIn newPacketLineIn(String input) { | |
| return new PacketLineIn(new ByteArrayInputStream(Constants.encode(input))); | |
| } | |
| } |