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: "Servers and clients SHOULD support groups with a modulus length of k
+ * bits, where 1024 <= k <= 8192".
+ * </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());
+ }
+ }
+ }
+}