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/12/01 08:05:50 UTC
[3/3] mina-sshd git commit: [SSHD-604] hmac-sha2-512 is using the
wrong algorithm
[SSHD-604] hmac-sha2-512 is using the wrong algorithm
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/e0041fc6
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/e0041fc6
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/e0041fc6
Branch: refs/heads/master
Commit: e0041fc60281b11037384abb79c3a94ef8bf8c5e
Parents: 7b18b09
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Tue Dec 1 09:05:28 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Tue Dec 1 09:05:28 2015 +0200
----------------------------------------------------------------------
.../sshd/common/channel/ChannelFactory.java | 2 +-
.../org/apache/sshd/common/mac/BaseMac.java | 25 +-
.../org/apache/sshd/common/mac/BuiltinMacs.java | 72 +++--
.../java/org/apache/sshd/common/mac/Mac.java | 12 +-
.../org/apache/sshd/common/mac/MacFactory.java | 4 +-
.../apache/sshd/common/mac/MacInformation.java | 41 +++
.../sshd/common/util/buffer/BufferUtils.java | 107 +++++++
.../java/org/apache/sshd/KeyReExchangeTest.java | 26 +-
.../org/apache/sshd/client/scp/ScpTest.java | 2 +-
.../apache/sshd/common/mac/BuiltinMacsTest.java | 1 -
.../org/apache/sshd/common/mac/MacTest.java | 240 ++++++++-------
.../apache/sshd/common/mac/MacVectorsTest.java | 298 +++++++++++++++++++
.../org/apache/sshd/common/util/BufferTest.java | 42 ---
.../sshd/common/util/buffer/BufferTest.java | 50 ++++
.../common/util/buffer/BufferUtilsTest.java | 51 ++++
.../apache/sshd/util/test/BaseTestSupport.java | 13 +
.../test/OutputCountTrackingOutputStream.java | 56 ++++
17 files changed, 842 insertions(+), 200 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelFactory.java
index cef6549..a19d56c 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/channel/ChannelFactory.java
@@ -26,6 +26,6 @@ import org.apache.sshd.common.NamedFactory;
*/
// CHECKSTYLE:OFF
public interface ChannelFactory extends NamedFactory<Channel> {
-
+ // nothing extra
}
//CHECKSTYLE:ON
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
index 4ef732d..76e09e8 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/BaseMac.java
@@ -20,6 +20,7 @@ package org.apache.sshd.common.mac;
import javax.crypto.spec.SecretKeySpec;
+import org.apache.sshd.common.util.NumberUtils;
import org.apache.sshd.common.util.SecurityUtils;
/**
@@ -48,11 +49,16 @@ public class BaseMac implements Mac {
}
@Override
- public int getBlockSize() {
+ public final int getBlockSize() {
return bsize;
}
@Override
+ public final int getDefaultBlockSize() {
+ return defbsize;
+ }
+
+ @Override
public void init(byte[] key) throws Exception {
if (key.length > defbsize) {
byte[] tmp = new byte[defbsize];
@@ -74,9 +80,19 @@ public class BaseMac implements Mac {
update(tmp, 0, 4);
}
+ @Override // TODO make this a default method in Java 8
+ public void update(byte buf[]) {
+ update(buf, 0, NumberUtils.length(buf));
+ }
+
@Override
- public void update(byte foo[], int s, int l) {
- mac.update(foo, s, l);
+ public void update(byte buf[], int offset, int len) {
+ mac.update(buf, offset, len);
+ }
+
+ @Override // TODO make this a default method in Java 8
+ public void doFinal(byte[] buf) throws Exception {
+ doFinal(buf, 0);
}
@Override
@@ -91,7 +107,8 @@ public class BaseMac implements Mac {
@Override
public String toString() {
- return getClass().getSimpleName() + "[" + getAlgorithm() + "] - " + getBlockSize() + " bits";
+ return getClass().getSimpleName() + "[" + getAlgorithm() + "] - "
+ + getBlockSize() + "/" + getDefaultBlockSize() + " bits";
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
index b501d69..eac8855 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/BuiltinMacs.java
@@ -42,42 +42,12 @@ import org.apache.sshd.common.util.ValidateUtils;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public enum BuiltinMacs implements MacFactory {
- hmacmd5(Constants.HMAC_MD5) {
- @Override
- public Mac create() {
- return new BaseMac("HmacMD5", 16, 16);
- }
- },
- hmacmd596(Constants.HMAC_MD5_96) {
- @Override
- public Mac create() {
- return new BaseMac("HmacMD5", 12, 16);
- }
- },
- hmacsha1(Constants.HMAC_SHA1) {
- @Override
- public Mac create() {
- return new BaseMac("HmacSHA1", 20, 20);
- }
- },
- hmacsha196(Constants.HMAC_SHA1_96) {
- @Override
- public Mac create() {
- return new BaseMac("HmacSHA1", 12, 20);
- }
- },
- hmacsha256(Constants.HMAC_SHA2_256) {
- @Override
- public Mac create() {
- return new BaseMac("HmacSHA256", 32, 32);
- }
- },
- hmacsha512(Constants.HMAC_SHA2_512) {
- @Override
- public Mac create() {
- return new BaseMac("HmacSHA1", 64, 64);
- }
- };
+ hmacmd5(Constants.HMAC_MD5, "HmacMD5", 16, 16),
+ hmacmd596(Constants.HMAC_MD5_96, "HmacMD5", 12, 16),
+ hmacsha1(Constants.HMAC_SHA1, "HmacSHA1", 20, 20),
+ hmacsha196(Constants.HMAC_SHA1_96, "HmacSHA1", 12, 20),
+ hmacsha256(Constants.HMAC_SHA2_256, "HmacSHA256", 32, 32),
+ hmacsha512(Constants.HMAC_SHA2_512, "HmacSHA512", 64, 65);
public static final Set<BuiltinMacs> VALUES =
Collections.unmodifiableSet(EnumSet.allOf(BuiltinMacs.class));
@@ -86,9 +56,20 @@ public enum BuiltinMacs implements MacFactory {
new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
private final String factoryName;
+ private final String algorithm;
+ private final int defbsize;
+ private final int bsize;
+
+ BuiltinMacs(String factoryName, String algorithm, int bsize, int defbsize) {
+ this.factoryName = factoryName;
+ this.algorithm = algorithm;
+ this.bsize = bsize;
+ this.defbsize = defbsize;
+ }
- BuiltinMacs(String facName) {
- factoryName = facName;
+ @Override
+ public Mac create() {
+ return new BaseMac(getAlgorithm(), getBlockSize(), getDefaultBlockSize());
}
@Override
@@ -97,6 +78,21 @@ public enum BuiltinMacs implements MacFactory {
}
@Override
+ public final String getAlgorithm() {
+ return algorithm;
+ }
+
+ @Override
+ public final int getBlockSize() {
+ return bsize;
+ }
+
+ @Override
+ public final int getDefaultBlockSize() {
+ return defbsize;
+ }
+
+ @Override
public final boolean isSupported() {
return true;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java
index 81d9929..0da9472 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/Mac.java
@@ -24,16 +24,16 @@ package org.apache.sshd.common.mac;
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
-public interface Mac {
- String getAlgorithm();
-
- int getBlockSize();
-
+public interface Mac extends MacInformation {
void init(byte[] key) throws Exception;
- void update(byte[] foo, int start, int len);
+ void update(byte[] buf);
+
+ void update(byte[] buf, int start, int len);
void updateUInt(long foo);
+ void doFinal(byte[] buf) throws Exception;
+
void doFinal(byte[] buf, int offset) throws Exception;
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
index 483426d..4463600 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacFactory.java
@@ -25,7 +25,7 @@ import org.apache.sshd.common.BuiltinFactory;
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
// CHECKSTYLE:OFF
-public interface MacFactory extends BuiltinFactory<Mac> {
-
+public interface MacFactory extends MacInformation, BuiltinFactory<Mac> {
+ // nothing extra
}
//CHECKSTYLE:ON
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java
new file mode 100644
index 0000000..583165f
--- /dev/null
+++ b/sshd-core/src/main/java/org/apache/sshd/common/mac/MacInformation.java
@@ -0,0 +1,41 @@
+/*
+ * 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.mac;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public interface MacInformation {
+ /**
+ * @return MAC algorithm name
+ */
+ String getAlgorithm();
+
+ /**
+ * @return MAC output block size in bytes - may be less than the default
+ * - e.g., MD5-96
+ */
+ int getBlockSize();
+
+ /**
+ * @return The "natural" MAC block size in bytes
+ */
+ int getDefaultBlockSize();
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/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 eed9679..1eb0e13 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
@@ -87,6 +87,113 @@ public final class BufferUtils {
}
/**
+ * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+ * @param csq The {@link CharSequence} containing the HEX encoded bytes
+ * @return The decoded bytes
+ * @throws IllegalArgumentException If invalid HEX sequence length
+ * @throws NumberFormatException If invalid HEX characters found
+ * @see #decodeHex(char, CharSequence, int, int)
+ */
+ public static byte[] decodeHex(char separator, CharSequence csq) {
+ return decodeHex(separator, csq, 0, GenericUtils.length(csq));
+ }
+
+ /**
+ * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+ * @param csq The {@link CharSequence} containing the HEX encoded bytes
+ * @param start Start offset of the HEX sequence (inclusive)
+ * @param end End offset of the HEX sequence (exclusive)
+ * @return The decoded bytes
+ * @throws IllegalArgumentException If invalid HEX sequence length
+ * @throws NumberFormatException If invalid HEX characters found
+ */
+ public static byte[] decodeHex(char separator, CharSequence csq, int start, int end) {
+ int len = end - start;
+ ValidateUtils.checkTrue(len >= 0, "Bad HEX sequence length: %d", len);
+ if (len == 0) {
+ return GenericUtils.EMPTY_BYTE_ARRAY;
+ }
+
+ int delta = 2;
+ byte[] bytes;
+ if (separator != EMPTY_HEX_SEPARATOR) {
+ // last character cannot be the separator
+ ValidateUtils.checkTrue((len % 3) == 2, "Invalid separated HEX sequence length: %d", len);
+ bytes = new byte[(len + 1) / 3];
+ delta++;
+ } else {
+ ValidateUtils.checkTrue((len & 0x01) == 0, "Invalid contiguous HEX sequence length: %d", len);
+ bytes = new byte[len >>> 1];
+ }
+
+ int writeLen = 0;
+ for (int curPos = start; curPos < end; curPos += delta, writeLen++) {
+ bytes[writeLen] = fromHex(csq.charAt(curPos), csq.charAt(curPos + 1));
+ }
+ assert writeLen == bytes.length;
+
+ return bytes;
+ }
+
+ /**
+ * @param <S> The {@link OutputStream} generic type
+ * @param stream The target {@link OutputStream}
+ * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+ * @param csq The {@link CharSequence} containing the HEX encoded bytes
+ * @return The number of bytes written to the stream
+ * @throws IOException If failed to write
+ * @throws IllegalArgumentException If invalid HEX sequence length
+ * @throws NumberFormatException If invalid HEX characters found
+ * @see #decodeHex(OutputStream, char, CharSequence, int, int)
+ */
+ public static <S extends OutputStream> int decodeHex(S stream, char separator, CharSequence csq) throws IOException {
+ return decodeHex(stream, separator, csq, 0, GenericUtils.length(csq));
+ }
+
+ /**
+ * @param <S> The {@link OutputStream} generic type
+ * @param stream The target {@link OutputStream}
+ * @param separator The separator between the HEX values - may be {@link #EMPTY_HEX_SEPARATOR}
+ * @param csq The {@link CharSequence} containing the HEX encoded bytes
+ * @param start Start offset of the HEX sequence (inclusive)
+ * @param end End offset of the HEX sequence (exclusive)
+ * @return The number of bytes written to the stream
+ * @throws IOException If failed to write
+ * @throws IllegalArgumentException If invalid HEX sequence length
+ * @throws NumberFormatException If invalid HEX characters found
+ */
+ public static <S extends OutputStream> int decodeHex(S stream, char separator, CharSequence csq, int start, int end) throws IOException {
+ int len = end - start;
+ ValidateUtils.checkTrue(len >= 0, "Bad HEX sequence length: %d", len);
+
+ int delta = 2;
+ if (separator != EMPTY_HEX_SEPARATOR) {
+ // last character cannot be the separator
+ ValidateUtils.checkTrue((len % 3) == 2, "Invalid separated HEX sequence length: %d", len);
+ delta++;
+ } else {
+ ValidateUtils.checkTrue((len & 0x01) == 0, "Invalid contiguous HEX sequence length: %d", len);
+ }
+
+ int writeLen = 0;
+ for (int curPos = start; curPos < end; curPos += delta, writeLen++) {
+ stream.write(fromHex(csq.charAt(curPos), csq.charAt(curPos + 1)) & 0xFF);
+ }
+
+ return writeLen;
+ }
+
+ public static byte fromHex(char hi, char lo) throws NumberFormatException {
+ int hiValue = HEX_DIGITS.indexOf(((hi >= 'A') && (hi <= 'F')) ? ('a' + (hi - 'A')) : hi);
+ int loValue = HEX_DIGITS.indexOf(((lo >= 'A') && (lo <= 'F')) ? ('a' + (lo - 'A')) : lo);
+ if ((hiValue < 0) || (loValue < 0)) {
+ throw new NumberFormatException("fromHex(" + new String(new char[]{hi, lo}) + ") non-HEX characters");
+ }
+
+ return (byte) ((hiValue << 4) + loValue);
+ }
+
+ /**
* Read a 32-bit value in network order
*
* @param input The {@link InputStream}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/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 34323a6..a1b62b6 100644
--- a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
@@ -34,6 +34,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.channel.ChannelShell;
import org.apache.sshd.client.channel.ClientChannel;
@@ -53,6 +54,7 @@ import org.apache.sshd.server.ServerFactoryManager;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
import org.apache.sshd.util.test.JSchLogger;
+import org.apache.sshd.util.test.OutputCountTrackingOutputStream;
import org.apache.sshd.util.test.SimpleUserInfo;
import org.apache.sshd.util.test.TeeOutputStream;
import org.junit.After;
@@ -494,13 +496,29 @@ public class KeyReExchangeTest extends BaseTestSupport {
try (ChannelShell channel = session.createShellChannel();
PipedOutputStream pipedIn = new PipedOutputStream();
- OutputStream teeOut = new TeeOutputStream(sent, pipedIn);
- OutputStream err = new NullOutputStream();
+ OutputStream sentTracker = new OutputCountTrackingOutputStream(sent) {
+ @Override
+ protected long updateWriteCount(long delta) {
+ long result = super.updateWriteCount(delta);
+ outputDebugMessage("SENT write count=%d", result);
+ return result;
+ }
+ };
+ OutputStream teeOut = new TeeOutputStream(sentTracker, pipedIn);
+ OutputStream stderr = new NullOutputStream();
+ OutputStream stdout = new OutputCountTrackingOutputStream(out) {
+ @Override
+ protected long updateWriteCount(long delta) {
+ long result = super.updateWriteCount(delta);
+ outputDebugMessage("OUT write count=%d", result);
+ return result;
+ }
+ };
InputStream inPipe = new PipedInputStream(pipedIn)) {
channel.setIn(inPipe);
- channel.setOut(out);
- channel.setErr(err);
+ channel.setOut(stdout);
+ channel.setErr(stderr);
channel.open();
teeOut.write("this is my command\n".getBytes(StandardCharsets.UTF_8));
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/test/java/org/apache/sshd/client/scp/ScpTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/client/scp/ScpTest.java b/sshd-core/src/test/java/org/apache/sshd/client/scp/ScpTest.java
index fc34111..a5e7197 100644
--- a/sshd-core/src/test/java/org/apache/sshd/client/scp/ScpTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/client/scp/ScpTest.java
@@ -807,7 +807,7 @@ public class ScpTest extends BaseTestSupport {
info.keyExchangeAlgorithm, info.serverHostKeyAlgorithm,
info.clientToServerCryptoAlgorithm, info.serverToClientCryptoAlgorithm,
info.clientToServerMACAlgorithm, info.serverToClientMACAlgorithm);
- conn.authenticateWithPassword(getCurrentTestName(), getCurrentTestName());
+ assertTrue("Failed to authenticate", conn.authenticateWithPassword(getCurrentTestName(), getCurrentTestName()));
final SCPClient scp_client = new SCPClient(conn);
try (OutputStream output = scp_client.put(fileName, expected.length, remotePath, mode)) {
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
index 271abe5..820e434 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/mac/BuiltinMacsTest.java
@@ -160,5 +160,4 @@ public class BuiltinMacsTest extends BaseTestSupport {
assertNull("Extension not un-registered", BuiltinMacs.resolveFactory(name));
}
}
-
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
index cd0b6c0..b749e9a 100644
--- a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacTest.java
@@ -18,107 +18,116 @@
*/
package org.apache.sshd.common.mac;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.cipher.BuiltinCiphers;
-import org.apache.sshd.common.cipher.Cipher;
-import org.apache.sshd.common.random.Random;
+import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.util.test.BaseTestSupport;
import org.apache.sshd.util.test.JSchLogger;
import org.apache.sshd.util.test.SimpleUserInfo;
-import org.apache.sshd.util.test.Utils;
import org.junit.After;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
-import org.junit.Ignore;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import com.jcraft.jsch.JSch;
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.ConnectionInfo;
+
/**
- * Test Cipher algorithms.
+ * Test MAC algorithms.
*
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
public class MacTest extends BaseTestSupport {
+ private static final Collection<String> ganymedMacs =
+ Collections.unmodifiableSet(new TreeSet<String>(String.CASE_INSENSITIVE_ORDER) {
+ private static final long serialVersionUID = 1L; // we're not serializing it
+
+ {
+ String[] macs = Connection.getAvailableMACs();
+ if (GenericUtils.length(macs) > 0) {
+ for (String m : macs) {
+ add(m);
+ }
+ }
+ }
+ });
+
+ @Parameters(name = "factory={0}")
+ public static Collection<Object[]> parameters() {
+ List<Object[]> ret = new ArrayList<>();
+ for (MacFactory f : BuiltinMacs.VALUES) {
+ if (!f.isSupported()) {
+ System.out.println("Skip unsupported MAC " + f);
+ continue;
+ }
- private SshServer sshd;
- private int port;
-
- @Test
- public void testHMACMD5() throws Exception {
- setUp(BuiltinMacs.hmacmd5);
- runTest();
- }
-
- @Test
- public void testHMACMD596() throws Exception {
- setUp(BuiltinMacs.hmacmd596);
- runTest();
- }
-
- @Test
- public void testHMACSHA1() throws Exception {
- setUp(BuiltinMacs.hmacsha1);
- runTest();
- }
+ String name = f.getName();
+ // derive the JSCH implementation of the specific MAC
+ int pos = name.indexOf('-');
+ String remainder = name.substring(pos + 1);
+ pos = remainder.indexOf('-');
+
+ String className;
+ if (pos < 0) {
+ className = "HMAC" + remainder.toUpperCase();
+ } else {
+ String algorithm = remainder.substring(0, pos);
+ remainder = remainder.substring(pos + 1);
+ if ("sha2".equals(algorithm)) {
+ className = "HMACSHA" + remainder.toUpperCase();
+ } else {
+ className = "HMAC" + algorithm.toUpperCase() + remainder.toUpperCase();
+ }
+ }
- @Test
- public void testHMACSHA196() throws Exception {
- setUp(BuiltinMacs.hmacsha196);
- runTest();
- }
+ ret.add(new Object[]{f, "com.jcraft.jsch.jce." + className});
+ }
- @Test
- public void testHMACSHA256() throws Exception {
- setUp(BuiltinMacs.hmacsha256);
- runTest();
+ return ret;
}
- @Test
- @Ignore("Lead to ArrayIndexOutOfBoundsException in JSch")
- public void testHMACSHA512() throws Exception {
- setUp(BuiltinMacs.hmacsha512);
- runTest();
+ @BeforeClass
+ public static void jschnit() {
+ JSchLogger.init();
}
- @Test
- public void loadTest() throws Exception {
- Random random = Utils.getRandomizerInstance();
- loadTest(BuiltinCiphers.aes128cbc, random);
- loadTest(BuiltinCiphers.blowfishcbc, random);
- loadTest(BuiltinCiphers.tripledescbc, random);
- }
+ private final MacFactory factory;
+ private final String jschMacClass;
+ private SshServer sshd;
+ private int port;
- protected void loadTest(NamedFactory<Cipher> factory, Random random) throws Exception {
- Cipher cipher = factory.create();
- byte[] key = new byte[cipher.getBlockSize()];
- byte[] iv = new byte[cipher.getIVSize()];
- random.fill(key, 0, key.length);
- random.fill(iv, 0, iv.length);
- cipher.init(Cipher.Mode.Encrypt, key, iv);
-
- byte[] input = new byte[cipher.getBlockSize()];
- random.fill(input, 0, input.length);
- long t0 = System.currentTimeMillis();
- for (int i = 0; i < 100000; i++) {
- cipher.update(input, 0, input.length);
- }
- long t1 = System.currentTimeMillis();
- System.err.println(factory.getName() + ": " + (t1 - t0) + " ms");
+ public MacTest(MacFactory factory, String jschMacClass) {
+ this.factory = factory;
+ this.jschMacClass = jschMacClass;
}
-
- protected void setUp(NamedFactory<Mac> mac) throws Exception {
+ @Before
+ public void setUp() throws Exception {
sshd = setupTestServer();
sshd.setKeyPairProvider(createTestHostKeyProvider());
- sshd.setMacFactories(Arrays.<NamedFactory<Mac>>asList(mac));
+ sshd.setMacFactories(Arrays.<NamedFactory<Mac>>asList(factory));
sshd.start();
port = sshd.getPort();
}
@@ -130,52 +139,81 @@ public class MacTest extends BaseTestSupport {
}
}
- protected void runTest() throws Exception {
- JSchLogger.init();
+ @Test
+ public void testWithJSCH() throws Exception {
+ String macName = factory.getName();
+ Assume.assumeTrue("Known JSCH bug with " + macName, !BuiltinMacs.hmacsha512.equals(factory));
+
JSch sch = new JSch();
JSch.setConfig("cipher.s2c", "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,none");
JSch.setConfig("cipher.c2s", "aes128-cbc,3des-cbc,blowfish-cbc,aes192-cbc,aes256-cbc,none");
- JSch.setConfig("mac.s2c", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96,hmac-sha2-512");
- JSch.setConfig("mac.c2s", "hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96,hmac-sha2-512");
- JSch.setConfig("hmac-sha2-512", "com.jcraft.jsch.jce.HMACSHA512");
- com.jcraft.jsch.Session s = sch.getSession(getCurrentTestName(), TEST_LOCALHOST, port);
+ JSch.setConfig("mac.s2c", macName);
+ JSch.setConfig("mac.c2s", macName);
+ JSch.setConfig(macName, jschMacClass);
+
+ com.jcraft.jsch.Session session = sch.getSession(getCurrentTestName(), TEST_LOCALHOST, port);
try {
- s.setUserInfo(new SimpleUserInfo(getCurrentTestName()));
- s.connect();
- com.jcraft.jsch.Channel c = s.openChannel("shell");
- c.connect();
-
- try (OutputStream os = c.getOutputStream();
- InputStream is = c.getInputStream()) {
-
- String expected = "this is my command\n";
- byte[] bytes = expected.getBytes(StandardCharsets.UTF_8);
- byte[] data = new byte[bytes.length + Long.SIZE];
- for (int i = 0; i < 10; i++) {
- os.write(bytes);
- os.flush();
- int len = is.read(data);
- String str = new String(data, 0, len);
- assertEquals("Mismatched data at iteration " + i, expected, str);
- }
+ session.setUserInfo(new SimpleUserInfo(getCurrentTestName()));
+ session.connect();
+
+ com.jcraft.jsch.Channel channel = session.openChannel("shell");
+ channel.connect();
+
+ try (OutputStream stdin = channel.getOutputStream();
+ InputStream stdout = channel.getInputStream();
+ InputStream stderr = channel.getExtInputStream()) {
+ runShellTest(stdin, stdout);
} finally {
- c.disconnect();
+ channel.disconnect();
}
} finally {
- s.disconnect();
+ session.disconnect();
}
}
- static boolean checkCipher(String cipher) {
+ @Test
+ public void testWithGanymede() throws Exception {
+ String macName = factory.getName();
+ Assume.assumeTrue("Factory not supported: " + macName, ganymedMacs.contains(macName));
+
+ Connection conn = new Connection(TEST_LOCALHOST, port);
try {
- Class<?> c = Class.forName(cipher);
- com.jcraft.jsch.Cipher _c = (com.jcraft.jsch.Cipher) (c.newInstance());
- _c.init(com.jcraft.jsch.Cipher.ENCRYPT_MODE,
- new byte[_c.getBlockSize()],
- new byte[_c.getIVSize()]);
- return true;
- } catch (Exception e) {
- return false;
+ conn.setClient2ServerMACs(new String[]{macName});
+
+ ConnectionInfo info = conn.connect(null, (int) TimeUnit.SECONDS.toMillis(5L), (int) TimeUnit.SECONDS.toMillis(11L));
+ outputDebugMessage("Connected: kex=%s, key-type=%s, c2senc=%s, s2cenc=%s, c2mac=%s, s2cmac=%s",
+ info.keyExchangeAlgorithm, info.serverHostKeyAlgorithm,
+ info.clientToServerCryptoAlgorithm, info.serverToClientCryptoAlgorithm,
+ info.clientToServerMACAlgorithm, info.serverToClientMACAlgorithm);
+ assertTrue("Failed to authenticate", conn.authenticateWithPassword(getCurrentTestName(), getCurrentTestName()));
+
+ ch.ethz.ssh2.Session session = conn.openSession();
+ try {
+ session.startShell();
+ try (OutputStream stdin = session.getStdin();
+ InputStream stdout = session.getStdout();
+ InputStream stderr = session.getStderr()) {
+ runShellTest(stdin, stdout);
+ }
+ } finally {
+ session.close();
+ }
+ } finally {
+ conn.close();
+ }
+ }
+
+ private void runShellTest(OutputStream stdin, InputStream stdout) throws IOException {
+ String expected = "this is my command\n";
+ byte[] bytes = expected.getBytes(StandardCharsets.UTF_8);
+ byte[] data = new byte[bytes.length + Long.SIZE];
+ for (int index = 1; index <= 10; index++) {
+ stdin.write(bytes);
+ stdin.flush();
+
+ int len = stdout.read(data);
+ String str = new String(data, 0, len, StandardCharsets.UTF_8);
+ assertEquals("Mismatched data at iteration " + index, expected, str);
}
}
}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
new file mode 100644
index 0000000..de47a79
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/mac/MacVectorsTest.java
@@ -0,0 +1,298 @@
+/*
+ * 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.mac;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.util.GenericUtils;
+import org.apache.sshd.common.util.Pair;
+import org.apache.sshd.common.util.ValidateUtils;
+import org.apache.sshd.common.util.buffer.BufferUtils;
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests
+public class MacVectorsTest extends BaseTestSupport {
+ private final VectorSeed seed;
+ private final Factory<? extends Mac> macFactory;
+ private final byte[] expected;
+
+ public MacVectorsTest(VectorSeed seed, String factoryName, String expected) {
+ this.seed = ValidateUtils.checkNotNull(seed, "No seed");
+ this.macFactory = ValidateUtils.checkNotNull(BuiltinMacs.fromFactoryName(factoryName), "Unknown MAC: %s", factoryName);
+ this.expected = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, expected);
+ }
+
+ @Parameters(name = "factory={1}, expected={2}, seed={0}")
+ public static Collection<Object[]> parameters() {
+ List<Object[]> ret = new ArrayList<>();
+ for (VectorTestData vector : Collections.unmodifiableList(
+ Arrays.asList(
+ ///////////////// Test Cases for HMAC-MD5 ///////////////////////
+ // see https://tools.ietf.org/html/rfc2202
+ new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 1
+ "9294727a3638bb1c13f48ef8158bfc9d"))),
+ new VectorTestData("Jefe", "what do ya want for nothing?",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 2
+ "750c783e6ab0b503eaa86e310a5db738"))),
+ new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 3
+ "56be34521d144c88dbb8c733f0e8b3f6"))),
+ /* TODO see why it fails
+ new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 4
+ "697eaf0aca3a3aea3a75164746ffaa79"))),
+ */
+ new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 5
+ "56461ef2342edc00f9bab995690efd4c"),
+ new Pair<>(BuiltinMacs.Constants.HMAC_MD5_96,
+ "56461ef2342edc00f9bab995"))),
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 6
+ "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd"))),
+ */
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_MD5, // test case 7
+ "6f630fad67cda0ee1fb1f562db3aa53e"))),
+ */
+ ///////////////// Test Cases for HMAC-SHA-1 ///////////////////////
+ // see https://tools.ietf.org/html/rfc2202
+ new VectorTestData("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b", false, "Hi There",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 1
+ "b617318655057264e28bc0b6fb378c8ef146be00"))),
+ new VectorTestData("Jefe", "what do ya want for nothing?",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 2
+ "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"))),
+ new VectorTestData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", false, repeat("dd", 50), false,
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 3
+ "125d7342b9ac11cd91a39af48aa17b4f63f175d3"))),
+ /* TODO see why it fails
+ new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 4
+ "4c9007f4026250c6bc8414f9bf50c86c2d7235da"))),
+ */
+ new VectorTestData("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c", false, "Test With Truncation",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 5
+ "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA1_96,
+ "4c1a03424b55e07fe7f27be1"))),
+ /* TODO see why this fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 6
+ "aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
+ */
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 7
+ "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04"),
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA1_96,
+ "4c1a03424b55e07fe7f27be1"))),
+ */
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 8
+ "aa4ae5e15272d00e95705637ce8a3b55ed402112"))),
+ */
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 80), false, "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ Arrays.asList(new Pair<>(BuiltinMacs.Constants.HMAC_SHA1, // test case 9
+ "e8e99d0f45237d786d6bbaa7965c7808bbff1a91"))),
+ */
+
+ ///////////////// Test Cases for HMAC-SHA-2 ///////////////////////
+ // see https://tools.ietf.org/html/rfc4231
+ new VectorTestData(repeat("0b", 20), false, "Hi There",
+ Arrays.asList( // test case 1
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7"),
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "87aa7cdea5ef619d4ff0b4241a1d6cb02379f4e2ce4ec2787ad0b30545e17cdedaa833b7d6b8a702038b274eaea3f4e4be9d914eeb61f1702e696c203a126854"))),
+ new VectorTestData("Jefe", "what do ya want for nothing?",
+ Arrays.asList( // test case 2
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"),
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737"))),
+ new VectorTestData(repeat("aa", 20), false, repeat("dd", 50), false,
+ Arrays.asList( // test case 3
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"),
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb"))),
+ new VectorTestData("0102030405060708090a0b0c0d0e0f10111213141516171819", false, repeat("cd", 50), false,
+ Arrays.asList( // test case 4
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"),
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd"))),
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("0c", 20), false, "Test With Truncation",
+ Arrays.asList( // test case 5
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "a3b6167473100ee06e0c796c2955552b"),
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "415fad6271580a531d4179bc891d87a6"))),
+ */
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 131), false, "Test Using Larger Than Block-Size Key - Hash Key First",
+ Arrays.asList( // test case 6
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"),
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598"))),
+ */
+
+ /* TODO see why it fails
+ new VectorTestData(repeat("aa", 131), false, "This is a test using a larger than block-size"
+ + " key and a larger than block-size data."
+ + " The key needs to be hashed before being used"
+ + " by the HMAC algorithm",
+ Arrays.asList( // test case 7
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_256,
+ "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"),
+ new Pair<>(BuiltinMacs.Constants.HMAC_SHA2_512,
+ "e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58")))
+ */
+
+ // mark end
+ new VectorTestData("", false, "", false, Collections.<Pair<String,String>>emptyList())))) {
+ for (Pair<String, String> tc : vector.getResults()) {
+ ret.add(new Object[]{vector, tc.getFirst(), tc.getSecond()});
+ }
+ }
+
+ return ret;
+ }
+
+ @Test
+ public void testStandardVectorMac() throws Exception {
+ Mac mac = macFactory.create();
+ mac.init(seed.getKey());
+ mac.update(seed.getData());
+
+ byte[] actual = new byte[mac.getBlockSize()];
+ mac.doFinal(actual);
+ assertArrayEquals("Mismatched results for actual=" + BufferUtils.printHex(BufferUtils.EMPTY_HEX_SEPARATOR, actual), expected, actual);
+ }
+
+ private static class VectorSeed {
+ private final byte[] key;
+ private final String keyString;
+ private final byte[] data;
+ private final String dataString;
+
+ VectorSeed(String key, String data) {
+ this.key = key.getBytes(StandardCharsets.UTF_8);
+ this.keyString = key;
+ this.data = data.getBytes(StandardCharsets.UTF_8);
+ this.dataString = data;
+ }
+
+ VectorSeed(String key, boolean useKeyString, String data) {
+ this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
+ this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
+ this.data = data.getBytes(StandardCharsets.UTF_8);
+ this.dataString = data;
+ }
+
+ VectorSeed(String key, boolean useKeyString, String data, boolean useDataString) {
+ this.key = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, key);
+ this.keyString = useKeyString ? new String(this.key, StandardCharsets.UTF_8) : key;
+ this.data = BufferUtils.decodeHex(BufferUtils.EMPTY_HEX_SEPARATOR, data);
+ this.dataString = useDataString ? new String(this.data, StandardCharsets.UTF_8) : data;
+ }
+
+ public byte[] getKey() {
+ return key.clone(); // clone to avoid inadvertent change
+ }
+
+ public String getKeyString() {
+ return keyString;
+ }
+
+ public byte[] getData() {
+ return data.clone(); // clone to avoid inadvertent change
+ }
+
+ public String getDataString() {
+ return dataString;
+ }
+
+ @Override
+ public String toString() {
+ return "key=" + trimToLength(getKeyString(), 32) + ", data=" + trimToLength(getDataString(), 32);
+ }
+
+ private static CharSequence trimToLength(CharSequence csq, int maxLen) {
+ if (GenericUtils.length(csq) <= maxLen) {
+ return csq;
+ }
+
+ return csq.subSequence(0, maxLen) + "...";
+ }
+ }
+
+ private static class VectorTestData extends VectorSeed {
+ private final Collection<Pair<String, String>> results;
+
+ VectorTestData(String key, String data, Collection<Pair<String, String>> results) {
+ super(key, data);
+ this.results = results;
+ }
+
+ VectorTestData(String key, boolean useKeyString, String data, Collection<Pair<String, String>> results) {
+ super(key, useKeyString, data);
+ this.results = results;
+ }
+
+ VectorTestData(String key, boolean useKeyString, String data, boolean useDataString, Collection<Pair<String, String>> results) {
+ super(key, useKeyString, data, useDataString);
+ this.results = results;
+ }
+
+ public Collection<Pair<String, String>> getResults() {
+ return results;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/test/java/org/apache/sshd/common/util/BufferTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/BufferTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/BufferTest.java
deleted file mode 100644
index 9f6fac3..0000000
--- a/sshd-core/src/test/java/org/apache/sshd/common/util/BufferTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.sshd.common.util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-
-import org.apache.sshd.common.util.buffer.Buffer;
-import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
-import org.apache.sshd.util.test.BaseTestSupport;
-import org.junit.FixMethodOrder;
-import org.junit.Test;
-import org.junit.runners.MethodSorters;
-
-@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class BufferTest extends BaseTestSupport {
-
- @Test
- public void testGetLong() throws Exception {
- long v = 1234567890123456789L;
- ByteArrayOutputStream stream = new ByteArrayOutputStream();
- new DataOutputStream(stream).writeLong(v);
- Buffer buffer = new ByteArrayBuffer(stream.toByteArray());
- assertEquals(v, buffer.getLong());
- }
-}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
new file mode 100644
index 0000000..d5b2d0f
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferTest.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.common.util.buffer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+
+import org.apache.sshd.common.util.buffer.Buffer;
+import org.apache.sshd.common.util.buffer.ByteArrayBuffer;
+import org.apache.sshd.util.test.BaseTestSupport;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runners.MethodSorters;
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class BufferTest extends BaseTestSupport {
+ public BufferTest() {
+ super();
+ }
+
+ @Test
+ public void testGetLong() throws Exception {
+ long expected = 1234567890123456789L;
+
+ try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
+ try (DataOutputStream ds = new DataOutputStream(stream)) {
+ ds.writeLong(expected);
+ }
+
+ Buffer buffer = new ByteArrayBuffer(stream.toByteArray());
+ assertEquals("Mismatched recovered value", expected, buffer.getLong());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java b/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
new file mode 100644
index 0000000..23a6599
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/common/util/buffer/BufferUtilsTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sshd.common.util.buffer;
+
+import java.nio.charset.StandardCharsets;
+
+import 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 BufferUtilsTest extends BaseTestSupport {
+ public BufferUtilsTest() {
+ super();
+ }
+
+ @Test
+ public void testHexEncodeDecode() {
+ String expValue = getClass().getName() + "#" + getCurrentTestName();
+ byte[] expData = expValue.getBytes(StandardCharsets.UTF_8);
+ for (char sep : new char[]{BufferUtils.EMPTY_HEX_SEPARATOR, ':'}) {
+ String hexData = BufferUtils.printHex(sep, expData);
+ byte[] actData = BufferUtils.decodeHex(sep, hexData);
+ String actValue = new String(actData, StandardCharsets.UTF_8);
+ String sepName = (BufferUtils.EMPTY_HEX_SEPARATOR == sep) ? "EMPTY" : Character.toString(sep);
+ outputDebugMessage("Decode(sep=%s) expected=%s, actual=%s", sepName, expValue, actValue);
+ assertArrayEquals("Mismatched result for sep='" + sepName + "'", expData, actData);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java b/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
index 2e7d32b..d56750b 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/BaseTestSupport.java
@@ -254,6 +254,19 @@ public abstract class BaseTestSupport extends Assert {
return sb.toString();
}
+ public static String repeat(CharSequence csq, int nTimes) {
+ if (GenericUtils.isEmpty(csq) || (nTimes <= 0)) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(nTimes * csq.length());
+ for (int index = 0; index < nTimes; index++) {
+ sb.append(csq);
+ }
+
+ return sb.toString();
+ }
+
public static List<Object[]> parameterize(Collection<?> params) {
if (GenericUtils.isEmpty(params)) {
return Collections.emptyList();
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/e0041fc6/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java b/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
new file mode 100644
index 0000000..0cb6ec4
--- /dev/null
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
@@ -0,0 +1,56 @@
+/*
+ * 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.util.test;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class OutputCountTrackingOutputStream extends FilterOutputStream {
+ protected long writeCount;
+
+ public OutputCountTrackingOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ super.write(b);
+ updateWriteCount(1L);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ super.write(b, off, len);
+ updateWriteCount(len);
+ }
+
+ public long getWriteCount() {
+ return writeCount;
+ }
+
+ protected long updateWriteCount(long delta) {
+ writeCount += delta;
+ return writeCount;
+ }
+}