diff --git a/.gitignore b/.gitignore index cc50e6a7b..8f0cd5d75 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ .settings/ # Output dirs -out/ target/ classes/ build/ diff --git a/.travis.yml b/.travis.yml index 246dcd989..72119fb48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,30 +1,5 @@ language: java -dist: trusty -sudo: required - -services: - - docker - +sudo: false jdk: + - oraclejdk7 - oraclejdk8 - - openjdk8 - - oraclejdk9 - -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ - -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - -before_install: - - pip install --user codecov - -script: - - ./gradlew check - - ./gradlew integrationTest - -after_success: -- codecov diff --git a/README.adoc b/README.adoc index e5b698423..858d397f8 100644 --- a/README.adoc +++ b/README.adoc @@ -1,14 +1,11 @@ = sshj - SSHv2 library for Java Jeroen van Erp :sshj_groupid: com.hierynomus -:sshj_version: 0.23.0 +:sshj_version: 0.21.1 :source-highlighter: pygments -image:https://api.bintray.com/packages/hierynomus/maven/sshj/images/download.svg[link="https://bintray.com/hierynomus/maven/sshj/_latestVersion"] image:https://travis-ci.org/hierynomus/sshj.svg?branch=master[link="https://travis-ci.org/hierynomus/sshj"] image:https://api.codacy.com/project/badge/Grade/14a0a316bb9149739b5ea26dbfa8da8a["Codacy code quality", link="https://www.codacy.com/app/jeroen_2/sshj?utm_source=github.com&utm_medium=referral&utm_content=hierynomus/sshj&utm_campaign=Badge_Grade"] -image:https://codecov.io/gh/hierynomus/sshj/branch/master/graph/badge.svg["codecov", link="https://codecov.io/gh/hierynomus/sshj"] -image:http://www.javadoc.io/badge/com.hierynomus/sshj.svg?color=blue["JavaDocs", link="http://www.javadoc.io/doc/com.hierynomus/sshj"] image:https://maven-badges.herokuapp.com/maven-central/com.hierynomus/sshj/badge.svg["Maven Central",link="https://maven-badges.herokuapp.com/maven-central/com.hierynomus/sshj"] image:https://javadoc-emblem.rhcloud.com/doc/com.hierynomus/sshj/badge.svg["Javadoc",link="http://www.javadoc.io/doc/com.hierynomus/sshj"] @@ -78,7 +75,7 @@ key exchange:: `diffie-hellman-group16-sha256`, `diffie-hellman-group16-sha384@ssh.com`, `diffie-hellman-group16-sha512@ssh.com`, `diffie-hellman-group18-sha512@ssh.com` signatures:: - `ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384`, `ecdsa-sha2-nistp521`, `ssh-ed25519` + `ssh-rsa`, `ssh-dss`, `ecdsa-sha2-nistp256`, `ssh-ed25519` mac:: `hmac-md5`, `hmac-md5-96`, `hmac-sha1`, `hmac-sha1-96`, `hmac-sha2-256`, `hmac-sha2-512` @@ -107,16 +104,7 @@ Google Group: http://groups.google.com/group/sshj-users Fork away! == Release history -SSHJ 0.23.0 (2017-10-13):: -* Merged https://github.com/hierynomus/sshj/pulls/372[#372]: Upgrade to 'net.i2p.crypto:eddsa:0.2.0' -* Fixed https://github.com/hierynomus/sshj/issues/355[#355] and https://github.com/hierynomus/sshj/issues/354[#354]: Correctly decode signature bytes -* Fixed https://github.com/hierynomus/sshj/issues/365[#365]: Added support for new-style OpenSSH fingerprints of server keys -* Fixed https://github.com/hierynomus/sshj/issues/356[#356]: Fixed key type detection for ECDSA public keys -* Made SSHJ Java9 compatible -SSHJ 0.22.0 (2017-08-24):: -* Fixed https://github.com/hierynomus/sshj/pulls/341[#341]: Fixed path walking during recursive copy -* Merged https://github.com/hierynomus/sshj/pulls/338[#338]: Added ConsolePasswordFinder to read password from stdin -* Merged https://github.com/hierynomus/sshj/pulls/336[#336]: Added support for ecdsa-sha2-nistp384 and ecdsa-sha2-nistp521 signatures +SSHJ 0.22.0 (2017-??-??):: * Fixed https://github.com/hierynomus/sshj/issues/331[#331]: Added support for wildcards in known_hosts file SSHJ 0.21.1 (2017-04-25):: * Merged https://github.com/hierynomus/sshj/pulls/322[#322]: Fix regression from 40f956b (invalid length parameter on outputstream) diff --git a/build.gradle b/build.gradle index 81b1f30b6..5a883ebee 100644 --- a/build.gradle +++ b/build.gradle @@ -1,32 +1,22 @@ import java.text.SimpleDateFormat -import com.bmuschko.gradle.docker.tasks.container.* -import com.bmuschko.gradle.docker.tasks.image.* plugins { id "java" id "groovy" - id "jacoco" id "osgi" id "maven-publish" - id "com.bmuschko.docker-remote-api" version "3.2.1" - id 'pl.allegro.tech.build.axion-release' version '1.8.1' + id "org.ajoberstar.release-opinion" version "1.4.2" id "com.github.hierynomus.license" version "0.12.1" id "com.jfrog.bintray" version "1.7" - id 'ru.vyarus.java-lib' version '1.0.5' - // id 'ru.vyarus.pom' version '1.0.3' + id 'ru.vyarus.pom' version '1.0.3' id 'ru.vyarus.github-info' version '1.1.0' - id 'ru.vyarus.animalsniffer' version '1.4.2' } group = "com.hierynomus" - defaultTasks "build" repositories { mavenCentral() - maven { - url "https://dl.bintray.com/mockito/maven/" - } } sourceCompatibility = 1.6 @@ -34,21 +24,19 @@ targetCompatibility = 1.6 configurations.compile.transitive = false -def bouncycastleVersion = "1.57" +def bouncycastleVersion = "1.56" dependencies { - signature 'org.codehaus.mojo.signature:java16:1.1@signature' - compile "org.slf4j:slf4j-api:1.7.7" compile "org.bouncycastle:bcprov-jdk15on:$bouncycastleVersion" compile "org.bouncycastle:bcpkix-jdk15on:$bouncycastleVersion" compile "com.jcraft:jzlib:1.1.3" - compile "net.i2p.crypto:eddsa:0.2.0" + compile "net.i2p.crypto:eddsa:0.1.0" testCompile "junit:junit:4.11" testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' - testCompile "org.mockito:mockito-core:2.9.2" + testCompile "org.mockito:mockito-core:2.8.47" testCompile "org.apache.sshd:sshd-core:1.2.0" testRuntime "ch.qos.logback:logback-classic:1.1.2" testCompile 'org.glassfish.grizzly:grizzly-http-server:2.3.17' @@ -65,19 +53,14 @@ license { excludes(['**/djb/Curve25519.java', '**/sshj/common/Base64.java']) } -scmVersion { - tag { - prefix = 'v' - versionSeparator = '' - } - hooks { - pre 'fileUpdate', [file: 'README.adoc', pattern: { v, c -> /:sshj_version: .*/}, replacement: { v, c -> ":sshj_version: $v" }] - pre 'commit' +if (project.file('.git').isDirectory()) { + release { + grgit = org.ajoberstar.grgit.Grgit.open(project.projectDir) } +} else { + version = "0.0.0-no.git" } -project.version = scmVersion.version - // This disables the pedantic doclint feature of JDK8 if (JavaVersion.current().isJava8Compatible()) { tasks.withType(Javadoc) { @@ -95,6 +78,7 @@ task writeSshjVersionProperties { } jar.dependsOn writeSshjVersionProperties + jar { manifest { // please see http://bnd.bndtools.org/chapters/390-wrapping.html @@ -115,7 +99,14 @@ jar { } } -sourcesJar { +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} + +task sourcesJar(type: Jar) { + classifier = 'sources' + from sourceSets.main.allSource manifest { attributes( // Add the needed OSGI attributes @@ -128,27 +119,6 @@ sourcesJar { } } -configurations { - integrationTestCompile.extendsFrom testCompile - integrationTestRuntime.extendsFrom testRuntime -} - -sourceSets { - integrationTest { - groovy { - compileClasspath += sourceSets.main.output + sourceSets.test.output - runtimeClasspath += sourceSets.main.output + sourceSets.test.output - srcDir file('src/itest/groovy') - } - resources.srcDir file('src/itest/resources') - } -} - -task integrationTest(type: Test) { - testClassesDirs = sourceSets.integrationTest.output.classesDirs - classpath = sourceSets.integrationTest.runtimeClasspath -} - tasks.withType(Test) { testLogging { exceptionFormat = 'full' @@ -215,12 +185,21 @@ pom { } } +publishing.publications { + Sshj(MavenPublication) { + from components.java + artifact sourcesJar + artifact javadocJar + } +} + + if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey")) { bintray { user = project.property("bintrayUsername") key = project.property("bintrayApiKey") publish = true - publications = ["maven"] + publications = ["Sshj"] pkg { repo = "maven" name = project.name @@ -247,38 +226,4 @@ if (project.hasProperty("bintrayUsername") && project.hasProperty("bintrayApiKey } } -jacocoTestReport { - reports { - xml.enabled true - html.enabled true - } -} - - -task buildItestImage(type: DockerBuildImage) { - inputDir = file('src/itest/docker-image') - tag = 'sshj/sshd-itest' -} - -task createItestContainer(type: DockerCreateContainer) { - dependsOn buildItestImage - targetImageId { buildItestImage.getImageId() } - portBindings = ['2222:22'] -} - -task startItestContainer(type: DockerStartContainer) { - dependsOn createItestContainer - targetContainerId { createItestContainer.getContainerId() } -} - -task stopItestContainer(type: DockerStopContainer) { - targetContainerId { createItestContainer.getContainerId() } -} - -project.tasks.integrationTest.dependsOn(startItestContainer) -project.tasks.integrationTest.finalizedBy(stopItestContainer) - -project.tasks.release.dependsOn([project.tasks.integrationTest, project.tasks.build]) -project.tasks.release.finalizedBy(project.tasks.bintrayUpload) -project.tasks.jacocoTestReport.dependsOn(project.tasks.test) -project.tasks.check.dependsOn(project.tasks.jacocoTestReport) +project.tasks.release.dependsOn([project.tasks.build, project.tasks.bintrayUpload]) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 318b9e3e5..a02b60f27 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip diff --git a/src/itest/docker-image/Dockerfile b/src/itest/docker-image/Dockerfile deleted file mode 100644 index b306ac8ca..000000000 --- a/src/itest/docker-image/Dockerfile +++ /dev/null @@ -1,16 +0,0 @@ -FROM sickp/alpine-sshd:7.5 - -ADD id_rsa.pub /home/sshj/.ssh/authorized_keys - -ADD test-container/ssh_host_ecdsa_key /etc/ssh/ssh_host_ecdsa_key -ADD test-container/ssh_host_ecdsa_key.pub /etc/ssh/ssh_host_ecdsa_key.pub - -RUN \ - echo "root:smile" | chpasswd && \ - adduser -D -s /bin/ash sshj && \ - passwd -u sshj && \ - chmod 600 /home/sshj/.ssh/authorized_keys && \ - chmod 600 /etc/ssh/ssh_host_ecdsa_key && \ - chmod 644 /etc/ssh/ssh_host_ecdsa_key.pub && \ - chown -R sshj:sshj /home/sshj - diff --git a/src/itest/docker-image/id_rsa.pub b/src/itest/docker-image/id_rsa.pub deleted file mode 100644 index 6c50ee239..000000000 --- a/src/itest/docker-image/id_rsa.pub +++ /dev/null @@ -1 +0,0 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAoZ9l6Tkm2aL1tSBy2yw4xU5s8BE9MfqS/4J7DzvsYJxF6oQmTIjmStuhH/CT7UjuDtKXdXZUsIhKtafiizxGO8kHSzKDeitpth2RSr8ddMzZKyD6RNs7MfsgjA3UTtrrSrCXEY6O43S2cnuJrWzkPxtwxaQ3zOvDbS2tiulzyq0VzYmuhA/a4CyuQtJBuu+P2oqmu6pU/VB6IzONpvBvYbNPsH1WDmP7zko5wHPihXPCliztspKxS4DRtOZ7BGXyvg44UmIy0Kf4jOkaBV/eCCA4qH7ZHz71/5ceMOpszPcNOEmLGGYhwI+P3OuGMpkrSAv1f8IY6R8spZNncP6UaQ== no-passphrase diff --git a/src/itest/docker-image/test-container/ssh_host_ecdsa_key b/src/itest/docker-image/test-container/ssh_host_ecdsa_key deleted file mode 100644 index cac0cbe71..000000000 --- a/src/itest/docker-image/test-container/ssh_host_ecdsa_key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIOpOBFjqe0hjK/hs4WZ3dZqnzanq1L3/JbvV1TCkbe4ToAoGCCqGSM49 -AwEHoUQDQgAEVzkrS7Yj0nXML7A3mE08YDthfBR/ZbyYJDIq1vTzcqs6KTaCT529 -swNXWLHO+mbHviZcRiI57ULXHZ1emom/Jw== ------END EC PRIVATE KEY----- diff --git a/src/itest/docker-image/test-container/ssh_host_ecdsa_key.pub b/src/itest/docker-image/test-container/ssh_host_ecdsa_key.pub deleted file mode 100644 index 9b7f7995f..000000000 --- a/src/itest/docker-image/test-container/ssh_host_ecdsa_key.pub +++ /dev/null @@ -1 +0,0 @@ -ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBFc5K0u2I9J1zC+wN5hNPGA7YXwUf2W8mCQyKtb083KrOik2gk+dvbMDV1ixzvpmx74mXEYiOe1C1x2dXpqJvyc= root@404b27be2bf4 \ No newline at end of file diff --git a/src/itest/groovy/com/hierynomus/sshj/IntegrationBaseSpec.groovy b/src/itest/groovy/com/hierynomus/sshj/IntegrationBaseSpec.groovy deleted file mode 100644 index 19ae629c8..000000000 --- a/src/itest/groovy/com/hierynomus/sshj/IntegrationBaseSpec.groovy +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C)2009 - SSHJ Contributors - * - * Licensed 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 com.hierynomus.sshj - -import net.schmizz.sshj.DefaultConfig -import net.schmizz.sshj.SSHClient -import net.schmizz.sshj.transport.verification.PromiscuousVerifier -import spock.lang.Specification - -class IntegrationBaseSpec extends Specification { - protected static final int DOCKER_PORT = 2222; - protected static final String USERNAME = "sshj"; - protected final static String SERVER_IP = System.getProperty("serverIP", "127.0.0.1"); - - protected static SSHClient getConnectedClient() throws IOException { - SSHClient sshClient = new SSHClient(new DefaultConfig()); - sshClient.addHostKeyVerifier(new PromiscuousVerifier()); - sshClient.connect(SERVER_IP, DOCKER_PORT); - - return sshClient; - } - -} diff --git a/src/itest/groovy/com/hierynomus/sshj/IntegrationSpec.groovy b/src/itest/groovy/com/hierynomus/sshj/IntegrationSpec.groovy deleted file mode 100644 index 16d3e004c..000000000 --- a/src/itest/groovy/com/hierynomus/sshj/IntegrationSpec.groovy +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C)2009 - SSHJ Contributors - * - * Licensed 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 com.hierynomus.sshj - -import net.schmizz.sshj.DefaultConfig -import net.schmizz.sshj.SSHClient -import net.schmizz.sshj.transport.TransportException -import net.schmizz.sshj.userauth.UserAuthException - -class IntegrationSpec extends IntegrationBaseSpec { - - def "should accept correct key"() { - given: - SSHClient sshClient = new SSHClient(new DefaultConfig()) - sshClient.addHostKeyVerifier("d3:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3") // test-containers/ssh_host_ecdsa_key's fingerprint - - when: - sshClient.connect(SERVER_IP, DOCKER_PORT) - - then: - sshClient.isConnected() - } - - def "should decline wrong key"() throws IOException { - given: - SSHClient sshClient = new SSHClient(new DefaultConfig()) - sshClient.addHostKeyVerifier("d4:6a:a9:52:05:ab:b5:48:dd:73:60:18:0c:3a:f0:a3") - - when: - sshClient.connect(SERVER_IP, DOCKER_PORT) - - then: - thrown(TransportException.class) - } - - def "should authenticate"() { - given: - SSHClient client = getConnectedClient() - - when: - client.authPublickey("sshj", "src/test/resources/id_rsa") - - then: - client.isAuthenticated() - } - - def "should not authenticate with wrong key"() { - given: - SSHClient client = getConnectedClient() - - when: - client.authPublickey("sshj", "src/test/resources/id_dsa") - - then: - thrown(UserAuthException.class) - !client.isAuthenticated() - } -} diff --git a/src/itest/groovy/com/hierynomus/sshj/sftp/FileWriteSpec.groovy b/src/itest/groovy/com/hierynomus/sshj/sftp/FileWriteSpec.groovy deleted file mode 100644 index d61187df4..000000000 --- a/src/itest/groovy/com/hierynomus/sshj/sftp/FileWriteSpec.groovy +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C)2009 - SSHJ Contributors - * - * Licensed 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 com.hierynomus.sshj.sftp - -import com.hierynomus.sshj.IntegrationBaseSpec -import net.schmizz.sshj.SSHClient -import net.schmizz.sshj.sftp.OpenMode -import net.schmizz.sshj.sftp.RemoteFile -import net.schmizz.sshj.sftp.SFTPClient - -import java.nio.charset.StandardCharsets - -import static org.codehaus.groovy.runtime.IOGroovyMethods.withCloseable - -class FileWriteSpec extends IntegrationBaseSpec { - - def "should append to file (GH issue #390)"() { - given: - SSHClient client = getConnectedClient() - client.authPublickey("sshj", "src/test/resources/id_rsa") - SFTPClient sftp = client.newSFTPClient() - def file = "/home/sshj/test.txt" - def initialText = "This is the initial text.\n".getBytes(StandardCharsets.UTF_16) - def appendText = "And here's the appended text.\n".getBytes(StandardCharsets.UTF_16) - - when: - withCloseable(sftp.open(file, EnumSet.of(OpenMode.WRITE, OpenMode.CREAT))) { RemoteFile initial -> - initial.write(0, initialText, 0, initialText.length) - } - - then: - withCloseable(sftp.open(file, EnumSet.of(OpenMode.READ))) { RemoteFile read -> - def bytes = new byte[initialText.length] - read.read(0, bytes, 0, bytes.length) - bytes == initialText - } - - when: - withCloseable(sftp.open(file, EnumSet.of(OpenMode.WRITE, OpenMode.APPEND))) { RemoteFile append -> - append.write(0, appendText, 0, appendText.length) - } - - then: - withCloseable(sftp.open(file, EnumSet.of(OpenMode.READ))) { RemoteFile read -> - def bytes = new byte[initialText.length + appendText.length] - read.read(0, bytes, 0, bytes.length) - Arrays.copyOfRange(bytes, 0, initialText.length) == initialText - Arrays.copyOfRange(bytes, initialText.length, initialText.length + appendText.length) == appendText - } - - cleanup: - sftp.close() - client.close() - } -} diff --git a/src/main/java/com/hierynomus/sshj/backport/Jdk7HttpProxySocket.java b/src/main/java/com/hierynomus/sshj/backport/Jdk7HttpProxySocket.java index 56ea256e6..a0d617258 100644 --- a/src/main/java/com/hierynomus/sshj/backport/Jdk7HttpProxySocket.java +++ b/src/main/java/com/hierynomus/sshj/backport/Jdk7HttpProxySocket.java @@ -15,12 +15,12 @@ */ package com.hierynomus.sshj.backport; -import net.schmizz.sshj.common.IOUtils; - import java.io.IOException; import java.io.InputStream; import java.net.*; +import net.schmizz.sshj.common.IOUtils; + public class Jdk7HttpProxySocket extends Socket { private Proxy httpProxy = null; diff --git a/src/main/java/com/hierynomus/sshj/signature/Ed25519PublicKey.java b/src/main/java/com/hierynomus/sshj/signature/Ed25519PublicKey.java index 7bad0d3c5..a9f49bf25 100644 --- a/src/main/java/com/hierynomus/sshj/signature/Ed25519PublicKey.java +++ b/src/main/java/com/hierynomus/sshj/signature/Ed25519PublicKey.java @@ -23,6 +23,8 @@ import java.util.Arrays; +import static net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable.CURVE_ED25519_SHA512; + /** * Our own extension of the EdDSAPublicKey that comes from ECC-25519, as that class does not implement equality. * The code uses the equality of the keys as an indicator whether they're the same during host key verification. @@ -32,7 +34,7 @@ public class Ed25519PublicKey extends EdDSAPublicKey { public Ed25519PublicKey(EdDSAPublicKeySpec spec) { super(spec); - EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName("Ed25519"); + EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512); if (!spec.getParams().getCurve().equals(ed25519.getCurve())) { throw new SSHRuntimeException("Cannot create Ed25519 Public Key from wrong spec"); } diff --git a/src/main/java/com/hierynomus/sshj/signature/SignatureEdDSA.java b/src/main/java/com/hierynomus/sshj/signature/SignatureEdDSA.java index 19a1da4ab..dee8f2752 100644 --- a/src/main/java/com/hierynomus/sshj/signature/SignatureEdDSA.java +++ b/src/main/java/com/hierynomus/sshj/signature/SignatureEdDSA.java @@ -16,16 +16,14 @@ package com.hierynomus.sshj.signature; import net.i2p.crypto.eddsa.EdDSAEngine; +import net.schmizz.sshj.common.Buffer; import net.schmizz.sshj.common.KeyType; import net.schmizz.sshj.common.SSHRuntimeException; -import net.schmizz.sshj.signature.AbstractSignature; import net.schmizz.sshj.signature.Signature; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.SignatureException; +import java.security.*; -public class SignatureEdDSA extends AbstractSignature { +public class SignatureEdDSA implements Signature { public static class Factory implements net.schmizz.sshj.common.Factory.Named { @Override @@ -39,18 +37,54 @@ public Signature create() { } } - SignatureEdDSA() { - super(getEngine()); - } + final EdDSAEngine engine; - private static EdDSAEngine getEngine() { + protected SignatureEdDSA() { try { - return new EdDSAEngine(MessageDigest.getInstance("SHA-512")); + engine = new EdDSAEngine(MessageDigest.getInstance("SHA-512")); } catch (NoSuchAlgorithmException e) { throw new SSHRuntimeException(e); } } + @Override + public void init(PublicKey pubkey, PrivateKey prvkey) { + try { + if (pubkey != null) { + engine.initVerify(pubkey); + } + + if (prvkey != null) { + engine.initSign(prvkey); + } + } catch (InvalidKeyException e) { + throw new SSHRuntimeException(e); + } + } + + @Override + public void update(byte[] H) { + update(H, 0, H.length); + } + + @Override + public void update(byte[] H, int off, int len) { + try { + engine.update(H, off, len); + } catch (SignatureException e) { + throw new SSHRuntimeException(e); + } + } + + @Override + public byte[] sign() { + try { + return engine.sign(); + } catch (SignatureException e) { + throw new SSHRuntimeException(e); + } + } + @Override public byte[] encode(byte[] signature) { return signature; @@ -59,9 +93,17 @@ public byte[] encode(byte[] signature) { @Override public boolean verify(byte[] sig) { try { - return signature.verify(extractSig(sig, "ssh-ed25519")); + Buffer.PlainBuffer plainBuffer = new Buffer.PlainBuffer(sig); + String algo = plainBuffer.readString(); + if (!"ssh-ed25519".equals(algo)) { + throw new SSHRuntimeException("Expected 'ssh-ed25519' key algorithm, but was: " + algo); + } + byte[] bytes = plainBuffer.readBytes(); + return engine.verify(bytes); } catch (SignatureException e) { throw new SSHRuntimeException(e); + } catch (Buffer.BufferException e) { + throw new SSHRuntimeException(e); } } } diff --git a/src/main/java/com/hierynomus/sshj/transport/cipher/BlockCiphers.java b/src/main/java/com/hierynomus/sshj/transport/cipher/BlockCiphers.java index 99cf09f34..ea7818dae 100644 --- a/src/main/java/com/hierynomus/sshj/transport/cipher/BlockCiphers.java +++ b/src/main/java/com/hierynomus/sshj/transport/cipher/BlockCiphers.java @@ -19,15 +19,14 @@ import net.schmizz.sshj.transport.cipher.Cipher; /** - * All BlockCiphers supported by SSH according to the following RFCs: + * All BlockCiphers supported by SSH according to the following RFCs * - * + * - https://tools.ietf.org/html/rfc4344#section-3.1 + * - https://tools.ietf.org/html/rfc4253#section-6.3 * - * Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are deprecated and scheduled to be removed. + * TODO: https://tools.ietf.org/html/rfc5647 + * + * Some of the Ciphers are still implemented in net.schmizz.sshj.transport.cipher.*. These are scheduled to be migrated to here. */ @SuppressWarnings("PMD.MethodNamingConventions") public class BlockCiphers { @@ -35,30 +34,9 @@ public class BlockCiphers { public static final String COUNTER_MODE = "CTR"; public static final String CIPHER_BLOCK_CHAINING_MODE = "CBC"; - public static Factory AES128CTR() { - return new Factory(16, 128, "aes128-ctr", "AES", COUNTER_MODE); - } - public static Factory AES192CTR() { - return new Factory(16, 192, "aes192-ctr", "AES", COUNTER_MODE); - } - public static Factory AES256CTR() { - return new Factory(16, 256, "aes256-ctr", "AES", COUNTER_MODE); - } - public static Factory AES128CBC() { - return new Factory(16, 128, "aes128-cbc", "AES", CIPHER_BLOCK_CHAINING_MODE); - } - public static Factory AES192CBC() { - return new Factory(16, 192, "aes192-cbc", "AES", CIPHER_BLOCK_CHAINING_MODE); - } - public static Factory AES256CBC() { - return new Factory(16, 256, "aes256-cbc", "AES", CIPHER_BLOCK_CHAINING_MODE); - } public static Factory BlowfishCTR() { return new Factory(8, 256, "blowfish-ctr", "Blowfish", COUNTER_MODE); } - public static Factory BlowfishCBC() { - return new Factory(8, 128, "blowfish-cbc", "Blowfish", CIPHER_BLOCK_CHAINING_MODE); - } public static Factory Twofish128CTR() { return new Factory(16, 128, "twofish128-ctr", "Twofish", COUNTER_MODE); } @@ -113,9 +91,6 @@ public static Factory Cast128CBC() { public static Factory TripleDESCTR() { return new Factory(8, 192, "3des-ctr", "DESede", COUNTER_MODE); } - public static Factory TripleDESCBC() { - return new Factory(8, 192, "3des-cbc", "DESede", CIPHER_BLOCK_CHAINING_MODE); - } /** Named factory for BlockCipher */ public static class Factory diff --git a/src/main/java/com/hierynomus/sshj/transport/kex/DHGroups.java b/src/main/java/com/hierynomus/sshj/transport/kex/DHGroups.java index d182e2a5d..3efbddd83 100644 --- a/src/main/java/com/hierynomus/sshj/transport/kex/DHGroups.java +++ b/src/main/java/com/hierynomus/sshj/transport/kex/DHGroups.java @@ -15,15 +15,14 @@ */ package com.hierynomus.sshj.transport.kex; -import net.schmizz.sshj.transport.digest.Digest; -import net.schmizz.sshj.transport.digest.SHA1; -import net.schmizz.sshj.transport.digest.SHA256; -import net.schmizz.sshj.transport.digest.SHA512; +import net.schmizz.sshj.transport.digest.*; import net.schmizz.sshj.transport.kex.KeyExchange; import java.math.BigInteger; import static net.schmizz.sshj.transport.kex.DHGroupData.*; +import static net.schmizz.sshj.transport.kex.DHGroupData.P16; +import static net.schmizz.sshj.transport.kex.DHGroupData.P18; /** * Factory methods for Diffie Hellmann KEX algorithms based on MODP groups / Oakley Groups diff --git a/src/main/java/com/hierynomus/sshj/transport/verification/KnownHostMatchers.java b/src/main/java/com/hierynomus/sshj/transport/verification/KnownHostMatchers.java index 08e5d8b78..2c52d96b1 100644 --- a/src/main/java/com/hierynomus/sshj/transport/verification/KnownHostMatchers.java +++ b/src/main/java/com/hierynomus/sshj/transport/verification/KnownHostMatchers.java @@ -135,7 +135,7 @@ private static class WildcardHostMatcher implements HostMatcher { private final Pattern pattern; public WildcardHostMatcher(String hostEntry) { - this.pattern = Pattern.compile("^" + hostEntry.replace("[", "\\[").replace("]", "\\]").replace(".", "\\.").replace("*", ".*").replace("?", ".") + "$"); + this.pattern = Pattern.compile(hostEntry.replace(".", "\\.").replace("*", ".*").replace("?", ".")); } @Override diff --git a/src/main/java/com/hierynomus/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java b/src/main/java/com/hierynomus/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java index 1e12badea..1d28f1487 100644 --- a/src/main/java/com/hierynomus/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java +++ b/src/main/java/com/hierynomus/sshj/userauth/keyprovider/OpenSSHKeyV1KeyFile.java @@ -15,6 +15,14 @@ */ package com.hierynomus.sshj.userauth.keyprovider; +import java.io.BufferedReader; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.PublicKey; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import net.i2p.crypto.eddsa.EdDSAPrivateKey; import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; @@ -23,14 +31,8 @@ import net.schmizz.sshj.userauth.keyprovider.BaseFileKeyProvider; import net.schmizz.sshj.userauth.keyprovider.FileKeyProvider; import net.schmizz.sshj.userauth.keyprovider.KeyFormat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.io.BufferedReader; -import java.io.IOException; -import java.security.GeneralSecurityException; -import java.security.KeyPair; -import java.security.PublicKey; +import static net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable.CURVE_ED25519_SHA512; /** * Reads a key file in the new OpenSSH format. @@ -153,6 +155,6 @@ private KeyPair readUnencrypted(final PlainBuffer keyBuffer, final PublicKey pub throw new IOException("Padding of key format contained wrong byte at position: " + i); } } - return new KeyPair(publicKey, new EdDSAPrivateKey(new EdDSAPrivateKeySpec(privKey, EdDSANamedCurveTable.getByName("Ed25519")))); + return new KeyPair(publicKey, new EdDSAPrivateKey(new EdDSAPrivateKeySpec(privKey, EdDSANamedCurveTable.getByName(CURVE_ED25519_SHA512)))); } } diff --git a/src/main/java/net/schmizz/concurrent/Event.java b/src/main/java/net/schmizz/concurrent/Event.java index d02cd9328..b5a449fb3 100644 --- a/src/main/java/net/schmizz/concurrent/Event.java +++ b/src/main/java/net/schmizz/concurrent/Event.java @@ -15,11 +15,11 @@ */ package net.schmizz.concurrent; -import net.schmizz.sshj.common.LoggerFactory; - import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; +import net.schmizz.sshj.common.LoggerFactory; + /** * An event can be set, cleared, or awaited, similar to Python's {@code threading.event}. The key difference is that a * waiter may be delivered an exception of parameterized type {@code T}. diff --git a/src/main/java/net/schmizz/concurrent/Promise.java b/src/main/java/net/schmizz/concurrent/Promise.java index 7511648d0..8e6cce3f6 100644 --- a/src/main/java/net/schmizz/concurrent/Promise.java +++ b/src/main/java/net/schmizz/concurrent/Promise.java @@ -15,7 +15,6 @@ */ package net.schmizz.concurrent; -import net.schmizz.sshj.common.LoggerFactory; import org.slf4j.Logger; import java.util.concurrent.TimeUnit; @@ -23,6 +22,8 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; +import net.schmizz.sshj.common.LoggerFactory; + /** * Represents promised data of the parameterized type {@code V} and allows waiting on it. An exception may also be * delivered to a waiter, and will be of the parameterized type {@code T}. diff --git a/src/main/java/net/schmizz/sshj/AndroidConfig.java b/src/main/java/net/schmizz/sshj/AndroidConfig.java index 9c230dd53..235d9fab9 100644 --- a/src/main/java/net/schmizz/sshj/AndroidConfig.java +++ b/src/main/java/net/schmizz/sshj/AndroidConfig.java @@ -15,8 +15,6 @@ */ package net.schmizz.sshj; -import com.hierynomus.sshj.signature.SignatureEdDSA; - import net.schmizz.sshj.common.SecurityUtils; import net.schmizz.sshj.signature.SignatureDSA; import net.schmizz.sshj.signature.SignatureRSA; @@ -30,18 +28,9 @@ public class AndroidConfig SecurityUtils.registerSecurityProvider("org.spongycastle.jce.provider.BouncyCastleProvider"); } - public AndroidConfig(){ - super(); - initKeyExchangeFactories(true); - initRandomFactory(true); - initFileKeyProviderFactories(true); - } - // don't add ECDSA protected void initSignatureFactories() { - setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory(), - // but add EdDSA - new SignatureEdDSA.Factory()); + setSignatureFactories(new SignatureRSA.Factory(), new SignatureDSA.Factory()); } @Override diff --git a/src/main/java/net/schmizz/sshj/ConfigImpl.java b/src/main/java/net/schmizz/sshj/ConfigImpl.java index 70df287cb..fce2cfda8 100644 --- a/src/main/java/net/schmizz/sshj/ConfigImpl.java +++ b/src/main/java/net/schmizz/sshj/ConfigImpl.java @@ -16,8 +16,8 @@ package net.schmizz.sshj; import net.schmizz.keepalive.KeepAliveProvider; -import net.schmizz.sshj.common.Factory; import net.schmizz.sshj.common.LoggerFactory; +import net.schmizz.sshj.common.Factory; import net.schmizz.sshj.signature.Signature; import net.schmizz.sshj.transport.cipher.Cipher; import net.schmizz.sshj.transport.compression.Compression; diff --git a/src/main/java/net/schmizz/sshj/DefaultConfig.java b/src/main/java/net/schmizz/sshj/DefaultConfig.java index b2400d6b8..256b4e317 100644 --- a/src/main/java/net/schmizz/sshj/DefaultConfig.java +++ b/src/main/java/net/schmizz/sshj/DefaultConfig.java @@ -15,12 +15,22 @@ */ package net.schmizz.sshj; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + +import org.slf4j.Logger; + import com.hierynomus.sshj.signature.SignatureEdDSA; import com.hierynomus.sshj.transport.cipher.BlockCiphers; import com.hierynomus.sshj.transport.cipher.StreamCiphers; import com.hierynomus.sshj.transport.kex.DHGroups; import com.hierynomus.sshj.transport.kex.ExtendedDHGroups; import com.hierynomus.sshj.userauth.keyprovider.OpenSSHKeyV1KeyFile; + import net.schmizz.keepalive.KeepAliveProvider; import net.schmizz.sshj.common.Factory; import net.schmizz.sshj.common.LoggerFactory; @@ -28,13 +38,26 @@ import net.schmizz.sshj.signature.SignatureDSA; import net.schmizz.sshj.signature.SignatureECDSA; import net.schmizz.sshj.signature.SignatureRSA; -import net.schmizz.sshj.transport.cipher.*; +import net.schmizz.sshj.transport.cipher.AES128CBC; +import net.schmizz.sshj.transport.cipher.AES128CTR; +import net.schmizz.sshj.transport.cipher.AES192CBC; +import net.schmizz.sshj.transport.cipher.AES192CTR; +import net.schmizz.sshj.transport.cipher.AES256CBC; +import net.schmizz.sshj.transport.cipher.AES256CTR; +import net.schmizz.sshj.transport.cipher.BlowfishCBC; +import net.schmizz.sshj.transport.cipher.Cipher; +import net.schmizz.sshj.transport.cipher.TripleDESCBC; import net.schmizz.sshj.transport.compression.NoneCompression; import net.schmizz.sshj.transport.kex.Curve25519SHA256; import net.schmizz.sshj.transport.kex.DHGexSHA1; import net.schmizz.sshj.transport.kex.DHGexSHA256; import net.schmizz.sshj.transport.kex.ECDHNistP; -import net.schmizz.sshj.transport.mac.*; +import net.schmizz.sshj.transport.mac.HMACMD5; +import net.schmizz.sshj.transport.mac.HMACMD596; +import net.schmizz.sshj.transport.mac.HMACSHA1; +import net.schmizz.sshj.transport.mac.HMACSHA196; +import net.schmizz.sshj.transport.mac.HMACSHA2256; +import net.schmizz.sshj.transport.mac.HMACSHA2512; import net.schmizz.sshj.transport.random.BouncyCastleRandom; import net.schmizz.sshj.transport.random.JCERandom; import net.schmizz.sshj.transport.random.SingletonRandomFactory; @@ -42,10 +65,6 @@ import net.schmizz.sshj.userauth.keyprovider.PKCS5KeyFile; import net.schmizz.sshj.userauth.keyprovider.PKCS8KeyFile; import net.schmizz.sshj.userauth.keyprovider.PuTTYKeyFile; -import org.slf4j.Logger; - -import java.io.IOException; -import java.util.*; /** * A {@link net.schmizz.sshj.Config} that is initialized as follows. Items marked with an asterisk are added to the config only if @@ -53,7 +72,9 @@ *

*