http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java new file mode 100644 index 0000000..0c8f43c --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/HostConfigEntryTest.java @@ -0,0 +1,325 @@ +/* + * 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.client.config.hosts; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +public class HostConfigEntryTest extends JUnitTestSupport { + public HostConfigEntryTest() { + super(); + } + + @Test + public void testNegatingPatternOverridesAll() { + String testHost = "37.77.34.7"; + String[] elements = GenericUtils.split(testHost, '.'); + StringBuilder sb = new StringBuilder(testHost.length() + Byte.SIZE); + List<HostPatternValue> patterns = new ArrayList<>(elements.length + 1); + // all wildcard patterns are not negated - only the actual host + patterns.add(HostPatternsHolder.toPattern(Character.toString(HostPatternsHolder.NEGATION_CHAR_PATTERN) + testHost)); + + for (int i = 0; i < elements.length; i++) { + sb.setLength(0); + + for (int j = 0; j < elements.length; j++) { + if (j > 0) { + sb.append('.'); + } + if (i == j) { + sb.append(HostPatternsHolder.WILDCARD_PATTERN); + } else { + sb.append(elements[j]); + } + } + + patterns.add(HostPatternsHolder.toPattern(sb)); + } + + for (int index = 0; index < patterns.size(); index++) { + assertFalse("Unexpected match for " + patterns, HostPatternsHolder.isHostMatch(testHost, 0, patterns)); + Collections.shuffle(patterns); + } + } + + @Test + public void testHostWildcardPatternMatching() { + String pkgName = getClass().getPackage().getName(); + String[] elements = GenericUtils.split(pkgName, '.'); + StringBuilder sb = new StringBuilder(pkgName.length() + Long.SIZE + 1).append(HostPatternsHolder.WILDCARD_PATTERN); + for (int index = elements.length - 1; index >= 0; index--) { + sb.append('.').append(elements[index]); + } + + String value = sb.toString(); + HostPatternValue pp = HostPatternsHolder.toPattern(value); + Pattern pattern = pp.getPattern(); + String domain = value.substring(1); // chomp the wildcard prefix + for (String host : new String[] { + getClass().getSimpleName(), + getCurrentTestName(), + getClass().getSimpleName() + "-" + getCurrentTestName(), + getClass().getSimpleName() + "." + getCurrentTestName(), + }) { + sb.setLength(0); // start from scratch + sb.append(host).append(domain); + + testCaseInsensitivePatternMatching(sb.toString(), pattern, true); + } + } + + @Test + public void testIPAddressWildcardPatternMatching() { + StringBuilder sb = new StringBuilder().append("10.0.0."); + int sbLen = sb.length(); + + Pattern pattern = HostPatternsHolder.toPattern(sb.append(HostPatternsHolder.WILDCARD_PATTERN)).getPattern(); + for (int v = 0; v <= 255; v++) { + sb.setLength(sbLen); // start from scratch + sb.append(v); + + String address = sb.toString(); + assertTrue("No match for " + address, HostPatternsHolder.isHostMatch(address, pattern)); + } + } + + @Test + public void testHostSingleCharPatternMatching() { + String value = getCurrentTestName(); + StringBuilder sb = new StringBuilder(value); + for (boolean restoreOriginal : new boolean[] {true, false}) { + for (int index = 0; index < value.length(); index++) { + sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN); + testCaseInsensitivePatternMatching(value, HostPatternsHolder.toPattern(sb.toString()).getPattern(), true); + if (restoreOriginal) { + sb.setCharAt(index, value.charAt(index)); + } + } + } + } + + @Test + public void testIPAddressSingleCharPatternMatching() { + StringBuilder sb = new StringBuilder().append("10.0.0."); + int sbLen = sb.length(); + + for (int v = 0; v <= 255; v++) { + sb.setLength(sbLen); // start from scratch + sb.append(v); + + String address = sb.toString(); + // replace the added digits with single char pattern + for (int index = sbLen; index < sb.length(); index++) { + sb.setCharAt(index, HostPatternsHolder.SINGLE_CHAR_PATTERN); + } + + String pattern = sb.toString(); + HostPatternValue pp = HostPatternsHolder.toPattern(pattern); + assertTrue("No match for " + address + " on pattern=" + pattern, HostPatternsHolder.isHostMatch(address, 0, Collections.singletonList(pp))); + } + } + + @Test + public void testIsValidPatternChar() { + for (char ch = '\0'; ch <= ' '; ch++) { + assertFalse("Unexpected valid character (0x" + Integer.toHexString(ch & 0xFF) + ")", HostPatternsHolder.isValidPatternChar(ch)); + } + + for (char ch = 'a'; ch <= 'z'; ch++) { + assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch)); + } + + for (char ch = 'A'; ch <= 'Z'; ch++) { + assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch)); + } + + for (char ch = '0'; ch <= '9'; ch++) { + assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch)); + } + + for (char ch : new char[] {'-', '_', '.', HostPatternsHolder.SINGLE_CHAR_PATTERN, HostPatternsHolder.WILDCARD_PATTERN}) { + assertTrue("Valid character not recognized: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch)); + } + + for (char ch : new char[] { + '(', ')', '{', '}', '[', ']', '@', + '#', '$', '^', '&', '%', '~', '<', '>', + ',', '/', '\\', '\'', '"', ':', ';' + }) { + assertFalse("Unexpected valid character: " + Character.toString(ch), HostPatternsHolder.isValidPatternChar(ch)); + } + + for (char ch = 0x7E; ch <= 0xFF; ch++) { + assertFalse("Unexpected valid character (0x" + Integer.toHexString(ch & 0xFF) + ")", HostPatternsHolder.isValidPatternChar(ch)); + } + } + + @Test + public void testResolvePort() { + final int originalPort = Short.MAX_VALUE; + final int preferredPort = 7365; + assertEquals("Mismatched entry port preference", + preferredPort, HostConfigEntry.resolvePort(originalPort, preferredPort)); + + for (int entryPort : new int[] {-1, 0}) { + assertEquals("Non-preferred original port for entry port=" + entryPort, + originalPort, HostConfigEntry.resolvePort(originalPort, entryPort)); + } + } + + @Test + public void testResolveUsername() { + final String originalUser = getCurrentTestName(); + final String preferredUser = getClass().getSimpleName(); + assertSame("Mismatched entry user preference", + preferredUser, HostConfigEntry.resolveUsername(originalUser, preferredUser)); + + for (String entryUser : new String[] {null, ""}) { + assertSame("Non-preferred original user for entry user='" + entryUser + "'", + originalUser, HostConfigEntry.resolveUsername(originalUser, entryUser)); + } + } + + @Test + public void testReadSimpleHostsConfigEntries() throws IOException { + validateHostConfigEntries(readHostConfigEntries()); + } + + @Test + public void testReadGlobalHostsConfigEntries() throws IOException { + List<HostConfigEntry> entries = validateHostConfigEntries(readHostConfigEntries()); + assertTrue("Not enough entries read", GenericUtils.size(entries) > 1); + + // global entry MUST be 1st one + HostConfigEntry globalEntry = entries.get(0); + assertEquals("Mismatched global entry pattern", HostPatternsHolder.ALL_HOSTS_PATTERN, globalEntry.getHost()); + + for (int index = 1; index < entries.size(); index++) { + HostConfigEntry entry = entries.get(index); + assertFalse("No target host for " + entry, GenericUtils.isEmpty(entry.getHostName())); + assertTrue("No target port for " + entry, entry.getPort() > 0); + assertFalse("No username for " + entry, GenericUtils.isEmpty(entry.getUsername())); + assertFalse("No identities for " + entry, GenericUtils.isEmpty(entry.getIdentities())); + assertFalse("No properties for " + entry, GenericUtils.isEmpty(entry.getProperties())); + } + } + + @Test + public void testReadMultipleHostPatterns() throws IOException { + List<HostConfigEntry> entries = validateHostConfigEntries(readHostConfigEntries()); + assertEquals("Mismatched number of entries", 1, GenericUtils.size(entries)); + assertEquals("Mismatched number of patterns", 3, GenericUtils.size(entries.get(0).getPatterns())); + } + + @Test + public void testResolveIdentityFilePath() throws Exception { + final String hostValue = getClass().getSimpleName(); + final int portValue = 7365; + final String userValue = getCurrentTestName(); + + Exception err = null; + for (String pattern : new String[] { + "~/.ssh/%h.key", + "%d/.ssh/%h.key", + "/home/%u/.ssh/id_rsa_%p", + "/home/%u/.ssh/id_%r_rsa", + "/home/%u/.ssh/%h/%l.key" + }) { + try { + String result = HostConfigEntry.resolveIdentityFilePath(pattern, hostValue, portValue, userValue); + System.out.append('\t').append(pattern).append(" => ").println(result); + } catch (Exception e) { + System.err.append("Failed (").append(e.getClass().getSimpleName()) + .append(") to process pattern=").append(pattern) + .append(": ").println(e.getMessage()); + err = e; + } + } + + if (err != null) { + throw err; + } + } + + @Test + public void testFindBestMatch() { + final String hostValue = getCurrentTestName(); + HostConfigEntry expected = new HostConfigEntry(hostValue, hostValue, 7365, hostValue); + List<HostConfigEntry> matches = new ArrayList<>(); + matches.add(new HostConfigEntry(HostPatternsHolder.ALL_HOSTS_PATTERN, + getClass().getSimpleName(), Short.MAX_VALUE, getClass().getSimpleName())); + matches.add(new HostConfigEntry(hostValue + Character.toString(HostPatternsHolder.WILDCARD_PATTERN), + getClass().getSimpleName(), Byte.MAX_VALUE, getClass().getSimpleName())); + matches.add(expected); + + for (int index = 0; index < matches.size(); index++) { + HostConfigEntry actual = HostConfigEntry.findBestMatch(matches); + assertSame("Mismatched best match for " + matches, expected, actual); + Collections.shuffle(matches); + } + } + + private static <C extends Collection<HostConfigEntry>> C validateHostConfigEntries(C entries) { + assertFalse("No entries", GenericUtils.isEmpty(entries)); + + for (HostConfigEntry entry : entries) { + assertFalse("No pattern for " + entry, GenericUtils.isEmpty(entry.getHost())); + assertFalse("No extra properties for " + entry, GenericUtils.isEmpty(entry.getProperties())); + } + + return entries; + } + + private List<HostConfigEntry> readHostConfigEntries() throws IOException { + return readHostConfigEntries(getCurrentTestName() + ".config.txt"); + } + + private List<HostConfigEntry> readHostConfigEntries(String resourceName) throws IOException { + URL url = getClass().getResource(resourceName); + assertNotNull("Missing resource " + resourceName, url); + return HostConfigEntry.readHostConfigEntries(url); + } + + private static void testCaseInsensitivePatternMatching(String value, Pattern pattern, boolean expected) { + for (int index = 0; index < value.length(); index++) { + boolean actual = HostPatternsHolder.isHostMatch(value, pattern); + assertEquals("Mismatched match result for " + value + " on pattern=" + pattern.pattern(), expected, actual); + value = shuffleCase(value); + } + } +}
http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java new file mode 100644 index 0000000..80d58d0 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/client/config/hosts/KnownHostHashValueTest.java @@ -0,0 +1,79 @@ +/* + * 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.client.config.hosts; + +import java.util.Arrays; +import java.util.Collection; + +import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.Parameterized.UseParametersRunnerFactory; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests +@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class) +@Category({ NoIoTestCase.class }) +public class KnownHostHashValueTest extends JUnitTestSupport { + private final String hostName; + private final String hashValue; + private final KnownHostHashValue hash; + + public KnownHostHashValueTest(String hostName, String hashValue) { + this.hostName = hostName; + this.hashValue = hashValue; + this.hash = KnownHostHashValue.parse(hashValue); + } + + @Parameters(name = "host={0}, hash={1}") + public static Collection<Object[]> parameters() { + return Arrays.<Object[]>asList( + (Object[]) new String[]{"192.168.1.61", "|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg="}); + } + + @Test + public void testDecodeEncode() { + assertSame("Mismatched digester", KnownHostHashValue.DEFAULT_DIGEST, hash.getDigester()); + assertEquals("Mismatched encoded form", hashValue, hash.toString()); + } + + @Test + public void testHostMatch() { + assertTrue("Specified host does not match", hash.isHostMatch(hostName)); + assertFalse("Unexpected host match", hash.isHostMatch(getCurrentTestName())); + } + + @Test + public void testCalculateHashValue() throws Exception { + byte[] expected = hash.getDigestValue(); + byte[] actual = KnownHostHashValue.calculateHashValue(hostName, hash.getDigester(), hash.getSaltValue()); + assertArrayEquals("Mismatched hash value", expected, actual); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java new file mode 100644 index 0000000..19375ec --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/BuiltinClientIdentitiesWatcherTest.java @@ -0,0 +1,161 @@ +/* + * 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.client.config.keys; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.EnumMap; +import java.util.Map; +import java.util.Objects; + +import org.apache.sshd.common.config.keys.BuiltinIdentities; +import org.apache.sshd.common.config.keys.FilePasswordProvider; +import org.apache.sshd.common.config.keys.KeyUtils; +import org.apache.sshd.common.keyprovider.KeyIdentityProvider; +import org.apache.sshd.common.keyprovider.KeyPairProvider; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.ValidateUtils; +import org.apache.sshd.common.util.io.IoUtils; +import org.apache.sshd.util.test.CommonTestSupportUtils; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +public class BuiltinClientIdentitiesWatcherTest extends JUnitTestSupport { + public BuiltinClientIdentitiesWatcherTest() { + super(); + } + + @Test + public void testMultipleFilesWatch() throws Exception { + KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(createTestHostKeyProvider()); + String keyType = ValidateUtils.checkNotNullAndNotEmpty(KeyUtils.getKeyType(identity), "Cannot determine identity key type"); + + Path dir = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName())); + Map<BuiltinIdentities, Path> locationsMap = new EnumMap<>(BuiltinIdentities.class); + Map<BuiltinIdentities, KeyPair> idsMap = new EnumMap<>(BuiltinIdentities.class); + for (BuiltinIdentities id : BuiltinIdentities.VALUES) { + Path idFile = dir.resolve(ClientIdentity.getIdentityFileName(id)); + Files.deleteIfExists(idFile); + assertNull("Multiple file mappings for " + id, locationsMap.put(id, idFile)); + assertNull("Multiple identity mappings for " + id, idsMap.put(id, KeyUtils.cloneKeyPair(keyType, identity))); + } + + ClientIdentityLoader loader = new ClientIdentityLoader() { + @Override + public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException { + BuiltinIdentities id = findIdentity(location); + assertNotNull("Invalid location: " + location, id); + return idsMap.get(id); + } + + @Override + public boolean isValidLocation(String location) throws IOException { + return findIdentity(location) != null; + } + + private BuiltinIdentities findIdentity(String location) { + if (GenericUtils.isEmpty(location)) { + return null; + } + + for (Map.Entry<BuiltinIdentities, Path> le : locationsMap.entrySet()) { + Path path = le.getValue(); + if (String.CASE_INSENSITIVE_ORDER.compare(location, path.toString()) == 0) { + return le.getKey(); + } + } + + return null; + } + }; + + Map<BuiltinIdentities, KeyPair> existing = new EnumMap<>(BuiltinIdentities.class); + KeyPairProvider watcher = new BuiltinClientIdentitiesWatcher(dir, false, loader, FilePasswordProvider.EMPTY, false); + testMultipleFilesWatch("No files", watcher, existing.values()); + + for (BuiltinIdentities id : BuiltinIdentities.VALUES) { + String phase = id + " + " + Objects.toString(existing.keySet()); + touchIdentityFile(locationsMap.get(id)); + existing.put(id, idsMap.get(id)); + + for (int index = 0; index < Byte.SIZE; index++) { + testMultipleFilesWatch(phase + "[" + index + "]", watcher, existing.values()); + } + } + + testMultipleFilesWatch("All files", watcher, existing.values()); + + for (BuiltinIdentities id : BuiltinIdentities.VALUES) { + existing.remove(id); + Files.deleteIfExists(locationsMap.get(id)); + String phase = Objects.toString(existing.keySet()) + " - " + id; + + for (int index = 0; index < Byte.SIZE; index++) { + testMultipleFilesWatch(phase + "[" + index + "]", watcher, existing.values()); + } + } + } + + private static void touchIdentityFile(Path idFile) throws IOException { + OpenOption[] options = IoUtils.EMPTY_OPEN_OPTIONS; + if (Files.exists(idFile, IoUtils.EMPTY_LINK_OPTIONS)) { + options = new OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.APPEND}; + } + + try (OutputStream out = Files.newOutputStream(idFile, options)) { + out.write(new Date(System.currentTimeMillis()).toString().getBytes(StandardCharsets.UTF_8)); + out.write('\n'); + } + } + + private static void testMultipleFilesWatch(String phase, KeyIdentityProvider watcher, Collection<? extends KeyPair> expected) { + Iterable<KeyPair> keys = watcher.loadKeys(); + Collection<KeyPair> actual = new ArrayList<>(); + for (KeyPair kp : keys) { + actual.add(kp); + } + assertEquals(phase + ": mismatched sizes", GenericUtils.size(expected), GenericUtils.size(actual)); + + if (!GenericUtils.isEmpty(expected)) { + for (KeyPair kp : expected) { + assertTrue(phase + ": missing key", actual.contains(kp)); + } + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java new file mode 100644 index 0000000..144a3b0 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityFileWatcherTest.java @@ -0,0 +1,121 @@ +/* + * 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.client.config.keys; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.util.Date; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.sshd.common.config.keys.FilePasswordProvider; +import org.apache.sshd.common.util.io.IoUtils; +import org.apache.sshd.util.test.CommonTestSupportUtils; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +public class ClientIdentityFileWatcherTest extends JUnitTestSupport { + public ClientIdentityFileWatcherTest() { + super(); + } + + @Test + public void testIdentityReload() throws Exception { + Path dir = assertHierarchyTargetFolderExists(getTempTargetRelativeFile(getClass().getSimpleName())); + Path idFile = dir.resolve(getCurrentTestName() + ".pem"); + KeyPair identity = CommonTestSupportUtils.getFirstKeyPair(createTestHostKeyProvider()); + ClientIdentityLoader loader = new ClientIdentityLoader() { + @Override + public KeyPair loadClientIdentity(String location, FilePasswordProvider provider) throws IOException, GeneralSecurityException { + assertTrue("Invalid location: " + location, isValidLocation(location)); + return identity; + } + + @Override + public boolean isValidLocation(String location) throws IOException { + return Objects.equals(location, toString()); + } + + @Override + public String toString() { + return Objects.toString(idFile); + } + }; + + AtomicInteger reloadCount = new AtomicInteger(0); + ClientIdentityProvider idProvider = new ClientIdentityFileWatcher(idFile, loader, FilePasswordProvider.EMPTY, false) { + @Override + protected KeyPair reloadClientIdentity(Path path) throws IOException, GeneralSecurityException { + assertEquals("Mismatched client identity path", idFile, path); + reloadCount.incrementAndGet(); + return super.reloadClientIdentity(path); + } + }; + Files.deleteIfExists(idFile); + + testIdentityReload("Non-existing", reloadCount, idProvider, null, 0); + + touchIdentityFile(idFile); + for (int index = 1; index < Byte.SIZE; index++) { + testIdentityReload("Created iteration " + 1, reloadCount, idProvider, identity, 1); + } + + touchIdentityFile(idFile); + for (int index = 1; index < Byte.SIZE; index++) { + testIdentityReload("Modified iteration " + 1, reloadCount, idProvider, identity, 2); + } + } + + private static void touchIdentityFile(Path idFile) throws IOException { + OpenOption[] options = IoUtils.EMPTY_OPEN_OPTIONS; + if (Files.exists(idFile, IoUtils.EMPTY_LINK_OPTIONS)) { + options = new OpenOption[]{StandardOpenOption.WRITE, StandardOpenOption.APPEND}; + } + + try (OutputStream out = Files.newOutputStream(idFile, options)) { + out.write(new Date(System.currentTimeMillis()).toString().getBytes(StandardCharsets.UTF_8)); + out.write('\n'); + } + } + + private static void testIdentityReload( + String phase, Number reloadCount, ClientIdentityProvider provider, KeyPair expectedIdentity, int expectedCount) + throws Exception { + KeyPair actualIdentity = provider.getClientIdentity(); + assertSame(phase + ": mismatched identity", expectedIdentity, actualIdentity); + assertEquals(phase + ": mismatched re-load count", expectedCount, reloadCount.intValue()); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java new file mode 100644 index 0000000..bc2f02c --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/client/config/keys/ClientIdentityTest.java @@ -0,0 +1,100 @@ +/* + * 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.client.config.keys; + +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.security.KeyPair; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; +import java.util.Map; + +import org.apache.sshd.common.config.keys.BuiltinIdentities; +import org.apache.sshd.common.config.keys.IdentityUtils; +import org.apache.sshd.common.keyprovider.KeyIdentityProvider; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.io.IoUtils; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +public class ClientIdentityTest extends JUnitTestSupport { + public ClientIdentityTest() { + super(); + } + + @Test + public void testLoadClientIdentities() throws Exception { + Path resFolder = getTestResourcesFolder(); + LinkOption[] options = IoUtils.getLinkOptions(true); + Collection<BuiltinIdentities> expected = EnumSet.noneOf(BuiltinIdentities.class); + for (BuiltinIdentities type : BuiltinIdentities.VALUES) { + String fileName = ClientIdentity.getIdentityFileName(type); + Path file = resFolder.resolve(fileName); + if (!Files.exists(file, options)) { + System.out.println("Skip non-existing identity file " + file); + continue; + } + + if (!type.isSupported()) { + System.out.println("Skip unsupported identity file " + file); + continue; + } + + expected.add(type); + } + + Map<String, KeyPair> ids = ClientIdentity.loadDefaultIdentities( + resFolder, + false, // don't be strict + null, // none of the files is password protected + options); + assertEquals("Mismatched loaded ids count", GenericUtils.size(expected), GenericUtils.size(ids)); + + Collection<KeyPair> pairs = new ArrayList<>(ids.size()); + for (BuiltinIdentities type : BuiltinIdentities.VALUES) { + if (expected.contains(type)) { + KeyPair kp = ids.get(type.getName()); + assertNotNull("No key pair loaded for " + type, kp); + pairs.add(kp); + } + } + + KeyIdentityProvider provider = IdentityUtils.createKeyPairProvider(ids, true /* supported only */); + assertNotNull("No provider generated", provider); + + Iterable<KeyPair> keys = provider.loadKeys(); + for (KeyPair kp : keys) { + assertTrue("Unexpected loaded key: " + kp, pairs.remove(kp)); + } + + assertEquals("Not all pairs listed", 0, pairs.size()); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java new file mode 100644 index 0000000..1d123d3 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/SshConstantsTest.java @@ -0,0 +1,75 @@ +/* + * 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; + +import java.util.Collection; + +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +public class SshConstantsTest extends JUnitTestSupport { + public SshConstantsTest() { + super(); + } + + @Test + public void testGetDisconnectReason() { + for (int reason = SshConstants.SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT; reason <= SshConstants.SSH2_DISCONNECT_ILLEGAL_USER_NAME; reason++) { + String name = SshConstants.getDisconnectReasonName(reason); + assertTrue("Mismatched name for reason=" + reason + ": " + name, name.startsWith("SSH2_DISCONNECT_")); + } + } + + @Test + public void testGetOpenErrorName() { + for (int code = SshConstants.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; code <= SshConstants.SSH_OPEN_RESOURCE_SHORTAGE; code++) { + String name = SshConstants.getOpenErrorCodeName(code); + assertTrue("Mismatched name for code=" + code + ": " + name, name.startsWith("SSH_OPEN_")); + } + } + + @Test + public void testAmbiguousOpcodes() throws Exception { + 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.isAmbiguousOpcode(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.isAmbiguousOpcode(cmd)); + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java new file mode 100644 index 0000000..c0978d4 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/VersionPropertiesTest.java @@ -0,0 +1,45 @@ +/* + * 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; + +import java.util.Map; + +import org.apache.sshd.common.config.VersionProperties; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class VersionPropertiesTest extends JUnitTestSupport { + public VersionPropertiesTest() { + super(); + } + + @Test + public void testNonEmptyProperties() { + Map<?, ?> props = VersionProperties.getVersionProperties(); + assertTrue(GenericUtils.isNotEmpty(props)); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.java new file mode 100644 index 0000000..6611702 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES192CTRTest.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.cipher; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class AES192CTRTest extends BaseCipherTest { + public AES192CTRTest() { + super(); + } + + @Test + public void testEncryptDecrypt() throws Exception { + // for AES 256 bits we need the JCE unlimited strength policy + ensureKeySizeSupported(16, 24, "AES", "AES/CTR/NoPadding"); + testEncryptDecrypt(BuiltinCiphers.aes192ctr); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.java new file mode 100644 index 0000000..dd39fd4 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/AES256CBCTest.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.cipher; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class AES256CBCTest extends BaseCipherTest { + public AES256CBCTest() { + super(); + } + + @Test + public void testEncryptDecrypt() throws Exception { + // for AES 256 bits we need the JCE unlimited strength policy + ensureKeySizeSupported(16, 32, "AES", "AES/CBC/NoPadding"); + testEncryptDecrypt(BuiltinCiphers.aes256cbc); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java new file mode 100644 index 0000000..1c37449 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR128Test.java @@ -0,0 +1,39 @@ +/* + * 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.cipher; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class ARCFOUR128Test extends BaseCipherTest { + public ARCFOUR128Test() { + super(); + } + + @Test + public void testEncryptDecrypt() throws Exception { + testEncryptDecrypt(BuiltinCiphers.arcfour128); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.java new file mode 100644 index 0000000..5511e0f --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ARCFOUR256Test.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.cipher; + +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class ARCFOUR256Test extends BaseCipherTest { + public ARCFOUR256Test() { + super(); + } + + @Test + public void testEncryptDecrypt() throws Exception { + // for RC4 256 bits we need the JCE unlimited strength policy + ensureKeySizeSupported(32, "ARCFOUR", "RC4"); + testEncryptDecrypt(BuiltinCiphers.arcfour256); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java new file mode 100644 index 0000000..514cba4 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/BaseCipherTest.java @@ -0,0 +1,95 @@ +/* + * 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.cipher; + +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; + +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.cipher.Cipher.Mode; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.Assume; +import org.junit.experimental.categories.Category; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@Category({ NoIoTestCase.class }) +public abstract class BaseCipherTest extends JUnitTestSupport { + protected BaseCipherTest() { + super(); + } + + protected void ensureKeySizeSupported(int bsize, String algorithm, String transformation) throws GeneralSecurityException { + try { + javax.crypto.Cipher cipher = SecurityUtils.getCipher(transformation); + byte[] key = new byte[bsize]; + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm)); + } catch (GeneralSecurityException e) { + if (e instanceof InvalidKeyException) { // NOTE: assumption violations are NOT test failures... + Assume.assumeTrue(algorithm + "/" + transformation + "[" + bsize + "] N/A", false); + } + + throw e; + } + } + + protected void ensureKeySizeSupported(int ivsize, int bsize, String algorithm, String transformation) throws GeneralSecurityException { + try { + javax.crypto.Cipher cipher = SecurityUtils.getCipher(transformation); + byte[] key = new byte[bsize]; + byte[] iv = new byte[ivsize]; + cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm), new IvParameterSpec(iv)); + } catch (GeneralSecurityException e) { + if (e instanceof InvalidKeyException) { + Assume.assumeTrue(algorithm + "/" + transformation + "[" + bsize + "/" + ivsize + "]", false /* force exception */); + } + + throw e; + } + } + + protected void testEncryptDecrypt(NamedFactory<Cipher> factory) throws Exception { + String facName = factory.getName(); + Cipher enc = factory.create(); + int keySize = enc.getBlockSize(); + int ivSize = enc.getIVSize(); + byte[] key = new byte[keySize]; + byte[] iv = new byte[ivSize]; + enc.init(Mode.Encrypt, key, iv); + + byte[] expected = facName.getBytes(StandardCharsets.UTF_8); + byte[] workBuf = expected.clone(); // need to clone since the cipher works in-line + enc.update(workBuf, 0, workBuf.length); + + Cipher dec = factory.create(); + dec.init(Mode.Decrypt, key, iv); + byte[] actual = workBuf.clone(); + dec.update(actual, 0, actual.length); + + assertArrayEquals(facName, expected, actual); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java new file mode 100644 index 0000000..2294606 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/cipher/ECCurvesTest.java @@ -0,0 +1,107 @@ +/* + * 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.cipher; + +import java.util.EnumSet; +import java.util.Set; + +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +public class ECCurvesTest extends JUnitTestSupport { + public ECCurvesTest() { + super(); + } + + @Test + public void testFromName() { + for (ECCurves expected : ECCurves.VALUES) { + String name = expected.getName(); + for (int index = 0; index < name.length(); index++) { + ECCurves actual = ECCurves.fromCurveName(name); + assertSame(name, expected, actual); + name = shuffleCase(name); + } + } + } + + @Test + public void testAllNamesListed() { + Set<ECCurves> listed = EnumSet.noneOf(ECCurves.class); + for (String name : ECCurves.NAMES) { + ECCurves c = ECCurves.fromCurveName(name); + assertNotNull("No curve for listed name=" + name, c); + assertTrue("Duplicated listed name: " + name, listed.add(c)); + } + + assertEquals("Mismatched listed vs. values", ECCurves.VALUES, listed); + } + + @Test + public void testFromKeySize() { + for (ECCurves expected : ECCurves.VALUES) { + String name = expected.getName(); + ECCurves actual = ECCurves.fromCurveSize(expected.getKeySize()); + assertSame(name, expected, actual); + } + } + + @Test + public void testFromCurveParameters() { + for (ECCurves expected : ECCurves.VALUES) { + String name = expected.getName(); + ECCurves actual = ECCurves.fromCurveParameters(expected.getParameters()); + assertSame(name, expected, actual); + } + } + + @Test + public void testFromKeyType() { + for (ECCurves expected : ECCurves.VALUES) { + String keyType = expected.getKeyType(); + for (int index = 0; index < keyType.length(); index++) { + ECCurves actual = ECCurves.fromKeyType(keyType); + assertSame(keyType, expected, actual); + keyType = shuffleCase(keyType); + } + } + } + + @Test + public void testAllKeyTypesListed() { + Set<ECCurves> listed = EnumSet.noneOf(ECCurves.class); + for (String name : ECCurves.KEY_TYPES) { + ECCurves c = ECCurves.fromKeyType(name); + assertNotNull("No curve for listed key type=" + name, c); + assertTrue("Duplicated listed key type: " + name, listed.add(c)); + } + + assertEquals("Mismatched listed vs. values", ECCurves.VALUES, listed); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java b/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java new file mode 100644 index 0000000..3d1ed00 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/compression/BuiltinCompressionsTest.java @@ -0,0 +1,171 @@ +/* + * 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.compression; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.NamedResource; +import org.apache.sshd.common.compression.BuiltinCompressions.ParseResult; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.MethodSorters; +import org.mockito.Mockito; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +public class BuiltinCompressionsTest extends JUnitTestSupport { + public BuiltinCompressionsTest() { + super(); + } + + @Test + public void testFromFactoryName() { + for (BuiltinCompressions expected : BuiltinCompressions.VALUES) { + String name = expected.getName(); + + for (int index = 0; index < name.length(); index++) { + BuiltinCompressions actual = BuiltinCompressions.fromFactoryName(name); + assertSame(name, expected, actual); + name = shuffleCase(name); + } + } + } + + @Test + public void testAllConstantsCovered() throws Exception { + Set<BuiltinCompressions> avail = EnumSet.noneOf(BuiltinCompressions.class); + Field[] fields = BuiltinCompressions.Constants.class.getFields(); + for (Field f : fields) { + String name = (String) f.get(null); + BuiltinCompressions value = BuiltinCompressions.fromFactoryName(name); + assertNotNull("No match found for " + name, value); + assertTrue(name + " re-specified", avail.add(value)); + } + + assertEquals("Incomplete coverage", BuiltinCompressions.VALUES, avail); + } + + @Test + public void testParseCompressionsList() { + List<String> builtin = NamedResource.getNameList(BuiltinCompressions.VALUES); + List<String> unknown = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), getCurrentTestName()); + Random rnd = new Random(); + for (int index = 0; index < (builtin.size() + unknown.size()); index++) { + Collections.shuffle(builtin, rnd); + Collections.shuffle(unknown, rnd); + + List<String> weavedList = new ArrayList<>(builtin.size() + unknown.size()); + for (int bIndex = 0, uIndex = 0; (bIndex < builtin.size()) || (uIndex < unknown.size());) { + boolean useBuiltin = false; + if (bIndex < builtin.size()) { + useBuiltin = uIndex >= unknown.size() || rnd.nextBoolean(); + } + + if (useBuiltin) { + weavedList.add(builtin.get(bIndex)); + bIndex++; + } else if (uIndex < unknown.size()) { + weavedList.add(unknown.get(uIndex)); + uIndex++; + } + } + + String fullList = GenericUtils.join(weavedList, ','); + ParseResult result = BuiltinCompressions.parseCompressionsList(fullList); + List<String> parsed = NamedResource.getNameList(result.getParsedFactories()); + List<String> missing = result.getUnsupportedFactories(); + + // makes sure not only that the contents are the same but also the order + assertListEquals(fullList + "[parsed]", builtin, parsed); + assertListEquals(fullList + "[unsupported]", unknown, missing); + } + } + + @Test + public void testResolveFactoryOnBuiltinValues() { + for (NamedFactory<Compression> expected : BuiltinCompressions.VALUES) { + String name = expected.getName(); + NamedFactory<Compression> actual = BuiltinCompressions.resolveFactory(name); + assertSame(name, expected, actual); + } + } + + @Test + public void testNotAllowedToRegisterBuiltinFactories() { + for (CompressionFactory expected : BuiltinCompressions.VALUES) { + try { + BuiltinCompressions.registerExtension(expected); + fail("Unexpected success for " + expected.getName()); + } catch (IllegalArgumentException e) { + // expected - ignored + } + } + } + + @Test(expected = IllegalArgumentException.class) + public void testNotAllowedToOverrideRegisteredFactories() { + CompressionFactory expected = Mockito.mock(CompressionFactory.class); + Mockito.when(expected.getName()).thenReturn(getCurrentTestName()); + + String name = expected.getName(); + try { + for (int index = 1; index <= Byte.SIZE; index++) { + BuiltinCompressions.registerExtension(expected); + assertEquals("Unexpected success at attempt #" + index, 1, index); + } + } finally { + BuiltinCompressions.unregisterExtension(name); + } + } + + @Test + public void testResolveFactoryOnRegisteredExtension() { + CompressionFactory expected = Mockito.mock(CompressionFactory.class); + Mockito.when(expected.getName()).thenReturn(getCurrentTestName()); + + String name = expected.getName(); + try { + assertNull("Extension already registered", BuiltinCompressions.resolveFactory(name)); + BuiltinCompressions.registerExtension(expected); + + NamedFactory<Compression> actual = BuiltinCompressions.resolveFactory(name); + assertSame("Mismatched resolved instance", expected, actual); + } finally { + NamedFactory<Compression> actual = BuiltinCompressions.unregisterExtension(name); + assertSame("Mismatched unregistered instance", expected, actual); + assertNull("Extension not un-registered", BuiltinCompressions.resolveFactory(name)); + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java new file mode 100644 index 0000000..0466d95 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/config/TimeValueConfigTest.java @@ -0,0 +1,57 @@ +/* + * 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.config; + +import java.util.concurrent.TimeUnit; + +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runners.MethodSorters; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Category({ NoIoTestCase.class }) +public class TimeValueConfigTest extends JUnitTestSupport { + public TimeValueConfigTest() { + super(); + } + + @Test + public void testDurationOf() { + Object[] values = { + "600", TimeUnit.SECONDS.toMillis(600L), + "10m", TimeUnit.MINUTES.toMillis(10L), + "1h30m", TimeUnit.MINUTES.toMillis(90L), + "2d", TimeUnit.DAYS.toMillis(2L), + "3w", TimeUnit.DAYS.toMillis(3L * 7L) + }; + for (int index = 0; index < values.length; index += 2) { + String s = (String) values[index]; + Number expected = (Number) values[index + 1]; + long actual = TimeValueConfig.durationOf(s); + assertEquals(s, expected.longValue(), actual); + } + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java new file mode 100644 index 0000000..d912998 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/AuthorizedKeyEntryLoginOptionsParseTest.java @@ -0,0 +1,124 @@ +/* + * 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.config.keys; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; +import org.apache.sshd.util.test.JUnitTestSupport; +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; +import org.junit.runners.Parameterized.UseParametersRunnerFactory; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests +@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class) +public class AuthorizedKeyEntryLoginOptionsParseTest extends JUnitTestSupport { + private final String value; + private final String loginPart; + private final String keyPart; + private final Map<String, String> options; + + public AuthorizedKeyEntryLoginOptionsParseTest(String value, String loginPart, String keyPart, Map<String, String> options) { + this.value = value; + this.loginPart = loginPart; + this.keyPart = keyPart; + this.options = options; + } + + @Parameters(name = "{0}") + public static List<Object[]> parameters() { + List<Object[]> params = new ArrayList<>(); + addData(params, "ssh-rsa AAAAB2...19Q==", "[email protected]", "from=\"*.sales.example.net,!pc.sales.example.net\""); + addData(params, "ssh-dss AAAAC3...51R==", "example.net", "command=\"dump /home\"", "no-pty", "no-port-forwarding"); + addData(params, "ssh-dss AAAAB5...21S==", "", "permitopen=\"192.0.2.1:80\"", "permitopen=\"192.0.2.2:25\""); + addData(params, "ssh-rsa AAAA...==", "[email protected]", "tunnel=\"0\"", "command=\"sh /etc/netstart tun0\""); + addData(params, "ssh-rsa AAAA1C8...32Tv==", "[email protected]", "!restrict", "command=\"uptime\""); + addData(params, "ssh-rsa AAAA1f8...IrrC5==", "[email protected]", "restrict", "!pty", "command=\"nethack\""); + return params; + } + + private static void addData(List<Object[]> params, String keyData, String comment, String... comps) { + StringBuilder sb = new StringBuilder(); + + Map<String, String> optionsMap = new HashMap<>(); + for (String c : comps) { + if (sb.length() > 0) { + sb.append(','); + } + sb.append(c); + + int pos = c.indexOf('='); + if (pos > 0) { + String name = c.substring(0, pos); + String value = GenericUtils.stripQuotes(c.substring(pos + 1)).toString(); + String prev = optionsMap.put(name, value); + if (prev != null) { + optionsMap.put(name, prev + "," + value); + } + } else { + optionsMap.put(c, Boolean.toString(c.charAt(0) != AuthorizedKeyEntry.BOOLEAN_OPTION_NEGATION_INDICATOR)); + } + } + + int pos = sb.length(); + + sb.append(' ').append(keyData); + if (GenericUtils.isNotEmpty(comment)) { + sb.append(' ').append(comment); + } + + String value = sb.toString(); + params.add(new Object[] {value, value.substring(0, pos), value.substring(pos + 1), optionsMap}); + } + + @Test + public void testResolveEntryComponents() { + Map.Entry<String, String> actual = AuthorizedKeyEntry.resolveEntryComponents(value); + assertNotNull(value, actual); + assertEquals("login(" + value + ")", loginPart, actual.getKey()); + assertEquals("remainder(" + value + ")", keyPart, actual.getValue()); + } + + @Test + public void testParseLoginOptions() { + Map<String, String> parsed = AuthorizedKeyEntry.parseLoginOptions(loginPart); + options.forEach((key, expected) -> { + String actual = parsed.get(key); + assertEquals(key, expected, actual); + }); + assertEquals("Mismatched size", options.size(), parsed.size()); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + value + "]"; + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java new file mode 100644 index 0000000..4735846 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/BuiltinIdentitiesTest.java @@ -0,0 +1,116 @@ +/* + * 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.config.keys; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.util.List; + +import org.apache.sshd.common.util.security.SecurityUtils; +import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.Parameterized.UseParametersRunnerFactory; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests +@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class) +@Category({ NoIoTestCase.class }) +public class BuiltinIdentitiesTest extends JUnitTestSupport { + private final BuiltinIdentities expected; + + public BuiltinIdentitiesTest(BuiltinIdentities expected) { + this.expected = expected; + } + + @Parameters(name = "{0}") + public static List<Object[]> parameters() { + return parameterize(BuiltinIdentities.VALUES); + } + + @BeforeClass // Dirty hack around the parameterized run + public static void testAllConstantsCovered() throws Exception { + Field[] fields = BuiltinIdentities.Constants.class.getFields(); + for (Field f : fields) { + int mods = f.getModifiers(); + if (!Modifier.isStatic(mods)) { + continue; + } + + if (!Modifier.isFinal(mods)) { + continue; + } + + Class<?> type = f.getType(); + if (!String.class.isAssignableFrom(type)) { + continue; + } + + String name = f.getName(); + String value = (String) f.get(null); + BuiltinIdentities id = BuiltinIdentities.fromName(value); + assertNotNull("No match found for field " + name + "=" + value, id); + } + } + + @Test + public void testFromName() { + String name = expected.getName(); + for (int index = 0, count = name.length(); index < count; index++) { + assertSame(name, expected, BuiltinIdentities.fromName(name)); + name = shuffleCase(name); + } + } + + @Test + public void testFromAlgorithm() { + String algorithm = expected.getAlgorithm(); + for (int index = 0, count = algorithm.length(); index < count; index++) { + assertSame(algorithm, expected, BuiltinIdentities.fromAlgorithm(algorithm)); + algorithm = shuffleCase(algorithm); + } + } + + @Test + public void testFromKey() throws GeneralSecurityException { + Assume.assumeTrue("Unsupported built-in identity", expected.isSupported()); + KeyPairGenerator gen = SecurityUtils.getKeyPairGenerator(expected.getAlgorithm()); + KeyPair kp = gen.generateKeyPair(); + outputDebugMessage("Checking built-in identity: %s", expected); + assertSame(expected + "[pair]", expected, BuiltinIdentities.fromKeyPair(kp)); + assertSame(expected + "[public]", expected, BuiltinIdentities.fromKey(kp.getPublic())); + assertSame(expected + "[private]", expected, BuiltinIdentities.fromKey(kp.getPrivate())); + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/10de190e/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java ---------------------------------------------------------------------- diff --git a/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java new file mode 100644 index 0000000..eedbfe7 --- /dev/null +++ b/sshd-common/src/test/java/org/apache/sshd/common/config/keys/KeyRandomArtTest.java @@ -0,0 +1,117 @@ +/* + * 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.config.keys; + +import java.security.KeyPair; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.apache.sshd.common.cipher.ECCurves; +import org.apache.sshd.common.util.GenericUtils; +import org.apache.sshd.common.util.security.SecurityUtils; +import org.apache.sshd.util.test.CommonTestSupportUtils; +import org.apache.sshd.util.test.JUnit4ClassRunnerWithParametersFactory; +import org.apache.sshd.util.test.JUnitTestSupport; +import org.apache.sshd.util.test.NoIoTestCase; +import org.junit.AfterClass; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; +import org.junit.runners.MethodSorters; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.junit.runners.Parameterized.UseParametersRunnerFactory; + +/** + * @author <a href="mailto:[email protected]">Apache MINA SSHD Project</a> + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(Parameterized.class) // see https://github.com/junit-team/junit/wiki/Parameterized-tests +@UseParametersRunnerFactory(JUnit4ClassRunnerWithParametersFactory.class) +@Category({ NoIoTestCase.class }) +public class KeyRandomArtTest extends JUnitTestSupport { + private static final Collection<KeyPair> KEYS = new LinkedList<>(); + + private final String algorithm; + private final int keySize; + private final KeyPair keyPair; + + public KeyRandomArtTest(String algorithm, int keySize) throws Exception { + this.algorithm = algorithm; + this.keySize = keySize; + this.keyPair = CommonTestSupportUtils.generateKeyPair(algorithm, keySize); + KEYS.add(this.keyPair); + } + + @Parameters(name = "algorithm={0}, key-size={1}") + public static List<Object[]> parameters() { + List<Object[]> params = new ArrayList<>(); + for (int keySize : RSA_SIZES) { + params.add(new Object[]{KeyUtils.RSA_ALGORITHM, keySize}); + } + + for (int keySize : DSS_SIZES) { + params.add(new Object[]{KeyUtils.DSS_ALGORITHM, keySize}); + } + + if (SecurityUtils.isECCSupported()) { + for (ECCurves curve : ECCurves.VALUES) { + params.add(new Object[]{KeyUtils.EC_ALGORITHM, curve.getKeySize()}); + } + } + + if (SecurityUtils.isEDDSACurveSupported()) { + for (int keySize : ED25519_SIZES) { + params.add(new Object[]{SecurityUtils.EDDSA, keySize}); + } + } + return params; + } + + @AfterClass + public static void dumpAllArts() throws Exception { + KeyRandomArt.combine(System.out, ' ', () -> KEYS); + } + + @Test + public void testRandomArtString() throws Exception { + KeyRandomArt art = new KeyRandomArt(keyPair.getPublic()); + assertEquals("Mismatched algorithm", algorithm, art.getAlgorithm()); + assertEquals("Mismatched key size", keySize, art.getKeySize()); + + String s = art.toString(); + String[] lines = GenericUtils.split(s, '\n'); + assertEquals("Mismatched lines count", KeyRandomArt.FLDSIZE_Y + 2, lines.length); + + for (int index = 0; index < lines.length; index++) { + String l = lines[index]; + if ((l.length() > 0) && (l.charAt(l.length() - 1) == '\r')) { + l = l.substring(0, l.length() - 1); + lines[index] = l; + } + System.out.append('\t').println(l); + + assertTrue("Mismatched line length #" + (index + 1) + ": " + l.length(), l.length() >= (KeyRandomArt.FLDSIZE_X + 2)); + } + } +}
