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 2015/11/22 06:21:18 UTC

[1/2] mina-sshd git commit: [SSHD-589] Auto-detect max. supported DH group exchange key size

Repository: mina-sshd
Updated Branches:
  refs/heads/master 652ad7de1 -> 82b8c9288


[SSHD-589] Auto-detect max. supported DH group exchange key size


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/d966cb68
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/d966cb68
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/d966cb68

Branch: refs/heads/master
Commit: d966cb68297d03e15ed249b9218a937c31e424ae
Parents: 652ad7d
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Sun Nov 22 07:20:49 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Sun Nov 22 07:20:49 2015 +0200

----------------------------------------------------------------------
 .../org/apache/sshd/client/kex/DHGClient.java   |  17 ++-
 .../org/apache/sshd/client/kex/DHGEXClient.java |  31 +++--
 .../org/apache/sshd/common/SshConstants.java    |  46 +++++--
 .../sshd/common/kex/BuiltinDHFactories.java     |   7 +-
 .../org/apache/sshd/common/kex/KeyExchange.java |  39 ++++++
 .../apache/sshd/common/util/GenericUtils.java   | 121 ++++++++++++----
 .../apache/sshd/common/util/NumberUtils.java    | 138 +++++++++++++++++++
 .../sshd/common/util/ReflectionUtils.java       |  47 +++++++
 .../apache/sshd/common/util/SecurityUtils.java  | 110 ++++++++++++++-
 .../sshd/common/util/logging/LoggingUtils.java  | 123 +++++++++++++++--
 .../org/apache/sshd/server/kex/DHGEXServer.java |  59 ++++----
 .../org/apache/sshd/server/kex/DHGServer.java   |  27 ++--
 .../java/org/apache/sshd/KeyReExchangeTest.java |   3 +
 .../org/apache/sshd/client/kex/KexTest.java     |   6 +
 .../apache/sshd/common/SshConstantsTest.java    |  20 ++-
 .../apache/sshd/common/kex/KeyExchangeTest.java |  72 ++++++++++
 .../sshd/common/util/SecurityUtilsTest.java     |   8 ++
 17 files changed, 765 insertions(+), 109 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
index 8e06415..95af92c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
@@ -80,7 +80,9 @@ public class DHGClient extends AbstractDHClientKeyExchange {
         hash.init();
         e = dh.getE();
 
-        log.debug("Send SSH_MSG_KEXDH_INIT");
+        if (log.isDebugEnabled()) {
+            log.debug("init({})[{}] Send SSH_MSG_KEXDH_INIT", this, s);
+        }
         Buffer buffer = s.createBuffer(SshConstants.SSH_MSG_KEXDH_INIT);
         buffer.putMPInt(e);
 
@@ -94,13 +96,15 @@ public class DHGClient extends AbstractDHClientKeyExchange {
     @Override
     public boolean next(Buffer buffer) throws Exception {
         int cmd = buffer.getUByte();
+        Session session = getSession();
+        if (log.isDebugEnabled()) {
+            log.debug("next({})[{}] process command={}", this, session, KeyExchange.Utils.getSimpleKexOpcodeName(cmd));
+        }
         if (cmd != SshConstants.SSH_MSG_KEXDH_REPLY) {
             throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
-                    "Protocol error: expected packet SSH_MSG_KEXDH_REPLY, got " + cmd);
+                    "Protocol error: expected packet SSH_MSG_KEXDH_REPLY, got " + KeyExchange.Utils.getSimpleKexOpcodeName(cmd));
         }
 
-        log.debug("Received SSH_MSG_KEXDH_REPLY");
-
         byte[] k_s = buffer.getBytes();
         f = buffer.getMPIntAsBytes();
         byte[] sig = buffer.getBytes();
@@ -126,7 +130,6 @@ public class DHGClient extends AbstractDHClientKeyExchange {
         hash.update(buffer.array(), 0, buffer.available());
         h = hash.digest();
 
-        Session session = getSession();
         FactoryManager manager = session.getFactoryManager();
         Signature verif = ValidateUtils.checkNotNull(NamedFactory.Utils.create(manager.getSignatureFactories(), keyAlg),
                 "No verifier located for algorithm=%s",
@@ -139,4 +142,8 @@ public class DHGClient extends AbstractDHClientKeyExchange {
         return true;
     }
 
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + factory.getName() + "]";
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
index 0ba3756..ac00fe3 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
@@ -34,6 +34,7 @@ import org.apache.sshd.common.session.AbstractSession;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.signature.Signature;
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.BufferUtils;
@@ -47,15 +48,17 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
 
     protected final DHFactory factory;
     protected byte expected;
-    protected int min = 1024;
-    protected int prf = 4096;
-    protected int max = 8192;
+    protected int min = SecurityUtils.MIN_DHGEX_KEY_SIZE;
+    protected int prf;
+    protected int max;
     protected AbstractDH dh;
     protected byte[] p;
     protected byte[] g;
 
     protected DHGEXClient(DHFactory factory) {
         this.factory = ValidateUtils.checkNotNull(factory, "No factory");
+        this.max = SecurityUtils.getMaxDHGroupExchangeKeySize();
+        this.prf = Math.min(SecurityUtils.PREFERRED_DHGEX_KEY_SIZE, max);
     }
 
     public static KeyExchangeFactory newFactory(final DHFactory delegate) {
@@ -95,13 +98,18 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
     @Override
     public boolean next(Buffer buffer) throws Exception {
         int cmd = buffer.getUByte();
+        Session session = getSession();
+        if (log.isDebugEnabled()) {
+            log.debug("next({})[{}] process command={}", this, session, KeyExchange.Utils.getGroupKexOpcodeName(cmd));
+        }
+
         if (cmd != expected) {
             throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
-                    "Protocol error: expected packet " + expected + ", got " + cmd);
+                    "Protocol error: expected packet " + KeyExchange.Utils.getGroupKexOpcodeName(expected)
+                  + ", got " + KeyExchange.Utils.getGroupKexOpcodeName(cmd));
         }
 
         if (cmd == SshConstants.SSH_MSG_KEX_DH_GEX_GROUP) {
-            log.debug("Received SSH_MSG_KEX_DH_GEX_GROUP");
             p = buffer.getMPIntAsBytes();
             g = buffer.getMPIntAsBytes();
 
@@ -110,8 +118,9 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
             hash.init();
             e = dh.getE();
 
-            log.debug("Send SSH_MSG_KEX_DH_GEX_INIT");
-            Session session = getSession();
+            if (log.isDebugEnabled()) {
+                log.debug("next({})[{}] Send SSH_MSG_KEX_DH_GEX_INIT", this, session);
+            }
             buffer = session.prepareBuffer(SshConstants.SSH_MSG_KEX_DH_GEX_INIT, BufferUtils.clear(buffer));
             buffer.putMPInt(e);
             session.writePacket(buffer);
@@ -120,7 +129,6 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
         }
 
         if (cmd == SshConstants.SSH_MSG_KEX_DH_GEX_REPLY) {
-            log.debug("Received SSH_MSG_KEX_DH_GEX_REPLY");
             byte[] k_s = buffer.getBytes();
             f = buffer.getMPIntAsBytes();
             byte[] sig = buffer.getBytes();
@@ -151,7 +159,6 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
             hash.update(buffer.array(), 0, buffer.available());
             h = hash.digest();
 
-            Session session = getSession();
             FactoryManager manager = session.getFactoryManager();
             Signature verif = ValidateUtils.checkNotNull(
                     NamedFactory.Utils.create(manager.getSignatureFactories(), keyAlg),
@@ -166,11 +173,15 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
             return true;
         }
 
-        throw new IllegalStateException("Unknown command value: " + cmd);
+        throw new IllegalStateException("Unknown command value: " + KeyExchange.Utils.getGroupKexOpcodeName(cmd));
     }
 
     protected AbstractDH getDH(BigInteger p, BigInteger g) throws Exception {
         return factory.create(p, g);
     }
 
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + factory.getName() + "]";
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java b/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
index a7beba6..a254d48 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/SshConstants.java
@@ -19,7 +19,6 @@
 package org.apache.sshd.common;
 
 import java.lang.reflect.Field;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Map;
@@ -91,16 +90,6 @@ public final class SshConstants {
     public static final byte SSH_MSG_CHANNEL_SUCCESS = 99;
     public static final byte SSH_MSG_CHANNEL_FAILURE = 100;
 
-    // Names of opcodes that have the same value
-    public static final Set<String> AMBIGUOUS_OPCODES =
-            Collections.unmodifiableSet(
-                    new HashSet<String>(
-                            Arrays.asList(
-                                "SSH_MSG_KEX_FIRST", "SSH_MSG_KEXDH_INIT", "SSH_MSG_KEX_DH_GEX_REQUEST_OLD",
-                                "SSH_MSG_KEXDH_REPLY", "SSH_MSG_KEX_DH_GEX_GROUP",
-                                "SSH_MSG_USERAUTH_INFO_REQUEST", "SSH_MSG_USERAUTH_PK_OK", "SSH_MSG_USERAUTH_PASSWD_CHANGEREQ"
-                                    )));
-
     //
     // Disconnect error codes
     //
@@ -134,14 +123,45 @@ public final class SshConstants {
         throw new UnsupportedOperationException("No instance allowed");
     }
 
-    private static class LazyMessagesMapHolder {
+    private static class LazyAmbiguousOpcodesHolder {
+        private static final Set<Integer> AMBIGUOUS_OPCODES =
+            Collections.unmodifiableSet(
+                new HashSet<Integer>(
+                        LoggingUtils.getAmbiguousMenmonics(SshConstants.class, "SSH_MSG_").values()));
+    }
 
+    /**
+     * @param cmd The command value
+     * @return {@code true} if this value is used by several <U>different</U> messages
+     * @see #getAmbiguousOpcodes()
+     */
+    public static boolean isAmbigouosOpcode(int cmd) {
+        return getAmbiguousOpcodes().contains(cmd);
+    }
+
+    /**
+     * @return A {@link Set} of opcodes that are used by several <U>different</U> messages
+     */
+    @SuppressWarnings("synthetic-access")
+    public static Set<Integer> getAmbiguousOpcodes() {
+        return LazyAmbiguousOpcodesHolder.AMBIGUOUS_OPCODES;
+    }
+
+    private static class LazyMessagesMapHolder {
         private static final Map<Integer, String> MESSAGES_MAP =
                 LoggingUtils.generateMnemonicMap(SshConstants.class, new Predicate<Field>() {
                     @Override
                     public boolean evaluate(Field f) {
                         String name = f.getName();
-                        return name.startsWith("SSH_MSG_") && (!AMBIGUOUS_OPCODES.contains(name));
+                        if (!name.startsWith("SSH_MSG_")) {
+                            return false;
+                        }
+
+                        try {
+                            return !isAmbigouosOpcode(f.getByte(null));
+                        } catch (Exception e) {
+                            return false;
+                        }
                     }
                 });
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
index a875ea8..b269714 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/BuiltinDHFactories.java
@@ -81,6 +81,11 @@ public enum BuiltinDHFactories implements DHFactory {
         public boolean isGroupExchange() {
             return true;
         }
+
+        @Override
+        public boolean isSupported() {  // avoid "Prime size must be multiple of 64, and can only range from 512 to 2048 (inclusive)"
+            return SecurityUtils.isDHGroupExchangeSupported();
+        }
     },
     dhgex256(Constants.DIFFIE_HELLMAN_GROUP_EXCHANGE_SHA256) {
         @Override
@@ -95,7 +100,7 @@ public enum BuiltinDHFactories implements DHFactory {
 
         @Override
         public boolean isSupported() {  // avoid "Prime size must be multiple of 64, and can only range from 512 to 2048 (inclusive)"
-            return SecurityUtils.isBouncyCastleRegistered();
+            return SecurityUtils.isDHGroupExchangeSupported();
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java
index 77dd6de..73c2c21 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java
@@ -19,10 +19,14 @@
 package org.apache.sshd.common.kex;
 
 import java.security.PublicKey;
+import java.util.Map;
 
+import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.digest.Digest;
 import org.apache.sshd.common.session.AbstractSession;
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.logging.LoggingUtils;
 
 /**
  * Key exchange algorithm.
@@ -79,4 +83,39 @@ public interface KeyExchange {
      * @return The server's {@link PublicKey}
      */
     PublicKey getServerKey();
+
+    /**
+     * A helper class for key exchange related operations
+     * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+     */
+    // CHECKSTYLE:OFF
+    final class Utils {
+    // CHECKSTYLE:ON
+        public static final Map<Integer, String> GROUP_KEX_OPCODES_MAP =
+                LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH_MSG_KEX_DH_GEX_");
+        public static final Map<Integer, String> SIMPLE_KEX_OPCODES_MAP =
+                LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH_MSG_KEXDH_");
+
+        private Utils() {
+            throw new UnsupportedOperationException("No instance allowed");
+        }
+
+        public static String getGroupKexOpcodeName(int cmd) {
+            String name = GROUP_KEX_OPCODES_MAP.get(cmd);
+            if (GenericUtils.isEmpty(name)) {
+                return SshConstants.getCommandMessageName(cmd);
+            } else {
+                return name;
+            }
+        }
+
+        public static String getSimpleKexOpcodeName(int cmd) {
+            String name = SIMPLE_KEX_OPCODES_MAP.get(cmd);
+            if (GenericUtils.isEmpty(name)) {
+                return SshConstants.getCommandMessageName(cmd);
+            } else {
+                return name;
+            }
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
index 6e55663..db8a930 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/GenericUtils.java
@@ -33,12 +33,16 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.SortedMap;
 import java.util.SortedSet;
+import java.util.TreeMap;
 import java.util.TreeSet;
 
 import javax.management.MBeanException;
 import javax.management.ReflectionException;
 
+import org.apache.sshd.common.Factory;
+
 /**
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
@@ -47,11 +51,6 @@ public final class GenericUtils {
     public static final byte[] EMPTY_BYTE_ARRAY = {};
     public static final String[] EMPTY_STRING_ARRAY = {};
     public static final Object[] EMPTY_OBJECT_ARRAY = {};
-    public static final List<Class<?>> NUMERIC_PRIMITIVE_CLASSES =
-            Collections.unmodifiableList(Arrays.<Class<?>>asList(
-                        Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE,
-                        Float.TYPE, Double.TYPE
-                    ));
 
     /**
      * The complement of {@link String#CASE_INSENSITIVE_ORDER}
@@ -79,6 +78,16 @@ public final class GenericUtils {
         }
     };
 
+    @SuppressWarnings("rawtypes")
+    private static final Factory CASE_INSENSITIVE_MAP_FACTORY = new Factory() {
+        @Override
+        @SuppressWarnings("unchecked")
+        public Object create() {
+            return new TreeMap(String.CASE_INSENSITIVE_ORDER);
+        }
+
+    };
+
     private GenericUtils() {
         throw new UnsupportedOperationException("No instance");
     }
@@ -225,6 +234,10 @@ public final class GenericUtils {
         return length(a) <= 0;
     }
 
+    public static boolean isEmpty(long[] a) {
+        return length(a) <= 0;
+    }
+
     public static int length(byte... a) {
         return a == null ? 0 : a.length;
     }
@@ -233,6 +246,10 @@ public final class GenericUtils {
         return a == null ? 0 : a.length;
     }
 
+    public static int length(long... a) {
+        return a == null ? 0 : a.length;
+    }
+
     @SafeVarargs
     public static <T> int length(T... a) {
         return a == null ? 0 : a.length;
@@ -346,6 +363,82 @@ public final class GenericUtils {
         return set;
     }
 
+    @SuppressWarnings("unchecked")
+    public static <V> Factory<SortedMap<String, V>> caseInsensitiveMap() {
+        return CASE_INSENSITIVE_MAP_FACTORY;
+    }
+
+    @SafeVarargs
+    public static <K, V> Map<K, V> mapValues(
+            Transformer<? super V, ? extends K> keyMapper, Factory<? extends Map<K, V>> mapCreator, V ... values) {
+        return mapValues(keyMapper, mapCreator, isEmpty(values) ? Collections.<V>emptyList() : Arrays.asList(values));
+    }
+
+    /**
+     * Creates a map out of a group of values
+     *
+     * @param keyMapper The {@link Transformer} that generates a key for a given value.
+     * If the returned key is {@code null} then the value is not mapped
+     * @param mapCreator The {@link Factory} used to create the result map - provided
+     * non-empty group of values
+     * @param values The values to be mapped
+     * @return The resulting {@link Map} - <B>Note:</B> no validation is made to ensure
+     * that 2 (or more) values are not mapped to the same key
+     */
+    public static <K, V> Map<K, V> mapValues(
+            Transformer<? super V, ? extends K> keyMapper, Factory<? extends Map<K, V>> mapCreator, Collection<? extends V> values) {
+        if (isEmpty(values)) {
+            return Collections.emptyMap();
+        }
+
+        Map<K, V> map = mapCreator.create();
+        for (V v : values) {
+            K k = keyMapper.transform(v);
+            if (k == null) {
+                continue;   // debug breakpoint
+            }
+            map.put(k, v);
+        }
+
+        return map;
+    }
+
+    /**
+     * Returns a list of all the values that were accepted by a predicate
+     *
+     * @param <T> The type of value being evaluated
+     * @param acceptor The {@link Predicate} to consult whether a member is selected
+     * @param values The values to be scanned
+     * @return A {@link List} of all the values that were accepted by the predicate
+     */
+    @SafeVarargs
+    public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, T ... values) {
+        return selectMatchingMembers(acceptor, isEmpty(values) ? Collections.<T>emptyList() : Arrays.asList(values));
+    }
+
+    /**
+     * Returns a list of all the values that were accepted by a predicate
+     *
+     * @param <T> The type of value being evaluated
+     * @param acceptor The {@link Predicate} to consult whether a member is selected
+     * @param values The values to be scanned
+     * @return A {@link List} of all the values that were accepted by the predicate
+     */
+    public static <T> List<T> selectMatchingMembers(Predicate<? super T> acceptor, Collection<? extends T> values) {
+        if (isEmpty(values)) {
+            return Collections.emptyList();
+        }
+
+        List<T> matches = new ArrayList<T>(values.size());
+        for (T v : values) {
+            if (acceptor.evaluate(v)) {
+                matches.add(v);
+            }
+        }
+
+        return matches;
+    }
+
     /**
      * @param s The {@link CharSequence} to be checked
      * @return If the sequence contains any of the {@link #QUOTES}
@@ -472,22 +565,4 @@ public final class GenericUtils {
         current.addSuppressed(extra);
         return current;
     }
-
-    // TODO in JDK-8 use Long.hashCode(long)
-    public static int hashCode(long value) {
-        return (int) (value ^ (value >>> 32));
-    }
-
-    public static boolean isNumericClass(Class<?> clazz) {
-        if (clazz == null) {
-            return false;
-        }
-
-        // turns out that the primitive types are not assignable to Number
-        if (Number.class.isAssignableFrom(clazz)) {
-            return true;
-        }
-
-        return NUMERIC_PRIMITIVE_CLASSES.indexOf(clazz) >= 0;
-    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
new file mode 100644
index 0000000..686762f
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/NumberUtils.java
@@ -0,0 +1,138 @@
+/*
+ * 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.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class NumberUtils {
+    /**
+     * A {@link List} of all the {@link Class} types used to represent the
+     * primitive numerical values
+     */
+    public static final List<Class<?>> NUMERIC_PRIMITIVE_CLASSES =
+            Collections.unmodifiableList(Arrays.<Class<?>>asList(
+                        Byte.TYPE, Short.TYPE, Integer.TYPE, Long.TYPE,
+                        Float.TYPE, Double.TYPE
+                    ));
+
+    /**
+     * A {@link List} containing all the pure powers of 2 for a {@code long}
+     * value. The value at index <I>n</I> is 2 to the power of <I>n</I>
+     */
+    public static final List<Long> POWERS_OF_TWO =
+            Collections.unmodifiableList(new ArrayList<Long>(Long.SIZE) {
+                private static final long serialVersionUID = 1L;    // we're not serializing it
+
+                {
+                    long value = 1L;
+                    for (int power = 0; power < Long.SIZE; power++, value <<= 1) {
+                        add(Long.valueOf(value));
+                    }
+                }
+            });
+
+    private NumberUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static boolean isPowerOf2(long value) {
+        for (Long l : POWERS_OF_TWO) {
+            if (value == l.longValue()) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static long getNextPowerOf2(long value) {
+        long j = 1L;
+        while (j < value) {
+            j <<= 1;
+        }
+        return j;
+    }
+
+    public static int getNextPowerOf2(int value) {
+        int j = 1;
+        while (j < value) {
+            j <<= 1;
+        }
+        return j;
+    }
+
+    public static int hashCode(long ... values) {
+        if (GenericUtils.length(values) <= 0) {
+            return 0;
+        }
+
+        int hash = hashCode(values[0]);
+        for (int index = 1; index < values.length; index++) {
+            hash += 31 * hash + hashCode(values[index]);
+        }
+
+        return hash;
+    }
+
+    // TODO in JDK-8 use Long.hashCode(long)
+    public static int hashCode(long value) {
+        return (int) (value ^ (value >>> 32));
+    }
+
+    /**
+     * @param clazz The {@link Class} to examine - ignored if {@code null}
+     * @return If the class is a {@link Number} or one of the primitive numerical types
+     * @see #NUMERIC_PRIMITIVE_CLASSES
+     */
+    public static boolean isNumericClass(Class<?> clazz) {
+        if (clazz == null) {
+            return false;
+        }
+
+        // turns out that the primitive types are not assignable to Number
+        if (Number.class.isAssignableFrom(clazz)) {
+            return true;
+        }
+
+        return NUMERIC_PRIMITIVE_CLASSES.indexOf(clazz) >= 0;
+    }
+
+    /**
+     * Converts a {@link Number} into an {@link Integer} if not already such
+     *
+     * @param n The {@link Number} - ignored if {@code null}
+     * @return The equivalent {@link Integer} value
+     */
+    public static Integer toInteger(Number n) {
+        if (n == null) {
+            return null;
+        } else if (n instanceof Integer) {
+            return (Integer) n;
+        } else {
+            return Integer.valueOf(n.intValue());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
new file mode 100644
index 0000000..089d6f6
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/ReflectionUtils.java
@@ -0,0 +1,47 @@
+/*
+ * 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.lang.reflect.Field;
+import java.util.Collection;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public final class ReflectionUtils {
+    public static final Transformer<Field, String> FIELD_NAME_EXTRACTOR = new Transformer<Field, String>() {
+        @Override
+        public String transform(Field f) {
+            return (f == null) ? null : f.getName();
+        }
+    };
+
+    private ReflectionUtils() {
+        throw new UnsupportedOperationException("No instance");
+    }
+
+    public static Collection<Field> getMatchingFields(Class<?> clazz, Predicate<? super Field> acceptor) {
+        return GenericUtils.selectMatchingMembers(acceptor, clazz.getFields());
+    }
+
+    public static Collection<Field> getMatchingDeclaredFields(Class<?> clazz, Predicate<? super Field> acceptor) {
+        return GenericUtils.selectMatchingMembers(acceptor, clazz.getDeclaredFields());
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
index 53152b6..21d874a 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/SecurityUtils.java
@@ -23,6 +23,7 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
+import java.math.BigInteger;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Path;
 import java.security.GeneralSecurityException;
@@ -34,9 +35,12 @@ import java.security.NoSuchProviderException;
 import java.security.SecureRandom;
 import java.security.Signature;
 import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicInteger;
+
 import javax.crypto.Cipher;
 import javax.crypto.KeyAgreement;
 import javax.crypto.Mac;
+import javax.crypto.spec.DHParameterSpec;
 
 import org.apache.sshd.common.config.keys.FilePasswordProvider;
 import org.apache.sshd.common.keyprovider.AbstractClassLoadableResourceKeyPairProvider;
@@ -64,9 +68,40 @@ import org.slf4j.LoggerFactory;
  * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
  */
 public final class SecurityUtils {
+    /**
+     * Bouncycastle JCE provider name
+     */
     public static final String BOUNCY_CASTLE = "BC";
 
-    private static final Logger LOG = LoggerFactory.getLogger(SecurityUtils.class);
+    /**
+     * System property used to configure the value for the maximum supported Diffie-Hellman
+     * Group Exchange key size. If not set, then an internal auto-discovery mechanism is employed.
+     * If set to negative value then Diffie-Hellman Group Exchange is disabled. If set to a
+     * negative value then Diffie-Hellman Group Exchange is disabled
+     */
+    public static final String MAX_DHGEX_KEY_SIZE_PROP = "org.apache.sshd.maxDHGexKeySize";
+
+    /**
+     * The min. key size value used for testing whether Diffie-Hellman Group Exchange
+     * is supported or not. According to <A HREF="https://tools.ietf.org/html/rfc4419">RFC 4419</A>
+     * section 3: &quot;Servers and clients SHOULD support groups with a modulus length of k
+     * bits, where 1024 <= k <= 8192&quot;.
+     * </code>
+     */
+    public static final int MIN_DHGEX_KEY_SIZE = 1024;
+    // Keys of size > 1024 are not support by default with JCE
+    public static final int DEFAULT_DHGEX_KEY_SIZE = MIN_DHGEX_KEY_SIZE;
+    public static final int PREFERRED_DHGEX_KEY_SIZE = 4096;
+    public static final int MAX_DHGEX_KEY_SIZE = 8192;
+
+    /**
+     * System property used to control whether to automatically register the
+     * Bouncyastle JCE provider
+     * @see #DEFAULT_REGISTER_BOUNCY_CASTLE
+     */
+    public static final String REGISTER_BOUNCY_CASTLE_PROP = "org.apache.sshd.registerBouncyCastle";
+
+    private static final AtomicInteger MAX_DHG_KEY_SIZE_HOLDER = new AtomicInteger(0);
 
     private static String securityProvider;
     private static Boolean registerBouncyCastle;
@@ -89,6 +124,63 @@ public final class SecurityUtils {
         return hasEcc;
     }
 
+    /**
+     * @return {@code true} if Diffie-Hellman Group Exchange is supported
+     * @see #getMaxDHGroupExchangeKeySize()
+     */
+    public static boolean isDHGroupExchangeSupported() {
+        return getMaxDHGroupExchangeKeySize() > 0;
+    }
+
+    /**
+     * @return The maximum supported Diffie-Hellman Group Exchange key size,
+     * or non-positive if not supported
+     */
+    public static int getMaxDHGroupExchangeKeySize() {
+        int maxSupportedKeySize;
+        synchronized (MAX_DHG_KEY_SIZE_HOLDER) {
+            maxSupportedKeySize = MAX_DHG_KEY_SIZE_HOLDER.get();
+            if (maxSupportedKeySize != 0) { // 1st time we are called ?
+                return maxSupportedKeySize;
+            }
+
+            String propValue = System.getProperty(MAX_DHGEX_KEY_SIZE_PROP);
+            if (GenericUtils.isEmpty(propValue)) {
+                maxSupportedKeySize = -1;
+                // Go down from max. to min. to ensure we stop at 1st maximum value success
+                for (int testKeySize = MAX_DHGEX_KEY_SIZE; testKeySize >= MIN_DHGEX_KEY_SIZE; testKeySize -= 1024) {
+                    if (isDHGroupExchangeSupported(testKeySize)) {
+                        maxSupportedKeySize = testKeySize;
+                        break;
+                    }
+                }
+            } else {
+                maxSupportedKeySize = Integer.parseInt(propValue);
+                // negative is OK - means user wants to disable DH group exchange
+                ValidateUtils.checkTrue(maxSupportedKeySize != 0,
+                        "Configured " + MAX_DHGEX_KEY_SIZE_PROP + " value must be non-zero: %d", maxSupportedKeySize);
+            }
+
+            MAX_DHG_KEY_SIZE_HOLDER.set(maxSupportedKeySize);
+        }
+
+        return maxSupportedKeySize;
+    }
+
+    public static boolean isDHGroupExchangeSupported(int maxKeySize) {
+        ValidateUtils.checkTrue(maxKeySize > Byte.SIZE, "Invalid max. key size: %d", maxKeySize);
+
+        try {
+            BigInteger r = new BigInteger("0").setBit(maxKeySize - 1);
+            DHParameterSpec dhSkipParamSpec = new DHParameterSpec(r, r);
+            KeyPairGenerator kpg = getKeyPairGenerator("DH");
+            kpg.initialize(dhSkipParamSpec);
+            return true;
+        } catch (GeneralSecurityException t) {
+            return false;
+        }
+    }
+
     public static synchronized void setSecurityProvider(String securityProvider) {
         SecurityUtils.securityProvider = securityProvider;
         registrationDone = false;
@@ -113,20 +205,22 @@ public final class SecurityUtils {
     private static void register() {
         if (!registrationDone) {
             if (registerBouncyCastle == null) {
-                String prop = System.getProperty("org.apache.sshd.registerBouncyCastle");
+                String prop = System.getProperty(REGISTER_BOUNCY_CASTLE_PROP);
                 if (!GenericUtils.isEmpty(prop)) {
                     registerBouncyCastle = Boolean.valueOf(prop);
                 }
             }
+
             if ((securityProvider == null) && ((registerBouncyCastle == null) || registerBouncyCastle)) {
                 // Use an inner class to avoid a strong dependency from SshServer on BouncyCastle
                 try {
                     new BouncyCastleRegistration().call();
                 } catch (Throwable t) {
+                    Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
                     if (registerBouncyCastle == null) {
-                        LOG.info("BouncyCastle not registered, using the default JCE provider");
+                        logger.info("BouncyCastle not registered, using the default JCE provider");
                     } else {
-                        LOG.error("Failed to register BouncyCastle as the defaut JCE provider");
+                        logger.error("Failed {} to register BouncyCastle as the defaut JCE provider: {}", t.getClass().getSimpleName(), t.getMessage());
                         throw new RuntimeException("Failed to register BouncyCastle as the defaut JCE provider", t);
                     }
                 }
@@ -141,14 +235,16 @@ public final class SecurityUtils {
         @SuppressWarnings("synthetic-access")
         @Override
         public Void call() throws Exception {
+            // no need for a logger specific to this class since this is a one-time call
+            Logger logger = LoggerFactory.getLogger(SecurityUtils.class);
             if (java.security.Security.getProvider(BOUNCY_CASTLE) == null) {
-                LOG.info("Trying to register BouncyCastle as a JCE provider");
+                logger.info("Trying to register BouncyCastle as a JCE provider");
                 java.security.Security.addProvider(new BouncyCastleProvider());
                 MessageDigest.getInstance("MD5", BOUNCY_CASTLE);
                 KeyAgreement.getInstance("DH", BOUNCY_CASTLE);
-                LOG.info("Registration succeeded");
+                logger.info("Registration succeeded");
             } else {
-                LOG.info("BouncyCastle already registered as a JCE provider");
+                logger.info("BouncyCastle already registered as a JCE provider");
             }
             securityProvider = BOUNCY_CASTLE;
             return null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
index 4f896a4..55797b1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/logging/LoggingUtils.java
@@ -21,13 +21,20 @@ package org.apache.sshd.common.util.logging;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.TreeMap;
 import java.util.logging.Level;
 
 import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.Predicate;
+import org.apache.sshd.common.util.ReflectionUtils;
 import org.slf4j.Logger;
 
 /**
@@ -70,30 +77,90 @@ public final class LoggingUtils {
      * (besides being a {@link Number} and {@code public static final}).
      * @return A {@link Map} of all the matching fields, where key=the field's {@link Integer}
      * value and mapping=the field's name
+     * @see #getMnemonicFields(Class, Predicate)
      */
     public static Map<Integer, String> generateMnemonicMap(Class<?> clazz, Predicate<? super Field> acceptor) {
-        Map<Integer, String> result = new HashMap<>();
-        for (Field f : clazz.getFields()) {
+        Collection<Field> fields = getMnemonicFields(clazz, acceptor);
+        if (GenericUtils.isEmpty(fields)) {
+            return Collections.emptyMap();
+        }
+
+        Map<Integer, String> result = new HashMap<>(fields.size());
+        for (Field f : fields) {
             String name = f.getName();
-            int mods = f.getModifiers();
-            if ((!Modifier.isPublic(mods)) || (!Modifier.isStatic(mods)) || (!Modifier.isFinal(mods))) {
-                continue;
+            try {
+                Number value = (Number) f.get(null);
+                String prev = result.put(NumberUtils.toInteger(value), name);
+                if (prev != null) {
+                    continue;   // debug breakpoint
+                }
+            } catch (Exception e) {
+                continue;   // debug breakpoint
             }
+        }
 
-            Class<?> type = f.getType();
-            if (!GenericUtils.isNumericClass(type)) {
-                continue;
-            }
+        return result;
+    }
 
-            if (!acceptor.evaluate(f)) {
-                continue;
+    /**
+     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+     * that have a common prefix and whose value is used by several of the other
+     * matching fields
+     *
+     * @param clazz The {@link Class} to query
+     * @param commonPrefix The expected common prefix
+     * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
+     * fields in this map. The key is the field's name and value is its associated opcode.
+     * @see #getAmbiguousMenmonics(Class, Predicate)
+     */
+    public static Map<String, Integer> getAmbiguousMenmonics(Class<?> clazz, final String commonPrefix) {
+        return getAmbiguousMenmonics(clazz, new Predicate<Field>() {
+            @Override
+            public boolean evaluate(Field f) {
+                String name = f.getName();
+                return name.startsWith(commonPrefix);
             }
+        });
+    }
 
+    /**
+     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+     * that are also accepted by the predicate and whose value is used by several of the other
+     * matching fields
+     *
+     * @param clazz The {@link Class} to query
+     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
+     * (besides being a {@link Number} and {@code public static final}).
+     * @return A {@link Map} of all the mnemonic fields names whose value is the same as other
+     * fields in this map. The key is the field's name and value is its associated opcode.
+     * @see #getMnemonicFields(Class, Predicate)
+     */
+    public static Map<String, Integer> getAmbiguousMenmonics(Class<?> clazz, Predicate<? super Field> acceptor) {
+        Collection<Field> fields = getMnemonicFields(clazz, acceptor);
+        if (GenericUtils.isEmpty(fields)) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, Integer> result = new TreeMap<String, Integer>(String.CASE_INSENSITIVE_ORDER);
+        Map<Integer, List<String>> opcodesMap = new HashMap<>(fields.size());
+        for (Field f : fields) {
+            String name = f.getName();
             try {
                 Number value = (Number) f.get(null);
-                String prev = result.put(value.intValue(), name);
-                if (prev != null) {
-                    continue;   // debug breakpoint
+                Integer key = NumberUtils.toInteger(value);
+                List<String> nameList = opcodesMap.get(key);
+                if (nameList == null) {
+                    nameList = new ArrayList<String>();
+                    opcodesMap.put(key, nameList);
+                }
+                nameList.add(name);
+
+                int numOpcodes = nameList.size();
+                if (numOpcodes > 1) {
+                    result.put(name, key);
+                    if (numOpcodes == 2) {  // add the 1st name as well
+                        result.put(nameList.get(0), key);
+                    }
                 }
             } catch (Exception e) {
                 continue;   // debug breakpoint
@@ -104,6 +171,34 @@ public final class LoggingUtils {
     }
 
     /**
+     * Scans using reflection API for all <U>numeric {@code public static final}</U> fields
+     * that are also accepted by the predicate.
+     *
+     * @param clazz The {@link Class} to query
+     * @param acceptor The {@link Predicate} used to decide whether to process the {@link Field}
+     * (besides being a {@link Number} and {@code public static final}).
+     * @return A {@link Collection} of all the fields that have satisfied all conditions
+     */
+    public static Collection<Field> getMnemonicFields(Class<?> clazz, final Predicate<? super Field> acceptor) {
+        return ReflectionUtils.getMatchingFields(clazz, new Predicate<Field>() {
+            @Override
+            public boolean evaluate(Field f) {
+                int mods = f.getModifiers();
+                if ((!Modifier.isPublic(mods)) || (!Modifier.isStatic(mods)) || (!Modifier.isFinal(mods))) {
+                    return false;
+                }
+
+                Class<?> type = f.getType();
+                if (!NumberUtils.isNumericClass(type)) {
+                    return false;
+                }
+
+                return acceptor.evaluate(f);
+            }
+        });
+    }
+
+    /**
      * Verifies if the given level is above the required threshold for logging.
      *
      * @param level     The {@link Level} to evaluate

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
index 87536a4..3a0c4c5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
@@ -98,14 +98,16 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
     @Override
     public boolean next(Buffer buffer) throws Exception {
         int cmd = buffer.getUByte();
-
         ServerSession session = getServerSession();
+        if (log.isDebugEnabled()) {
+            log.debug("next({})[{}] process command={}", this, session, KeyExchange.Utils.getGroupKexOpcodeName(cmd));
+        }
+
         if (cmd == SshConstants.SSH_MSG_KEX_DH_GEX_REQUEST_OLD && expected == SshConstants.SSH_MSG_KEX_DH_GEX_REQUEST) {
-            log.debug("Received SSH_MSG_KEX_DH_GEX_REQUEST_OLD on {}", session);
             oldRequest = true;
-            min = 1024;
+            min = SecurityUtils.MIN_DHGEX_KEY_SIZE;
             prf = buffer.getInt();
-            max = 8192;
+            max = SecurityUtils.getMaxDHGroupExchangeKeySize();
 
             if (max < min || prf < min || max < prf) {
                 throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
@@ -116,7 +118,10 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
             hash = dh.getHash();
             hash.init();
 
-            log.debug("Send SSH_MSG_KEX_DH_GEX_GROUP on {}", session);
+            if (log.isDebugEnabled()) {
+                log.debug("next({})[{}] send SSH_MSG_KEX_DH_GEX_GROUP", this, session);
+            }
+
             buffer = session.prepareBuffer(SshConstants.SSH_MSG_KEX_DH_GEX_GROUP, BufferUtils.clear(buffer));
             buffer.putMPInt(dh.getP());
             buffer.putMPInt(dh.getG());
@@ -125,8 +130,8 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
             expected = SshConstants.SSH_MSG_KEX_DH_GEX_INIT;
             return false;
         }
+
         if (cmd == SshConstants.SSH_MSG_KEX_DH_GEX_REQUEST && expected == SshConstants.SSH_MSG_KEX_DH_GEX_REQUEST) {
-            log.debug("Received SSH_MSG_KEX_DH_GEX_REQUEST on {}", session);
             min = buffer.getInt();
             prf = buffer.getInt();
             max = buffer.getInt();
@@ -139,7 +144,9 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
             hash = dh.getHash();
             hash.init();
 
-            log.debug("Send SSH_MSG_KEX_DH_GEX_GROUP on {}", session);
+            if (log.isDebugEnabled()) {
+                log.debug("next({})[{}] Send SSH_MSG_KEX_DH_GEX_GROUP", this, session);
+            }
             buffer = session.prepareBuffer(SshConstants.SSH_MSG_KEX_DH_GEX_GROUP, BufferUtils.clear(buffer));
             buffer.putMPInt(dh.getP());
             buffer.putMPInt(dh.getG());
@@ -148,13 +155,14 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
             expected = SshConstants.SSH_MSG_KEX_DH_GEX_INIT;
             return false;
         }
+
         if (cmd != expected) {
             throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
-                    "Protocol error: expected packet " + expected + ", got " + cmd);
+                    "Protocol error: expected packet " + KeyExchange.Utils.getGroupKexOpcodeName(expected)
+                  + ", got " + KeyExchange.Utils.getGroupKexOpcodeName(cmd));
         }
 
         if (cmd == SshConstants.SSH_MSG_KEX_DH_GEX_INIT) {
-            log.debug("Received SSH_MSG_KEX_DH_GEX_INIT on {}", session);
             e = buffer.getMPIntAsBytes();
             dh.setF(e);
             k = dh.getK();
@@ -163,7 +171,7 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
             byte[] k_s;
             KeyPair kp = ValidateUtils.checkNotNull(session.getHostKey(), "No server key pair available");
             String algo = session.getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);
-            FactoryManager manager = session.getFactoryManager();
+            FactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No factory manager");
             Signature sig = ValidateUtils.checkNotNull(
                     NamedFactory.Utils.create(manager.getSignatureFactories(), algo),
                     "Unknown negotiated server keys: %s",
@@ -203,13 +211,15 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
             sigH = buffer.getCompactData();
 
             if (log.isTraceEnabled()) {
-                log.trace("{}[K_S]:  {}", session, BufferUtils.printHex(k_s));
-                log.trace("{}[f]:    {}", session, BufferUtils.printHex(f));
-                log.trace("{}[sigH]: {}", session, BufferUtils.printHex(sigH));
+                log.trace("next({})[{}][K_S]:  {}", this, session, BufferUtils.printHex(k_s));
+                log.trace("next({})[{}][f]:    {}", this, session, BufferUtils.printHex(f));
+                log.trace("next({})[{}][sigH]: {}", this, session, BufferUtils.printHex(sigH));
             }
 
             // Send response
-            log.debug("Send SSH_MSG_KEX_DH_GEX_REPLY on {}", session);
+            if (log.isDebugEnabled()) {
+                log.debug("next({})[{}] Send SSH_MSG_KEX_DH_GEX_REPLY", this, session);
+            }
             buffer.clear();
             buffer.rpos(5);
             buffer.wpos(5);
@@ -227,12 +237,10 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
     private DHG chooseDH(int min, int prf, int max) throws Exception {
         List<Moduli.DhGroup> groups = loadModuliGroups();
 
-        min = Math.max(min, 1024);
-        prf = Math.max(prf, 1024);
-        // Keys of size > 1024 are not support by default with JCE, so only enable
-        // those if BouncyCastle is registered
-        prf = Math.min(prf, SecurityUtils.isBouncyCastleRegistered() ? 8192 : 1024);
-        max = Math.min(max, 8192);
+        min = Math.max(min, SecurityUtils.MIN_DHGEX_KEY_SIZE);
+        prf = Math.max(prf, SecurityUtils.MIN_DHGEX_KEY_SIZE);
+        prf = Math.min(prf, SecurityUtils.getMaxDHGroupExchangeKeySize());
+        max = Math.min(max, SecurityUtils.getMaxDHGroupExchangeKeySize());
         int bestSize = 0;
         List<Moduli.DhGroup> selected = new ArrayList<>();
         for (Moduli.DhGroup group : groups) {
@@ -247,15 +255,16 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
                 selected.add(group);
             }
         }
+
+        ServerSession session = getServerSession();
         if (selected.isEmpty()) {
-            log.warn("No suitable primes found, defaulting to DHG1");
+            log.warn("chooseDH({})[{}] No suitable primes found, defaulting to DHG1", this, session);
             return getDH(new BigInteger(DHGroupData.getP1()), new BigInteger(DHGroupData.getG()));
         }
 
-        ServerSession session = getServerSession();
         FactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No factory manager");
         Factory<Random> factory = ValidateUtils.checkNotNull(manager.getRandomFactory(), "No random factory");
-        Random random = factory.create();
+        Random random = ValidateUtils.checkNotNull(factory.create(), "No random generator");
         int which = random.random(selected.size());
         Moduli.DhGroup group = selected.get(which);
         return getDH(group.p, group.g);
@@ -300,4 +309,8 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
         return (DHG) factory.create(p, g);
     }
 
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + factory.getName() + "]";
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
index eaa8abb..4a45cd5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
@@ -82,19 +82,23 @@ public class DHGServer extends AbstractDHServerKeyExchange {
     @Override
     public boolean next(Buffer buffer) throws Exception {
         int cmd = buffer.getUByte();
+        ServerSession session = getServerSession();
+        if (log.isDebugEnabled()) {
+            log.debug("next({})[{}] process command={}", this, session, KeyExchange.Utils.getSimpleKexOpcodeName(cmd));
+        }
+
         if (cmd != SshConstants.SSH_MSG_KEXDH_INIT) {
             throw new SshException(SshConstants.SSH2_DISCONNECT_KEY_EXCHANGE_FAILED,
-                    "Protocol error: expected packet " + SshConstants.SSH_MSG_KEXDH_INIT + ", got " + cmd);
+                    "Protocol error: expected packet SSH_MSG_KEXDH_INIT, got " + KeyExchange.Utils.getSimpleKexOpcodeName(cmd));
         }
-        ServerSession session = getServerSession();
-        log.debug("Received SSH_MSG_KEXDH_INIT on {}", session);
+
         e = buffer.getMPIntAsBytes();
         dh.setF(e);
         k = dh.getK();
 
         KeyPair kp = ValidateUtils.checkNotNull(session.getHostKey(), "No server key pair available");
         String algo = session.getNegotiatedKexParameter(KexProposalOption.SERVERKEYS);
-        FactoryManager manager = session.getFactoryManager();
+        FactoryManager manager = ValidateUtils.checkNotNull(session.getFactoryManager(), "No factory manager");
         Signature sig = ValidateUtils.checkNotNull(
                 NamedFactory.Utils.create(manager.getSignatureFactories(), algo),
                 "Unknown negotiated server keys: %s",
@@ -125,13 +129,15 @@ public class DHGServer extends AbstractDHServerKeyExchange {
         sigH = buffer.getCompactData();
 
         if (log.isTraceEnabled()) {
-            log.trace("{}[K_S]:  {}", session, BufferUtils.printHex(k_s));
-            log.trace("{}[f]:    {}", session, BufferUtils.printHex(f));
-            log.trace("{}[sigH]: {}", session, BufferUtils.printHex(sigH));
+            log.trace("next({})[{}][K_S]:  {}", this, session, BufferUtils.printHex(k_s));
+            log.trace("next({})[{}][f]:    {}", this, session, BufferUtils.printHex(f));
+            log.trace("next({})[{}][sigH]: {}", this, session, BufferUtils.printHex(sigH));
         }
 
         // Send response
-        log.debug("Send SSH_MSG_KEXDH_REPLY on {}", session);
+        if (log.isDebugEnabled()) {
+            log.debug("next({})[{}] Send SSH_MSG_KEXDH_REPLY", this, session);
+        }
         buffer.clear();
         buffer.rpos(5);
         buffer.wpos(5);
@@ -142,4 +148,9 @@ public class DHGServer extends AbstractDHServerKeyExchange {
         session.writePacket(buffer);
         return true;
     }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "[" + factory.getName() + "]";
+    }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
index d6e38ec..f8619bb 100644
--- a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
@@ -48,6 +48,7 @@ import org.apache.sshd.common.kex.KeyExchange;
 import org.apache.sshd.common.session.Session;
 import org.apache.sshd.common.session.SessionListener;
 import org.apache.sshd.common.subsystem.sftp.SftpConstants;
+import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.server.ServerFactoryManager;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
@@ -55,6 +56,7 @@ import org.apache.sshd.util.test.JSchLogger;
 import org.apache.sshd.util.test.SimpleUserInfo;
 import org.apache.sshd.util.test.TeeOutputStream;
 import org.junit.After;
+import org.junit.Assume;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.runners.MethodSorters;
@@ -192,6 +194,7 @@ public class KeyReExchangeTest extends BaseTestSupport {
 
     @Test
     public void testReExchangeFromJschClient() throws Exception {
+        Assume.assumeTrue("DH Group Exchange not supported", SecurityUtils.isDHGroupExchangeSupported());
         setUp(0, 0);
 
         JSchLogger.init();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
index b56c3cb..439210d 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java
@@ -36,6 +36,7 @@ import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.kex.BuiltinDHFactories;
 import org.apache.sshd.common.kex.KeyExchange;
+import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.server.SshServer;
 import org.apache.sshd.util.test.BaseTestSupport;
 import org.apache.sshd.util.test.TeeOutputStream;
@@ -87,6 +88,11 @@ public class KexTest extends BaseTestSupport {
 
     @Test
     public void testClientKeyExchange() throws Exception {
+        if (factory.isGroupExchange()) {
+            assertEquals(factory.getName() + " not supported even though DH group exchange supported",
+                         SecurityUtils.isDHGroupExchangeSupported(), factory.isSupported());
+        }
+
         Assume.assumeTrue(factory.getName() + " not supported", factory.isSupported());
         testClient(ClientBuilder.DH2KEX.transform(factory));
     }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java
index c43097f..552418f 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/SshConstantsTest.java
@@ -19,8 +19,9 @@
 
 package org.apache.sshd.common;
 
-import java.lang.reflect.Field;
+import java.util.Collection;
 
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.util.test.BaseTestSupport;
 import org.junit.FixMethodOrder;
 import org.junit.Test;
@@ -53,10 +54,19 @@ public class SshConstantsTest extends BaseTestSupport {
 
     @Test
     public void testAmbiguousOpcodes() throws Exception {
-        for (String name : SshConstants.AMBIGUOUS_OPCODES) {
-            Field f = SshConstants.class.getField(name);
-            int cmd = f.getByte(null) & 0xFF;
-            assertEquals("Mismatched mnemonic for " + name, Integer.toString(cmd), SshConstants.getCommandMessageName(cmd));
+        int[] knownAmbiguities = { 30, 31, 60 };
+        Collection<Integer> opcodes = SshConstants.getAmbiguousOpcodes();
+        assertTrue("Not enough ambiguities found", GenericUtils.size(opcodes) >= knownAmbiguities.length);
+
+        for (int cmd : knownAmbiguities) {
+            assertEquals("Mismatched mnemonic for known ambiguity=" + cmd, Integer.toString(cmd), SshConstants.getCommandMessageName(cmd));
+            assertTrue("Known ambiguity not reported as such: " + cmd, SshConstants.isAmbigouosOpcode(cmd));
+            assertTrue("Known ambiguity=" + cmd + " not listed: " + opcodes, opcodes.contains(cmd));
+        }
+
+        for (Integer cmd : opcodes) {
+            assertEquals("Mismatched mnemonic for " + cmd, cmd.toString(), SshConstants.getCommandMessageName(cmd));
+            assertTrue("Opcode not detected as ambiguous: " + cmd, SshConstants.isAmbigouosOpcode(cmd));
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/test/java/org/apache/sshd/common/kex/KeyExchangeTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/kex/KeyExchangeTest.java b/sshd-core/src/test/java/org/apache/sshd/common/kex/KeyExchangeTest.java
new file mode 100644
index 0000000..9de3fc5
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/kex/KeyExchangeTest.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.kex;
+
+import java.util.Map;
+
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.util.Transformer;
+import org.apache.sshd.util.test.BaseTestSupport;
+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 KeyExchangeTest extends BaseTestSupport {
+    public KeyExchangeTest() {
+        super();
+    }
+
+    @Test
+    public void testSimpleKexOpcodeName() {
+        testKexOpcodeName(KeyExchange.Utils.SIMPLE_KEX_OPCODES_MAP, new Transformer<Integer, String>() {
+            @Override
+            public String transform(Integer cmd) {
+                return KeyExchange.Utils.getSimpleKexOpcodeName(cmd);
+            }
+        });
+    }
+
+    @Test
+    public void testGroupKexOpcodeName() {
+        testKexOpcodeName(KeyExchange.Utils.GROUP_KEX_OPCODES_MAP, new Transformer<Integer, String>() {
+            @Override
+            public String transform(Integer cmd) {
+                return KeyExchange.Utils.getGroupKexOpcodeName(cmd);
+            }
+        });
+    }
+
+    private static void testKexOpcodeName(Map<Integer, String> opsMap, Transformer<Integer, String> xformer) {
+        for (Map.Entry<Integer, String> oe : opsMap.entrySet()) {
+            Integer cmd = oe.getKey();
+            String expected = oe.getValue();
+            String actual = xformer.transform(cmd);
+            assertSame("Mismatched results for cmd=" + cmd, expected, actual);
+
+            if (SshConstants.isAmbigouosOpcode(cmd)) {
+                assertEquals("Unexpected ambiguous command resolution for " + cmd, cmd.toString(), SshConstants.getCommandMessageName(cmd));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d966cb68/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
index 9571b06..cbe530a 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/util/SecurityUtilsTest.java
@@ -170,4 +170,12 @@ public class SecurityUtilsTest extends BaseTestSupport {
 
         return kp;
     }
+
+    @Test
+    public void testBouncyCastleRegistrationSettings() {
+        Assume.assumeTrue("Bouncycastle not registered", SecurityUtils.isBouncyCastleRegistered());
+        assertTrue("DH Group Exchange not supported", SecurityUtils.isDHGroupExchangeSupported());
+        assertEquals("Mismatched max. DH group exchange key size", SecurityUtils.MAX_DHGEX_KEY_SIZE, SecurityUtils.getMaxDHGroupExchangeKeySize());
+        assertTrue("ECC not supported", SecurityUtils.hasEcc());
+    }
 }


[2/2] mina-sshd git commit: Moved some code to NumberUtils

Posted by lg...@apache.org.
Moved some code to NumberUtils


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/82b8c928
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/82b8c928
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/82b8c928

Branch: refs/heads/master
Commit: 82b8c9288fbdc3afbac21308d15a688250152028
Parents: d966cb6
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Sun Nov 22 07:21:05 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Sun Nov 22 07:21:05 2015 +0200

----------------------------------------------------------------------
 .../openssh/OpenSSHStatExtensionInfo.java       | 17 ++---
 .../extensions/SpaceAvailableExtensionInfo.java | 11 ++-
 .../sshd/common/util/buffer/BufferUtils.java    | 14 +---
 .../sshd/common/util/NumberUtilsTest.java       | 74 ++++++++++++++++++++
 4 files changed, 85 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/82b8c928/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/OpenSSHStatExtensionInfo.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/OpenSSHStatExtensionInfo.java b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/OpenSSHStatExtensionInfo.java
index f831533..a9cd944 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/OpenSSHStatExtensionInfo.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/subsystem/sftp/extensions/openssh/OpenSSHStatExtensionInfo.java
@@ -19,7 +19,7 @@
 
 package org.apache.sshd.client.subsystem.sftp.extensions.openssh;
 
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
 /**
@@ -58,18 +58,9 @@ public class OpenSSHStatExtensionInfo implements Cloneable {
 
     @Override
     public int hashCode() {
-        int result =  GenericUtils.hashCode(this.f_bsize);
-        result = 31 * result + GenericUtils.hashCode(this.f_frsize);
-        result = 31 * result + GenericUtils.hashCode(this.f_blocks);
-        result = 31 * result + GenericUtils.hashCode(this.f_bfree);
-        result = 31 * result + GenericUtils.hashCode(this.f_bavail);
-        result = 31 * result + GenericUtils.hashCode(this.f_files);
-        result = 31 * result + GenericUtils.hashCode(this.f_ffree);
-        result = 31 * result + GenericUtils.hashCode(this.f_favail);
-        result = 31 * result + GenericUtils.hashCode(this.f_fsid);
-        result = 31 * result + GenericUtils.hashCode(this.f_flag);
-        result = 31 * result + GenericUtils.hashCode(this.f_namemax);
-        return result;
+        return NumberUtils.hashCode(this.f_bsize, this.f_frsize, this.f_blocks,
+                this.f_bfree, this.f_bavail, this.f_files, this.f_ffree,
+                this.f_favail, this.f_fsid, this.f_flag, this.f_namemax);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/82b8c928/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SpaceAvailableExtensionInfo.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SpaceAvailableExtensionInfo.java b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SpaceAvailableExtensionInfo.java
index 83dc914..16dc184 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SpaceAvailableExtensionInfo.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/subsystem/sftp/extensions/SpaceAvailableExtensionInfo.java
@@ -22,7 +22,7 @@ package org.apache.sshd.common.subsystem.sftp.extensions;
 import java.io.IOException;
 import java.nio.file.FileStore;
 
-import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 
 /**
@@ -58,12 +58,9 @@ public class SpaceAvailableExtensionInfo implements Cloneable {
 
     @Override
     public int hashCode() {
-        int result = GenericUtils.hashCode(bytesOnDevice);
-        result = 31 * result + GenericUtils.hashCode(unusedBytesOnDevice);
-        result = 31 * result + GenericUtils.hashCode(bytesAvailableToUser);
-        result = 31 * result + GenericUtils.hashCode(unusedBytesAvailableToUser);
-        result = 31 * result + bytesPerAllocationUnit;
-        return result;
+        return NumberUtils.hashCode(bytesOnDevice, unusedBytesOnDevice,
+                    bytesAvailableToUser, unusedBytesAvailableToUser,
+                    bytesPerAllocationUnit);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/82b8c928/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
index d0143ab..676549f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/util/buffer/BufferUtils.java
@@ -25,6 +25,7 @@ import java.io.StreamCorruptedException;
 
 import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.Int2IntFunction;
+import org.apache.sshd.common.util.NumberUtils;
 import org.apache.sshd.common.util.ValidateUtils;
 import org.apache.sshd.common.util.io.IoUtils;
 
@@ -310,18 +311,9 @@ public final class BufferUtils {
         }
         return true;
     }
-
-    public static int getNextPowerOf2(int i) {
+    public static int getNextPowerOf2(int value) {
         // for 0-7 return 8
-        if (i < Byte.SIZE) {
-            return Byte.SIZE;
-        }
-
-        int j = 1;
-        while (j < i) {
-            j <<= 1;
-        }
-        return j;
+        return (value < Byte.SIZE) ? Byte.SIZE : NumberUtils.getNextPowerOf2(value);
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/82b8c928/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
new file mode 100644
index 0000000..ed42e02
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/util/NumberUtilsTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.BaseTestSupport;
+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 NumberUtilsTest extends BaseTestSupport {
+    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.longValue()));
+        }
+    }
+
+    @Test
+    public void testNextPowerOf2() {
+        for (Long v : NumberUtils.POWERS_OF_TWO) {
+            long expected = v.longValue();
+            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());
+            }
+        }
+    }
+}