You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2018/09/06 16:03:41 UTC

[31/51] [abbrv] mina-sshd git commit: [SSHD-842] Split common utilities code from sshd-core into sshd-common (new artifact)

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
new file mode 100644
index 0000000..0ea58f7
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/BuiltinSignaturesTest.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.signature;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import org.apache.sshd.common.NamedResource;
+import org.apache.sshd.common.signature.BuiltinSignatures.ParseResult;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+import org.mockito.Mockito;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BuiltinSignaturesTest extends JUnitTestSupport {
+    public BuiltinSignaturesTest() {
+        super();
+    }
+
+    @Test
+    public void testFromName() {
+        for (BuiltinSignatures expected : BuiltinSignatures.VALUES) {
+            String name = expected.getName();
+            BuiltinSignatures actual = BuiltinSignatures.fromFactoryName(name);
+            assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testParseSignaturesList() {
+        List<String> builtin = NamedResource.getNameList(BuiltinSignatures.VALUES);
+        List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName());
+        Random rnd = new Random();
+        for (int index = 0; index < (builtin.size() + unknown.size()); index++) {
+            Collections.shuffle(builtin, rnd);
+            Collections.shuffle(unknown, rnd);
+
+            List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size());
+            for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) {
+                boolean useBuiltin = false;
+                if (bIndex < builtin.size()) {
+                    useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean();
+                }
+
+                if (useBuiltin) {
+                    weavedList.add(builtin.get(bIndex));
+                    bIndex++;
+                } else if (uIndex < unknown.size()) {
+                    weavedList.add(unknown.get(uIndex));
+                    uIndex++;
+                }
+            }
+
+            String fullList = GenericUtils.join(weavedList, ',');
+            ParseResult result = BuiltinSignatures.parseSignatureList(fullList);
+            List<String> parsed = NamedResource.getNameList(result.getParsedFactories());
+            List<String> missing = result.getUnsupportedFactories();
+
+            // makes sure not only that the contents are the same but also the order
+            assertListEquals(fullList + "[parsed]", builtin, parsed);
+            assertListEquals(fullList + "[unsupported]", unknown, missing);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnBuiltinValues() {
+        for (SignatureFactory expected : BuiltinSignatures.VALUES) {
+            String name = expected.getName();
+            SignatureFactory actual = BuiltinSignatures.resolveFactory(name);
+            assertSame(name, expected, actual);
+        }
+    }
+
+    @Test
+    public void testNotAllowedToRegisterBuiltinFactories() {
+        for (SignatureFactory expected : BuiltinSignatures.VALUES) {
+            try {
+                BuiltinSignatures.registerExtension(expected);
+                fail("Unexpected success for " + expected.getName());
+            } catch (IllegalArgumentException e) {
+                // expected - ignored
+            }
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testNotAllowedToOverrideRegisteredFactories() {
+        SignatureFactory expected = Mockito.mock(SignatureFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String name = expected.getName();
+        try {
+            for (int index = 1; index <= Byte.SIZE; index++) {
+                BuiltinSignatures.registerExtension(expected);
+                assertEquals("Unexpected success at attempt #" + index, 1, index);
+            }
+        } finally {
+            BuiltinSignatures.unregisterExtension(name);
+        }
+    }
+
+    @Test
+    public void testResolveFactoryOnRegisteredExtension() {
+        SignatureFactory expected = Mockito.mock(SignatureFactory.class);
+        Mockito.when(expected.getName()).thenReturn(getCurrentTestName());
+
+        String name = expected.getName();
+        try {
+            assertNull("Extension already registered", BuiltinSignatures.resolveFactory(name));
+            BuiltinSignatures.registerExtension(expected);
+
+            SignatureFactory actual = BuiltinSignatures.resolveFactory(name);
+            assertSame("Mismatched resolved instance", expected, actual);
+        } finally {
+            SignatureFactory actual = BuiltinSignatures.unregisterExtension(name);
+            assertSame("Mismatched unregistered instance", expected, actual);
+            assertNull("Extension not un-registered", BuiltinSignatures.resolveFactory(name));
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
new file mode 100644
index 0000000..d9f507d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureDSATest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.spec.DSAPublicKeySpec;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SignatureDSATest extends JUnitTestSupport {
+    public SignatureDSATest() {
+        super();
+    }
+
+    @Test
+    public void testTooShortSignature() throws Exception {
+        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.DSS_ALGORITHM);
+        SignatureDSA signatureDSA = new SignatureDSA(KeyUtils.DSS_ALGORITHM) {
+            @Override
+            protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+                return java.security.Signature.getInstance(algo);
+
+            }
+        };
+
+        byte[] y = new byte[] {
+            0, -92, 59, 5, 72, 124, 101, 124, -18, 114, 7, 100, 98, -61, 73, -104,
+            120, -98, 54, 118, 17, -62, 91, -110, 29, 98, 50, -101, -41, 99, -116,
+            101, 107, -123, 124, -97, 62, 119, 88, -109, -110, -1, 109, 119, -51,
+            69, -98, -105, 2, -69, -121, -82, -118, 23, -6, 96, -61, -65, 102, -58,
+            -74, 32, -104, 116, -6, -35, -83, -10, -88, -68, 106, -112, 72, -2, 35,
+            38, 15, -11, -22, 30, -114, -46, -47, -18, -17, -71, 24, -25, 28, 13, 29,
+            -40, 101, 18, 81, 45, -120, -67, -53, -41, 11, 50, -89, -33, 50, 54, -14,
+            -91, -35, 12, -42, 13, -84, -19, 100, -3, -85, -18, 74, 99, -49, 64, -49,
+            51, -83, -82, -127, 116, 64 };
+        byte[] p = new byte[] {
+            0, -3, 127, 83, -127, 29, 117, 18, 41, 82, -33, 74, -100, 46, -20, -28,
+            -25, -10, 17, -73, 82, 60, -17, 68, 0, -61, 30, 63, -128, -74, 81, 38,
+            105, 69, 93, 64, 34, 81, -5, 89, 61, -115, 88, -6, -65, -59, -11, -70,
+            48, -10, -53, -101, 85, 108, -41, -127, 59, -128, 29, 52, 111, -14, 102,
+            96, -73, 107, -103, 80, -91, -92, -97, -97, -24, 4, 123, 16, 34, -62, 79,
+            -69, -87, -41, -2, -73, -58, 27, -8, 59, 87, -25, -58, -88, -90, 21, 15, 4,
+            -5, -125, -10, -45, -59, 30, -61, 2, 53, 84, 19, 90, 22, -111, 50, -10, 117,
+            -13, -82, 43, 97, -41, 42, -17, -14, 34, 3, 25, -99, -47, 72, 1, -57 };
+        byte[] q = new byte[] {
+            0, -105, 96, 80, -113, 21, 35, 11, -52, -78, -110, -71, -126, -94, -21,
+            -124, 11, -16, 88, 28, -11 };
+        byte[] g = new byte[] {
+            0, -9, -31, -96, -123, -42, -101, 61, -34, -53, -68, -85, 92, 54, -72, 87,
+            -71, 121, -108, -81, -69, -6, 58, -22, -126, -7, 87, 76, 11, 61, 7, -126,
+            103, 81, 89, 87, -114, -70, -44, 89, 79, -26, 113, 7, 16, -127, -128, -76,
+            73, 22, 113, 35, -24, 76, 40, 22, 19, -73, -49, 9, 50, -116, -56, -90, -31,
+            60, 22, 122, -117, 84, 124, -115, 40, -32, -93, -82, 30, 43, -77, -90, 117,
+            -111, 110, -93, 127, 11, -6, 33, 53, 98, -15, -5, 98, 122, 1, 36, 59, -52,
+            -92, -15, -66, -88, 81, -112, -119, -88, -125, -33, -31, 90, -27, -97, 6,
+            -110, -117, 102, 94, -128, 123, 85, 37, 100, 1, 76, 59, -2, -49, 73, 42 };
+
+        BigInteger bigY = new BigInteger(y);
+        BigInteger bigP = new BigInteger(p);
+        BigInteger bigQ = new BigInteger(q);
+        BigInteger bigG = new BigInteger(g);
+
+        DSAPublicKeySpec dsaPublicKey = new DSAPublicKeySpec(bigY, bigP, bigQ, bigG);
+        signatureDSA.initVerifier(kf.generatePublic(dsaPublicKey));
+        byte[] h = new byte[] {
+            -4, 111, -103, 111, 72, -106, 105, -19, 81, -123, 84, -13, -40, -53, -3,
+            -97, -8, 43, -22, -2, -23, -15, 28, 116, -63, 96, -79, -127, -84, 63, -6, -94 };
+        signatureDSA.update(h);
+
+        byte[] sig_of_h = new byte[] {
+            0, 0, 0, 7, 115, 115, 104, 45, 100, 115, 115, 0, 0, 0, 40, 0, 79,
+            84, 118, -50, 11, -117, -112, 52, -25, -78, -50, -20, 6, -69, -26,
+            7, 90, -34, -124, 80, 76, -32, -23, -8, 43, 38, -48, -89, -17, -60,
+            -1, -78, 112, -88, 14, -39, -78, -98, -80 };
+        boolean verified = signatureDSA.verify(sig_of_h);
+
+        assertTrue(verified);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
new file mode 100644
index 0000000..5671ac3
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignatureRSATest.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.signature;
+
+import java.math.BigInteger;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.RSAPublicKeySpec;
+import java.util.Base64;
+import java.util.Map;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.util.security.SecurityUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.BeforeClass;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class SignatureRSATest extends JUnitTestSupport {
+    private static final Base64.Decoder B64_DECODER = Base64.getDecoder();
+    @SuppressWarnings("checkstyle:linelength")
+    private static final byte[] TEST_MSG =
+            B64_DECODER.decode("AAAAFPHgK1MeV9zNnok3pwNJhCd8SONqMgAAAAlidWlsZHVzZXIAAAAOc3NoLWNvbm5lY3Rpb24AAAAJcHVibGlja2V5AQAAAAdzc2gtcnNhAAABFQAAAAdzc2gtcnNhAAAAASMAAAEBAMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
+    @SuppressWarnings("checkstyle:linelength")
+    private static final byte[] TEST_SIGNATURE =
+            B64_DECODER.decode("AAAAB3NzaC1yc2EAAAD/+Ntnf4qfr2J1voDS6I+u3VRjtMn+LdWJsAZfkLDxRkK1rQxP7QAjLdNqpT4CkWHp8dtoTGFlBFt6NieNJCMTA2KSOxJMZKsX7e/lHkh7C+vhQvJ9eLTKWjCxSFUrcM0NvFhmwbRCffwXSHvAKak4wbmofxQMpd+G4jZkNMz5kGpmeICBcNjRLPb7oXzuGr/g4x/3ge5Qaawqrg/gcZr/sKN6SdE8SszgKYO0SB320N4gcUoShVdLYr9uwdJ+kJoobfkUK6Or171JCctP/cu2nM79lDqVnJw/2jOG8OnTc8zRDXAh0RKoR5rOU8cOHm0Ls2MATsFdnyRU5FGUxqZ+");
+    private static PublicKey testKey;
+
+    public SignatureRSATest() {
+        super();
+    }
+
+    @BeforeClass
+    public static void initializeTestKey() throws GeneralSecurityException {
+        byte[] exp = B64_DECODER.decode("Iw==");
+        @SuppressWarnings("checkstyle:linelength")
+        byte[] mod = B64_DECODER.decode("AMs9HO/NH/Now+6fSnESebaG4wzaYQWA1b/q1TGV1wHNtCg9fGFGVSKs0VxKF4cfVyrSLtgLjnlXQTn+Lm7xiYKGbBbsTQWOqEDaBVBsRbAkxIkpuvr6/EBxwrtDbKmSQYTJZVJSD2bZRYjGsR9gpZXPorOOKFd5EPCMHXsqnhp2hidTGH7cK6RuLk7MNnPISsY0Nbx8/ZvikiPROGcoTZ8bzUv4IaLr3veW6epSeQem8tJqhnrpTHhbLU99zf045M0Gsnk/azjjlBM+qrHZ5FNdC1kowJnLtf2Oy/rUQNpkGJtcBPT8xvreV0wLsn9t3hSxzsc0+VkDNTQRlfU+o3M=");
+        KeyFactory kf = SecurityUtils.getKeyFactory(KeyUtils.RSA_ALGORITHM);
+        testKey = kf.generatePublic(new RSAPublicKeySpec(new BigInteger(mod), new BigInteger(exp)));
+    }
+
+    @Test   // see SSHD-642
+    public void testLeadingZeroesBC() throws Throwable {
+        testLeadingZeroes(new Factory<SignatureRSA>() {
+            @Override
+            public SignatureRSA create() {
+                return new SignatureRSA() {
+                    @Override
+                    protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+                        assertFalse("Signature not initialized for verification", forSigning);
+                        java.security.Signature signature = super.doInitSignature(algo, forSigning);
+                        if (SecurityUtils.isBouncyCastleRegistered()) {
+                            Provider provider = signature.getProvider();
+                            String name = provider.getName();
+                            assertEquals("Mismatched BC provider name", SecurityUtils.BOUNCY_CASTLE, name);
+                        }
+                        return signature;
+                    }
+                };
+            }
+        });
+    }
+
+    @Test   // see SSHD-642
+    public void testLeadingZeroesJCE() throws Throwable {
+        testLeadingZeroes(() -> new SignatureRSA() {
+            @Override
+            protected java.security.Signature doInitSignature(String algo, boolean forSigning) throws GeneralSecurityException {
+                assertFalse("Signature not initialized for verification", forSigning);
+                java.security.Signature signature = java.security.Signature.getInstance(algo);
+                Provider provider = signature.getProvider();
+                String name = provider.getName();
+                assertNotEquals("BC provider used although not required", SecurityUtils.BOUNCY_CASTLE, name);
+                return signature;
+            }
+        });
+    }
+
+    private void testLeadingZeroes(Factory<? extends SignatureRSA> factory) throws Exception {
+        SignatureRSA rsa = factory.create();
+        rsa.initVerifier(testKey);
+
+        int vSize = rsa.getVerifierSignatureSize();
+        assertTrue("Verifier signature size not initialized", vSize > 0);
+
+        // make sure padding is required
+        Map.Entry<String, byte[]> encoding = rsa.extractEncodedSignature(TEST_SIGNATURE);
+        assertNotNull("Signature is not encoded", encoding);
+        byte[] data = encoding.getValue();
+        assertTrue("Signature data size (" + data.length + ") not below verifier size (" + vSize + ")", data.length < vSize);
+
+        rsa.update(TEST_MSG);
+        assertTrue("Failed to verify", rsa.verify(TEST_SIGNATURE));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
new file mode 100644
index 0000000..2e15341
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/signature/SignaturesDevelopment.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.signature;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.common.util.security.eddsa.EdDSASecurityProviderUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.Ignore;
+
+/**
+ * A &quot;scratch-pad&quot; class for testing signatures related code during development
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@Ignore("Used only for development")
+public class SignaturesDevelopment extends JUnitTestSupport {
+    public SignaturesDevelopment() {
+        super();
+    }
+
+    public static void testSignatureFactory(
+            SignatureFactory factory, KeyPair kp, byte[] data, boolean generateSignature, byte[] signature)
+                throws Exception {
+        Signature signer = factory.create();
+        if (generateSignature) {
+            signer.initSigner(kp.getPrivate());
+            signer.update(data);
+            signature = signer.sign();
+            System.out.append('\t').append("Signature: ").println(BufferUtils.toHex(':', signature));
+        } else {
+            signer.initVerifier(kp.getPublic());
+            signer.update(data);
+            if (signer.verify(signature)) {
+                System.out.append('\t').println("Valid signature");
+            } else {
+                System.err.append('\t').println("Invalid signature");
+            }
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////////////
+
+    // args[0]=signatureName, args[1]=publicKey, args[2]=privateKey, args[3]=sign/verify, args[4]=data, args[5]=signature(if verify required)
+    public static void main(String[] args) throws Exception {
+        SignatureFactory factory = BuiltinSignatures.resolveFactory(args[0]);
+        // TODO recover public/private keys according to factory name
+        byte[] publicKey = BufferUtils.decodeHex(':', args[1]);
+        PublicKey pubKey = EdDSASecurityProviderUtils.generateEDDSAPublicKey(publicKey);
+        byte[] privateKey = BufferUtils.decodeHex(':', args[2]);
+        PrivateKey prvKey = EdDSASecurityProviderUtils.generateEDDSAPrivateKey(privateKey);
+        String op = args[3];
+        byte[] data = BufferUtils.decodeHex(':', args[4]);
+        byte[] signature = GenericUtils.EMPTY_BYTE_ARRAY;
+        if ("verify".equalsIgnoreCase(op)) {
+            signature = BufferUtils.decodeHex(':', args[5]);
+        }
+
+        testSignatureFactory(factory, new KeyPair(pubKey, prvKey), data, "sign".equalsIgnoreCase(op), signature);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
new file mode 100644
index 0000000..1619cd3
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/EventListenerUtilsTest.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.EventListener;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class EventListenerUtilsTest extends JUnitTestSupport {
+    public EventListenerUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testProxyWrapper() {
+        List<ProxyListenerImpl> impls = new ArrayList<>();
+        for (int index = 0; index < Byte.SIZE; index++) {
+            impls.add(new ProxyListenerImpl());
+        }
+
+        ProxyListener listener = EventListenerUtils.proxyWrapper(ProxyListener.class, impls);
+        String expStr = getCurrentTestName();
+        Number expNum = System.currentTimeMillis();
+        listener.callMeWithString(expStr);
+        listener.callMeWithNumber(expNum);
+
+        for (int index = 0; index < impls.size(); index++) {
+            ProxyListenerImpl l = impls.get(index);
+            assertSame("Mismatched string at listener #" + index, expStr, l.getStringValue());
+            assertSame("Mismatched number at listener #" + index, expNum, l.getNumberValue());
+        }
+    }
+
+    @Test
+    public void testListenerInstanceComparatorOnProxy() {
+        Comparator<? super EventListener> comparator = EventListenerUtils.LISTENER_INSTANCE_COMPARATOR;
+        ProxyListener p1 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+        assertEquals("Mismatched self reference comparison", 0, comparator.compare(p1, p1));
+
+        EventListener l = new EventListener() { /* nothing extra */ };
+        assertEquals("Mismatched proxy vs. non-proxy result", 1, Integer.signum(comparator.compare(p1, l)));
+        assertEquals("Mismatched non-proxy vs. proxy result", -1, Integer.signum(comparator.compare(l, p1)));
+
+        ProxyListener p2 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+        int p1vsp2 = Integer.signum(comparator.compare(p1, p2));
+        assertNotEquals("Mismatched p1 vs. p2 comparison", 0, p1vsp2);
+        assertEquals("Mismatched p2 vs. p1 comparison result", 0 - p1vsp2, Integer.signum(comparator.compare(p2, p1)));
+    }
+
+    @Test
+    public void testListenerInstanceComparatorOnNonProxy() {
+        Comparator<? super EventListener> comparator = EventListenerUtils.LISTENER_INSTANCE_COMPARATOR;
+        EventListener l1 = new EventListener() { /* nothing extra */ };
+        assertEquals("Mismatched self reference comparison", 0, comparator.compare(l1, l1));
+
+        EventListener l2 = new EventListener() { /* nothing extra */ };
+        int l1vsl2 = Integer.signum(comparator.compare(l1, l2));
+        assertNotEquals("Mismatched l1 vs. l2 comparison result", 0, l1vsl2);
+        assertEquals("Mismatched l2 vs. l1 comparison result", 0 - l1vsl2, Integer.signum(comparator.compare(l2, l1)));
+    }
+
+    @Test
+    public void testSynchronizedListenersSetOnProxies() {
+        ProxyListener p1 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+        Set<ProxyListener> s = EventListenerUtils.synchronizedListenersSet();
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            boolean modified = s.add(p1);
+            assertEquals("Mismatched p1 modification indicator at attempt #" + index, index == 1, modified);
+            assertEquals("Mismatched p1 set size at attempt #" + index, 1, s.size());
+        }
+
+        ProxyListener p2 = EventListenerUtils.proxyWrapper(ProxyListener.class, Collections.singletonList(new ProxyListenerImpl()));
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            boolean modified = s.add(p2);
+            assertEquals("Mismatched p2 modification indicator at attempt #" + index, index == 1, modified);
+            assertEquals("Mismatched p2 set size at attempt #" + index, 2, s.size());
+        }
+
+        assertTrue("Failed to remove p1", s.remove(p1));
+        assertEquals("Mismatched post p1-remove size", 1, s.size());
+        assertTrue("Failed to remove p2", s.remove(p2));
+        assertEquals("Mismatched post p2-remove size", 0, s.size());
+    }
+
+    interface ProxyListener extends SshdEventListener {
+        void callMeWithString(String s);
+
+        void callMeWithNumber(Number n);
+    }
+
+    static class ProxyListenerImpl implements ProxyListener {
+        private String strValue;
+        private Number numValue;
+
+        ProxyListenerImpl() {
+            super();
+        }
+
+        public String getStringValue() {
+            return strValue;
+        }
+
+        @Override
+        public void callMeWithString(String s) {
+            strValue = s;
+        }
+
+        public Number getNumberValue() {
+            return numValue;
+        }
+
+        @Override
+        public void callMeWithNumber(Number n) {
+            numValue = n;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
new file mode 100644
index 0000000..5727e88
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/GenericUtilsTest.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category(NoIoTestCase.class)
+public class GenericUtilsTest extends JUnitTestSupport {
+    public GenericUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testSplitAndJoin() {
+        List<String> expected = Collections.unmodifiableList(
+                Arrays.asList(getClass().getPackage().getName().replace('.', '/'), getClass().getSimpleName(), getCurrentTestName()));
+
+        // NOTE: we also test characters that have meaning in String.split(...) as regex ones
+        for (char ch : new char[]{',', '.', '*', '?'}) {
+            String sep = String.valueOf(ch);
+            String s = GenericUtils.join(expected, sep);
+            String[] actual = GenericUtils.split(s, ch);
+            assertEquals("Mismatched split length for separator=" + sep, expected.size(), GenericUtils.length((Object[]) actual));
+
+            for (int index = 0; index < actual.length; index++) {
+                String e = expected.get(index);
+                String a = actual[index];
+                if (!e.endsWith(a)) {
+                    fail("Mismatched value at index=" + index + " for separator=" + sep + ": expected=" + e + ", actual=" + a);
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testStripQuotes() {
+        String expected = getCurrentTestName();
+        assertSame("Unexpected un-quoted stripping", expected, GenericUtils.stripQuotes(expected));
+
+        StringBuilder sb = new StringBuilder(2 + expected.length()).append('|').append(expected).append('|');
+        for (int index = 0; index < GenericUtils.QUOTES.length(); index++) {
+            char delim = GenericUtils.QUOTES.charAt(index);
+            sb.setCharAt(0, delim);
+            sb.setCharAt(sb.length() - 1, delim);
+
+            CharSequence actual = GenericUtils.stripQuotes(sb);
+            assertEquals("Mismatched result for delim (" + delim + ")", expected, actual.toString());
+        }
+    }
+
+    @Test
+    public void testStripOnlyFirstLayerQuotes() {
+        StringBuilder sb = new StringBuilder().append("||").append(getCurrentTestName()).append("||");
+        char[] delims = {'\'', '"', '"', '\''};
+        for (int index = 0; index < delims.length; index += 2) {
+            char topDelim = delims[index];
+            char innerDelim = delims[index + 1];
+            sb.setCharAt(0, topDelim);
+            sb.setCharAt(1, innerDelim);
+            sb.setCharAt(sb.length() - 2, innerDelim);
+            sb.setCharAt(sb.length() - 1, topDelim);
+
+            CharSequence expected = sb.subSequence(1, sb.length() - 1);
+            CharSequence actual = GenericUtils.stripQuotes(sb);
+            assertEquals("Mismatched result for delim (" + topDelim + "/" + innerDelim + ")", expected.toString(), actual.toString());
+        }
+    }
+
+    @Test
+    public void testStripDelimiters() {
+        String expected = getCurrentTestName();
+        final char delim = '|';
+        assertSame("Unexpected un-delimited stripping", expected, GenericUtils.stripDelimiters(expected, delim));
+
+        CharSequence actual = GenericUtils.stripDelimiters(
+                new StringBuilder(2 + expected.length()).append(delim).append(expected).append(delim), delim);
+        assertEquals("Mismatched stripped values", expected, actual.toString());
+    }
+
+    @Test
+    public void testStripDelimitersOnlyIfOnBothEnds() {
+        final char delim = '$';
+        StringBuilder expected = new StringBuilder().append(delim).append(getCurrentTestName()).append(delim);
+        for (int index : new int[]{0, expected.length() - 1}) {
+            // restore original delimiters
+            expected.setCharAt(0, delim);
+            expected.setCharAt(expected.length() - 1, delim);
+            // trash one end
+            expected.setCharAt(index, (char) (delim + 1));
+
+            assertSame("Mismatched result for delim at index=" + index, expected, GenericUtils.stripDelimiters(expected, delim));
+        }
+    }
+
+    @Test
+    public void testAccumulateExceptionOnNullValues() {
+        assertNull("Unexpected null/null result", GenericUtils.accumulateException(null, null));
+
+        Throwable expected = new NoSuchMethodException(getClass().getName() + "#" + getCurrentTestName());
+        assertSame("Mismatched null/extra result", expected, GenericUtils.accumulateException(null, expected));
+        assertSame("Mismatched current/null result", expected, GenericUtils.accumulateException(expected, null));
+    }
+
+    @Test
+    public void testAccumulateExceptionOnExistingCurrent() {
+        RuntimeException[] expected = new RuntimeException[]{
+            new IllegalArgumentException(getCurrentTestName()),
+            new ClassCastException(getClass().getName()),
+            new NoSuchElementException(getClass().getPackage().getName())
+        };
+        RuntimeException current = new UnsupportedOperationException("top");
+        for (RuntimeException extra : expected) {
+            RuntimeException actual = GenericUtils.accumulateException(current, extra);
+            assertSame("Mismatched returned actual exception", current, actual);
+        }
+
+        Throwable[] actual = current.getSuppressed();
+        assertArrayEquals("Suppressed", expected, actual);
+    }
+
+    @Test
+    public void testNullOrEmptyCharArrayComparison() {
+        char[][] values = new char[][]{null, GenericUtils.EMPTY_CHAR_ARRAY};
+        for (char[] c1 : values) {
+            for (char[] c2 : values) {
+                assertEquals(((c1 == null) ? "null" : "empty") + " vs. " + ((c2 == null) ? "null" : "empty"), 0, GenericUtils.compare(c1, c2));
+            }
+        }
+    }
+
+    @Test
+    public void testCharArrayComparison() {
+        String s1 = getClass().getSimpleName();
+        char[] c1 = s1.toCharArray();
+        assertEquals("Same value equality", 0, GenericUtils.compare(c1, s1.toCharArray()));
+
+        String s2 = getCurrentTestName();
+        char[] c2 = s2.toCharArray();
+        assertEquals("s1 vs. s2", Integer.signum(s1.compareTo(s2)), Integer.signum(GenericUtils.compare(c1, c2)));
+        assertEquals("s2 vs. s1", Integer.signum(s2.compareTo(s1)), Integer.signum(GenericUtils.compare(c2, c1)));
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
new file mode 100644
index 0000000..b0c48ca
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/Int2IntFunctionTest.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Random;
+import java.util.function.IntUnaryOperator;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class Int2IntFunctionTest extends JUnitTestSupport {
+    public Int2IntFunctionTest() {
+        super();
+    }
+
+    @Test
+    public void testAdd() {
+        int factor = Byte.SIZE;
+        IntUnaryOperator func = Int2IntFunction.add(factor);
+        for (int index = 1, sum = 0; index <= Byte.SIZE; index++) {
+            sum = func.applyAsInt(sum);
+            assertEquals(factor * index, sum);
+        }
+    }
+
+    @Test
+    public void testAddIdentity() {
+        IntUnaryOperator func = Int2IntFunction.add(0);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int expected = rnd.nextInt();
+            int actual = func.applyAsInt(expected);
+            assertEquals(expected, actual);
+        }
+    }
+
+    @Test
+    public void testSub() {
+        int factor = Byte.SIZE;
+        IntUnaryOperator func = Int2IntFunction.sub(factor);
+        for (int index = 1, sum = 0; index <= Byte.SIZE; index++) {
+            sum = func.applyAsInt(sum);
+            assertEquals(factor * index * -1, sum);
+        }
+    }
+
+    @Test
+    public void testSubIdentity() {
+        IntUnaryOperator func = Int2IntFunction.sub(0);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int expected = rnd.nextInt();
+            int actual = func.applyAsInt(expected);
+            assertEquals(expected, actual);
+        }
+    }
+
+    @Test
+    public void testMul() {
+        int factor = 2;
+        IntUnaryOperator func = Int2IntFunction.mul(factor);
+        for (int index = 1, mul = 1, expected = factor; index <= Byte.SIZE; index++, expected *= factor) {
+            mul = func.applyAsInt(mul);
+            assertEquals(expected, mul);
+        }
+    }
+
+    @Test
+    public void testMulIdentity() {
+        IntUnaryOperator func = Int2IntFunction.mul(1);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int expected = rnd.nextInt();
+            int actual = func.applyAsInt(expected);
+            assertEquals(expected, actual);
+        }
+    }
+
+    @Test
+    public void testMulZero() {
+        IntUnaryOperator func = Int2IntFunction.mul(0);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int value = rnd.nextInt();
+            int actual = func.applyAsInt(value);
+            assertEquals(Integer.toString(value), 0, actual);
+        }
+    }
+
+    @Test
+    public void testConstant() {
+        int expected = 377347;
+        IntUnaryOperator func = Int2IntFunction.constant(expected);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int value = rnd.nextInt();
+            int actual = func.applyAsInt(value);
+            assertEquals(Integer.toString(value), expected, actual);
+        }
+    }
+
+    @Test
+    public void testDiv() {
+        int factor = 2;
+        IntUnaryOperator func = Int2IntFunction.div(factor);
+        for (int index = 1, quot = 65536, expected = quot / factor; index <= Byte.SIZE; index++, expected /= factor) {
+            quot = func.applyAsInt(quot);
+            assertEquals(expected, quot);
+        }
+    }
+
+    @Test
+    public void testDivIdentity() {
+        IntUnaryOperator func = Int2IntFunction.div(1);
+        Random rnd = new Random(System.nanoTime());
+        for (int index = 1; index <= Byte.SIZE; index++) {
+            int expected = rnd.nextInt();
+            int actual = func.applyAsInt(expected);
+            assertEquals(expected, actual);
+        }
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testDivZeroFactor() {
+        IntUnaryOperator func = Int2IntFunction.div(0);
+        fail("Unexpected success: " + func);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
new file mode 100644
index 0000000..927cecb
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class NumberUtilsTest extends JUnitTestSupport {
+    public NumberUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testPowersOf2List() {
+        assertEquals("Mismatched values size for " + NumberUtils.POWERS_OF_TWO, Long.SIZE, GenericUtils.size(NumberUtils.POWERS_OF_TWO));
+        long expected = 1L;
+        for (int index = 0; index < Long.SIZE; index++, expected <<= 1) {
+            Long actual = NumberUtils.POWERS_OF_TWO.get(index);
+            assertEquals("Mismatched value at index=" + index, Long.toHexString(expected), Long.toHexString(actual));
+        }
+    }
+
+    @Test
+    public void testNextPowerOf2() {
+        for (Long v : NumberUtils.POWERS_OF_TWO) {
+            long expected = v;
+            if (expected > 2L) {
+                assertEquals("Mismatched lower bound value", expected, NumberUtils.getNextPowerOf2(expected - 1L));
+            }
+
+            if (expected > 0L) {    // avoid the negative value
+                assertEquals("Mismatched exact value", expected, NumberUtils.getNextPowerOf2(expected));
+            }
+        }
+    }
+
+    @Test
+    public void testToInteger() {
+        assertNull("Unexpected null value", NumberUtils.toInteger(null));
+        for (Number n : new Number[]{
+                Byte.valueOf(Byte.MAX_VALUE), Short.valueOf(Short.MIN_VALUE),
+                Integer.valueOf(Short.MAX_VALUE), Long.valueOf(82007160L)}) {
+            Integer i = NumberUtils.toInteger(n);
+            if (n instanceof Integer) {
+                assertSame("Unexpected conversion", n, i);
+            } else {
+                assertEquals("Mismatched values", n.intValue(), i.intValue());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
new file mode 100644
index 0000000..af78a11
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/OsUtilsTest.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Objects;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class OsUtilsTest extends JUnitTestSupport {
+    public OsUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testSetOsTypeByProperty() {
+        try {
+            for (String osType : new String[]{"Some-Windows", "Some-Linux"}) {
+                OsUtils.setWin32(null); // force re-detection
+
+                try {
+                    boolean expected = osType.contains("Windows");
+                    System.setProperty(OsUtils.OS_TYPE_OVERRIDE_PROP, osType);
+                    boolean actual = OsUtils.isWin32();
+                    assertEquals(osType, expected, actual);
+                } finally {
+                    System.clearProperty(OsUtils.OS_TYPE_OVERRIDE_PROP);
+                }
+            }
+        } finally {
+            OsUtils.setWin32(null); // force re-detection
+        }
+    }
+
+    @Test
+    public void testSetOsTypeProgrammatically() {
+        try {
+            for (boolean expected : new boolean[]{true, false}) {
+                OsUtils.setWin32(expected); // force value
+                assertEquals("Mismatched detection value", expected, OsUtils.isWin32());
+            }
+        } finally {
+            OsUtils.setWin32(null); // force re-detection
+        }
+    }
+
+    @Test
+    public void testSetCurrentUserByProperty() {
+        try {
+            for (String expected : new String[]{getClass().getSimpleName(), getCurrentTestName()}) {
+                OsUtils.setCurrentUser(null); // force re-detection
+
+                try {
+                    System.setProperty(OsUtils.CURRENT_USER_OVERRIDE_PROP, expected);
+                    String actual = OsUtils.getCurrentUser();
+                    assertEquals("Mismatched reported current user", expected, actual);
+                } finally {
+                    System.clearProperty(OsUtils.CURRENT_USER_OVERRIDE_PROP);
+                }
+            }
+        } finally {
+            OsUtils.setCurrentUser(null); // force re-detection
+        }
+    }
+
+    @Test
+    public void testSetCurrentUserProgrammatically() {
+        try {
+            for (String expected : new String[]{getClass().getSimpleName(), getCurrentTestName()}) {
+                OsUtils.setCurrentUser(expected); // force value
+                assertEquals("Mismatched detection value", expected, OsUtils.getCurrentUser());
+            }
+        } finally {
+            OsUtils.setCurrentUser(null); // force re-detection
+        }
+    }
+
+    @Test
+    public void testSetJavaVersionByProperty() {
+        try {
+            for (String value : new String[]{"7.3.6_5", "37.77.34_7-" + getCurrentTestName()}) {
+                OsUtils.setJavaVersion(null); // force re-detection
+
+                try {
+                    System.setProperty(OsUtils.JAVA_VERSION_OVERRIDE_PROP, value);
+                    String expected = value.replace('_', '.');
+                    String actual = Objects.toString(OsUtils.getJavaVersion(), null);
+                    assertTrue("Mismatched reported version value: " + actual, expected.startsWith(actual));
+                } finally {
+                    System.clearProperty(OsUtils.JAVA_VERSION_OVERRIDE_PROP);
+                }
+            }
+        } finally {
+            OsUtils.setJavaVersion(null); // force re-detection
+        }
+    }
+
+    @Test
+    public void testSetJavaVersionProgrammatically() {
+        try {
+            for (VersionInfo expected : new VersionInfo[]{VersionInfo.parse("7.3.6.5"), VersionInfo.parse("37.77.34.7")}) {
+                OsUtils.setJavaVersion(expected); // force value
+                assertEquals("Mismatched detection value", expected, OsUtils.getJavaVersion());
+            }
+        } finally {
+            OsUtils.setJavaVersion(null); // force re-detection
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
new file mode 100644
index 0000000..3044699
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/SelectorUtilsTest.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util;
+
+import java.io.File;
+import java.util.Random;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.Assume;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class SelectorUtilsTest extends JUnitTestSupport {
+    public SelectorUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testApplyLinuxSeparatorSlashifyRules() {
+        testApplySlashifyRules('/');
+    }
+
+    @Test
+    public void testApplyWindowsSeparatorSlashifyRules() {
+        testApplySlashifyRules('\\');
+    }
+
+    private void testApplySlashifyRules(char slash) {
+        for (String expected : new String[]{
+            null, "", getCurrentTestName(),
+            getClass().getSimpleName() + Character.toString(slash) + getCurrentTestName(),
+            Character.toString(slash)  + getClass().getSimpleName(),
+            Character.toString(slash)  + getClass().getSimpleName() + Character.toString(slash)  + getCurrentTestName()
+        }) {
+            String actual = SelectorUtils.applySlashifyRules(expected, slash);
+            assertSame("Mismatched results for '" + expected + "'", expected, actual);
+        }
+
+        String[] comps = {getClass().getSimpleName(),  getCurrentTestName()};
+        Random rnd = new Random(System.nanoTime());
+        StringBuilder sb = new StringBuilder(Byte.MAX_VALUE);
+        for (int index = 0; index < Long.SIZE; index++) {
+            if (sb.length() > 0) {
+                sb.setLength(0);        // start from scratch
+            }
+
+            boolean prepend = rnd.nextBoolean();
+            if (prepend) {
+                slashify(sb, rnd, slash);
+            }
+
+            sb.append(comps[0]);
+            for (int j = 1; j < comps.length; j++) {
+                slashify(sb, rnd, slash);
+                sb.append(comps[j]);
+            }
+
+            boolean append = rnd.nextBoolean();
+            if (append) {
+                slashify(sb, rnd, slash);
+            }
+
+            String path = sb.toString();
+            sb.setLength(0);
+            if (prepend) {
+                sb.append(slash);
+            }
+
+            sb.append(comps[0]);
+            for (int j = 1; j < comps.length; j++) {
+                sb.append(slash).append(comps[j]);
+            }
+
+            if (append) {
+                sb.append(slash).append('.');
+            }
+
+            String expected = sb.toString();
+            String actual = SelectorUtils.applySlashifyRules(path, slash);
+            assertEquals("Mismatched results for path=" + path, expected, actual);
+        }
+    }
+
+    private static int slashify(StringBuilder sb, Random rnd, char slash) {
+        int slashes = 1 /* at least one slash */ + rnd.nextInt(Byte.SIZE);
+        for (int k = 0; k < slashes; k++) {
+            sb.append(slash);
+        }
+
+        return slashes;
+    }
+
+    @Test
+    public void testTranslateToFileSystemPath() {
+        String path = getClass().getPackage().getName().replace('.', File.separatorChar)
+                    + File.separator + getClass().getSimpleName()
+                    + File.separator + getCurrentTestName();
+        for (String expected : new String[] {null, "", path}) {
+            String actual = SelectorUtils.translateToFileSystemPath(expected, File.separator, File.separator);
+            assertSame("Mismatched instance for translated result", expected, actual);
+        }
+
+        for (String fsSeparator : new String[] {String.valueOf('.'), "##"}) {
+            String expected = path.replace(File.separator, fsSeparator);
+            String actual = SelectorUtils.translateToFileSystemPath(path, File.separator, fsSeparator);
+            assertEquals("Mismatched translation result for separator='" + fsSeparator + "'", expected, actual);
+
+            actual = SelectorUtils.translateToFileSystemPath(actual, fsSeparator, File.separator);
+            assertEquals("Mismatched translation revert for separator='" + fsSeparator + "'", path, actual);
+        }
+    }
+
+    @Test
+    public void testAbsoluteWindowsPathTranslation() {
+        Assume.assumeTrue("Not tested on Windows", OsUtils.isWin32());
+        String expected = detectTargetFolder().toString();
+        for (String prefix : new String[]{"", "/"}) {
+            String actual = SelectorUtils.translateToLocalPath(prefix + expected.replace('/', File.separatorChar));
+            assertEquals("Mismatched result for prefix='" + prefix + "'", expected, actual);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
new file mode 100644
index 0000000..10e6f5b
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/ThreadUtilsTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import java.util.Collection;
+
+import org.apache.sshd.common.util.threads.CloseableExecutorService;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class ThreadUtilsTest extends JUnitTestSupport {
+    public ThreadUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testProtectExecutorServiceShutdown() {
+        for (boolean shutdownOnExit : new boolean[]{true, false}) {
+            assertNull("Unexpected instance for shutdown=" + shutdownOnExit, ThreadUtils.protectExecutorServiceShutdown(null, shutdownOnExit));
+        }
+
+        CloseableExecutorService service = ThreadUtils.newSingleThreadExecutor("pool");
+        try {
+            assertSame("Unexpected wrapped instance", service, ThreadUtils.protectExecutorServiceShutdown(service, true));
+
+            CloseableExecutorService wrapped = ThreadUtils.protectExecutorServiceShutdown(service, false);
+            try {
+                assertNotSame("No wrapping occurred", service, wrapped);
+
+                wrapped.shutdown();
+                assertTrue("Wrapped service not shutdown", wrapped.isShutdown());
+                assertFalse("Protected service is shutdown", service.isShutdown());
+
+                Collection<?> running = wrapped.shutdownNow();
+                assertTrue("Non-empty runners list", running.isEmpty());
+                assertTrue("Wrapped service not shutdownNow", wrapped.isShutdown());
+                assertFalse("Protected service is shutdownNow", service.isShutdown());
+            } finally {
+                wrapped.shutdownNow();  // just in case
+            }
+        } finally {
+            service.shutdownNow();  // just in case...
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
new file mode 100644
index 0000000..9efd5f9
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/ValidateUtilsTest.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category(NoIoTestCase.class)
+public class ValidateUtilsTest extends JUnitTestSupport {
+    public ValidateUtilsTest() {
+        super();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void checkNotNull() {
+        ValidateUtils.checkNotNull(getClass(), getCurrentTestName());
+        ValidateUtils.checkNotNull(null, getCurrentTestName());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
new file mode 100644
index 0000000..834be24
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/VersionInfoTest.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class VersionInfoTest extends JUnitTestSupport {
+    public VersionInfoTest() {
+        super();
+    }
+
+    @Test
+    public void testLessThan4Components() {
+        VersionInfo expected = new VersionInfo(73, 65);
+        VersionInfo actual = VersionInfo.parse(NumberUtils.join('.', expected.getMajorVersion(), expected.getMinorVersion()));
+        assertEquals("Mismatched result", expected, actual);
+    }
+
+    @Test
+    public void testMoreThan4Components() {
+        VersionInfo expected = new VersionInfo(7, 3, 6, 5);
+        VersionInfo actual = VersionInfo.parse(expected.toString() + ".3.7.7.7.3.4.7");
+        assertEquals("Mismatched result", expected, actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
new file mode 100644
index 0000000..6f41b76
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.buffer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BufferTest extends JUnitTestSupport {
+    public BufferTest() {
+        super();
+    }
+
+    @Test
+    public void testGetLong() throws Exception {
+        long expected = 1234567890123456789L;
+
+        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+            try (DataOutputStream ds = new DataOutputStream(stream)) {
+                ds.writeLong(expected);
+            }
+
+            Buffer buffer = new ByteArrayBuffer(stream.toByteArray());
+            assertEquals("Mismatched recovered value", expected, buffer.getLong());
+        }
+    }
+
+    @Test
+    public void testPutCharsWithNullOrEmptyValue() {
+        Buffer buffer = new ByteArrayBuffer(Integer.SIZE);
+        for (char[] chars : new char[][]{null, GenericUtils.EMPTY_CHAR_ARRAY}) {
+            buffer.putChars(chars);
+
+            String value = buffer.getString();
+            assertEquals("Mismatched value for " + ((chars == null) ? "null" : "empty") + " characters", "", value);
+        }
+    }
+
+    @Test
+    public void testPutCharsOnNonEmptyValue() {
+        String expected = getCurrentTestName();
+        Buffer buffer = new ByteArrayBuffer(expected.length() + Byte.SIZE);
+        buffer.putChars(expected.toCharArray());
+
+        String actual = buffer.getString();
+        assertEquals("Mismatched recovered values", expected, actual);
+    }
+
+    @Test
+    public void testPutAndWipeChars() {
+        String expected = getCurrentTestName();
+        char[] chars = expected.toCharArray();
+        Buffer buffer = new ByteArrayBuffer(chars.length + Byte.SIZE);
+        buffer.putAndWipeChars(chars);
+
+        String actual = buffer.getString();
+        assertEquals("Mismatched recovered values", expected, actual);
+
+        for (int index = 0; index < chars.length; index++) {
+            assertEquals("Character not wiped at index=" + index, 0, chars[index]);
+        }
+    }
+
+    @Test
+    public void testPutAndWipeBytes() {
+        String expected = getCurrentTestName();
+        byte[] bytes = expected.getBytes(StandardCharsets.UTF_8);
+        Buffer buffer = new ByteArrayBuffer(bytes.length + Byte.SIZE);
+        buffer.putAndWipeBytes(bytes);
+        String actual = buffer.getString();
+        assertEquals("Mismatched recovered values", expected, actual);
+
+        for (int index = 0; index < bytes.length; index++) {
+            assertEquals("Value not wiped at index=" + index, 0, bytes[index]);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
new file mode 100644
index 0000000..78122b7
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Random;
+
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class BufferUtilsTest extends JUnitTestSupport {
+    public BufferUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testHexEncodeDecode() {
+        String expValue = getClass().getName() + "#" + getCurrentTestName();
+        byte[] expData = expValue.getBytes(StandardCharsets.UTF_8);
+        for (char sep : new char[]{BufferUtils.EMPTY_HEX_SEPARATOR, ':'}) {
+            String hexData = BufferUtils.toHex(sep, expData);
+            byte[] actData = BufferUtils.decodeHex(sep, hexData);
+            String actValue = new String(actData, StandardCharsets.UTF_8);
+            String sepName = (BufferUtils.EMPTY_HEX_SEPARATOR == sep) ? "EMPTY" : Character.toString(sep);
+            outputDebugMessage("Decode(sep=%s) expected=%s, actual=%s", sepName, expValue, actValue);
+            assertArrayEquals("Mismatched result for sep='" + sepName + "'", expData, actData);
+        }
+    }
+
+    @Test
+    public void testGetCompactClone() {
+        byte[] expected = getCurrentTestName().getBytes(StandardCharsets.UTF_8);
+        final int testOffset = Byte.SIZE / 2;
+        byte[] data = new byte[expected.length + 2 * testOffset];
+        Random rnd = new Random(System.nanoTime());
+        rnd.nextBytes(data);
+        System.arraycopy(expected, 0, data, testOffset, expected.length);
+
+        Buffer buf = ByteArrayBuffer.getCompactClone(data, testOffset, expected.length);
+        assertEquals("Mismatched cloned buffer read position", 0, buf.rpos());
+        assertEquals("Mismatched cloned buffer available size", expected.length, buf.available());
+
+        byte[] actual = buf.array();
+        assertNotSame("Original data not cloned", data, actual);
+        assertArrayEquals("Mismatched cloned contents", expected, actual);
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
new file mode 100644
index 0000000..6ed1187
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/closeable/CloseableUtilsTest.java
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.closeable;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.sshd.common.Closeable;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.DefaultCloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.util.threads.ThreadUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class CloseableUtilsTest extends JUnitTestSupport {
+    public CloseableUtilsTest() {
+        super();
+    }
+
+    @Test
+    public void testCloseImmediateNotCalledIfAlreadyClosed() throws IOException {
+        Closeable closeable = new IoBaseCloseable() {
+            @Override
+            public CloseFuture close(boolean immediately) {
+                fail("Unexpected call to close(" + immediately + ")");
+                return null;
+            }
+
+            @Override
+            public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to addCloseFutureListener");
+            }
+
+            @Override
+            public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to removeCloseFutureListener");
+            }
+
+            @Override
+            public boolean isClosed() {
+                return true;
+            }
+
+            @Override
+            public boolean isClosing() {
+                return false;
+            }
+        };
+        closeable.close();
+    }
+
+    @Test
+    public void testCloseImmediateNotCalledIfIsClosing() throws IOException {
+        Closeable closeable = new IoBaseCloseable() {
+            @Override
+            public CloseFuture close(boolean immediately) {
+                fail("Unexpected call to close(" + immediately + ")");
+                return null;
+            }
+
+            @Override
+            public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to addCloseFutureListener");
+            }
+
+            @Override
+            public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to removeCloseFutureListener");
+            }
+
+            @Override
+            public boolean isClosed() {
+                return false;
+            }
+
+            @Override
+            public boolean isClosing() {
+                return true;
+            }
+        };
+        closeable.close();
+    }
+
+    @Test
+    public void testCloseImmediateCalledAndWait() throws Exception {
+        DefaultCloseFuture future = new DefaultCloseFuture(this, this);
+        AtomicInteger callsCount = new AtomicInteger(0);
+        Closeable closeable = new IoBaseCloseable() {
+            @Override
+            public CloseFuture close(boolean immediately) {
+                assertTrue("Closure is not immediate", immediately);
+                assertEquals("Multiple close immediate calls", 1, callsCount.incrementAndGet());
+                return future;
+            }
+
+            @Override
+            public void addCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to addCloseFutureListener");
+            }
+
+            @Override
+            public void removeCloseFutureListener(SshFutureListener<CloseFuture> listener) {
+                fail("Unexpected call to removeCloseFutureListener");
+            }
+
+            @Override
+            public boolean isClosed() {
+                return false;
+            }
+
+            @Override
+            public boolean isClosing() {
+                return false;
+            }
+        };
+
+        ExecutorService service = ThreadUtils.newSingleThreadExecutor(getCurrentTestName());
+        try {
+            Future<?> task = service.submit((Runnable) () -> {
+                try {
+                    closeable.close();
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            });
+            future.setClosed();  // signal close complete
+            task.get(5L, TimeUnit.SECONDS);  // make sure #await call terminated
+            assertEquals("Close immediate not called", 1, callsCount.get());
+        } finally {
+            service.shutdownNow();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
----------------------------------------------------------------------
diff --git a/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java b/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
new file mode 100644
index 0000000..113758d
--- /dev/null
+++ b/sshd-common/src/test/java/org/apache/sshd/common/util/io/EmptyInputStreamTest.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.apache.sshd.util.test.NoIoTestCase;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.MethodSorters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Category({ NoIoTestCase.class })
+public class EmptyInputStreamTest extends JUnitTestSupport {
+    public EmptyInputStreamTest() {
+        super();
+    }
+
+    @Test
+    public void testEmptyInputStream() throws IOException {
+        try (EmptyInputStream in = new EmptyInputStream()) {
+            testEmptyInputStream(in, false);
+        }
+    }
+
+    @Test
+    public void testCloseableEmptyInputStream() throws IOException {
+        try (EmptyInputStream in = new CloseableEmptyInputStream()) {
+            testEmptyInputStream(in, true);
+        }
+    }
+
+    private void testEmptyInputStream(InputStream in, boolean failAfterClose) throws IOException {
+        testEmptyInputStream("open", in, false);
+        in.close();
+        testEmptyInputStream("closed", in, failAfterClose);
+    }
+
+    private void testEmptyInputStream(String message, InputStream in, boolean errorExpected) {
+        assertFalse(message + ": unexpected markSupported()", in.markSupported());
+        try {
+            in.mark(Long.SIZE);
+            fail(message + ": unexpected mark success");
+        } catch (UnsupportedOperationException e) {
+            // expected
+        }
+
+        try {
+            int len = in.available();
+            assertFalse(message + ": Unexpected success in available(): " + len, errorExpected);
+            assertEquals(message + ": Mismatched available() result", 0, len);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on available(): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            int data = in.read();
+            assertFalse(message + ": Unexpected success in read(): " + data, errorExpected);
+            assertEquals(message + ": Mismatched read() result", -1, data);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on read(): " + e.getMessage(), errorExpected);
+        }
+
+        byte[] bytes = new byte[Byte.SIZE];
+        try {
+            int len = in.read(bytes);
+            assertFalse(message + ": Unexpected success in read([]): " + BufferUtils.toHex(':', bytes), errorExpected);
+            assertEquals(message + ": Mismatched read([]) result", -1, len);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on read([]): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            int len = in.read(bytes, 0, bytes.length);
+            assertFalse(message + ": Unexpected success in read([],int,int): " + BufferUtils.toHex(':', bytes), errorExpected);
+            assertEquals(message + ": Mismatched read([],int,int) result", -1, len);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on read([],int,int): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            long len = in.skip(Byte.MAX_VALUE);
+            assertFalse(message + ": Unexpected success in skip(): " + len, errorExpected);
+            assertEquals(message + ": Mismatched skip() result", 0L, len);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on skip(): " + e.getMessage(), errorExpected);
+        }
+
+        try {
+            in.reset();
+            assertFalse(message + ": Unexpected success in reset()", errorExpected);
+        } catch (IOException e) {
+            assertTrue(message + ": Unexpected error on reset(): " + e.getMessage(), errorExpected);
+        }
+    }
+}