diff --git a/files-jsch/build.gradle b/files-jsch/build.gradle new file mode 100644 index 0000000..ac62149 --- /dev/null +++ b/files-jsch/build.gradle @@ -0,0 +1,4 @@ +dependencies { + testImplementation testLibs.testcontainers + testImplementation testLibs.testcontainers.junit.jupiter +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/AgentConnector.java b/files-jsch/src/main/java/com/jcraft/jsch/AgentConnector.java new file mode 100644 index 0000000..eb29f4a --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/AgentConnector.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface AgentConnector { + String getName(); + + boolean isAvailable(); + + void query(Buffer buffer) throws AgentProxyException; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/AgentIdentity.java b/files-jsch/src/main/java/com/jcraft/jsch/AgentIdentity.java new file mode 100644 index 0000000..3f83e4f --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/AgentIdentity.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class AgentIdentity implements Identity { + + private AgentProxy agent; + private byte[] blob; + private String comment; + private String algname; + + AgentIdentity(AgentProxy agent, byte[] blob, String comment) { + this.agent = agent; + this.blob = blob; + this.comment = comment; + algname = Util.byte2str((new Buffer(blob)).getString()); + } + + @Override + public boolean setPassphrase(byte[] passphrase) throws JSchException { + return true; + } + + @Override + public byte[] getPublicKeyBlob() { + return blob; + } + + @Override + public byte[] getSignature(byte[] data) { + return agent.sign(blob, data, null); + } + + @Override + public byte[] getSignature(byte[] data, String alg) { + return agent.sign(blob, data, alg); + } + + @Override + public String getAlgName() { + return algname; + } + + @Override + public String getName() { + return comment; + } + + @Override + public boolean isEncrypted() { + return false; + } + + @Override + public void clear() {} +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/AgentIdentityRepository.java b/files-jsch/src/main/java/com/jcraft/jsch/AgentIdentityRepository.java new file mode 100644 index 0000000..c4e2516 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/AgentIdentityRepository.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Vector; + +public class AgentIdentityRepository implements IdentityRepository { + + private AgentProxy agent; + + public AgentIdentityRepository(AgentConnector connector) { + this.agent = new AgentProxy(connector); + } + + @Override + public Vector getIdentities() { + return agent.getIdentities(); + } + + @Override + public boolean add(byte[] identity) { + return agent.addIdentity(identity); + } + + @Override + public boolean remove(byte[] blob) { + return agent.removeIdentity(blob); + } + + @Override + public void removeAll() { + agent.removeAllIdentities(); + } + + @Override + public String getName() { + return agent.getConnector().getName(); + } + + @Override + public int getStatus() { + if (agent.getConnector().isAvailable()) { + return RUNNING; + } else { + return NOTRUNNING; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/AgentProxy.java b/files-jsch/src/main/java/com/jcraft/jsch/AgentProxy.java new file mode 100644 index 0000000..a582d04 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/AgentProxy.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2012 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Vector; + +class AgentProxy { + + private static final byte SSH_AGENTC_REQUEST_RSA_IDENTITIES = 1; + private static final byte SSH_AGENT_RSA_IDENTITIES_ANSWER = 2; + private static final byte SSH_AGENTC_RSA_CHALLENGE = 3; + private static final byte SSH_AGENT_RSA_RESPONSE = 4; + private static final byte SSH_AGENT_FAILURE = 5; + private static final byte SSH_AGENT_SUCCESS = 6; + private static final byte SSH_AGENTC_ADD_RSA_IDENTITY = 7; + private static final byte SSH_AGENTC_REMOVE_RSA_IDENTITY = 8; + private static final byte SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES = 9; + + private static final byte SSH2_AGENTC_REQUEST_IDENTITIES = 11; + private static final byte SSH2_AGENT_IDENTITIES_ANSWER = 12; + private static final byte SSH2_AGENTC_SIGN_REQUEST = 13; + private static final byte SSH2_AGENT_SIGN_RESPONSE = 14; + private static final byte SSH2_AGENTC_ADD_IDENTITY = 17; + private static final byte SSH2_AGENTC_REMOVE_IDENTITY = 18; + private static final byte SSH2_AGENTC_REMOVE_ALL_IDENTITIES = 19; + + private static final byte SSH_AGENTC_ADD_SMARTCARD_KEY = 20; + private static final byte SSH_AGENTC_REMOVE_SMARTCARD_KEY = 21; + + private static final byte SSH_AGENTC_LOCK = 22; + private static final byte SSH_AGENTC_UNLOCK = 23; + + private static final byte SSH_AGENTC_ADD_RSA_ID_CONSTRAINED = 24; + private static final byte SSH2_AGENTC_ADD_ID_CONSTRAINED = 25; + private static final byte SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED = 26; + + private static final byte SSH_AGENT_CONSTRAIN_LIFETIME = 1; + private static final byte SSH_AGENT_CONSTRAIN_CONFIRM = 2; + + private static final byte SSH2_AGENT_FAILURE = 30; + + private static final byte SSH_COM_AGENT2_FAILURE = 102; + + // private static final byte SSH_AGENT_OLD_SIGNATURE = 0x1; + private static final int SSH_AGENT_RSA_SHA2_256 = 0x2; + private static final int SSH_AGENT_RSA_SHA2_512 = 0x4; + + private static final int MAX_AGENT_IDENTITIES = 2048; + + private final byte[] buf = new byte[1024]; + private final Buffer buffer = new Buffer(buf); + + private AgentConnector connector; + + AgentProxy(AgentConnector connector) { + this.connector = connector; + } + + synchronized Vector getIdentities() { + Vector identities = new Vector<>(); + + int required_size = 1 + 4; + buffer.reset(); + buffer.checkFreeSize(required_size); + buffer.putInt(required_size - 4); + buffer.putByte(SSH2_AGENTC_REQUEST_IDENTITIES); + + try { + connector.query(buffer); + } catch (AgentProxyException e) { + buffer.rewind(); + buffer.putByte(SSH_AGENT_FAILURE); + return identities; + } + + int rcode = buffer.getByte(); + + // System.out.println(rcode == SSH2_AGENT_IDENTITIES_ANSWER); + + if (rcode != SSH2_AGENT_IDENTITIES_ANSWER) { + return identities; + } + + int count = buffer.getInt(); + // System.out.println(count); + if (count <= 0 || count > MAX_AGENT_IDENTITIES) { + return identities; + } + + for (int i = 0; i < count; i++) { + byte[] blob = buffer.getString(); + String comment = Util.byte2str(buffer.getString()); + identities.add(new AgentIdentity(this, blob, comment)); + } + + return identities; + } + + synchronized byte[] sign(byte[] blob, byte[] data, String alg) { + int flags = 0x0; + if (alg != null) { + if (alg.equals("rsa-sha2-256")) { + flags = SSH_AGENT_RSA_SHA2_256; + } else if (alg.equals("rsa-sha2-512")) { + flags = SSH_AGENT_RSA_SHA2_512; + } + } + + int required_size = 1 + 4 * 4 + blob.length + data.length; + buffer.reset(); + buffer.checkFreeSize(required_size); + buffer.putInt(required_size - 4); + buffer.putByte(SSH2_AGENTC_SIGN_REQUEST); + buffer.putString(blob); + buffer.putString(data); + buffer.putInt(flags); + + try { + connector.query(buffer); + } catch (AgentProxyException e) { + buffer.rewind(); + buffer.putByte(SSH_AGENT_FAILURE); + } + + int rcode = buffer.getByte(); + + // System.out.println(rcode == SSH2_AGENT_SIGN_RESPONSE); + + if (rcode != SSH2_AGENT_SIGN_RESPONSE) { + return null; + } + + return buffer.getString(); + } + + synchronized boolean removeIdentity(byte[] blob) { + int required_size = 1 + 4 * 2 + blob.length; + buffer.reset(); + buffer.checkFreeSize(required_size); + buffer.putInt(required_size - 4); + buffer.putByte(SSH2_AGENTC_REMOVE_IDENTITY); + buffer.putString(blob); + + try { + connector.query(buffer); + } catch (AgentProxyException e) { + buffer.rewind(); + buffer.putByte(SSH_AGENT_FAILURE); + } + + int rcode = buffer.getByte(); + + // System.out.println(rcode == SSH_AGENT_SUCCESS); + + return rcode == SSH_AGENT_SUCCESS; + } + + synchronized void removeAllIdentities() { + int required_size = 1 + 4; + buffer.reset(); + buffer.checkFreeSize(required_size); + buffer.putInt(required_size - 4); + buffer.putByte(SSH2_AGENTC_REMOVE_ALL_IDENTITIES); + + try { + connector.query(buffer); + } catch (AgentProxyException e) { + buffer.rewind(); + buffer.putByte(SSH_AGENT_FAILURE); + } + + // int rcode = buffer.getByte(); + + // System.out.println(rcode == SSH_AGENT_SUCCESS); + } + + synchronized boolean addIdentity(byte[] identity) { + int required_size = 1 + 4 + identity.length; + buffer.reset(); + buffer.checkFreeSize(required_size); + buffer.putInt(required_size - 4); + buffer.putByte(SSH2_AGENTC_ADD_IDENTITY); + buffer.putByte(identity); + + try { + connector.query(buffer); + } catch (AgentProxyException e) { + buffer.rewind(); + buffer.putByte(SSH_AGENT_FAILURE); + } + + int rcode = buffer.getByte(); + + // System.out.println(rcode == SSH_AGENT_SUCCESS); + + return rcode == SSH_AGENT_SUCCESS; + } + + synchronized boolean isRunning() { + int required_size = 1 + 4; + buffer.reset(); + buffer.checkFreeSize(required_size); + buffer.putInt(required_size - 4); + buffer.putByte(SSH2_AGENTC_REQUEST_IDENTITIES); + + try { + connector.query(buffer); + } catch (AgentProxyException e) { + return false; + } + + int rcode = buffer.getByte(); + + // System.out.println(rcode == SSH2_AGENT_IDENTITIES_ANSWER); + + return rcode == SSH2_AGENT_IDENTITIES_ANSWER; + } + + synchronized AgentConnector getConnector() { + return connector; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/AgentProxyException.java b/files-jsch/src/main/java/com/jcraft/jsch/AgentProxyException.java new file mode 100644 index 0000000..b8e88c6 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/AgentProxyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class AgentProxyException extends Exception { + private static final long serialVersionUID = -1L; + + public AgentProxyException(String message) { + super(message); + } + + public AgentProxyException(String message, Throwable e) { + super(message, e); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Argon2.java b/files-jsch/src/main/java/com/jcraft/jsch/Argon2.java new file mode 100644 index 0000000..d826c85 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Argon2.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface Argon2 extends KDF { + public static final int ARGON2D = 0; + public static final int ARGON2I = 1; + public static final int ARGON2ID = 2; + public static final int V10 = 0x10; + public static final int V13 = 0x13; + + void init(byte[] salt, int iteration, int type, byte[] additional, byte[] secret, int memory, + int parallelism, int version) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/BCrypt.java b/files-jsch/src/main/java/com/jcraft/jsch/BCrypt.java new file mode 100644 index 0000000..c6b8cd9 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/BCrypt.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface BCrypt extends KDF { + void init(byte[] salt, int iteration) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Buffer.java b/files-jsch/src/main/java/com/jcraft/jsch/Buffer.java new file mode 100644 index 0000000..e57fef6 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Buffer.java @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class Buffer { + final byte[] tmp = new byte[4]; + byte[] buffer; + int index; + int s; + + public Buffer(int size) { + buffer = new byte[size]; + index = 0; + s = 0; + } + + public Buffer(byte[] buffer) { + this.buffer = buffer; + index = 0; + s = 0; + } + + public Buffer() { + this(1024 * 10 * 2); + } + + public void putByte(byte foo) { + buffer[index++] = foo; + } + + public void putByte(byte[] foo) { + putByte(foo, 0, foo.length); + } + + public void putByte(byte[] foo, int begin, int length) { + System.arraycopy(foo, begin, buffer, index, length); + index += length; + } + + public void putString(byte[] foo) { + putString(foo, 0, foo.length); + } + + public void putString(byte[] foo, int begin, int length) { + putInt(length); + putByte(foo, begin, length); + } + + public void putInt(int val) { + tmp[0] = (byte) (val >>> 24); + tmp[1] = (byte) (val >>> 16); + tmp[2] = (byte) (val >>> 8); + tmp[3] = (byte) (val); + System.arraycopy(tmp, 0, buffer, index, 4); + index += 4; + } + + public void putLong(long val) { + tmp[0] = (byte) (val >>> 56); + tmp[1] = (byte) (val >>> 48); + tmp[2] = (byte) (val >>> 40); + tmp[3] = (byte) (val >>> 32); + System.arraycopy(tmp, 0, buffer, index, 4); + tmp[0] = (byte) (val >>> 24); + tmp[1] = (byte) (val >>> 16); + tmp[2] = (byte) (val >>> 8); + tmp[3] = (byte) (val); + System.arraycopy(tmp, 0, buffer, index + 4, 4); + index += 8; + } + + void skip(int n) { + index += n; + } + + void putPad(int n) { + while (n > 0) { + buffer[index++] = (byte) 0; + n--; + } + } + + public void putMPInt(byte[] foo) { + int i = foo.length; + if ((foo[0] & 0x80) != 0) { + i++; + putInt(i); + putByte((byte) 0); + } else { + putInt(i); + } + putByte(foo); + } + + public int getLength() { + return index - s; + } + + public int getOffSet() { + return s; + } + + public void setOffSet(int s) { + this.s = s; + } + + public long getLong() { + long foo = getInt() & 0xffffffffL; + foo = ((foo << 32)) | (getInt() & 0xffffffffL); + return foo; + } + + public int getInt() { + int foo = getShort(); + foo = ((foo << 16) & 0xffff0000) | (getShort() & 0xffff); + return foo; + } + + public long getUInt() { + long foo = 0L; + long bar = 0L; + foo = getByte(); + foo = ((foo << 8) & 0xff00) | (getByte() & 0xff); + bar = getByte(); + bar = ((bar << 8) & 0xff00) | (getByte() & 0xff); + foo = ((foo << 16) & 0xffff0000) | (bar & 0xffff); + return foo; + } + + int getShort() { + int foo = getByte(); + foo = ((foo << 8) & 0xff00) | (getByte() & 0xff); + return foo; + } + + public int getByte() { + return (buffer[s++] & 0xff); + } + + public void getByte(byte[] foo) { + getByte(foo, 0, foo.length); + } + + void getByte(byte[] foo, int start, int len) { + System.arraycopy(buffer, s, foo, start, len); + s += len; + } + + public int getByte(int len) { + int foo = s; + s += len; + return foo; + } + + public byte[] getMPInt() { + int i = getInt(); // uint32 + if (i < 0 || // bigger than 0x7fffffff + i > 8 * 1024) { + // TODO: an exception should be thrown. + i = 8 * 1024; // the session will be broken, but working around OOME. + } + byte[] foo = new byte[i]; + getByte(foo, 0, i); + return foo; + } + + public byte[] getMPIntBits() { + int bits = getInt(); + int bytes = (bits + 7) / 8; + byte[] foo = new byte[bytes]; + getByte(foo, 0, bytes); + if ((foo[0] & 0x80) != 0) { + byte[] bar = new byte[foo.length + 1]; + bar[0] = 0; // ?? + System.arraycopy(foo, 0, bar, 1, foo.length); + foo = bar; + } + return foo; + } + + public byte[] getString() { + int i = getInt(); // uint32 + if (i < 0 || // bigger than 0x7fffffff + i > 256 * 1024) { + // TODO: an exception should be thrown. + i = 256 * 1024; // the session will be broken, but working around OOME. + } + byte[] foo = new byte[i]; + getByte(foo, 0, i); + return foo; + } + + byte[] getString(int[] start, int[] len) { + int i = getInt(); + start[0] = getByte(i); + len[0] = i; + return buffer; + } + + public void reset() { + index = 0; + s = 0; + } + + public void shift() { + if (s == 0) + return; + System.arraycopy(buffer, s, buffer, 0, index - s); + index = index - s; + s = 0; + } + + void rewind() { + s = 0; + } + + byte getCommand() { + return buffer[5]; + } + + // Hardcode this since we can't use dynamic Session value + private static final int buffer_margin = 32 + // maximum padding length + 64 + // maximum mac length + 32; // margin for deflater; deflater may inflate data + + void checkFreeSize(int n) { + int size = index + n + buffer_margin; + if (buffer.length < size) { + int i = buffer.length * 2; + if (i < size) + i = size; + byte[] tmp = new byte[i]; + System.arraycopy(buffer, 0, tmp, 0, index); + buffer = tmp; + } + } + + byte[][] getBytes(int n, String msg) throws JSchException { + byte[][] tmp = new byte[n][]; + for (int i = 0; i < n; i++) { + int j = getInt(); + if (getLength() < j) { + throw new JSchException(msg); + } + tmp[i] = new byte[j]; + getByte(tmp[i]); + } + return tmp; + } + + /* + * static Buffer fromBytes(byte[]... args){ int length = args.length*4; for(int i = 0; i < + * args.length; i++){ length += args[i].length; } Buffer buf = new Buffer(length); for(int i = 0; + * i < args.length; i++){ buf.putString(args[i]); } return buf; } + */ + + static Buffer fromBytes(byte[][] args) { + int length = args.length * 4; + for (int i = 0; i < args.length; i++) { + length += args[i].length; + } + Buffer buf = new Buffer(length); + for (int i = 0; i < args.length; i++) { + buf.putString(args[i]); + } + return buf; + } + + /* + * static String[] chars={ "0","1","2","3","4","5","6","7","8","9", "a","b","c","d","e","f" }; + * static void dump_buffer(){ int foo; for(int i=0; i>>4)&0xf]); + * System.err.print(chars[foo&0xf]); if(i%16==15){ System.err.println(""); continue; } if(i>0 && + * i%2==1){ System.err.print(" "); } } System.err.println(""); } static void dump(byte[] b){ + * dump(b, 0, b.length); } static void dump(byte[] b, int s, int l){ for(int i=s; i pool = new Vector<>(); + + static Channel getChannel(String type, Session session) { + Channel ret = null; + if (type.equals("session")) { + ret = new ChannelSession(); + } + if (type.equals("shell")) { + ret = new ChannelShell(); + } + if (type.equals("exec")) { + ret = new ChannelExec(); + } + if (type.equals("x11")) { + ret = new ChannelX11(); + } + if (type.equals("auth-agent@openssh.com")) { + ret = new ChannelAgentForwarding(); + } + if (type.equals("direct-tcpip")) { + ret = new ChannelDirectTCPIP(); + } + if (type.equals("forwarded-tcpip")) { + ret = new ChannelForwardedTCPIP(); + } + if (type.equals("sftp")) { + ChannelSftp sftp = new ChannelSftp(); + boolean useWriteFlushWorkaround = + session.getConfig("use_sftp_write_flush_workaround").equals("yes"); + sftp.setUseWriteFlushWorkaround(useWriteFlushWorkaround); + ret = sftp; + } + if (type.equals("subsystem")) { + ret = new ChannelSubsystem(); + } + if (type.equals("direct-streamlocal@openssh.com")) { + ret = new ChannelDirectStreamLocal(); + } + if (ret == null) { + return null; + } + ret.setSession(session); + return ret; + } + + static Channel getChannel(int id, Session session) { + synchronized (pool) { + for (int i = 0; i < pool.size(); i++) { + Channel c = pool.elementAt(i); + if (c.id == id && c.session == session) + return c; + } + } + return null; + } + + static void del(Channel c) { + synchronized (pool) { + pool.removeElement(c); + } + } + + int id; + volatile int recipient = -1; + protected byte[] type = Util.str2byte("foo"); + volatile int lwsize_max = 0x100000; + volatile int lwsize = lwsize_max; // local initial window size + volatile int lmpsize = 0x4000; // local maximum packet size + + volatile long rwsize = 0; // remote initial window size + volatile int rmpsize = 0; // remote maximum packet size + + IO io = null; + Thread thread = null; + + volatile boolean eof_local = false; + volatile boolean eof_remote = false; + + volatile boolean close = false; + volatile boolean connected = false; + volatile boolean open_confirmation = false; + + volatile int exitstatus = -1; + + volatile int reply = 0; + volatile int connectTimeout = 0; + + protected Session session; + + int notifyme = 0; + + Channel() { + synchronized (pool) { + id = index++; + pool.addElement(this); + } + } + + synchronized void setRecipient(int foo) { + this.recipient = foo; + if (notifyme > 0) + notifyAll(); + } + + int getRecipient() { + return recipient; + } + + void init() throws JSchException {} + + public void connect() throws JSchException { + connect(0); + } + + public void connect(int connectTimeout) throws JSchException { + this.connectTimeout = connectTimeout; + try { + sendChannelOpen(); + start(); + } catch (Exception e) { + connected = false; + disconnect(); + if (e instanceof JSchException) + throw (JSchException) e; + throw new JSchException(e.toString(), e); + } + } + + public void setXForwarding(boolean foo) {} + + public void start() throws JSchException {} + + public boolean isEOF() { + return eof_remote; + } + + void getData(Buffer buf) { + setRecipient(buf.getInt()); + setRemoteWindowSize(buf.getUInt()); + setRemotePacketSize(buf.getInt()); + } + + public void setInputStream(InputStream in) { + io.setInputStream(in, false); + } + + public void setInputStream(InputStream in, boolean dontclose) { + io.setInputStream(in, dontclose); + } + + public void setOutputStream(OutputStream out) { + io.setOutputStream(out, false); + } + + public void setOutputStream(OutputStream out, boolean dontclose) { + io.setOutputStream(out, dontclose); + } + + public void setExtOutputStream(OutputStream out) { + io.setExtOutputStream(out, false); + } + + public void setExtOutputStream(OutputStream out, boolean dontclose) { + io.setExtOutputStream(out, dontclose); + } + + public InputStream getInputStream() throws IOException { + Session _session = this.session; + if (_session != null && isConnected() && _session.getLogger().isEnabled(Logger.WARN)) { + _session.getLogger().log(Logger.WARN, "getInputStream() should be called before connect()"); + } + + int max_input_buffer_size = 32 * 1024; + try { + max_input_buffer_size = Integer.parseInt(getSession().getConfig("max_input_buffer_size")); + } catch (Exception e) { + } + PipedInputStream in = new MyPipedInputStream(32 * 1024, // this value should be customizable. + max_input_buffer_size); + boolean resizable = 32 * 1024 < max_input_buffer_size; + io.setOutputStream(new PassiveOutputStream(in, resizable), false); + return in; + } + + public InputStream getExtInputStream() throws IOException { + Session _session = this.session; + if (_session != null && isConnected() && _session.getLogger().isEnabled(Logger.WARN)) { + _session.getLogger().log(Logger.WARN, + "getExtInputStream() should be called before connect()"); + } + + int max_input_buffer_size = 32 * 1024; + try { + max_input_buffer_size = Integer.parseInt(getSession().getConfig("max_input_buffer_size")); + } catch (Exception e) { + } + PipedInputStream in = new MyPipedInputStream(32 * 1024, // this value should be customizable. + max_input_buffer_size); + boolean resizable = 32 * 1024 < max_input_buffer_size; + io.setExtOutputStream(new PassiveOutputStream(in, resizable), false); + return in; + } + + public OutputStream getOutputStream() throws IOException { + + final Channel channel = this; + OutputStream out = new OutputStream() { + private int dataLen = 0; + private Buffer buffer = null; + private Packet packet = null; + private boolean closed = false; + + private synchronized void init() throws IOException { + buffer = new Buffer(rmpsize); + packet = new Packet(buffer); + + byte[] _buf = buffer.buffer; + try { + if (_buf.length - (14 + 0) - getSession().getBufferMargin() <= 0) { + buffer = null; + packet = null; + throw new IOException("failed to initialize the channel."); + } + } catch (JSchException e) { + throw new IOException("failed to initialize the channel.", e); + } + } + + byte[] b = new byte[1]; + + @Override + public void write(int w) throws IOException { + b[0] = (byte) w; + write(b, 0, 1); + } + + @Override + public void write(byte[] buf, int s, int l) throws IOException { + if (packet == null) { + init(); + } + + if (closed) { + throw new IOException("Already closed"); + } + + byte[] _buf = buffer.buffer; + int _bufl = _buf.length; + try { + while (l > 0) { + int _l = l; + int buffer_margin = getSession().getBufferMargin(); + if (l > _bufl - (14 + dataLen) - buffer_margin) { + _l = _bufl - (14 + dataLen) - buffer_margin; + } + + if (_l <= 0) { + flush(); + continue; + } + + System.arraycopy(buf, s, _buf, 14 + dataLen, _l); + dataLen += _l; + s += _l; + l -= _l; + } + } catch (JSchException e) { + throw new IOException(e.toString(), e); + } + } + + @Override + public void flush() throws IOException { + if (closed) { + throw new IOException("Already closed"); + } + if (dataLen == 0) + return; + packet.reset(); + buffer.putByte((byte) Session.SSH_MSG_CHANNEL_DATA); + buffer.putInt(recipient); + buffer.putInt(dataLen); + buffer.skip(dataLen); + try { + int foo = dataLen; + dataLen = 0; + synchronized (channel) { + if (!channel.close) + getSession().write(packet, channel, foo); + } + } catch (Exception e) { + close(); + throw new IOException(e.toString(), e); + } + } + + @Override + public void close() throws IOException { + if (packet == null) { + try { + init(); + } catch (IOException e) { + // close should be finished silently. + return; + } + } + if (closed) { + return; + } + if (dataLen > 0) { + flush(); + } + channel.eof(); + closed = true; + } + }; + return out; + } + + static class MyPipedInputStream extends PipedInputStream { + private int BUFFER_SIZE = 1024; + private int max_buffer_size = BUFFER_SIZE; + + MyPipedInputStream() throws IOException { + super(); + } + + MyPipedInputStream(int size) throws IOException { + super(); + buffer = new byte[size]; + BUFFER_SIZE = size; + max_buffer_size = size; + } + + MyPipedInputStream(int size, int max_buffer_size) throws IOException { + this(size); + this.max_buffer_size = max_buffer_size; + } + + MyPipedInputStream(PipedOutputStream out) throws IOException { + super(out); + } + + MyPipedInputStream(PipedOutputStream out, int size) throws IOException { + super(out); + buffer = new byte[size]; + BUFFER_SIZE = size; + } + + /* + * TODO: We should have our own Piped[I/O]Stream implementation. Before accepting data, JDK's + * PipedInputStream will check the existence of reader thread, and if it is not alive, the + * stream will be closed. That behavior may cause the problem if multiple threads make access to + * it. + */ + public synchronized void updateReadSide() throws IOException { + if (available() != 0) { // not empty + return; + } + in = 0; + out = 0; + buffer[in++] = 0; + read(); + } + + private int freeSpace() { + int size = 0; + if (out < in) { + size = buffer.length - in; + } else if (in < out) { + if (in == -1) + size = buffer.length; + else + size = out - in; + } + return size; + } + + synchronized void checkSpace(int len) throws IOException { + int size = freeSpace(); + if (size < len) { + int datasize = buffer.length - size; + int foo = buffer.length; + while ((foo - datasize) < len) { + foo *= 2; + } + + if (foo > max_buffer_size) { + foo = max_buffer_size; + } + if ((foo - datasize) < len) + return; + + byte[] tmp = new byte[foo]; + if (out < in) { + System.arraycopy(buffer, 0, tmp, 0, buffer.length); + } else if (in < out) { + if (in == -1) { + } else { + System.arraycopy(buffer, 0, tmp, 0, in); + System.arraycopy(buffer, out, tmp, tmp.length - (buffer.length - out), + (buffer.length - out)); + out = tmp.length - (buffer.length - out); + } + } else if (in == out) { + System.arraycopy(buffer, 0, tmp, 0, buffer.length); + in = buffer.length; + } + buffer = tmp; + } else if (buffer.length == size && size > BUFFER_SIZE) { + int i = size / 2; + if (i < BUFFER_SIZE) + i = BUFFER_SIZE; + byte[] tmp = new byte[i]; + buffer = tmp; + } + } + } + + void setLocalWindowSizeMax(int foo) { + this.lwsize_max = foo; + } + + void setLocalWindowSize(int foo) { + this.lwsize = foo; + } + + void setLocalPacketSize(int foo) { + this.lmpsize = foo; + } + + synchronized void setRemoteWindowSize(long foo) { + this.rwsize = foo; + } + + synchronized void addRemoteWindowSize(long foo) { + this.rwsize += foo; + if (notifyme > 0) + notifyAll(); + } + + void setRemotePacketSize(int foo) { + this.rmpsize = foo; + } + + abstract void run(); + + void write(byte[] foo) throws IOException { + write(foo, 0, foo.length); + } + + void write(byte[] foo, int s, int l) throws IOException { + try { + io.put(foo, s, l); + } catch (NullPointerException e) { + } + } + + void write_ext(byte[] foo, int s, int l) throws IOException { + try { + io.put_ext(foo, s, l); + } catch (NullPointerException e) { + } + } + + void eof_remote() { + eof_remote = true; + try { + io.out_close(); + } catch (NullPointerException e) { + } + } + + void eof() { + if (eof_local) + return; + eof_local = true; + + int i = getRecipient(); + if (i == -1) + return; + + try { + Buffer buf = new Buffer(100); + Packet packet = new Packet(buf); + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_EOF); + buf.putInt(i); + synchronized (this) { + if (!close) + getSession().write(packet); + } + } catch (Exception e) { + // System.err.println("Channel.eof"); + // e.printStackTrace(); + } + /* + * if(!isConnected()){ disconnect(); } + */ + } + + /* + * http://www1.ietf.org/internet-drafts/draft-ietf-secsh-connect-24.txt + * + * 5.3 Closing a Channel When a party will no longer send more data to a channel, it SHOULD send + * SSH_MSG_CHANNEL_EOF. + * + * byte SSH_MSG_CHANNEL_EOF uint32 recipient_channel + * + * No explicit response is sent to this message. However, the application may send EOF to whatever + * is at the other end of the channel. Note that the channel remains open after this message, and + * more data may still be sent in the other direction. This message does not consume window space + * and can be sent even if no window space is available. + * + * When either party wishes to terminate the channel, it sends SSH_MSG_CHANNEL_CLOSE. Upon + * receiving this message, a party MUST send back a SSH_MSG_CHANNEL_CLOSE unless it has already + * sent this message for the channel. The channel is considered closed for a party when it has + * both sent and received SSH_MSG_CHANNEL_CLOSE, and the party may then reuse the channel number. + * A party MAY send SSH_MSG_CHANNEL_CLOSE without having sent or received SSH_MSG_CHANNEL_EOF. + * + * byte SSH_MSG_CHANNEL_CLOSE uint32 recipient_channel + * + * This message does not consume window space and can be sent even if no window space is + * available. + * + * It is recommended that any data sent before this message is delivered to the actual + * destination, if possible. + */ + + void close() { + if (close) + return; + close = true; + eof_local = eof_remote = true; + + int i = getRecipient(); + if (i == -1) + return; + + try { + Buffer buf = new Buffer(100); + Packet packet = new Packet(buf); + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_CLOSE); + buf.putInt(i); + synchronized (this) { + getSession().write(packet); + } + } catch (Exception e) { + // e.printStackTrace(); + } + } + + public boolean isClosed() { + return close; + } + + static void disconnect(Session session) { + Channel[] channels = null; + int count = 0; + synchronized (pool) { + channels = new Channel[pool.size()]; + for (int i = 0; i < pool.size(); i++) { + try { + Channel c = pool.elementAt(i); + if (c.session == session) { + channels[count++] = c; + } + } catch (Exception e) { + } + } + } + for (int i = 0; i < count; i++) { + channels[i].disconnect(); + } + } + + public void disconnect() { + // System.err.println(this+":disconnect "+io+" "+connected); + // Thread.dumpStack(); + + try { + + synchronized (this) { + if (!connected) { + return; + } + connected = false; + } + + close(); + + eof_remote = eof_local = true; + + thread = null; + + try { + if (io != null) { + io.close(); + } + } catch (Exception e) { + // e.printStackTrace(); + } + // io=null; + } finally { + Channel.del(this); + } + } + + public boolean isConnected() { + Session _session = this.session; + if (_session != null) { + return _session.isConnected() && connected; + } + return false; + } + + public void sendSignal(String signal) throws Exception { + RequestSignal request = new RequestSignal(); + request.setSignal(signal); + request.request(getSession(), this); + } + + // public String toString(){ + // return "Channel: type="+new + // String(type)+",id="+id+",recipient="+recipient+",window_size="+window_size+",packet_size="+packet_size; + // } + + /* + * class OutputThread extends Thread{ Channel c; OutputThread(Channel c){ this.c=c;} public void + * run(){c.output_thread();} } + */ + + static class PassiveInputStream extends MyPipedInputStream { + PipedOutputStream os; + + PassiveInputStream(PipedOutputStream out, int size) throws IOException { + super(out, size); + this.os = out; + } + + PassiveInputStream(PipedOutputStream out) throws IOException { + super(out); + this.os = out; + } + + @Override + public void close() throws IOException { + if (this.os != null) { + this.os.close(); + } + this.os = null; + } + } + + static class PassiveOutputStream extends PipedOutputStream { + private MyPipedInputStream _sink = null; + + PassiveOutputStream(PipedInputStream in, boolean resizable_buffer) throws IOException { + super(in); + if (resizable_buffer && (in instanceof MyPipedInputStream)) { + this._sink = (MyPipedInputStream) in; + } + } + + @Override + public void write(int b) throws IOException { + if (_sink != null) { + _sink.checkSpace(1); + } + super.write(b); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (_sink != null) { + _sink.checkSpace(len); + } + super.write(b, off, len); + } + } + + void setExitStatus(int status) { + exitstatus = status; + } + + public int getExitStatus() { + return exitstatus; + } + + void setSession(Session session) { + this.session = session; + } + + public Session getSession() throws JSchException { + Session _session = session; + if (_session == null) { + throw new JSchException("session is not available"); + } + return _session; + } + + public int getId() { + return id; + } + + protected void sendOpenConfirmation() throws Exception { + Buffer buf = new Buffer(200); + Packet packet = new Packet(buf); + packet.reset(); + buf.putByte((byte) SSH_MSG_CHANNEL_OPEN_CONFIRMATION); + buf.putInt(getRecipient()); + buf.putInt(id); + buf.putInt(lwsize); + buf.putInt(lmpsize); + getSession().write(packet); + } + + protected void sendOpenFailure(int reasoncode) { + try { + Buffer buf = new Buffer(200); + Packet packet = new Packet(buf); + packet.reset(); + buf.putByte((byte) SSH_MSG_CHANNEL_OPEN_FAILURE); + buf.putInt(getRecipient()); + buf.putInt(reasoncode); + buf.putString(Util.str2byte("open failed")); + buf.putString(Util.empty); + getSession().write(packet); + } catch (Exception e) { + } + } + + protected Packet genChannelOpenPacket() { + Buffer buf = new Buffer(200); + Packet packet = new Packet(buf); + // byte SSH_MSG_CHANNEL_OPEN(90) + // string channel type // + // uint32 sender channel // 0 + // uint32 initial window size // 0x100000(65536) + // uint32 maxmum packet size // 0x4000(16384) + packet.reset(); + buf.putByte((byte) 90); + buf.putString(this.type); + buf.putInt(this.id); + buf.putInt(this.lwsize); + buf.putInt(this.lmpsize); + return packet; + } + + protected void sendChannelOpen() throws Exception { + Session _session = getSession(); + if (!_session.isConnected()) { + throw new JSchException("session is down"); + } + + Packet packet = genChannelOpenPacket(); + _session.write(packet); + + int retry = 2000; + long start = System.currentTimeMillis(); + long timeout = connectTimeout; + if (timeout != 0L) + retry = 1; + synchronized (this) { + while (this.getRecipient() == -1 && _session.isConnected() && retry > 0) { + if (timeout > 0L) { + if ((System.currentTimeMillis() - start) > timeout) { + retry = 0; + continue; + } + } + try { + long t = timeout == 0L ? 10L : timeout; + this.notifyme = 1; + wait(t); + } catch (InterruptedException e) { + } finally { + this.notifyme = 0; + } + retry--; + } + } + if (!_session.isConnected()) { + throw new JSchException("session is down"); + } + if (this.getRecipient() == -1) { // timeout + throw new JSchException("channel is not opened."); + } + if (this.open_confirmation == false) { // SSH_MSG_CHANNEL_OPEN_FAILURE + throw new JSchException("channel is not opened."); + } + connected = true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ChannelAgentForwarding.java b/files-jsch/src/main/java/com/jcraft/jsch/ChannelAgentForwarding.java new file mode 100644 index 0000000..34e2004 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ChannelAgentForwarding.java @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.util.Vector; + +class ChannelAgentForwarding extends Channel { + + private static final int LOCAL_WINDOW_SIZE_MAX = 0x20000; + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; + + private static final byte SSH_AGENTC_REQUEST_RSA_IDENTITIES = 1; + private static final byte SSH_AGENT_RSA_IDENTITIES_ANSWER = 2; + private static final byte SSH_AGENTC_RSA_CHALLENGE = 3; + private static final byte SSH_AGENT_RSA_RESPONSE = 4; + private static final byte SSH_AGENT_FAILURE = 5; + private static final byte SSH_AGENT_SUCCESS = 6; + private static final byte SSH_AGENTC_ADD_RSA_IDENTITY = 7; + private static final byte SSH_AGENTC_REMOVE_RSA_IDENTITY = 8; + private static final byte SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES = 9; + + private static final byte SSH2_AGENTC_REQUEST_IDENTITIES = 11; + private static final byte SSH2_AGENT_IDENTITIES_ANSWER = 12; + private static final byte SSH2_AGENTC_SIGN_REQUEST = 13; + private static final byte SSH2_AGENT_SIGN_RESPONSE = 14; + private static final byte SSH2_AGENTC_ADD_IDENTITY = 17; + private static final byte SSH2_AGENTC_REMOVE_IDENTITY = 18; + private static final byte SSH2_AGENTC_REMOVE_ALL_IDENTITIES = 19; + private static final byte SSH2_AGENT_FAILURE = 30; + + // static private final int SSH_AGENT_OLD_SIGNATURE=0x1; + private static final int SSH_AGENT_RSA_SHA2_256 = 0x2; + private static final int SSH_AGENT_RSA_SHA2_512 = 0x4; + + private Buffer rbuf = null; + private Buffer wbuf = null; + private Packet packet = null; + private Buffer mbuf = null; + + ChannelAgentForwarding() { + super(); + + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; + + type = Util.str2byte("auth-agent@openssh.com"); + rbuf = new Buffer(); + rbuf.reset(); + // wbuf=new Buffer(rmpsize); + // packet=new Packet(wbuf); + mbuf = new Buffer(); + connected = true; + } + + @Override + void run() { + try { + sendOpenConfirmation(); + } catch (Exception e) { + close = true; + disconnect(); + } + } + + @Override + void write(byte[] foo, int s, int l) throws IOException { + + if (packet == null) { + wbuf = new Buffer(rmpsize); + packet = new Packet(wbuf); + } + + rbuf.shift(); + if (rbuf.buffer.length < rbuf.index + l) { + byte[] newbuf = new byte[rbuf.s + l]; + System.arraycopy(rbuf.buffer, 0, newbuf, 0, rbuf.buffer.length); + rbuf.buffer = newbuf; + } + + rbuf.putByte(foo, s, l); + + int mlen = rbuf.getInt(); + if (mlen > rbuf.getLength()) { + rbuf.s -= 4; + return; + } + + int typ = rbuf.getByte(); + + Session _session = null; + try { + _session = getSession(); + } catch (JSchException e) { + throw new IOException(e.toString(), e); + } + + IdentityRepository irepo = _session.getIdentityRepository(); + UserInfo userinfo = _session.getUserInfo(); + + mbuf.reset(); + + if (typ == SSH2_AGENTC_REQUEST_IDENTITIES) { + mbuf.putByte(SSH2_AGENT_IDENTITIES_ANSWER); + Vector identities = irepo.getIdentities(); + synchronized (identities) { + int count = 0; + for (int i = 0; i < identities.size(); i++) { + Identity identity = identities.elementAt(i); + if (identity.getPublicKeyBlob() != null) + count++; + } + mbuf.putInt(count); + for (int i = 0; i < identities.size(); i++) { + Identity identity = identities.elementAt(i); + byte[] pubkeyblob = identity.getPublicKeyBlob(); + if (pubkeyblob == null) + continue; + mbuf.putString(pubkeyblob); + mbuf.putString(Util.empty); + } + } + } else if (typ == SSH_AGENTC_REQUEST_RSA_IDENTITIES) { + mbuf.putByte(SSH_AGENT_RSA_IDENTITIES_ANSWER); + mbuf.putInt(0); + } else if (typ == SSH2_AGENTC_SIGN_REQUEST) { + byte[] blob = rbuf.getString(); + byte[] data = rbuf.getString(); + int flags = rbuf.getInt(); + + // if((flags & SSH_AGENT_OLD_SIGNATURE)!=0){ // old OpenSSH 2.0, 2.1 + // datafellows = SSH_BUG_SIGBLOB; + // } + + Vector identities = irepo.getIdentities(); + Identity identity = null; + synchronized (identities) { + for (int i = 0; i < identities.size(); i++) { + Identity _identity = identities.elementAt(i); + if (_identity.getPublicKeyBlob() == null) + continue; + if (!Util.array_equals(blob, _identity.getPublicKeyBlob())) { + continue; + } + if (_identity.isEncrypted()) { + if (userinfo == null) + continue; + while (_identity.isEncrypted()) { + if (!userinfo.promptPassphrase("Passphrase for " + _identity.getName())) { + break; + } + + String _passphrase = userinfo.getPassphrase(); + if (_passphrase == null) { + break; + } + + byte[] passphrase = Util.str2byte(_passphrase); + try { + if (_identity.setPassphrase(passphrase)) { + break; + } + } catch (JSchException e) { + break; + } + } + } + + if (!_identity.isEncrypted()) { + identity = _identity; + break; + } + } + } + + byte[] signature = null; + + if (identity != null) { + Buffer kbuf = new Buffer(blob); + String keytype = Util.byte2str(kbuf.getString()); + if (keytype.equals("ssh-rsa")) { + if ((flags & SSH_AGENT_RSA_SHA2_256) != 0) { + signature = identity.getSignature(data, "rsa-sha2-256"); + } else if ((flags & SSH_AGENT_RSA_SHA2_512) != 0) { + signature = identity.getSignature(data, "rsa-sha2-512"); + } else { + signature = identity.getSignature(data, "ssh-rsa"); + } + } else { + signature = identity.getSignature(data); + } + } + + if (signature == null) { + mbuf.putByte(SSH2_AGENT_FAILURE); + } else { + mbuf.putByte(SSH2_AGENT_SIGN_RESPONSE); + mbuf.putString(signature); + } + } else if (typ == SSH2_AGENTC_REMOVE_IDENTITY) { + byte[] blob = rbuf.getString(); + irepo.remove(blob); + mbuf.putByte(SSH_AGENT_SUCCESS); + } else if (typ == SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES) { + mbuf.putByte(SSH_AGENT_SUCCESS); + } else if (typ == SSH2_AGENTC_REMOVE_ALL_IDENTITIES) { + irepo.removeAll(); + mbuf.putByte(SSH_AGENT_SUCCESS); + } else if (typ == SSH2_AGENTC_ADD_IDENTITY) { + int fooo = rbuf.getLength(); + byte[] tmp = new byte[fooo]; + rbuf.getByte(tmp); + boolean result = irepo.add(tmp); + mbuf.putByte(result ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE); + } else { + rbuf.skip(rbuf.getLength() - 1); + mbuf.putByte(SSH_AGENT_FAILURE); + } + + byte[] response = new byte[mbuf.getLength()]; + mbuf.getByte(response); + send(response); + } + + private void send(byte[] message) { + packet.reset(); + wbuf.putByte((byte) Session.SSH_MSG_CHANNEL_DATA); + wbuf.putInt(recipient); + wbuf.putInt(4 + message.length); + wbuf.putString(message); + + try { + getSession().write(packet, this, 4 + message.length); + } catch (Exception e) { + } + } + + @Override + void eof_remote() { + super.eof_remote(); + eof(); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ChannelDirectStreamLocal.java b/files-jsch/src/main/java/com/jcraft/jsch/ChannelDirectStreamLocal.java new file mode 100644 index 0000000..babb722 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ChannelDirectStreamLocal.java @@ -0,0 +1,65 @@ +package com.jcraft.jsch; + +import static com.jcraft.jsch.Session.SSH_MSG_CHANNEL_OPEN; + +/** + * Extension of {@link ChannelDirectTCPIP} to support socket forwarding. + * + *

+ * https://raw.githubusercontent.com/openssh/openssh-portable/master/PROTOCOL + */ +public class ChannelDirectStreamLocal extends ChannelDirectTCPIP { + + private static final int LOCAL_WINDOW_SIZE_MAX = 0x20000; + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; + private static final byte[] _type = Util.str2byte("direct-streamlocal@openssh.com"); + + private String socketPath; + + ChannelDirectStreamLocal() { + super(); + type = _type; + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; + } + + @Override + protected Packet genChannelOpenPacket() { + + if (socketPath == null) { + session.getLogger().log(Logger.FATAL, "socketPath must be set"); + throw new RuntimeException("socketPath must be set"); + } + + /* + * Similar to direct-tcpip, direct-streamlocal is sent by the client to request that the server + * make a connection to a Unix domain socket. + * + * byte SSH_MSG_CHANNEL_OPEN string "direct-streamlocal@openssh.com" uint32 sender channel + * uint32 initial window size uint32 maximum packet size string socket path string reserved + * uint32 reserved + */ + + Buffer buf = new Buffer(50 + socketPath.length() + session.getBufferMargin()); + Packet packet = new Packet(buf); + packet.reset(); + buf.putByte((byte) SSH_MSG_CHANNEL_OPEN); + buf.putString(this.type); + buf.putInt(id); + buf.putInt(lwsize); + buf.putInt(lmpsize); + buf.putString(Util.str2byte(socketPath)); + buf.putString(Util.str2byte(originator_IP_address)); + buf.putInt(originator_port); + return packet; + } + + public String getSocketPath() { + return socketPath; + } + + public void setSocketPath(String socketPath) { + this.socketPath = socketPath; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java b/files-jsch/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java new file mode 100644 index 0000000..c1522ef --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ChannelDirectTCPIP.java @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.InputStream; +import java.io.OutputStream; + +public class ChannelDirectTCPIP extends Channel { + + private static final int LOCAL_WINDOW_SIZE_MAX = 0x20000; + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; + private static final byte[] _type = Util.str2byte("direct-tcpip"); + String host; + int port; + + String originator_IP_address = "127.0.0.1"; + int originator_port = 0; + + ChannelDirectTCPIP() { + super(); + type = _type; + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; + } + + @Override + void init() { + io = new IO(); + } + + @Override + public void connect(int connectTimeout) throws JSchException { + this.connectTimeout = connectTimeout; + try { + Session _session = getSession(); + if (!_session.isConnected()) { + throw new JSchException("session is down"); + } + + if (io.in != null) { + thread = new Thread(this::run); + thread.setName("DirectTCPIP thread " + _session.getHost()); + if (_session.daemon_thread) { + thread.setDaemon(_session.daemon_thread); + } + thread.start(); + } else { + sendChannelOpen(); + } + } catch (Exception e) { + io.close(); + io = null; + Channel.del(this); + if (e instanceof JSchException) { + throw (JSchException) e; + } + } + } + + @Override + void run() { + + try { + sendChannelOpen(); + + Buffer buf = new Buffer(rmpsize); + Packet packet = new Packet(buf); + Session _session = getSession(); + int i = 0; + + while (isConnected() && thread != null && io != null && io.in != null) { + i = io.in.read(buf.buffer, 14, buf.buffer.length - 14 - _session.getBufferMargin()); + if (i <= 0) { + eof(); + break; + } + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(i); + buf.skip(i); + synchronized (this) { + if (close) + break; + _session.write(packet, this, i); + } + } + } catch (Exception e) { + // Whenever an exception is thrown by sendChannelOpen(), + // 'connected' is false. + if (!connected) { + connected = true; + } + disconnect(); + return; + } + + eof(); + disconnect(); + } + + @Override + public void setInputStream(InputStream in) { + io.setInputStream(in); + } + + @Override + public void setOutputStream(OutputStream out) { + io.setOutputStream(out); + } + + public void setHost(String host) { + this.host = host; + } + + public void setPort(int port) { + this.port = port; + } + + public void setOrgIPAddress(String foo) { + this.originator_IP_address = foo; + } + + public void setOrgPort(int foo) { + this.originator_port = foo; + } + + @Override + protected Packet genChannelOpenPacket() { + Buffer buf = new Buffer(50 + // 6 + 4*8 + 12 + host.length() + originator_IP_address.length() + session.getBufferMargin()); + Packet packet = new Packet(buf); + // byte SSH_MSG_CHANNEL_OPEN(90) + // string channel type // + // uint32 sender channel // 0 + // uint32 initial window size // 0x100000(65536) + // uint32 maxmum packet size // 0x4000(16384) + packet.reset(); + buf.putByte((byte) 90); + buf.putString(this.type); + buf.putInt(id); + buf.putInt(lwsize); + buf.putInt(lmpsize); + buf.putString(Util.str2byte(host)); + buf.putInt(port); + buf.putString(Util.str2byte(originator_IP_address)); + buf.putInt(originator_port); + return packet; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ChannelExec.java b/files-jsch/src/main/java/com/jcraft/jsch/ChannelExec.java new file mode 100644 index 0000000..56143bb --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ChannelExec.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class ChannelExec extends ChannelSession { + + byte[] command = new byte[0]; + + @Override + public void start() throws JSchException { + Session _session = getSession(); + try { + sendRequests(); + Request request = new RequestExec(command); + request.request(_session, this); + } catch (Exception e) { + if (e instanceof JSchException) + throw (JSchException) e; + throw new JSchException("ChannelExec", e); + } + + if (io.in != null) { + thread = new Thread(this::run); + thread.setName("Exec thread " + _session.getHost()); + if (_session.daemon_thread) { + thread.setDaemon(_session.daemon_thread); + } + thread.start(); + } + } + + public void setCommand(String command) { + this.command = Util.str2byte(command); + } + + public void setCommand(byte[] command) { + this.command = command; + } + + @Override + void init() throws JSchException { + io.setInputStream(getSession().in); + io.setOutputStream(getSession().out); + } + + public void setErrStream(OutputStream out) { + setExtOutputStream(out); + } + + public void setErrStream(OutputStream out, boolean dontclose) { + setExtOutputStream(out, dontclose); + } + + public InputStream getErrStream() throws IOException { + return getExtInputStream(); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java b/files-jsch/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java new file mode 100644 index 0000000..15de382 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ChannelForwardedTCPIP.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.PipedOutputStream; +import java.net.Socket; +import java.util.Vector; + +public class ChannelForwardedTCPIP extends Channel { + + private static Vector pool = new Vector<>(); + + private static final int LOCAL_WINDOW_SIZE_MAX = 0x20000; + // static private final int LOCAL_WINDOW_SIZE_MAX=0x100000; + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; + + private static final int TIMEOUT = 10 * 1000; + + private Socket socket = null; + private ForwardedTCPIPDaemon daemon = null; + private Config config = null; + + ChannelForwardedTCPIP() { + super(); + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; + io = new IO(); + connected = true; + } + + @Override + public void run() { + try { + if (config instanceof ConfigDaemon) { + ConfigDaemon _config = (ConfigDaemon) config; + Class c = + Class.forName(_config.target).asSubclass(ForwardedTCPIPDaemon.class); + daemon = c.getDeclaredConstructor().newInstance(); + + PipedOutputStream out = new PipedOutputStream(); + io.setInputStream(new PassiveInputStream(out, 32 * 1024), false); + + daemon.setChannel(this, getInputStream(), out); + daemon.setArg(_config.arg); + new Thread(daemon).start(); + } else { + ConfigLHost _config = (ConfigLHost) config; + socket = + (_config.factory == null) ? Util.createSocket(_config.target, _config.lport, TIMEOUT) + : _config.factory.createSocket(_config.target, _config.lport); + socket.setTcpNoDelay(true); + io.setInputStream(socket.getInputStream()); + io.setOutputStream(socket.getOutputStream()); + } + sendOpenConfirmation(); + } catch (Exception e) { + sendOpenFailure(SSH_OPEN_ADMINISTRATIVELY_PROHIBITED); + close = true; + disconnect(); + return; + } + + thread = Thread.currentThread(); + Buffer buf = new Buffer(rmpsize); + Packet packet = new Packet(buf); + int i = 0; + try { + Session _session = getSession(); + while (thread != null && io != null && io.in != null) { + i = io.in.read(buf.buffer, 14, buf.buffer.length - 14 - _session.getBufferMargin()); + if (i <= 0) { + eof(); + break; + } + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(i); + buf.skip(i); + synchronized (this) { + if (close) + break; + _session.write(packet, this, i); + } + } + } catch (Exception e) { + // System.err.println(e); + } + // thread=null; + // eof(); + disconnect(); + } + + @Override + void getData(Buffer buf) { + setRecipient(buf.getInt()); + setRemoteWindowSize(buf.getUInt()); + setRemotePacketSize(buf.getInt()); + byte[] addr = buf.getString(); + int port = buf.getInt(); + byte[] orgaddr = buf.getString(); + int orgport = buf.getInt(); + + /* + * System.err.println("addr: "+Util.byte2str(addr)); System.err.println("port: "+port); + * System.err.println("orgaddr: "+Util.byte2str(orgaddr)); + * System.err.println("orgport: "+orgport); + */ + + Session _session = null; + try { + _session = getSession(); + } catch (JSchException e) { + // session has been already down. + } + + this.config = getPort(_session, Util.byte2str(addr), port); + if (this.config == null) + this.config = getPort(_session, null, port); + + if (this.config == null) { + if (_session.getLogger().isEnabled(Logger.ERROR)) { + _session.getLogger().log(Logger.ERROR, + "ChannelForwardedTCPIP: " + Util.byte2str(addr) + ":" + port + " is not registered."); + } + } + } + + private static Config getPort(Session session, String address_to_bind, int rport) { + synchronized (pool) { + for (int i = 0; i < pool.size(); i++) { + Config bar = pool.elementAt(i); + if (bar.session != session) + continue; + if (bar.rport != rport) { + if (bar.rport != 0 || bar.allocated_rport != rport) + continue; + } + if (address_to_bind != null && !bar.address_to_bind.equals(address_to_bind)) + continue; + return bar; + } + return null; + } + } + + static String[] getPortForwarding(Session session) { + Vector foo = new Vector<>(); + synchronized (pool) { + for (int i = 0; i < pool.size(); i++) { + Config config = pool.elementAt(i); + if (config.session == session) { + if (config instanceof ConfigDaemon) + foo.addElement(config.allocated_rport + ":" + config.target + ":"); + else + foo.addElement( + config.allocated_rport + ":" + config.target + ":" + ((ConfigLHost) config).lport); + } + } + } + String[] bar = new String[foo.size()]; + for (int i = 0; i < foo.size(); i++) { + bar[i] = foo.elementAt(i); + } + return bar; + } + + static String normalize(String address) { + if (address == null) { + return "localhost"; + } else if (address.length() == 0 || address.equals("*")) { + return ""; + } else { + return address; + } + } + + static void addPort(Session session, String _address_to_bind, int port, int allocated_port, + String target, int lport, SocketFactory factory) throws JSchException { + String address_to_bind = normalize(_address_to_bind); + synchronized (pool) { + if (getPort(session, address_to_bind, port) != null) { + throw new JSchException("PortForwardingR: remote port " + port + " is already registered."); + } + ConfigLHost config = new ConfigLHost(); + config.session = session; + config.rport = port; + config.allocated_rport = allocated_port; + config.target = target; + config.lport = lport; + config.address_to_bind = address_to_bind; + config.factory = factory; + pool.addElement(config); + } + } + + static void addPort(Session session, String _address_to_bind, int port, int allocated_port, + String daemon, Object[] arg) throws JSchException { + String address_to_bind = normalize(_address_to_bind); + synchronized (pool) { + if (getPort(session, address_to_bind, port) != null) { + throw new JSchException("PortForwardingR: remote port " + port + " is already registered."); + } + ConfigDaemon config = new ConfigDaemon(); + config.session = session; + config.rport = port; + config.allocated_rport = port; + config.target = daemon; + config.arg = arg; + config.address_to_bind = address_to_bind; + pool.addElement(config); + } + } + + static void delPort(ChannelForwardedTCPIP c) { + Session _session = null; + try { + _session = c.getSession(); + } catch (JSchException e) { + // session has been already down. + } + if (_session != null && c.config != null) + delPort(_session, c.config.rport); + } + + static void delPort(Session session, int rport) { + delPort(session, null, rport); + } + + static void delPort(Session session, String address_to_bind, int rport) { + synchronized (pool) { + Config foo = getPort(session, normalize(address_to_bind), rport); + if (foo == null) + foo = getPort(session, null, rport); + if (foo == null) + return; + pool.removeElement(foo); + if (address_to_bind == null) { + address_to_bind = foo.address_to_bind; + } + if (address_to_bind == null) { + address_to_bind = "0.0.0.0"; + } + } + + Buffer buf = new Buffer(200); // ?? + Packet packet = new Packet(buf); + + try { + // byte SSH_MSG_GLOBAL_REQUEST 80 + // string "cancel-tcpip-forward" + // boolean want_reply + // string address_to_bind (e.g. "127.0.0.1") + // uint32 port number to bind + packet.reset(); + buf.putByte((byte) 80 /* SSH_MSG_GLOBAL_REQUEST */); + buf.putString(Util.str2byte("cancel-tcpip-forward")); + buf.putByte((byte) 0); + buf.putString(Util.str2byte(address_to_bind)); + buf.putInt(rport); + session.write(packet); + } catch (Exception e) { + // throw new JSchException(e.toString(), e); + } + } + + static void delPort(Session session) { + int[] rport = null; + int count = 0; + synchronized (pool) { + rport = new int[pool.size()]; + for (int i = 0; i < pool.size(); i++) { + Config config = pool.elementAt(i); + if (config.session == session) { + rport[count++] = config.rport; // ((Integer)bar[1]).intValue(); + } + } + } + for (int i = 0; i < count; i++) { + delPort(session, rport[i]); + } + } + + public int getRemotePort() { + return (config != null ? config.rport : 0); + } + + private void setSocketFactory(SocketFactory factory) { + if (config != null && (config instanceof ConfigLHost)) + ((ConfigLHost) config).factory = factory; + } + + abstract static class Config { + Session session; + int rport; + int allocated_rport; + String address_to_bind; + String target; + } + + static class ConfigDaemon extends Config { + Object[] arg; + } + + static class ConfigLHost extends Config { + int lport; + SocketFactory factory; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ChannelSession.java b/files-jsch/src/main/java/com/jcraft/jsch/ChannelSession.java new file mode 100644 index 0000000..dbb8be5 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ChannelSession.java @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Enumeration; +import java.util.Hashtable; + +class ChannelSession extends Channel { + private static byte[] _session = Util.str2byte("session"); + + protected boolean agent_forwarding = false; + protected boolean xforwading = false; + protected Hashtable env = null; + + protected boolean pty = false; + + protected String ttype = "vt100"; + protected int tcol = 80; + protected int trow = 24; + protected int twp = 640; + protected int thp = 480; + protected byte[] terminal_mode = null; + + ChannelSession() { + super(); + type = _session; + io = new IO(); + } + + /** + * Enable the agent forwarding. + * + * @param enable + */ + public void setAgentForwarding(boolean enable) { + agent_forwarding = enable; + } + + /** + * Enable the X11 forwarding. Refer to RFC4254 6.3.1. Requesting X11 Forwarding. + * + * @param enable + */ + @Override + public void setXForwarding(boolean enable) { + xforwading = enable; + } + + /** + * @deprecated Use #setEnv(String, String) or #setEnv(byte[], byte[]) instead. + * @see #setEnv(String, String) + * @see #setEnv(byte[], byte[]) + */ + @Deprecated + public void setEnv(Hashtable env) { + synchronized (this) { + this.env = env; + } + } + + /** + * Set the environment variable. If name and value are needed to be + * passed to the remote in your favorite encoding, use {@link #setEnv(byte[], byte[])}. Refer to + * RFC4254 6.4 Environment Variable Passing. + * + * @param name A name for environment variable. + * @param value A value for environment variable. + */ + public void setEnv(String name, String value) { + setEnv(Util.str2byte(name), Util.str2byte(value)); + } + + /** + * Set the environment variable. Refer to RFC4254 6.4 Environment Variable Passing. + * + * @param name A name of environment variable. + * @param value A value of environment variable. + * @see #setEnv(String, String) + */ + public void setEnv(byte[] name, byte[] value) { + synchronized (this) { + getEnv().put(name, value); + } + } + + private Hashtable getEnv() { + if (env == null) + env = new Hashtable<>(); + return env; + } + + /** + * Allocate a Pseudo-Terminal. Refer to RFC4254 6.2. Requesting a Pseudo-Terminal. + * + * @param enable + */ + public void setPty(boolean enable) { + pty = enable; + } + + /** + * Set the terminal mode. + * + * @param terminal_mode + */ + public void setTerminalMode(byte[] terminal_mode) { + this.terminal_mode = terminal_mode; + } + + /** + * Change the window dimension interactively. Refer to RFC4254 6.7. Window Dimension Change + * Message. + * + * @param col terminal width, columns + * @param row terminal height, rows + * @param wp terminal width, pixels + * @param hp terminal height, pixels + */ + public void setPtySize(int col, int row, int wp, int hp) { + setPtyType(this.ttype, col, row, wp, hp); + if (!pty || !isConnected()) { + return; + } + try { + RequestWindowChange request = new RequestWindowChange(); + request.setSize(col, row, wp, hp); + request.request(getSession(), this); + } catch (Exception e) { + // System.err.println("ChannelSessio.setPtySize: "+e); + } + } + + /** + * Set the terminal type. This method is not effective after Channel#connect(). + * + * @param ttype terminal type(for example, "vt100") + * @see #setPtyType(String, int, int, int, int) + */ + public void setPtyType(String ttype) { + setPtyType(ttype, 80, 24, 640, 480); + } + + /** + * Set the terminal type. This method is not effective after Channel#connect(). + * + * @param ttype terminal type(for example, "vt100") + * @param col terminal width, columns + * @param row terminal height, rows + * @param wp terminal width, pixels + * @param hp terminal height, pixels + */ + public void setPtyType(String ttype, int col, int row, int wp, int hp) { + this.ttype = ttype; + this.tcol = col; + this.trow = row; + this.twp = wp; + this.thp = hp; + } + + protected void sendRequests() throws Exception { + Session _session = getSession(); + Request request; + if (agent_forwarding) { + request = new RequestAgentForwarding(); + request.request(_session, this); + } + + if (xforwading) { + request = new RequestX11(); + request.request(_session, this); + } + + if (pty) { + request = new RequestPtyReq(); + ((RequestPtyReq) request).setTType(ttype); + ((RequestPtyReq) request).setTSize(tcol, trow, twp, thp); + if (terminal_mode != null) { + ((RequestPtyReq) request).setTerminalMode(terminal_mode); + } + request.request(_session, this); + } + + if (env != null) { + for (Enumeration _env = env.keys(); _env.hasMoreElements();) { + byte[] name = _env.nextElement(); + byte[] value = env.get(name); + request = new RequestEnv(); + ((RequestEnv) request).setEnv(toByteArray(name), toByteArray(value)); + request.request(_session, this); + } + } + } + + private byte[] toByteArray(Object o) { + if (o instanceof String) { + return Util.str2byte((String) o); + } + return (byte[]) o; + } + + @Override + void run() { + // System.err.println(this+":run >"); + + Buffer buf = new Buffer(rmpsize); + Packet packet = new Packet(buf); + int i = -1; + try { + Session _session = getSession(); + while (isConnected() && thread != null && io != null && io.in != null) { + i = io.in.read(buf.buffer, 14, buf.buffer.length - 14 - _session.getBufferMargin()); + if (i == 0) + continue; + if (i == -1) { + eof(); + break; + } + if (close) + break; + // System.out.println("write: "+i); + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(i); + buf.skip(i); + _session.write(packet, this, i); + } + } catch (Exception e) { + // System.err.println("# ChannelExec.run"); + // e.printStackTrace(); + } + Thread _thread = thread; + if (_thread != null) { + synchronized (_thread) { + _thread.notifyAll(); + } + } + thread = null; + // System.err.println(this+":run <"); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ChannelSftp.java b/files-jsch/src/main/java/com/jcraft/jsch/ChannelSftp.java new file mode 100644 index 0000000..3c1c497 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ChannelSftp.java @@ -0,0 +1,3039 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Hashtable; +import java.util.Vector; + +public class ChannelSftp extends ChannelSession { + + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 32 * 1024; + private static final int LOCAL_WINDOW_SIZE_MAX = (64 * LOCAL_MAXIMUM_PACKET_SIZE); + + private static final byte SSH_FXP_INIT = 1; + private static final byte SSH_FXP_VERSION = 2; + private static final byte SSH_FXP_OPEN = 3; + private static final byte SSH_FXP_CLOSE = 4; + private static final byte SSH_FXP_READ = 5; + private static final byte SSH_FXP_WRITE = 6; + private static final byte SSH_FXP_LSTAT = 7; + private static final byte SSH_FXP_FSTAT = 8; + private static final byte SSH_FXP_SETSTAT = 9; + private static final byte SSH_FXP_FSETSTAT = 10; + private static final byte SSH_FXP_OPENDIR = 11; + private static final byte SSH_FXP_READDIR = 12; + private static final byte SSH_FXP_REMOVE = 13; + private static final byte SSH_FXP_MKDIR = 14; + private static final byte SSH_FXP_RMDIR = 15; + private static final byte SSH_FXP_REALPATH = 16; + private static final byte SSH_FXP_STAT = 17; + private static final byte SSH_FXP_RENAME = 18; + private static final byte SSH_FXP_READLINK = 19; + private static final byte SSH_FXP_SYMLINK = 20; + private static final byte SSH_FXP_STATUS = 101; + private static final byte SSH_FXP_HANDLE = 102; + private static final byte SSH_FXP_DATA = 103; + private static final byte SSH_FXP_NAME = 104; + private static final byte SSH_FXP_ATTRS = 105; + private static final byte SSH_FXP_EXTENDED = (byte) 200; + private static final byte SSH_FXP_EXTENDED_REPLY = (byte) 201; + + // pflags + private static final int SSH_FXF_READ = 0x00000001; + private static final int SSH_FXF_WRITE = 0x00000002; + private static final int SSH_FXF_APPEND = 0x00000004; + private static final int SSH_FXF_CREAT = 0x00000008; + private static final int SSH_FXF_TRUNC = 0x00000010; + private static final int SSH_FXF_EXCL = 0x00000020; + + private static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001; + private static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002; + private static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004; + private static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; + private static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000; + + public static final int SSH_FX_OK = 0; + public static final int SSH_FX_EOF = 1; + public static final int SSH_FX_NO_SUCH_FILE = 2; + public static final int SSH_FX_PERMISSION_DENIED = 3; + public static final int SSH_FX_FAILURE = 4; + public static final int SSH_FX_BAD_MESSAGE = 5; + public static final int SSH_FX_NO_CONNECTION = 6; + public static final int SSH_FX_CONNECTION_LOST = 7; + public static final int SSH_FX_OP_UNSUPPORTED = 8; + /* + * SSH_FX_OK Indicates successful completion of the operation. SSH_FX_EOF indicates end-of-file + * condition; for SSH_FX_READ it means that no more data is available in the file, and for + * SSH_FX_READDIR it indicates that no more files are contained in the directory. + * SSH_FX_NO_SUCH_FILE is returned when a reference is made to a file which should exist but + * doesn't. SSH_FX_PERMISSION_DENIED is returned when the authenticated user does not have + * sufficient permissions to perform the operation. SSH_FX_FAILURE is a generic catch-all error + * message; it should be returned if an error occurs for which there is no more specific error + * code defined. SSH_FX_BAD_MESSAGE may be returned if a badly formatted packet or protocol + * incompatibility is detected. SSH_FX_NO_CONNECTION is a pseudo-error which indicates that the + * client has no connection to the server (it can only be generated locally by the client, and + * MUST NOT be returned by servers). SSH_FX_CONNECTION_LOST is a pseudo-error which indicates that + * the connection to the server has been lost (it can only be generated locally by the client, and + * MUST NOT be returned by servers). SSH_FX_OP_UNSUPPORTED indicates that an attempt was made to + * perform an operation which is not supported for the server (it may be generated locally by the + * client if e.g. the version number exchange indicates that a required feature is not supported + * by the server, or it may be returned by the server if the server does not implement an + * operation). + */ + private static final int MAX_MSG_LENGTH = 256 * 1024; + + public static final int OVERWRITE = 0; + public static final int RESUME = 1; + public static final int APPEND = 2; + + private boolean interactive = false; + private int seq = 1; + private int[] ackid = new int[1]; + + private Buffer buf; + private Packet packet; + + // The followings will be used in file uploading. + private Buffer obuf; + private Packet opacket; + + private int client_version = 3; + private int server_version = 3; + private String version = String.valueOf(client_version); + + private Hashtable extensions = null; + private InputStream io_in = null; + + private boolean extension_posix_rename = false; + private boolean extension_statvfs = false; + // private boolean extension_fstatvfs = false; + private boolean extension_hardlink = false; + + /* + * 10. Changes from previous protocol versions The SSH File Transfer Protocol has changed over + * time, before it's standardization. The following is a description of the incompatible changes + * between different versions. 10.1 Changes between versions 3 and 2 o The SSH_FXP_READLINK and + * SSH_FXP_SYMLINK messages were added. o The SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY messages + * were added. o The SSH_FXP_STATUS message was changed to include fields `error message' and + * `language tag'. 10.2 Changes between versions 2 and 1 o The SSH_FXP_RENAME message was added. + * 10.3 Changes between versions 1 and 0 o Implementation changes, no actual protocol changes. + */ + + private static final String file_separator = File.separator; + private static final char file_separatorc = File.separatorChar; + private static boolean fs_is_bs = (byte) File.separatorChar == '\\'; + + private String cwd; + private String home; + private String lcwd; + + private Charset fEncoding = StandardCharsets.UTF_8; + private boolean fEncoding_is_utf8 = true; + + private boolean useWriteFlushWorkaround = true; + + private RequestQueue rq = new RequestQueue(16); + + /** + * Specify how many requests may be sent at any one time. Increasing this value may slightly + * improve file transfer speed but will increase memory usage. The default is 16 requests. + * + * @param bulk_requests how many requests may be outstanding at any one time. + */ + public void setBulkRequests(int bulk_requests) throws JSchException { + if (bulk_requests > 0) + rq = new RequestQueue(bulk_requests); + else + throw new JSchException("setBulkRequests: " + bulk_requests + " must be greater than 0."); + } + + /** + * This method will return the value how many requests may be sent at any one time. + * + * @return how many requests may be sent at any one time. + */ + public int getBulkRequests() { + return rq.size(); + } + + public void setUseWriteFlushWorkaround(boolean useWriteFlushWorkaround) { + this.useWriteFlushWorkaround = useWriteFlushWorkaround; + } + + public boolean getUseWriteFlushWorkaround() { + return useWriteFlushWorkaround; + } + + public ChannelSftp() { + super(); + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; + } + + @Override + void init() {} + + @Override + public void start() throws JSchException { + try { + + PipedOutputStream pos = new PipedOutputStream(); + io.setOutputStream(pos); + PipedInputStream pis = new MyPipedInputStream(pos, rq.size() * rmpsize); + io.setInputStream(pis); + + io_in = io.in; + + if (io_in == null) { + throw new JSchException("channel is down"); + } + + Request request = new RequestSftp(); + request.request(getSession(), this); + + /* + * System.err.println("lmpsize: "+lmpsize); System.err.println("lwsize: "+lwsize); + * System.err.println("rmpsize: "+rmpsize); System.err.println("rwsize: "+rwsize); + */ + + buf = new Buffer(lmpsize); + packet = new Packet(buf); + + obuf = new Buffer(rmpsize); + opacket = new Packet(obuf); + + int i = 0; + int length; + int type; + byte[] str; + + // send SSH_FXP_INIT + sendINIT(); + + // receive SSH_FXP_VERSION + Header header = new Header(); + header = header(buf, header); + length = header.length; + if (length > MAX_MSG_LENGTH) { + throw new SftpException(SSH_FX_FAILURE, "Received message is too long: " + length); + } + type = header.type; // 2 -> SSH_FXP_VERSION + server_version = header.rid; + // System.err.println("SFTP protocol server-version="+server_version); + extensions = new Hashtable<>(); + if (length > 0) { + // extension data + fill(buf, length); + byte[] extension_name = null; + byte[] extension_data = null; + while (length > 0) { + extension_name = buf.getString(); + length -= (4 + extension_name.length); + extension_data = buf.getString(); + length -= (4 + extension_data.length); + extensions.put(Util.byte2str(extension_name), Util.byte2str(extension_data)); + } + } + + if (extensions.get("posix-rename@openssh.com") != null + && extensions.get("posix-rename@openssh.com").equals("1")) { + extension_posix_rename = true; + } + + if (extensions.get("statvfs@openssh.com") != null + && extensions.get("statvfs@openssh.com").equals("2")) { + extension_statvfs = true; + } + + /* + * if(extensions.get("fstatvfs@openssh.com")!=null && + * extensions.get("fstatvfs@openssh.com").equals("2")){ extension_fstatvfs = true; } + */ + + if (extensions.get("hardlink@openssh.com") != null + && extensions.get("hardlink@openssh.com").equals("1")) { + extension_hardlink = true; + } + + lcwd = new File(".").getCanonicalPath(); + } catch (Exception e) { + // System.err.println(e); + if (e instanceof JSchException) + throw (JSchException) e; + throw new JSchException(e.toString(), e); + } + } + + public void quit() { + disconnect(); + } + + public void exit() { + disconnect(); + } + + public void lcd(String path) throws SftpException { + path = localAbsolutePath(path); + if ((new File(path)).isDirectory()) { + try { + path = (new File(path)).getCanonicalPath(); + } catch (Exception e) { + } + lcwd = path; + return; + } + throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such directory"); + } + + public void cd(String path) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + path = isUnique(path); + + byte[] str = _realpath(path); + SftpATTRS attr = _stat(str); + + if ((attr.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS) == 0) { + throw new SftpException(SSH_FX_FAILURE, "Can't change directory: " + path); + } + if (!attr.isDir()) { + throw new SftpException(SSH_FX_FAILURE, "Can't change directory: " + path); + } + + setCwd(Util.byte2str(str, fEncoding)); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void put(String src, String dst) throws SftpException { + put(src, dst, null, OVERWRITE); + } + + public void put(String src, String dst, int mode) throws SftpException { + put(src, dst, null, mode); + } + + public void put(String src, String dst, SftpProgressMonitor monitor) throws SftpException { + put(src, dst, monitor, OVERWRITE); + } + + /** + * Sends data from src file to dst file. The mode should be + * OVERWRITE, RESUME or APPEND. + * + * @param src source file + * @param dst destination file + * @param monitor progress monitor + * @param mode how data should be added to dst + */ + public void put(String src, String dst, SftpProgressMonitor monitor, int mode) + throws SftpException { + + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + src = localAbsolutePath(src); + dst = remoteAbsolutePath(dst); + + Vector v = glob_remote(dst); + int vsize = v.size(); + if (vsize != 1) { + if (vsize == 0) { + if (isPattern(dst)) + throw new SftpException(SSH_FX_FAILURE, dst); + else + dst = Util.unquote(dst); + } + throw new SftpException(SSH_FX_FAILURE, v.toString()); + } else { + dst = v.elementAt(0); + } + + boolean isRemoteDir = isRemoteDir(dst); + + v = glob_local(src); + vsize = v.size(); + + StringBuilder dstsb = null; + if (isRemoteDir) { + if (!dst.endsWith("/")) { + dst += "/"; + } + dstsb = new StringBuilder(dst); + } else if (vsize > 1) { + throw new SftpException(SSH_FX_FAILURE, + "Copying multiple files, but the destination is missing or a file."); + } + + for (int j = 0; j < vsize; j++) { + String _src = v.elementAt(j); + String _dst = null; + if (isRemoteDir) { + int i = _src.lastIndexOf(file_separatorc); + if (fs_is_bs) { + int ii = _src.lastIndexOf('/'); + if (ii != -1 && ii > i) + i = ii; + } + if (i == -1) + dstsb.append(_src); + else + dstsb.append(_src.substring(i + 1)); + _dst = dstsb.toString(); + dstsb.delete(dst.length(), _dst.length()); + } else { + _dst = dst; + } + // System.err.println("_dst "+_dst); + + long size_of_dst = 0; + if (mode == RESUME) { + try { + SftpATTRS attr = _stat(_dst); + size_of_dst = attr.getSize(); + } catch (Exception eee) { + // System.err.println(eee); + } + long size_of_src = new File(_src).length(); + if (size_of_src < size_of_dst) { + throw new SftpException(SSH_FX_FAILURE, "failed to resume for " + _dst); + } + if (size_of_src == size_of_dst) { + return; + } + } + + if (monitor != null) { + monitor.init(SftpProgressMonitor.PUT, _src, _dst, (new File(_src)).length()); + if (mode == RESUME) { + monitor.count(size_of_dst); + } + } + try (InputStream fis = new FileInputStream(_src)) { + _put(fis, _dst, monitor, mode); + } + } + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void put(InputStream src, String dst) throws SftpException { + put(src, dst, null, OVERWRITE); + } + + public void put(InputStream src, String dst, int mode) throws SftpException { + put(src, dst, null, mode); + } + + public void put(InputStream src, String dst, SftpProgressMonitor monitor) throws SftpException { + put(src, dst, monitor, OVERWRITE); + } + + /** + * Sends data from the input stream src to dst file. The + * mode should be OVERWRITE, RESUME or APPEND. + * + * @param src input stream + * @param dst destination file + * @param monitor progress monitor + * @param mode how data should be added to dst + */ + public void put(InputStream src, String dst, SftpProgressMonitor monitor, int mode) + throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + dst = remoteAbsolutePath(dst); + + Vector v = glob_remote(dst); + int vsize = v.size(); + if (vsize != 1) { + if (vsize == 0) { + if (isPattern(dst)) + throw new SftpException(SSH_FX_FAILURE, dst); + else + dst = Util.unquote(dst); + } + throw new SftpException(SSH_FX_FAILURE, v.toString()); + } else { + dst = v.elementAt(0); + } + + if (monitor != null) { + monitor.init(SftpProgressMonitor.PUT, "-", dst, SftpProgressMonitor.UNKNOWN_SIZE); + } + + _put(src, dst, monitor, mode); + } catch (Exception e) { + if (e instanceof SftpException) { + if (((SftpException) e).id == SSH_FX_FAILURE && isRemoteDir(dst)) { + throw new SftpException(SSH_FX_FAILURE, dst + " is a directory"); + } + throw (SftpException) e; + } + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void _put(InputStream src, String dst, SftpProgressMonitor monitor, int mode) + throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + byte[] dstb = Util.str2byte(dst, fEncoding); + long skip = 0; + if (mode == RESUME || mode == APPEND) { + try { + SftpATTRS attr = _stat(dstb); + skip = attr.getSize(); + } catch (Exception eee) { + // System.err.println(eee); + } + } + if (mode == RESUME && skip > 0) { + long skipped = src.skip(skip); + if (skipped < skip) { + throw new SftpException(SSH_FX_FAILURE, "failed to resume for " + dst); + } + } + + if (mode == OVERWRITE) { + sendOPENW(dstb); + } else { + sendOPENA(dstb); + } + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) { + throw new SftpException(SSH_FX_FAILURE, "invalid type=" + type); + } + if (type == SSH_FXP_STATUS) { + int i = buf.getInt(); + throwStatusError(buf, i); + } + byte[] handle = buf.getString(); // handle + byte[] data = null; + + boolean dontcopy = true; + + int buffer_margin = session.getBufferMargin(); + if (!dontcopy) { // This case will not work anymore. + data = new byte[obuf.buffer.length - (5 + 13 + 21 + handle.length + buffer_margin)]; + } + + long offset = 0; + if (mode == RESUME || mode == APPEND) { + offset += skip; + } + + int startid = seq; + int ackcount = 0; + int _s = 0; + int _datalen = 0; + + if (!dontcopy) { // This case will not work anymore. + _datalen = data.length; + } else { + data = obuf.buffer; + _s = 5 + 13 + 21 + handle.length; + _datalen = obuf.buffer.length - _s - buffer_margin; + } + + int bulk_requests = rq.size(); + + while (true) { + int nread = 0; + int count = 0; + int s = _s; + int datalen = _datalen; + + do { + nread = src.read(data, s, datalen); + if (nread > 0) { + s += nread; + datalen -= nread; + count += nread; + } + } while (datalen > 0 && nread > 0); + if (count <= 0) + break; + + int foo = count; + while (foo > 0) { + if ((seq - 1) == startid || ((seq - startid) - ackcount) >= bulk_requests) { + while (((seq - startid) - ackcount) >= bulk_requests) { + if (checkStatus(ackid, header)) { + int _ackid = ackid[0]; + if (startid > _ackid || _ackid > seq - 1) { + if (_ackid == seq) { + if (getSession().getLogger().isEnabled(Logger.ERROR)) { + getSession().getLogger().log(Logger.ERROR, + "ack error: startid=" + startid + " seq=" + seq + " _ackid=" + _ackid); + } + } else { + throw new SftpException(SSH_FX_FAILURE, + "ack error: startid=" + startid + " seq=" + seq + " _ackid=" + _ackid); + } + } + ackcount++; + } else { + break; + } + } + } + if (dontcopy) { + foo -= sendWRITE(handle, offset, data, 0, foo); + if (data != obuf.buffer) { + data = obuf.buffer; + _datalen = obuf.buffer.length - _s - buffer_margin; + } + } else { + foo -= sendWRITE(handle, offset, data, _s, foo); + } + } + offset += count; + if (monitor != null && !monitor.count(count)) { + break; + } + } + int _ackcount = seq - startid; + while (_ackcount > ackcount) { + if (!checkStatus(null, header)) { + break; + } + ackcount++; + } + if (monitor != null) + monitor.end(); + _sendCLOSE(handle, header); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public OutputStream put(String dst) throws SftpException { + return put(dst, (SftpProgressMonitor) null, OVERWRITE); + } + + public OutputStream put(String dst, final int mode) throws SftpException { + return put(dst, (SftpProgressMonitor) null, mode); + } + + public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode) + throws SftpException { + return put(dst, monitor, mode, 0); + } + + /** + * Sends data from the output stream to dst file. The mode should be + * OVERWRITE, RESUME or APPEND. + * + * @param dst destination file + * @param monitor progress monitor + * @param mode how data should be added to dst + * @param offset data will be added at offset + * @return output stream, which accepts data to be transferred. + */ + public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode, + long offset) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + dst = remoteAbsolutePath(dst); + dst = isUnique(dst); + + if (isRemoteDir(dst)) { + throw new SftpException(SSH_FX_FAILURE, dst + " is a directory"); + } + + byte[] dstb = Util.str2byte(dst, fEncoding); + + long skip = 0; + if (mode == RESUME || mode == APPEND) { + try { + SftpATTRS attr = _stat(dstb); + skip = attr.getSize(); + } catch (Exception eee) { + // System.err.println(eee); + } + } + + if (monitor != null) { + monitor.init(SftpProgressMonitor.PUT, "-", dst, SftpProgressMonitor.UNKNOWN_SIZE); + } + + if (mode == OVERWRITE) { + sendOPENW(dstb); + } else { + sendOPENA(dstb); + } + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + if (type == SSH_FXP_STATUS) { + int i = buf.getInt(); + throwStatusError(buf, i); + } + final byte[] handle = buf.getString(); // handle + + if (mode == RESUME || mode == APPEND) { + offset += skip; + } + + final long[] _offset = new long[1]; + _offset[0] = offset; + OutputStream out = new OutputStream() { + private boolean init = true; + private boolean isClosed = false; + private int[] ackid = new int[1]; + private int startid = 0; + private int _ackid = 0; + private int ackcount = 0; + private int writecount = 0; + private Header header = new Header(); + + @Override + public void write(byte[] d) throws IOException { + write(d, 0, d.length); + } + + @Override + public void write(byte[] d, int s, int len) throws IOException { + if (init) { + startid = seq; + _ackid = seq; + init = false; + } + + if (isClosed) { + throw new IOException("stream already closed"); + } + + try { + int _len = len; + while (_len > 0) { + if (useWriteFlushWorkaround && rwsize < 21 + handle.length + _len + 4) { + flush(); + } + int sent = sendWRITE(handle, _offset[0], d, s, _len); + writecount++; + _offset[0] += sent; + s += sent; + _len -= sent; + if ((seq - 1) == startid || io_in.available() >= 1024) { + while (io_in.available() > 0) { + if (checkStatus(ackid, header)) { + _ackid = ackid[0]; + if (startid > _ackid || _ackid > seq - 1) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + ackcount++; + } else { + break; + } + } + } + } + if (monitor != null && !monitor.count(len)) { + close(); + throw new IOException("canceled"); + } + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e.toString(), e); + } + } + + byte[] _data = new byte[1]; + + @Override + public void write(int foo) throws IOException { + _data[0] = (byte) foo; + write(_data, 0, 1); + } + + @Override + public void flush() throws IOException { + + if (isClosed) { + throw new IOException("stream already closed"); + } + + if (!init) { + try { + while (writecount > ackcount) { + if (!checkStatus(null, header)) { + break; + } + ackcount++; + } + } catch (SftpException e) { + throw new IOException(e.toString(), e); + } + } + } + + @Override + public void close() throws IOException { + if (isClosed) { + return; + } + flush(); + if (monitor != null) + monitor.end(); + try { + _sendCLOSE(handle, header); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e.toString(), e); + } + isClosed = true; + } + }; + return out; + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void get(String src, String dst) throws SftpException { + get(src, dst, null, OVERWRITE); + } + + public void get(String src, String dst, SftpProgressMonitor monitor) throws SftpException { + get(src, dst, monitor, OVERWRITE); + } + + public void get(String src, String dst, SftpProgressMonitor monitor, int mode) + throws SftpException { + // System.out.println("get: "+src+" "+dst); + + boolean _dstExist = false; + String _dst = null; + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + src = remoteAbsolutePath(src); + dst = localAbsolutePath(dst); + + Vector v = glob_remote(src); + int vsize = v.size(); + if (vsize == 0) { + throw new SftpException(SSH_FX_NO_SUCH_FILE, "No such file"); + } + + File dstFile = new File(dst); + boolean isDstDir = dstFile.isDirectory(); + StringBuilder dstsb = null; + if (isDstDir) { + if (!dst.endsWith(file_separator)) { + dst += file_separator; + } + dstsb = new StringBuilder(dst); + } else if (vsize > 1) { + throw new SftpException(SSH_FX_FAILURE, + "Copying multiple files, but destination is missing or a file."); + } + + for (int j = 0; j < vsize; j++) { + String _src = v.elementAt(j); + SftpATTRS attr = _stat(_src); + if (attr.isDir()) { + throw new SftpException(SSH_FX_FAILURE, "not supported to get directory " + _src); + } + + _dst = null; + if (isDstDir) { + int i = _src.lastIndexOf('/'); + if (i == -1) + dstsb.append(_src); + else + dstsb.append(_src.substring(i + 1)); + _dst = dstsb.toString(); + if (_dst.indexOf("..") != -1) { + String dstc = (new File(dst)).getCanonicalPath(); + String _dstc = (new File(_dst)).getCanonicalPath(); + if (!(_dstc.length() > dstc.length() + && _dstc.substring(0, dstc.length() + 1).equals(dstc + file_separator))) { + throw new SftpException(SSH_FX_FAILURE, "writing to an unexpected file " + _src); + } + } + dstsb.delete(dst.length(), _dst.length()); + } else { + _dst = dst; + } + + File _dstFile = new File(_dst); + if (mode == RESUME) { + long size_of_src = attr.getSize(); + long size_of_dst = _dstFile.length(); + if (size_of_dst > size_of_src) { + throw new SftpException(SSH_FX_FAILURE, "failed to resume for " + _dst); + } + if (size_of_dst == size_of_src) { + return; + } + } + + if (monitor != null) { + monitor.init(SftpProgressMonitor.GET, _src, _dst, attr.getSize()); + if (mode == RESUME) { + monitor.count(_dstFile.length()); + } + } + + _dstExist = _dstFile.exists(); + try (OutputStream fos = mode == OVERWRITE ? new FileOutputStream(_dst) + : new FileOutputStream(_dst, true) /* append */) { + // System.err.println("_get: "+_src+", "+_dst); + _get(_src, fos, monitor, mode, new File(_dst).length()); + } + } + } catch (Exception e) { + if (!_dstExist && _dst != null) { + File _dstFile = new File(_dst); + if (_dstFile.exists() && _dstFile.length() == 0) { + _dstFile.delete(); + } + } + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void get(String src, OutputStream dst) throws SftpException { + get(src, dst, null, OVERWRITE, 0); + } + + public void get(String src, OutputStream dst, SftpProgressMonitor monitor) throws SftpException { + get(src, dst, monitor, OVERWRITE, 0); + } + + public void get(String src, OutputStream dst, SftpProgressMonitor monitor, int mode, long skip) + throws SftpException { + // System.err.println("get: "+src+", "+dst); + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + src = remoteAbsolutePath(src); + src = isUnique(src); + + if (monitor != null) { + SftpATTRS attr = _stat(src); + monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize()); + if (mode == RESUME) { + monitor.count(skip); + } + } + _get(src, dst, monitor, mode, skip); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + private void _get(String src, OutputStream dst, SftpProgressMonitor monitor, int mode, long skip) + throws SftpException { + // System.err.println("_get: "+src+", "+dst); + + byte[] srcb = Util.str2byte(src, fEncoding); + try { + sendOPENR(srcb); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + + if (type == SSH_FXP_STATUS) { + int i = buf.getInt(); + throwStatusError(buf, i); + } + + byte[] handle = buf.getString(); // filename + + long offset = 0; + if (mode == RESUME) { + offset += skip; + } + + int request_max = 1; + rq.init(); + long request_offset = offset; + + int request_len = buf.buffer.length - 13; + if (server_version == 0) { + request_len = 1024; + } + + loop: while (true) { + + while (rq.count() < request_max) { + sendREAD(handle, request_offset, request_len, rq); + request_offset += request_len; + } + + header = header(buf, header); + length = header.length; + type = header.type; + + RequestQueue.Request rr = null; + try { + rr = rq.get(header.rid); + } catch (RequestQueue.OutOfOrderException e) { + request_offset = e.offset; + skip(header.length); + rq.cancel(header, buf); + continue; + } + + if (type == SSH_FXP_STATUS) { + fill(buf, length); + int i = buf.getInt(); + if (i == SSH_FX_EOF) { + break loop; + } + throwStatusError(buf, i); + } + + if (type != SSH_FXP_DATA) { + break loop; + } + + buf.rewind(); + fill(buf.buffer, 0, 4); + length -= 4; + int length_of_data = buf.getInt(); // length of data + + /* + * Since sftp protocol version 6, "end-of-file" has been defined, byte SSH_FXP_DATA uint32 + * request-id string data bool end-of-file [optional] but some sftpd server will send such a + * field in the sftp protocol 3 ;-( + */ + int optional_data = length - length_of_data; + + int foo = length_of_data; + while (foo > 0) { + int bar = foo; + if (bar > buf.buffer.length) { + bar = buf.buffer.length; + } + int data_len = io_in.read(buf.buffer, 0, bar); + if (data_len < 0) { + break loop; + } + + dst.write(buf.buffer, 0, data_len); + + offset += data_len; + foo -= data_len; + + if (monitor != null) { + if (!monitor.count(data_len)) { + skip(foo); + if (optional_data > 0) { + skip(optional_data); + } + break loop; + } + } + } + // System.err.println("length: "+length); // length should be 0 + + if (optional_data > 0) { + skip(optional_data); + } + + if (length_of_data < rr.length) { // + rq.cancel(header, buf); + sendREAD(handle, rr.offset + length_of_data, (int) (rr.length - length_of_data), rq); + request_offset = rr.offset + rr.length; + } + + if (request_max < rq.size()) { + request_max++; + } + } + dst.flush(); + + if (monitor != null) + monitor.end(); + + rq.cancel(header, buf); + + _sendCLOSE(handle, header); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + private class RequestQueue { + class OutOfOrderException extends Exception { + private static final long serialVersionUID = -1L; + long offset; + + OutOfOrderException(long offset) { + this.offset = offset; + } + } + + class Request { + int id; + long offset; + long length; + } + + Request[] rrq = null; + int head, count; + + RequestQueue(int size) { + rrq = new Request[size]; + for (int i = 0; i < rrq.length; i++) { + rrq[i] = new Request(); + } + init(); + } + + void init() { + head = count = 0; + } + + void add(int id, long offset, int length) { + if (count == 0) + head = 0; + int tail = head + count; + if (tail >= rrq.length) + tail -= rrq.length; + rrq[tail].id = id; + rrq[tail].offset = offset; + rrq[tail].length = length; + count++; + } + + Request get(int id) throws OutOfOrderException, SftpException { + count -= 1; + int i = head; + head++; + if (head == rrq.length) + head = 0; + if (rrq[i].id != id) { + long offset = getOffset(); + boolean find = false; + for (int j = 0; j < rrq.length; j++) { + if (rrq[j].id == id) { + find = true; + rrq[j].id = 0; + break; + } + } + if (find) + throw new OutOfOrderException(offset); + throw new SftpException(SSH_FX_FAILURE, "RequestQueue: unknown request id " + id); + } + rrq[i].id = 0; + return rrq[i]; + } + + int count() { + return count; + } + + int size() { + return rrq.length; + } + + void cancel(Header header, Buffer buf) throws IOException { + int _count = count; + for (int i = 0; i < _count; i++) { + header = header(buf, header); + int length = header.length; + for (int j = 0; j < rrq.length; j++) { + if (rrq[j].id == header.rid) { + rrq[j].id = 0; + break; + } + } + skip(length); + } + init(); + } + + long getOffset() { + long result = Long.MAX_VALUE; + + for (int i = 0; i < rrq.length; i++) { + if (rrq[i].id == 0) + continue; + if (result > rrq[i].offset) + result = rrq[i].offset; + } + + return result; + } + } + + public InputStream get(String src) throws SftpException { + return get(src, null, 0L); + } + + public InputStream get(String src, SftpProgressMonitor monitor) throws SftpException { + return get(src, monitor, 0L); + } + + /** + * @deprecated This method will be deleted in the future. + */ + @Deprecated + public InputStream get(String src, int mode) throws SftpException { + return get(src, null, 0L); + } + + /** + * @deprecated This method will be deleted in the future. + */ + @Deprecated + public InputStream get(String src, final SftpProgressMonitor monitor, final int mode) + throws SftpException { + return get(src, monitor, 0L); + } + + public InputStream get(String src, final SftpProgressMonitor monitor, final long skip) + throws SftpException { + + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + src = remoteAbsolutePath(src); + src = isUnique(src); + + byte[] srcb = Util.str2byte(src, fEncoding); + + SftpATTRS attr = _stat(srcb); + if (monitor != null) { + monitor.init(SftpProgressMonitor.GET, src, "??", attr.getSize()); + } + + sendOPENR(srcb); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + if (type == SSH_FXP_STATUS) { + int i = buf.getInt(); + throwStatusError(buf, i); + } + + final byte[] handle = buf.getString(); // handle + + rq.init(); + + InputStream in = new InputStream() { + long offset = skip; + boolean closed = false; + int rest_length = 0; + byte[] _data = new byte[1]; + byte[] rest_byte = new byte[1024]; + Header header = new Header(); + int request_max = 1; + long request_offset = offset; + + @Override + public int read() throws IOException { + if (closed) + return -1; + int i = read(_data, 0, 1); + if (i == -1) { + return -1; + } else { + return _data[0] & 0xff; + } + } + + @Override + public int read(byte[] d) throws IOException { + if (closed) + return -1; + return read(d, 0, d.length); + } + + @Override + public int read(byte[] d, int s, int len) throws IOException { + if (closed) + return -1; + if (d == null) { + throw new NullPointerException(); + } + if (s < 0 || len < 0 || s + len > d.length) { + throw new IndexOutOfBoundsException(); + } + if (len == 0) { + return 0; + } + + if (rest_length > 0) { + int foo = rest_length; + if (foo > len) + foo = len; + System.arraycopy(rest_byte, 0, d, s, foo); + if (foo != rest_length) { + System.arraycopy(rest_byte, foo, rest_byte, 0, rest_length - foo); + } + + if (monitor != null) { + if (!monitor.count(foo)) { + close(); + return -1; + } + } + + rest_length -= foo; + return foo; + } + + if (buf.buffer.length - 13 < len) { + len = buf.buffer.length - 13; + } + if (server_version == 0 && len > 1024) { + len = 1024; + } + + if (rq.count() == 0 || true // working around slow transfer speed for + // some sftp servers including Titan FTP. + ) { + int request_len = buf.buffer.length - 13; + if (server_version == 0) { + request_len = 1024; + } + + while (rq.count() < request_max) { + try { + sendREAD(handle, request_offset, request_len, rq); + } catch (Exception e) { + throw new IOException("error"); + } + request_offset += request_len; + } + } + + header = header(buf, header); + rest_length = header.length; + int type = header.type; + int id = header.rid; + + RequestQueue.Request rr = null; + try { + rr = rq.get(header.rid); + } catch (RequestQueue.OutOfOrderException e) { + request_offset = e.offset; + skip(header.length); + rq.cancel(header, buf); + return 0; + } catch (SftpException e) { + throw new IOException("error: " + e.toString(), e); + } + + if (type != SSH_FXP_STATUS && type != SSH_FXP_DATA) { + throw new IOException("error"); + } + if (type == SSH_FXP_STATUS) { + fill(buf, rest_length); + int i = buf.getInt(); + rest_length = 0; + if (i == SSH_FX_EOF) { + close(); + return -1; + } + // throwStatusError(buf, i); + throw new IOException("error"); + } + + buf.rewind(); + fill(buf.buffer, 0, 4); + int length_of_data = buf.getInt(); + rest_length -= 4; + + /* + * Since sftp protocol version 6, "end-of-file" has been defined, byte SSH_FXP_DATA uint32 + * request-id string data bool end-of-file [optional] but some sftpd server will send such + * a field in the sftp protocol 3 ;-( + */ + int optional_data = rest_length - length_of_data; + + offset += length_of_data; + int foo = length_of_data; + if (foo > 0) { + int bar = foo; + if (bar > len) { + bar = len; + } + int i = io_in.read(d, s, bar); + if (i < 0) { + return -1; + } + foo -= i; + rest_length = foo; + + if (foo > 0) { + if (rest_byte.length < foo) { + rest_byte = new byte[foo]; + } + int _s = 0; + int _len = foo; + int j; + while (_len > 0) { + j = io_in.read(rest_byte, _s, _len); + if (j <= 0) + break; + _s += j; + _len -= j; + } + } + + if (optional_data > 0) { + io_in.skip(optional_data); + } + + if (length_of_data < rr.length) { // + rq.cancel(header, buf); + try { + sendREAD(handle, rr.offset + length_of_data, (int) (rr.length - length_of_data), + rq); + } catch (Exception e) { + throw new IOException("error"); + } + request_offset = rr.offset + rr.length; + } + + if (request_max < rq.size()) { + request_max++; + } + + if (monitor != null) { + if (!monitor.count(i)) { + close(); + return -1; + } + } + + return i; + } + return 0; // ?? + } + + @Override + public void close() throws IOException { + if (closed) + return; + closed = true; + if (monitor != null) + monitor.end(); + rq.cancel(header, buf); + try { + _sendCLOSE(handle, header); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOException(e.toString(), e); + } + } + }; + return in; + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public Vector ls(String path) throws SftpException { + final Vector v = new Vector<>(); + LsEntrySelector selector = new LsEntrySelector() { + @Override + public int select(LsEntry entry) { + v.addElement(entry); + return CONTINUE; + } + }; + ls(path, selector); + return v; + } + + /** + * List files specified by the remote path. Each files and directories will be passed + * to LsEntrySelector#select(LsEntry) method, and if that method returns + * LsEntrySelector#BREAK, the operation will be canceled immediately. + * + * @see LsEntrySelector + * @since 0.1.47 + */ + public void ls(String path, LsEntrySelector selector) throws SftpException { + // System.out.println("ls: "+path); + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + byte[] pattern = null; + Vector v = new Vector<>(); + + int foo = path.lastIndexOf('/'); + String dir = path.substring(0, ((foo == 0) ? 1 : foo)); + String _pattern = path.substring(foo + 1); + dir = Util.unquote(dir); + + // If pattern has included '*' or '?', we need to convert + // to UTF-8 string before globbing. + byte[][] _pattern_utf8 = new byte[1][]; + boolean pattern_has_wildcard = isPattern(_pattern, _pattern_utf8); + + if (pattern_has_wildcard) { + pattern = _pattern_utf8[0]; + } else { + String upath = Util.unquote(path); + // SftpATTRS attr=_lstat(upath); + SftpATTRS attr = _stat(upath); + if (attr.isDir()) { + pattern = null; + dir = upath; + } else { + /* + * // If we can generage longname by ourself, // we don't have to use openDIR. String + * filename=Util.unquote(_pattern); String longname=... v.addElement(new LsEntry(filename, + * longname, attr)); return v; + */ + + if (fEncoding_is_utf8) { + pattern = _pattern_utf8[0]; + pattern = Util.unquote(pattern); + } else { + _pattern = Util.unquote(_pattern); + pattern = Util.str2byte(_pattern, fEncoding); + } + } + } + + sendOPENDIR(Util.str2byte(dir, fEncoding)); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + if (type == SSH_FXP_STATUS) { + int i = buf.getInt(); + throwStatusError(buf, i); + } + + int cancel = LsEntrySelector.CONTINUE; + byte[] handle = buf.getString(); // handle + + while (cancel == LsEntrySelector.CONTINUE) { + + sendREADDIR(handle); + + header = header(buf, header); + length = header.length; + type = header.type; + if (type != SSH_FXP_STATUS && type != SSH_FXP_NAME) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + if (type == SSH_FXP_STATUS) { + fill(buf, length); + int i = buf.getInt(); + if (i == SSH_FX_EOF) + break; + throwStatusError(buf, i); + } + + buf.rewind(); + fill(buf.buffer, 0, 4); + length -= 4; + int count = buf.getInt(); + + byte[] str; + int flags; + + buf.reset(); + while (count > 0) { + if (length > 0) { + buf.shift(); + int j = (buf.buffer.length > (buf.index + length)) ? length + : (buf.buffer.length - buf.index); + int i = fill(buf.buffer, buf.index, j); + buf.index += i; + length -= i; + } + byte[] filename = buf.getString(); + byte[] longname = null; + if (server_version <= 3) { + longname = buf.getString(); + } + SftpATTRS attrs = SftpATTRS.getATTR(buf); + + if (cancel == LsEntrySelector.BREAK) { + count--; + continue; + } + + boolean find = false; + String f = null; + if (pattern == null) { + find = true; + } else if (!pattern_has_wildcard) { + find = Util.array_equals(pattern, filename); + } else { + byte[] _filename = filename; + if (!fEncoding_is_utf8) { + f = Util.byte2str(_filename, fEncoding); + _filename = Util.str2byte(f, StandardCharsets.UTF_8); + } + find = Util.glob(pattern, _filename); + } + + if (find) { + if (f == null) { + f = Util.byte2str(filename, fEncoding); + } + String l = null; + if (longname == null) { + // TODO: we need to generate long name from attrs + // for the sftp protocol 4(and later). + l = attrs.toString() + " " + f; + } else { + l = Util.byte2str(longname, fEncoding); + } + + cancel = selector.select(new LsEntry(f, l, attrs)); + } + + count--; + } + } + _sendCLOSE(handle, header); + + /* + * if(v.size()==1 && pattern_has_wildcard){ LsEntry le=(LsEntry)v.elementAt(0); + * if(le.getAttrs().isDir()){ String f=le.getFilename(); if(isPattern(f)){ f=Util.quote(f); } + * if(!dir.endsWith("/")){ dir+="/"; } v=null; return ls(dir+f); } } + */ + + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public String readlink(String path) throws SftpException { + try { + if (server_version < 3) { + throw new SftpException(SSH_FX_OP_UNSUPPORTED, + "The remote sshd is too old to support symlink operation."); + } + + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + + path = isUnique(path); + + sendREADLINK(Util.str2byte(path, fEncoding)); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS && type != SSH_FXP_NAME) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + if (type == SSH_FXP_NAME) { + int count = buf.getInt(); // count + byte[] filename = null; + for (int i = 0; i < count; i++) { + filename = buf.getString(); + if (server_version <= 3) { + byte[] longname = buf.getString(); + } + SftpATTRS.getATTR(buf); + } + return Util.byte2str(filename, fEncoding); + } + + int i = buf.getInt(); + throwStatusError(buf, i); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + return null; + } + + public void symlink(String oldpath, String newpath) throws SftpException { + if (server_version < 3) { + throw new SftpException(SSH_FX_OP_UNSUPPORTED, + "The remote sshd is too old to support symlink operation."); + } + + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + String _oldpath = remoteAbsolutePath(oldpath); + newpath = remoteAbsolutePath(newpath); + + _oldpath = isUnique(_oldpath); + if (oldpath.charAt(0) != '/') { // relative path + String cwd = getCwd(); + oldpath = _oldpath.substring(cwd.length() + (cwd.endsWith("/") ? 0 : 1)); + } else { + oldpath = _oldpath; + } + + if (isPattern(newpath)) { + throw new SftpException(SSH_FX_FAILURE, newpath); + } + newpath = Util.unquote(newpath); + + sendSYMLINK(Util.str2byte(oldpath, fEncoding), Util.str2byte(newpath, fEncoding)); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + + int i = buf.getInt(); + if (i == SSH_FX_OK) + return; + throwStatusError(buf, i); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void hardlink(String oldpath, String newpath) throws SftpException { + if (!extension_hardlink) { + throw new SftpException(SSH_FX_OP_UNSUPPORTED, "hardlink@openssh.com is not supported"); + } + + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + String _oldpath = remoteAbsolutePath(oldpath); + newpath = remoteAbsolutePath(newpath); + + _oldpath = isUnique(_oldpath); + if (oldpath.charAt(0) != '/') { // relative path + String cwd = getCwd(); + oldpath = _oldpath.substring(cwd.length() + (cwd.endsWith("/") ? 0 : 1)); + } else { + oldpath = _oldpath; + } + + if (isPattern(newpath)) { + throw new SftpException(SSH_FX_FAILURE, newpath); + } + newpath = Util.unquote(newpath); + + sendHARDLINK(Util.str2byte(oldpath, fEncoding), Util.str2byte(newpath, fEncoding)); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + + int i = buf.getInt(); + if (i == SSH_FX_OK) + return; + throwStatusError(buf, i); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void rename(String oldpath, String newpath) throws SftpException { + if (server_version < 2) { + throw new SftpException(SSH_FX_OP_UNSUPPORTED, + "The remote sshd is too old to support rename operation."); + } + + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + oldpath = remoteAbsolutePath(oldpath); + newpath = remoteAbsolutePath(newpath); + + oldpath = isUnique(oldpath); + + Vector v = glob_remote(newpath); + int vsize = v.size(); + if (vsize >= 2) { + throw new SftpException(SSH_FX_FAILURE, v.toString()); + } + if (vsize == 1) { + newpath = v.elementAt(0); + } else { // vsize==0 + if (isPattern(newpath)) + throw new SftpException(SSH_FX_FAILURE, newpath); + newpath = Util.unquote(newpath); + } + + sendRENAME(Util.str2byte(oldpath, fEncoding), Util.str2byte(newpath, fEncoding)); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + + int i = buf.getInt(); + if (i == SSH_FX_OK) + return; + throwStatusError(buf, i); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void rm(String path) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + + Vector v = glob_remote(path); + int vsize = v.size(); + + Header header = new Header(); + + for (int j = 0; j < vsize; j++) { + path = v.elementAt(j); + sendREMOVE(Util.str2byte(path, fEncoding)); + + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + int i = buf.getInt(); + if (i != SSH_FX_OK) { + throwStatusError(buf, i); + } + } + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + private boolean isRemoteDir(String path) { + try { + sendSTAT(Util.str2byte(path, fEncoding)); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_ATTRS) { + return false; + } + SftpATTRS attr = SftpATTRS.getATTR(buf); + return attr.isDir(); + } catch (Exception e) { + } + return false; + } + + public void chgrp(int gid, String path) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + + Vector v = glob_remote(path); + int vsize = v.size(); + for (int j = 0; j < vsize; j++) { + path = v.elementAt(j); + + SftpATTRS attr = _stat(path); + + attr.setFLAGS(0); + attr.setUIDGID(attr.uid, gid); + _setStat(path, attr); + } + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void chown(int uid, String path) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + + Vector v = glob_remote(path); + int vsize = v.size(); + for (int j = 0; j < vsize; j++) { + path = v.elementAt(j); + + SftpATTRS attr = _stat(path); + + attr.setFLAGS(0); + attr.setUIDGID(uid, attr.gid); + _setStat(path, attr); + } + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void chmod(int permissions, String path) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + + Vector v = glob_remote(path); + int vsize = v.size(); + for (int j = 0; j < vsize; j++) { + path = v.elementAt(j); + + SftpATTRS attr = _stat(path); + + attr.setFLAGS(0); + attr.setPERMISSIONS(permissions); + _setStat(path, attr); + } + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void setMtime(String path, int mtime) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + + Vector v = glob_remote(path); + int vsize = v.size(); + for (int j = 0; j < vsize; j++) { + path = v.elementAt(j); + + SftpATTRS attr = _stat(path); + + attr.setFLAGS(0); + attr.setACMODTIME(attr.getATime(), mtime); + _setStat(path, attr); + } + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void rmdir(String path) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + + Vector v = glob_remote(path); + int vsize = v.size(); + + Header header = new Header(); + + for (int j = 0; j < vsize; j++) { + path = v.elementAt(j); + sendRMDIR(Util.str2byte(path, fEncoding)); + + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + + int i = buf.getInt(); + if (i != SSH_FX_OK) { + throwStatusError(buf, i); + } + } + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void mkdir(String path) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + + sendMKDIR(Util.str2byte(path, fEncoding), null); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + + int i = buf.getInt(); + if (i == SSH_FX_OK) + return; + throwStatusError(buf, i); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public SftpATTRS stat(String path) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + path = isUnique(path); + + return _stat(path); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + // return null; + } + + private SftpATTRS _stat(byte[] path) throws SftpException { + try { + + sendSTAT(path); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_ATTRS) { + if (type == SSH_FXP_STATUS) { + int i = buf.getInt(); + throwStatusError(buf, i); + } + throw new SftpException(SSH_FX_FAILURE, ""); + } + SftpATTRS attr = SftpATTRS.getATTR(buf); + return attr; + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + // return null; + } + + private SftpATTRS _stat(String path) throws SftpException { + return _stat(Util.str2byte(path, fEncoding)); + } + + public SftpStatVFS statVFS(String path) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + path = isUnique(path); + + return _statVFS(path); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + // return null; + } + + private SftpStatVFS _statVFS(byte[] path) throws SftpException { + if (!extension_statvfs) { + throw new SftpException(SSH_FX_OP_UNSUPPORTED, "statvfs@openssh.com is not supported"); + } + + try { + + sendSTATVFS(path); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != (SSH_FXP_EXTENDED_REPLY & 0xff)) { + if (type == SSH_FXP_STATUS) { + int i = buf.getInt(); + throwStatusError(buf, i); + } + throw new SftpException(SSH_FX_FAILURE, ""); + } else { + SftpStatVFS stat = SftpStatVFS.getStatVFS(buf); + return stat; + } + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + // return null; + } + + private SftpStatVFS _statVFS(String path) throws SftpException { + return _statVFS(Util.str2byte(path, fEncoding)); + } + + public SftpATTRS lstat(String path) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + path = isUnique(path); + + return _lstat(path); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + private SftpATTRS _lstat(String path) throws SftpException { + try { + sendLSTAT(Util.str2byte(path, fEncoding)); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_ATTRS) { + if (type == SSH_FXP_STATUS) { + int i = buf.getInt(); + throwStatusError(buf, i); + } + throw new SftpException(SSH_FX_FAILURE, ""); + } + SftpATTRS attr = SftpATTRS.getATTR(buf); + return attr; + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + private byte[] _realpath(String path) throws SftpException, IOException, Exception { + sendREALPATH(Util.str2byte(path, fEncoding)); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS && type != SSH_FXP_NAME) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + int i; + if (type == SSH_FXP_STATUS) { + i = buf.getInt(); + throwStatusError(buf, i); + } + i = buf.getInt(); // count + + byte[] str = null; + while (i-- > 0) { + str = buf.getString(); // absolute path; + if (server_version <= 3) { + byte[] lname = buf.getString(); // long filename + } + SftpATTRS attr = SftpATTRS.getATTR(buf); // dummy attribute + } + return str; + } + + public void setStat(String path, SftpATTRS attr) throws SftpException { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + path = remoteAbsolutePath(path); + + Vector v = glob_remote(path); + int vsize = v.size(); + for (int j = 0; j < vsize; j++) { + path = v.elementAt(j); + _setStat(path, attr); + } + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + private void _setStat(String path, SftpATTRS attr) throws SftpException { + try { + sendSETSTAT(Util.str2byte(path, fEncoding), attr); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + int i = buf.getInt(); + if (i != SSH_FX_OK) { + throwStatusError(buf, i); + } + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public String pwd() throws SftpException { + return getCwd(); + } + + public String lpwd() { + return lcwd; + } + + public String version() { + return version; + } + + public String getHome() throws SftpException { + if (home == null) { + try { + ((MyPipedInputStream) io_in).updateReadSide(); + + byte[] _home = _realpath(""); + home = Util.byte2str(_home, fEncoding); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + return home; + } + + private String getCwd() throws SftpException { + if (cwd == null) + cwd = getHome(); + return cwd; + } + + private void setCwd(String cwd) { + this.cwd = cwd; + } + + private void read(byte[] buf, int s, int l) throws IOException, SftpException { + int i = 0; + while (l > 0) { + i = io_in.read(buf, s, l); + if (i <= 0) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + s += i; + l -= i; + } + } + + private boolean checkStatus(int[] ackid, Header header) throws IOException, SftpException { + header = header(buf, header); + int length = header.length; + int type = header.type; + if (ackid != null) + ackid[0] = header.rid; + + fill(buf, length); + + if (type != SSH_FXP_STATUS) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + int i = buf.getInt(); + if (i != SSH_FX_OK) { + throwStatusError(buf, i); + } + return true; + } + + private boolean _sendCLOSE(byte[] handle, Header header) throws Exception { + sendCLOSE(handle); + return checkStatus(null, header); + } + + private void sendINIT() throws Exception { + packet.reset(); + putHEAD(SSH_FXP_INIT, 5); + buf.putInt(3); // version 3 + getSession().write(packet, this, 5 + 4); + } + + private void sendREALPATH(byte[] path) throws Exception { + sendPacketPath(SSH_FXP_REALPATH, path); + } + + private void sendSTAT(byte[] path) throws Exception { + sendPacketPath(SSH_FXP_STAT, path); + } + + private void sendSTATVFS(byte[] path) throws Exception { + sendPacketPath((byte) 0, path, "statvfs@openssh.com"); + } + + /* + * private void sendFSTATVFS(byte[] handle) throws Exception{ sendPacketPath((byte)0, handle, + * "fstatvfs@openssh.com"); } + */ + private void sendLSTAT(byte[] path) throws Exception { + sendPacketPath(SSH_FXP_LSTAT, path); + } + + private void sendFSTAT(byte[] handle) throws Exception { + sendPacketPath(SSH_FXP_FSTAT, handle); + } + + private void sendSETSTAT(byte[] path, SftpATTRS attr) throws Exception { + packet.reset(); + putHEAD(SSH_FXP_SETSTAT, 9 + path.length + attr.length()); + buf.putInt(seq++); + buf.putString(path); // path + attr.dump(buf); + getSession().write(packet, this, 9 + path.length + attr.length() + 4); + } + + private void sendREMOVE(byte[] path) throws Exception { + sendPacketPath(SSH_FXP_REMOVE, path); + } + + private void sendMKDIR(byte[] path, SftpATTRS attr) throws Exception { + packet.reset(); + putHEAD(SSH_FXP_MKDIR, 9 + path.length + (attr != null ? attr.length() : 4)); + buf.putInt(seq++); + buf.putString(path); // path + if (attr != null) + attr.dump(buf); + else + buf.putInt(0); + getSession().write(packet, this, 9 + path.length + (attr != null ? attr.length() : 4) + 4); + } + + private void sendRMDIR(byte[] path) throws Exception { + sendPacketPath(SSH_FXP_RMDIR, path); + } + + private void sendSYMLINK(byte[] p1, byte[] p2) throws Exception { + sendPacketPath(SSH_FXP_SYMLINK, p1, p2); + } + + private void sendHARDLINK(byte[] p1, byte[] p2) throws Exception { + sendPacketPath((byte) 0, p1, p2, "hardlink@openssh.com"); + } + + private void sendREADLINK(byte[] path) throws Exception { + sendPacketPath(SSH_FXP_READLINK, path); + } + + private void sendOPENDIR(byte[] path) throws Exception { + sendPacketPath(SSH_FXP_OPENDIR, path); + } + + private void sendREADDIR(byte[] path) throws Exception { + sendPacketPath(SSH_FXP_READDIR, path); + } + + private void sendRENAME(byte[] p1, byte[] p2) throws Exception { + sendPacketPath(SSH_FXP_RENAME, p1, p2, + extension_posix_rename ? "posix-rename@openssh.com" : null); + } + + private void sendCLOSE(byte[] path) throws Exception { + sendPacketPath(SSH_FXP_CLOSE, path); + } + + private void sendOPENR(byte[] path) throws Exception { + sendOPEN(path, SSH_FXF_READ); + } + + private void sendOPENW(byte[] path) throws Exception { + sendOPEN(path, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); + } + + private void sendOPENA(byte[] path) throws Exception { + sendOPEN(path, SSH_FXF_WRITE | /* SSH_FXF_APPEND | */ SSH_FXF_CREAT); + } + + private void sendOPEN(byte[] path, int mode) throws Exception { + packet.reset(); + putHEAD(SSH_FXP_OPEN, 17 + path.length); + buf.putInt(seq++); + buf.putString(path); + buf.putInt(mode); + buf.putInt(0); // attrs + getSession().write(packet, this, 17 + path.length + 4); + } + + private void sendPacketPath(byte fxp, byte[] path) throws Exception { + sendPacketPath(fxp, path, (String) null); + } + + private void sendPacketPath(byte fxp, byte[] path, String extension) throws Exception { + packet.reset(); + int len = 9 + path.length; + if (extension == null) { + putHEAD(fxp, len); + buf.putInt(seq++); + } else { + len += (4 + extension.length()); + putHEAD(SSH_FXP_EXTENDED, len); + buf.putInt(seq++); + buf.putString(Util.str2byte(extension)); + } + buf.putString(path); // path + getSession().write(packet, this, len + 4); + } + + private void sendPacketPath(byte fxp, byte[] p1, byte[] p2) throws Exception { + sendPacketPath(fxp, p1, p2, null); + } + + private void sendPacketPath(byte fxp, byte[] p1, byte[] p2, String extension) throws Exception { + packet.reset(); + int len = 13 + p1.length + p2.length; + if (extension == null) { + putHEAD(fxp, len); + buf.putInt(seq++); + } else { + len += (4 + extension.length()); + putHEAD(SSH_FXP_EXTENDED, len); + buf.putInt(seq++); + buf.putString(Util.str2byte(extension)); + } + buf.putString(p1); + buf.putString(p2); + getSession().write(packet, this, len + 4); + } + + private int sendWRITE(byte[] handle, long offset, byte[] data, int start, int length) + throws Exception { + int _length = length; + opacket.reset(); + Session _session = getSession(); + int buffer_margin = _session.getBufferMargin(); + if (obuf.buffer.length < obuf.index + 13 + 21 + handle.length + length + buffer_margin) { + _length = obuf.buffer.length - (obuf.index + 13 + 21 + handle.length + buffer_margin); + // System.err.println("_length="+_length+" length="+length); + } + + putHEAD(obuf, SSH_FXP_WRITE, 21 + handle.length + _length); // 14 + obuf.putInt(seq++); // 4 + obuf.putString(handle); // 4+handle.length + obuf.putLong(offset); // 8 + if (obuf.buffer != data) { + obuf.putString(data, start, _length); // 4+_length + } else { + obuf.putInt(_length); + obuf.skip(_length); + } + _session.write(opacket, this, 21 + handle.length + _length + 4); + return _length; + } + + private void sendREAD(byte[] handle, long offset, int length) throws Exception { + sendREAD(handle, offset, length, null); + } + + private void sendREAD(byte[] handle, long offset, int length, RequestQueue rrq) throws Exception { + packet.reset(); + putHEAD(SSH_FXP_READ, 21 + handle.length); + buf.putInt(seq++); + buf.putString(handle); + buf.putLong(offset); + buf.putInt(length); + getSession().write(packet, this, 21 + handle.length + 4); + if (rrq != null) { + rrq.add(seq - 1, offset, length); + } + } + + private void putHEAD(Buffer buf, byte type, int length) throws Exception { + buf.putByte((byte) Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(length + 4); + buf.putInt(length); + buf.putByte(type); + } + + private void putHEAD(byte type, int length) throws Exception { + putHEAD(buf, type, length); + } + + private Vector glob_remote(String _path) throws Exception { + Vector v = new Vector<>(); + int i = 0; + + int foo = _path.lastIndexOf('/'); + if (foo < 0) { // it is not absolute path. + v.addElement(Util.unquote(_path)); + return v; + } + + String dir = _path.substring(0, ((foo == 0) ? 1 : foo)); + String _pattern = _path.substring(foo + 1); + + dir = Util.unquote(dir); + + byte[] pattern = null; + byte[][] _pattern_utf8 = new byte[1][]; + boolean pattern_has_wildcard = isPattern(_pattern, _pattern_utf8); + + if (!pattern_has_wildcard) { + if (!dir.equals("/")) + dir += "/"; + v.addElement(dir + Util.unquote(_pattern)); + return v; + } + + pattern = _pattern_utf8[0]; + + sendOPENDIR(Util.str2byte(dir, fEncoding)); + + Header header = new Header(); + header = header(buf, header); + int length = header.length; + int type = header.type; + + fill(buf, length); + + if (type != SSH_FXP_STATUS && type != SSH_FXP_HANDLE) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + if (type == SSH_FXP_STATUS) { + i = buf.getInt(); + throwStatusError(buf, i); + } + + byte[] handle = buf.getString(); // filename + String pdir = null; // parent directory + + while (true) { + sendREADDIR(handle); + header = header(buf, header); + length = header.length; + type = header.type; + + if (type != SSH_FXP_STATUS && type != SSH_FXP_NAME) { + throw new SftpException(SSH_FX_FAILURE, ""); + } + if (type == SSH_FXP_STATUS) { + fill(buf, length); + break; + } + + buf.rewind(); + fill(buf.buffer, 0, 4); + length -= 4; + int count = buf.getInt(); + + byte[] str; + int flags; + + buf.reset(); + while (count > 0) { + if (length > 0) { + buf.shift(); + int j = + (buf.buffer.length > (buf.index + length)) ? length : (buf.buffer.length - buf.index); + i = io_in.read(buf.buffer, buf.index, j); + if (i <= 0) + break; + buf.index += i; + length -= i; + } + + byte[] filename = buf.getString(); + // System.err.println("filename: "+new String(filename)); + if (server_version <= 3) { + str = buf.getString(); // longname + } + SftpATTRS attrs = SftpATTRS.getATTR(buf); + + byte[] _filename = filename; + String f = null; + boolean found = false; + + if (!fEncoding_is_utf8) { + f = Util.byte2str(filename, fEncoding); + _filename = Util.str2byte(f, StandardCharsets.UTF_8); + } + found = Util.glob(pattern, _filename); + + if (found) { + if (f == null) { + f = Util.byte2str(filename, fEncoding); + } + if (pdir == null) { + pdir = dir; + if (!pdir.endsWith("/")) { + pdir += "/"; + } + } + v.addElement(pdir + f); + } + count--; + } + } + if (_sendCLOSE(handle, header)) + return v; + return null; + } + + private boolean isPattern(byte[] path) { + int length = path.length; + int i = 0; + while (i < length) { + if (path[i] == '*' || path[i] == '?') + return true; + if (path[i] == '\\' && (i + 1) < length) + i++; + i++; + } + return false; + } + + private Vector glob_local(String _path) throws Exception { + // System.err.println("glob_local: "+_path); + Vector v = new Vector<>(); + byte[] path = Util.str2byte(_path, StandardCharsets.UTF_8); + int i = path.length - 1; + while (i >= 0) { + if (path[i] != '*' && path[i] != '?') { + i--; + continue; + } + if (!fs_is_bs && i > 0 && path[i - 1] == '\\') { + i--; + if (i > 0 && path[i - 1] == '\\') { + i--; + i--; + continue; + } + } + break; + } + + if (i < 0) { + v.addElement(fs_is_bs ? _path : Util.unquote(_path)); + return v; + } + + while (i >= 0) { + if (path[i] == file_separatorc || (fs_is_bs && path[i] == '/')) { // On Windows, '/' is also + // the separator. + break; + } + i--; + } + + if (i < 0) { + v.addElement(fs_is_bs ? _path : Util.unquote(_path)); + return v; + } + + byte[] dir; + if (i == 0) { + dir = new byte[] {(byte) file_separatorc}; + } else { + dir = new byte[i]; + System.arraycopy(path, 0, dir, 0, i); + } + + byte[] pattern = new byte[path.length - i - 1]; + System.arraycopy(path, i + 1, pattern, 0, pattern.length); + + // System.err.println("dir: "+new String(dir)+" pattern: "+new String(pattern)); + try { + String[] children = (new File(Util.byte2str(dir, StandardCharsets.UTF_8))).list(); + String pdir = Util.byte2str(dir) + file_separator; + for (int j = 0; j < children.length; j++) { + // System.err.println("children: "+children[j]); + if (Util.glob(pattern, Util.str2byte(children[j], StandardCharsets.UTF_8))) { + v.addElement(pdir + children[j]); + } + } + } catch (Exception e) { + } + return v; + } + + private void throwStatusError(Buffer buf, int i) throws SftpException { + if (server_version >= 3 && // WindRiver's sftp will send invalid + buf.getLength() >= 4) { // SSH_FXP_STATUS packet. + byte[] str = buf.getString(); + // byte[] tag=buf.getString(); + throw new SftpException(i, Util.byte2str(str, StandardCharsets.UTF_8)); + } else { + throw new SftpException(i, "Failure"); + } + } + + private static boolean isLocalAbsolutePath(String path) { + return (new File(path)).isAbsolute(); + } + + @Override + public void disconnect() { + super.disconnect(); + } + + private boolean isPattern(String path, byte[][] utf8) { + byte[] _path = Util.str2byte(path, StandardCharsets.UTF_8); + if (utf8 != null) + utf8[0] = _path; + return isPattern(_path); + } + + private boolean isPattern(String path) { + return isPattern(path, null); + } + + private void fill(Buffer buf, int len) throws IOException { + buf.reset(); + fill(buf.buffer, 0, len); + buf.skip(len); + } + + private int fill(byte[] buf, int s, int len) throws IOException { + int i = 0; + int foo = s; + while (len > 0) { + i = io_in.read(buf, s, len); + if (i <= 0) { + throw new IOException("inputstream is closed"); + // return (s-foo)==0 ? i : s-foo; + } + s += i; + len -= i; + } + return s - foo; + } + + private void skip(long foo) throws IOException { + while (foo > 0) { + long bar = io_in.skip(foo); + if (bar <= 0) + break; + foo -= bar; + } + } + + static class Header { + int length; + int type; + int rid; + } + + private Header header(Buffer buf, Header header) throws IOException { + buf.rewind(); + int i = fill(buf.buffer, 0, 9); + header.length = buf.getInt() - 5; + header.type = buf.getByte() & 0xff; + header.rid = buf.getInt(); + return header; + } + + private String remoteAbsolutePath(String path) throws SftpException { + if (path.charAt(0) == '/') + return path; + String cwd = getCwd(); + // if(cwd.equals(getHome())) return path; + if (cwd.endsWith("/")) + return cwd + path; + return cwd + "/" + path; + } + + private String localAbsolutePath(String path) { + if (isLocalAbsolutePath(path)) + return path; + if (lcwd.endsWith(file_separator)) + return lcwd + path; + return lcwd + file_separator + path; + } + + /** + * This method will check if the given string can be expanded to the unique string. If it can be + * expanded to mutiple files, SftpException will be thrown. + * + * @return the returned string is unquoted. + */ + private String isUnique(String path) throws SftpException, Exception { + Vector v = glob_remote(path); + if (v.size() != 1) { + throw new SftpException(SSH_FX_FAILURE, path + " is not unique: " + v.toString()); + } + return v.elementAt(0); + } + + public int getServerVersion() throws SftpException { + if (!isConnected()) { + throw new SftpException(SSH_FX_FAILURE, "The channel is not connected."); + } + return server_version; + } + + @Deprecated + public void setFilenameEncoding(String encoding) throws SftpException { + try { + setFilenameEncoding(Charset.forName(encoding)); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public void setFilenameEncoding(Charset encoding) { + fEncoding = encoding; + fEncoding_is_utf8 = fEncoding.equals(StandardCharsets.UTF_8); + } + + public String getExtension(String key) { + if (extensions == null) + return null; + return extensions.get(key); + } + + public String realpath(String path) throws SftpException { + try { + byte[] _path = _realpath(remoteAbsolutePath(path)); + return Util.byte2str(_path, fEncoding); + } catch (Exception e) { + if (e instanceof SftpException) + throw (SftpException) e; + throw new SftpException(SSH_FX_FAILURE, e.toString(), e); + } + } + + public static class LsEntry implements Comparable { + private String filename; + private String longname; + private SftpATTRS attrs; + + LsEntry(String filename, String longname, SftpATTRS attrs) { + setFilename(filename); + setLongname(longname); + setAttrs(attrs); + } + + public String getFilename() { + return filename; + }; + + void setFilename(String filename) { + this.filename = filename; + }; + + public String getLongname() { + return longname; + }; + + void setLongname(String longname) { + this.longname = longname; + }; + + public SftpATTRS getAttrs() { + return attrs; + }; + + void setAttrs(SftpATTRS attrs) { + this.attrs = attrs; + }; + + @Override + public String toString() { + return longname; + } + + @Override + public int compareTo(LsEntry o) { + return filename.compareTo(o.getFilename()); + } + } + + /** + * This interface will be passed as an argument for ls method. + * + * @see LsEntry + * @see #ls(String, LsEntrySelector) + * @since 0.1.47 + */ + public interface LsEntrySelector { + public final int CONTINUE = 0; + public final int BREAK = 1; + + /** + * The select method will be invoked in ls method for each file entry. + * If this method returns BREAK, ls will be canceled. + * + * @param entry one of entry from ls + * @return if BREAK is returned, the 'ls' operation will be canceled. + */ + public int select(LsEntry entry); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ChannelShell.java b/files-jsch/src/main/java/com/jcraft/jsch/ChannelShell.java new file mode 100644 index 0000000..7fc3d3d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ChannelShell.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class ChannelShell extends ChannelSession { + + ChannelShell() { + super(); + pty = true; + } + + @Override + public void start() throws JSchException { + Session _session = getSession(); + try { + sendRequests(); + + Request request = new RequestShell(); + request.request(_session, this); + } catch (Exception e) { + if (e instanceof JSchException) + throw (JSchException) e; + throw new JSchException("ChannelShell", e); + } + + if (io.in != null) { + thread = new Thread(this::run); + thread.setName("Shell for " + _session.host); + if (_session.daemon_thread) { + thread.setDaemon(_session.daemon_thread); + } + thread.start(); + } + } + + @Override + void init() throws JSchException { + io.setInputStream(getSession().in); + io.setOutputStream(getSession().out); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ChannelSubsystem.java b/files-jsch/src/main/java/com/jcraft/jsch/ChannelSubsystem.java new file mode 100644 index 0000000..537ae39 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ChannelSubsystem.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class ChannelSubsystem extends ChannelSession { + boolean want_reply = true; + String subsystem = ""; + + public void setWantReply(boolean foo) { + want_reply = foo; + } + + public void setSubsystem(String foo) { + subsystem = foo; + } + + @Override + public void start() throws JSchException { + Session _session = getSession(); + try { + Request request; + if (xforwading) { + request = new RequestX11(); + request.request(_session, this); + } + if (pty) { + request = new RequestPtyReq(); + request.request(_session, this); + } + request = new RequestSubsystem(); + ((RequestSubsystem) request).request(_session, this, subsystem, want_reply); + } catch (Exception e) { + if (e instanceof JSchException) { + throw (JSchException) e; + } + throw new JSchException("ChannelSubsystem", e); + } + if (io.in != null) { + thread = new Thread(this::run); + thread.setName("Subsystem for " + _session.host); + if (_session.daemon_thread) { + thread.setDaemon(_session.daemon_thread); + } + thread.start(); + } + } + + @Override + void init() throws JSchException { + io.setInputStream(getSession().in); + io.setOutputStream(getSession().out); + } + + public void setErrStream(OutputStream out) { + setExtOutputStream(out); + } + + public InputStream getErrStream() throws IOException { + return getExtInputStream(); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ChannelX11.java b/files-jsch/src/main/java/com/jcraft/jsch/ChannelX11.java new file mode 100644 index 0000000..92e29d2 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ChannelX11.java @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.net.Socket; +import java.util.Hashtable; + +class ChannelX11 extends Channel { + + private static final int LOCAL_WINDOW_SIZE_MAX = 0x20000; + private static final int LOCAL_MAXIMUM_PACKET_SIZE = 0x4000; + + private static final int TIMEOUT = 10 * 1000; + + private static String host = "127.0.0.1"; + private static int port = 6000; + + private boolean init = true; + + static byte[] cookie = null; + private static byte[] cookie_hex = null; + + private static Hashtable faked_cookie_pool = new Hashtable<>(); + private static Hashtable faked_cookie_hex_pool = new Hashtable<>(); + + private static byte[] table = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, + 0x62, 0x63, 0x64, 0x65, 0x66}; + + private Socket socket = null; + + static int revtable(byte foo) { + for (int i = 0; i < table.length; i++) { + if (table[i] == foo) + return i; + } + return 0; + } + + static void setCookie(String foo) { + cookie_hex = Util.str2byte(foo); + cookie = new byte[16]; + for (int i = 0; i < 16; i++) { + cookie[i] = (byte) (((revtable(cookie_hex[i * 2]) << 4) & 0xf0) + | ((revtable(cookie_hex[i * 2 + 1])) & 0xf)); + } + } + + static void setHost(String foo) { + host = foo; + } + + static void setPort(int foo) { + port = foo; + } + + static byte[] getFakedCookie(Session session) { + synchronized (faked_cookie_hex_pool) { + byte[] foo = faked_cookie_hex_pool.get(session); + if (foo == null) { + Random random = Session.random; + foo = new byte[16]; + synchronized (random) { + random.fill(foo, 0, 16); + } + /* + * System.err.print("faked_cookie: "); for(int i=0; i>> 4) & 0xf]; + bar[2 * i + 1] = table[(foo[i]) & 0xf]; + } + faked_cookie_hex_pool.put(session, bar); + foo = bar; + } + return foo; + } + } + + static void removeFakedCookie(Session session) { + synchronized (faked_cookie_hex_pool) { + faked_cookie_hex_pool.remove(session); + faked_cookie_pool.remove(session); + } + } + + ChannelX11() { + super(); + + lwsize_max = LOCAL_WINDOW_SIZE_MAX; + lwsize = LOCAL_WINDOW_SIZE_MAX; + lmpsize = LOCAL_MAXIMUM_PACKET_SIZE; + + type = Util.str2byte("x11"); + + connected = true; + /* + * try{ socket=Util.createSocket(host, port, TIMEOUT); socket.setTcpNoDelay(true); io=new IO(); + * io.setInputStream(socket.getInputStream()); io.setOutputStream(socket.getOutputStream()); } + * catch(Exception e){ //System.err.println(e); } + */ + } + + @Override + void run() { + + try { + socket = Util.createSocket(host, port, TIMEOUT); + socket.setTcpNoDelay(true); + io = new IO(); + io.setInputStream(socket.getInputStream()); + io.setOutputStream(socket.getOutputStream()); + sendOpenConfirmation(); + } catch (Exception e) { + sendOpenFailure(SSH_OPEN_ADMINISTRATIVELY_PROHIBITED); + close = true; + disconnect(); + return; + } + + thread = Thread.currentThread(); + Buffer buf = new Buffer(rmpsize); + Packet packet = new Packet(buf); + int i = 0; + try { + Session _session = getSession(); + while (thread != null && io != null && io.in != null) { + i = io.in.read(buf.buffer, 14, buf.buffer.length - 14 - _session.getBufferMargin()); + if (i <= 0) { + eof(); + break; + } + if (close) + break; + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_DATA); + buf.putInt(recipient); + buf.putInt(i); + buf.skip(i); + _session.write(packet, this, i); + } + } catch (Exception e) { + // System.err.println(e); + } + disconnect(); + } + + private byte[] cache = new byte[0]; + + private byte[] addCache(byte[] foo, int s, int l) { + byte[] bar = new byte[cache.length + l]; + System.arraycopy(foo, s, bar, cache.length, l); + if (cache.length > 0) + System.arraycopy(cache, 0, bar, 0, cache.length); + cache = bar; + return cache; + } + + @Override + void write(byte[] foo, int s, int l) throws IOException { + // if(eof_local)return; + + if (init) { + + Session _session = null; + try { + _session = getSession(); + } catch (JSchException e) { + throw new IOException(e.toString(), e); + } + + foo = addCache(foo, s, l); + s = 0; + l = foo.length; + + if (l < 9) + return; + + int plen = (foo[s + 6] & 0xff) * 256 + (foo[s + 7] & 0xff); + int dlen = (foo[s + 8] & 0xff) * 256 + (foo[s + 9] & 0xff); + + if ((foo[s] & 0xff) == 0x42) { + } else if ((foo[s] & 0xff) == 0x6c) { + plen = ((plen >>> 8) & 0xff) | ((plen << 8) & 0xff00); + dlen = ((dlen >>> 8) & 0xff) | ((dlen << 8) & 0xff00); + } else { + // ?? + } + + if (l < 12 + plen + ((-plen) & 3) + dlen) + return; + + byte[] bar = new byte[dlen]; + System.arraycopy(foo, s + 12 + plen + ((-plen) & 3), bar, 0, dlen); + byte[] faked_cookie = null; + + synchronized (faked_cookie_pool) { + faked_cookie = faked_cookie_pool.get(_session); + } + + /* + * System.err.print("faked_cookie: "); for(int i=0; i c = Class.forName(session.getConfig(sha_name)).asSubclass(HASH.class); + sha = c.getDeclaredConstructor().newInstance(); + sha.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + buf = new Buffer(); + packet = new Packet(buf); + + packet.reset(); + buf.putByte((byte) SSH_MSG_KEX_ECDH_INIT); + + try { + Class c = + Class.forName(session.getConfig("ecdh-sha2-nistp")).asSubclass(ECDH.class); + ecdh = c.getDeclaredConstructor().newInstance(); + ecdh.init(key_size); + + Q_C = ecdh.getQ(); + buf.putString(Q_C); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + if (V_S == null) { // This is a really ugly hack for Session.checkKexes ;-( + return; + } + + session.write(packet); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "SSH_MSG_KEX_ECDH_INIT sent"); + session.getLogger().log(Logger.INFO, "expecting SSH_MSG_KEX_ECDH_REPLY"); + } + + state = SSH_MSG_KEX_ECDH_REPLY; + } + + @Override + public boolean next(Buffer _buf) throws Exception { + int i, j; + switch (state) { + case SSH_MSG_KEX_ECDH_REPLY: + // The server responds with: + // byte SSH_MSG_KEX_ECDH_REPLY + // string K_S, server's public host key + // string Q_S, server's ephemeral public key octet string + // string the signature on the exchange hash + j = _buf.getInt(); + j = _buf.getByte(); + j = _buf.getByte(); + if (j != SSH_MSG_KEX_ECDH_REPLY) { + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_ECDH_REPLY " + j); + } + return false; + } + + K_S = _buf.getString(); + + byte[] Q_S = _buf.getString(); + + byte[][] r_s = KeyPairECDSA.fromPoint(Q_S); + + // RFC 5656, + // 4. ECDH Key Exchange + // All elliptic curve public keys MUST be validated after they are + // received. An example of a validation algorithm can be found in + // Section 3.2.2 of [SEC1]. If a key fails validation, + // the key exchange MUST fail. + if (!ecdh.validate(r_s[0], r_s[1])) { + return false; + } + + K = encodeAsMPInt(normalize(ecdh.getSecret(r_s[0], r_s[1]))); + + byte[] sig_of_H = _buf.getString(); + + // The hash H is computed as the HASH hash of the concatenation of the + // following: + // string V_C, client's identification string (CR and LF excluded) + // string V_S, server's identification string (CR and LF excluded) + // string I_C, payload of the client's SSH_MSG_KEXINIT + // string I_S, payload of the server's SSH_MSG_KEXINIT + // string K_S, server's public host key + // string Q_C, client's ephemeral public key octet string + // string Q_S, server's ephemeral public key octet string + // mpint K, shared secret + + // This value is called the exchange hash, and it is used to authenti- + // cate the key exchange. + buf.reset(); + buf.putString(V_C); + buf.putString(V_S); + buf.putString(I_C); + buf.putString(I_S); + buf.putString(K_S); + buf.putString(Q_C); + buf.putString(Q_S); + byte[] foo = new byte[buf.getLength()]; + buf.getByte(foo); + + sha.update(foo, 0, foo.length); + sha.update(K, 0, K.length); + H = sha.digest(); + + i = 0; + j = 0; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + String alg = Util.byte2str(K_S, i, j); + i += j; + + boolean result = verify(alg, K_S, i, sig_of_H); + + state = STATE_END; + return result; + } + return false; + } + + @Override + public int getState() { + return state; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG1.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG1.java new file mode 100644 index 0000000..1620861 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG1.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG1 extends DHGN { + + static final byte[] g = {2}; + static final byte[] p = {(byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9, (byte) 0x0F, (byte) 0xDA, + (byte) 0xA2, (byte) 0x21, (byte) 0x68, (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, + (byte) 0x62, (byte) 0x8B, (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1, (byte) 0x29, + (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A, (byte) 0x67, (byte) 0xCC, (byte) 0x74, + (byte) 0x02, (byte) 0x0B, (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B, + (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79, (byte) 0x8E, (byte) 0x34, + (byte) 0x04, (byte) 0xDD, (byte) 0xEF, (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, + (byte) 0x3A, (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A, (byte) 0x6D, + (byte) 0xF2, (byte) 0x5F, (byte) 0x14, (byte) 0x37, (byte) 0x4F, (byte) 0xE1, (byte) 0x35, + (byte) 0x6D, (byte) 0x6D, (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85, + (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E, (byte) 0x7E, (byte) 0xC6, (byte) 0xF4, + (byte) 0x4C, (byte) 0x42, (byte) 0xE9, (byte) 0xA6, (byte) 0x37, (byte) 0xED, (byte) 0x6B, + (byte) 0x0B, (byte) 0xFF, (byte) 0x5C, (byte) 0xB6, (byte) 0xF4, (byte) 0x06, (byte) 0xB7, + (byte) 0xED, (byte) 0xEE, (byte) 0x38, (byte) 0x6B, (byte) 0xFB, (byte) 0x5A, (byte) 0x89, + (byte) 0x9F, (byte) 0xA5, (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C, + (byte) 0x4B, (byte) 0x1F, (byte) 0xE6, (byte) 0x49, (byte) 0x28, (byte) 0x66, (byte) 0x51, + (byte) 0xEC, (byte) 0xE6, (byte) 0x53, (byte) 0x81, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + + @Override + byte[] G() { + return g; + } + + @Override + byte[] P() { + return p; + } + + @Override + String sha_name() { + return "sha-1"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG14.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG14.java new file mode 100644 index 0000000..d72a471 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG14.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG14 extends DHG14N { + + @Override + String sha_name() { + return "sha-1"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG14224.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG14224.java new file mode 100644 index 0000000..27e8475 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG14224.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG14224 extends DHG14N { + + @Override + String sha_name() { + return "sha-224"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG14256.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG14256.java new file mode 100644 index 0000000..8193308 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG14256.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG14256 extends DHG14N { + + @Override + String sha_name() { + return "sha-256"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG14N.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG14N.java new file mode 100644 index 0000000..31e1fba --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG14N.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +abstract class DHG14N extends DHGN { + + static final byte[] g = {2}; + static final byte[] p = {(byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9, (byte) 0x0F, (byte) 0xDA, + (byte) 0xA2, (byte) 0x21, (byte) 0x68, (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, + (byte) 0x62, (byte) 0x8B, (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1, (byte) 0x29, + (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A, (byte) 0x67, (byte) 0xCC, (byte) 0x74, + (byte) 0x02, (byte) 0x0B, (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B, + (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79, (byte) 0x8E, (byte) 0x34, + (byte) 0x04, (byte) 0xDD, (byte) 0xEF, (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, + (byte) 0x3A, (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A, (byte) 0x6D, + (byte) 0xF2, (byte) 0x5F, (byte) 0x14, (byte) 0x37, (byte) 0x4F, (byte) 0xE1, (byte) 0x35, + (byte) 0x6D, (byte) 0x6D, (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85, + (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E, (byte) 0x7E, (byte) 0xC6, (byte) 0xF4, + (byte) 0x4C, (byte) 0x42, (byte) 0xE9, (byte) 0xA6, (byte) 0x37, (byte) 0xED, (byte) 0x6B, + (byte) 0x0B, (byte) 0xFF, (byte) 0x5C, (byte) 0xB6, (byte) 0xF4, (byte) 0x06, (byte) 0xB7, + (byte) 0xED, (byte) 0xEE, (byte) 0x38, (byte) 0x6B, (byte) 0xFB, (byte) 0x5A, (byte) 0x89, + (byte) 0x9F, (byte) 0xA5, (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C, + (byte) 0x4B, (byte) 0x1F, (byte) 0xE6, (byte) 0x49, (byte) 0x28, (byte) 0x66, (byte) 0x51, + (byte) 0xEC, (byte) 0xE4, (byte) 0x5B, (byte) 0x3D, (byte) 0xC2, (byte) 0x00, (byte) 0x7C, + (byte) 0xB8, (byte) 0xA1, (byte) 0x63, (byte) 0xBF, (byte) 0x05, (byte) 0x98, (byte) 0xDA, + (byte) 0x48, (byte) 0x36, (byte) 0x1C, (byte) 0x55, (byte) 0xD3, (byte) 0x9A, (byte) 0x69, + (byte) 0x16, (byte) 0x3F, (byte) 0xA8, (byte) 0xFD, (byte) 0x24, (byte) 0xCF, (byte) 0x5F, + (byte) 0x83, (byte) 0x65, (byte) 0x5D, (byte) 0x23, (byte) 0xDC, (byte) 0xA3, (byte) 0xAD, + (byte) 0x96, (byte) 0x1C, (byte) 0x62, (byte) 0xF3, (byte) 0x56, (byte) 0x20, (byte) 0x85, + (byte) 0x52, (byte) 0xBB, (byte) 0x9E, (byte) 0xD5, (byte) 0x29, (byte) 0x07, (byte) 0x70, + (byte) 0x96, (byte) 0x96, (byte) 0x6D, (byte) 0x67, (byte) 0x0C, (byte) 0x35, (byte) 0x4E, + (byte) 0x4A, (byte) 0xBC, (byte) 0x98, (byte) 0x04, (byte) 0xF1, (byte) 0x74, (byte) 0x6C, + (byte) 0x08, (byte) 0xCA, (byte) 0x18, (byte) 0x21, (byte) 0x7C, (byte) 0x32, (byte) 0x90, + (byte) 0x5E, (byte) 0x46, (byte) 0x2E, (byte) 0x36, (byte) 0xCE, (byte) 0x3B, (byte) 0xE3, + (byte) 0x9E, (byte) 0x77, (byte) 0x2C, (byte) 0x18, (byte) 0x0E, (byte) 0x86, (byte) 0x03, + (byte) 0x9B, (byte) 0x27, (byte) 0x83, (byte) 0xA2, (byte) 0xEC, (byte) 0x07, (byte) 0xA2, + (byte) 0x8F, (byte) 0xB5, (byte) 0xC5, (byte) 0x5D, (byte) 0xF0, (byte) 0x6F, (byte) 0x4C, + (byte) 0x52, (byte) 0xC9, (byte) 0xDE, (byte) 0x2B, (byte) 0xCB, (byte) 0xF6, (byte) 0x95, + (byte) 0x58, (byte) 0x17, (byte) 0x18, (byte) 0x39, (byte) 0x95, (byte) 0x49, (byte) 0x7C, + (byte) 0xEA, (byte) 0x95, (byte) 0x6A, (byte) 0xE5, (byte) 0x15, (byte) 0xD2, (byte) 0x26, + (byte) 0x18, (byte) 0x98, (byte) 0xFA, (byte) 0x05, (byte) 0x10, (byte) 0x15, (byte) 0x72, + (byte) 0x8E, (byte) 0x5A, (byte) 0x8A, (byte) 0xAC, (byte) 0xAA, (byte) 0x68, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + + @Override + byte[] G() { + return g; + } + + @Override + byte[] P() { + return p; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG15.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG15.java new file mode 100644 index 0000000..0c13ee4 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG15.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG15 extends DHG15N { + + @Override + String sha_name() { + return "sha-512"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG15256.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG15256.java new file mode 100644 index 0000000..a441e27 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG15256.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG15256 extends DHG15N { + + @Override + String sha_name() { + return "sha-256"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG15384.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG15384.java new file mode 100644 index 0000000..076d8c3 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG15384.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG15384 extends DHG15N { + + @Override + String sha_name() { + return "sha-384"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG15N.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG15N.java new file mode 100644 index 0000000..d2572ee --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG15N.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +abstract class DHG15N extends DHGN { + + static final byte[] g = {2}; + static final byte[] p = {(byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9, (byte) 0x0F, (byte) 0xDA, + (byte) 0xA2, (byte) 0x21, (byte) 0x68, (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, + (byte) 0x62, (byte) 0x8B, (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1, (byte) 0x29, + (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A, (byte) 0x67, (byte) 0xCC, (byte) 0x74, + (byte) 0x02, (byte) 0x0B, (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B, + (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79, (byte) 0x8E, (byte) 0x34, + (byte) 0x04, (byte) 0xDD, (byte) 0xEF, (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, + (byte) 0x3A, (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A, (byte) 0x6D, + (byte) 0xF2, (byte) 0x5F, (byte) 0x14, (byte) 0x37, (byte) 0x4F, (byte) 0xE1, (byte) 0x35, + (byte) 0x6D, (byte) 0x6D, (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85, + (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E, (byte) 0x7E, (byte) 0xC6, (byte) 0xF4, + (byte) 0x4C, (byte) 0x42, (byte) 0xE9, (byte) 0xA6, (byte) 0x37, (byte) 0xED, (byte) 0x6B, + (byte) 0x0B, (byte) 0xFF, (byte) 0x5C, (byte) 0xB6, (byte) 0xF4, (byte) 0x06, (byte) 0xB7, + (byte) 0xED, (byte) 0xEE, (byte) 0x38, (byte) 0x6B, (byte) 0xFB, (byte) 0x5A, (byte) 0x89, + (byte) 0x9F, (byte) 0xA5, (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C, + (byte) 0x4B, (byte) 0x1F, (byte) 0xE6, (byte) 0x49, (byte) 0x28, (byte) 0x66, (byte) 0x51, + (byte) 0xEC, (byte) 0xE4, (byte) 0x5B, (byte) 0x3D, (byte) 0xC2, (byte) 0x00, (byte) 0x7C, + (byte) 0xB8, (byte) 0xA1, (byte) 0x63, (byte) 0xBF, (byte) 0x05, (byte) 0x98, (byte) 0xDA, + (byte) 0x48, (byte) 0x36, (byte) 0x1C, (byte) 0x55, (byte) 0xD3, (byte) 0x9A, (byte) 0x69, + (byte) 0x16, (byte) 0x3F, (byte) 0xA8, (byte) 0xFD, (byte) 0x24, (byte) 0xCF, (byte) 0x5F, + (byte) 0x83, (byte) 0x65, (byte) 0x5D, (byte) 0x23, (byte) 0xDC, (byte) 0xA3, (byte) 0xAD, + (byte) 0x96, (byte) 0x1C, (byte) 0x62, (byte) 0xF3, (byte) 0x56, (byte) 0x20, (byte) 0x85, + (byte) 0x52, (byte) 0xBB, (byte) 0x9E, (byte) 0xD5, (byte) 0x29, (byte) 0x07, (byte) 0x70, + (byte) 0x96, (byte) 0x96, (byte) 0x6D, (byte) 0x67, (byte) 0x0C, (byte) 0x35, (byte) 0x4E, + (byte) 0x4A, (byte) 0xBC, (byte) 0x98, (byte) 0x04, (byte) 0xF1, (byte) 0x74, (byte) 0x6C, + (byte) 0x08, (byte) 0xCA, (byte) 0x18, (byte) 0x21, (byte) 0x7C, (byte) 0x32, (byte) 0x90, + (byte) 0x5E, (byte) 0x46, (byte) 0x2E, (byte) 0x36, (byte) 0xCE, (byte) 0x3B, (byte) 0xE3, + (byte) 0x9E, (byte) 0x77, (byte) 0x2C, (byte) 0x18, (byte) 0x0E, (byte) 0x86, (byte) 0x03, + (byte) 0x9B, (byte) 0x27, (byte) 0x83, (byte) 0xA2, (byte) 0xEC, (byte) 0x07, (byte) 0xA2, + (byte) 0x8F, (byte) 0xB5, (byte) 0xC5, (byte) 0x5D, (byte) 0xF0, (byte) 0x6F, (byte) 0x4C, + (byte) 0x52, (byte) 0xC9, (byte) 0xDE, (byte) 0x2B, (byte) 0xCB, (byte) 0xF6, (byte) 0x95, + (byte) 0x58, (byte) 0x17, (byte) 0x18, (byte) 0x39, (byte) 0x95, (byte) 0x49, (byte) 0x7C, + (byte) 0xEA, (byte) 0x95, (byte) 0x6A, (byte) 0xE5, (byte) 0x15, (byte) 0xD2, (byte) 0x26, + (byte) 0x18, (byte) 0x98, (byte) 0xFA, (byte) 0x05, (byte) 0x10, (byte) 0x15, (byte) 0x72, + (byte) 0x8E, (byte) 0x5A, (byte) 0x8A, (byte) 0xAA, (byte) 0xC4, (byte) 0x2D, (byte) 0xAD, + (byte) 0x33, (byte) 0x17, (byte) 0x0D, (byte) 0x04, (byte) 0x50, (byte) 0x7A, (byte) 0x33, + (byte) 0xA8, (byte) 0x55, (byte) 0x21, (byte) 0xAB, (byte) 0xDF, (byte) 0x1C, (byte) 0xBA, + (byte) 0x64, (byte) 0xEC, (byte) 0xFB, (byte) 0x85, (byte) 0x04, (byte) 0x58, (byte) 0xDB, + (byte) 0xEF, (byte) 0x0A, (byte) 0x8A, (byte) 0xEA, (byte) 0x71, (byte) 0x57, (byte) 0x5D, + (byte) 0x06, (byte) 0x0C, (byte) 0x7D, (byte) 0xB3, (byte) 0x97, (byte) 0x0F, (byte) 0x85, + (byte) 0xA6, (byte) 0xE1, (byte) 0xE4, (byte) 0xC7, (byte) 0xAB, (byte) 0xF5, (byte) 0xAE, + (byte) 0x8C, (byte) 0xDB, (byte) 0x09, (byte) 0x33, (byte) 0xD7, (byte) 0x1E, (byte) 0x8C, + (byte) 0x94, (byte) 0xE0, (byte) 0x4A, (byte) 0x25, (byte) 0x61, (byte) 0x9D, (byte) 0xCE, + (byte) 0xE3, (byte) 0xD2, (byte) 0x26, (byte) 0x1A, (byte) 0xD2, (byte) 0xEE, (byte) 0x6B, + (byte) 0xF1, (byte) 0x2F, (byte) 0xFA, (byte) 0x06, (byte) 0xD9, (byte) 0x8A, (byte) 0x08, + (byte) 0x64, (byte) 0xD8, (byte) 0x76, (byte) 0x02, (byte) 0x73, (byte) 0x3E, (byte) 0xC8, + (byte) 0x6A, (byte) 0x64, (byte) 0x52, (byte) 0x1F, (byte) 0x2B, (byte) 0x18, (byte) 0x17, + (byte) 0x7B, (byte) 0x20, (byte) 0x0C, (byte) 0xBB, (byte) 0xE1, (byte) 0x17, (byte) 0x57, + (byte) 0x7A, (byte) 0x61, (byte) 0x5D, (byte) 0x6C, (byte) 0x77, (byte) 0x09, (byte) 0x88, + (byte) 0xC0, (byte) 0xBA, (byte) 0xD9, (byte) 0x46, (byte) 0xE2, (byte) 0x08, (byte) 0xE2, + (byte) 0x4F, (byte) 0xA0, (byte) 0x74, (byte) 0xE5, (byte) 0xAB, (byte) 0x31, (byte) 0x43, + (byte) 0xDB, (byte) 0x5B, (byte) 0xFC, (byte) 0xE0, (byte) 0xFD, (byte) 0x10, (byte) 0x8E, + (byte) 0x4B, (byte) 0x82, (byte) 0xD1, (byte) 0x20, (byte) 0xA9, (byte) 0x3A, (byte) 0xD2, + (byte) 0xCA, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF}; + + @Override + byte[] G() { + return g; + } + + @Override + byte[] P() { + return p; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG16.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG16.java new file mode 100644 index 0000000..5555e93 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG16.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG16 extends DHG16N { + + @Override + String sha_name() { + return "sha-512"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG16384.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG16384.java new file mode 100644 index 0000000..61ac2c2 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG16384.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG16384 extends DHG16N { + + @Override + String sha_name() { + return "sha-384"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG16N.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG16N.java new file mode 100644 index 0000000..96078a1 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG16N.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +abstract class DHG16N extends DHGN { + + static final byte[] g = {2}; + static final byte[] p = {(byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9, (byte) 0x0F, (byte) 0xDA, + (byte) 0xA2, (byte) 0x21, (byte) 0x68, (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, + (byte) 0x62, (byte) 0x8B, (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1, (byte) 0x29, + (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A, (byte) 0x67, (byte) 0xCC, (byte) 0x74, + (byte) 0x02, (byte) 0x0B, (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B, + (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79, (byte) 0x8E, (byte) 0x34, + (byte) 0x04, (byte) 0xDD, (byte) 0xEF, (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, + (byte) 0x3A, (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A, (byte) 0x6D, + (byte) 0xF2, (byte) 0x5F, (byte) 0x14, (byte) 0x37, (byte) 0x4F, (byte) 0xE1, (byte) 0x35, + (byte) 0x6D, (byte) 0x6D, (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85, + (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E, (byte) 0x7E, (byte) 0xC6, (byte) 0xF4, + (byte) 0x4C, (byte) 0x42, (byte) 0xE9, (byte) 0xA6, (byte) 0x37, (byte) 0xED, (byte) 0x6B, + (byte) 0x0B, (byte) 0xFF, (byte) 0x5C, (byte) 0xB6, (byte) 0xF4, (byte) 0x06, (byte) 0xB7, + (byte) 0xED, (byte) 0xEE, (byte) 0x38, (byte) 0x6B, (byte) 0xFB, (byte) 0x5A, (byte) 0x89, + (byte) 0x9F, (byte) 0xA5, (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C, + (byte) 0x4B, (byte) 0x1F, (byte) 0xE6, (byte) 0x49, (byte) 0x28, (byte) 0x66, (byte) 0x51, + (byte) 0xEC, (byte) 0xE4, (byte) 0x5B, (byte) 0x3D, (byte) 0xC2, (byte) 0x00, (byte) 0x7C, + (byte) 0xB8, (byte) 0xA1, (byte) 0x63, (byte) 0xBF, (byte) 0x05, (byte) 0x98, (byte) 0xDA, + (byte) 0x48, (byte) 0x36, (byte) 0x1C, (byte) 0x55, (byte) 0xD3, (byte) 0x9A, (byte) 0x69, + (byte) 0x16, (byte) 0x3F, (byte) 0xA8, (byte) 0xFD, (byte) 0x24, (byte) 0xCF, (byte) 0x5F, + (byte) 0x83, (byte) 0x65, (byte) 0x5D, (byte) 0x23, (byte) 0xDC, (byte) 0xA3, (byte) 0xAD, + (byte) 0x96, (byte) 0x1C, (byte) 0x62, (byte) 0xF3, (byte) 0x56, (byte) 0x20, (byte) 0x85, + (byte) 0x52, (byte) 0xBB, (byte) 0x9E, (byte) 0xD5, (byte) 0x29, (byte) 0x07, (byte) 0x70, + (byte) 0x96, (byte) 0x96, (byte) 0x6D, (byte) 0x67, (byte) 0x0C, (byte) 0x35, (byte) 0x4E, + (byte) 0x4A, (byte) 0xBC, (byte) 0x98, (byte) 0x04, (byte) 0xF1, (byte) 0x74, (byte) 0x6C, + (byte) 0x08, (byte) 0xCA, (byte) 0x18, (byte) 0x21, (byte) 0x7C, (byte) 0x32, (byte) 0x90, + (byte) 0x5E, (byte) 0x46, (byte) 0x2E, (byte) 0x36, (byte) 0xCE, (byte) 0x3B, (byte) 0xE3, + (byte) 0x9E, (byte) 0x77, (byte) 0x2C, (byte) 0x18, (byte) 0x0E, (byte) 0x86, (byte) 0x03, + (byte) 0x9B, (byte) 0x27, (byte) 0x83, (byte) 0xA2, (byte) 0xEC, (byte) 0x07, (byte) 0xA2, + (byte) 0x8F, (byte) 0xB5, (byte) 0xC5, (byte) 0x5D, (byte) 0xF0, (byte) 0x6F, (byte) 0x4C, + (byte) 0x52, (byte) 0xC9, (byte) 0xDE, (byte) 0x2B, (byte) 0xCB, (byte) 0xF6, (byte) 0x95, + (byte) 0x58, (byte) 0x17, (byte) 0x18, (byte) 0x39, (byte) 0x95, (byte) 0x49, (byte) 0x7C, + (byte) 0xEA, (byte) 0x95, (byte) 0x6A, (byte) 0xE5, (byte) 0x15, (byte) 0xD2, (byte) 0x26, + (byte) 0x18, (byte) 0x98, (byte) 0xFA, (byte) 0x05, (byte) 0x10, (byte) 0x15, (byte) 0x72, + (byte) 0x8E, (byte) 0x5A, (byte) 0x8A, (byte) 0xAA, (byte) 0xC4, (byte) 0x2D, (byte) 0xAD, + (byte) 0x33, (byte) 0x17, (byte) 0x0D, (byte) 0x04, (byte) 0x50, (byte) 0x7A, (byte) 0x33, + (byte) 0xA8, (byte) 0x55, (byte) 0x21, (byte) 0xAB, (byte) 0xDF, (byte) 0x1C, (byte) 0xBA, + (byte) 0x64, (byte) 0xEC, (byte) 0xFB, (byte) 0x85, (byte) 0x04, (byte) 0x58, (byte) 0xDB, + (byte) 0xEF, (byte) 0x0A, (byte) 0x8A, (byte) 0xEA, (byte) 0x71, (byte) 0x57, (byte) 0x5D, + (byte) 0x06, (byte) 0x0C, (byte) 0x7D, (byte) 0xB3, (byte) 0x97, (byte) 0x0F, (byte) 0x85, + (byte) 0xA6, (byte) 0xE1, (byte) 0xE4, (byte) 0xC7, (byte) 0xAB, (byte) 0xF5, (byte) 0xAE, + (byte) 0x8C, (byte) 0xDB, (byte) 0x09, (byte) 0x33, (byte) 0xD7, (byte) 0x1E, (byte) 0x8C, + (byte) 0x94, (byte) 0xE0, (byte) 0x4A, (byte) 0x25, (byte) 0x61, (byte) 0x9D, (byte) 0xCE, + (byte) 0xE3, (byte) 0xD2, (byte) 0x26, (byte) 0x1A, (byte) 0xD2, (byte) 0xEE, (byte) 0x6B, + (byte) 0xF1, (byte) 0x2F, (byte) 0xFA, (byte) 0x06, (byte) 0xD9, (byte) 0x8A, (byte) 0x08, + (byte) 0x64, (byte) 0xD8, (byte) 0x76, (byte) 0x02, (byte) 0x73, (byte) 0x3E, (byte) 0xC8, + (byte) 0x6A, (byte) 0x64, (byte) 0x52, (byte) 0x1F, (byte) 0x2B, (byte) 0x18, (byte) 0x17, + (byte) 0x7B, (byte) 0x20, (byte) 0x0C, (byte) 0xBB, (byte) 0xE1, (byte) 0x17, (byte) 0x57, + (byte) 0x7A, (byte) 0x61, (byte) 0x5D, (byte) 0x6C, (byte) 0x77, (byte) 0x09, (byte) 0x88, + (byte) 0xC0, (byte) 0xBA, (byte) 0xD9, (byte) 0x46, (byte) 0xE2, (byte) 0x08, (byte) 0xE2, + (byte) 0x4F, (byte) 0xA0, (byte) 0x74, (byte) 0xE5, (byte) 0xAB, (byte) 0x31, (byte) 0x43, + (byte) 0xDB, (byte) 0x5B, (byte) 0xFC, (byte) 0xE0, (byte) 0xFD, (byte) 0x10, (byte) 0x8E, + (byte) 0x4B, (byte) 0x82, (byte) 0xD1, (byte) 0x20, (byte) 0xA9, (byte) 0x21, (byte) 0x08, + (byte) 0x01, (byte) 0x1A, (byte) 0x72, (byte) 0x3C, (byte) 0x12, (byte) 0xA7, (byte) 0x87, + (byte) 0xE6, (byte) 0xD7, (byte) 0x88, (byte) 0x71, (byte) 0x9A, (byte) 0x10, (byte) 0xBD, + (byte) 0xBA, (byte) 0x5B, (byte) 0x26, (byte) 0x99, (byte) 0xC3, (byte) 0x27, (byte) 0x18, + (byte) 0x6A, (byte) 0xF4, (byte) 0xE2, (byte) 0x3C, (byte) 0x1A, (byte) 0x94, (byte) 0x68, + (byte) 0x34, (byte) 0xB6, (byte) 0x15, (byte) 0x0B, (byte) 0xDA, (byte) 0x25, (byte) 0x83, + (byte) 0xE9, (byte) 0xCA, (byte) 0x2A, (byte) 0xD4, (byte) 0x4C, (byte) 0xE8, (byte) 0xDB, + (byte) 0xBB, (byte) 0xC2, (byte) 0xDB, (byte) 0x04, (byte) 0xDE, (byte) 0x8E, (byte) 0xF9, + (byte) 0x2E, (byte) 0x8E, (byte) 0xFC, (byte) 0x14, (byte) 0x1F, (byte) 0xBE, (byte) 0xCA, + (byte) 0xA6, (byte) 0x28, (byte) 0x7C, (byte) 0x59, (byte) 0x47, (byte) 0x4E, (byte) 0x6B, + (byte) 0xC0, (byte) 0x5D, (byte) 0x99, (byte) 0xB2, (byte) 0x96, (byte) 0x4F, (byte) 0xA0, + (byte) 0x90, (byte) 0xC3, (byte) 0xA2, (byte) 0x23, (byte) 0x3B, (byte) 0xA1, (byte) 0x86, + (byte) 0x51, (byte) 0x5B, (byte) 0xE7, (byte) 0xED, (byte) 0x1F, (byte) 0x61, (byte) 0x29, + (byte) 0x70, (byte) 0xCE, (byte) 0xE2, (byte) 0xD7, (byte) 0xAF, (byte) 0xB8, (byte) 0x1B, + (byte) 0xDD, (byte) 0x76, (byte) 0x21, (byte) 0x70, (byte) 0x48, (byte) 0x1C, (byte) 0xD0, + (byte) 0x06, (byte) 0x91, (byte) 0x27, (byte) 0xD5, (byte) 0xB0, (byte) 0x5A, (byte) 0xA9, + (byte) 0x93, (byte) 0xB4, (byte) 0xEA, (byte) 0x98, (byte) 0x8D, (byte) 0x8F, (byte) 0xDD, + (byte) 0xC1, (byte) 0x86, (byte) 0xFF, (byte) 0xB7, (byte) 0xDC, (byte) 0x90, (byte) 0xA6, + (byte) 0xC0, (byte) 0x8F, (byte) 0x4D, (byte) 0xF4, (byte) 0x35, (byte) 0xC9, (byte) 0x34, + (byte) 0x06, (byte) 0x31, (byte) 0x99, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + + @Override + byte[] G() { + return g; + } + + @Override + byte[] P() { + return p; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG17.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG17.java new file mode 100644 index 0000000..6d2c647 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG17.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG17 extends DHGN { + + static final byte[] g = {2}; + static final byte[] p = + {(byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xC9, (byte) 0x0F, (byte) 0xDA, (byte) 0xA2, (byte) 0x21, + (byte) 0x68, (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, (byte) 0x62, (byte) 0x8B, + (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1, (byte) 0x29, (byte) 0x02, (byte) 0x4E, + (byte) 0x08, (byte) 0x8A, (byte) 0x67, (byte) 0xCC, (byte) 0x74, (byte) 0x02, (byte) 0x0B, + (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B, (byte) 0x22, (byte) 0x51, + (byte) 0x4A, (byte) 0x08, (byte) 0x79, (byte) 0x8E, (byte) 0x34, (byte) 0x04, (byte) 0xDD, + (byte) 0xEF, (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, (byte) 0x3A, (byte) 0x43, + (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A, (byte) 0x6D, (byte) 0xF2, (byte) 0x5F, + (byte) 0x14, (byte) 0x37, (byte) 0x4F, (byte) 0xE1, (byte) 0x35, (byte) 0x6D, (byte) 0x6D, + (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85, (byte) 0xB5, (byte) 0x76, + (byte) 0x62, (byte) 0x5E, (byte) 0x7E, (byte) 0xC6, (byte) 0xF4, (byte) 0x4C, (byte) 0x42, + (byte) 0xE9, (byte) 0xA6, (byte) 0x37, (byte) 0xED, (byte) 0x6B, (byte) 0x0B, (byte) 0xFF, + (byte) 0x5C, (byte) 0xB6, (byte) 0xF4, (byte) 0x06, (byte) 0xB7, (byte) 0xED, (byte) 0xEE, + (byte) 0x38, (byte) 0x6B, (byte) 0xFB, (byte) 0x5A, (byte) 0x89, (byte) 0x9F, (byte) 0xA5, + (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C, (byte) 0x4B, (byte) 0x1F, + (byte) 0xE6, (byte) 0x49, (byte) 0x28, (byte) 0x66, (byte) 0x51, (byte) 0xEC, (byte) 0xE4, + (byte) 0x5B, (byte) 0x3D, (byte) 0xC2, (byte) 0x00, (byte) 0x7C, (byte) 0xB8, (byte) 0xA1, + (byte) 0x63, (byte) 0xBF, (byte) 0x05, (byte) 0x98, (byte) 0xDA, (byte) 0x48, (byte) 0x36, + (byte) 0x1C, (byte) 0x55, (byte) 0xD3, (byte) 0x9A, (byte) 0x69, (byte) 0x16, (byte) 0x3F, + (byte) 0xA8, (byte) 0xFD, (byte) 0x24, (byte) 0xCF, (byte) 0x5F, (byte) 0x83, (byte) 0x65, + (byte) 0x5D, (byte) 0x23, (byte) 0xDC, (byte) 0xA3, (byte) 0xAD, (byte) 0x96, (byte) 0x1C, + (byte) 0x62, (byte) 0xF3, (byte) 0x56, (byte) 0x20, (byte) 0x85, (byte) 0x52, (byte) 0xBB, + (byte) 0x9E, (byte) 0xD5, (byte) 0x29, (byte) 0x07, (byte) 0x70, (byte) 0x96, (byte) 0x96, + (byte) 0x6D, (byte) 0x67, (byte) 0x0C, (byte) 0x35, (byte) 0x4E, (byte) 0x4A, (byte) 0xBC, + (byte) 0x98, (byte) 0x04, (byte) 0xF1, (byte) 0x74, (byte) 0x6C, (byte) 0x08, (byte) 0xCA, + (byte) 0x18, (byte) 0x21, (byte) 0x7C, (byte) 0x32, (byte) 0x90, (byte) 0x5E, (byte) 0x46, + (byte) 0x2E, (byte) 0x36, (byte) 0xCE, (byte) 0x3B, (byte) 0xE3, (byte) 0x9E, (byte) 0x77, + (byte) 0x2C, (byte) 0x18, (byte) 0x0E, (byte) 0x86, (byte) 0x03, (byte) 0x9B, (byte) 0x27, + (byte) 0x83, (byte) 0xA2, (byte) 0xEC, (byte) 0x07, (byte) 0xA2, (byte) 0x8F, (byte) 0xB5, + (byte) 0xC5, (byte) 0x5D, (byte) 0xF0, (byte) 0x6F, (byte) 0x4C, (byte) 0x52, (byte) 0xC9, + (byte) 0xDE, (byte) 0x2B, (byte) 0xCB, (byte) 0xF6, (byte) 0x95, (byte) 0x58, (byte) 0x17, + (byte) 0x18, (byte) 0x39, (byte) 0x95, (byte) 0x49, (byte) 0x7C, (byte) 0xEA, (byte) 0x95, + (byte) 0x6A, (byte) 0xE5, (byte) 0x15, (byte) 0xD2, (byte) 0x26, (byte) 0x18, (byte) 0x98, + (byte) 0xFA, (byte) 0x05, (byte) 0x10, (byte) 0x15, (byte) 0x72, (byte) 0x8E, (byte) 0x5A, + (byte) 0x8A, (byte) 0xAA, (byte) 0xC4, (byte) 0x2D, (byte) 0xAD, (byte) 0x33, (byte) 0x17, + (byte) 0x0D, (byte) 0x04, (byte) 0x50, (byte) 0x7A, (byte) 0x33, (byte) 0xA8, (byte) 0x55, + (byte) 0x21, (byte) 0xAB, (byte) 0xDF, (byte) 0x1C, (byte) 0xBA, (byte) 0x64, (byte) 0xEC, + (byte) 0xFB, (byte) 0x85, (byte) 0x04, (byte) 0x58, (byte) 0xDB, (byte) 0xEF, (byte) 0x0A, + (byte) 0x8A, (byte) 0xEA, (byte) 0x71, (byte) 0x57, (byte) 0x5D, (byte) 0x06, (byte) 0x0C, + (byte) 0x7D, (byte) 0xB3, (byte) 0x97, (byte) 0x0F, (byte) 0x85, (byte) 0xA6, (byte) 0xE1, + (byte) 0xE4, (byte) 0xC7, (byte) 0xAB, (byte) 0xF5, (byte) 0xAE, (byte) 0x8C, (byte) 0xDB, + (byte) 0x09, (byte) 0x33, (byte) 0xD7, (byte) 0x1E, (byte) 0x8C, (byte) 0x94, (byte) 0xE0, + (byte) 0x4A, (byte) 0x25, (byte) 0x61, (byte) 0x9D, (byte) 0xCE, (byte) 0xE3, (byte) 0xD2, + (byte) 0x26, (byte) 0x1A, (byte) 0xD2, (byte) 0xEE, (byte) 0x6B, (byte) 0xF1, (byte) 0x2F, + (byte) 0xFA, (byte) 0x06, (byte) 0xD9, (byte) 0x8A, (byte) 0x08, (byte) 0x64, (byte) 0xD8, + (byte) 0x76, (byte) 0x02, (byte) 0x73, (byte) 0x3E, (byte) 0xC8, (byte) 0x6A, (byte) 0x64, + (byte) 0x52, (byte) 0x1F, (byte) 0x2B, (byte) 0x18, (byte) 0x17, (byte) 0x7B, (byte) 0x20, + (byte) 0x0C, (byte) 0xBB, (byte) 0xE1, (byte) 0x17, (byte) 0x57, (byte) 0x7A, (byte) 0x61, + (byte) 0x5D, (byte) 0x6C, (byte) 0x77, (byte) 0x09, (byte) 0x88, (byte) 0xC0, (byte) 0xBA, + (byte) 0xD9, (byte) 0x46, (byte) 0xE2, (byte) 0x08, (byte) 0xE2, (byte) 0x4F, (byte) 0xA0, + (byte) 0x74, (byte) 0xE5, (byte) 0xAB, (byte) 0x31, (byte) 0x43, (byte) 0xDB, (byte) 0x5B, + (byte) 0xFC, (byte) 0xE0, (byte) 0xFD, (byte) 0x10, (byte) 0x8E, (byte) 0x4B, (byte) 0x82, + (byte) 0xD1, (byte) 0x20, (byte) 0xA9, (byte) 0x21, (byte) 0x08, (byte) 0x01, (byte) 0x1A, + (byte) 0x72, (byte) 0x3C, (byte) 0x12, (byte) 0xA7, (byte) 0x87, (byte) 0xE6, (byte) 0xD7, + (byte) 0x88, (byte) 0x71, (byte) 0x9A, (byte) 0x10, (byte) 0xBD, (byte) 0xBA, (byte) 0x5B, + (byte) 0x26, (byte) 0x99, (byte) 0xC3, (byte) 0x27, (byte) 0x18, (byte) 0x6A, (byte) 0xF4, + (byte) 0xE2, (byte) 0x3C, (byte) 0x1A, (byte) 0x94, (byte) 0x68, (byte) 0x34, (byte) 0xB6, + (byte) 0x15, (byte) 0x0B, (byte) 0xDA, (byte) 0x25, (byte) 0x83, (byte) 0xE9, (byte) 0xCA, + (byte) 0x2A, (byte) 0xD4, (byte) 0x4C, (byte) 0xE8, (byte) 0xDB, (byte) 0xBB, (byte) 0xC2, + (byte) 0xDB, (byte) 0x04, (byte) 0xDE, (byte) 0x8E, (byte) 0xF9, (byte) 0x2E, (byte) 0x8E, + (byte) 0xFC, (byte) 0x14, (byte) 0x1F, (byte) 0xBE, (byte) 0xCA, (byte) 0xA6, (byte) 0x28, + (byte) 0x7C, (byte) 0x59, (byte) 0x47, (byte) 0x4E, (byte) 0x6B, (byte) 0xC0, (byte) 0x5D, + (byte) 0x99, (byte) 0xB2, (byte) 0x96, (byte) 0x4F, (byte) 0xA0, (byte) 0x90, (byte) 0xC3, + (byte) 0xA2, (byte) 0x23, (byte) 0x3B, (byte) 0xA1, (byte) 0x86, (byte) 0x51, (byte) 0x5B, + (byte) 0xE7, (byte) 0xED, (byte) 0x1F, (byte) 0x61, (byte) 0x29, (byte) 0x70, (byte) 0xCE, + (byte) 0xE2, (byte) 0xD7, (byte) 0xAF, (byte) 0xB8, (byte) 0x1B, (byte) 0xDD, (byte) 0x76, + (byte) 0x21, (byte) 0x70, (byte) 0x48, (byte) 0x1C, (byte) 0xD0, (byte) 0x06, (byte) 0x91, + (byte) 0x27, (byte) 0xD5, (byte) 0xB0, (byte) 0x5A, (byte) 0xA9, (byte) 0x93, (byte) 0xB4, + (byte) 0xEA, (byte) 0x98, (byte) 0x8D, (byte) 0x8F, (byte) 0xDD, (byte) 0xC1, (byte) 0x86, + (byte) 0xFF, (byte) 0xB7, (byte) 0xDC, (byte) 0x90, (byte) 0xA6, (byte) 0xC0, (byte) 0x8F, + (byte) 0x4D, (byte) 0xF4, (byte) 0x35, (byte) 0xC9, (byte) 0x34, (byte) 0x02, (byte) 0x84, + (byte) 0x92, (byte) 0x36, (byte) 0xC3, (byte) 0xFA, (byte) 0xB4, (byte) 0xD2, (byte) 0x7C, + (byte) 0x70, (byte) 0x26, (byte) 0xC1, (byte) 0xD4, (byte) 0xDC, (byte) 0xB2, (byte) 0x60, + (byte) 0x26, (byte) 0x46, (byte) 0xDE, (byte) 0xC9, (byte) 0x75, (byte) 0x1E, (byte) 0x76, + (byte) 0x3D, (byte) 0xBA, (byte) 0x37, (byte) 0xBD, (byte) 0xF8, (byte) 0xFF, (byte) 0x94, + (byte) 0x06, (byte) 0xAD, (byte) 0x9E, (byte) 0x53, (byte) 0x0E, (byte) 0xE5, (byte) 0xDB, + (byte) 0x38, (byte) 0x2F, (byte) 0x41, (byte) 0x30, (byte) 0x01, (byte) 0xAE, (byte) 0xB0, + (byte) 0x6A, (byte) 0x53, (byte) 0xED, (byte) 0x90, (byte) 0x27, (byte) 0xD8, (byte) 0x31, + (byte) 0x17, (byte) 0x97, (byte) 0x27, (byte) 0xB0, (byte) 0x86, (byte) 0x5A, (byte) 0x89, + (byte) 0x18, (byte) 0xDA, (byte) 0x3E, (byte) 0xDB, (byte) 0xEB, (byte) 0xCF, (byte) 0x9B, + (byte) 0x14, (byte) 0xED, (byte) 0x44, (byte) 0xCE, (byte) 0x6C, (byte) 0xBA, (byte) 0xCE, + (byte) 0xD4, (byte) 0xBB, (byte) 0x1B, (byte) 0xDB, (byte) 0x7F, (byte) 0x14, (byte) 0x47, + (byte) 0xE6, (byte) 0xCC, (byte) 0x25, (byte) 0x4B, (byte) 0x33, (byte) 0x20, (byte) 0x51, + (byte) 0x51, (byte) 0x2B, (byte) 0xD7, (byte) 0xAF, (byte) 0x42, (byte) 0x6F, (byte) 0xB8, + (byte) 0xF4, (byte) 0x01, (byte) 0x37, (byte) 0x8C, (byte) 0xD2, (byte) 0xBF, (byte) 0x59, + (byte) 0x83, (byte) 0xCA, (byte) 0x01, (byte) 0xC6, (byte) 0x4B, (byte) 0x92, (byte) 0xEC, + (byte) 0xF0, (byte) 0x32, (byte) 0xEA, (byte) 0x15, (byte) 0xD1, (byte) 0x72, (byte) 0x1D, + (byte) 0x03, (byte) 0xF4, (byte) 0x82, (byte) 0xD7, (byte) 0xCE, (byte) 0x6E, (byte) 0x74, + (byte) 0xFE, (byte) 0xF6, (byte) 0xD5, (byte) 0x5E, (byte) 0x70, (byte) 0x2F, (byte) 0x46, + (byte) 0x98, (byte) 0x0C, (byte) 0x82, (byte) 0xB5, (byte) 0xA8, (byte) 0x40, (byte) 0x31, + (byte) 0x90, (byte) 0x0B, (byte) 0x1C, (byte) 0x9E, (byte) 0x59, (byte) 0xE7, (byte) 0xC9, + (byte) 0x7F, (byte) 0xBE, (byte) 0xC7, (byte) 0xE8, (byte) 0xF3, (byte) 0x23, (byte) 0xA9, + (byte) 0x7A, (byte) 0x7E, (byte) 0x36, (byte) 0xCC, (byte) 0x88, (byte) 0xBE, (byte) 0x0F, + (byte) 0x1D, (byte) 0x45, (byte) 0xB7, (byte) 0xFF, (byte) 0x58, (byte) 0x5A, (byte) 0xC5, + (byte) 0x4B, (byte) 0xD4, (byte) 0x07, (byte) 0xB2, (byte) 0x2B, (byte) 0x41, (byte) 0x54, + (byte) 0xAA, (byte) 0xCC, (byte) 0x8F, (byte) 0x6D, (byte) 0x7E, (byte) 0xBF, (byte) 0x48, + (byte) 0xE1, (byte) 0xD8, (byte) 0x14, (byte) 0xCC, (byte) 0x5E, (byte) 0xD2, (byte) 0x0F, + (byte) 0x80, (byte) 0x37, (byte) 0xE0, (byte) 0xA7, (byte) 0x97, (byte) 0x15, (byte) 0xEE, + (byte) 0xF2, (byte) 0x9B, (byte) 0xE3, (byte) 0x28, (byte) 0x06, (byte) 0xA1, (byte) 0xD5, + (byte) 0x8B, (byte) 0xB7, (byte) 0xC5, (byte) 0xDA, (byte) 0x76, (byte) 0xF5, (byte) 0x50, + (byte) 0xAA, (byte) 0x3D, (byte) 0x8A, (byte) 0x1F, (byte) 0xBF, (byte) 0xF0, (byte) 0xEB, + (byte) 0x19, (byte) 0xCC, (byte) 0xB1, (byte) 0xA3, (byte) 0x13, (byte) 0xD5, (byte) 0x5C, + (byte) 0xDA, (byte) 0x56, (byte) 0xC9, (byte) 0xEC, (byte) 0x2E, (byte) 0xF2, (byte) 0x96, + (byte) 0x32, (byte) 0x38, (byte) 0x7F, (byte) 0xE8, (byte) 0xD7, (byte) 0x6E, (byte) 0x3C, + (byte) 0x04, (byte) 0x68, (byte) 0x04, (byte) 0x3E, (byte) 0x8F, (byte) 0x66, (byte) 0x3F, + (byte) 0x48, (byte) 0x60, (byte) 0xEE, (byte) 0x12, (byte) 0xBF, (byte) 0x2D, (byte) 0x5B, + (byte) 0x0B, (byte) 0x74, (byte) 0x74, (byte) 0xD6, (byte) 0xE6, (byte) 0x94, (byte) 0xF9, + (byte) 0x1E, (byte) 0x6D, (byte) 0xCC, (byte) 0x40, (byte) 0x24, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,}; + + @Override + byte[] G() { + return g; + } + + @Override + byte[] P() { + return p; + } + + @Override + String sha_name() { + return "sha-512"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHG18.java b/files-jsch/src/main/java/com/jcraft/jsch/DHG18.java new file mode 100644 index 0000000..5be8951 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHG18.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHG18 extends DHGN { + + static final byte[] g = {2}; + static final byte[] p = {(byte) 0x00, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xC9, (byte) 0x0F, (byte) 0xDA, + (byte) 0xA2, (byte) 0x21, (byte) 0x68, (byte) 0xC2, (byte) 0x34, (byte) 0xC4, (byte) 0xC6, + (byte) 0x62, (byte) 0x8B, (byte) 0x80, (byte) 0xDC, (byte) 0x1C, (byte) 0xD1, (byte) 0x29, + (byte) 0x02, (byte) 0x4E, (byte) 0x08, (byte) 0x8A, (byte) 0x67, (byte) 0xCC, (byte) 0x74, + (byte) 0x02, (byte) 0x0B, (byte) 0xBE, (byte) 0xA6, (byte) 0x3B, (byte) 0x13, (byte) 0x9B, + (byte) 0x22, (byte) 0x51, (byte) 0x4A, (byte) 0x08, (byte) 0x79, (byte) 0x8E, (byte) 0x34, + (byte) 0x04, (byte) 0xDD, (byte) 0xEF, (byte) 0x95, (byte) 0x19, (byte) 0xB3, (byte) 0xCD, + (byte) 0x3A, (byte) 0x43, (byte) 0x1B, (byte) 0x30, (byte) 0x2B, (byte) 0x0A, (byte) 0x6D, + (byte) 0xF2, (byte) 0x5F, (byte) 0x14, (byte) 0x37, (byte) 0x4F, (byte) 0xE1, (byte) 0x35, + (byte) 0x6D, (byte) 0x6D, (byte) 0x51, (byte) 0xC2, (byte) 0x45, (byte) 0xE4, (byte) 0x85, + (byte) 0xB5, (byte) 0x76, (byte) 0x62, (byte) 0x5E, (byte) 0x7E, (byte) 0xC6, (byte) 0xF4, + (byte) 0x4C, (byte) 0x42, (byte) 0xE9, (byte) 0xA6, (byte) 0x37, (byte) 0xED, (byte) 0x6B, + (byte) 0x0B, (byte) 0xFF, (byte) 0x5C, (byte) 0xB6, (byte) 0xF4, (byte) 0x06, (byte) 0xB7, + (byte) 0xED, (byte) 0xEE, (byte) 0x38, (byte) 0x6B, (byte) 0xFB, (byte) 0x5A, (byte) 0x89, + (byte) 0x9F, (byte) 0xA5, (byte) 0xAE, (byte) 0x9F, (byte) 0x24, (byte) 0x11, (byte) 0x7C, + (byte) 0x4B, (byte) 0x1F, (byte) 0xE6, (byte) 0x49, (byte) 0x28, (byte) 0x66, (byte) 0x51, + (byte) 0xEC, (byte) 0xE4, (byte) 0x5B, (byte) 0x3D, (byte) 0xC2, (byte) 0x00, (byte) 0x7C, + (byte) 0xB8, (byte) 0xA1, (byte) 0x63, (byte) 0xBF, (byte) 0x05, (byte) 0x98, (byte) 0xDA, + (byte) 0x48, (byte) 0x36, (byte) 0x1C, (byte) 0x55, (byte) 0xD3, (byte) 0x9A, (byte) 0x69, + (byte) 0x16, (byte) 0x3F, (byte) 0xA8, (byte) 0xFD, (byte) 0x24, (byte) 0xCF, (byte) 0x5F, + (byte) 0x83, (byte) 0x65, (byte) 0x5D, (byte) 0x23, (byte) 0xDC, (byte) 0xA3, (byte) 0xAD, + (byte) 0x96, (byte) 0x1C, (byte) 0x62, (byte) 0xF3, (byte) 0x56, (byte) 0x20, (byte) 0x85, + (byte) 0x52, (byte) 0xBB, (byte) 0x9E, (byte) 0xD5, (byte) 0x29, (byte) 0x07, (byte) 0x70, + (byte) 0x96, (byte) 0x96, (byte) 0x6D, (byte) 0x67, (byte) 0x0C, (byte) 0x35, (byte) 0x4E, + (byte) 0x4A, (byte) 0xBC, (byte) 0x98, (byte) 0x04, (byte) 0xF1, (byte) 0x74, (byte) 0x6C, + (byte) 0x08, (byte) 0xCA, (byte) 0x18, (byte) 0x21, (byte) 0x7C, (byte) 0x32, (byte) 0x90, + (byte) 0x5E, (byte) 0x46, (byte) 0x2E, (byte) 0x36, (byte) 0xCE, (byte) 0x3B, (byte) 0xE3, + (byte) 0x9E, (byte) 0x77, (byte) 0x2C, (byte) 0x18, (byte) 0x0E, (byte) 0x86, (byte) 0x03, + (byte) 0x9B, (byte) 0x27, (byte) 0x83, (byte) 0xA2, (byte) 0xEC, (byte) 0x07, (byte) 0xA2, + (byte) 0x8F, (byte) 0xB5, (byte) 0xC5, (byte) 0x5D, (byte) 0xF0, (byte) 0x6F, (byte) 0x4C, + (byte) 0x52, (byte) 0xC9, (byte) 0xDE, (byte) 0x2B, (byte) 0xCB, (byte) 0xF6, (byte) 0x95, + (byte) 0x58, (byte) 0x17, (byte) 0x18, (byte) 0x39, (byte) 0x95, (byte) 0x49, (byte) 0x7C, + (byte) 0xEA, (byte) 0x95, (byte) 0x6A, (byte) 0xE5, (byte) 0x15, (byte) 0xD2, (byte) 0x26, + (byte) 0x18, (byte) 0x98, (byte) 0xFA, (byte) 0x05, (byte) 0x10, (byte) 0x15, (byte) 0x72, + (byte) 0x8E, (byte) 0x5A, (byte) 0x8A, (byte) 0xAA, (byte) 0xC4, (byte) 0x2D, (byte) 0xAD, + (byte) 0x33, (byte) 0x17, (byte) 0x0D, (byte) 0x04, (byte) 0x50, (byte) 0x7A, (byte) 0x33, + (byte) 0xA8, (byte) 0x55, (byte) 0x21, (byte) 0xAB, (byte) 0xDF, (byte) 0x1C, (byte) 0xBA, + (byte) 0x64, (byte) 0xEC, (byte) 0xFB, (byte) 0x85, (byte) 0x04, (byte) 0x58, (byte) 0xDB, + (byte) 0xEF, (byte) 0x0A, (byte) 0x8A, (byte) 0xEA, (byte) 0x71, (byte) 0x57, (byte) 0x5D, + (byte) 0x06, (byte) 0x0C, (byte) 0x7D, (byte) 0xB3, (byte) 0x97, (byte) 0x0F, (byte) 0x85, + (byte) 0xA6, (byte) 0xE1, (byte) 0xE4, (byte) 0xC7, (byte) 0xAB, (byte) 0xF5, (byte) 0xAE, + (byte) 0x8C, (byte) 0xDB, (byte) 0x09, (byte) 0x33, (byte) 0xD7, (byte) 0x1E, (byte) 0x8C, + (byte) 0x94, (byte) 0xE0, (byte) 0x4A, (byte) 0x25, (byte) 0x61, (byte) 0x9D, (byte) 0xCE, + (byte) 0xE3, (byte) 0xD2, (byte) 0x26, (byte) 0x1A, (byte) 0xD2, (byte) 0xEE, (byte) 0x6B, + (byte) 0xF1, (byte) 0x2F, (byte) 0xFA, (byte) 0x06, (byte) 0xD9, (byte) 0x8A, (byte) 0x08, + (byte) 0x64, (byte) 0xD8, (byte) 0x76, (byte) 0x02, (byte) 0x73, (byte) 0x3E, (byte) 0xC8, + (byte) 0x6A, (byte) 0x64, (byte) 0x52, (byte) 0x1F, (byte) 0x2B, (byte) 0x18, (byte) 0x17, + (byte) 0x7B, (byte) 0x20, (byte) 0x0C, (byte) 0xBB, (byte) 0xE1, (byte) 0x17, (byte) 0x57, + (byte) 0x7A, (byte) 0x61, (byte) 0x5D, (byte) 0x6C, (byte) 0x77, (byte) 0x09, (byte) 0x88, + (byte) 0xC0, (byte) 0xBA, (byte) 0xD9, (byte) 0x46, (byte) 0xE2, (byte) 0x08, (byte) 0xE2, + (byte) 0x4F, (byte) 0xA0, (byte) 0x74, (byte) 0xE5, (byte) 0xAB, (byte) 0x31, (byte) 0x43, + (byte) 0xDB, (byte) 0x5B, (byte) 0xFC, (byte) 0xE0, (byte) 0xFD, (byte) 0x10, (byte) 0x8E, + (byte) 0x4B, (byte) 0x82, (byte) 0xD1, (byte) 0x20, (byte) 0xA9, (byte) 0x21, (byte) 0x08, + (byte) 0x01, (byte) 0x1A, (byte) 0x72, (byte) 0x3C, (byte) 0x12, (byte) 0xA7, (byte) 0x87, + (byte) 0xE6, (byte) 0xD7, (byte) 0x88, (byte) 0x71, (byte) 0x9A, (byte) 0x10, (byte) 0xBD, + (byte) 0xBA, (byte) 0x5B, (byte) 0x26, (byte) 0x99, (byte) 0xC3, (byte) 0x27, (byte) 0x18, + (byte) 0x6A, (byte) 0xF4, (byte) 0xE2, (byte) 0x3C, (byte) 0x1A, (byte) 0x94, (byte) 0x68, + (byte) 0x34, (byte) 0xB6, (byte) 0x15, (byte) 0x0B, (byte) 0xDA, (byte) 0x25, (byte) 0x83, + (byte) 0xE9, (byte) 0xCA, (byte) 0x2A, (byte) 0xD4, (byte) 0x4C, (byte) 0xE8, (byte) 0xDB, + (byte) 0xBB, (byte) 0xC2, (byte) 0xDB, (byte) 0x04, (byte) 0xDE, (byte) 0x8E, (byte) 0xF9, + (byte) 0x2E, (byte) 0x8E, (byte) 0xFC, (byte) 0x14, (byte) 0x1F, (byte) 0xBE, (byte) 0xCA, + (byte) 0xA6, (byte) 0x28, (byte) 0x7C, (byte) 0x59, (byte) 0x47, (byte) 0x4E, (byte) 0x6B, + (byte) 0xC0, (byte) 0x5D, (byte) 0x99, (byte) 0xB2, (byte) 0x96, (byte) 0x4F, (byte) 0xA0, + (byte) 0x90, (byte) 0xC3, (byte) 0xA2, (byte) 0x23, (byte) 0x3B, (byte) 0xA1, (byte) 0x86, + (byte) 0x51, (byte) 0x5B, (byte) 0xE7, (byte) 0xED, (byte) 0x1F, (byte) 0x61, (byte) 0x29, + (byte) 0x70, (byte) 0xCE, (byte) 0xE2, (byte) 0xD7, (byte) 0xAF, (byte) 0xB8, (byte) 0x1B, + (byte) 0xDD, (byte) 0x76, (byte) 0x21, (byte) 0x70, (byte) 0x48, (byte) 0x1C, (byte) 0xD0, + (byte) 0x06, (byte) 0x91, (byte) 0x27, (byte) 0xD5, (byte) 0xB0, (byte) 0x5A, (byte) 0xA9, + (byte) 0x93, (byte) 0xB4, (byte) 0xEA, (byte) 0x98, (byte) 0x8D, (byte) 0x8F, (byte) 0xDD, + (byte) 0xC1, (byte) 0x86, (byte) 0xFF, (byte) 0xB7, (byte) 0xDC, (byte) 0x90, (byte) 0xA6, + (byte) 0xC0, (byte) 0x8F, (byte) 0x4D, (byte) 0xF4, (byte) 0x35, (byte) 0xC9, (byte) 0x34, + (byte) 0x02, (byte) 0x84, (byte) 0x92, (byte) 0x36, (byte) 0xC3, (byte) 0xFA, (byte) 0xB4, + (byte) 0xD2, (byte) 0x7C, (byte) 0x70, (byte) 0x26, (byte) 0xC1, (byte) 0xD4, (byte) 0xDC, + (byte) 0xB2, (byte) 0x60, (byte) 0x26, (byte) 0x46, (byte) 0xDE, (byte) 0xC9, (byte) 0x75, + (byte) 0x1E, (byte) 0x76, (byte) 0x3D, (byte) 0xBA, (byte) 0x37, (byte) 0xBD, (byte) 0xF8, + (byte) 0xFF, (byte) 0x94, (byte) 0x06, (byte) 0xAD, (byte) 0x9E, (byte) 0x53, (byte) 0x0E, + (byte) 0xE5, (byte) 0xDB, (byte) 0x38, (byte) 0x2F, (byte) 0x41, (byte) 0x30, (byte) 0x01, + (byte) 0xAE, (byte) 0xB0, (byte) 0x6A, (byte) 0x53, (byte) 0xED, (byte) 0x90, (byte) 0x27, + (byte) 0xD8, (byte) 0x31, (byte) 0x17, (byte) 0x97, (byte) 0x27, (byte) 0xB0, (byte) 0x86, + (byte) 0x5A, (byte) 0x89, (byte) 0x18, (byte) 0xDA, (byte) 0x3E, (byte) 0xDB, (byte) 0xEB, + (byte) 0xCF, (byte) 0x9B, (byte) 0x14, (byte) 0xED, (byte) 0x44, (byte) 0xCE, (byte) 0x6C, + (byte) 0xBA, (byte) 0xCE, (byte) 0xD4, (byte) 0xBB, (byte) 0x1B, (byte) 0xDB, (byte) 0x7F, + (byte) 0x14, (byte) 0x47, (byte) 0xE6, (byte) 0xCC, (byte) 0x25, (byte) 0x4B, (byte) 0x33, + (byte) 0x20, (byte) 0x51, (byte) 0x51, (byte) 0x2B, (byte) 0xD7, (byte) 0xAF, (byte) 0x42, + (byte) 0x6F, (byte) 0xB8, (byte) 0xF4, (byte) 0x01, (byte) 0x37, (byte) 0x8C, (byte) 0xD2, + (byte) 0xBF, (byte) 0x59, (byte) 0x83, (byte) 0xCA, (byte) 0x01, (byte) 0xC6, (byte) 0x4B, + (byte) 0x92, (byte) 0xEC, (byte) 0xF0, (byte) 0x32, (byte) 0xEA, (byte) 0x15, (byte) 0xD1, + (byte) 0x72, (byte) 0x1D, (byte) 0x03, (byte) 0xF4, (byte) 0x82, (byte) 0xD7, (byte) 0xCE, + (byte) 0x6E, (byte) 0x74, (byte) 0xFE, (byte) 0xF6, (byte) 0xD5, (byte) 0x5E, (byte) 0x70, + (byte) 0x2F, (byte) 0x46, (byte) 0x98, (byte) 0x0C, (byte) 0x82, (byte) 0xB5, (byte) 0xA8, + (byte) 0x40, (byte) 0x31, (byte) 0x90, (byte) 0x0B, (byte) 0x1C, (byte) 0x9E, (byte) 0x59, + (byte) 0xE7, (byte) 0xC9, (byte) 0x7F, (byte) 0xBE, (byte) 0xC7, (byte) 0xE8, (byte) 0xF3, + (byte) 0x23, (byte) 0xA9, (byte) 0x7A, (byte) 0x7E, (byte) 0x36, (byte) 0xCC, (byte) 0x88, + (byte) 0xBE, (byte) 0x0F, (byte) 0x1D, (byte) 0x45, (byte) 0xB7, (byte) 0xFF, (byte) 0x58, + (byte) 0x5A, (byte) 0xC5, (byte) 0x4B, (byte) 0xD4, (byte) 0x07, (byte) 0xB2, (byte) 0x2B, + (byte) 0x41, (byte) 0x54, (byte) 0xAA, (byte) 0xCC, (byte) 0x8F, (byte) 0x6D, (byte) 0x7E, + (byte) 0xBF, (byte) 0x48, (byte) 0xE1, (byte) 0xD8, (byte) 0x14, (byte) 0xCC, (byte) 0x5E, + (byte) 0xD2, (byte) 0x0F, (byte) 0x80, (byte) 0x37, (byte) 0xE0, (byte) 0xA7, (byte) 0x97, + (byte) 0x15, (byte) 0xEE, (byte) 0xF2, (byte) 0x9B, (byte) 0xE3, (byte) 0x28, (byte) 0x06, + (byte) 0xA1, (byte) 0xD5, (byte) 0x8B, (byte) 0xB7, (byte) 0xC5, (byte) 0xDA, (byte) 0x76, + (byte) 0xF5, (byte) 0x50, (byte) 0xAA, (byte) 0x3D, (byte) 0x8A, (byte) 0x1F, (byte) 0xBF, + (byte) 0xF0, (byte) 0xEB, (byte) 0x19, (byte) 0xCC, (byte) 0xB1, (byte) 0xA3, (byte) 0x13, + (byte) 0xD5, (byte) 0x5C, (byte) 0xDA, (byte) 0x56, (byte) 0xC9, (byte) 0xEC, (byte) 0x2E, + (byte) 0xF2, (byte) 0x96, (byte) 0x32, (byte) 0x38, (byte) 0x7F, (byte) 0xE8, (byte) 0xD7, + (byte) 0x6E, (byte) 0x3C, (byte) 0x04, (byte) 0x68, (byte) 0x04, (byte) 0x3E, (byte) 0x8F, + (byte) 0x66, (byte) 0x3F, (byte) 0x48, (byte) 0x60, (byte) 0xEE, (byte) 0x12, (byte) 0xBF, + (byte) 0x2D, (byte) 0x5B, (byte) 0x0B, (byte) 0x74, (byte) 0x74, (byte) 0xD6, (byte) 0xE6, + (byte) 0x94, (byte) 0xF9, (byte) 0x1E, (byte) 0x6D, (byte) 0xBE, (byte) 0x11, (byte) 0x59, + (byte) 0x74, (byte) 0xA3, (byte) 0x92, (byte) 0x6F, (byte) 0x12, (byte) 0xFE, (byte) 0xE5, + (byte) 0xE4, (byte) 0x38, (byte) 0x77, (byte) 0x7C, (byte) 0xB6, (byte) 0xA9, (byte) 0x32, + (byte) 0xDF, (byte) 0x8C, (byte) 0xD8, (byte) 0xBE, (byte) 0xC4, (byte) 0xD0, (byte) 0x73, + (byte) 0xB9, (byte) 0x31, (byte) 0xBA, (byte) 0x3B, (byte) 0xC8, (byte) 0x32, (byte) 0xB6, + (byte) 0x8D, (byte) 0x9D, (byte) 0xD3, (byte) 0x00, (byte) 0x74, (byte) 0x1F, (byte) 0xA7, + (byte) 0xBF, (byte) 0x8A, (byte) 0xFC, (byte) 0x47, (byte) 0xED, (byte) 0x25, (byte) 0x76, + (byte) 0xF6, (byte) 0x93, (byte) 0x6B, (byte) 0xA4, (byte) 0x24, (byte) 0x66, (byte) 0x3A, + (byte) 0xAB, (byte) 0x63, (byte) 0x9C, (byte) 0x5A, (byte) 0xE4, (byte) 0xF5, (byte) 0x68, + (byte) 0x34, (byte) 0x23, (byte) 0xB4, (byte) 0x74, (byte) 0x2B, (byte) 0xF1, (byte) 0xC9, + (byte) 0x78, (byte) 0x23, (byte) 0x8F, (byte) 0x16, (byte) 0xCB, (byte) 0xE3, (byte) 0x9D, + (byte) 0x65, (byte) 0x2D, (byte) 0xE3, (byte) 0xFD, (byte) 0xB8, (byte) 0xBE, (byte) 0xFC, + (byte) 0x84, (byte) 0x8A, (byte) 0xD9, (byte) 0x22, (byte) 0x22, (byte) 0x2E, (byte) 0x04, + (byte) 0xA4, (byte) 0x03, (byte) 0x7C, (byte) 0x07, (byte) 0x13, (byte) 0xEB, (byte) 0x57, + (byte) 0xA8, (byte) 0x1A, (byte) 0x23, (byte) 0xF0, (byte) 0xC7, (byte) 0x34, (byte) 0x73, + (byte) 0xFC, (byte) 0x64, (byte) 0x6C, (byte) 0xEA, (byte) 0x30, (byte) 0x6B, (byte) 0x4B, + (byte) 0xCB, (byte) 0xC8, (byte) 0x86, (byte) 0x2F, (byte) 0x83, (byte) 0x85, (byte) 0xDD, + (byte) 0xFA, (byte) 0x9D, (byte) 0x4B, (byte) 0x7F, (byte) 0xA2, (byte) 0xC0, (byte) 0x87, + (byte) 0xE8, (byte) 0x79, (byte) 0x68, (byte) 0x33, (byte) 0x03, (byte) 0xED, (byte) 0x5B, + (byte) 0xDD, (byte) 0x3A, (byte) 0x06, (byte) 0x2B, (byte) 0x3C, (byte) 0xF5, (byte) 0xB3, + (byte) 0xA2, (byte) 0x78, (byte) 0xA6, (byte) 0x6D, (byte) 0x2A, (byte) 0x13, (byte) 0xF8, + (byte) 0x3F, (byte) 0x44, (byte) 0xF8, (byte) 0x2D, (byte) 0xDF, (byte) 0x31, (byte) 0x0E, + (byte) 0xE0, (byte) 0x74, (byte) 0xAB, (byte) 0x6A, (byte) 0x36, (byte) 0x45, (byte) 0x97, + (byte) 0xE8, (byte) 0x99, (byte) 0xA0, (byte) 0x25, (byte) 0x5D, (byte) 0xC1, (byte) 0x64, + (byte) 0xF3, (byte) 0x1C, (byte) 0xC5, (byte) 0x08, (byte) 0x46, (byte) 0x85, (byte) 0x1D, + (byte) 0xF9, (byte) 0xAB, (byte) 0x48, (byte) 0x19, (byte) 0x5D, (byte) 0xED, (byte) 0x7E, + (byte) 0xA1, (byte) 0xB1, (byte) 0xD5, (byte) 0x10, (byte) 0xBD, (byte) 0x7E, (byte) 0xE7, + (byte) 0x4D, (byte) 0x73, (byte) 0xFA, (byte) 0xF3, (byte) 0x6B, (byte) 0xC3, (byte) 0x1E, + (byte) 0xCF, (byte) 0xA2, (byte) 0x68, (byte) 0x35, (byte) 0x90, (byte) 0x46, (byte) 0xF4, + (byte) 0xEB, (byte) 0x87, (byte) 0x9F, (byte) 0x92, (byte) 0x40, (byte) 0x09, (byte) 0x43, + (byte) 0x8B, (byte) 0x48, (byte) 0x1C, (byte) 0x6C, (byte) 0xD7, (byte) 0x88, (byte) 0x9A, + (byte) 0x00, (byte) 0x2E, (byte) 0xD5, (byte) 0xEE, (byte) 0x38, (byte) 0x2B, (byte) 0xC9, + (byte) 0x19, (byte) 0x0D, (byte) 0xA6, (byte) 0xFC, (byte) 0x02, (byte) 0x6E, (byte) 0x47, + (byte) 0x95, (byte) 0x58, (byte) 0xE4, (byte) 0x47, (byte) 0x56, (byte) 0x77, (byte) 0xE9, + (byte) 0xAA, (byte) 0x9E, (byte) 0x30, (byte) 0x50, (byte) 0xE2, (byte) 0x76, (byte) 0x56, + (byte) 0x94, (byte) 0xDF, (byte) 0xC8, (byte) 0x1F, (byte) 0x56, (byte) 0xE8, (byte) 0x80, + (byte) 0xB9, (byte) 0x6E, (byte) 0x71, (byte) 0x60, (byte) 0xC9, (byte) 0x80, (byte) 0xDD, + (byte) 0x98, (byte) 0xED, (byte) 0xD3, (byte) 0xDF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, + (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + + @Override + byte[] G() { + return g; + } + + @Override + byte[] P() { + return p; + } + + @Override + String sha_name() { + return "sha-512"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHGEX.java b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX.java new file mode 100644 index 0000000..393b0ba --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.math.BigInteger; + +abstract class DHGEX extends KeyExchange { + + private static final int SSH_MSG_KEX_DH_GEX_GROUP = 31; + private static final int SSH_MSG_KEX_DH_GEX_INIT = 32; + private static final int SSH_MSG_KEX_DH_GEX_REPLY = 33; + private static final int SSH_MSG_KEX_DH_GEX_REQUEST = 34; + + int min; + int preferred; + int max; + + private int state; + + DH dh; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + + private Buffer buf; + private Packet packet; + + private byte[] p; + private byte[] g; + private byte[] e; + + protected String hash; + + @Override + public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) + throws Exception { + this.V_S = V_S; + this.V_C = V_C; + this.I_S = I_S; + this.I_C = I_C; + + try { + Class c = Class.forName(session.getConfig(hash)).asSubclass(HASH.class); + sha = c.getDeclaredConstructor().newInstance(); + sha.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + buf = new Buffer(); + packet = new Packet(buf); + + try { + Class c = Class.forName(session.getConfig("dh")).asSubclass(DH.class); + min = Integer.parseInt(session.getConfig("dhgex_min")); + max = Integer.parseInt(session.getConfig("dhgex_max")); + preferred = Integer.parseInt(session.getConfig("dhgex_preferred")); + if (min <= 0 || max <= 0 || preferred <= 0 || preferred < min || preferred > max) { + throw new JSchException( + "Invalid DHGEX sizes: min=" + min + " max=" + max + " preferred=" + preferred); + } + dh = c.getDeclaredConstructor().newInstance(); + dh.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + packet.reset(); + buf.putByte((byte) SSH_MSG_KEX_DH_GEX_REQUEST); + buf.putInt(min); + buf.putInt(preferred); + buf.putInt(max); + session.write(packet); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, + "SSH_MSG_KEX_DH_GEX_REQUEST(" + min + "<" + preferred + "<" + max + ") sent"); + session.getLogger().log(Logger.INFO, "expecting SSH_MSG_KEX_DH_GEX_GROUP"); + } + + state = SSH_MSG_KEX_DH_GEX_GROUP; + } + + @Override + public boolean next(Buffer _buf) throws Exception { + int i, j; + switch (state) { + case SSH_MSG_KEX_DH_GEX_GROUP: + // byte SSH_MSG_KEX_DH_GEX_GROUP(31) + // mpint p, safe prime + // mpint g, generator for subgroup in GF (p) + _buf.getInt(); + _buf.getByte(); + j = _buf.getByte(); + if (j != SSH_MSG_KEX_DH_GEX_GROUP) { + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_DH_GEX_GROUP " + j); + } + return false; + } + + p = _buf.getMPInt(); + g = _buf.getMPInt(); + + int bits = new BigInteger(1, p).bitLength(); + if (bits < min || bits > max) { + return false; + } + + dh.setP(p); + dh.setG(g); + // The client responds with: + // byte SSH_MSG_KEX_DH_GEX_INIT(32) + // mpint e <- g^x mod p + // x is a random number (1 < x < (p-1)/2) + + e = dh.getE(); + + packet.reset(); + buf.putByte((byte) SSH_MSG_KEX_DH_GEX_INIT); + buf.putMPInt(e); + session.write(packet); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "SSH_MSG_KEX_DH_GEX_INIT sent"); + session.getLogger().log(Logger.INFO, "expecting SSH_MSG_KEX_DH_GEX_REPLY"); + } + + state = SSH_MSG_KEX_DH_GEX_REPLY; + return true; + // break; + + case SSH_MSG_KEX_DH_GEX_REPLY: + // The server responds with: + // byte SSH_MSG_KEX_DH_GEX_REPLY(33) + // string server public host key and certificates (K_S) + // mpint f + // string signature of H + j = _buf.getInt(); + j = _buf.getByte(); + j = _buf.getByte(); + if (j != SSH_MSG_KEX_DH_GEX_REPLY) { + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_DH_GEX_REPLY " + j); + } + return false; + } + + K_S = _buf.getString(); + + byte[] f = _buf.getMPInt(); + byte[] sig_of_H = _buf.getString(); + + dh.setF(f); + + dh.checkRange(); + + K = encodeAsMPInt(normalize(dh.getK())); + + // The hash H is computed as the HASH hash of the concatenation of the + // following: + // string V_C, the client's version string (CR and NL excluded) + // string V_S, the server's version string (CR and NL excluded) + // string I_C, the payload of the client's SSH_MSG_KEXINIT + // string I_S, the payload of the server's SSH_MSG_KEXINIT + // string K_S, the host key + // uint32 min, minimal size in bits of an acceptable group + // uint32 n, preferred size in bits of the group the server should send + // uint32 max, maximal size in bits of an acceptable group + // mpint p, safe prime + // mpint g, generator for subgroup + // mpint e, exchange value sent by the client + // mpint f, exchange value sent by the server + // mpint K, the shared secret + // This value is called the exchange hash, and it is used to authenti- + // cate the key exchange. + + buf.reset(); + buf.putString(V_C); + buf.putString(V_S); + buf.putString(I_C); + buf.putString(I_S); + buf.putString(K_S); + buf.putInt(min); + buf.putInt(preferred); + buf.putInt(max); + buf.putMPInt(p); + buf.putMPInt(g); + buf.putMPInt(e); + buf.putMPInt(f); + + byte[] foo = new byte[buf.getLength()]; + buf.getByte(foo); + sha.update(foo, 0, foo.length); + sha.update(K, 0, K.length); + + H = sha.digest(); + + // System.err.print("H -> "); dump(H, 0, H.length); + + i = 0; + j = 0; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + String alg = Util.byte2str(K_S, i, j); + i += j; + + boolean result = verify(alg, K_S, i, sig_of_H); + + state = STATE_END; + return result; + } + return false; + } + + @Override + public int getState() { + return state; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHGEX1.java b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX1.java new file mode 100644 index 0000000..a950bfe --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX1.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHGEX1 extends DHGEX { + DHGEX1() { + hash = "sha-1"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHGEX224.java b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX224.java new file mode 100644 index 0000000..7dc0067 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX224.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHGEX224 extends DHGEX { + DHGEX224() { + hash = "sha-224"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHGEX256.java b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX256.java new file mode 100644 index 0000000..100447d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX256.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHGEX256 extends DHGEX { + DHGEX256() { + hash = "sha-256"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHGEX384.java b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX384.java new file mode 100644 index 0000000..e8611e9 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX384.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHGEX384 extends DHGEX { + DHGEX384() { + hash = "sha-384"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHGEX512.java b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX512.java new file mode 100644 index 0000000..f126623 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHGEX512.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class DHGEX512 extends DHGEX { + DHGEX512() { + hash = "sha-512"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHGN.java b/files-jsch/src/main/java/com/jcraft/jsch/DHGN.java new file mode 100644 index 0000000..ddb41df --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHGN.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +abstract class DHGN extends KeyExchange { + + private static final int SSH_MSG_KEXDH_INIT = 30; + private static final int SSH_MSG_KEXDH_REPLY = 31; + + private int state; + + DH dh; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + + byte[] e; + + private Buffer buf; + private Packet packet; + + abstract byte[] G(); + + abstract byte[] P(); + + abstract String sha_name(); + + @Override + public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) + throws Exception { + this.V_S = V_S; + this.V_C = V_C; + this.I_S = I_S; + this.I_C = I_C; + + try { + Class c = Class.forName(session.getConfig(sha_name())).asSubclass(HASH.class); + sha = c.getDeclaredConstructor().newInstance(); + sha.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + buf = new Buffer(); + packet = new Packet(buf); + + try { + Class c = Class.forName(session.getConfig("dh")).asSubclass(DH.class); + dh = c.getDeclaredConstructor().newInstance(); + dh.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + dh.setP(P()); + dh.setG(G()); + // The client responds with: + // byte SSH_MSG_KEXDH_INIT(30) + // mpint e <- g^x mod p + // x is a random number (1 < x < (p-1)/2) + + e = dh.getE(); + packet.reset(); + buf.putByte((byte) SSH_MSG_KEXDH_INIT); + buf.putMPInt(e); + + if (V_S == null) { // This is a really ugly hack for Session.checkKexes ;-( + return; + } + + session.write(packet); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "SSH_MSG_KEXDH_INIT sent"); + session.getLogger().log(Logger.INFO, "expecting SSH_MSG_KEXDH_REPLY"); + } + + state = SSH_MSG_KEXDH_REPLY; + } + + @Override + public boolean next(Buffer _buf) throws Exception { + int i, j; + + switch (state) { + case SSH_MSG_KEXDH_REPLY: + // The server responds with: + // byte SSH_MSG_KEXDH_REPLY(31) + // string server public host key and certificates (K_S) + // mpint f + // string signature of H + j = _buf.getInt(); + j = _buf.getByte(); + j = _buf.getByte(); + if (j != SSH_MSG_KEXDH_REPLY) { + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEXDH_REPLY " + j); + } + return false; + } + + K_S = _buf.getString(); + + byte[] f = _buf.getMPInt(); + byte[] sig_of_H = _buf.getString(); + + dh.setF(f); + + dh.checkRange(); + + K = encodeAsMPInt(normalize(dh.getK())); + + // The hash H is computed as the HASH hash of the concatenation of the + // following: + // string V_C, the client's version string (CR and NL excluded) + // string V_S, the server's version string (CR and NL excluded) + // string I_C, the payload of the client's SSH_MSG_KEXINIT + // string I_S, the payload of the server's SSH_MSG_KEXINIT + // string K_S, the host key + // mpint e, exchange value sent by the client + // mpint f, exchange value sent by the server + // mpint K, the shared secret + // This value is called the exchange hash, and it is used to authenti- + // cate the key exchange. + buf.reset(); + buf.putString(V_C); + buf.putString(V_S); + buf.putString(I_C); + buf.putString(I_S); + buf.putString(K_S); + buf.putMPInt(e); + buf.putMPInt(f); + byte[] foo = new byte[buf.getLength()]; + buf.getByte(foo); + + sha.update(foo, 0, foo.length); + sha.update(K, 0, K.length); + H = sha.digest(); + // System.err.print("H -> "); //dump(H, 0, H.length); + + i = 0; + j = 0; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + String alg = Util.byte2str(K_S, i, j); + i += j; + + boolean result = verify(alg, K_S, i, sig_of_H); + + state = STATE_END; + return result; + } + return false; + } + + @Override + public int getState() { + return state; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHXEC.java b/files-jsch/src/main/java/com/jcraft/jsch/DHXEC.java new file mode 100644 index 0000000..b8cf55c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHXEC.java @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +abstract class DHXEC extends KeyExchange { + + private static final int SSH_MSG_KEX_ECDH_INIT = 30; + private static final int SSH_MSG_KEX_ECDH_REPLY = 31; + private int state; + + byte[] Q_C; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + + byte[] e; + + private Buffer buf; + private Packet packet; + + private XDH xdh; + + protected String sha_name; + protected String curve_name; + protected int key_len; + + @Override + public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) + throws Exception { + this.V_S = V_S; + this.V_C = V_C; + this.I_S = I_S; + this.I_C = I_C; + + try { + Class c = Class.forName(session.getConfig(sha_name)).asSubclass(HASH.class); + sha = c.getDeclaredConstructor().newInstance(); + sha.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + buf = new Buffer(); + packet = new Packet(buf); + + packet.reset(); + buf.putByte((byte) SSH_MSG_KEX_ECDH_INIT); + + try { + Class c = Class.forName(session.getConfig("xdh")).asSubclass(XDH.class); + xdh = c.getDeclaredConstructor().newInstance(); + xdh.init(curve_name, key_len); + + Q_C = xdh.getQ(); + buf.putString(Q_C); + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException(e.toString(), e); + } + + if (V_S == null) { // This is a really ugly hack for Session.checkKexes ;-( + return; + } + + session.write(packet); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "SSH_MSG_KEX_ECDH_INIT sent"); + session.getLogger().log(Logger.INFO, "expecting SSH_MSG_KEX_ECDH_REPLY"); + } + + state = SSH_MSG_KEX_ECDH_REPLY; + } + + @Override + public boolean next(Buffer _buf) throws Exception { + int i, j; + switch (state) { + case SSH_MSG_KEX_ECDH_REPLY: + // The server responds with: + // byte SSH_MSG_KEX_ECDH_REPLY + // string K_S, server's public host key + // string Q_S, server's ephemeral public key octet string + // string the signature on the exchange hash + j = _buf.getInt(); + j = _buf.getByte(); + j = _buf.getByte(); + if (j != SSH_MSG_KEX_ECDH_REPLY) { + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_ECDH_REPLY " + j); + } + return false; + } + + K_S = _buf.getString(); + + byte[] Q_S = _buf.getString(); + + // RFC 5656, + // 4. ECDH Key Exchange + // All elliptic curve public keys MUST be validated after they are + // received. An example of a validation algorithm can be found in + // Section 3.2.2 of [SEC1]. If a key fails validation, + // the key exchange MUST fail. + if (!xdh.validate(Q_S)) { + return false; + } + + K = encodeAsMPInt(normalize(xdh.getSecret(Q_S))); + + byte[] sig_of_H = _buf.getString(); + + // The hash H is computed as the HASH hash of the concatenation of the + // following: + // string V_C, client's identification string (CR and LF excluded) + // string V_S, server's identification string (CR and LF excluded) + // string I_C, payload of the client's SSH_MSG_KEXINIT + // string I_S, payload of the server's SSH_MSG_KEXINIT + // string K_S, server's public host key + // string Q_C, client's ephemeral public key octet string + // string Q_S, server's ephemeral public key octet string + // mpint K, shared secret + + // This value is called the exchange hash, and it is used to authenti- + // cate the key exchange. + // RFC 8731, + // 3.1. Shared Secret Encoding + // The shared secret, K, is defined in [RFC4253] and [RFC5656] as an + // integer encoded as a multiple precision integer (mpint). + // Curve25519/448 outputs a binary string X, which is the 32- or 56-byte + // point obtained by scalar multiplication of the other side's public + // key and the local private key scalar. The 32 or 56 bytes of X are + // converted into K by interpreting the octets as an unsigned fixed- + // length integer encoded in network byte order. + // + // The mpint K is then encoded using the process described in Section 5 + // of [RFC4251], and the resulting bytes are fed as described in + // [RFC4253] to the key exchange method's hash function to generate + // encryption keys. + buf.reset(); + buf.putString(V_C); + buf.putString(V_S); + buf.putString(I_C); + buf.putString(I_S); + buf.putString(K_S); + buf.putString(Q_C); + buf.putString(Q_S); + byte[] foo = new byte[buf.getLength()]; + buf.getByte(foo); + + sha.update(foo, 0, foo.length); + sha.update(K, 0, K.length); + H = sha.digest(); + + i = 0; + j = 0; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + String alg = Util.byte2str(K_S, i, j); + i += j; + + boolean result = verify(alg, K_S, i, sig_of_H); + + state = STATE_END; + return result; + } + return false; + } + + @Override + public int getState() { + return state; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/DHXECKEM.java b/files-jsch/src/main/java/com/jcraft/jsch/DHXECKEM.java new file mode 100644 index 0000000..1bee11f --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/DHXECKEM.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +abstract class DHXECKEM extends KeyExchange { + + private static final int SSH_MSG_KEX_ECDH_INIT = 30; + private static final int SSH_MSG_KEX_ECDH_REPLY = 31; + private int state; + + byte[] Q_C; + + byte[] V_S; + byte[] V_C; + byte[] I_S; + byte[] I_C; + + byte[] e; + + private Buffer buf; + private Packet packet; + + private KEM kem; + private XDH xdh; + + protected String kem_name; + protected String sha_name; + protected String curve_name; + protected int kem_pubkey_len; + protected int kem_encap_len; + protected int xec_key_len; + + @Override + public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) + throws Exception { + this.V_S = V_S; + this.V_C = V_C; + this.I_S = I_S; + this.I_C = I_C; + + try { + Class c = Class.forName(session.getConfig(sha_name)).asSubclass(HASH.class); + sha = c.getDeclaredConstructor().newInstance(); + sha.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + buf = new Buffer(); + packet = new Packet(buf); + + packet.reset(); + // command + string len + Q_C len + buf.checkFreeSize(1 + 4 + kem_pubkey_len + xec_key_len); + buf.putByte((byte) SSH_MSG_KEX_ECDH_INIT); + + try { + Class k = Class.forName(session.getConfig(kem_name)).asSubclass(KEM.class); + kem = k.getDeclaredConstructor().newInstance(); + kem.init(); + + Class c = Class.forName(session.getConfig("xdh")).asSubclass(XDH.class); + xdh = c.getDeclaredConstructor().newInstance(); + xdh.init(curve_name, xec_key_len); + + byte[] kem_public_key_C = kem.getPublicKey(); + byte[] xec_public_key_C = xdh.getQ(); + Q_C = new byte[kem_pubkey_len + xec_key_len]; + System.arraycopy(kem_public_key_C, 0, Q_C, 0, kem_pubkey_len); + System.arraycopy(xec_public_key_C, 0, Q_C, kem_pubkey_len, xec_key_len); + buf.putString(Q_C); + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException(e.toString(), e); + } + + if (V_S == null) { // This is a really ugly hack for Session.checkKexes ;-( + return; + } + + session.write(packet); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "SSH_MSG_KEX_ECDH_INIT sent"); + session.getLogger().log(Logger.INFO, "expecting SSH_MSG_KEX_ECDH_REPLY"); + } + + state = SSH_MSG_KEX_ECDH_REPLY; + } + + @Override + public boolean next(Buffer _buf) throws Exception { + int i, j; + switch (state) { + case SSH_MSG_KEX_ECDH_REPLY: + // The server responds with: + // byte SSH_MSG_KEX_ECDH_REPLY + // string K_S, server's public host key + // string Q_S, server's ephemeral public key octet string + // string the signature on the exchange hash + j = _buf.getInt(); + j = _buf.getByte(); + j = _buf.getByte(); + if (j != SSH_MSG_KEX_ECDH_REPLY) { + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "type: must be SSH_MSG_KEX_ECDH_REPLY " + j); + } + return false; + } + + K_S = _buf.getString(); + + byte[] Q_S = _buf.getString(); + if (Q_S.length != kem_encap_len + xec_key_len) { + return false; + } + + byte[] encapsulation = new byte[kem_encap_len]; + byte[] xec_public_key_S = new byte[xec_key_len]; + System.arraycopy(Q_S, 0, encapsulation, 0, kem_encap_len); + System.arraycopy(Q_S, kem_encap_len, xec_public_key_S, 0, xec_key_len); + + // RFC 5656, + // 4. ECDH Key Exchange + // All elliptic curve public keys MUST be validated after they are + // received. An example of a validation algorithm can be found in + // Section 3.2.2 of [SEC1]. If a key fails validation, + // the key exchange MUST fail. + if (!xdh.validate(xec_public_key_S)) { + return false; + } + + byte[] tmp = null; + try { + tmp = kem.decapsulate(encapsulation); + sha.update(tmp, 0, tmp.length); + } finally { + Util.bzero(tmp); + } + try { + tmp = normalize(xdh.getSecret(xec_public_key_S)); + sha.update(tmp, 0, tmp.length); + } finally { + Util.bzero(tmp); + } + K = encodeAsString(sha.digest()); + + byte[] sig_of_H = _buf.getString(); + + // The hash H is computed as the HASH hash of the concatenation of the + // following: + // string V_C, client's identification string (CR and LF excluded) + // string V_S, server's identification string (CR and LF excluded) + // string I_C, payload of the client's SSH_MSG_KEXINIT + // string I_S, payload of the server's SSH_MSG_KEXINIT + // string K_S, server's public host key + // string Q_C, client's ephemeral public key octet string + // string Q_S, server's ephemeral public key octet string + // string K, shared secret + + // draft-josefsson-ntruprime-ssh-02, + // 3. Key Exchange Method: sntrup761x25519-sha512 + // ... + // The SSH_MSG_KEX_ECDH_REPLY's signature value is computed as described + // in [RFC5656] with the following changes. Instead of encoding the + // shared secret K as 'mpint', it MUST be encoded as 'string'. The + // shared secret K value MUST be the 64-byte output octet string of the + // SHA-512 hash computed with the input as the 32-byte octet string key + // output from the key encapsulation mechanism of sntrup761 concatenated + // with the 32-byte octet string of X25519(a, X25519(b, 9)) = X25519(b, + // X25519(a, 9)). + buf.reset(); + buf.putString(V_C); + buf.putString(V_S); + buf.putString(I_C); + buf.putString(I_S); + buf.putString(K_S); + buf.putString(Q_C); + buf.putString(Q_S); + byte[] foo = new byte[buf.getLength()]; + buf.getByte(foo); + + sha.update(foo, 0, foo.length); + sha.update(K, 0, K.length); + H = sha.digest(); + + i = 0; + j = 0; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + String alg = Util.byte2str(K_S, i, j); + i += j; + + boolean result = verify(alg, K_S, i, sig_of_H); + + state = STATE_END; + return result; + } + return false; + } + + @Override + public int getState() { + return state; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ECDH.java b/files-jsch/src/main/java/com/jcraft/jsch/ECDH.java new file mode 100644 index 0000000..46aa51e --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ECDH.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface ECDH { + void init(int size) throws Exception; + + byte[] getSecret(byte[] r, byte[] s) throws Exception; + + byte[] getQ() throws Exception; + + boolean validate(byte[] r, byte[] s) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java b/files-jsch/src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java new file mode 100644 index 0000000..ef0fedc --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ForwardedTCPIPDaemon.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.InputStream; +import java.io.OutputStream; + +public interface ForwardedTCPIPDaemon extends Runnable { + void setChannel(ChannelForwardedTCPIP channel, InputStream in, OutputStream out); + + void setArg(Object[] arg); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/GSSContext.java b/files-jsch/src/main/java/com/jcraft/jsch/GSSContext.java new file mode 100644 index 0000000..c780e87 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/GSSContext.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface GSSContext { + public void create(String user, String host) throws JSchException; + + public boolean isEstablished(); + + public byte[] init(byte[] token, int s, int l) throws JSchException; + + public byte[] getMIC(byte[] message, int s, int l); + + public void dispose(); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/HASH.java b/files-jsch/src/main/java/com/jcraft/jsch/HASH.java new file mode 100644 index 0000000..f1e4744 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/HASH.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface HASH { + void init() throws Exception; + + int getBlockSize(); + + void update(byte[] foo, int start, int len) throws Exception; + + byte[] digest() throws Exception; + + default String name() { + return ""; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/HostKey.java b/files-jsch/src/main/java/com/jcraft/jsch/HostKey.java new file mode 100644 index 0000000..0f9922b --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/HostKey.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Locale; + +public class HostKey { + + private static final byte[][] names = + {Util.str2byte("ssh-dss"), Util.str2byte("ssh-rsa"), Util.str2byte("ecdsa-sha2-nistp256"), + Util.str2byte("ecdsa-sha2-nistp384"), Util.str2byte("ecdsa-sha2-nistp521"), + Util.str2byte("ssh-ed25519"), Util.str2byte("ssh-ed448")}; + + public static final int UNKNOWN = -1; + public static final int GUESS = 0; + public static final int SSHDSS = 1; + public static final int SSHRSA = 2; + public static final int ECDSA256 = 3; + public static final int ECDSA384 = 4; + public static final int ECDSA521 = 5; + public static final int ED25519 = 6; + public static final int ED448 = 7; + + protected String marker; + protected String host; + protected int type; + protected byte[] key; + protected String comment; + + public HostKey(String host, byte[] key) throws JSchException { + this(host, GUESS, key); + } + + public HostKey(String host, int type, byte[] key) throws JSchException { + this(host, type, key, null); + } + + public HostKey(String host, int type, byte[] key, String comment) throws JSchException { + this("", host, type, key, comment); + } + + public HostKey(String marker, String host, int type, byte[] key, String comment) + throws JSchException { + this.marker = marker; + this.host = host; + if (type == GUESS) { + if (key[8] == 'd') { + this.type = SSHDSS; + } else if (key[8] == 'r') { + this.type = SSHRSA; + } else if (key[8] == 'e' && key[10] == '2') { + this.type = ED25519; + } else if (key[8] == 'e' && key[10] == '4') { + this.type = ED448; + } else if (key[8] == 'a' && key[20] == '2') { + this.type = ECDSA256; + } else if (key[8] == 'a' && key[20] == '3') { + this.type = ECDSA384; + } else if (key[8] == 'a' && key[20] == '5') { + this.type = ECDSA521; + } else { + throw new JSchException("invalid key type"); + } + } else { + this.type = type; + } + this.key = key; + this.comment = comment; + } + + public String getHost() { + return host; + } + + public String getType() { + if (type == SSHDSS || type == SSHRSA || type == ED25519 || type == ED448 || type == ECDSA256 + || type == ECDSA384 || type == ECDSA521) { + return Util.byte2str(names[type - 1]); + } + return "UNKNOWN"; + } + + protected static int name2type(String name) { + for (int i = 0; i < names.length; i++) { + if (Util.byte2str(names[i]).equals(name)) { + return i + 1; + } + } + return UNKNOWN; + } + + public String getKey() { + return Util.byte2str(Util.toBase64(key, 0, key.length, true)); + } + + public String getFingerPrint(JSch jsch) { + HASH hash = null; + try { + String _c = JSch.getConfig("FingerprintHash").toLowerCase(Locale.ROOT); + Class c = Class.forName(JSch.getConfig(_c)).asSubclass(HASH.class); + hash = c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + if (jsch.getInstanceLogger().isEnabled(Logger.ERROR)) { + jsch.getInstanceLogger().log(Logger.ERROR, "getFingerPrint: " + e.getMessage(), e); + } + } + return Util.getFingerPrint(hash, key, false, true); + } + + public String getComment() { + return comment; + } + + public String getMarker() { + return marker; + } + + boolean isMatched(String _host) { + return isIncluded(_host); + } + + private boolean isIncluded(String _host) { + int i = 0; + String hosts = this.host; + int hostslen = hosts.length(); + int hostlen = _host.length(); + int j; + while (i < hostslen) { + j = hosts.indexOf(',', i); + if (j == -1) { + if (hostlen != hostslen - i) + return false; + return hosts.regionMatches(true, i, _host, 0, hostlen); + } + if (hostlen == (j - i)) { + if (hosts.regionMatches(true, i, _host, 0, hostlen)) + return true; + } + i = j + 1; + } + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/HostKeyRepository.java b/files-jsch/src/main/java/com/jcraft/jsch/HostKeyRepository.java new file mode 100644 index 0000000..7683da3 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/HostKeyRepository.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2004-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface HostKeyRepository { + final int OK = 0; + final int NOT_INCLUDED = 1; + final int CHANGED = 2; + + /** + * Checks if host is included with the key. + * + * @return #NOT_INCLUDED, #OK or #CHANGED + * @see #NOT_INCLUDED + * @see #OK + * @see #CHANGED + */ + int check(String host, byte[] key); + + /** + * Adds a host key hostkey + * + * @param hostkey a host key to be added + * @param ui a user interface for showing messages or promping inputs. + * @see UserInfo + */ + void add(HostKey hostkey, UserInfo ui); + + /** + * Removes a host key if there exists mached key with host, type. + * + * @see #remove(String host, String type, byte[] key) + */ + void remove(String host, String type); + + /** + * Removes a host key if there exists a matched key with host, type and + * key. + */ + void remove(String host, String type, byte[] key); + + /** + * Returns id of this repository. + * + * @return identity in String + */ + String getKnownHostsRepositoryID(); + + /** + * Retuns a list for host keys managed in this repository. + * + * @see #getHostKey(String host, String type) + */ + HostKey[] getHostKey(); + + /** + * Retuns a list for host keys managed in this repository. + * + * @param host a hostname used in searching host keys. If null is given, every host + * key will be listed. + * @param type a key type used in searching host keys, and it should be "ssh-dss" or "ssh-rsa". If + * null is given, a key type type will not be ignored. + */ + HostKey[] getHostKey(String host, String type); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/IO.java b/files-jsch/src/main/java/com/jcraft/jsch/IO.java new file mode 100644 index 0000000..d00241a --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/IO.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.SocketException; + +class IO { + InputStream in; + OutputStream out; + OutputStream out_ext; + + private boolean in_dontclose = false; + private boolean out_dontclose = false; + private boolean out_ext_dontclose = false; + + void setOutputStream(OutputStream out) { + this.out = out; + } + + void setOutputStream(OutputStream out, boolean dontclose) { + this.out_dontclose = dontclose; + setOutputStream(out); + } + + void setExtOutputStream(OutputStream out) { + this.out_ext = out; + } + + void setExtOutputStream(OutputStream out, boolean dontclose) { + this.out_ext_dontclose = dontclose; + setExtOutputStream(out); + } + + void setInputStream(InputStream in) { + this.in = in; + } + + void setInputStream(InputStream in, boolean dontclose) { + this.in_dontclose = dontclose; + setInputStream(in); + } + + void put(Packet p) throws IOException, SocketException { + out.write(p.buffer.buffer, 0, p.buffer.index); + out.flush(); + } + + void put(byte[] array, int begin, int length) throws IOException { + out.write(array, begin, length); + out.flush(); + } + + void put_ext(byte[] array, int begin, int length) throws IOException { + out_ext.write(array, begin, length); + out_ext.flush(); + } + + int getByte() throws IOException { + return in.read(); + } + + void getByte(byte[] array) throws IOException { + getByte(array, 0, array.length); + } + + void getByte(byte[] array, int begin, int length) throws IOException { + do { + int completed = in.read(array, begin, length); + if (completed < 0) { + throw new IOException("End of IO Stream Read"); + } + begin += completed; + length -= completed; + } while (length > 0); + } + + void out_close() { + try { + if (out != null && !out_dontclose) + out.close(); + out = null; + } catch (Exception ee) { + } + } + + void close() { + try { + if (in != null && !in_dontclose) + in.close(); + in = null; + } catch (Exception ee) { + } + + out_close(); + + try { + if (out_ext != null && !out_ext_dontclose) + out_ext.close(); + out_ext = null; + } catch (Exception ee) { + } + } + + /* + * void finalize() throws Throwable{ try{ if(in!=null) in.close(); } catch(Exception ee){} try{ + * if(out!=null) out.close(); } catch(Exception ee){} try{ if(out_ext!=null) out_ext.close(); } + * catch(Exception ee){} } + */ +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Identity.java b/files-jsch/src/main/java/com/jcraft/jsch/Identity.java new file mode 100644 index 0000000..2520cd2 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Identity.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface Identity { + + /** + * Decrypts this identity with the specified pass-phrase. + * + * @param passphrase the pass-phrase for this identity. + * @return true if the decryption is succeeded or this identity is not cyphered. + */ + public boolean setPassphrase(byte[] passphrase) throws JSchException; + + /** + * Returns the public-key blob. + * + * @return the public-key blob + */ + public byte[] getPublicKeyBlob(); + + /** + * Signs on data with this identity, and returns the result. + * + *

+ * IMPORTANT NOTE:
+ * The {@link #getSignature(byte[], String)} method should be overridden to ensure {@code ssh-rsa} + * type public keys function with the {@code rsa-sha2-256} or {@code rsa-sha2-512} signature + * algorithms. + * + * @param data data to be signed + * @return the signature + * @see #getSignature(byte[], String) + */ + public byte[] getSignature(byte[] data); + + /** + * Signs on data with this identity, and returns the result. + * + *

+ * IMPORTANT NOTE:
+ * The default implementation of this method simply calls {@link #getSignature(byte[])}, which + * will fail with {@code ssh-rsa} type public keys when utilized with the {@code rsa-sha2-256} or + * {@code rsa-sha2-512} signature algorithms:
+ * it exists only to maintain backwards compatibility of this interface. + * + *

+ * This default method should be overridden by implementations to ensure the {@code rsa-sha2-256} + * and {@code rsa-sha2-512} signature algorithms function correctly. + * + * @param data data to be signed + * @param alg signature algorithm to use + * @return the signature + * @since 0.1.57 + * @see #getSignature(byte[]) + */ + public default byte[] getSignature(byte[] data, String alg) { + return getSignature(data); + } + + /** + * This method is deprecated and the default implmentation of this method will throw an + * {@link UnsupportedOperationException}. + * + * @deprecated The decryption should be done automatically in {@link #setPassphrase(byte[])} + * @return true if the decryption is succeeded or this identity is not cyphered. + * @see #setPassphrase(byte[]) + */ + @Deprecated + public default boolean decrypt() { + throw new UnsupportedOperationException("not implemented"); + } + + /** + * Returns the name of the key algorithm. + * + * @return the name of the key algorithm + */ + public String getAlgName(); + + /** + * Returns the name of this identity. It will be useful to identify this object in the + * {@link IdentityRepository}. + * + * @return the name of this identity + */ + public String getName(); + + /** + * Returns true if this identity is cyphered. + * + * @return true if this identity is cyphered. + */ + public boolean isEncrypted(); + + /** Disposes internally allocated data, like byte array for the private key. */ + public void clear(); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/IdentityFile.java b/files-jsch/src/main/java/com/jcraft/jsch/IdentityFile.java new file mode 100644 index 0000000..caf4013 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/IdentityFile.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class IdentityFile implements Identity { + private KeyPair kpair; + private String identity; + + static IdentityFile newInstance(String prvfile, String pubfile, JSch.InstanceLogger instLogger) + throws JSchException { + KeyPair kpair = KeyPair.load(instLogger, prvfile, pubfile); + return new IdentityFile(prvfile, kpair); + } + + static IdentityFile newInstance(String name, byte[] prvkey, byte[] pubkey, + JSch.InstanceLogger instLogger) throws JSchException { + + KeyPair kpair = KeyPair.load(instLogger, prvkey, pubkey); + return new IdentityFile(name, kpair); + } + + private IdentityFile(String name, KeyPair kpair) { + this.identity = name; + this.kpair = kpair; + } + + /** + * Decrypts this identity with the specified pass-phrase. + * + * @param passphrase the pass-phrase for this identity. + * @return true if the decryption is succeeded or this identity is not cyphered. + */ + @Override + public boolean setPassphrase(byte[] passphrase) throws JSchException { + return kpair.decrypt(passphrase); + } + + /** + * Returns the public-key blob. + * + * @return the public-key blob + */ + @Override + public byte[] getPublicKeyBlob() { + return kpair.getPublicKeyBlob(); + } + + /** + * Signs on data with this identity, and returns the result. + * + * @param data data to be signed + * @return the signature + */ + @Override + public byte[] getSignature(byte[] data) { + return kpair.getSignature(data); + } + + /** + * Signs on data with this identity, and returns the result. + * + * @param data data to be signed + * @param alg signature algorithm to use + * @return the signature + */ + @Override + public byte[] getSignature(byte[] data, String alg) { + return kpair.getSignature(data, alg); + } + + /** + * Returns the name of the key algorithm. + * + * @return the name of the key algorithm + */ + @Override + public String getAlgName() { + return kpair.getKeyTypeString(); + } + + /** + * Returns the name of this identity. It will be useful to identify this object in the + * {@link IdentityRepository}. + * + * @return the name of this identity + */ + @Override + public String getName() { + return identity; + } + + /** + * Returns true if this identity is cyphered. + * + * @return true if this identity is cyphered. + */ + @Override + public boolean isEncrypted() { + return kpair.isEncrypted(); + } + + /** Disposes internally allocated data, like byte array for the private key. */ + @Override + public void clear() { + kpair.dispose(); + kpair = null; + } + + /** + * Returns an instance of {@link KeyPair} used in this {@link Identity}. + * + * @return an instance of {@link KeyPair} used in this {@link Identity}. + */ + public KeyPair getKeyPair() { + return kpair; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/IdentityRepository.java b/files-jsch/src/main/java/com/jcraft/jsch/IdentityRepository.java new file mode 100644 index 0000000..cc0a91c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/IdentityRepository.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Vector; + +public interface IdentityRepository { + public static final int UNAVAILABLE = 0; + public static final int NOTRUNNING = 1; + public static final int RUNNING = 2; + + public String getName(); + + public int getStatus(); + + public Vector getIdentities(); + + public boolean add(byte[] identity); + + public boolean remove(byte[] blob); + + public void removeAll(); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/IdentityRepositoryWrapper.java b/files-jsch/src/main/java/com/jcraft/jsch/IdentityRepositoryWrapper.java new file mode 100644 index 0000000..73b7e71 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/IdentityRepositoryWrapper.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Vector; + +/** + * JSch will accept ciphered keys, but some implementations of IdentityRepository can not. For + * example, IdentityRepository for ssh-agent and pageant only accept plain keys. The following class + * has been introduced to cache ciphered keys for them, and pass them whenever they are de-ciphered. + */ +class IdentityRepositoryWrapper implements IdentityRepository { + private IdentityRepository ir; + private Vector cache = new Vector<>(); + private boolean keep_in_cache = false; + + IdentityRepositoryWrapper(IdentityRepository ir) { + this(ir, false); + } + + IdentityRepositoryWrapper(IdentityRepository ir, boolean keep_in_cache) { + this.ir = ir; + this.keep_in_cache = keep_in_cache; + } + + @Override + public String getName() { + return ir.getName(); + } + + @Override + public int getStatus() { + return ir.getStatus(); + } + + @Override + public boolean add(byte[] identity) { + return ir.add(identity); + } + + @Override + public boolean remove(byte[] blob) { + return ir.remove(blob); + } + + @Override + public void removeAll() { + cache.removeAllElements(); + ir.removeAll(); + } + + @Override + public Vector getIdentities() { + Vector result = new Vector<>(); + for (int i = 0; i < cache.size(); i++) { + Identity identity = cache.elementAt(i); + result.add(identity); + } + Vector tmp = ir.getIdentities(); + for (int i = 0; i < tmp.size(); i++) { + result.add(tmp.elementAt(i)); + } + return result; + } + + void add(Identity identity) { + if (!keep_in_cache && !identity.isEncrypted() && (identity instanceof IdentityFile)) { + try { + ir.add(((IdentityFile) identity).getKeyPair().forSSHAgent()); + } catch (JSchException e) { + // an exception will not be thrown. + } + } else + cache.addElement(identity); + } + + void check() { + if (cache.size() > 0) { + Object[] identities = cache.toArray(); + for (int i = 0; i < identities.length; i++) { + Identity identity = (Identity) (identities[i]); + cache.removeElement(identity); + add(identity); + } + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSch.java b/files-jsch/src/main/java/com/jcraft/jsch/JSch.java new file mode 100644 index 0000000..486fb25 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSch.java @@ -0,0 +1,694 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +public class JSch { + /** The version number. */ + public static final String VERSION = Version.getVersion(); + + static Hashtable config = new Hashtable<>(); + + static { + config.put("kex", Util.getSystemProperty("jsch.kex", + "curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256")); + config.put("server_host_key", Util.getSystemProperty("jsch.server_host_key", + "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256")); + config.put("prefer_known_host_key_types", + Util.getSystemProperty("jsch.prefer_known_host_key_types", "yes")); + config.put("enable_strict_kex", Util.getSystemProperty("jsch.enable_strict_kex", "yes")); + config.put("require_strict_kex", Util.getSystemProperty("jsch.require_strict_kex", "no")); + config.put("enable_server_sig_algs", + Util.getSystemProperty("jsch.enable_server_sig_algs", "yes")); + config.put("enable_ext_info_in_auth", + Util.getSystemProperty("jsch.enable_ext_info_in_auth", "yes")); + config.put("cipher.s2c", Util.getSystemProperty("jsch.cipher", + "aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com")); + config.put("cipher.c2s", Util.getSystemProperty("jsch.cipher", + "aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com")); + config.put("mac.s2c", Util.getSystemProperty("jsch.mac", + "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1")); + config.put("mac.c2s", Util.getSystemProperty("jsch.mac", + "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1")); + config.put("compression.s2c", Util.getSystemProperty("jsch.compression", "none")); + config.put("compression.c2s", Util.getSystemProperty("jsch.compression", "none")); + + config.put("lang.s2c", Util.getSystemProperty("jsch.lang", "")); + config.put("lang.c2s", Util.getSystemProperty("jsch.lang", "")); + + config.put("dhgex_min", Util.getSystemProperty("jsch.dhgex_min", "2048")); + config.put("dhgex_max", Util.getSystemProperty("jsch.dhgex_max", "8192")); + config.put("dhgex_preferred", Util.getSystemProperty("jsch.dhgex_preferred", "3072")); + + config.put("compression_level", Util.getSystemProperty("jsch.compression_level", "6")); + + config.put("diffie-hellman-group-exchange-sha1", "com.jcraft.jsch.DHGEX1"); + config.put("diffie-hellman-group1-sha1", "com.jcraft.jsch.DHG1"); + config.put("diffie-hellman-group14-sha1", "com.jcraft.jsch.DHG14"); + config.put("diffie-hellman-group-exchange-sha256", "com.jcraft.jsch.DHGEX256"); + config.put("diffie-hellman-group-exchange-sha224@ssh.com", "com.jcraft.jsch.DHGEX224"); + config.put("diffie-hellman-group-exchange-sha384@ssh.com", "com.jcraft.jsch.DHGEX384"); + config.put("diffie-hellman-group-exchange-sha512@ssh.com", "com.jcraft.jsch.DHGEX512"); + config.put("diffie-hellman-group14-sha256", "com.jcraft.jsch.DHG14256"); + config.put("diffie-hellman-group15-sha512", "com.jcraft.jsch.DHG15"); + config.put("diffie-hellman-group16-sha512", "com.jcraft.jsch.DHG16"); + config.put("diffie-hellman-group17-sha512", "com.jcraft.jsch.DHG17"); + config.put("diffie-hellman-group18-sha512", "com.jcraft.jsch.DHG18"); + config.put("diffie-hellman-group14-sha256@ssh.com", "com.jcraft.jsch.DHG14256"); + config.put("diffie-hellman-group14-sha224@ssh.com", "com.jcraft.jsch.DHG14224"); + config.put("diffie-hellman-group15-sha256@ssh.com", "com.jcraft.jsch.DHG15256"); + config.put("diffie-hellman-group15-sha384@ssh.com", "com.jcraft.jsch.DHG15384"); + config.put("diffie-hellman-group16-sha512@ssh.com", "com.jcraft.jsch.DHG16"); + config.put("diffie-hellman-group16-sha384@ssh.com", "com.jcraft.jsch.DHG16384"); + config.put("diffie-hellman-group18-sha512@ssh.com", "com.jcraft.jsch.DHG18"); + config.put("ecdsa-sha2-nistp256", "com.jcraft.jsch.jce.SignatureECDSA256"); + config.put("ecdsa-sha2-nistp384", "com.jcraft.jsch.jce.SignatureECDSA384"); + config.put("ecdsa-sha2-nistp521", "com.jcraft.jsch.jce.SignatureECDSA521"); + + config.put("ecdh-sha2-nistp256", "com.jcraft.jsch.DHEC256"); + config.put("ecdh-sha2-nistp384", "com.jcraft.jsch.DHEC384"); + config.put("ecdh-sha2-nistp521", "com.jcraft.jsch.DHEC521"); + + config.put("ecdh-sha2-nistp", "com.jcraft.jsch.jce.ECDHN"); + + config.put("curve25519-sha256", "com.jcraft.jsch.DH25519"); + config.put("curve25519-sha256@libssh.org", "com.jcraft.jsch.DH25519"); + config.put("curve448-sha512", "com.jcraft.jsch.DH448"); + config.put("sntrup761x25519-sha512@openssh.com", "com.jcraft.jsch.DH25519SNTRUP761"); + + config.put("sntrup761", "com.jcraft.jsch.bc.SNTRUP761"); + + config.put("dh", "com.jcraft.jsch.jce.DH"); + config.put("3des-cbc", "com.jcraft.jsch.jce.TripleDESCBC"); + config.put("blowfish-cbc", "com.jcraft.jsch.jce.BlowfishCBC"); + config.put("hmac-sha1", "com.jcraft.jsch.jce.HMACSHA1"); + config.put("hmac-sha1-96", "com.jcraft.jsch.jce.HMACSHA196"); + config.put("hmac-sha2-256", "com.jcraft.jsch.jce.HMACSHA256"); + config.put("hmac-sha2-512", "com.jcraft.jsch.jce.HMACSHA512"); + config.put("hmac-md5", "com.jcraft.jsch.jce.HMACMD5"); + config.put("hmac-md5-96", "com.jcraft.jsch.jce.HMACMD596"); + config.put("hmac-sha1-etm@openssh.com", "com.jcraft.jsch.jce.HMACSHA1ETM"); + config.put("hmac-sha1-96-etm@openssh.com", "com.jcraft.jsch.jce.HMACSHA196ETM"); + config.put("hmac-sha2-256-etm@openssh.com", "com.jcraft.jsch.jce.HMACSHA256ETM"); + config.put("hmac-sha2-512-etm@openssh.com", "com.jcraft.jsch.jce.HMACSHA512ETM"); + config.put("hmac-md5-etm@openssh.com", "com.jcraft.jsch.jce.HMACMD5ETM"); + config.put("hmac-md5-96-etm@openssh.com", "com.jcraft.jsch.jce.HMACMD596ETM"); + config.put("hmac-sha256-2@ssh.com", "com.jcraft.jsch.jce.HMACSHA2562SSHCOM"); + config.put("hmac-sha224@ssh.com", "com.jcraft.jsch.jce.HMACSHA224SSHCOM"); + config.put("hmac-sha256@ssh.com", "com.jcraft.jsch.jce.HMACSHA256SSHCOM"); + config.put("hmac-sha384@ssh.com", "com.jcraft.jsch.jce.HMACSHA384SSHCOM"); + config.put("hmac-sha512@ssh.com", "com.jcraft.jsch.jce.HMACSHA512SSHCOM"); + config.put("sha-1", "com.jcraft.jsch.jce.SHA1"); + config.put("sha-224", "com.jcraft.jsch.jce.SHA224"); + config.put("sha-256", "com.jcraft.jsch.jce.SHA256"); + config.put("sha-384", "com.jcraft.jsch.jce.SHA384"); + config.put("sha-512", "com.jcraft.jsch.jce.SHA512"); + config.put("md5", "com.jcraft.jsch.jce.MD5"); + config.put("sha1", "com.jcraft.jsch.jce.SHA1"); + config.put("sha224", "com.jcraft.jsch.jce.SHA224"); + config.put("sha256", "com.jcraft.jsch.jce.SHA256"); + config.put("sha384", "com.jcraft.jsch.jce.SHA384"); + config.put("sha512", "com.jcraft.jsch.jce.SHA512"); + config.put("signature.dss", "com.jcraft.jsch.jce.SignatureDSA"); + config.put("ssh-rsa", "com.jcraft.jsch.jce.SignatureRSA"); + config.put("rsa-sha2-256", "com.jcraft.jsch.jce.SignatureRSASHA256"); + config.put("rsa-sha2-512", "com.jcraft.jsch.jce.SignatureRSASHA512"); + config.put("ssh-rsa-sha224@ssh.com", "com.jcraft.jsch.jce.SignatureRSASHA224SSHCOM"); + config.put("ssh-rsa-sha256@ssh.com", "com.jcraft.jsch.jce.SignatureRSASHA256SSHCOM"); + config.put("ssh-rsa-sha384@ssh.com", "com.jcraft.jsch.jce.SignatureRSASHA384SSHCOM"); + config.put("ssh-rsa-sha512@ssh.com", "com.jcraft.jsch.jce.SignatureRSASHA512SSHCOM"); + config.put("keypairgen.dsa", "com.jcraft.jsch.jce.KeyPairGenDSA"); + config.put("keypairgen.rsa", "com.jcraft.jsch.jce.KeyPairGenRSA"); + config.put("keypairgen.ecdsa", "com.jcraft.jsch.jce.KeyPairGenECDSA"); + config.put("random", "com.jcraft.jsch.jce.Random"); + + config.put("hmac-ripemd160", "com.jcraft.jsch.bc.HMACRIPEMD160"); + config.put("hmac-ripemd160@openssh.com", "com.jcraft.jsch.bc.HMACRIPEMD160OpenSSH"); + config.put("hmac-ripemd160-etm@openssh.com", "com.jcraft.jsch.bc.HMACRIPEMD160ETM"); + + config.put("none", "com.jcraft.jsch.CipherNone"); + + config.put("aes128-gcm@openssh.com", "com.jcraft.jsch.jce.AES128GCM"); + config.put("aes256-gcm@openssh.com", "com.jcraft.jsch.jce.AES256GCM"); + + config.put("aes128-cbc", "com.jcraft.jsch.jce.AES128CBC"); + config.put("aes192-cbc", "com.jcraft.jsch.jce.AES192CBC"); + config.put("aes256-cbc", "com.jcraft.jsch.jce.AES256CBC"); + config.put("rijndael-cbc@lysator.liu.se", "com.jcraft.jsch.jce.AES256CBC"); + + config.put("chacha20-poly1305@openssh.com", "com.jcraft.jsch.bc.ChaCha20Poly1305"); + config.put("cast128-cbc", "com.jcraft.jsch.bc.CAST128CBC"); + config.put("cast128-ctr", "com.jcraft.jsch.bc.CAST128CTR"); + config.put("twofish128-cbc", "com.jcraft.jsch.bc.Twofish128CBC"); + config.put("twofish192-cbc", "com.jcraft.jsch.bc.Twofish192CBC"); + config.put("twofish256-cbc", "com.jcraft.jsch.bc.Twofish256CBC"); + config.put("twofish-cbc", "com.jcraft.jsch.bc.Twofish256CBC"); + config.put("twofish128-ctr", "com.jcraft.jsch.bc.Twofish128CTR"); + config.put("twofish192-ctr", "com.jcraft.jsch.bc.Twofish192CTR"); + config.put("twofish256-ctr", "com.jcraft.jsch.bc.Twofish256CTR"); + config.put("seed-cbc@ssh.com", "com.jcraft.jsch.bc.SEEDCBC"); + + config.put("aes128-ctr", "com.jcraft.jsch.jce.AES128CTR"); + config.put("aes192-ctr", "com.jcraft.jsch.jce.AES192CTR"); + config.put("aes256-ctr", "com.jcraft.jsch.jce.AES256CTR"); + config.put("3des-ctr", "com.jcraft.jsch.jce.TripleDESCTR"); + config.put("blowfish-ctr", "com.jcraft.jsch.jce.BlowfishCTR"); + config.put("arcfour", "com.jcraft.jsch.jce.ARCFOUR"); + config.put("arcfour128", "com.jcraft.jsch.jce.ARCFOUR128"); + config.put("arcfour256", "com.jcraft.jsch.jce.ARCFOUR256"); + + config.put("userauth.none", "com.jcraft.jsch.UserAuthNone"); + config.put("userauth.password", "com.jcraft.jsch.UserAuthPassword"); + config.put("userauth.keyboard-interactive", "com.jcraft.jsch.UserAuthKeyboardInteractive"); + config.put("userauth.publickey", "com.jcraft.jsch.UserAuthPublicKey"); + config.put("userauth.gssapi-with-mic", "com.jcraft.jsch.UserAuthGSSAPIWithMIC"); + config.put("gssapi-with-mic.krb5", "com.jcraft.jsch.jgss.GSSContextKrb5"); + + config.put("zlib", "com.jcraft.jsch.jzlib.Compression"); + config.put("zlib@openssh.com", "com.jcraft.jsch.jzlib.Compression"); + + config.put("pbkdf", "com.jcraft.jsch.jce.PBKDF"); + config.put("pbkdf2-hmac-sha1", "com.jcraft.jsch.jce.PBKDF2HMACSHA1"); + config.put("pbkdf2-hmac-sha224", "com.jcraft.jsch.jce.PBKDF2HMACSHA224"); + config.put("pbkdf2-hmac-sha256", "com.jcraft.jsch.jce.PBKDF2HMACSHA256"); + config.put("pbkdf2-hmac-sha384", "com.jcraft.jsch.jce.PBKDF2HMACSHA384"); + config.put("pbkdf2-hmac-sha512", "com.jcraft.jsch.jce.PBKDF2HMACSHA512"); + config.put("pbkdf2-hmac-sha512-224", "com.jcraft.jsch.jce.PBKDF2HMACSHA512224"); + config.put("pbkdf2-hmac-sha512-256", "com.jcraft.jsch.jce.PBKDF2HMACSHA512256"); + config.put("bcrypt", "com.jcraft.jsch.jbcrypt.JBCrypt"); + config.put("argon2", "com.jcraft.jsch.bc.Argon2"); + config.put("scrypt", "com.jcraft.jsch.bc.SCrypt"); + + if (JavaVersion.getVersion() >= 11) { + config.put("xdh", "com.jcraft.jsch.jce.XDH"); + } else { + config.put("xdh", "com.jcraft.jsch.bc.XDH"); + } + + if (JavaVersion.getVersion() >= 15) { + config.put("keypairgen.eddsa", "com.jcraft.jsch.jce.KeyPairGenEdDSA"); + config.put("ssh-ed25519", "com.jcraft.jsch.jce.SignatureEd25519"); + config.put("ssh-ed448", "com.jcraft.jsch.jce.SignatureEd448"); + } else { + config.put("keypairgen.eddsa", "com.jcraft.jsch.bc.KeyPairGenEdDSA"); + config.put("ssh-ed25519", "com.jcraft.jsch.bc.SignatureEd25519"); + config.put("ssh-ed448", "com.jcraft.jsch.bc.SignatureEd448"); + } + config.put("keypairgen_fromprivate.eddsa", "com.jcraft.jsch.bc.KeyPairGenEdDSA"); + + config.put("StrictHostKeyChecking", "ask"); + config.put("HashKnownHosts", "no"); + + config.put("PreferredAuthentications", Util.getSystemProperty("jsch.preferred_authentications", + "gssapi-with-mic,publickey,keyboard-interactive,password")); + config.put("PubkeyAcceptedAlgorithms", Util.getSystemProperty("jsch.client_pubkey", + "ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,rsa-sha2-512,rsa-sha2-256")); + config.put("enable_pubkey_auth_query", + Util.getSystemProperty("jsch.enable_pubkey_auth_query", "yes")); + config.put("try_additional_pubkey_algorithms", + Util.getSystemProperty("jsch.try_additional_pubkey_algorithms", "yes")); + config.put("enable_auth_none", Util.getSystemProperty("jsch.enable_auth_none", "yes")); + config.put("use_sftp_write_flush_workaround", + Util.getSystemProperty("jsch.use_sftp_write_flush_workaround", "yes")); + + config.put("CheckCiphers", + Util.getSystemProperty("jsch.check_ciphers", "chacha20-poly1305@openssh.com")); + config.put("CheckMacs", Util.getSystemProperty("jsch.check_macs", "")); + config.put("CheckKexes", Util.getSystemProperty("jsch.check_kexes", + "sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,curve448-sha512")); + config.put("CheckSignatures", + Util.getSystemProperty("jsch.check_signatures", "ssh-ed25519,ssh-ed448")); + config.put("FingerprintHash", Util.getSystemProperty("jsch.fingerprint_hash", "sha256")); + + config.put("MaxAuthTries", Util.getSystemProperty("jsch.max_auth_tries", "6")); + config.put("ClearAllForwardings", "no"); + } + + final InstanceLogger instLogger = new InstanceLogger(); + + private Vector sessionPool = new Vector<>(); + + private IdentityRepository defaultIdentityRepository = new LocalIdentityRepository(instLogger); + + private IdentityRepository identityRepository = defaultIdentityRepository; + + private ConfigRepository configRepository = null; + + /** + * Sets the identityRepository, which will be referred in the public key + * authentication. + * + * @param identityRepository if null is given, the default repository, which usually + * refers to ~/.ssh/, will be used. + * @see #getIdentityRepository() + */ + public synchronized void setIdentityRepository(IdentityRepository identityRepository) { + if (identityRepository == null) { + this.identityRepository = defaultIdentityRepository; + } else { + this.identityRepository = identityRepository; + } + } + + public synchronized IdentityRepository getIdentityRepository() { + return this.identityRepository; + } + + public ConfigRepository getConfigRepository() { + return this.configRepository; + } + + public void setConfigRepository(ConfigRepository configRepository) { + this.configRepository = configRepository; + } + + private HostKeyRepository known_hosts = null; + + static final Logger DEVNULL = new Logger() { + @Override + public boolean isEnabled(int level) { + return false; + } + + @Override + public void log(int level, String message) {} + }; + static Logger logger = DEVNULL; + + public JSch() {} + + /** + * Instantiates the Session object with host. The user name and port + * number will be retrieved from ConfigRepository. If user name is not given, the system property + * "user.name" will be referred. + * + * @param host hostname + * @throws JSchException if username or host are invalid. + * @return the instance of Session class. + * @see #getSession(String username, String host, int port) + * @see Session + * @see ConfigRepository + */ + public Session getSession(String host) throws JSchException { + return getSession(null, host, 22); + } + + /** + * Instantiates the Session object with username and host. + * The TCP port 22 will be used in making the connection. Note that the TCP connection must not be + * established until Session#connect(). + * + * @param username user name + * @param host hostname + * @throws JSchException if username or host are invalid. + * @return the instance of Session class. + * @see #getSession(String username, String host, int port) + * @see Session + */ + public Session getSession(String username, String host) throws JSchException { + return getSession(username, host, 22); + } + + /** + * Instantiates the Session object with given username, + * host and port. Note that the TCP connection must not be established + * until Session#connect(). + * + * @param username user name + * @param host hostname + * @param port port number + * @throws JSchException if username or host are invalid. + * @return the instance of Session class. + * @see #getSession(String username, String host, int port) + * @see Session + */ + public Session getSession(String username, String host, int port) throws JSchException { + if (host == null) { + throw new JSchException("host must not be null."); + } + Session s = new Session(this, username, host, port); + return s; + } + + protected void addSession(Session session) { + synchronized (sessionPool) { + sessionPool.addElement(session); + } + } + + protected boolean removeSession(Session session) { + synchronized (sessionPool) { + return sessionPool.remove(session); + } + } + + /** + * Sets the hostkey repository. + * + * @param hkrepo + * @see HostKeyRepository + * @see KnownHosts + */ + public void setHostKeyRepository(HostKeyRepository hkrepo) { + known_hosts = hkrepo; + } + + /** + * Sets the instance of KnownHosts, which refers to filename. + * + * @param filename filename of known_hosts file. + * @throws JSchException if the given filename is invalid. + * @see KnownHosts + */ + public void setKnownHosts(String filename) throws JSchException { + if (known_hosts == null) + known_hosts = new KnownHosts(this); + if (known_hosts instanceof KnownHosts) { + synchronized (known_hosts) { + ((KnownHosts) known_hosts).setKnownHosts(filename); + } + } + } + + /** + * Sets the instance of KnownHosts generated with stream. + * + * @param stream the instance of InputStream from known_hosts file. + * @throws JSchException if an I/O error occurs. + * @see KnownHosts + */ + public void setKnownHosts(InputStream stream) throws JSchException { + if (known_hosts == null) + known_hosts = new KnownHosts(this); + if (known_hosts instanceof KnownHosts) { + synchronized (known_hosts) { + ((KnownHosts) known_hosts).setKnownHosts(stream); + } + } + } + + /** + * Returns the current hostkey repository. By the default, this method will the instance of + * KnownHosts. + * + * @return current hostkey repository. + * @see HostKeyRepository + * @see KnownHosts + */ + public HostKeyRepository getHostKeyRepository() { + if (known_hosts == null) + known_hosts = new KnownHosts(this); + return known_hosts; + } + + /** + * Sets the private key, which will be referred in the public key authentication. + * + * @param prvkey filename of the private key. + * @throws JSchException if prvkey is invalid. + * @see #addIdentity(String prvkey, String passphrase) + */ + public void addIdentity(String prvkey) throws JSchException { + addIdentity(prvkey, (byte[]) null); + } + + /** + * Sets the private key, which will be referred in the public key authentication. Before + * registering it into identityRepository, it will be deciphered with passphrase. + * + * @param prvkey filename of the private key. + * @param passphrase passphrase for prvkey. + * @throws JSchException if passphrase is not right. + * @see #addIdentity(String prvkey, byte[] passphrase) + */ + public void addIdentity(String prvkey, String passphrase) throws JSchException { + byte[] _passphrase = null; + if (passphrase != null) { + _passphrase = Util.str2byte(passphrase); + } + addIdentity(prvkey, _passphrase); + if (_passphrase != null) + Util.bzero(_passphrase); + } + + /** + * Sets the private key, which will be referred in the public key authentication. Before + * registering it into identityRepository, it will be deciphered with passphrase. + * + * @param prvkey filename of the private key. + * @param passphrase passphrase for prvkey. + * @throws JSchException if passphrase is not right. + * @see #addIdentity(String prvkey, String pubkey, byte[] passphrase) + */ + public void addIdentity(String prvkey, byte[] passphrase) throws JSchException { + Identity identity = IdentityFile.newInstance(prvkey, null, instLogger); + addIdentity(identity, passphrase); + } + + /** + * Sets the private key, which will be referred in the public key authentication. Before + * registering it into identityRepository, it will be deciphered with passphrase. + * + * @param prvkey filename of the private key. + * @param pubkey filename of the public key. + * @param passphrase passphrase for prvkey. + * @throws JSchException if passphrase is not right. + */ + public void addIdentity(String prvkey, String pubkey, byte[] passphrase) throws JSchException { + Identity identity = IdentityFile.newInstance(prvkey, pubkey, instLogger); + addIdentity(identity, passphrase); + } + + /** + * Sets the private key, which will be referred in the public key authentication. Before + * registering it into identityRepository, it will be deciphered with passphrase. + * + * @param name name of the identity to be used to retrieve it in the identityRepository. + * @param prvkey private key in byte array. + * @param pubkey public key in byte array. + * @param passphrase passphrase for prvkey. + */ + public void addIdentity(String name, byte[] prvkey, byte[] pubkey, byte[] passphrase) + throws JSchException { + Identity identity = IdentityFile.newInstance(name, prvkey, pubkey, instLogger); + addIdentity(identity, passphrase); + } + + /** + * Sets the private key, which will be referred in the public key authentication. Before + * registering it into identityRepository, it will be deciphered with passphrase. + * + * @param identity private key. + * @param passphrase passphrase for identity. + * @throws JSchException if passphrase is not right. + */ + public void addIdentity(Identity identity, byte[] passphrase) throws JSchException { + if (passphrase != null) { + try { + byte[] goo = new byte[passphrase.length]; + System.arraycopy(passphrase, 0, goo, 0, passphrase.length); + passphrase = goo; + identity.setPassphrase(passphrase); + } finally { + Util.bzero(passphrase); + } + } + + if (identityRepository instanceof LocalIdentityRepository) { + ((LocalIdentityRepository) identityRepository).add(identity); + } else if (identity instanceof IdentityFile && !identity.isEncrypted()) { + identityRepository.add(((IdentityFile) identity).getKeyPair().forSSHAgent()); + } else { + synchronized (this) { + if (!(identityRepository instanceof IdentityRepositoryWrapper)) { + setIdentityRepository(new IdentityRepositoryWrapper(identityRepository)); + } + } + ((IdentityRepositoryWrapper) identityRepository).add(identity); + } + } + + /** + * @deprecated use #removeIdentity(Identity identity) + */ + @Deprecated + public void removeIdentity(String name) throws JSchException { + Vector identities = identityRepository.getIdentities(); + for (int i = 0; i < identities.size(); i++) { + Identity identity = identities.elementAt(i); + if (!identity.getName().equals(name)) + continue; + if (identityRepository instanceof LocalIdentityRepository) { + ((LocalIdentityRepository) identityRepository).remove(identity); + } else + identityRepository.remove(identity.getPublicKeyBlob()); + } + } + + /** + * Removes the identity from identityRepository. + * + * @param identity the indentity to be removed. + * @throws JSchException if identity is invalid. + */ + public void removeIdentity(Identity identity) throws JSchException { + identityRepository.remove(identity.getPublicKeyBlob()); + } + + /** + * Lists names of identities included in the identityRepository. + * + * @return names of identities + * @throws JSchException if identityReposory has problems. + */ + public Vector getIdentityNames() throws JSchException { + Vector foo = new Vector<>(); + Vector identities = identityRepository.getIdentities(); + for (int i = 0; i < identities.size(); i++) { + Identity identity = identities.elementAt(i); + foo.addElement(identity.getName()); + } + return foo; + } + + /** + * Removes all identities from identityRepository. + * + * @throws JSchException if identityReposory has problems. + */ + public void removeAllIdentity() throws JSchException { + identityRepository.removeAll(); + } + + /** + * Returns the config value for the specified key. + * + * @param key key for the configuration. + * @return config value + */ + public static String getConfig(String key) { + synchronized (config) { + if (key.equals("PubkeyAcceptedKeyTypes")) { + key = "PubkeyAcceptedAlgorithms"; + } + return config.get(key); + } + } + + /** + * Sets or Overrides the configuration. + * + * @param newconf configurations + */ + public static void setConfig(Hashtable newconf) { + synchronized (config) { + for (Enumeration e = newconf.keys(); e.hasMoreElements();) { + String newkey = e.nextElement(); + String key = + (newkey.equals("PubkeyAcceptedKeyTypes") ? "PubkeyAcceptedAlgorithms" : newkey); + config.put(key, newconf.get(newkey)); + } + } + } + + /** + * Sets or Overrides the configuration. + * + * @param key key for the configuration + * @param value value for the configuration + */ + public static void setConfig(String key, String value) { + if (key.equals("PubkeyAcceptedKeyTypes")) { + config.put("PubkeyAcceptedAlgorithms", value); + } else { + config.put(key, value); + } + } + + /** + * Sets the logger + * + * @param logger logger or null if no logging should take place + * @see Logger + */ + public static void setLogger(Logger logger) { + if (logger == null) + logger = DEVNULL; + JSch.logger = logger; + } + + /** + * Returns a logger to be used for this particular instance of JSch + * + * @return The logger that is used by this instance. If no particular logger has been set, the + * statically set logger is returned. + */ + public Logger getInstanceLogger() { + return instLogger.getLogger(); + } + + /** + * Sets a logger to be used for this particular instance of JSch + * + * @param logger The logger to be used or null if the statically set logger should be + * used + */ + public void setInstanceLogger(Logger logger) { + instLogger.setLogger(logger); + } + + /** + * Returns the statically set logger, i.e. the logger being used by all JSch instances without + * explicitly set logger. + * + * @return The logger + */ + public static Logger getLogger() { + return logger; + } + + static class InstanceLogger { + private Logger logger; + + private InstanceLogger() {} + + Logger getLogger() { + if (logger == null) { + return JSch.logger; + } + return logger; + } + + void setLogger(Logger logger) { + this.logger = logger; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchAlgoNegoFailException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchAlgoNegoFailException.java new file mode 100644 index 0000000..6e66825 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchAlgoNegoFailException.java @@ -0,0 +1,71 @@ +package com.jcraft.jsch; + +import java.util.Locale; + +/** + * Extension of {@link JSchException} to indicate when a connection fails during algorithm + * negotiation. + */ +public class JSchAlgoNegoFailException extends JSchException { + + private static final long serialVersionUID = -1L; + + private final String algorithmName; + private final String jschProposal; + private final String serverProposal; + + JSchAlgoNegoFailException(int algorithmIndex, String jschProposal, String serverProposal) { + super(failString(algorithmIndex, jschProposal, serverProposal)); + algorithmName = algorithmNameFromIndex(algorithmIndex); + this.jschProposal = jschProposal; + this.serverProposal = serverProposal; + } + + /** Get the algorithm name. */ + public String getAlgorithmName() { + return algorithmName; + } + + /** Get the JSch algorithm proposal. */ + public String getJSchProposal() { + return jschProposal; + } + + /** Get the server algorithm proposal. */ + public String getServerProposal() { + return serverProposal; + } + + private static String failString(int algorithmIndex, String jschProposal, String serverProposal) { + return String.format(Locale.ROOT, + "Algorithm negotiation fail: algorithmName=\"%s\" jschProposal=\"%s\" serverProposal=\"%s\"", + algorithmNameFromIndex(algorithmIndex), jschProposal, serverProposal); + } + + private static String algorithmNameFromIndex(int algorithmIndex) { + switch (algorithmIndex) { + case KeyExchange.PROPOSAL_KEX_ALGS: + return "kex"; + case KeyExchange.PROPOSAL_SERVER_HOST_KEY_ALGS: + return "server_host_key"; + case KeyExchange.PROPOSAL_ENC_ALGS_CTOS: + return "cipher.c2s"; + case KeyExchange.PROPOSAL_ENC_ALGS_STOC: + return "cipher.s2c"; + case KeyExchange.PROPOSAL_MAC_ALGS_CTOS: + return "mac.c2s"; + case KeyExchange.PROPOSAL_MAC_ALGS_STOC: + return "mac.s2c"; + case KeyExchange.PROPOSAL_COMP_ALGS_CTOS: + return "compression.c2s"; + case KeyExchange.PROPOSAL_COMP_ALGS_STOC: + return "compression.s2c"; + case KeyExchange.PROPOSAL_LANG_CTOS: + return "lang.c2s"; + case KeyExchange.PROPOSAL_LANG_STOC: + return "lang.s2c"; + default: + return ""; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchAuthCancelException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchAuthCancelException.java new file mode 100644 index 0000000..8e6be4b --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchAuthCancelException.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class JSchAuthCancelException extends JSchException { + private static final long serialVersionUID = -1L; + String method; + + JSchAuthCancelException() { + super(); + } + + JSchAuthCancelException(String s) { + super(s); + this.method = s; + } + + public String getMethod() { + return method; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchChangedHostKeyException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchChangedHostKeyException.java new file mode 100644 index 0000000..4a1ee61 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchChangedHostKeyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchChangedHostKeyException extends JSchHostKeyException { + private static final long serialVersionUID = -1L; + + JSchChangedHostKeyException() { + super(); + } + + JSchChangedHostKeyException(String s) { + super(s); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchException.java new file mode 100644 index 0000000..aecee10 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchException.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchException extends Exception { + private static final long serialVersionUID = -1L; + + public JSchException() { + super(); + } + + public JSchException(String s) { + super(s); + } + + public JSchException(String s, Throwable e) { + super(s, e); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchHostKeyException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchHostKeyException.java new file mode 100644 index 0000000..91a3f89 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchHostKeyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public abstract class JSchHostKeyException extends JSchException { + private static final long serialVersionUID = -1L; + + JSchHostKeyException() { + super(); + } + + JSchHostKeyException(String s) { + super(s); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchPartialAuthException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchPartialAuthException.java new file mode 100644 index 0000000..e41d3dc --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchPartialAuthException.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class JSchPartialAuthException extends JSchException { + private static final long serialVersionUID = -1L; + String methods; + + public JSchPartialAuthException() { + super(); + } + + public JSchPartialAuthException(String s) { + super(s); + this.methods = s; + } + + public String getMethods() { + return methods; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchProxyException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchProxyException.java new file mode 100644 index 0000000..88f8d99 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchProxyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchProxyException extends JSchException { + private static final long serialVersionUID = -1L; + + public JSchProxyException(String s) { + super(s); + } + + public JSchProxyException(String s, Throwable e) { + super(s, e); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchRevokedHostKeyException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchRevokedHostKeyException.java new file mode 100644 index 0000000..f25e2f6 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchRevokedHostKeyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchRevokedHostKeyException extends JSchHostKeyException { + private static final long serialVersionUID = -1L; + + JSchRevokedHostKeyException() { + super(); + } + + JSchRevokedHostKeyException(String s) { + super(s); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchSessionDisconnectException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchSessionDisconnectException.java new file mode 100644 index 0000000..cb994f9 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchSessionDisconnectException.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchSessionDisconnectException extends JSchException { + private static final long serialVersionUID = -1L; + + // RFC 4253 11.1. + private final int reasonCode; // RFC 4250 4.2.2. + private final String description; + private final String languageTag; + + JSchSessionDisconnectException(String s, int reasonCode, String description, String languageTag) { + super(s); + this.reasonCode = reasonCode; + this.description = description; + this.languageTag = languageTag; + } + + public int getReasonCode() { + return reasonCode; + } + + public String getDescription() { + return description; + } + + public String getLanguageTag() { + return languageTag; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchStrictKexException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchStrictKexException.java new file mode 100644 index 0000000..3454c1d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchStrictKexException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchStrictKexException extends JSchException { + private static final long serialVersionUID = -1L; + + JSchStrictKexException() { + super(); + } + + JSchStrictKexException(String s) { + super(s); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JSchUnknownHostKeyException.java b/files-jsch/src/main/java/com/jcraft/jsch/JSchUnknownHostKeyException.java new file mode 100644 index 0000000..1957bda --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JSchUnknownHostKeyException.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class JSchUnknownHostKeyException extends JSchHostKeyException { + private static final long serialVersionUID = -1L; + + JSchUnknownHostKeyException() { + super(); + } + + JSchUnknownHostKeyException(String s) { + super(s); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JavaVersion.java b/files-jsch/src/main/java/com/jcraft/jsch/JavaVersion.java new file mode 100644 index 0000000..7f91721 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JavaVersion.java @@ -0,0 +1,8 @@ +package com.jcraft.jsch; + +final class JavaVersion { + + static int getVersion() { + return Runtime.version().feature(); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JplLogger.java b/files-jsch/src/main/java/com/jcraft/jsch/JplLogger.java new file mode 100644 index 0000000..f7fddf6 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JplLogger.java @@ -0,0 +1,46 @@ +package com.jcraft.jsch; + +import java.lang.System.Logger; +import java.lang.System.Logger.Level; + +public class JplLogger implements com.jcraft.jsch.Logger { + + private static final Logger logger = System.getLogger(JSch.class.getName()); + + public JplLogger() {} + + @Override + public boolean isEnabled(int level) { + return logger.isLoggable(getLevel(level)); + } + + @Override + public void log(int level, String message) { + logger.log(getLevel(level), message); + } + + @Override + public void log(int level, String message, Throwable cause) { + if (cause == null) { + logger.log(getLevel(level), message); + return; + } + logger.log(getLevel(level), message, cause); + } + + private static Level getLevel(int level) { + switch (level) { + case com.jcraft.jsch.Logger.DEBUG: + return Level.DEBUG; + case com.jcraft.jsch.Logger.INFO: + return Level.INFO; + case com.jcraft.jsch.Logger.WARN: + return Level.WARNING; + case com.jcraft.jsch.Logger.ERROR: + case com.jcraft.jsch.Logger.FATAL: + return Level.ERROR; + default: + return Level.TRACE; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/JulLogger.java b/files-jsch/src/main/java/com/jcraft/jsch/JulLogger.java new file mode 100644 index 0000000..2984bdf --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/JulLogger.java @@ -0,0 +1,46 @@ +package com.jcraft.jsch; + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class JulLogger implements com.jcraft.jsch.Logger { + + private static final Logger logger = Logger.getLogger(JSch.class.getName()); + + public JulLogger() {} + + @Override + public boolean isEnabled(int level) { + return logger.isLoggable(getLevel(level)); + } + + @Override + public void log(int level, String message) { + log(level, message, null); + } + + @Override + public void log(int level, String message, Throwable cause) { + if (cause == null) { + logger.log(getLevel(level), message); + return; + } + logger.log(getLevel(level), message, cause); + } + + static Level getLevel(int level) { + switch (level) { + case com.jcraft.jsch.Logger.DEBUG: + return Level.FINE; + case com.jcraft.jsch.Logger.INFO: + return Level.INFO; + case com.jcraft.jsch.Logger.WARN: + return Level.WARNING; + case com.jcraft.jsch.Logger.ERROR: + case com.jcraft.jsch.Logger.FATAL: + return Level.SEVERE; + default: + return Level.FINER; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KDF.java b/files-jsch/src/main/java/com/jcraft/jsch/KDF.java new file mode 100644 index 0000000..d6b53ed --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KDF.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface KDF { + byte[] getKey(byte[] pass, int size); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KEM.java b/files-jsch/src/main/java/com/jcraft/jsch/KEM.java new file mode 100644 index 0000000..90dcaac --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KEM.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface KEM { + void init() throws Exception; + + byte[] getPublicKey() throws Exception; + + byte[] decapsulate(byte[] encapsulation) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyExchange.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyExchange.java new file mode 100644 index 0000000..3090391 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyExchange.java @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Locale; + +public abstract class KeyExchange { + + static final int PROPOSAL_KEX_ALGS = 0; + static final int PROPOSAL_SERVER_HOST_KEY_ALGS = 1; + static final int PROPOSAL_ENC_ALGS_CTOS = 2; + static final int PROPOSAL_ENC_ALGS_STOC = 3; + static final int PROPOSAL_MAC_ALGS_CTOS = 4; + static final int PROPOSAL_MAC_ALGS_STOC = 5; + static final int PROPOSAL_COMP_ALGS_CTOS = 6; + static final int PROPOSAL_COMP_ALGS_STOC = 7; + static final int PROPOSAL_LANG_CTOS = 8; + static final int PROPOSAL_LANG_STOC = 9; + static final int PROPOSAL_MAX = 10; + static final String[] PROPOSAL_NAMES = + {"KEX algorithms", "host key algorithms", "ciphers c2s", "ciphers s2c", "MACs c2s", + "MACs s2c", "compression c2s", "compression s2c", "languages c2s", "languages s2c"}; + + // static String kex_algs="diffie-hellman-group-exchange-sha1"+ + // ",diffie-hellman-group1-sha1"; + + // static String kex="diffie-hellman-group-exchange-sha1"; + static String kex = "diffie-hellman-group1-sha1"; + static String server_host_key = "ssh-rsa,ssh-dss"; + static String enc_c2s = "blowfish-cbc"; + static String enc_s2c = "blowfish-cbc"; + static String mac_c2s = "hmac-md5"; // hmac-md5,hmac-sha1,hmac-ripemd160, + // hmac-sha1-96,hmac-md5-96 + static String mac_s2c = "hmac-md5"; + // static String comp_c2s="none"; // zlib + // static String comp_s2c="none"; + static String lang_c2s = ""; + static String lang_s2c = ""; + + public static final int STATE_END = 0; + + protected Session session = null; + protected HASH sha = null; + protected byte[] K = null; + protected byte[] H = null; + protected byte[] K_S = null; + + public abstract void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) + throws Exception; + + void doInit(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) throws Exception { + this.session = session; + init(session, V_S, V_C, I_S, I_C); + } + + public abstract boolean next(Buffer buf) throws Exception; + + public abstract int getState(); + + protected final int RSA = 0; + protected final int DSS = 1; + protected final int ECDSA = 2; + protected final int EDDSA = 3; + private int type = 0; + private String key_alg_name = ""; + + public String getKeyType() { + if (type == DSS) + return "DSA"; + if (type == RSA) + return "RSA"; + if (type == EDDSA) + return "EDDSA"; + return "ECDSA"; + } + + public String getKeyAlgorithName() { + return key_alg_name; + } + + protected static String[] guess(Session session, byte[] I_S, byte[] I_C) throws Exception { + String[] guess = new String[PROPOSAL_MAX]; + Buffer sb = new Buffer(I_S); + sb.setOffSet(17); + Buffer cb = new Buffer(I_C); + cb.setOffSet(17); + + if (session.getLogger().isEnabled(Logger.INFO)) { + for (int i = 0; i < PROPOSAL_MAX; i++) { + session.getLogger().log(Logger.INFO, + "server proposal: " + PROPOSAL_NAMES[i] + ": " + Util.byte2str(sb.getString())); + } + for (int i = 0; i < PROPOSAL_MAX; i++) { + session.getLogger().log(Logger.INFO, + "client proposal: " + PROPOSAL_NAMES[i] + ": " + Util.byte2str(cb.getString())); + } + sb.setOffSet(17); + cb.setOffSet(17); + } + + for (int i = 0; i < PROPOSAL_MAX; i++) { + byte[] sp = sb.getString(); // server proposal + byte[] cp = cb.getString(); // client proposal + int j = 0; + int k = 0; + + loop: while (j < cp.length) { + while (j < cp.length && cp[j] != ',') + j++; + if (k == j) + throw new JSchAlgoNegoFailException(i, Util.byte2str(cp), Util.byte2str(sp)); + String algorithm = Util.byte2str(cp, k, j - k); + int l = 0; + int m = 0; + while (l < sp.length) { + while (l < sp.length && sp[l] != ',') + l++; + if (m == l) + throw new JSchAlgoNegoFailException(i, Util.byte2str(cp), Util.byte2str(sp)); + if (algorithm.equals(Util.byte2str(sp, m, l - m))) { + guess[i] = algorithm; + break loop; + } + l++; + m = l; + } + j++; + k = j; + } + if (j == 0) { + guess[i] = ""; + } else if (guess[i] == null) { + throw new JSchAlgoNegoFailException(i, Util.byte2str(cp), Util.byte2str(sp)); + } + } + + boolean _s2cAEAD = false; + boolean _c2sAEAD = false; + try { + Class _s2cclazz = + Class.forName(session.getConfig(guess[PROPOSAL_ENC_ALGS_STOC])).asSubclass(Cipher.class); + Cipher _s2ccipher = _s2cclazz.getDeclaredConstructor().newInstance(); + _s2cAEAD = _s2ccipher.isAEAD(); + if (_s2cAEAD) { + guess[PROPOSAL_MAC_ALGS_STOC] = null; + } + + Class _c2sclazz = + Class.forName(session.getConfig(guess[PROPOSAL_ENC_ALGS_CTOS])).asSubclass(Cipher.class); + Cipher _c2scipher = _c2sclazz.getDeclaredConstructor().newInstance(); + _c2sAEAD = _c2scipher.isAEAD(); + if (_c2sAEAD) { + guess[PROPOSAL_MAC_ALGS_CTOS] = null; + } + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException(e.toString(), e); + } + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "kex: algorithm: " + guess[PROPOSAL_KEX_ALGS]); + session.getLogger().log(Logger.INFO, + "kex: host key algorithm: " + guess[PROPOSAL_SERVER_HOST_KEY_ALGS]); + session.getLogger().log(Logger.INFO, + "kex: server->client" + " cipher: " + guess[PROPOSAL_ENC_ALGS_STOC] + " MAC: " + + (_s2cAEAD ? ("") : (guess[PROPOSAL_MAC_ALGS_STOC])) + " compression: " + + guess[PROPOSAL_COMP_ALGS_STOC]); + session.getLogger().log(Logger.INFO, + "kex: client->server" + " cipher: " + guess[PROPOSAL_ENC_ALGS_CTOS] + " MAC: " + + (_c2sAEAD ? ("") : (guess[PROPOSAL_MAC_ALGS_CTOS])) + " compression: " + + guess[PROPOSAL_COMP_ALGS_CTOS]); + } + + return guess; + } + + public String getFingerPrint() { + HASH hash = null; + try { + String _c = session.getConfig("FingerprintHash").toLowerCase(Locale.ROOT); + Class c = Class.forName(session.getConfig(_c)).asSubclass(HASH.class); + hash = c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "getFingerPrint: " + e.getMessage(), e); + } + } + return Util.getFingerPrint(hash, getHostKey(), true, false); + } + + byte[] getK() { + return K; + } + + void clearK() { + Util.bzero(K); + K = null; + } + + byte[] getH() { + return H; + } + + HASH getHash() { + return sha; + } + + byte[] getHostKey() { + return K_S; + } + + /* + * It seems JCE included in Oracle's Java7u6(and later) has suddenly changed its behavior. The + * secrete generated by KeyAgreement#generateSecret() may start with 0, even if it is a positive + * value. See https://bugs.openjdk.org/browse/JDK-7146728. + */ + protected byte[] normalize(byte[] secret) { + // This should be a timing safe version of the following: + // if (secret.length > 1 && secret[0] == 0 && (secret[1] & 0x80) == 0) { + // byte[] tmp = new byte[secret.length - 1]; + // System.arraycopy(secret, 1, tmp, 0, tmp.length); + // Util.bzero(secret); + // return normalize(tmp); + // } else { + // return secret; + // } + + int len = secret.length; + if (len < 2) { + return secret; + } + + // secret[0] == 0 + int a = 0; + int s0 = secret[0] & 0xff; + for (int i = 0; i < 8; i++) { + int j = s0 >>> i; + j &= 0x1; + a |= j; + } + a ^= 0x1; + + // (secret[1..n] & 0x80) == 0 && secret[1..n] != 0 + int offset = 0; + for (int i = 1; i < len; i++) { + int j = secret[i] & 0x80; + j >>>= 7; + j ^= 0x1; + a &= j; + offset += a; + j = secret[i] & 0x7f; + for (int k = 0; k < 7; k++) { + int l = j >>> k; + l &= 0x1; + l ^= 0x1; + a &= l; + } + } + + len -= offset; + // Try to remain timing safe by performing an allocation + copy for leading bytes removed + byte[] foo = new byte[len]; + byte[] bar = new byte[offset]; + System.arraycopy(secret, 0, bar, 0, offset); + System.arraycopy(secret, offset, foo, 0, len); + Util.bzero(secret); + return foo; + } + + protected boolean verify(String alg, byte[] K_S, int index, byte[] sig_of_H) throws Exception { + int i, j; + + i = index; + boolean result = false; + + if (alg.equals("ssh-rsa")) { + byte[] tmp; + byte[] ee; + byte[] n; + + type = RSA; + key_alg_name = alg; + + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + tmp = new byte[j]; + System.arraycopy(K_S, i, tmp, 0, j); + i += j; + ee = tmp; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + tmp = new byte[j]; + System.arraycopy(K_S, i, tmp, 0, j); + i += j; + n = tmp; + + SignatureRSA sig = null; + Buffer buf = new Buffer(sig_of_H); + String foo = Util.byte2str(buf.getString()); + try { + Class c = + Class.forName(session.getConfig(foo)).asSubclass(SignatureRSA.class); + sig = c.getDeclaredConstructor().newInstance(); + sig.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + sig.setPubKey(ee, n); + sig.update(H); + result = sig.verify(sig_of_H); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "ssh_rsa_verify: " + foo + " signature " + result); + } + } else if (alg.equals("ssh-dss")) { + byte[] q = null; + byte[] tmp; + byte[] p; + byte[] g; + byte[] f; + + type = DSS; + key_alg_name = alg; + + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + tmp = new byte[j]; + System.arraycopy(K_S, i, tmp, 0, j); + i += j; + p = tmp; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + tmp = new byte[j]; + System.arraycopy(K_S, i, tmp, 0, j); + i += j; + q = tmp; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + tmp = new byte[j]; + System.arraycopy(K_S, i, tmp, 0, j); + i += j; + g = tmp; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + tmp = new byte[j]; + System.arraycopy(K_S, i, tmp, 0, j); + i += j; + f = tmp; + + SignatureDSA sig = null; + try { + Class c = + Class.forName(session.getConfig("signature.dss")).asSubclass(SignatureDSA.class); + sig = c.getDeclaredConstructor().newInstance(); + sig.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + sig.setPubKey(f, p, q, g); + sig.update(H); + result = sig.verify(sig_of_H); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "ssh_dss_verify: signature " + result); + } + } else if (alg.equals("ecdsa-sha2-nistp256") || alg.equals("ecdsa-sha2-nistp384") + || alg.equals("ecdsa-sha2-nistp521")) { + byte[] tmp; + byte[] r; + byte[] s; + + // RFC 5656, + type = ECDSA; + key_alg_name = alg; + + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + tmp = new byte[j]; + System.arraycopy(K_S, i, tmp, 0, j); + i += j; + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + i++; + tmp = new byte[(j - 1) / 2]; + System.arraycopy(K_S, i, tmp, 0, tmp.length); + i += (j - 1) / 2; + r = tmp; + tmp = new byte[(j - 1) / 2]; + System.arraycopy(K_S, i, tmp, 0, tmp.length); + i += (j - 1) / 2; + s = tmp; + + SignatureECDSA sig = null; + try { + Class c = + Class.forName(session.getConfig(alg)).asSubclass(SignatureECDSA.class); + sig = c.getDeclaredConstructor().newInstance(); + sig.init(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + sig.setPubKey(r, s); + + sig.update(H); + + result = sig.verify(sig_of_H); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "ssh_ecdsa_verify: " + alg + " signature " + result); + } + } else if (alg.equals("ssh-ed25519") || alg.equals("ssh-ed448")) { + byte[] tmp; + + // RFC 8709, + type = EDDSA; + key_alg_name = alg; + + j = ((K_S[i++] << 24) & 0xff000000) | ((K_S[i++] << 16) & 0x00ff0000) + | ((K_S[i++] << 8) & 0x0000ff00) | ((K_S[i++]) & 0x000000ff); + tmp = new byte[j]; + System.arraycopy(K_S, i, tmp, 0, j); + i += j; + + SignatureEdDSA sig = null; + try { + Class c = + Class.forName(session.getConfig(alg)).asSubclass(SignatureEdDSA.class); + sig = c.getDeclaredConstructor().newInstance(); + sig.init(); + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException(e.toString(), e); + } + + sig.setPubKey(tmp); + + sig.update(H); + + result = sig.verify(sig_of_H); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "ssh_eddsa_verify: " + alg + " signature " + result); + } + } else { + if (session.getLogger().isEnabled(Logger.ERROR)) { + session.getLogger().log(Logger.ERROR, "unknown alg: " + alg); + } + } + + return result; + } + + protected byte[] encodeAsMPInt(byte[] raw) { + int i = (raw[0] & 0x80) >>> 7; + int len = raw.length + i; + byte[] foo = new byte[len + 4]; + // Try to remain timing safe by performing an extra allocation when i == 0 + byte[] bar = new byte[i ^ 0x1]; + foo[0] = (byte) (len >>> 24); + foo[1] = (byte) (len >>> 16); + foo[2] = (byte) (len >>> 8); + foo[3] = (byte) (len); + System.arraycopy(raw, 0, foo, 4 + i, len - i); + Util.bzero(raw); + return foo; + } + + protected byte[] encodeAsString(byte[] raw) { + int len = raw.length; + byte[] foo = new byte[len + 4]; + foo[0] = (byte) (len >>> 24); + foo[1] = (byte) (len >>> 16); + foo[2] = (byte) (len >>> 8); + foo[3] = (byte) (len); + System.arraycopy(raw, 0, foo, 4, len); + Util.bzero(raw); + return foo; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPair.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPair.java new file mode 100644 index 0000000..5492b46 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPair.java @@ -0,0 +1,1648 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public abstract class KeyPair { + + /** DEFERRED should not be be used. */ + public static final int DEFERRED = -1; + + public static final int ERROR = 0; + public static final int DSA = 1; + public static final int RSA = 2; + public static final int ECDSA = 3; + public static final int UNKNOWN = 4; + public static final int ED25519 = 5; + public static final int ED448 = 6; + + static final int VENDOR_OPENSSH = 0; + static final int VENDOR_FSECURE = 1; + static final int VENDOR_PUTTY = 2; + static final int VENDOR_PKCS8 = 3; + static final int VENDOR_OPENSSH_V1 = 4; + static final int VENDOR_PUTTY_V3 = 5; + + int vendor = VENDOR_OPENSSH; + + private static final byte[] AUTH_MAGIC = Util.str2byte("openssh-key-v1\0"); + private static final byte[] cr = Util.str2byte("\n"); + + public static KeyPair genKeyPair(JSch jsch, int type) throws JSchException { + return genKeyPair(jsch, type, 1024); + } + + public static KeyPair genKeyPair(JSch jsch, int type, int key_size) throws JSchException { + KeyPair kpair = null; + if (type == DSA) { + kpair = new KeyPairDSA(jsch.instLogger); + } else if (type == RSA) { + kpair = new KeyPairRSA(jsch.instLogger); + } else if (type == ECDSA) { + kpair = new KeyPairECDSA(jsch.instLogger); + } else if (type == ED25519) { + kpair = new KeyPairEd25519(jsch.instLogger); + } else if (type == ED448) { + kpair = new KeyPairEd448(jsch.instLogger); + } + if (kpair != null) { + kpair.generate(key_size); + } + return kpair; + } + + abstract void generate(int key_size) throws JSchException; + + abstract byte[] getBegin(); + + abstract byte[] getEnd(); + + public abstract int getKeySize(); + + public abstract byte[] getSignature(byte[] data); + + public abstract byte[] getSignature(byte[] data, String alg); + + public abstract Signature getVerifier(); + + public abstract Signature getVerifier(String alg); + + public abstract byte[] forSSHAgent() throws JSchException; + + public String getPublicKeyComment() { + return publicKeyComment; + } + + public void setPublicKeyComment(String publicKeyComment) { + this.publicKeyComment = publicKeyComment; + } + + protected String publicKeyComment = "no comment"; + + JSch.InstanceLogger instLogger; + protected Cipher cipher; + private KDF kdf; + private HASH sha1; + private HASH hash; + private Random random; + + private byte[] passphrase; + + KeyPair(JSch.InstanceLogger instLogger) { + this.instLogger = instLogger; + } + + static byte[][] header = + {Util.str2byte("Proc-Type: 4,ENCRYPTED"), Util.str2byte("DEK-Info: DES-EDE3-CBC,")}; + + abstract byte[] getPrivateKey(); + + /** + * Writes the plain private key to the given output stream. + * + * @param out output stream + * @see #writePrivateKey(OutputStream out, byte[] passphrase) + */ + public void writePrivateKey(OutputStream out) { + this.writePrivateKey(out, null); + } + + /** + * Writes the cyphered private key to the given output stream. + * + * @param out output stream + * @param passphrase a passphrase to encrypt the private key + */ + public void writePrivateKey(OutputStream out, byte[] passphrase) { + if (passphrase == null) + passphrase = this.passphrase; + + byte[] plain = getPrivateKey(); + byte[][] _iv = new byte[1][]; + byte[] encoded = encrypt(plain, _iv, passphrase); + if (encoded != plain) + Util.bzero(plain); + byte[] iv = _iv[0]; + byte[] prv = Util.toBase64(encoded, 0, encoded.length, true); + + try { + out.write(getBegin()); + out.write(cr); + if (passphrase != null) { + out.write(header[0]); + out.write(cr); + out.write(header[1]); + for (int i = 0; i < iv.length; i++) { + out.write(b2a((byte) ((iv[i] >>> 4) & 0x0f))); + out.write(b2a((byte) (iv[i] & 0x0f))); + } + out.write(cr); + out.write(cr); + } + int i = 0; + while (i < prv.length) { + if (i + 64 < prv.length) { + out.write(prv, i, 64); + out.write(cr); + i += 64; + continue; + } + out.write(prv, i, prv.length - i); + out.write(cr); + break; + } + out.write(getEnd()); + out.write(cr); + // out.close(); + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to write private key", e); + } + } + } + + private static byte[] space = Util.str2byte(" "); + + abstract byte[] getKeyTypeName(); + + public abstract int getKeyType(); + + /** + * Wrapper to provide the String representation of {@code #getKeyTypeName()}. + * + * @return the standard SSH key type string + */ + public String getKeyTypeString() { + return Util.byte2str(getKeyTypeName()); + } + + /** + * Returns the blob of the public key. + * + * @return blob of the public key + */ + public byte[] getPublicKeyBlob() { + // TODO JSchException should be thrown + // if(publickeyblob == null) + // throw new JSchException("public-key blob is not available"); + return publickeyblob; + } + + /** + * Writes the public key with the specified comment to the output stream. + * + * @param out output stream + * @param comment comment + */ + public void writePublicKey(OutputStream out, String comment) { + byte[] pubblob = getPublicKeyBlob(); + byte[] pub = Util.toBase64(pubblob, 0, pubblob.length, true); + try { + out.write(getKeyTypeName()); + out.write(space); + out.write(pub, 0, pub.length); + out.write(space); + out.write(Util.str2byte(comment)); + out.write(cr); + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to write public key", e); + } + } + } + + /** + * Writes the public key with the specified comment to the file. + * + * @param name file name + * @param comment comment + * @see #writePublicKey(OutputStream out, String comment) + */ + public void writePublicKey(String name, String comment) + throws FileNotFoundException, IOException { + try (OutputStream fos = new FileOutputStream(name)) { + writePublicKey(fos, comment); + } + } + + /** + * Writes the public key with the specified comment to the output stream in the format defined in + * http://www.ietf.org/rfc/rfc4716.txt + * + * @param out output stream + * @param comment comment + */ + public void writeSECSHPublicKey(OutputStream out, String comment) { + byte[] pubblob = getPublicKeyBlob(); + byte[] pub = Util.toBase64(pubblob, 0, pubblob.length, true); + try { + out.write(Util.str2byte("---- BEGIN SSH2 PUBLIC KEY ----")); + out.write(cr); + out.write(Util.str2byte("Comment: \"" + comment + "\"")); + out.write(cr); + int index = 0; + while (index < pub.length) { + int len = 70; + if ((pub.length - index) < len) + len = pub.length - index; + out.write(pub, index, len); + out.write(cr); + index += len; + } + out.write(Util.str2byte("---- END SSH2 PUBLIC KEY ----")); + out.write(cr); + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to write public key", e); + } + } + } + + /** + * Writes the public key with the specified comment to the output stream in the format defined in + * http://www.ietf.org/rfc/rfc4716.txt + * + * @param name file name + * @param comment comment + * @see #writeSECSHPublicKey(OutputStream out, String comment) + */ + public void writeSECSHPublicKey(String name, String comment) + throws FileNotFoundException, IOException { + try (OutputStream fos = new FileOutputStream(name)) { + writeSECSHPublicKey(fos, comment); + } + } + + /** + * Writes the plain private key to the file. + * + * @param name file name + * @see #writePrivateKey(String name, byte[] passphrase) + */ + public void writePrivateKey(String name) throws FileNotFoundException, IOException { + this.writePrivateKey(name, null); + } + + /** + * Writes the cyphered private key to the file. + * + * @param name file name + * @param passphrase a passphrase to encrypt the private key + * @see #writePrivateKey(OutputStream out, byte[] passphrase) + */ + public void writePrivateKey(String name, byte[] passphrase) + throws FileNotFoundException, IOException { + try (OutputStream fos = new FileOutputStream(name)) { + writePrivateKey(fos, passphrase); + } + } + + /** + * Returns the finger-print of the public key. + * + * @return finger print + */ + public String getFingerPrint() { + if (hash == null) + hash = genHash(); + byte[] kblob = getPublicKeyBlob(); + if (kblob == null) + return null; + return Util.getFingerPrint(hash, kblob, false, true); + } + + private byte[] encrypt(byte[] plain, byte[][] _iv, byte[] passphrase) { + if (passphrase == null) + return plain; + + if (cipher == null) + cipher = genCipher(); + byte[] iv = _iv[0] = new byte[cipher.getIVSize()]; + + if (random == null) + random = genRandom(); + random.fill(iv, 0, iv.length); + + byte[] key = genKey(passphrase, iv); + byte[] encoded = plain; + + // PKCS#5Padding + { + // int bsize=cipher.getBlockSize(); + int bsize = cipher.getIVSize(); + byte[] foo = new byte[(encoded.length / bsize + 1) * bsize]; + System.arraycopy(encoded, 0, foo, 0, encoded.length); + int padding = bsize - encoded.length % bsize; + for (int i = foo.length - 1; (foo.length - padding) <= i; i--) { + foo[i] = (byte) padding; + } + encoded = foo; + } + + try { + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + cipher.update(encoded, 0, encoded.length, encoded, 0); + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to encrypt key", e); + } + } + Util.bzero(key); + return encoded; + } + + abstract boolean parse(byte[] data); + + private byte[] decrypt(byte[] data, byte[] passphrase, byte[] iv) { + + try { + byte[] key = genKey(passphrase, iv); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + Util.bzero(key); + byte[] plain = new byte[data.length]; + cipher.update(data, 0, data.length, plain, 0); + return plain; + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to decrypt key", e); + } + } + return null; + } + + int writeSEQUENCE(byte[] buf, int index, int len) { + buf[index++] = 0x30; + index = writeLength(buf, index, len); + return index; + } + + int writeINTEGER(byte[] buf, int index, byte[] data) { + buf[index++] = 0x02; + index = writeLength(buf, index, data.length); + System.arraycopy(data, 0, buf, index, data.length); + index += data.length; + return index; + } + + int writeOCTETSTRING(byte[] buf, int index, byte[] data) { + buf[index++] = 0x04; + index = writeLength(buf, index, data.length); + System.arraycopy(data, 0, buf, index, data.length); + index += data.length; + return index; + } + + int writeDATA(byte[] buf, byte n, int index, byte[] data) { + buf[index++] = n; + index = writeLength(buf, index, data.length); + System.arraycopy(data, 0, buf, index, data.length); + index += data.length; + return index; + } + + int countLength(int len) { + int i = 1; + if (len <= 0x7f) + return i; + while (len > 0) { + len >>>= 8; + i++; + } + return i; + } + + int writeLength(byte[] data, int index, int len) { + int i = countLength(len) - 1; + if (i == 0) { + data[index++] = (byte) len; + return index; + } + data[index++] = (byte) (0x80 | i); + int j = index + i; + while (i > 0) { + data[index + i - 1] = (byte) (len & 0xff); + len >>>= 8; + i--; + } + return j; + } + + private Random genRandom() { + if (random == null) { + try { + Class c = + Class.forName(JSch.getConfig("random")).asSubclass(Random.class); + random = c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create random", e); + } + } + } + return random; + } + + private HASH genHash() { + try { + Class c = Class.forName(JSch.getConfig("md5")).asSubclass(HASH.class); + hash = c.getDeclaredConstructor().newInstance(); + hash.init(); + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create hash", e); + } + } + return hash; + } + + private Cipher genCipher() { + try { + Class c = + Class.forName(JSch.getConfig("3des-cbc")).asSubclass(Cipher.class); + cipher = c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create cipher", e); + } + } + return cipher; + } + + /* + * hash is MD5 h(0) <- hash(passphrase, iv); h(n) <- hash(h(n-1), passphrase, iv); key <- + * (h(0),...,h(n))[0,..,key.length]; + */ + synchronized byte[] genKey(byte[] passphrase, byte[] iv) { + if (cipher == null) + cipher = genCipher(); + if (hash == null) + hash = genHash(); + + byte[] key = new byte[cipher.getBlockSize()]; + int hsize = hash.getBlockSize(); + byte[] hn = new byte[key.length / hsize * hsize + (key.length % hsize == 0 ? 0 : hsize)]; + try { + byte[] tmp = null; + if (vendor == VENDOR_OPENSSH) { + for (int index = 0; index + hsize <= hn.length;) { + if (tmp != null) { + hash.update(tmp, 0, tmp.length); + } + hash.update(passphrase, 0, passphrase.length); + hash.update(iv, 0, iv.length > 8 ? 8 : iv.length); + tmp = hash.digest(); + System.arraycopy(tmp, 0, hn, index, tmp.length); + index += tmp.length; + } + System.arraycopy(hn, 0, key, 0, key.length); + } else if (vendor == VENDOR_OPENSSH_V1) { + tmp = kdf.getKey(passphrase, cipher.getBlockSize() + cipher.getIVSize()); + System.arraycopy(tmp, 0, key, 0, key.length); + System.arraycopy(tmp, key.length, iv, 0, iv.length); + Util.bzero(tmp); + } else if (vendor == VENDOR_FSECURE) { + for (int index = 0; index + hsize <= hn.length;) { + if (tmp != null) { + hash.update(tmp, 0, tmp.length); + } + hash.update(passphrase, 0, passphrase.length); + tmp = hash.digest(); + System.arraycopy(tmp, 0, hn, index, tmp.length); + index += tmp.length; + } + System.arraycopy(hn, 0, key, 0, key.length); + } else if (vendor == VENDOR_PUTTY) { + byte[] i = new byte[4]; + + sha1.update(i, 0, i.length); + sha1.update(passphrase, 0, passphrase.length); + tmp = sha1.digest(); + System.arraycopy(tmp, 0, key, 0, tmp.length); + Util.bzero(tmp); + + i[3] = (byte) 1; + sha1.update(i, 0, i.length); + sha1.update(passphrase, 0, passphrase.length); + tmp = sha1.digest(); + System.arraycopy(tmp, 0, key, tmp.length, key.length - tmp.length); + Util.bzero(tmp); + } else if (vendor == VENDOR_PUTTY_V3) { + tmp = kdf.getKey(passphrase, cipher.getBlockSize() + cipher.getIVSize() + 32); + System.arraycopy(tmp, 0, key, 0, key.length); + System.arraycopy(tmp, key.length, iv, 0, iv.length); + Util.bzero(tmp); + } + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to generate key from passphrase", e); + } + } + return key; + } + + /** + * @deprecated use #writePrivateKey(OutputStream out, byte[] passphrase) + */ + @Deprecated + public void setPassphrase(String passphrase) { + if (passphrase == null || passphrase.length() == 0) { + setPassphrase((byte[]) null); + } else { + setPassphrase(Util.str2byte(passphrase)); + } + } + + /** + * @deprecated use #writePrivateKey(String name, byte[] passphrase) + */ + @Deprecated + public void setPassphrase(byte[] passphrase) { + if (passphrase != null && passphrase.length == 0) + passphrase = null; + this.passphrase = passphrase; + } + + protected boolean encrypted = false; + protected byte[] data = null; + private byte[] iv = null; + private byte[] publickeyblob = null; + + public boolean isEncrypted() { + return encrypted; + } + + public boolean decrypt(String _passphrase) { + if (_passphrase == null || _passphrase.length() == 0) { + return !encrypted; + } + return decrypt(Util.str2byte(_passphrase)); + } + + public boolean decrypt(byte[] _passphrase) { + + if (!encrypted) { + return true; + } + if (_passphrase == null) { + return !encrypted; + } + byte[] bar = new byte[_passphrase.length]; + System.arraycopy(_passphrase, 0, bar, 0, bar.length); + _passphrase = bar; + byte[] foo = null; + try { + foo = decrypt(data, _passphrase, iv); + if (parse(foo)) { + encrypted = false; + Util.bzero(data); + } + } finally { + Util.bzero(_passphrase); + Util.bzero(foo); + } + return !encrypted; + } + + public static KeyPair load(JSch jsch, String prvkey) throws JSchException { + String pubkey = prvkey + ".pub"; + if (!new File(pubkey).exists()) { + pubkey = null; + } + return load(jsch.instLogger, prvkey, pubkey); + } + + public static KeyPair load(JSch jsch, String prvfile, String pubfile) throws JSchException { + return load(jsch.instLogger, prvfile, pubfile); + } + + static KeyPair load(JSch.InstanceLogger instLogger, String prvfile, String pubfile) + throws JSchException { + + byte[] prvkey = null; + byte[] pubkey = null; + + try { + prvkey = Util.fromFile(prvfile); + } catch (IOException e) { + throw new JSchException(e.toString(), e); + } + + String _pubfile = pubfile; + if (pubfile == null) { + _pubfile = prvfile + ".pub"; + } + + try { + pubkey = Util.fromFile(_pubfile); + } catch (IOException e) { + if (pubfile != null) { + throw new JSchException(e.toString(), e); + } + } + + try { + return load(instLogger, prvkey, pubkey); + } finally { + Util.bzero(prvkey); + } + } + + public static KeyPair load(JSch jsch, byte[] prvkey, byte[] pubkey) throws JSchException { + return load(jsch.instLogger, prvkey, pubkey); + } + + static KeyPair load(JSch.InstanceLogger instLogger, byte[] prvkey, byte[] pubkey) + throws JSchException { + + byte[] iv = new byte[8]; // 8 + boolean encrypted = true; + byte[] data = null; + + byte[] publickeyblob = null; + + int type = ERROR; + int vendor = VENDOR_OPENSSH; + String publicKeyComment = ""; + Cipher cipher = null; + + // prvkey from "ssh-add" command on the remote. + if (pubkey == null && prvkey != null + && (prvkey.length > 11 && prvkey[0] == 0 && prvkey[1] == 0 && prvkey[2] == 0 && + // length of key type string + (prvkey[3] == 7 || prvkey[3] == 9 || prvkey[3] == 11 || prvkey[3] == 19))) { + + Buffer buf = new Buffer(prvkey); + buf.skip(prvkey.length); // for using Buffer#available() + String _type = Util.byte2str(buf.getString()); // ssh-rsa or ssh-dss + buf.rewind(); + + KeyPair kpair = null; + if (_type.equals("ssh-rsa")) { + kpair = KeyPairRSA.fromSSHAgent(instLogger, buf); + } else if (_type.equals("ssh-dss")) { + kpair = KeyPairDSA.fromSSHAgent(instLogger, buf); + } else if (_type.equals("ecdsa-sha2-nistp256") || _type.equals("ecdsa-sha2-nistp384") + || _type.equals("ecdsa-sha2-nistp521")) { + kpair = KeyPairECDSA.fromSSHAgent(instLogger, buf); + } else if (_type.equals("ssh-ed25519")) { + kpair = KeyPairEd25519.fromSSHAgent(instLogger, buf); + } else if (_type.equals("ssh-ed448")) { + kpair = KeyPairEd448.fromSSHAgent(instLogger, buf); + } else { + throw new JSchException("privatekey: invalid key " + _type); + } + return kpair; + } + + try { + byte[] buf = prvkey; + + if (buf != null) { + KeyPair ppk = loadPPK(instLogger, buf); + if (ppk != null) + return ppk; + } + + int len = (buf != null ? buf.length : 0); + int i = 0; + + // skip garbage lines. + while (i < len) { + if (buf[i] == '-' && i + 4 < len && buf[i + 1] == '-' && buf[i + 2] == '-' + && buf[i + 3] == '-' && buf[i + 4] == '-') { + break; + } + i++; + } + + while (i < len) { + if (buf[i] == 'B' && i + 3 < len && buf[i + 1] == 'E' && buf[i + 2] == 'G' + && buf[i + 3] == 'I') { + i += 6; + if (i + 2 >= len) + throw new JSchException("invalid privatekey"); + if (buf[i] == 'D' && buf[i + 1] == 'S' && buf[i + 2] == 'A') { + type = DSA; + } else if (buf[i] == 'R' && buf[i + 1] == 'S' && buf[i + 2] == 'A') { + type = RSA; + } else if (buf[i] == 'E' && buf[i + 1] == 'C') { + type = ECDSA; + } else if (buf[i] == 'S' && buf[i + 1] == 'S' && buf[i + 2] == 'H') { // FSecure + type = UNKNOWN; + vendor = VENDOR_FSECURE; + } else if (i + 6 < len && buf[i] == 'P' && buf[i + 1] == 'R' && buf[i + 2] == 'I' + && buf[i + 3] == 'V' && buf[i + 4] == 'A' && buf[i + 5] == 'T' && buf[i + 6] == 'E') { + type = UNKNOWN; + vendor = VENDOR_PKCS8; + encrypted = false; + i += 3; + } else if (i + 8 < len && buf[i] == 'E' && buf[i + 1] == 'N' && buf[i + 2] == 'C' + && buf[i + 3] == 'R' && buf[i + 4] == 'Y' && buf[i + 5] == 'P' && buf[i + 6] == 'T' + && buf[i + 7] == 'E' && buf[i + 8] == 'D') { + type = UNKNOWN; + vendor = VENDOR_PKCS8; + i += 5; + } else if (isOpenSSHPrivateKey(buf, i, len)) { + type = UNKNOWN; + vendor = VENDOR_OPENSSH_V1; + } else { + throw new JSchException("invalid privatekey"); + } + i += 3; + continue; + } + if (buf[i] == 'A' && i + 7 < len && buf[i + 1] == 'E' && buf[i + 2] == 'S' + && buf[i + 3] == '-' && buf[i + 4] == '2' && buf[i + 5] == '5' && buf[i + 6] == '6' + && buf[i + 7] == '-') { + i += 8; + if (Session.checkCipher(JSch.getConfig("aes256-cbc"))) { + Class c = + Class.forName(JSch.getConfig("aes256-cbc")).asSubclass(Cipher.class); + cipher = c.getDeclaredConstructor().newInstance(); + // key=new byte[cipher.getBlockSize()]; + iv = new byte[cipher.getIVSize()]; + } else { + throw new JSchException("privatekey: aes256-cbc is not available"); + } + continue; + } + if (buf[i] == 'A' && i + 7 < len && buf[i + 1] == 'E' && buf[i + 2] == 'S' + && buf[i + 3] == '-' && buf[i + 4] == '1' && buf[i + 5] == '9' && buf[i + 6] == '2' + && buf[i + 7] == '-') { + i += 8; + if (Session.checkCipher(JSch.getConfig("aes192-cbc"))) { + Class c = + Class.forName(JSch.getConfig("aes192-cbc")).asSubclass(Cipher.class); + cipher = c.getDeclaredConstructor().newInstance(); + // key=new byte[cipher.getBlockSize()]; + iv = new byte[cipher.getIVSize()]; + } else { + throw new JSchException("privatekey: aes192-cbc is not available"); + } + continue; + } + if (buf[i] == 'A' && i + 7 < len && buf[i + 1] == 'E' && buf[i + 2] == 'S' + && buf[i + 3] == '-' && buf[i + 4] == '1' && buf[i + 5] == '2' && buf[i + 6] == '8' + && buf[i + 7] == '-') { + i += 8; + if (Session.checkCipher(JSch.getConfig("aes128-cbc"))) { + Class c = + Class.forName(JSch.getConfig("aes128-cbc")).asSubclass(Cipher.class); + cipher = c.getDeclaredConstructor().newInstance(); + // key=new byte[cipher.getBlockSize()]; + iv = new byte[cipher.getIVSize()]; + } else { + throw new JSchException("privatekey: aes128-cbc is not available"); + } + continue; + } + if (buf[i] == 'C' && i + 3 < len && buf[i + 1] == 'B' && buf[i + 2] == 'C' + && buf[i + 3] == ',') { + i += 4; + for (int ii = 0; ii < iv.length; ii++) { + iv[ii] = (byte) (((a2b(buf[i++]) << 4) & 0xf0) + (a2b(buf[i++]) & 0xf)); + } + continue; + } + if (buf[i] == '\r' && i + 1 < buf.length && buf[i + 1] == '\n') { + i++; + continue; + } + if (buf[i] == '\n' && i + 1 < buf.length) { + if (buf[i + 1] == '\n') { + i += 2; + break; + } + if (buf[i + 1] == '\r' && i + 2 < buf.length && buf[i + 2] == '\n') { + i += 3; + break; + } + boolean inheader = false; + for (int j = i + 1; j < buf.length; j++) { + if (buf[j] == '\n') + break; + // if(buf[j]=='\r') break; + if (buf[j] == ':') { + inheader = true; + break; + } + } + if (!inheader) { + i++; + if (vendor != VENDOR_PKCS8) + encrypted = false; // no passphrase + break; + } + } + i++; + } + + if (buf != null) { + + if (type == ERROR) { + throw new JSchException("invalid privatekey"); + } + + int start = i; + while (i < len) { + if (buf[i] == '-') { + break; + } + i++; + } + + if ((len - i) == 0 || (i - start) == 0) { + throw new JSchException("invalid privatekey"); + } + + // The content of 'buf' will be changed, so it should be copied. + byte[] tmp = new byte[i - start]; + System.arraycopy(buf, start, tmp, 0, tmp.length); + byte[] _buf = tmp; + + start = 0; + i = 0; + + int _len = _buf.length; + while (i < _len) { + if (_buf[i] == '\n') { + boolean xd = (i > 0 && _buf[i - 1] == '\r'); + // ignore \n (or \r\n) + System.arraycopy(_buf, i + 1, _buf, i - (xd ? 1 : 0), _len - (i + 1)); + if (xd) { + _len--; + i--; + } + _len--; + continue; + } + if (_buf[i] == '-') { + break; + } + i++; + } + + if (i - start > 0) + data = Util.fromBase64(_buf, start, i - start); + + Util.bzero(_buf); + } + + if (vendor == VENDOR_OPENSSH_V1) { + return loadOpenSSHKeyv1(instLogger, data); + } else if (data != null && data.length > 4 && // FSecure + data[0] == (byte) 0x3f && data[1] == (byte) 0x6f && data[2] == (byte) 0xf9 + && data[3] == (byte) 0xeb) { + + Buffer _buf = new Buffer(data); + _buf.getInt(); // 0x3f6ff9be + _buf.getInt(); + byte[] _type = _buf.getString(); + String _cipher = Util.byte2str(_buf.getString()); + if (_cipher.equals("3des-cbc")) { + _buf.getInt(); + byte[] foo = new byte[data.length - _buf.getOffSet()]; + _buf.getByte(foo); + data = foo; + encrypted = true; + throw new JSchException( + "cipher " + _cipher + " is not supported for this privatekey format"); + } else if (_cipher.equals("none")) { + _buf.getInt(); + _buf.getInt(); + + encrypted = false; + + byte[] foo = new byte[data.length - _buf.getOffSet()]; + _buf.getByte(foo); + data = foo; + } else { + throw new JSchException( + "cipher " + _cipher + " is not supported for this privatekey format"); + } + } + + if (pubkey != null) { + try { + buf = pubkey; + len = buf.length; + if (buf.length > 4 && // FSecure's public key + buf[0] == '-' && buf[1] == '-' && buf[2] == '-' && buf[3] == '-') { + + boolean valid = true; + i = 0; + do { + i++; + } while (buf.length > i && buf[i] != '\n'); + if (buf.length <= i) { + valid = false; + } + + while (valid) { + if (buf[i] == '\n') { + boolean inheader = false; + for (int j = i + 1; j < buf.length; j++) { + if (buf[j] == '\n') + break; + if (buf[j] == ':') { + inheader = true; + break; + } + } + if (!inheader) { + i++; + break; + } + } + i++; + } + if (buf.length <= i) { + valid = false; + } + + int start = i; + while (valid && i < len) { + if (buf[i] == '\n') { + System.arraycopy(buf, i + 1, buf, i, len - i - 1); + len--; + continue; + } + if (buf[i] == '-') { + break; + } + i++; + } + if (valid) { + publickeyblob = Util.fromBase64(buf, start, i - start); + if (prvkey == null || type == UNKNOWN) { + if (publickeyblob[8] == 'd') { + type = DSA; + } else if (publickeyblob[8] == 'r') { + type = RSA; + } + } + } + } else { + if (buf[0] == 's' && buf[1] == 's' && buf[2] == 'h' && buf[3] == '-') { + if (prvkey == null && buf.length > 7) { + if (buf[4] == 'd') { + type = DSA; + } else if (buf[4] == 'r') { + type = RSA; + } else if (buf[4] == 'e' && buf[6] == '2') { + type = ED25519; + } else if (buf[4] == 'e' && buf[6] == '4') { + type = ED448; + } + } + i = 0; + while (i < len) { + if (buf[i] == ' ') + break; + i++; + } + i++; + if (i < len) { + int start = i; + while (i < len) { + if (buf[i] == ' ') + break; + i++; + } + publickeyblob = Util.fromBase64(buf, start, i - start); + } + if (i++ < len) { + int start = i; + while (i < len) { + if (buf[i] == '\n') + break; + i++; + } + if (i > 0 && buf[i - 1] == '\r') + i--; + if (start < i) { + publicKeyComment = Util.byte2str(buf, start, i - start); + } + } + } else if (buf[0] == 'e' && buf[1] == 'c' && buf[2] == 'd' && buf[3] == 's') { + if (prvkey == null && buf.length > 7) { + type = ECDSA; + } + i = 0; + while (i < len) { + if (buf[i] == ' ') + break; + i++; + } + i++; + if (i < len) { + int start = i; + while (i < len) { + if (buf[i] == ' ') + break; + i++; + } + publickeyblob = Util.fromBase64(buf, start, i - start); + } + if (i++ < len) { + int start = i; + while (i < len) { + if (buf[i] == '\n') + break; + i++; + } + if (i > 0 && buf[i - 1] == '\r') + i--; + if (start < i) { + publicKeyComment = Util.byte2str(buf, start, i - start); + } + } + } + } + } catch (Exception ee) { + if (instLogger.getLogger().isEnabled(Logger.WARN)) { + instLogger.getLogger().log(Logger.WARN, "failed to parse public key", ee); + } + } + } + + KeyPair kpair = null; + if (type == DSA) { + kpair = new KeyPairDSA(instLogger); + } else if (type == RSA) { + kpair = new KeyPairRSA(instLogger); + } else if (type == ECDSA) { + kpair = new KeyPairECDSA(instLogger, pubkey); + } else if (type == ED25519) { + kpair = new KeyPairEd25519(instLogger, pubkey, null); + } else if (type == ED448) { + kpair = new KeyPairEd448(instLogger, pubkey, null); + } else if (vendor == VENDOR_PKCS8) { + kpair = new KeyPairPKCS8(instLogger); + } + + if (kpair != null) { + kpair.encrypted = encrypted; + kpair.publickeyblob = publickeyblob; + kpair.vendor = vendor; + kpair.publicKeyComment = publicKeyComment; + kpair.cipher = cipher; + + if (encrypted) { + kpair.encrypted = true; + kpair.iv = iv; + kpair.data = data; + } else { + if (kpair.parse(data)) { + kpair.encrypted = false; + } else { + throw new JSchException("invalid privatekey"); + } + } + } + + return kpair; + } catch (Exception | NoClassDefFoundError e) { + Util.bzero(data); + if (e instanceof JSchException) + throw (JSchException) e; + throw new JSchException(e.toString(), e); + } + } + + static KeyPair loadOpenSSHKeyv1(JSch.InstanceLogger instLogger, byte[] data) + throws JSchException { + if (data == null) { + throw new JSchException("invalid privatekey"); + } + + Buffer buffer = new Buffer(data); + byte[] magic = new byte[AUTH_MAGIC.length]; + buffer.getByte(magic); + if (!Util.arraysequals(AUTH_MAGIC, magic)) { + throw new JSchException("Invalid openssh v1 format."); + } + + String cipherName = Util.byte2str(buffer.getString()); + String kdfName = Util.byte2str(buffer.getString()); // string kdfname + byte[] kdfOptions = buffer.getString(); // string kdfoptions + + int nrKeys = buffer.getInt(); // int number of keys N; Should be 1 + if (nrKeys != 1) { + throw new JSchException("We don't support having more than 1 key in the file (yet)."); + } + + byte[] publickeyblob = buffer.getString(); + KeyPair kpair = parsePubkeyBlob(instLogger, publickeyblob, null); + kpair.encrypted = !"none".equals(cipherName); + kpair.publickeyblob = publickeyblob; + kpair.vendor = VENDOR_OPENSSH_V1; + kpair.publicKeyComment = ""; + kpair.data = buffer.getString(); + + try { + if (!kpair.encrypted) { + if (!kpair.parse(kpair.data)) { + throw new JSchException("invalid privatekey"); + } else { + Util.bzero(kpair.data); + } + } else { + if (Session.checkCipher(JSch.getConfig(cipherName))) { + try { + Class c = + Class.forName(JSch.getConfig(cipherName)).asSubclass(Cipher.class); + kpair.cipher = c.getDeclaredConstructor().newInstance(); + kpair.iv = new byte[kpair.cipher.getIVSize()]; + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException("cipher " + cipherName + " is not available", e); + } + } else { + throw new JSchException("cipher " + cipherName + " is not available"); + } + + try { + Buffer kdfOpts = new Buffer(kdfOptions); + byte[] salt = kdfOpts.getString(); + int rounds = kdfOpts.getInt(); + Class c = + Class.forName(JSch.getConfig(kdfName)).asSubclass(BCrypt.class); + BCrypt bcrypt = c.getDeclaredConstructor().newInstance(); + bcrypt.init(salt, rounds); + kpair.kdf = bcrypt; + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException("kdf " + kdfName + " is not available", e); + } + } + + return kpair; + } catch (Exception e) { + Util.bzero(kpair.data); + throw e; + } + } + + private static boolean isOpenSSHPrivateKey(byte[] buf, int i, int len) { + String ident = "OPENSSH PRIVATE KEY-----"; + return i + ident.length() < len + && ident.equals(Util.byte2str(Arrays.copyOfRange(buf, i, i + ident.length()))); + } + + private static byte a2b(byte c) { + if ('0' <= c && c <= '9') + return (byte) (c - '0'); + return (byte) (c - 'a' + 10); + } + + private static byte b2a(byte c) { + if (0 <= c && c <= 9) + return (byte) (c + '0'); + return (byte) (c - 10 + 'A'); + } + + public void dispose() { + Util.bzero(passphrase); + } + + @SuppressWarnings("deprecation") + @Override + public void finalize() { + dispose(); + } + + static KeyPair loadPPK(JSch.InstanceLogger instLogger, byte[] buf) throws JSchException { + byte[] pubkey = null; + byte[] prvkey = null; + byte[] _prvkey = null; + int lines = 0; + + Buffer buffer = new Buffer(buf); + Map v = new HashMap<>(); + + while (true) { + if (!parseHeader(buffer, v)) + break; + } + + int ppkVersion; + String typ = v.get("PuTTY-User-Key-File-2"); + if (typ == null) { + typ = v.get("PuTTY-User-Key-File-3"); + if (typ == null) { + return null; + } else { + ppkVersion = VENDOR_PUTTY_V3; + } + } else { + ppkVersion = VENDOR_PUTTY; + } + + try { + lines = Integer.parseInt(v.get("Public-Lines")); + pubkey = parseLines(buffer, lines); + + while (true) { + if (!parseHeader(buffer, v)) + break; + } + + lines = Integer.parseInt(v.get("Private-Lines")); + _prvkey = parseLines(buffer, lines); + + while (true) { + if (!parseHeader(buffer, v)) + break; + } + + prvkey = Util.fromBase64(_prvkey, 0, _prvkey.length); + pubkey = Util.fromBase64(pubkey, 0, pubkey.length); + + KeyPair kpair = parsePubkeyBlob(instLogger, pubkey, typ); + kpair.encrypted = !v.get("Encryption").equals("none"); + kpair.publickeyblob = pubkey; + kpair.vendor = ppkVersion; + kpair.publicKeyComment = v.get("Comment"); + if (kpair.encrypted) { + if (Session.checkCipher(JSch.getConfig("aes256-cbc"))) { + try { + Class c = + Class.forName(JSch.getConfig("aes256-cbc")).asSubclass(Cipher.class); + kpair.cipher = c.getDeclaredConstructor().newInstance(); + kpair.iv = new byte[kpair.cipher.getIVSize()]; + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException("The cipher 'aes256-cbc' is required, but it is not available.", + e); + } + } else { + throw new JSchException("The cipher 'aes256-cbc' is required, but it is not available."); + } + + if (ppkVersion == VENDOR_PUTTY) { + try { + Class c = Class.forName(JSch.getConfig("sha-1")).asSubclass(HASH.class); + HASH sha1 = c.getDeclaredConstructor().newInstance(); + sha1.init(); + kpair.sha1 = sha1; + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException("'sha-1' is required, but it is not available.", e); + } + } else { + String argonTypeStr = v.get("Key-Derivation"); + String saltStr = v.get("Argon2-Salt"); + if (argonTypeStr == null || saltStr == null + || (saltStr != null && saltStr.length() % 2 != 0)) { + throw new JSchException("Invalid argon2 params."); + } + + int argonType; + switch (argonTypeStr) { + case "Argon2d": + argonType = Argon2.ARGON2D; + break; + case "Argon2i": + argonType = Argon2.ARGON2I; + break; + case "Argon2id": + argonType = Argon2.ARGON2ID; + break; + default: + throw new JSchException("Invalid argon2 params."); + } + + try { + int memory = Integer.parseInt(v.get("Argon2-Memory")); + int passes = Integer.parseInt(v.get("Argon2-Passes")); + int parallelism = Integer.parseInt(v.get("Argon2-Parallelism")); + + byte[] salt = new byte[saltStr.length() / 2]; + for (int i = 0; i < salt.length; i++) { + int j = i * 2; + salt[i] = (byte) Integer.parseInt(saltStr.substring(j, j + 2), 16); + } + + Class c = + Class.forName(JSch.getConfig("argon2")).asSubclass(Argon2.class); + Argon2 argon2 = c.getDeclaredConstructor().newInstance(); + argon2.init(salt, passes, argonType, new byte[0], new byte[0], memory, parallelism, + Argon2.V13); + kpair.kdf = argon2; + } catch (NumberFormatException e) { + throw new JSchException("Invalid argon2 params.", e); + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException("'argon2' is required, but it is not available.", e); + } + } + + kpair.data = prvkey; + } else { + kpair.data = prvkey; + if (!kpair.parse(prvkey)) { + throw new JSchException("invalid privatekey"); + } else { + Util.bzero(prvkey); + } + } + return kpair; + } catch (Exception e) { + Util.bzero(prvkey); + throw e; + } finally { + Util.bzero(_prvkey); + } + } + + private static KeyPair parsePubkeyBlob(JSch.InstanceLogger instLogger, byte[] pubkeyblob, + String typ) throws JSchException { + Buffer _buf = new Buffer(pubkeyblob); + _buf.skip(pubkeyblob.length); + + String pubkeyType = Util.byte2str(_buf.getString()); + if (typ == null || typ.equals("")) { + typ = pubkeyType; + } else if (!typ.equals(pubkeyType)) { + throw new JSchException( + "pubkeyblob type [" + pubkeyType + "] does not match expected type [" + typ + "]"); + } + + if (typ.equals("ssh-rsa")) { + byte[] pub_array = new byte[_buf.getInt()]; + _buf.getByte(pub_array); + byte[] n_array = new byte[_buf.getInt()]; + _buf.getByte(n_array); + + return new KeyPairRSA(instLogger, n_array, pub_array, null); + } else if (typ.equals("ssh-dss")) { + byte[] p_array = new byte[_buf.getInt()]; + _buf.getByte(p_array); + byte[] q_array = new byte[_buf.getInt()]; + _buf.getByte(q_array); + byte[] g_array = new byte[_buf.getInt()]; + _buf.getByte(g_array); + byte[] y_array = new byte[_buf.getInt()]; + _buf.getByte(y_array); + + return new KeyPairDSA(instLogger, p_array, q_array, g_array, y_array, null); + } else if (typ.equals("ecdsa-sha2-nistp256") || typ.equals("ecdsa-sha2-nistp384") + || typ.equals("ecdsa-sha2-nistp521")) { + byte[] name = _buf.getString(); // nistpXXX + + int len = _buf.getInt(); + int x04 = _buf.getByte(); // in case of x04 it is uncompressed + // https://tools.ietf.org/html/rfc5480#page-7 + byte[] r_array = new byte[(len - 1) / 2]; + byte[] s_array = new byte[(len - 1) / 2]; + _buf.getByte(r_array); + _buf.getByte(s_array); + + return new KeyPairECDSA(instLogger, name, r_array, s_array, null); + } else if (typ.equals("ssh-ed25519") || typ.equals("ssh-ed448")) { + byte[] pub_array = new byte[_buf.getInt()]; + _buf.getByte(pub_array); + + if (typ.equals("ssh-ed25519")) { + return new KeyPairEd25519(instLogger, pub_array, null); + } else { + return new KeyPairEd448(instLogger, pub_array, null); + } + } else { + throw new JSchException("key type " + typ + " is not supported"); + } + } + + private static byte[] parseLines(Buffer buffer, int lines) { + byte[] buf = buffer.buffer; + int index = buffer.index; + byte[] data = null; + + int i = index; + while (lines-- > 0) { + while (buf.length > i) { + byte c = buf[i++]; + if (c == '\r' || c == '\n') { + int len = i - index - 1; + if (data == null) { + data = new byte[len]; + System.arraycopy(buf, index, data, 0, len); + } else if (len > 0) { + byte[] tmp = new byte[data.length + len]; + System.arraycopy(data, 0, tmp, 0, data.length); + System.arraycopy(buf, index, tmp, data.length, len); + Util.bzero(data); // clear + data = tmp; + } + break; + } + } + if (i < buf.length && buf[i] == '\n') + i++; + index = i; + } + + if (data != null) + buffer.index = index; + + return data; + } + + private static boolean parseHeader(Buffer buffer, Map v) { + byte[] buf = buffer.buffer; + int index = buffer.index; + String key = null; + String value = null; + for (int i = index; i < buf.length; i++) { + if (buf[i] == '\r' || buf[i] == '\n') { + if (i + 1 < buf.length && buf[i + 1] == '\n') { + i++; + } + break; + } + if (buf[i] == ':') { + key = Util.byte2str(buf, index, i - index); + i++; + if (i < buf.length && buf[i] == ' ') { + i++; + } + index = i; + break; + } + } + + if (key == null) + return false; + + for (int i = index; i < buf.length; i++) { + if (buf[i] == '\r' || buf[i] == '\n') { + value = Util.byte2str(buf, index, i - index); + i++; + if (i < buf.length && buf[i] == '\n') { + i++; + } + index = i; + break; + } + } + + if (value != null) { + v.put(key, value); + buffer.index = index; + } + + return (key != null && value != null); + } + + void copy(KeyPair kpair) { + this.publickeyblob = kpair.publickeyblob; + this.vendor = kpair.vendor; + this.publicKeyComment = kpair.publicKeyComment; + this.cipher = kpair.cipher; + } + + static class ASN1Exception extends Exception { + private static final long serialVersionUID = -1L; + } + + static class ASN1 { + byte[] buf; + int start; + int length; + + ASN1(byte[] buf) throws ASN1Exception { + this(buf, 0, buf.length); + } + + ASN1(byte[] buf, int start, int length) throws ASN1Exception { + this.buf = buf; + this.start = start; + this.length = length; + if (start + length > buf.length) + throw new ASN1Exception(); + } + + int getType() { + return buf[start] & 0xff; + } + + boolean isSEQUENCE() { + return getType() == (0x30 & 0xff); + } + + boolean isINTEGER() { + return getType() == (0x02 & 0xff); + } + + boolean isOBJECT() { + return getType() == (0x06 & 0xff); + } + + boolean isOCTETSTRING() { + return getType() == (0x04 & 0xff); + } + + boolean isNULL() { + return getType() == (0x05 & 0xff); + } + + boolean isBITSTRING() { + return getType() == (0x03 & 0xff); + } + + boolean isCONTEXTPRIMITIVE(int tag) { + if ((tag & ~0xff) != 0 || (tag & 0x60) != 0) { + throw new IllegalArgumentException(); + } + return getType() == ((tag | 0x80) & 0xff); + } + + boolean isCONTEXTCONSTRUCTED(int tag) { + if ((tag & ~0xff) != 0 || (tag & 0x40) != 0) { + throw new IllegalArgumentException(); + } + return getType() == ((tag | 0xa0) & 0xff); + } + + private int getLength(int[] indexp) { + int index = indexp[0]; + int length = buf[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (buf[index++] & 0xff); + } + } + indexp[0] = index; + return length; + } + + byte[] getContent() { + int[] indexp = new int[1]; + indexp[0] = start + 1; + int length = getLength(indexp); + int index = indexp[0]; + byte[] tmp = new byte[length]; + System.arraycopy(buf, index, tmp, 0, tmp.length); + return tmp; + } + + ASN1[] getContents() throws ASN1Exception { + int typ = buf[start]; + int[] indexp = new int[1]; + indexp[0] = start + 1; + int length = getLength(indexp); + if (typ == 0x05) { + return new ASN1[0]; + } + int index = indexp[0]; + List values = new ArrayList<>(); + while (length > 0) { + index++; + length--; + int tmp = index; + indexp[0] = index; + int l = getLength(indexp); + index = indexp[0]; + length -= (index - tmp); + values.add(new ASN1(buf, tmp - 1, 1 + (index - tmp) + l)); + index += l; + length -= l; + } + ASN1[] result = new ASN1[values.size()]; + values.toArray(result); + return result; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairDSA.java new file mode 100644 index 0000000..d030525 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairDSA.java @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.math.BigInteger; + +class KeyPairDSA extends KeyPair { + private byte[] P_array; + private byte[] Q_array; + private byte[] G_array; + private byte[] pub_array; + private byte[] prv_array; + + // private int key_size=0; + private int key_size = 1024; + + KeyPairDSA(JSch.InstanceLogger instLogger) { + this(instLogger, null, null, null, null, null); + } + + KeyPairDSA(JSch.InstanceLogger instLogger, byte[] P_array, byte[] Q_array, byte[] G_array, + byte[] pub_array, byte[] prv_array) { + super(instLogger); + this.P_array = P_array; + this.Q_array = Q_array; + this.G_array = G_array; + this.pub_array = pub_array; + this.prv_array = prv_array; + if (P_array != null) + key_size = (new BigInteger(P_array)).bitLength(); + } + + @Override + void generate(int key_size) throws JSchException { + this.key_size = key_size; + try { + Class c = + Class.forName(JSch.getConfig("keypairgen.dsa")).asSubclass(KeyPairGenDSA.class); + KeyPairGenDSA keypairgen = c.getDeclaredConstructor().newInstance(); + keypairgen.init(key_size); + P_array = keypairgen.getP(); + Q_array = keypairgen.getQ(); + G_array = keypairgen.getG(); + pub_array = keypairgen.getY(); + prv_array = keypairgen.getX(); + + keypairgen = null; + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + } + + private static final byte[] begin = Util.str2byte("-----BEGIN DSA PRIVATE KEY-----"); + private static final byte[] end = Util.str2byte("-----END DSA PRIVATE KEY-----"); + + @Override + byte[] getBegin() { + return begin; + } + + @Override + byte[] getEnd() { + return end; + } + + @Override + byte[] getPrivateKey() { + int content = 1 + countLength(1) + 1 + // INTEGER + 1 + countLength(P_array.length) + P_array.length + // INTEGER P + 1 + countLength(Q_array.length) + Q_array.length + // INTEGER Q + 1 + countLength(G_array.length) + G_array.length + // INTEGER G + 1 + countLength(pub_array.length) + pub_array.length + // INTEGER pub + 1 + countLength(prv_array.length) + prv_array.length; // INTEGER prv + + int total = 1 + countLength(content) + content; // SEQUENCE + + byte[] plain = new byte[total]; + int index = 0; + index = writeSEQUENCE(plain, index, content); + index = writeINTEGER(plain, index, new byte[1]); // 0 + index = writeINTEGER(plain, index, P_array); + index = writeINTEGER(plain, index, Q_array); + index = writeINTEGER(plain, index, G_array); + index = writeINTEGER(plain, index, pub_array); + index = writeINTEGER(plain, index, prv_array); + return plain; + } + + @Override + boolean parse(byte[] plain) { + try { + + if (vendor == VENDOR_FSECURE) { + if (plain[0] != 0x30) { // FSecure + Buffer buf = new Buffer(plain); + buf.getInt(); + P_array = buf.getMPIntBits(); + G_array = buf.getMPIntBits(); + Q_array = buf.getMPIntBits(); + pub_array = buf.getMPIntBits(); + prv_array = buf.getMPIntBits(); + if (P_array != null) + key_size = (new BigInteger(P_array)).bitLength(); + return true; + } + return false; + } else if (vendor == VENDOR_PUTTY || vendor == VENDOR_PUTTY_V3) { + Buffer buf = new Buffer(plain); + buf.skip(plain.length); + + try { + byte[][] tmp = buf.getBytes(1, ""); + prv_array = tmp[0]; + } catch (JSchException e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + + return true; + } + + // OPENSSH Key v1 Format + else if (vendor == VENDOR_OPENSSH_V1) { + + final Buffer prvKEyBuffer = new Buffer(plain); + int checkInt1 = prvKEyBuffer.getInt(); // uint32 checkint1 + int checkInt2 = prvKEyBuffer.getInt(); // uint32 checkint2 + if (checkInt1 != checkInt2) { + throw new JSchException("check failed"); + } + // The private key section contains both the public key and the private key + String keyType = Util.byte2str(prvKEyBuffer.getString()); // string keytype + + P_array = prvKEyBuffer.getMPInt(); + Q_array = prvKEyBuffer.getMPInt(); + G_array = prvKEyBuffer.getMPInt(); + pub_array = prvKEyBuffer.getMPInt(); + prv_array = prvKEyBuffer.getMPInt(); + publicKeyComment = Util.byte2str(prvKEyBuffer.getString()); + // if(P_array!=null) key_size = (new BigInteger(P_array)).bitLength(); + return true; + } + + int index = 0; + int length = 0; + + if (plain[index] != 0x30) + return false; + index++; // SEQUENCE + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + + if (plain[index] != 0x02) + return false; + index++; // INTEGER + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + P_array = new byte[length]; + System.arraycopy(plain, index, P_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + Q_array = new byte[length]; + System.arraycopy(plain, index, Q_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + G_array = new byte[length]; + System.arraycopy(plain, index, G_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + pub_array = new byte[length]; + System.arraycopy(plain, index, pub_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + prv_array = new byte[length]; + System.arraycopy(plain, index, prv_array, 0, length); + index += length; + + if (P_array != null) + key_size = (new BigInteger(P_array)).bitLength(); + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + return true; + } + + @Override + public byte[] getPublicKeyBlob() { + byte[] foo = super.getPublicKeyBlob(); + if (foo != null) + return foo; + + if (P_array == null) + return null; + byte[][] tmp = new byte[5][]; + tmp[0] = sshdss; + tmp[1] = P_array; + tmp[2] = Q_array; + tmp[3] = G_array; + tmp[4] = pub_array; + return Buffer.fromBytes(tmp).buffer; + } + + private static final byte[] sshdss = Util.str2byte("ssh-dss"); + + @Override + byte[] getKeyTypeName() { + return sshdss; + } + + @Override + public int getKeyType() { + return DSA; + } + + @Override + public int getKeySize() { + return key_size; + } + + @Override + public byte[] getSignature(byte[] data) { + try { + Class c = + Class.forName(JSch.getConfig("signature.dss")).asSubclass(SignatureDSA.class); + SignatureDSA dsa = c.getDeclaredConstructor().newInstance(); + dsa.init(); + dsa.setPrvKey(prv_array, P_array, Q_array, G_array); + + dsa.update(data); + byte[] sig = dsa.sign(); + byte[][] tmp = new byte[2][]; + tmp[0] = sshdss; + tmp[1] = sig; + return Buffer.fromBytes(tmp).buffer; + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to generate signature", e); + } + } + return null; + } + + @Override + public byte[] getSignature(byte[] data, String alg) { + return getSignature(data); + } + + @Override + public Signature getVerifier() { + try { + Class c = + Class.forName(JSch.getConfig("signature.dss")).asSubclass(SignatureDSA.class); + SignatureDSA dsa = c.getDeclaredConstructor().newInstance(); + dsa.init(); + + if (pub_array == null && P_array == null && getPublicKeyBlob() != null) { + Buffer buf = new Buffer(getPublicKeyBlob()); + buf.getString(); + P_array = buf.getString(); + Q_array = buf.getString(); + G_array = buf.getString(); + pub_array = buf.getString(); + } + + dsa.setPubKey(pub_array, P_array, Q_array, G_array); + return dsa; + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create verifier", e); + } + } + return null; + } + + @Override + public Signature getVerifier(String alg) { + return getVerifier(); + } + + static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException { + + byte[][] tmp = buf.getBytes(7, "invalid key format"); + + byte[] P_array = tmp[1]; + byte[] Q_array = tmp[2]; + byte[] G_array = tmp[3]; + byte[] pub_array = tmp[4]; + byte[] prv_array = tmp[5]; + KeyPairDSA kpair = new KeyPairDSA(instLogger, P_array, Q_array, G_array, pub_array, prv_array); + kpair.publicKeyComment = Util.byte2str(tmp[6]); + kpair.vendor = VENDOR_OPENSSH; + return kpair; + } + + @Override + public byte[] forSSHAgent() throws JSchException { + if (isEncrypted()) { + throw new JSchException("key is encrypted."); + } + Buffer buf = new Buffer(); + buf.putString(sshdss); + buf.putString(P_array); + buf.putString(Q_array); + buf.putString(G_array); + buf.putString(pub_array); + buf.putString(prv_array); + buf.putString(Util.str2byte(publicKeyComment)); + byte[] result = new byte[buf.getLength()]; + buf.getByte(result, 0, result.length); + return result; + } + + @Override + public void dispose() { + super.dispose(); + Util.bzero(prv_array); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairECDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairECDSA.java new file mode 100644 index 0000000..c322135 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairECDSA.java @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Arrays; + +class KeyPairECDSA extends KeyPair { + + private static byte[][] oids = {{(byte) 0x06, (byte) 0x08, (byte) 0x2a, (byte) 0x86, (byte) 0x48, // 256 + (byte) 0xce, (byte) 0x3d, (byte) 0x03, (byte) 0x01, (byte) 0x07}, + {(byte) 0x06, (byte) 0x05, (byte) 0x2b, (byte) 0x81, (byte) 0x04, // 384 + (byte) 0x00, (byte) 0x22}, + {(byte) 0x06, (byte) 0x05, (byte) 0x2b, (byte) 0x81, (byte) 0x04, // 521 + (byte) 0x00, (byte) 0x23},}; + + private static String[] names = {"nistp256", "nistp384", "nistp521"}; + + private byte[] name = Util.str2byte(names[0]); + private byte[] r_array; + private byte[] s_array; + private byte[] prv_array; + + private int key_size = 256; + + KeyPairECDSA(JSch.InstanceLogger instLogger) { + this(instLogger, null, null, null, null); + } + + KeyPairECDSA(JSch.InstanceLogger instLogger, byte[] pubkey) { + this(instLogger, null, null, null, null); + + if (pubkey != null) { + byte[] name = new byte[8]; + System.arraycopy(pubkey, 11, name, 0, 8); + if (Util.array_equals(name, Util.str2byte("nistp384"))) { + key_size = 384; + this.name = name; + } + if (Util.array_equals(name, Util.str2byte("nistp521"))) { + key_size = 521; + this.name = name; + } + } + } + + KeyPairECDSA(JSch.InstanceLogger instLogger, byte[] name, byte[] r_array, byte[] s_array, + byte[] prv_array) { + super(instLogger); + if (name != null) + this.name = name; + this.r_array = r_array; + this.s_array = s_array; + this.prv_array = prv_array; + if (prv_array != null) + key_size = prv_array.length >= 64 ? 521 : (prv_array.length >= 48 ? 384 : 256); + } + + @Override + void generate(int key_size) throws JSchException { + this.key_size = key_size; + try { + Class c = + Class.forName(JSch.getConfig("keypairgen.ecdsa")).asSubclass(KeyPairGenECDSA.class); + KeyPairGenECDSA keypairgen = c.getDeclaredConstructor().newInstance(); + keypairgen.init(key_size); + prv_array = keypairgen.getD(); + r_array = keypairgen.getR(); + s_array = keypairgen.getS(); + name = Util.str2byte(names[prv_array.length >= 64 ? 2 : (prv_array.length >= 48 ? 1 : 0)]); + keypairgen = null; + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + } + + private static final byte[] begin = Util.str2byte("-----BEGIN EC PRIVATE KEY-----"); + private static final byte[] end = Util.str2byte("-----END EC PRIVATE KEY-----"); + + @Override + byte[] getBegin() { + return begin; + } + + @Override + byte[] getEnd() { + return end; + } + + @Override + byte[] getPrivateKey() { + + byte[] tmp = new byte[1]; + tmp[0] = 1; + + byte[] oid = oids[(r_array.length >= 64) ? 2 : ((r_array.length >= 48) ? 1 : 0)]; + + byte[] point = toPoint(r_array, s_array); + + int bar = ((point.length + 1) & 0x80) == 0 ? 3 : 4; + byte[] foo = new byte[point.length + bar]; + System.arraycopy(point, 0, foo, bar, point.length); + foo[0] = 0x03; // BITSTRING + if (bar == 3) { + foo[1] = (byte) (point.length + 1); + } else { + foo[1] = (byte) 0x81; + foo[2] = (byte) (point.length + 1); + } + point = foo; + + int content = 1 + countLength(tmp.length) + tmp.length + 1 + countLength(prv_array.length) + + prv_array.length + 1 + countLength(oid.length) + oid.length + 1 + + countLength(point.length) + point.length; + + int total = 1 + countLength(content) + content; // SEQUENCE + + byte[] plain = new byte[total]; + int index = 0; + index = writeSEQUENCE(plain, index, content); + index = writeINTEGER(plain, index, tmp); + index = writeOCTETSTRING(plain, index, prv_array); + index = writeDATA(plain, (byte) 0xa0, index, oid); + index = writeDATA(plain, (byte) 0xa1, index, point); + + return plain; + } + + @Override + boolean parse(byte[] plain) { + try { + + if (vendor == VENDOR_FSECURE) { + /* + * if(plain[0]!=0x30){ // FSecure return true; } return false; + */ + return false; + } else if (vendor == VENDOR_PUTTY || vendor == VENDOR_PUTTY_V3) { + Buffer buf = new Buffer(plain); + buf.skip(plain.length); + + try { + byte[][] tmp = buf.getBytes(1, ""); + prv_array = tmp[0]; + key_size = prv_array.length >= 64 ? 521 : (prv_array.length >= 48 ? 384 : 256); + } catch (JSchException e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + + return true; + } + + // OPENSSH Key v1 Format + if (vendor == VENDOR_OPENSSH_V1) { + + final Buffer prvKeyBuffer = new Buffer(plain); + int checkInt1 = prvKeyBuffer.getInt(); // uint32 checkint1 + int checkInt2 = prvKeyBuffer.getInt(); // uint32 checkint2 + if (checkInt1 != checkInt2) { + throw new JSchException("check failed"); + } + + String keyType = Util.byte2str(prvKeyBuffer.getString()); // string keytype + + name = prvKeyBuffer.getString(); + if (!Arrays.asList(names).contains(Util.byte2str(name))) { + throw new IllegalArgumentException("unknown curve name " + Util.byte2str(name)); + } + + final int keyLen = prvKeyBuffer.getInt(); + final int x04 = prvKeyBuffer.getByte(); // in case of x04 it is uncompressed + // https://tools.ietf.org/html/rfc5480#page-7 + final byte[] x = new byte[(keyLen - 1) / 2]; + final byte[] y = new byte[(keyLen - 1) / 2]; + prvKeyBuffer.getByte(x); + prvKeyBuffer.getByte(y); + + prv_array = prvKeyBuffer.getString(); + publicKeyComment = Util.byte2str(prvKeyBuffer.getString()); + r_array = x; + s_array = y; + key_size = x.length >= 64 ? 521 : (x.length >= 48 ? 384 : 256); + + return true; + } + + int index = 0; + int length = 0; + + if (plain[index] != 0x30) + return false; + index++; // SEQUENCE + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + + if (plain[index] != 0x02) + return false; + index++; // INTEGER + + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + + index += length; + index++; // 0x04 + + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + + prv_array = new byte[length]; + System.arraycopy(plain, index, prv_array, 0, length); + + index += length; + + index++; // 0xa0 + + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + + byte[] oid_array = new byte[length]; + System.arraycopy(plain, index, oid_array, 0, length); + index += length; + + for (int i = 0; i < oids.length; i++) { + if (Util.array_equals(oids[i], oid_array)) { + name = Util.str2byte(names[i]); + break; + } + } + + index++; // 0xa1 + + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + + byte[] Q_array = new byte[length]; + System.arraycopy(plain, index, Q_array, 0, length); + index += length; + + byte[][] tmp = fromPoint(Q_array); + r_array = tmp[0]; + s_array = tmp[1]; + + if (prv_array != null) + key_size = prv_array.length >= 64 ? 521 : (prv_array.length >= 48 ? 384 : 256); + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + return true; + } + + @Override + public byte[] getPublicKeyBlob() { + byte[] foo = super.getPublicKeyBlob(); + + if (foo != null) + return foo; + + if (r_array == null) + return null; + + byte[][] tmp = new byte[3][]; + tmp[0] = Util.str2byte("ecdsa-sha2-" + Util.byte2str(name)); + tmp[1] = name; + tmp[2] = new byte[1 + r_array.length + s_array.length]; + tmp[2][0] = 4; // POINT_CONVERSION_UNCOMPRESSED + System.arraycopy(r_array, 0, tmp[2], 1, r_array.length); + System.arraycopy(s_array, 0, tmp[2], 1 + r_array.length, s_array.length); + + return Buffer.fromBytes(tmp).buffer; + } + + @Override + byte[] getKeyTypeName() { + return Util.str2byte("ecdsa-sha2-" + Util.byte2str(name)); + } + + @Override + public int getKeyType() { + return ECDSA; + } + + @Override + public int getKeySize() { + return key_size; + } + + @Override + public byte[] getSignature(byte[] data) { + try { + Class c = + Class.forName(JSch.getConfig("ecdsa-sha2-" + Util.byte2str(name))) + .asSubclass(SignatureECDSA.class); + SignatureECDSA ecdsa = c.getDeclaredConstructor().newInstance(); + ecdsa.init(); + ecdsa.setPrvKey(prv_array); + + ecdsa.update(data); + byte[] sig = ecdsa.sign(); + + byte[][] tmp = new byte[2][]; + tmp[0] = Util.str2byte("ecdsa-sha2-" + Util.byte2str(name)); + tmp[1] = sig; + return Buffer.fromBytes(tmp).buffer; + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to generate signature", e); + } + } + return null; + } + + @Override + public byte[] getSignature(byte[] data, String al) { + return getSignature(data); + } + + @Override + public Signature getVerifier() { + try { + Class c = + Class.forName(JSch.getConfig("ecdsa-sha2-" + Util.byte2str(name))) + .asSubclass(SignatureECDSA.class); + final SignatureECDSA ecdsa = c.getDeclaredConstructor().newInstance(); + ecdsa.init(); + + if (r_array == null && s_array == null && getPublicKeyBlob() != null) { + Buffer buf = new Buffer(getPublicKeyBlob()); + buf.getString(); // ecdsa-sha2-nistp256 + buf.getString(); // nistp256 + byte[][] tmp = fromPoint(buf.getString()); + r_array = tmp[0]; + s_array = tmp[1]; + } + ecdsa.setPubKey(r_array, s_array); + return ecdsa; + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create verifier", e); + } + } + return null; + } + + @Override + public Signature getVerifier(String alg) { + return getVerifier(); + } + + static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException { + + byte[][] tmp = buf.getBytes(5, "invalid key format"); + + byte[] name = tmp[1]; // nistp256 + byte[][] foo = fromPoint(tmp[2]); + byte[] r_array = foo[0]; + byte[] s_array = foo[1]; + + byte[] prv_array = tmp[3]; + KeyPairECDSA kpair = new KeyPairECDSA(instLogger, name, r_array, s_array, prv_array); + kpair.publicKeyComment = Util.byte2str(tmp[4]); + kpair.vendor = VENDOR_OPENSSH; + return kpair; + } + + @Override + public byte[] forSSHAgent() throws JSchException { + if (isEncrypted()) { + throw new JSchException("key is encrypted."); + } + Buffer buf = new Buffer(); + buf.putString(Util.str2byte("ecdsa-sha2-" + Util.byte2str(name))); + buf.putString(name); + buf.putString(toPoint(r_array, s_array)); + buf.putString(prv_array); + buf.putString(Util.str2byte(publicKeyComment)); + byte[] result = new byte[buf.getLength()]; + buf.getByte(result, 0, result.length); + return result; + } + + static byte[] toPoint(byte[] r_array, byte[] s_array) { + byte[] tmp = new byte[1 + r_array.length + s_array.length]; + tmp[0] = 0x04; + System.arraycopy(r_array, 0, tmp, 1, r_array.length); + System.arraycopy(s_array, 0, tmp, 1 + r_array.length, s_array.length); + return tmp; + } + + static byte[][] fromPoint(byte[] point) { + int i = 0; + while (point[i] != 4) + i++; + i++; + byte[][] tmp = new byte[2][]; + byte[] r_array = new byte[(point.length - i) / 2]; + byte[] s_array = new byte[(point.length - i) / 2]; + // point[0] == 0x04 == POINT_CONVERSION_UNCOMPRESSED + System.arraycopy(point, i, r_array, 0, r_array.length); + System.arraycopy(point, i + r_array.length, s_array, 0, s_array.length); + tmp[0] = r_array; + tmp[1] = s_array; + + return tmp; + } + + @Override + public void dispose() { + super.dispose(); + Util.bzero(prv_array); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairEd25519.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairEd25519.java new file mode 100644 index 0000000..eb7990a --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairEd25519.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Arrays; + +class KeyPairEd25519 extends KeyPairEdDSA { + + private static int keySize = 32; + + KeyPairEd25519(JSch.InstanceLogger instLogger) { + this(instLogger, null, null); + } + + KeyPairEd25519(JSch.InstanceLogger instLogger, byte[] pub_array, byte[] prv_array) { + super(instLogger, pub_array, prv_array); + } + + @Override + public int getKeyType() { + return ED25519; + } + + @Override + public int getKeySize() { + return keySize; + } + + @Override + String getSshName() { + return "ssh-ed25519"; + } + + @Override + String getJceName() { + return "Ed25519"; + } + + static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException { + + byte[][] tmp = buf.getBytes(4, "invalid key format"); + + byte[] pub_array = tmp[1]; + byte[] prv_array = Arrays.copyOf(tmp[2], keySize); + KeyPairEd25519 kpair = new KeyPairEd25519(instLogger, pub_array, prv_array); + kpair.publicKeyComment = Util.byte2str(tmp[3]); + kpair.vendor = VENDOR_OPENSSH; + return kpair; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairEd448.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairEd448.java new file mode 100644 index 0000000..e22093b --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairEd448.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Arrays; + +class KeyPairEd448 extends KeyPairEdDSA { + + private static int keySize = 57; + + KeyPairEd448(JSch.InstanceLogger instLogger) { + this(instLogger, null, null); + } + + KeyPairEd448(JSch.InstanceLogger instLogger, byte[] pub_array, byte[] prv_array) { + super(instLogger, pub_array, prv_array); + } + + @Override + public int getKeyType() { + return ED448; + } + + @Override + public int getKeySize() { + return keySize; + } + + @Override + String getSshName() { + return "ssh-ed448"; + } + + @Override + String getJceName() { + return "Ed448"; + } + + static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException { + + byte[][] tmp = buf.getBytes(4, "invalid key format"); + + byte[] pub_array = tmp[1]; + byte[] prv_array = Arrays.copyOf(tmp[2], keySize); + KeyPairEd448 kpair = new KeyPairEd448(instLogger, pub_array, prv_array); + kpair.publicKeyComment = Util.byte2str(tmp[3]); + kpair.vendor = VENDOR_OPENSSH; + return kpair; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java new file mode 100644 index 0000000..08ce3c2 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairEdDSA.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Arrays; + +abstract class KeyPairEdDSA extends KeyPair { + private byte[] pub_array; + private byte[] prv_array; + + KeyPairEdDSA(JSch.InstanceLogger instLogger, byte[] pub_array, byte[] prv_array) { + super(instLogger); + this.pub_array = pub_array; + this.prv_array = prv_array; + } + + abstract String getSshName(); + + abstract String getJceName(); + + @Override + void generate(int key_size) throws JSchException { + try { + Class c = + Class.forName(JSch.getConfig("keypairgen.eddsa")).asSubclass(KeyPairGenEdDSA.class); + KeyPairGenEdDSA keypairgen = c.getDeclaredConstructor().newInstance(); + keypairgen.init(getJceName(), getKeySize()); + pub_array = keypairgen.getPub(); + prv_array = keypairgen.getPrv(); + + keypairgen = null; + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException(e.toString(), e); + } + } + + // These methods appear to be for writing keys to a file. + // And since writing VENDOR_OPENSSH_V1 isn't supported yet, have these methods fail. + @Override + byte[] getBegin() { + throw new UnsupportedOperationException(); + } + + @Override + byte[] getEnd() { + throw new UnsupportedOperationException(); + } + + @Override + byte[] getPrivateKey() { + throw new UnsupportedOperationException(); + } + + @Override + boolean parse(byte[] plain) { + if (vendor == VENDOR_PUTTY || vendor == VENDOR_PUTTY_V3) { + Buffer buf = new Buffer(plain); + buf.skip(plain.length); + + try { + byte[][] tmp = buf.getBytes(1, ""); + prv_array = tmp[0]; + } catch (JSchException e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + + return true; + } else if (vendor == VENDOR_OPENSSH_V1) { + try { + // OPENSSH Key v1 Format + final Buffer buf = new Buffer(plain); + int checkInt1 = buf.getInt(); // uint32 checkint1 + int checkInt2 = buf.getInt(); // uint32 checkint2 + if (checkInt1 != checkInt2) { + throw new JSchException("check failed"); + } + String keyType = Util.byte2str(buf.getString()); // string keytype + pub_array = buf.getString(); // public key + // OpenSSH stores private key in first half of string and duplicate copy of public key in + // second half of string + byte[] tmp = buf.getString(); // secret key (private key + public key) + prv_array = Arrays.copyOf(tmp, getKeySize()); + publicKeyComment = Util.byte2str(buf.getString()); + return true; + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + } else if (vendor == VENDOR_PKCS8) { + try { + Class c = + Class.forName(JSch.getConfig("keypairgen_fromprivate.eddsa")) + .asSubclass(KeyPairGenEdDSA.class); + KeyPairGenEdDSA keypairgen = c.getDeclaredConstructor().newInstance(); + keypairgen.init(getJceName(), plain); + pub_array = keypairgen.getPub(); + prv_array = keypairgen.getPrv(); + return true; + } catch (Exception | NoClassDefFoundError e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + } else { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key"); + } + return false; + } + } + + @Override + public byte[] getPublicKeyBlob() { + byte[] foo = super.getPublicKeyBlob(); + if (foo != null) + return foo; + + if (pub_array == null) + return null; + byte[][] tmp = new byte[2][]; + tmp[0] = getKeyTypeName(); + tmp[1] = pub_array; + return Buffer.fromBytes(tmp).buffer; + } + + @Override + byte[] getKeyTypeName() { + return Util.str2byte(getSshName()); + } + + @Override + public byte[] getSignature(byte[] data) { + return getSignature(data, getSshName()); + } + + @Override + public byte[] getSignature(byte[] data, String alg) { + try { + Class c = + Class.forName(JSch.getConfig(alg)).asSubclass(SignatureEdDSA.class); + SignatureEdDSA eddsa = c.getDeclaredConstructor().newInstance(); + eddsa.init(); + eddsa.setPrvKey(prv_array); + + eddsa.update(data); + byte[] sig = eddsa.sign(); + byte[][] tmp = new byte[2][]; + tmp[0] = Util.str2byte(alg); + tmp[1] = sig; + return Buffer.fromBytes(tmp).buffer; + } catch (Exception | NoClassDefFoundError e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to generate signature", e); + } + } + return null; + } + + @Override + public Signature getVerifier() { + return getVerifier(getSshName()); + } + + @Override + public Signature getVerifier(String alg) { + try { + Class c = + Class.forName(JSch.getConfig(alg)).asSubclass(SignatureEdDSA.class); + SignatureEdDSA eddsa = c.getDeclaredConstructor().newInstance(); + eddsa.init(); + + if (pub_array == null && getPublicKeyBlob() != null) { + Buffer buf = new Buffer(getPublicKeyBlob()); + buf.getString(); + pub_array = buf.getString(); + } + + eddsa.setPubKey(pub_array); + return eddsa; + } catch (Exception | NoClassDefFoundError e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create verifier", e); + } + } + return null; + } + + @Override + public byte[] forSSHAgent() throws JSchException { + if (isEncrypted()) { + throw new JSchException("key is encrypted."); + } + Buffer buf = new Buffer(); + buf.putString(getKeyTypeName()); + buf.putString(pub_array); + byte[] tmp = new byte[prv_array.length + pub_array.length]; + System.arraycopy(prv_array, 0, tmp, 0, prv_array.length); + System.arraycopy(pub_array, 0, tmp, prv_array.length, pub_array.length); + buf.putString(tmp); + buf.putString(Util.str2byte(publicKeyComment)); + byte[] result = new byte[buf.getLength()]; + buf.getByte(result, 0, result.length); + return result; + } + + @Override + public void dispose() { + super.dispose(); + Util.bzero(prv_array); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenDSA.java new file mode 100644 index 0000000..e159403 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenDSA.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface KeyPairGenDSA { + void init(int key_size) throws Exception; + + byte[] getX(); + + byte[] getY(); + + byte[] getP(); + + byte[] getQ(); + + byte[] getG(); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenECDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenECDSA.java new file mode 100644 index 0000000..ee0a516 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenECDSA.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface KeyPairGenECDSA { + void init(int key_size) throws Exception; + + byte[] getD(); + + byte[] getR(); + + byte[] getS(); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenEdDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenEdDSA.java new file mode 100644 index 0000000..3c3a6c1 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenEdDSA.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface KeyPairGenEdDSA { + void init(String name, int keylen) throws Exception; + + byte[] getPub(); + + byte[] getPrv(); + + default void init(String name, byte[] prv) throws Exception { + throw new UnsupportedOperationException(); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenRSA.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenRSA.java new file mode 100644 index 0000000..e03fc17 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairGenRSA.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface KeyPairGenRSA { + void init(int key_size) throws Exception; + + byte[] getD(); + + byte[] getE(); + + byte[] getN(); + + byte[] getC(); + + byte[] getEP(); + + byte[] getEQ(); + + byte[] getP(); + + byte[] getQ(); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java new file mode 100644 index 0000000..131dd6d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairPKCS8.java @@ -0,0 +1,873 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +class KeyPairPKCS8 extends KeyPair { + private static final byte[] rsaEncryption = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01}; + + private static final byte[] dsaEncryption = + {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x38, (byte) 0x04, (byte) 0x01}; + + private static final byte[] ecPublicKey = + {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x02, (byte) 0x01}; + + private static final byte[] ed25519 = {(byte) 0x2b, (byte) 0x65, (byte) 0x70}; + + private static final byte[] ed448 = {(byte) 0x2b, (byte) 0x65, (byte) 0x71}; + + private static final byte[] secp256r1 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, + (byte) 0x3d, (byte) 0x03, (byte) 0x01, (byte) 0x07}; + + private static final byte[] secp384r1 = + {(byte) 0x2b, (byte) 0x81, (byte) 0x04, (byte) 0x00, (byte) 0x22}; + + private static final byte[] secp521r1 = + {(byte) 0x2b, (byte) 0x81, (byte) 0x04, (byte) 0x00, (byte) 0x23}; + + private static final byte[] pbes2 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0d}; + + private static final byte[] pbkdf2 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0c}; + + private static final byte[] scrypt = {(byte) 0x2b, (byte) 0x06, (byte) 0x01, (byte) 0x04, + (byte) 0x01, (byte) 0xda, (byte) 0x47, (byte) 0x04, (byte) 0x0b}; + + private static final byte[] hmacWithSha1 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x07}; + + private static final byte[] hmacWithSha224 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x08}; + + private static final byte[] hmacWithSha256 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x09}; + + private static final byte[] hmacWithSha384 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x0a}; + + private static final byte[] hmacWithSha512 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x0b}; + + private static final byte[] hmacWithSha512224 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, + (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x0c}; + + private static final byte[] hmacWithSha512256 = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, + (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x02, (byte) 0x0d}; + + private static final byte[] aes128cbc = {(byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01, + (byte) 0x65, (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x02}; + + private static final byte[] aes192cbc = {(byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01, + (byte) 0x65, (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x16}; + + private static final byte[] aes256cbc = {(byte) 0x60, (byte) 0x86, (byte) 0x48, (byte) 0x01, + (byte) 0x65, (byte) 0x03, (byte) 0x04, (byte) 0x01, (byte) 0x2a}; + + private static final byte[] descbc = + {(byte) 0x2b, (byte) 0x0e, (byte) 0x03, (byte) 0x02, (byte) 0x07}; + + private static final byte[] des3cbc = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x03, (byte) 0x07}; + + private static final byte[] rc2cbc = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x03, (byte) 0x02}; + + private static final byte[] rc5cbc = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, + (byte) 0xf7, (byte) 0x0d, (byte) 0x03, (byte) 0x09}; + + private static final byte[] pbeWithMD2AndDESCBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, + (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x01}; + + private static final byte[] pbeWithMD2AndRC2CBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, + (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x04}; + + private static final byte[] pbeWithMD5AndDESCBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, + (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x03}; + + private static final byte[] pbeWithMD5AndRC2CBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, + (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x06}; + + private static final byte[] pbeWithSHA1AndDESCBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, + (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0a}; + + private static final byte[] pbeWithSHA1AndRC2CBC = {(byte) 0x2a, (byte) 0x86, (byte) 0x48, + (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x05, (byte) 0x0b}; + + private KeyPair kpair = null; + + KeyPairPKCS8(JSch.InstanceLogger instLogger) { + super(instLogger); + } + + @Override + void generate(int key_size) throws JSchException {} + + private static final byte[] begin = Util.str2byte("-----BEGIN DSA PRIVATE KEY-----"); + private static final byte[] end = Util.str2byte("-----END DSA PRIVATE KEY-----"); + + @Override + byte[] getBegin() { + return begin; + } + + @Override + byte[] getEnd() { + return end; + } + + @Override + byte[] getPrivateKey() { + return null; + } + + @Override + boolean parse(byte[] plain) { + + /* + * from RFC5208 PrivateKeyInfo ::= SEQUENCE { version Version, privateKeyAlgorithm + * PrivateKeyAlgorithmIdentifier, privateKey PrivateKey, attributes [0] IMPLICIT Attributes + * OPTIONAL } Version ::= INTEGER PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier + * PrivateKey ::= OCTET STRING Attributes ::= SET OF Attribute } + */ + + byte[] _data = null; + byte[] prv_array = null; + byte[] _plain = null; + KeyPair _key = null; + try { + ASN1[] contents; + + ASN1 asn1 = new ASN1(plain); + if (!asn1.isSEQUENCE()) { + throw new ASN1Exception(); + } + + contents = asn1.getContents(); + if (contents.length < 3 || contents.length > 4) { + throw new ASN1Exception(); + } + if (!contents[0].isINTEGER()) { + throw new ASN1Exception(); + } + if (!contents[1].isSEQUENCE()) { + throw new ASN1Exception(); + } + if (!contents[2].isOCTETSTRING()) { + throw new ASN1Exception(); + } + // attributes [0] IMPLICIT Attributes OPTIONAL + if (contents.length > 3 && !contents[3].isCONTEXTCONSTRUCTED(0)) { + throw new ASN1Exception(); + } + + int version = parseASN1IntegerAsInt(contents[0].getContent()); + if (version != 0) { + throw new ASN1Exception(); + } + + ASN1 privateKeyAlgorithm = contents[1]; + ASN1 privateKey = contents[2]; + + contents = privateKeyAlgorithm.getContents(); + if (contents.length == 0) { + throw new ASN1Exception(); + } + if (!contents[0].isOBJECT()) { + throw new ASN1Exception(); + } + byte[] privateKeyAlgorithmID = contents[0].getContent(); + + _data = privateKey.getContent(); + + KeyPair _kpair = null; + if (Util.array_equals(privateKeyAlgorithmID, rsaEncryption)) { + if (contents.length != 2) { + throw new ASN1Exception(); + } + if (!contents[1].isNULL()) { + throw new ASN1Exception(); + } + + _kpair = new KeyPairRSA(instLogger); + _kpair.copy(this); + if (_kpair.parse(_data)) { + kpair = _kpair; + return true; + } else { + throw new JSchException("failed to parse RSA"); + } + } else if (Util.array_equals(privateKeyAlgorithmID, dsaEncryption)) { + List values = new ArrayList<>(3); + + if (contents.length > 1 && contents[1].isSEQUENCE()) { + contents = contents[1].getContents(); + if (contents.length != 3) { + throw new ASN1Exception(); + } + if (!contents[0].isINTEGER()) { + throw new ASN1Exception(); + } + if (!contents[1].isINTEGER()) { + throw new ASN1Exception(); + } + if (!contents[2].isINTEGER()) { + throw new ASN1Exception(); + } + + values.add(contents[0].getContent()); + values.add(contents[1].getContent()); + values.add(contents[2].getContent()); + } + + asn1 = new ASN1(_data); + if (values.size() == 0) { // embedded DSA parameters format + /* + * SEQUENCE SEQUENCE INTEGER // P_array INTEGER // Q_array INTEGER // G_array INTEGER // + * prv_array + */ + if (!asn1.isSEQUENCE()) { + throw new ASN1Exception(); + } + + contents = asn1.getContents(); + if (contents.length != 2) { + throw new ASN1Exception(); + } + if (!contents[0].isSEQUENCE()) { + throw new ASN1Exception(); + } + if (!contents[1].isINTEGER()) { + throw new ASN1Exception(); + } + + prv_array = contents[1].getContent(); + + contents = contents[0].getContents(); + if (contents.length != 3) { + throw new ASN1Exception(); + } + if (!contents[0].isINTEGER()) { + throw new ASN1Exception(); + } + if (!contents[1].isINTEGER()) { + throw new ASN1Exception(); + } + if (!contents[2].isINTEGER()) { + throw new ASN1Exception(); + } + + values.add(contents[0].getContent()); + values.add(contents[1].getContent()); + values.add(contents[2].getContent()); + } else { + /* + * INTEGER // prv_array + */ + if (!asn1.isINTEGER()) { + throw new ASN1Exception(); + } + prv_array = asn1.getContent(); + } + + byte[] P_array = values.get(0); + byte[] Q_array = values.get(1); + byte[] G_array = values.get(2); + // Y = g^X mode p + byte[] pub_array = (new BigInteger(G_array)) + .modPow(new BigInteger(prv_array), new BigInteger(P_array)).toByteArray(); + + _key = new KeyPairDSA(instLogger, P_array, Q_array, G_array, pub_array, prv_array); + _plain = _key.getPrivateKey(); + + _kpair = new KeyPairDSA(instLogger); + _kpair.copy(this); + if (_kpair.parse(_plain)) { + kpair = _kpair; + return true; + } else { + throw new JSchException("failed to parse DSA"); + } + } else if (Util.array_equals(privateKeyAlgorithmID, ecPublicKey)) { + if (contents.length != 2) { + throw new ASN1Exception(); + } + if (!contents[1].isOBJECT()) { + throw new ASN1Exception(); + } + + byte[] namedCurve = contents[1].getContent(); + byte[] name; + if (!Util.array_equals(namedCurve, secp256r1)) { + name = Util.str2byte("nistp256"); + } else if (!Util.array_equals(namedCurve, secp384r1)) { + name = Util.str2byte("nistp384"); + } else if (!Util.array_equals(namedCurve, secp521r1)) { + name = Util.str2byte("nistp521"); + } else { + throw new JSchException("unsupported named curve oid: " + Util.toHex(namedCurve)); + } + + ASN1 ecPrivateKey = new ASN1(_data); + if (!ecPrivateKey.isSEQUENCE()) { + throw new ASN1Exception(); + } + + // ECPrivateKey ::= SEQUENCE { + // version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + // privateKey OCTET STRING, + // parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + // publicKey [1] BIT STRING OPTIONAL + // } + contents = ecPrivateKey.getContents(); + if (contents.length < 3 || contents.length > 4) { + throw new ASN1Exception(); + } + if (!contents[0].isINTEGER()) { + throw new ASN1Exception(); + } + if (!contents[1].isOCTETSTRING()) { + throw new ASN1Exception(); + } + + version = parseASN1IntegerAsInt(contents[0].getContent()); + if (version != 1) { + throw new ASN1Exception(); + } + prv_array = contents[1].getContent(); + + // publicKey is required here since there is no other way to derive it. + ASN1 publicKey; + if (contents.length == 3) { + publicKey = contents[2]; + } else { + publicKey = contents[3]; + + // parameters [0] ECParameters {{ NamedCurve }} OPTIONAL + if (!contents[2].isCONTEXTCONSTRUCTED(0)) { + throw new ASN1Exception(); + } + + // NamedCurve isn't required here since it is already known. + // But if it is included, they should be the same... + ASN1[] goo = contents[2].getContents(); + if (goo.length != 1) { + throw new ASN1Exception(); + } + if (!goo[0].isOBJECT()) { + throw new ASN1Exception(); + } + if (!Util.array_equals(goo[0].getContent(), namedCurve)) { + throw new ASN1Exception(); + } + } + + // publicKey [1] BIT STRING OPTIONAL + if (!publicKey.isCONTEXTCONSTRUCTED(1)) { + throw new ASN1Exception(); + } + contents = publicKey.getContents(); + if (contents.length != 1) { + throw new ASN1Exception(); + } + if (!contents[0].isBITSTRING()) { + throw new ASN1Exception(); + } + + byte[] Q_array = contents[0].getContent(); + byte[][] tmp = KeyPairECDSA.fromPoint(Q_array); + byte[] r_array = tmp[0]; + byte[] s_array = tmp[1]; + + _key = new KeyPairECDSA(instLogger, name, r_array, s_array, prv_array); + _plain = _key.getPrivateKey(); + + _kpair = new KeyPairECDSA(instLogger); + _kpair.copy(this); + if (_kpair.parse(_plain)) { + kpair = _kpair; + return true; + } else { + throw new JSchException("failed to parse ECDSA"); + } + } else if (Util.array_equals(privateKeyAlgorithmID, ed25519) + || Util.array_equals(privateKeyAlgorithmID, ed448)) { + if (contents.length != 1) { + throw new ASN1Exception(); + } + ASN1 curvePrivateKey = new ASN1(_data); + if (!curvePrivateKey.isOCTETSTRING()) { + throw new ASN1Exception(); + } + + prv_array = curvePrivateKey.getContent(); + if (Util.array_equals(privateKeyAlgorithmID, ed25519)) { + _kpair = new KeyPairEd25519(instLogger); + } else { + _kpair = new KeyPairEd448(instLogger); + } + _kpair.copy(this); + if (_kpair.parse(prv_array)) { + kpair = _kpair; + return true; + } else { + throw new JSchException("failed to parse EdDSA"); + } + } else { + throw new JSchException( + "unsupported privateKeyAlgorithm oid: " + Util.toHex(privateKeyAlgorithmID)); + } + } catch (ASN1Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to parse key: ASN1 parsing error", + e); + } + return false; + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to parse key: " + e.getMessage(), + e); + } + return false; + } finally { + Util.bzero(_data); + Util.bzero(prv_array); + Util.bzero(_plain); + if (_key != null) { + _key.dispose(); + } + } + } + + @Override + public byte[] getPublicKeyBlob() { + if (kpair != null) { + return kpair.getPublicKeyBlob(); + } else { + return super.getPublicKeyBlob(); + } + } + + @Override + byte[] getKeyTypeName() { + if (kpair != null) { + return kpair.getKeyTypeName(); + } else { + return new byte[0]; + } + } + + @Override + public int getKeyType() { + if (kpair != null) { + return kpair.getKeyType(); + } else { + return UNKNOWN; + } + } + + @Override + public int getKeySize() { + return kpair.getKeySize(); + } + + @Override + public byte[] getSignature(byte[] data) { + return kpair.getSignature(data); + } + + @Override + public byte[] getSignature(byte[] data, String alg) { + return kpair.getSignature(data, alg); + } + + @Override + public Signature getVerifier() { + return kpair.getVerifier(); + } + + @Override + public Signature getVerifier(String alg) { + return kpair.getVerifier(alg); + } + + @Override + public byte[] forSSHAgent() throws JSchException { + return kpair.forSSHAgent(); + } + + @Override + public boolean decrypt(byte[] _passphrase) { + if (!isEncrypted()) { + return true; + } + if (_passphrase == null) { + return !isEncrypted(); + } + + /* + * SEQUENCE SEQUENCE OBJECT :PBES2 SEQUENCE SEQUENCE OBJECT :PBKDF2 SEQUENCE OCTET STRING [HEX + * DUMP]:E4E24ADC9C00BD4D INTEGER :0800 SEQUENCE OBJECT :aes-128-cbc OCTET STRING [HEX + * DUMP]:5B66E6B3BF03944C92317BC370CC3AD0 OCTET STRING [HEX DUMP]: + * + * or + * + * SEQUENCE SEQUENCE OBJECT :PBES2 SEQUENCE SEQUENCE OBJECT :PBKDF2 SEQUENCE OCTET STRING [HEX + * DUMP]:E4E24ADC9C00BD4D INTEGER :0800 SEQUENCE OBJECT :hmacWithSHA256 NULL SEQUENCE OBJECT + * :aes-128-cbc OCTET STRING [HEX DUMP]:5B66E6B3BF03944C92317BC370CC3AD0 OCTET STRING [HEX + * DUMP]: + * + * or + * + * SEQUENCE SEQUENCE OBJECT :pbeWithMD5AndDES-CBC SEQUENCE OCTET STRING [HEX + * DUMP]:DBF75ECB69E3C0FC INTEGER :0800 OCTET STRING [HEX DUMP] + */ + + byte[] _data = null; + byte[] key = null; + byte[] plain = null; + try { + ASN1[] contents; + + ASN1 asn1 = new ASN1(data); + if (!asn1.isSEQUENCE()) { + throw new ASN1Exception(); + } + + contents = asn1.getContents(); + if (contents.length != 2) { + throw new ASN1Exception(); + } + if (!contents[0].isSEQUENCE()) { + throw new ASN1Exception(); + } + if (!contents[1].isOCTETSTRING()) { + throw new ASN1Exception(); + } + + _data = contents[1].getContent(); + ASN1 pbes = contents[0]; + + contents = pbes.getContents(); + if (contents.length != 2) { + throw new ASN1Exception(); + } + if (!contents[0].isOBJECT()) { + throw new ASN1Exception(); + } + if (!contents[1].isSEQUENCE()) { + throw new ASN1Exception(); + } + + byte[] pbesid = contents[0].getContent(); + ASN1 pbesparam = contents[1]; + + String kdfname; + KDF kdfinst; + byte[] encryptfuncid; + ASN1 encryptparams; + + if (Util.array_equals(pbesid, pbes2)) { + contents = pbesparam.getContents(); + if (contents.length != 2) { + throw new ASN1Exception(); + } + + ASN1 kdf = contents[0]; + ASN1 encryptfunc = contents[1]; + + if (!kdf.isSEQUENCE()) { + throw new ASN1Exception(); + } + if (!encryptfunc.isSEQUENCE()) { + throw new ASN1Exception(); + } + + contents = encryptfunc.getContents(); + + if (contents.length != 2) { + throw new ASN1Exception(); + } + if (!contents[0].isOBJECT()) { + throw new ASN1Exception(); + } + + encryptfuncid = contents[0].getContent(); + encryptparams = contents[1]; + + contents = kdf.getContents(); + if (contents.length != 2) { + throw new ASN1Exception(); + } + if (!contents[0].isOBJECT()) { + throw new ASN1Exception(); + } + if (!contents[1].isSEQUENCE()) { + throw new ASN1Exception(); + } + + byte[] kdfid = contents[0].getContent(); + + if (Util.array_equals(kdfid, pbkdf2)) { + ASN1 pbkdf2func = contents[1]; + if (!pbkdf2func.isSEQUENCE()) { + throw new ASN1Exception(); + } + + ASN1 prf = null; + contents = pbkdf2func.getContents(); + if (contents.length < 2 || contents.length > 4) { + throw new ASN1Exception(); + } + if (!contents[0].isOCTETSTRING()) { + throw new ASN1Exception(); + } + if (!contents[1].isINTEGER()) { + throw new ASN1Exception(); + } + + if (contents.length == 4) { + if (!contents[2].isINTEGER()) { + throw new ASN1Exception(); + } + if (!contents[3].isSEQUENCE()) { + throw new ASN1Exception(); + } + prf = contents[3]; + } else if (contents.length == 3) { + if (contents[2].isSEQUENCE()) { + prf = contents[2]; + } else if (!contents[2].isINTEGER()) { + throw new ASN1Exception(); + } + } + + byte[] prfid = null; + byte[] salt = contents[0].getContent(); + int iterations = parseASN1IntegerAsInt(contents[1].getContent()); + + if (prf != null) { + contents = prf.getContents(); + if (contents.length != 2) { + throw new ASN1Exception(); + } + if (!contents[0].isOBJECT()) { + throw new ASN1Exception(); + } + if (!contents[1].isNULL()) { + throw new ASN1Exception(); + } + + prfid = contents[0].getContent(); + } + + kdfname = getPBKDF2Name(prfid); + PBKDF2 pbkdf2kdf = getPBKDF2(kdfname); + pbkdf2kdf.init(salt, iterations); + kdfinst = pbkdf2kdf; + } else if (Util.array_equals(kdfid, scrypt)) { + contents = contents[1].getContents(); + if (contents.length < 4 || contents.length > 5) { + throw new ASN1Exception(); + } + if (!contents[0].isOCTETSTRING()) { + throw new ASN1Exception(); + } + if (!contents[1].isINTEGER()) { + throw new ASN1Exception(); + } + if (!contents[2].isINTEGER()) { + throw new ASN1Exception(); + } + if (!contents[3].isINTEGER()) { + throw new ASN1Exception(); + } + if (contents.length > 4 && !contents[4].isINTEGER()) { + throw new ASN1Exception(); + } + + byte[] salt = contents[0].getContent(); + int cost = parseASN1IntegerAsInt(contents[1].getContent()); + int blocksize = parseASN1IntegerAsInt(contents[2].getContent()); + int parallel = parseASN1IntegerAsInt(contents[3].getContent()); + + kdfname = "scrypt"; + SCrypt scryptkdf = getSCrypt(); + scryptkdf.init(salt, cost, blocksize, parallel); + kdfinst = scryptkdf; + } else { + throw new JSchException("unsupported kdf oid: " + Util.toHex(kdfid)); + } + } else { + String message; + if (Util.array_equals(pbesid, pbeWithMD2AndDESCBC)) { + message = "pbeWithMD2AndDES-CBC unsupported"; + } else if (Util.array_equals(pbesid, pbeWithMD2AndRC2CBC)) { + message = "pbeWithMD2AndRC2-CBC unsupported"; + } else if (Util.array_equals(pbesid, pbeWithMD5AndDESCBC)) { + message = "pbeWithMD5AndDES-CBC unsupported"; + } else if (Util.array_equals(pbesid, pbeWithMD5AndRC2CBC)) { + message = "pbeWithMD5AndRC2-CBC unsupported"; + } else if (Util.array_equals(pbesid, pbeWithSHA1AndDESCBC)) { + message = "pbeWithSHA1AndDES-CBC unsupported"; + } else if (Util.array_equals(pbesid, pbeWithSHA1AndRC2CBC)) { + message = "pbeWithSHA1AndRC2-CBC unsupported"; + } else { + message = "unsupported encryption oid: " + Util.toHex(pbesid); + } + throw new JSchException(message); + } + + byte[][] ivp = new byte[1][]; + Cipher cipher = getCipher(encryptfuncid, encryptparams, ivp); + byte[] iv = ivp[0]; + + key = kdfinst.getKey(_passphrase, cipher.getBlockSize()); + if (key == null) { + throw new JSchException("failed to generate key from KDF " + kdfname); + } + cipher.init(Cipher.DECRYPT_MODE, key, iv); + plain = new byte[_data.length]; + cipher.update(_data, 0, _data.length, plain, 0); + if (parse(plain)) { + encrypted = false; + Util.bzero(data); + return true; + } else { + throw new JSchException("failed to parse decrypted key"); + } + } catch (ASN1Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to decrypt key: ASN1 parsing error", + e); + } + return false; + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "PKCS8: failed to decrypt key: " + e.getMessage(), + e); + } + return false; + } finally { + Util.bzero(_data); + Util.bzero(key); + Util.bzero(plain); + } + } + + static String getPBKDF2Name(byte[] id) throws JSchException { + String name = null; + if (id == null || Util.array_equals(id, hmacWithSha1)) { + name = "pbkdf2-hmac-sha1"; + } else if (Util.array_equals(id, hmacWithSha224)) { + name = "pbkdf2-hmac-sha224"; + } else if (Util.array_equals(id, hmacWithSha256)) { + name = "pbkdf2-hmac-sha256"; + } else if (Util.array_equals(id, hmacWithSha384)) { + name = "pbkdf2-hmac-sha384"; + } else if (Util.array_equals(id, hmacWithSha512)) { + name = "pbkdf2-hmac-sha512"; + } else if (Util.array_equals(id, hmacWithSha512224)) { + name = "pbkdf2-hmac-sha512-224"; + } else if (Util.array_equals(id, hmacWithSha512256)) { + name = "pbkdf2-hmac-sha512-256"; + } + + if (name == null) { + throw new JSchException("unsupported pbkdf2 function oid: " + Util.toHex(id)); + } + return name; + } + + static PBKDF2 getPBKDF2(String name) throws JSchException { + try { + Class c = Class.forName(JSch.getConfig(name)).asSubclass(PBKDF2.class); + return c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new JSchException(name + " is not supported", e); + } + } + + static SCrypt getSCrypt() throws JSchException { + try { + Class c = Class.forName(JSch.getConfig("scrypt")).asSubclass(SCrypt.class); + return c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new JSchException("scrypt is not supported", e); + } + } + + static Cipher getCipher(byte[] id, ASN1 encryptparams, byte[][] ivp) throws Exception { + String name = null; + if (Util.array_equals(id, aes128cbc)) { + name = "aes128-cbc"; + } else if (Util.array_equals(id, aes192cbc)) { + name = "aes192-cbc"; + } else if (Util.array_equals(id, aes256cbc)) { + name = "aes256-cbc"; + } else if (Util.array_equals(id, descbc)) { + throw new JSchException("unsupported cipher function: des-cbc"); + } else if (Util.array_equals(id, des3cbc)) { + throw new JSchException("unsupported cipher function: 3des-cbc"); + } else if (Util.array_equals(id, rc2cbc)) { + throw new JSchException("unsupported cipher function: rc2-cbc"); + } else if (Util.array_equals(id, rc5cbc)) { + throw new JSchException("unsupported cipher function: rc5-cbc"); + } + + if (name == null) { + throw new JSchException("unsupported cipher function oid: " + Util.toHex(id)); + } + + if (!encryptparams.isOCTETSTRING()) { + throw new ASN1Exception(); + } + ivp[0] = encryptparams.getContent(); + + try { + Class c = Class.forName(JSch.getConfig(name)).asSubclass(Cipher.class); + return c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new JSchException(name + " is not supported", e); + } + } + + static int parseASN1IntegerAsInt(byte[] content) { + BigInteger b = new BigInteger(content); + // https://github.com/mwiede/jsch/issues/392 not using intValueExact() because of Android + // incompatibility. + if (b.bitLength() <= 31) { + return b.intValue(); + } else { + throw new ArithmeticException("BigInteger out of int range"); + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KeyPairRSA.java b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairRSA.java new file mode 100644 index 0000000..af2c57f --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KeyPairRSA.java @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.math.BigInteger; + +class KeyPairRSA extends KeyPair { + private byte[] n_array; // modulus p multiply q + private byte[] pub_array; // e + private byte[] prv_array; // d e^-1 mod (p-1)(q-1) + + private byte[] p_array; // prime p + private byte[] q_array; // prime q + private byte[] ep_array; // prime exponent p dmp1 == prv mod (p-1) + private byte[] eq_array; // prime exponent q dmq1 == prv mod (q-1) + private byte[] c_array; // coefficient iqmp == modinv(q, p) == q^-1 mod p + + private int key_size = 1024; + + KeyPairRSA(JSch.InstanceLogger instLogger) { + this(instLogger, null, null, null); + } + + KeyPairRSA(JSch.InstanceLogger instLogger, byte[] n_array, byte[] pub_array, byte[] prv_array) { + super(instLogger); + this.n_array = n_array; + this.pub_array = pub_array; + this.prv_array = prv_array; + if (n_array != null) { + key_size = (new BigInteger(n_array)).bitLength(); + } + } + + @Override + void generate(int key_size) throws JSchException { + this.key_size = key_size; + try { + Class c = + Class.forName(JSch.getConfig("keypairgen.rsa")).asSubclass(KeyPairGenRSA.class); + KeyPairGenRSA keypairgen = c.getDeclaredConstructor().newInstance(); + keypairgen.init(key_size); + pub_array = keypairgen.getE(); + prv_array = keypairgen.getD(); + n_array = keypairgen.getN(); + + p_array = keypairgen.getP(); + q_array = keypairgen.getQ(); + ep_array = keypairgen.getEP(); + eq_array = keypairgen.getEQ(); + c_array = keypairgen.getC(); + + keypairgen = null; + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + } + + private static final byte[] begin = Util.str2byte("-----BEGIN RSA PRIVATE KEY-----"); + private static final byte[] end = Util.str2byte("-----END RSA PRIVATE KEY-----"); + + @Override + byte[] getBegin() { + return begin; + } + + @Override + byte[] getEnd() { + return end; + } + + @Override + byte[] getPrivateKey() { + int content = 1 + countLength(1) + 1 + // INTEGER + 1 + countLength(n_array.length) + n_array.length + // INTEGER N + 1 + countLength(pub_array.length) + pub_array.length + // INTEGER pub + 1 + countLength(prv_array.length) + prv_array.length + // INTEGER prv + 1 + countLength(p_array.length) + p_array.length + // INTEGER p + 1 + countLength(q_array.length) + q_array.length + // INTEGER q + 1 + countLength(ep_array.length) + ep_array.length + // INTEGER ep + 1 + countLength(eq_array.length) + eq_array.length + // INTEGER eq + 1 + countLength(c_array.length) + c_array.length; // INTEGER c + + int total = 1 + countLength(content) + content; // SEQUENCE + + byte[] plain = new byte[total]; + int index = 0; + index = writeSEQUENCE(plain, index, content); + index = writeINTEGER(plain, index, new byte[1]); // 0 + index = writeINTEGER(plain, index, n_array); + index = writeINTEGER(plain, index, pub_array); + index = writeINTEGER(plain, index, prv_array); + index = writeINTEGER(plain, index, p_array); + index = writeINTEGER(plain, index, q_array); + index = writeINTEGER(plain, index, ep_array); + index = writeINTEGER(plain, index, eq_array); + index = writeINTEGER(plain, index, c_array); + return plain; + } + + @Override + boolean parse(byte[] plain) { + + try { + int index = 0; + int length = 0; + + if (vendor == VENDOR_PUTTY || vendor == VENDOR_PUTTY_V3) { + Buffer buf = new Buffer(plain); + buf.skip(plain.length); + + try { + byte[][] tmp = buf.getBytes(4, ""); + prv_array = tmp[0]; + p_array = tmp[1]; + q_array = tmp[2]; + c_array = tmp[3]; + } catch (JSchException e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + + getEPArray(); + getEQArray(); + + return true; + } + + if (vendor == VENDOR_FSECURE) { + if (plain[index] != 0x30) { // FSecure + Buffer buf = new Buffer(plain); + pub_array = buf.getMPIntBits(); + prv_array = buf.getMPIntBits(); + n_array = buf.getMPIntBits(); + byte[] u_array = buf.getMPIntBits(); + p_array = buf.getMPIntBits(); + q_array = buf.getMPIntBits(); + if (n_array != null) { + key_size = (new BigInteger(n_array)).bitLength(); + } + + getEPArray(); + getEQArray(); + getCArray(); + + return true; + } + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key"); + } + return false; + } + + // OPENSSH Key v1 Format + if (vendor == VENDOR_OPENSSH_V1) { + final Buffer prvKEyBuffer = new Buffer(plain); + int checkInt1 = prvKEyBuffer.getInt(); // uint32 checkint1 + int checkInt2 = prvKEyBuffer.getInt(); // uint32 checkint2 + if (checkInt1 != checkInt2) { + throw new JSchException("check failed"); + } + String keyType = Util.byte2str(prvKEyBuffer.getString()); // string keytype + n_array = prvKEyBuffer.getMPInt(); // Modulus + pub_array = prvKEyBuffer.getMPInt(); // Public Exponent + prv_array = prvKEyBuffer.getMPInt(); // Private Exponent + c_array = prvKEyBuffer.getMPInt(); // iqmp (q^-1 mod p) + p_array = prvKEyBuffer.getMPInt(); // p (Prime 1) + q_array = prvKEyBuffer.getMPInt(); // q (Prime 2) + if (n_array != null) { + key_size = (new BigInteger(n_array)).bitLength(); + } + publicKeyComment = Util.byte2str(prvKEyBuffer.getString()); + + getEPArray(); + getEQArray(); + + return true; + } + + /* + * Key must be in the following ASN.1 DER encoding, RSAPrivateKey ::= SEQUENCE { version + * Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d + * prime1 INTEGER, -- p prime2 INTEGER, -- q exponent1 INTEGER, -- d mod (p-1) exponent2 + * INTEGER, -- d mod (q-1) coefficient INTEGER, -- (inverse of q) mod p otherPrimeInfos + * OtherPrimeInfos OPTIONAL } + */ + + index++; // SEQUENCE + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + + if (plain[index] != 0x02) + return false; + index++; // INTEGER + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + n_array = new byte[length]; + System.arraycopy(plain, index, n_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + pub_array = new byte[length]; + System.arraycopy(plain, index, pub_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + prv_array = new byte[length]; + System.arraycopy(plain, index, prv_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + p_array = new byte[length]; + System.arraycopy(plain, index, p_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + q_array = new byte[length]; + System.arraycopy(plain, index, q_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + ep_array = new byte[length]; + System.arraycopy(plain, index, ep_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + eq_array = new byte[length]; + System.arraycopy(plain, index, eq_array, 0, length); + index += length; + + index++; + length = plain[index++] & 0xff; + if ((length & 0x80) != 0) { + int foo = length & 0x7f; + length = 0; + while (foo-- > 0) { + length = (length << 8) + (plain[index++] & 0xff); + } + } + c_array = new byte[length]; + System.arraycopy(plain, index, c_array, 0, length); + index += length; + + if (n_array != null) { + key_size = (new BigInteger(n_array)).bitLength(); + } + + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to parse key", e); + } + return false; + } + return true; + } + + @Override + public byte[] getPublicKeyBlob() { + byte[] foo = super.getPublicKeyBlob(); + if (foo != null) + return foo; + + if (pub_array == null) + return null; + byte[][] tmp = new byte[3][]; + tmp[0] = sshrsa; + tmp[1] = pub_array; + tmp[2] = n_array; + return Buffer.fromBytes(tmp).buffer; + } + + private static final byte[] sshrsa = Util.str2byte("ssh-rsa"); + + @Override + byte[] getKeyTypeName() { + return sshrsa; + } + + @Override + public int getKeyType() { + return RSA; + } + + @Override + public int getKeySize() { + return key_size; + } + + @Override + public byte[] getSignature(byte[] data) { + return getSignature(data, "ssh-rsa"); + } + + @Override + public byte[] getSignature(byte[] data, String alg) { + try { + Class c = + Class.forName(JSch.getConfig(alg)).asSubclass(SignatureRSA.class); + SignatureRSA rsa = c.getDeclaredConstructor().newInstance(); + rsa.init(); + rsa.setPrvKey(prv_array, n_array); + + rsa.update(data); + byte[] sig = rsa.sign(); + byte[][] tmp = new byte[2][]; + tmp[0] = Util.str2byte(alg); + tmp[1] = sig; + return Buffer.fromBytes(tmp).buffer; + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to generate signature", e); + } + } + return null; + } + + @Override + public Signature getVerifier() { + return getVerifier("ssh-rsa"); + } + + @Override + public Signature getVerifier(String alg) { + try { + Class c = + Class.forName(JSch.getConfig(alg)).asSubclass(SignatureRSA.class); + SignatureRSA rsa = c.getDeclaredConstructor().newInstance(); + rsa.init(); + + if (pub_array == null && n_array == null && getPublicKeyBlob() != null) { + Buffer buf = new Buffer(getPublicKeyBlob()); + buf.getString(); + pub_array = buf.getString(); + n_array = buf.getString(); + } + + rsa.setPubKey(pub_array, n_array); + return rsa; + } catch (Exception e) { + if (instLogger.getLogger().isEnabled(Logger.ERROR)) { + instLogger.getLogger().log(Logger.ERROR, "failed to create verifier", e); + } + } + return null; + } + + static KeyPair fromSSHAgent(JSch.InstanceLogger instLogger, Buffer buf) throws JSchException { + + byte[][] tmp = buf.getBytes(8, "invalid key format"); + + byte[] n_array = tmp[1]; + byte[] pub_array = tmp[2]; + byte[] prv_array = tmp[3]; + KeyPairRSA kpair = new KeyPairRSA(instLogger, n_array, pub_array, prv_array); + kpair.c_array = tmp[4]; // iqmp + kpair.p_array = tmp[5]; + kpair.q_array = tmp[6]; + kpair.publicKeyComment = Util.byte2str(tmp[7]); + kpair.vendor = VENDOR_OPENSSH; + return kpair; + } + + @Override + public byte[] forSSHAgent() throws JSchException { + if (isEncrypted()) { + throw new JSchException("key is encrypted."); + } + Buffer buf = new Buffer(); + buf.putString(sshrsa); + buf.putString(n_array); + buf.putString(pub_array); + buf.putString(prv_array); + buf.putString(getCArray()); + buf.putString(p_array); + buf.putString(q_array); + buf.putString(Util.str2byte(publicKeyComment)); + byte[] result = new byte[buf.getLength()]; + buf.getByte(result, 0, result.length); + return result; + } + + private byte[] getEPArray() { + if (ep_array == null) { + ep_array = (new BigInteger(prv_array)).mod(new BigInteger(p_array).subtract(BigInteger.ONE)) + .toByteArray(); + } + return ep_array; + } + + private byte[] getEQArray() { + if (eq_array == null) { + eq_array = (new BigInteger(prv_array)).mod(new BigInteger(q_array).subtract(BigInteger.ONE)) + .toByteArray(); + } + return eq_array; + } + + private byte[] getCArray() { + if (c_array == null) { + c_array = (new BigInteger(q_array)).modInverse(new BigInteger(p_array)).toByteArray(); + } + return c_array; + } + + @Override + public void dispose() { + super.dispose(); + Util.bzero(prv_array); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/KnownHosts.java b/files-jsch/src/main/java/com/jcraft/jsch/KnownHosts.java new file mode 100644 index 0000000..950c19e --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/KnownHosts.java @@ -0,0 +1,648 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Vector; + +class KnownHosts implements HostKeyRepository { + private JSch jsch = null; + private String known_hosts = null; + private Vector pool = null; + + MAC hmacsha1; + + KnownHosts(JSch jsch) { + super(); + this.jsch = jsch; + getHMACSHA1(); + pool = new Vector<>(); + } + + void setKnownHosts(String filename) throws JSchException { + try { + known_hosts = filename; + InputStream fis = new FileInputStream(Util.checkTilde(filename)); + setKnownHosts(fis); + } catch (FileNotFoundException e) { + // The non-existing file should be allowed. + } + } + + void setKnownHosts(InputStream input) throws JSchException { + pool.removeAllElements(); + StringBuilder sb = new StringBuilder(); + byte i; + int j; + boolean error = false; + try (InputStream fis = input) { + String host; + String key = null; + int type; + byte[] buf = new byte[1024]; + int bufl = 0; + loop: while (true) { + bufl = 0; + while (true) { + j = fis.read(); + if (j == -1) { + if (bufl == 0) { + break loop; + } + break; + } + if (j == 0x0d) { + continue; + } + if (j == 0x0a) { + break; + } + if (buf.length <= bufl) { + if (bufl > 1024 * 10) + break; // too long... + byte[] newbuf = new byte[buf.length * 2]; + System.arraycopy(buf, 0, newbuf, 0, buf.length); + buf = newbuf; + } + buf[bufl++] = (byte) j; + } + + j = 0; + while (j < bufl) { + i = buf[j]; + if (i == ' ' || i == '\t') { + j++; + continue; + } + if (i == '#') { + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + break; + } + if (j >= bufl) { + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + sb.setLength(0); + while (j < bufl) { + i = buf[j++]; + if (i == 0x20 || i == '\t') { + break; + } + sb.append((char) i); + } + host = sb.toString(); + if (j >= bufl || host.length() == 0) { + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + while (j < bufl) { + i = buf[j]; + if (i == ' ' || i == '\t') { + j++; + continue; + } + break; + } + + String marker = ""; + if (host.charAt(0) == '@') { + marker = host; + + sb.setLength(0); + while (j < bufl) { + i = buf[j++]; + if (i == 0x20 || i == '\t') { + break; + } + sb.append((char) i); + } + host = sb.toString(); + if (j >= bufl || host.length() == 0) { + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + while (j < bufl) { + i = buf[j]; + if (i == ' ' || i == '\t') { + j++; + continue; + } + break; + } + } + + sb.setLength(0); + type = -1; + while (j < bufl) { + i = buf[j++]; + if (i == 0x20 || i == '\t') { + break; + } + sb.append((char) i); + } + String tmp = sb.toString(); + if (HostKey.name2type(tmp) != HostKey.UNKNOWN) { + type = HostKey.name2type(tmp); + } else { + j = bufl; + } + if (j >= bufl) { + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + while (j < bufl) { + i = buf[j]; + if (i == ' ' || i == '\t') { + j++; + continue; + } + break; + } + + sb.setLength(0); + while (j < bufl) { + i = buf[j++]; + if (i == 0x0d) { + continue; + } + if (i == 0x0a) { + break; + } + if (i == 0x20 || i == '\t') { + break; + } + sb.append((char) i); + } + key = sb.toString(); + if (key.length() == 0) { + addInvalidLine(Util.byte2str(buf, 0, bufl)); + continue loop; + } + + while (j < bufl) { + i = buf[j]; + if (i == ' ' || i == '\t') { + j++; + continue; + } + break; + } + + /** + * "man sshd" has following descriptions, Note that the lines in these files are typically + * hundreds of characters long, and you definitely don't want to type in the host keys by + * hand. Rather, generate them by a script, ssh-keyscan(1) or by taking + * /usr/local/etc/ssh_host_key.pub and adding the host names at the front. This means that a + * comment is allowed to appear at the end of each key entry. + */ + String comment = null; + if (j < bufl) { + sb.setLength(0); + while (j < bufl) { + i = buf[j++]; + if (i == 0x0d) { + continue; + } + if (i == 0x0a) { + break; + } + sb.append((char) i); + } + comment = sb.toString(); + } + + // System.err.println(host); + // System.err.println("|"+key+"|"); + + HostKey hk = null; + hk = new HashedHostKey(marker, host, type, + Util.fromBase64(Util.str2byte(key), 0, key.length()), comment); + pool.addElement(hk); + } + if (error) { + throw new JSchException("KnownHosts: invalid format"); + } + } catch (Exception e) { + if (e instanceof JSchException) + throw (JSchException) e; + throw new JSchException(e.toString(), e); + } + } + + private void addInvalidLine(String line) throws JSchException { + HostKey hk = new HostKey(line, HostKey.UNKNOWN, null); + pool.addElement(hk); + } + + String getKnownHostsFile() { + return known_hosts; + } + + @Override + public String getKnownHostsRepositoryID() { + return known_hosts; + } + + @Override + public int check(String host, byte[] key) { + int result = NOT_INCLUDED; + if (host == null) { + return result; + } + + HostKey hk = null; + try { + hk = new HostKey(host, HostKey.GUESS, key); + } catch (Exception e) { // unsupported key + jsch.getInstanceLogger().log(Logger.DEBUG, + "exception while trying to read key while checking host '" + host + "'", e); + return result; + } + + synchronized (pool) { + for (int i = 0; i < pool.size(); i++) { + HostKey _hk = pool.elementAt(i); + if (_hk.isMatched(host) && _hk.type == hk.type) { + if (Util.array_equals(_hk.key, key)) { + return OK; + } + result = CHANGED; + } + } + } + + if (result == NOT_INCLUDED && host.startsWith("[") && host.indexOf("]:") > 1) { + return check(host.substring(1, host.indexOf("]:")), key); + } + + return result; + } + + @Override + public void add(HostKey hostkey, UserInfo userinfo) { + int type = hostkey.type; + String host = hostkey.getHost(); + // byte[] key=hostkey.key; + + HostKey hk = null; + synchronized (pool) { + for (int i = 0; i < pool.size(); i++) { + hk = pool.elementAt(i); + if (hk.isMatched(host) && hk.type == type) { + /* + * if(Util.array_equals(hk.key, key)){ return; } if(hk.host.equals(host)){ hk.key=key; + * return; } else{ hk.host=deleteSubString(hk.host, host); break; } + */ + } + } + } + + hk = hostkey; + + pool.addElement(hk); + + syncKnownHostsFile(userinfo); + } + + void syncKnownHostsFile(UserInfo userinfo) { + String khFilename = getKnownHostsRepositoryID(); + if (khFilename == null) { + return; + } + boolean doSync = true; + File goo = new File(Util.checkTilde(khFilename)); + if (!goo.exists()) { + doSync = false; + if (userinfo != null) { + doSync = userinfo + .promptYesNo(khFilename + " does not exist.\n" + "Are you sure you want to create it?"); + goo = goo.getParentFile(); + if (doSync && goo != null && !goo.exists()) { + doSync = userinfo.promptYesNo("The parent directory " + goo + " does not exist.\n" + + "Are you sure you want to create it?"); + if (doSync) { + if (!goo.mkdirs()) { + userinfo.showMessage(goo + " has not been created."); + doSync = false; + } else { + userinfo.showMessage( + goo + " has been succesfully created.\nPlease check its access permission."); + } + } + } + if (goo == null) + doSync = false; + } + } + if (!doSync) { + return; + } + try { + sync(khFilename); + } catch (Exception e) { + jsch.getInstanceLogger().log(Logger.ERROR, "unable to sync known host file " + goo.getPath(), + e); + } + } + + @Override + public HostKey[] getHostKey() { + return getHostKey(null, (String) null); + } + + @Override + public HostKey[] getHostKey(String host, String type) { + synchronized (pool) { + List v = new ArrayList<>(); + for (int i = 0; i < pool.size(); i++) { + HostKey hk = pool.elementAt(i); + if (hk.type == HostKey.UNKNOWN) + continue; + if (host == null || (hk.isMatched(host) && (type == null || hk.getType().equals(type)))) { + v.add(hk); + } + } + HostKey[] foo = new HostKey[v.size()]; + for (int i = 0; i < v.size(); i++) { + foo[i] = v.get(i); + } + if (host != null && host.startsWith("[") && host.indexOf("]:") > 1) { + HostKey[] tmp = getHostKey(host.substring(1, host.indexOf("]:")), type); + if (tmp.length > 0) { + HostKey[] bar = new HostKey[foo.length + tmp.length]; + System.arraycopy(foo, 0, bar, 0, foo.length); + System.arraycopy(tmp, 0, bar, foo.length, tmp.length); + foo = bar; + } + } + return foo; + } + } + + @Override + public void remove(String host, String type) { + remove(host, type, null); + } + + @Override + public void remove(String host, String type, byte[] key) { + boolean sync = false; + synchronized (pool) { + for (int i = 0; i < pool.size(); i++) { + HostKey hk = pool.elementAt(i); + if (host == null || (hk.isMatched(host) && (type == null + || (hk.getType().equals(type) && (key == null || Util.array_equals(key, hk.key)))))) { + String hosts = hk.getHost(); + if (host == null || hosts.equals(host) + || ((hk instanceof HashedHostKey) && ((HashedHostKey) hk).isHashed())) { + pool.removeElement(hk); + i--; + } else { + hk.host = deleteSubString(hosts, host); + } + sync = true; + } + } + } + if (sync) { + try { + sync(); + } catch (Exception e) { + } ; + } + } + + void sync() throws IOException { + if (known_hosts != null) + sync(known_hosts); + } + + synchronized void sync(String foo) throws IOException { + if (foo == null) + return; + try (FileOutputStream fos = new FileOutputStream(Util.checkTilde(foo))) { + dump(fos); + } + } + + private static final byte[] space = {(byte) 0x20}; + private static final byte[] lf = Util.str2byte("\n"); + + void dump(OutputStream out) { + try { + HostKey hk; + synchronized (pool) { + for (int i = 0; i < pool.size(); i++) { + hk = pool.elementAt(i); + dumpHostKey(out, hk); + } + } + } catch (Exception e) { + jsch.getInstanceLogger().log(Logger.ERROR, "unable to dump known hosts", e); + } + } + + void dumpHostKey(OutputStream out, HostKey hk) throws IOException { + String marker = hk.getMarker(); + String host = hk.getHost(); + String type = hk.getType(); + String comment = hk.getComment(); + if (type.equals("UNKNOWN")) { + out.write(Util.str2byte(host)); + out.write(lf); + return; + } + if (marker.length() != 0) { + out.write(Util.str2byte(marker)); + out.write(space); + } + out.write(Util.str2byte(host)); + out.write(space); + out.write(Util.str2byte(type)); + out.write(space); + out.write(Util.str2byte(hk.getKey())); + + if (comment != null) { + out.write(space); + out.write(Util.str2byte(comment)); + } + out.write(lf); + } + + String deleteSubString(String hosts, String host) { + int i = 0; + int hostlen = host.length(); + int hostslen = hosts.length(); + int j; + while (i < hostslen) { + j = hosts.indexOf(',', i); + if (j == -1) + break; + if (!host.equals(hosts.substring(i, j))) { + i = j + 1; + continue; + } + return hosts.substring(0, i) + hosts.substring(j + 1); + } + if (hosts.endsWith(host) && hostslen - i == hostlen) { + return hosts.substring(0, (hostlen == hostslen) ? 0 : hostslen - hostlen - 1); + } + return hosts; + } + + MAC getHMACSHA1() throws IllegalArgumentException { + if (hmacsha1 == null) { + hmacsha1 = createHMAC(JSch.getConfig("hmac-sha1")); + } + + return hmacsha1; + } + + MAC createHMAC(String hmacClassname) throws IllegalArgumentException { + try { + Class c = Class.forName(hmacClassname).asSubclass(MAC.class); + return c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + jsch.getInstanceLogger().log(Logger.ERROR, + "unable to instantiate HMAC-class " + hmacClassname, e); + throw new IllegalArgumentException("instantiation of " + hmacClassname + " lead to an error", + e); + } + } + + HostKey createHashedHostKey(String host, byte[] key) throws JSchException { + HashedHostKey hhk = new HashedHostKey(host, key); + hhk.hash(); + return hhk; + } + + class HashedHostKey extends HostKey { + private static final String HASH_MAGIC = "|1|"; + private static final String HASH_DELIM = "|"; + + private boolean hashed = false; + byte[] salt = null; + byte[] hash = null; + + HashedHostKey(String host, byte[] key) throws JSchException { + this(host, GUESS, key); + } + + HashedHostKey(String host, int type, byte[] key) throws JSchException { + this("", host, type, key, null); + } + + HashedHostKey(String marker, String host, int type, byte[] key, String comment) + throws JSchException { + super(marker, host, type, key, comment); + if (this.host.startsWith(HASH_MAGIC) + && this.host.substring(HASH_MAGIC.length()).indexOf(HASH_DELIM) > 0) { + String data = this.host.substring(HASH_MAGIC.length()); + String _salt = data.substring(0, data.indexOf(HASH_DELIM)); + String _hash = data.substring(data.indexOf(HASH_DELIM) + 1); + salt = Util.fromBase64(Util.str2byte(_salt), 0, _salt.length()); + hash = Util.fromBase64(Util.str2byte(_hash), 0, _hash.length()); + int blockSize = hmacsha1.getBlockSize(); + if (salt.length != blockSize || hash.length != blockSize) { + salt = null; + hash = null; + return; + } + hashed = true; + } + } + + @Override + boolean isMatched(String _host) { + if (!hashed) { + return super.isMatched(_host); + } + try { + synchronized (hmacsha1) { + hmacsha1.init(salt); + byte[] foo = Util.str2byte(_host); + hmacsha1.update(foo, 0, foo.length); + byte[] bar = new byte[hmacsha1.getBlockSize()]; + hmacsha1.doFinal(bar, 0); + return Util.array_equals(hash, bar); + } + } catch (Exception e) { + jsch.getInstanceLogger().log(Logger.ERROR, + "an error occurred while trying to check hash for host " + _host, e); + } + return false; + } + + boolean isHashed() { + return hashed; + } + + void hash() { + if (hashed) + return; + if (salt == null) { + Random random = Session.random; + synchronized (random) { + salt = new byte[hmacsha1.getBlockSize()]; + random.fill(salt, 0, salt.length); + } + } + try { + synchronized (hmacsha1) { + hmacsha1.init(salt); + byte[] foo = Util.str2byte(host); + hmacsha1.update(foo, 0, foo.length); + hash = new byte[hmacsha1.getBlockSize()]; + hmacsha1.doFinal(hash, 0); + } + } catch (Exception e) { + jsch.getInstanceLogger().log(Logger.ERROR, + "an error occurred while trying to calculate the hash for host " + host, e); + salt = null; + hash = null; + return; + } + host = HASH_MAGIC + Util.byte2str(Util.toBase64(salt, 0, salt.length, true)) + HASH_DELIM + + Util.byte2str(Util.toBase64(hash, 0, hash.length, true)); + hashed = true; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java b/files-jsch/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java new file mode 100644 index 0000000..c2a6328 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/LocalIdentityRepository.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Vector; + +class LocalIdentityRepository implements IdentityRepository { + private static final String name = "Local Identity Repository"; + + private Vector identities = new Vector<>(); + private JSch.InstanceLogger instLogger; + + LocalIdentityRepository(JSch.InstanceLogger instLogger) { + this.instLogger = instLogger; + } + + @Override + public String getName() { + return name; + } + + @Override + public int getStatus() { + return RUNNING; + } + + @Override + public synchronized Vector getIdentities() { + removeDupulicates(); + Vector v = new Vector<>(); + for (int i = 0; i < identities.size(); i++) { + v.addElement(identities.elementAt(i)); + } + return v; + } + + public synchronized void add(Identity identity) { + if (!identities.contains(identity)) { + byte[] blob1 = identity.getPublicKeyBlob(); + if (blob1 == null) { + identities.addElement(identity); + return; + } + for (int i = 0; i < identities.size(); i++) { + byte[] blob2 = identities.elementAt(i).getPublicKeyBlob(); + if (blob2 != null && Util.array_equals(blob1, blob2)) { + if (!identity.isEncrypted() && identities.elementAt(i).isEncrypted()) { + remove(blob2); + } else { + return; + } + } + } + identities.addElement(identity); + } + } + + @Override + public synchronized boolean add(byte[] identity) { + try { + Identity _identity = IdentityFile.newInstance("from remote:", identity, null, instLogger); + add(_identity); + return true; + } catch (JSchException e) { + return false; + } + } + + synchronized void remove(Identity identity) { + if (identities.contains(identity)) { + identities.removeElement(identity); + identity.clear(); + } else { + remove(identity.getPublicKeyBlob()); + } + } + + @Override + public synchronized boolean remove(byte[] blob) { + if (blob == null) + return false; + for (int i = 0; i < identities.size(); i++) { + Identity _identity = identities.elementAt(i); + byte[] _blob = _identity.getPublicKeyBlob(); + if (_blob == null || !Util.array_equals(blob, _blob)) + continue; + identities.removeElement(_identity); + _identity.clear(); + return true; + } + return false; + } + + @Override + public synchronized void removeAll() { + for (int i = 0; i < identities.size(); i++) { + Identity identity = identities.elementAt(i); + identity.clear(); + } + identities.removeAllElements(); + } + + private void removeDupulicates() { + Vector v = new Vector<>(); + int len = identities.size(); + if (len == 0) + return; + for (int i = 0; i < len; i++) { + Identity foo = identities.elementAt(i); + byte[] foo_blob = foo.getPublicKeyBlob(); + if (foo_blob == null) + continue; + for (int j = i + 1; j < len; j++) { + Identity bar = identities.elementAt(j); + byte[] bar_blob = bar.getPublicKeyBlob(); + if (bar_blob == null) + continue; + if (Util.array_equals(foo_blob, bar_blob) && foo.isEncrypted() == bar.isEncrypted()) { + v.addElement(foo_blob); + break; + } + } + } + for (int i = 0; i < v.size(); i++) { + remove(v.elementAt(i)); + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Logger.java b/files-jsch/src/main/java/com/jcraft/jsch/Logger.java new file mode 100644 index 0000000..3d37f10 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Logger.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public interface Logger { + + public final int DEBUG = 0; + public final int INFO = 1; + public final int WARN = 2; + public final int ERROR = 3; + public final int FATAL = 4; + + public boolean isEnabled(int level); + + public void log(int level, String message); + + public default void log(int level, String message, Throwable cause) { + if (!isEnabled(level)) { + return; + } + if (cause != null) { + StringWriter sw = new StringWriter(); + try (PrintWriter pw = new PrintWriter(sw, true)) { + cause.printStackTrace(pw); + } + message += System.lineSeparator() + sw.toString(); + } + log(level, message); + } + + /* + * public final Logger SIMPLE_LOGGER=new Logger(){ public boolean isEnabled(int level){return + * true;} public void log(int level, String message){System.err.println(message);} }; final Logger + * DEVNULL=new Logger(){ public boolean isEnabled(int level){return false;} public void log(int + * level, String message){} }; + */ +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/MAC.java b/files-jsch/src/main/java/com/jcraft/jsch/MAC.java new file mode 100644 index 0000000..1f2d782 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/MAC.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface MAC { + String getName(); + + int getBlockSize(); + + void init(byte[] key) throws Exception; + + void update(byte[] foo, int start, int len); + + void update(int foo); + + void doFinal(byte[] buf, int offset); + + default boolean isEtM() { + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/OpenSSHConfig.java b/files-jsch/src/main/java/com/jcraft/jsch/OpenSSHConfig.java new file mode 100644 index 0000000..76328a2 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/OpenSSHConfig.java @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.Vector; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * This class implements ConfigRepository interface, and parses OpenSSH's configuration file. The + * following keywords will be recognized, + * + *

    + *
  • Host
  • + *
  • User
  • + *
  • Hostname
  • + *
  • Port
  • + *
  • PreferredAuthentications
  • + *
  • PubkeyAcceptedAlgorithms
  • + *
  • FingerprintHash
  • + *
  • IdentityFile
  • + *
  • NumberOfPasswordPrompts
  • + *
  • ConnectTimeout
  • + *
  • HostKeyAlias
  • + *
  • UserKnownHostsFile
  • + *
  • KexAlgorithms
  • + *
  • HostKeyAlgorithms
  • + *
  • Ciphers
  • + *
  • Macs
  • + *
  • Compression
  • + *
  • CompressionLevel
  • + *
  • ForwardAgent
  • + *
  • RequestTTY
  • + *
  • ServerAliveInterval
  • + *
  • LocalForward
  • + *
  • RemoteForward
  • + *
  • ClearAllForwardings
  • + *
+ * + * @see ConfigRepository + */ +public class OpenSSHConfig implements ConfigRepository { + + private static final Set keysWithListAdoption = Stream + .of("KexAlgorithms", "Ciphers", "HostKeyAlgorithms", "MACs", "PubkeyAcceptedAlgorithms", + "PubkeyAcceptedKeyTypes") + .map(string -> string.toUpperCase(Locale.ROOT)).collect(Collectors.toSet()); + + /** + * Parses the given string, and returns an instance of ConfigRepository. + * + * @param conf string, which includes OpenSSH's config + * @return an instanceof OpenSSHConfig + */ + public static OpenSSHConfig parse(String conf) throws IOException { + try (Reader r = new StringReader(conf)) { + try (BufferedReader br = new BufferedReader(r)) { + return new OpenSSHConfig(br); + } + } + } + + /** + * Parses the given file, and returns an instance of ConfigRepository. + * + * @param file OpenSSH's config file + * @return an instanceof OpenSSHConfig + */ + public static OpenSSHConfig parseFile(String file) throws IOException { + try (BufferedReader br = + Files.newBufferedReader(Paths.get(Util.checkTilde(file)), StandardCharsets.UTF_8)) { + return new OpenSSHConfig(br); + } + } + + OpenSSHConfig(BufferedReader br) throws IOException { + _parse(br); + } + + private final Hashtable> config = new Hashtable<>(); + private final Vector hosts = new Vector<>(); + + private void _parse(BufferedReader br) throws IOException { + String host = ""; + Vector kv = new Vector<>(); + String l = null; + + while ((l = br.readLine()) != null) { + l = l.trim(); + if (l.length() == 0 || l.startsWith("#")) + continue; + + String[] key_value = l.split("[= \t]", 2); + for (int i = 0; i < key_value.length; i++) + key_value[i] = key_value[i].trim(); + + if (key_value.length <= 1) + continue; + + if (key_value[0].equalsIgnoreCase("Host")) { + config.put(host, kv); + hosts.addElement(host); + host = key_value[1]; + kv = new Vector<>(); + } else { + kv.addElement(key_value); + } + } + config.put(host, kv); + hosts.addElement(host); + } + + @Override + public Config getConfig(String host) { + return new MyConfig(host); + } + + /** + * Returns mapping of jsch config property names to OpenSSH property names. + * + * @return map + */ + static Hashtable getKeymap() { + return keymap; + } + + private static final Hashtable keymap = new Hashtable<>(); + + static { + keymap.put("kex", "KexAlgorithms"); + keymap.put("server_host_key", "HostKeyAlgorithms"); + keymap.put("cipher.c2s", "Ciphers"); + keymap.put("cipher.s2c", "Ciphers"); + keymap.put("mac.c2s", "Macs"); + keymap.put("mac.s2c", "Macs"); + keymap.put("compression.s2c", "Compression"); + keymap.put("compression.c2s", "Compression"); + keymap.put("compression_level", "CompressionLevel"); + keymap.put("MaxAuthTries", "NumberOfPasswordPrompts"); + } + + class MyConfig implements Config { + + private String host; + private Vector> _configs = new Vector<>(); + + MyConfig(String host) { + this.host = host; + + _configs.addElement(config.get("")); + + byte[] _host = Util.str2byte(host); + if (hosts.size() > 1) { + for (int i = 1; i < hosts.size(); i++) { + boolean anyPositivePatternMatches = false; + boolean anyNegativePatternMatches = false; + String patterns[] = hosts.elementAt(i).split("[ \t]"); + for (int j = 0; j < patterns.length; j++) { + boolean negate = false; + String foo = patterns[j].trim(); + if (foo.startsWith("!")) { + negate = true; + foo = foo.substring(1).trim(); + } + if (Util.glob(Util.str2byte(foo), _host)) { + if (negate) { + anyNegativePatternMatches = true; + } else { + anyPositivePatternMatches = true; + } + } + } + + if (anyPositivePatternMatches && !anyNegativePatternMatches) { + _configs.addElement(config.get(hosts.elementAt(i))); + } + } + } + } + + private String find(String key) { + String originalKey = key; + if (keymap.get(key) != null) { + key = keymap.get(key); + } + key = key.toUpperCase(Locale.ROOT); + String value = null; + for (int i = 0; i < _configs.size(); i++) { + Vector v = _configs.elementAt(i); + for (int j = 0; j < v.size(); j++) { + String[] kv = v.elementAt(j); + if (kv[0].toUpperCase(Locale.ROOT).equals(key)) { + value = kv[1]; + break; + } + } + if (value != null) + break; + } + // TODO: The following change should be applied, + // but it is breaking changes. + // The consensus is required to enable it. + /* + * if(value!=null && (key.equals("SERVERALIVEINTERVAL") || key.equals("CONNECTTIMEOUT"))){ try + * { int timeout = Integer.parseInt(value); value = Integer.toString(timeout*1000); } catch + * (NumberFormatException e) { } } + */ + + if (keysWithListAdoption.contains(key) && value != null + && (value.startsWith("+") || value.startsWith("-") || value.startsWith("^"))) { + + String origConfig = JSch.getConfig(originalKey).trim(); + + if (value.startsWith("+")) { + value = origConfig + "," + value.substring(1).trim(); + } else if (value.startsWith("-")) { + List algList = + Arrays.stream(Util.split(origConfig, ",")).collect(Collectors.toList()); + for (String alg : Util.split(value.substring(1).trim(), ",")) { + algList.remove(alg.trim()); + } + value = String.join(",", algList); + } else if (value.startsWith("^")) { + value = value.substring(1).trim() + "," + origConfig; + } + } + + return value; + } + + private String[] multiFind(String key) { + key = key.toUpperCase(Locale.ROOT); + Vector value = new Vector<>(); + for (int i = 0; i < _configs.size(); i++) { + Vector v = _configs.elementAt(i); + for (int j = 0; j < v.size(); j++) { + String[] kv = v.elementAt(j); + if (kv[0].toUpperCase(Locale.ROOT).equals(key)) { + String foo = kv[1]; + if (foo != null) { + value.remove(foo); + value.addElement(foo); + } + } + } + } + String[] result = new String[value.size()]; + value.toArray(result); + return result; + } + + @Override + public String getHostname() { + return find("Hostname"); + } + + @Override + public String getUser() { + return find("User"); + } + + @Override + public int getPort() { + String foo = find("Port"); + int port = -1; + try { + port = Integer.parseInt(foo); + } catch (NumberFormatException e) { + // wrong format + } + return port; + } + + @Override + public String getValue(String key) { + if (key.equals("compression.s2c") || key.equals("compression.c2s")) { + String foo = find(key); + if (foo == null || foo.equals("no")) + return "none,zlib@openssh.com,zlib"; + return "zlib@openssh.com,zlib,none"; + } + return find(key); + } + + @Override + public String[] getValues(String key) { + return multiFind(key); + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/PBKDF.java b/files-jsch/src/main/java/com/jcraft/jsch/PBKDF.java new file mode 100644 index 0000000..67e6c9c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/PBKDF.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +/** Use PBKDF2 instead. */ +@Deprecated +public interface PBKDF { + byte[] getKey(byte[] pass, byte[] salt, int iteration, int size); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/PBKDF2.java b/files-jsch/src/main/java/com/jcraft/jsch/PBKDF2.java new file mode 100644 index 0000000..61ecfcd --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/PBKDF2.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface PBKDF2 extends KDF { + void init(byte[] salt, int iteration) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Packet.java b/files-jsch/src/main/java/com/jcraft/jsch/Packet.java new file mode 100644 index 0000000..c402d7d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Packet.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class Packet { + + private static Random random = null; + + static void setRandom(Random foo) { + random = foo; + } + + Buffer buffer; + byte[] ba4 = new byte[4]; + + Packet(Buffer buffer) { + this.buffer = buffer; + } + + void reset() { + buffer.index = 5; + } + + void padding(int bsize, boolean includePktLen) { + int len = buffer.index; + if (!includePktLen) { + len -= 4; + } + int pad = (-len) & (bsize - 1); + if (pad < bsize) { + pad += bsize; + } + len += pad; + if (includePktLen) { + len -= 4; + } + ba4[0] = (byte) (len >>> 24); + ba4[1] = (byte) (len >>> 16); + ba4[2] = (byte) (len >>> 8); + ba4[3] = (byte) (len); + System.arraycopy(ba4, 0, buffer.buffer, 0, 4); + buffer.buffer[4] = (byte) pad; + synchronized (random) { + random.fill(buffer.buffer, buffer.index, pad); + } + buffer.skip(pad); + // buffer.putPad(pad); + /* + * for(int i=0; i foo = new Vector<>(); + synchronized (pool) { + for (int i = 0; i < pool.size(); i++) { + PortWatcher p = pool.elementAt(i); + if (p.session == session) { + foo.addElement(p.lport + ":" + p.host + ":" + p.rport); + } + } + } + String[] bar = new String[foo.size()]; + for (int i = 0; i < foo.size(); i++) { + bar[i] = foo.elementAt(i); + } + return bar; + } + + static PortWatcher getPort(Session session, String address, int lport) throws JSchException { + InetAddress addr; + try { + addr = InetAddress.getByName(address); + } catch (UnknownHostException uhe) { + throw new JSchException("PortForwardingL: invalid address " + address + " specified.", uhe); + } + synchronized (pool) { + for (int i = 0; i < pool.size(); i++) { + PortWatcher p = pool.elementAt(i); + if (p.session == session && p.lport == lport) { + if (/* p.boundaddress.isAnyLocalAddress() || */ + (anyLocalAddress != null && p.boundaddress.equals(anyLocalAddress)) + || p.boundaddress.equals(addr)) + return p; + } + } + return null; + } + } + + private static String normalize(String address) { + if (address != null) { + if (address.length() == 0 || address.equals("*")) + address = "0.0.0.0"; + else if (address.equals("localhost")) + address = "127.0.0.1"; + } + return address; + } + + static PortWatcher addPort(Session session, String address, int lport, String host, int rport, + ServerSocketFactory ssf) throws JSchException { + address = normalize(address); + if (getPort(session, address, lport) != null) { + throw new JSchException( + "PortForwardingL: local port " + address + ":" + lport + " is already registered."); + } + PortWatcher pw = new PortWatcher(session, address, lport, host, rport, ssf); + pool.addElement(pw); + return pw; + } + + static void delPort(Session session, String address, int lport) throws JSchException { + address = normalize(address); + PortWatcher pw = getPort(session, address, lport); + if (pw == null) { + throw new JSchException( + "PortForwardingL: local port " + address + ":" + lport + " is not registered."); + } + pw.delete(); + pool.removeElement(pw); + } + + static void delPort(Session session) { + synchronized (pool) { + PortWatcher[] foo = new PortWatcher[pool.size()]; + int count = 0; + for (int i = 0; i < pool.size(); i++) { + PortWatcher p = pool.elementAt(i); + if (p.session == session) { + p.delete(); + foo[count++] = p; + } + } + for (int i = 0; i < count; i++) { + PortWatcher p = foo[i]; + pool.removeElement(p); + } + } + } + + PortWatcher(Session session, String address, int lport, String host, int rport, + ServerSocketFactory factory) throws JSchException { + this.session = session; + this.lport = lport; + this.host = host; + this.rport = rport; + bindLocalPort(address, lport, factory); + } + + public static PortWatcher addSocket(Session session, String bindAddress, int lport, + String socketPath, ServerSocketFactory ssf) throws JSchException { + String address = normalize(bindAddress); + if (getPort(session, address, lport) != null) { + throw new JSchException( + "PortForwardingL: local port " + address + ":" + lport + " is already registered."); + } + PortWatcher pw = new PortWatcher(session, address, lport, socketPath, ssf); + pool.addElement(pw); + return pw; + } + + void run() { + thread = this::run; + try { + while (thread != null) { + Socket socket = ss.accept(); + socket.setTcpNoDelay(true); + InputStream in = socket.getInputStream(); + OutputStream out = socket.getOutputStream(); + if (socketPath != null && socketPath.length() > 0) { + ChannelDirectStreamLocal channel = new ChannelDirectStreamLocal(); + channel.setSession(session); + channel.init(); + channel.setInputStream(in); + channel.setOutputStream(out); + session.addChannel(channel); + channel.setSocketPath(socketPath); + channel.setOrgIPAddress(socket.getInetAddress().getHostAddress()); + channel.setOrgPort(socket.getPort()); + channel.connect(connectTimeout); + } else { + ChannelDirectTCPIP channel = new ChannelDirectTCPIP(); + channel.setSession(session); + channel.init(); + channel.setInputStream(in); + channel.setOutputStream(out); + session.addChannel(channel); + channel.setHost(host); + channel.setPort(rport); + channel.setOrgIPAddress(socket.getInetAddress().getHostAddress()); + channel.setOrgPort(socket.getPort()); + channel.connect(connectTimeout); + if (channel.exitstatus != -1) { + } + } + } + } catch (Exception e) { + // System.err.println("! "+e); + } + delete(); + } + + void delete() { + thread = null; + try { + if (ss != null) + ss.close(); + ss = null; + } catch (Exception e) { + } + } + + void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Proxy.java b/files-jsch/src/main/java/com/jcraft/jsch/Proxy.java new file mode 100644 index 0000000..e214ed8 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Proxy.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +public interface Proxy { + void connect(SocketFactory socket_factory, String host, int port, int timeout) throws Exception; + + InputStream getInputStream(); + + OutputStream getOutputStream(); + + Socket getSocket(); + + void close(); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ProxyHTTP.java b/files-jsch/src/main/java/com/jcraft/jsch/ProxyHTTP.java new file mode 100644 index 0000000..728c25b --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ProxyHTTP.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +public class ProxyHTTP implements Proxy { + private static int DEFAULTPORT = 80; + private String proxy_host; + private int proxy_port; + private InputStream in; + private OutputStream out; + private Socket socket; + + private String user; + private String passwd; + + public ProxyHTTP(String proxy_host) { + int port = DEFAULTPORT; + String host = proxy_host; + if (proxy_host.indexOf(':') != -1) { + try { + host = proxy_host.substring(0, proxy_host.indexOf(':')); + port = Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':') + 1)); + } catch (Exception e) { + } + } + this.proxy_host = host; + this.proxy_port = port; + } + + public ProxyHTTP(String proxy_host, int proxy_port) { + this.proxy_host = proxy_host; + this.proxy_port = proxy_port; + } + + public void setUserPasswd(String user, String passwd) { + this.user = user; + this.passwd = passwd; + } + + @Override + public void connect(SocketFactory socket_factory, String host, int port, int timeout) + throws JSchException { + try { + if (socket_factory == null) { + socket = Util.createSocket(proxy_host, proxy_port, timeout); + in = socket.getInputStream(); + out = socket.getOutputStream(); + } else { + socket = socket_factory.createSocket(proxy_host, proxy_port); + in = socket_factory.getInputStream(socket); + out = socket_factory.getOutputStream(socket); + } + if (timeout > 0) { + socket.setSoTimeout(timeout); + } + socket.setTcpNoDelay(true); + + out.write(Util.str2byte("CONNECT " + host + ":" + port + " HTTP/1.0\r\n")); + + if (user != null && passwd != null) { + byte[] code = Util.str2byte(user + ":" + passwd); + code = Util.toBase64(code, 0, code.length, true); + out.write(Util.str2byte("Proxy-Authorization: Basic ")); + out.write(code); + out.write(Util.str2byte("\r\n")); + } + + out.write(Util.str2byte("\r\n")); + out.flush(); + + int foo = 0; + + StringBuilder sb = new StringBuilder(); + while (foo >= 0) { + foo = in.read(); + if (foo != 13) { + sb.append((char) foo); + continue; + } + foo = in.read(); + if (foo != 10) { + continue; + } + break; + } + if (foo < 0) { + throw new IOException(); + } + + String response = sb.toString(); + String reason = "Unknow reason"; + int code = -1; + try { + foo = response.indexOf(' '); + int bar = response.indexOf(' ', foo + 1); + code = Integer.parseInt(response.substring(foo + 1, bar)); + reason = response.substring(bar + 1); + } catch (Exception e) { + } + if (code != 200) { + throw new IOException("proxy error: " + reason); + } + + /* + * while(foo>=0){ foo=in.read(); if(foo!=13) continue; foo=in.read(); if(foo!=10) continue; + * foo=in.read(); if(foo!=13) continue; foo=in.read(); if(foo!=10) continue; break; } + */ + + int count = 0; + while (true) { + count = 0; + while (foo >= 0) { + foo = in.read(); + if (foo != 13) { + count++; + continue; + } + foo = in.read(); + if (foo != 10) { + continue; + } + break; + } + if (foo < 0) { + throw new IOException(); + } + if (count == 0) + break; + } + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + try { + if (socket != null) + socket.close(); + } catch (Exception eee) { + } + String message = "ProxyHTTP: " + e.toString(); + throw new JSchProxyException(message, e); + } + } + + @Override + public InputStream getInputStream() { + return in; + } + + @Override + public OutputStream getOutputStream() { + return out; + } + + @Override + public Socket getSocket() { + return socket; + } + + @Override + public void close() { + try { + if (in != null) + in.close(); + if (out != null) + out.close(); + if (socket != null) + socket.close(); + } catch (Exception e) { + } + in = null; + out = null; + socket = null; + } + + public static int getDefaultPort() { + return DEFAULTPORT; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ProxySOCKS4.java b/files-jsch/src/main/java/com/jcraft/jsch/ProxySOCKS4.java new file mode 100644 index 0000000..540bfce --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ProxySOCKS4.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file depends on following documents, - SOCKS: A protocol for TCP proxy across firewalls, + * Ying-Da Lee http://www.socks.nec.com/protocol/socks4.protocol + */ + +package com.jcraft.jsch; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; + +public class ProxySOCKS4 implements Proxy { + private static int DEFAULTPORT = 1080; + private String proxy_host; + private int proxy_port; + private InputStream in; + private OutputStream out; + private Socket socket; + private String user; + private String passwd; + + public ProxySOCKS4(String proxy_host) { + int port = DEFAULTPORT; + String host = proxy_host; + if (proxy_host.indexOf(':') != -1) { + try { + host = proxy_host.substring(0, proxy_host.indexOf(':')); + port = Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':') + 1)); + } catch (Exception e) { + } + } + this.proxy_host = host; + this.proxy_port = port; + } + + public ProxySOCKS4(String proxy_host, int proxy_port) { + this.proxy_host = proxy_host; + this.proxy_port = proxy_port; + } + + public void setUserPasswd(String user, String passwd) { + this.user = user; + this.passwd = passwd; + } + + @Override + public void connect(SocketFactory socket_factory, String host, int port, int timeout) + throws JSchException { + try { + if (socket_factory == null) { + socket = Util.createSocket(proxy_host, proxy_port, timeout); + // socket=new Socket(proxy_host, proxy_port); + in = socket.getInputStream(); + out = socket.getOutputStream(); + } else { + socket = socket_factory.createSocket(proxy_host, proxy_port); + in = socket_factory.getInputStream(socket); + out = socket_factory.getOutputStream(socket); + } + if (timeout > 0) { + socket.setSoTimeout(timeout); + } + socket.setTcpNoDelay(true); + + byte[] buf = new byte[1024]; + int index = 0; + + /* + * 1) CONNECT + * + * The client connects to the SOCKS server and sends a CONNECT request when it wants to + * establish a connection to an application server. The client includes in the request packet + * the IP address and the port number of the destination host, and userid, in the following + * format. + * + * +----+----+----+----+----+----+----+----+----+----+....+----+ | VN | CD | DSTPORT | DSTIP | + * USERID |NULL| +----+----+----+----+----+----+----+----+----+----+....+----+ # of bytes: 1 1 + * 2 4 variable 1 + * + * VN is the SOCKS protocol version number and should be 4. CD is the SOCKS command code and + * should be 1 for CONNECT request. NULL is a byte of all zero bits. + */ + + index = 0; + buf[index++] = 4; + buf[index++] = 1; + + buf[index++] = (byte) (port >>> 8); + buf[index++] = (byte) (port & 0xff); + + try { + InetAddress addr = InetAddress.getByName(host); + byte[] byteAddress = addr.getAddress(); + for (int i = 0; i < byteAddress.length; i++) { + buf[index++] = byteAddress[i]; + } + } catch (UnknownHostException uhe) { + throw new JSchProxyException("ProxySOCKS4: " + uhe.toString(), uhe); + } + + if (user != null) { + System.arraycopy(Util.str2byte(user), 0, buf, index, user.length()); + index += user.length(); + } + buf[index++] = 0; + out.write(buf, 0, index); + + /* + * The SOCKS server checks to see whether such a request should be granted based on any + * combination of source IP address, destination IP address, destination port number, the + * userid, and information it may obtain by consulting IDENT, cf. RFC 1413. If the request is + * granted, the SOCKS server makes a connection to the specified port of the destination host. + * A reply packet is sent to the client when this connection is established, or when the + * request is rejected or the operation fails. + * + * +----+----+----+----+----+----+----+----+ | VN | CD | DSTPORT | DSTIP | + * +----+----+----+----+----+----+----+----+ # of bytes: 1 1 2 4 + * + * VN is the version of the reply code and should be 0. CD is the result code with one of the + * following values: + * + * 90: request granted 91: request rejected or failed 92: request rejected becasue SOCKS + * server cannot connect to identd on the client 93: request rejected because the client + * program and identd report different user-ids + * + * The remaining fields are ignored. + */ + + int len = 8; + int s = 0; + while (s < len) { + int i = in.read(buf, s, len - s); + if (i <= 0) { + throw new JSchProxyException("ProxySOCKS4: stream is closed"); + } + s += i; + } + if (buf[0] != 0) { + throw new JSchProxyException("ProxySOCKS4: server returns VN " + buf[0]); + } + if (buf[1] != 90) { + try { + socket.close(); + } catch (Exception eee) { + } + String message = "ProxySOCKS4: server returns CD " + buf[1]; + throw new JSchProxyException(message); + } + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + try { + if (socket != null) + socket.close(); + } catch (Exception eee) { + } + throw new JSchProxyException("ProxySOCKS4: " + e.toString(), e); + } + } + + @Override + public InputStream getInputStream() { + return in; + } + + @Override + public OutputStream getOutputStream() { + return out; + } + + @Override + public Socket getSocket() { + return socket; + } + + @Override + public void close() { + try { + if (in != null) + in.close(); + if (out != null) + out.close(); + if (socket != null) + socket.close(); + } catch (Exception e) { + } + in = null; + out = null; + socket = null; + } + + public static int getDefaultPort() { + return DEFAULTPORT; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ProxySOCKS5.java b/files-jsch/src/main/java/com/jcraft/jsch/ProxySOCKS5.java new file mode 100644 index 0000000..ea6df01 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ProxySOCKS5.java @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file depends on following documents, - RFC 1928 SOCKS Protocol Verseion 5 - RFC 1929 + * Username/Password Authentication for SOCKS V5. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +public class ProxySOCKS5 implements Proxy { + private static int DEFAULTPORT = 1080; + private String proxy_host; + private int proxy_port; + private InputStream in; + private OutputStream out; + private Socket socket; + private String user; + private String passwd; + + public ProxySOCKS5(String proxy_host) { + int port = DEFAULTPORT; + String host = proxy_host; + if (proxy_host.indexOf(':') != -1) { + try { + host = proxy_host.substring(0, proxy_host.indexOf(':')); + port = Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':') + 1)); + } catch (Exception e) { + } + } + this.proxy_host = host; + this.proxy_port = port; + } + + public ProxySOCKS5(String proxy_host, int proxy_port) { + this.proxy_host = proxy_host; + this.proxy_port = proxy_port; + } + + public void setUserPasswd(String user, String passwd) { + this.user = user; + this.passwd = passwd; + } + + @Override + public void connect(SocketFactory socket_factory, String host, int port, int timeout) + throws JSchException { + try { + if (socket_factory == null) { + socket = Util.createSocket(proxy_host, proxy_port, timeout); + // socket=new Socket(proxy_host, proxy_port); + in = socket.getInputStream(); + out = socket.getOutputStream(); + } else { + socket = socket_factory.createSocket(proxy_host, proxy_port); + in = socket_factory.getInputStream(socket); + out = socket_factory.getOutputStream(socket); + } + if (timeout > 0) { + socket.setSoTimeout(timeout); + } + socket.setTcpNoDelay(true); + + byte[] buf = new byte[1024]; + int index = 0; + + /* + * +----+----------+----------+ |VER | NMETHODS | METHODS | +----+----------+----------+ | 1 | + * 1 | 1 to 255 | +----+----------+----------+ + * + * The VER field is set to X'05' for this version of the protocol. The NMETHODS field contains + * the number of method identifier octets that appear in the METHODS field. + * + * The values currently defined for METHOD are: + * + * o X'00' NO AUTHENTICATION REQUIRED o X'01' GSSAPI o X'02' USERNAME/PASSWORD o X'03' to + * X'7F' IANA ASSIGNED o X'80' to X'FE' RESERVED FOR PRIVATE METHODS o X'FF' NO ACCEPTABLE + * METHODS + */ + + buf[index++] = 5; + + buf[index++] = 2; + buf[index++] = 0; // NO AUTHENTICATION REQUIRED + buf[index++] = 2; // USERNAME/PASSWORD + + out.write(buf, 0, index); + + /* + * The server selects from one of the methods given in METHODS, and sends a METHOD selection + * message: + * + * +----+--------+ |VER | METHOD | +----+--------+ | 1 | 1 | +----+--------+ + */ + // in.read(buf, 0, 2); + fill(in, buf, 2); + + boolean check = false; + switch ((buf[1]) & 0xff) { + case 0: // NO AUTHENTICATION REQUIRED + check = true; + break; + case 2: // USERNAME/PASSWORD + if (user == null || passwd == null) + break; + + /* + * Once the SOCKS V5 server has started, and the client has selected the Username/Password + * Authentication protocol, the Username/Password subnegotiation begins. This begins with + * the client producing a Username/Password request: + * + * +----+------+----------+------+----------+ |VER | ULEN | UNAME | PLEN | PASSWD | + * +----+------+----------+------+----------+ | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + * +----+------+----------+------+----------+ + * + * The VER field contains the current version of the subnegotiation, which is X'01'. The + * ULEN field contains the length of the UNAME field that follows. The UNAME field + * contains the username as known to the source operating system. The PLEN field contains + * the length of the PASSWD field that follows. The PASSWD field contains the password + * association with the given UNAME. + */ + index = 0; + buf[index++] = 1; + buf[index++] = (byte) (user.length()); + System.arraycopy(Util.str2byte(user), 0, buf, index, user.length()); + index += user.length(); + buf[index++] = (byte) (passwd.length()); + System.arraycopy(Util.str2byte(passwd), 0, buf, index, passwd.length()); + index += passwd.length(); + + out.write(buf, 0, index); + + /* + * The server verifies the supplied UNAME and PASSWD, and sends the following response: + * + * +----+--------+ |VER | STATUS | +----+--------+ | 1 | 1 | +----+--------+ + * + * A STATUS field of X'00' indicates success. If the server returns a `failure' (STATUS + * value other than X'00') status, it MUST close the connection. + */ + // in.read(buf, 0, 2); + fill(in, buf, 2); + if (buf[1] == 0) + check = true; + break; + default: + } + + if (!check) { + try { + socket.close(); + } catch (Exception eee) { + } + throw new JSchProxyException("fail in SOCKS5 proxy"); + } + + /* + * The SOCKS request is formed as follows: + * + * +----+-----+-------+------+----------+----------+ |VER | CMD | RSV | ATYP | DST.ADDR | + * DST.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable + * | 2 | +----+-----+-------+------+----------+----------+ + * + * Where: + * + * o VER protocol version: X'05' o CMD o CONNECT X'01' o BIND X'02' o UDP ASSOCIATE X'03' o + * RSV RESERVED o ATYP address type of following address o IP V4 address: X'01' o DOMAINNAME: + * X'03' o IP V6 address: X'04' o DST.ADDR desired destination address o DST.PORT desired + * destination port in network octet order + */ + + index = 0; + buf[index++] = 5; + buf[index++] = 1; // CONNECT + buf[index++] = 0; + + byte[] hostb = Util.str2byte(host); + int len = hostb.length; + buf[index++] = 3; // DOMAINNAME + buf[index++] = (byte) (len); + System.arraycopy(hostb, 0, buf, index, len); + index += len; + buf[index++] = (byte) (port >>> 8); + buf[index++] = (byte) (port & 0xff); + + out.write(buf, 0, index); + + /* + * The SOCKS request information is sent by the client as soon as it has established a + * connection to the SOCKS server, and completed the authentication negotiations. The server + * evaluates the request, and returns a reply formed as follows: + * + * +----+-----+-------+------+----------+----------+ |VER | REP | RSV | ATYP | BND.ADDR | + * BND.PORT | +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00' | 1 | Variable + * | 2 | +----+-----+-------+------+----------+----------+ + * + * Where: + * + * o VER protocol version: X'05' o REP Reply field: o X'00' succeeded o X'01' general SOCKS + * server failure o X'02' connection not allowed by ruleset o X'03' Network unreachable o + * X'04' Host unreachable o X'05' Connection refused o X'06' TTL expired o X'07' Command not + * supported o X'08' Address type not supported o X'09' to X'FF' unassigned o RSV RESERVED o + * ATYP address type of following address o IP V4 address: X'01' o DOMAINNAME: X'03' o IP V6 + * address: X'04' o BND.ADDR server bound address o BND.PORT server bound port in network + * octet order + */ + + // in.read(buf, 0, 4); + fill(in, buf, 4); + + if (buf[1] != 0) { + try { + socket.close(); + } catch (Exception eee) { + } + throw new JSchProxyException("ProxySOCKS5: server returns " + buf[1]); + } + + switch (buf[3] & 0xff) { + case 1: + // in.read(buf, 0, 6); + fill(in, buf, 6); + break; + case 3: + // in.read(buf, 0, 1); + fill(in, buf, 1); + // in.read(buf, 0, buf[0]+2); + fill(in, buf, (buf[0] & 0xff) + 2); + break; + case 4: + // in.read(buf, 0, 18); + fill(in, buf, 18); + break; + default: + } + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + try { + if (socket != null) + socket.close(); + } catch (Exception eee) { + } + String message = "ProxySOCKS5: " + e.toString(); + throw new JSchProxyException(message, e); + } + } + + @Override + public InputStream getInputStream() { + return in; + } + + @Override + public OutputStream getOutputStream() { + return out; + } + + @Override + public Socket getSocket() { + return socket; + } + + @Override + public void close() { + try { + if (in != null) + in.close(); + if (out != null) + out.close(); + if (socket != null) + socket.close(); + } catch (Exception e) { + } + in = null; + out = null; + socket = null; + } + + public static int getDefaultPort() { + return DEFAULTPORT; + } + + private void fill(InputStream in, byte[] buf, int len) throws JSchException, IOException { + int s = 0; + while (s < len) { + int i = in.read(buf, s, len - s); + if (i <= 0) { + throw new JSchProxyException("ProxySOCKS5: stream is closed"); + } + s += i; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Random.java b/files-jsch/src/main/java/com/jcraft/jsch/Random.java new file mode 100644 index 0000000..cfa5235 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Random.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface Random { + void fill(byte[] foo, int start, int len); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Request.java b/files-jsch/src/main/java/com/jcraft/jsch/Request.java new file mode 100644 index 0000000..e4c7b95 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Request.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +abstract class Request { + private boolean reply = false; + private Session session = null; + private Channel channel = null; + + void request(Session session, Channel channel) throws Exception { + this.session = session; + this.channel = channel; + if (channel.connectTimeout > 0) { + setReply(true); + } + } + + boolean waitForReply() { + return reply; + } + + void setReply(boolean reply) { + this.reply = reply; + } + + void write(Packet packet) throws Exception { + if (reply) { + channel.reply = -1; + } + session.write(packet); + if (reply) { + long start = System.currentTimeMillis(); + long timeout = channel.connectTimeout; + while (channel.isConnected() && channel.reply == -1) { + try { + Thread.sleep(10); + } catch (Exception ee) { + } + if (timeout > 0L && (System.currentTimeMillis() - start) > timeout) { + channel.reply = 0; + throw new JSchException("channel request: timeout"); + } + } + + if (channel.reply == 0) { + throw new JSchException("failed to send channel request"); + } + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/RequestAgentForwarding.java b/files-jsch/src/main/java/com/jcraft/jsch/RequestAgentForwarding.java new file mode 100644 index 0000000..c2bb799 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/RequestAgentForwarding.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class RequestAgentForwarding extends Request { + @Override + public void request(Session session, Channel channel) throws Exception { + super.request(session, channel); + + setReply(false); + + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "auth-agent-req@openssh.com" + // boolean want reply // 0 + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("auth-agent-req@openssh.com")); + buf.putByte((byte) (waitForReply() ? 1 : 0)); + write(packet); + session.agent_forwarding = true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/RequestEnv.java b/files-jsch/src/main/java/com/jcraft/jsch/RequestEnv.java new file mode 100644 index 0000000..372f04a --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/RequestEnv.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class RequestEnv extends Request { + byte[] name = new byte[0]; + byte[] value = new byte[0]; + + void setEnv(byte[] name, byte[] value) { + this.name = name; + this.value = value; + } + + @Override + public void request(Session session, Channel channel) throws Exception { + super.request(session, channel); + setReply(false); + + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("env")); + buf.putByte((byte) 0); + buf.putString(name); + buf.putString(value); + write(packet); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/RequestExec.java b/files-jsch/src/main/java/com/jcraft/jsch/RequestExec.java new file mode 100644 index 0000000..4803955 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/RequestExec.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class RequestExec extends Request { + private byte[] command = new byte[0]; + + RequestExec(byte[] command) { + this.command = command; + } + + @Override + public void request(Session session, Channel channel) throws Exception { + super.request(session, channel); + + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + + // send + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "exec" + // boolean want reply // 0 + // string command + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("exec")); + buf.putByte((byte) (waitForReply() ? 1 : 0)); + buf.checkFreeSize(4 + command.length); + buf.putString(command); + write(packet); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/RequestPtyReq.java b/files-jsch/src/main/java/com/jcraft/jsch/RequestPtyReq.java new file mode 100644 index 0000000..c9a641c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/RequestPtyReq.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class RequestPtyReq extends Request { + private String ttype = "vt100"; + private int tcol = 80; + private int trow = 24; + private int twp = 640; + private int thp = 480; + + private byte[] terminal_mode = Util.empty; + + void setCode(String cookie) {} + + void setTType(String ttype) { + this.ttype = ttype; + } + + void setTerminalMode(byte[] terminal_mode) { + this.terminal_mode = terminal_mode; + } + + void setTSize(int tcol, int trow, int twp, int thp) { + this.tcol = tcol; + this.trow = trow; + this.twp = twp; + this.thp = thp; + } + + @Override + public void request(Session session, Channel channel) throws Exception { + super.request(session, channel); + + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("pty-req")); + buf.putByte((byte) (waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte(ttype)); + buf.putInt(tcol); + buf.putInt(trow); + buf.putInt(twp); + buf.putInt(thp); + buf.putString(terminal_mode); + write(packet); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/RequestSftp.java b/files-jsch/src/main/java/com/jcraft/jsch/RequestSftp.java new file mode 100644 index 0000000..47ef4af --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/RequestSftp.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class RequestSftp extends Request { + RequestSftp() { + setReply(true); + } + + @Override + public void request(Session session, Channel channel) throws Exception { + super.request(session, channel); + + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("subsystem")); + buf.putByte((byte) (waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte("sftp")); + write(packet); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/RequestShell.java b/files-jsch/src/main/java/com/jcraft/jsch/RequestShell.java new file mode 100644 index 0000000..408880b --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/RequestShell.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class RequestShell extends Request { + @Override + public void request(Session session, Channel channel) throws Exception { + super.request(session, channel); + + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + + // send + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "shell" + // boolean want reply // 0 + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("shell")); + buf.putByte((byte) (waitForReply() ? 1 : 0)); + write(packet); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/RequestSignal.java b/files-jsch/src/main/java/com/jcraft/jsch/RequestSignal.java new file mode 100644 index 0000000..9e4e424 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/RequestSignal.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class RequestSignal extends Request { + private String signal = "KILL"; + + public void setSignal(String foo) { + signal = foo; + } + + @Override + public void request(Session session, Channel channel) throws Exception { + super.request(session, channel); + + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("signal")); + buf.putByte((byte) (waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte(signal)); + write(packet); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/RequestSubsystem.java b/files-jsch/src/main/java/com/jcraft/jsch/RequestSubsystem.java new file mode 100644 index 0000000..9f83f1c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/RequestSubsystem.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class RequestSubsystem extends Request { + private String subsystem = null; + + public void request(Session session, Channel channel, String subsystem, boolean want_reply) + throws Exception { + setReply(want_reply); + this.subsystem = subsystem; + this.request(session, channel); + } + + @Override + public void request(Session session, Channel channel) throws Exception { + super.request(session, channel); + + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("subsystem")); + buf.putByte((byte) (waitForReply() ? 1 : 0)); + buf.putString(Util.str2byte(subsystem)); + write(packet); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/RequestWindowChange.java b/files-jsch/src/main/java/com/jcraft/jsch/RequestWindowChange.java new file mode 100644 index 0000000..6f83c80 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/RequestWindowChange.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class RequestWindowChange extends Request { + int width_columns = 80; + int height_rows = 24; + int width_pixels = 640; + int height_pixels = 480; + + void setSize(int col, int row, int wp, int hp) { + this.width_columns = col; + this.height_rows = row; + this.width_pixels = wp; + this.height_pixels = hp; + } + + @Override + public void request(Session session, Channel channel) throws Exception { + super.request(session, channel); + + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + + // byte SSH_MSG_CHANNEL_REQUEST + // uint32 recipient_channel + // string "window-change" + // boolean FALSE + // uint32 terminal width, columns + // uint32 terminal height, rows + // uint32 terminal width, pixels + // uint32 terminal height, pixels + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("window-change")); + buf.putByte((byte) (waitForReply() ? 1 : 0)); + buf.putInt(width_columns); + buf.putInt(height_rows); + buf.putInt(width_pixels); + buf.putInt(height_pixels); + write(packet); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/RequestX11.java b/files-jsch/src/main/java/com/jcraft/jsch/RequestX11.java new file mode 100644 index 0000000..f908010 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/RequestX11.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class RequestX11 extends Request { + public void setCookie(String cookie) { + ChannelX11.cookie = Util.str2byte(cookie); + } + + @Override + public void request(Session session, Channel channel) throws Exception { + super.request(session, channel); + + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + + // byte SSH_MSG_CHANNEL_REQUEST(98) + // uint32 recipient channel + // string request type // "x11-req" + // boolean want reply // 0 + // boolean single connection + // string x11 authentication protocol // "MIT-MAGIC-COOKIE-1". + // string x11 authentication cookie + // uint32 x11 screen number + packet.reset(); + buf.putByte((byte) Session.SSH_MSG_CHANNEL_REQUEST); + buf.putInt(channel.getRecipient()); + buf.putString(Util.str2byte("x11-req")); + buf.putByte((byte) (waitForReply() ? 1 : 0)); + buf.putByte((byte) 0); + buf.putString(Util.str2byte("MIT-MAGIC-COOKIE-1")); + buf.putString(ChannelX11.getFakedCookie(session)); + buf.putInt(0); + write(packet); + + session.x11_forwarding = true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SCrypt.java b/files-jsch/src/main/java/com/jcraft/jsch/SCrypt.java new file mode 100644 index 0000000..a107b61 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SCrypt.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface SCrypt extends KDF { + void init(byte[] salt, int cost, int blocksize, int parallel) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SSHAgentConnector.java b/files-jsch/src/main/java/com/jcraft/jsch/SSHAgentConnector.java new file mode 100644 index 0000000..24ca099 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SSHAgentConnector.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class SSHAgentConnector implements AgentConnector { + private static final int MAX_AGENT_REPLY_LEN = 256 * 1024; + + private USocketFactory factory; + private Path usocketPath; + + public SSHAgentConnector() throws AgentProxyException { + this(getUSocketFactory(), getSshAuthSocket()); + } + + public SSHAgentConnector(Path usocketPath) throws AgentProxyException { + this(getUSocketFactory(), usocketPath); + } + + public SSHAgentConnector(USocketFactory factory) throws AgentProxyException { + this(factory, getSshAuthSocket()); + } + + public SSHAgentConnector(USocketFactory factory, Path usocketPath) { + this.factory = factory; + this.usocketPath = usocketPath; + } + + @Override + public String getName() { + return "ssh-agent"; + } + + @Override + @SuppressWarnings("try") + public boolean isAvailable() { + try (SocketChannel foo = open()) { + return true; + } catch (IOException e) { + return false; + } + } + + private SocketChannel open() throws IOException { + return factory.connect(usocketPath); + } + + @Override + public void query(Buffer buffer) throws AgentProxyException { + try (SocketChannel sock = open()) { + writeFull(sock, buffer, 0, buffer.getLength()); + buffer.rewind(); + int i = readFull(sock, buffer, 0, 4); // length + i = buffer.getInt(); + if (i <= 0 || i > MAX_AGENT_REPLY_LEN) { + throw new AgentProxyException("Illegal length: " + i); + } + buffer.rewind(); + buffer.checkFreeSize(i); + i = readFull(sock, buffer, 0, i); + } catch (IOException e) { + throw new AgentProxyException(e.toString(), e); + } + } + + private static USocketFactory getUSocketFactory() throws AgentProxyException { + try { + return new UnixDomainSocketFactory(); + } catch (AgentProxyException e) { + throw e; + } + } + + private static Path getSshAuthSocket() throws AgentProxyException { + String ssh_auth_sock = Util.getSystemEnv("SSH_AUTH_SOCK"); + if (ssh_auth_sock == null) { + throw new AgentProxyException("SSH_AUTH_SOCK is not defined."); + } + return Paths.get(ssh_auth_sock); + } + + private static int readFull(SocketChannel sock, Buffer buffer, int s, int len) + throws IOException { + ByteBuffer bb = ByteBuffer.wrap(buffer.buffer, s, len); + int _len = len; + while (len > 0) { + int j = sock.read(bb); + if (j < 0) + return -1; + if (j > 0) { + len -= j; + } + } + return _len; + } + + private static int writeFull(SocketChannel sock, Buffer buffer, int s, int len) + throws IOException { + ByteBuffer bb = ByteBuffer.wrap(buffer.buffer, s, len); + int _len = len; + while (len > 0) { + int j = sock.write(bb); + if (j < 0) + return -1; + if (j > 0) { + len -= j; + } + } + return _len; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/ServerSocketFactory.java b/files-jsch/src/main/java/com/jcraft/jsch/ServerSocketFactory.java new file mode 100644 index 0000000..c60f64f --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/ServerSocketFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; + +public interface ServerSocketFactory { + public ServerSocket createServerSocket(int port, int backlog, InetAddress bindAddr) + throws IOException; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Session.java b/files-jsch/src/main/java/com/jcraft/jsch/Session.java new file mode 100644 index 0000000..8c348b4 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Session.java @@ -0,0 +1,3394 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.Vector; +import javax.crypto.AEADBadTagException; + +public class Session { + + // http://ietf.org/internet-drafts/draft-ietf-secsh-assignednumbers-01.txt + static final int SSH_MSG_DISCONNECT = 1; + static final int SSH_MSG_IGNORE = 2; + static final int SSH_MSG_UNIMPLEMENTED = 3; + static final int SSH_MSG_DEBUG = 4; + static final int SSH_MSG_SERVICE_REQUEST = 5; + static final int SSH_MSG_SERVICE_ACCEPT = 6; + static final int SSH_MSG_EXT_INFO = 7; + static final int SSH_MSG_KEXINIT = 20; + static final int SSH_MSG_NEWKEYS = 21; + static final int SSH_MSG_KEXDH_INIT = 30; + static final int SSH_MSG_KEXDH_REPLY = 31; + static final int SSH_MSG_KEX_DH_GEX_GROUP = 31; + static final int SSH_MSG_KEX_DH_GEX_INIT = 32; + static final int SSH_MSG_KEX_DH_GEX_REPLY = 33; + static final int SSH_MSG_KEX_DH_GEX_REQUEST = 34; + static final int SSH_MSG_GLOBAL_REQUEST = 80; + static final int SSH_MSG_REQUEST_SUCCESS = 81; + static final int SSH_MSG_REQUEST_FAILURE = 82; + static final int SSH_MSG_CHANNEL_OPEN = 90; + static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91; + static final int SSH_MSG_CHANNEL_OPEN_FAILURE = 92; + static final int SSH_MSG_CHANNEL_WINDOW_ADJUST = 93; + static final int SSH_MSG_CHANNEL_DATA = 94; + static final int SSH_MSG_CHANNEL_EXTENDED_DATA = 95; + static final int SSH_MSG_CHANNEL_EOF = 96; + static final int SSH_MSG_CHANNEL_CLOSE = 97; + static final int SSH_MSG_CHANNEL_REQUEST = 98; + static final int SSH_MSG_CHANNEL_SUCCESS = 99; + static final int SSH_MSG_CHANNEL_FAILURE = 100; + + private static final int PACKET_MAX_SIZE = 256 * 1024; + + private byte[] V_S; // server version + private byte[] V_C = Util.str2byte("SSH-2.0-JSCH_" + JSch.VERSION); // client version + + private byte[] I_C; // the payload of the client's SSH_MSG_KEXINIT + private byte[] I_S; // the payload of the server's SSH_MSG_KEXINIT + private byte[] K_S; // the host key + + private byte[] session_id; + + private byte[] IVc2s; + private byte[] IVs2c; + private byte[] Ec2s; + private byte[] Es2c; + private byte[] MACc2s; + private byte[] MACs2c; + + // RFC 4253 6.4. each direction must run independently hence we have an incoming and outgoing + // sequence number + private int seqi = 0; + private int seqo = 0; + + String[] guess = null; + private Cipher s2ccipher; + private Cipher c2scipher; + private MAC s2cmac; + private MAC c2smac; + // private byte[] mac_buf; + private byte[] s2cmac_result1; + private byte[] s2cmac_result2; + + private Compression deflater; + private Compression inflater; + + private IO io; + private Socket socket; + private int timeout = 0; + + private volatile boolean isConnected = false; + + private volatile boolean doExtInfo = false; + private boolean enable_server_sig_algs = true; + private boolean enable_ext_info_in_auth = true; + + private volatile boolean initialKex = true; + private volatile boolean doStrictKex = false; + private boolean enable_strict_kex = true; + private boolean require_strict_kex = false; + + private volatile boolean isAuthed = false; + + private Thread connectThread = null; + private Object lock = new Object(); + + boolean x11_forwarding = false; + boolean agent_forwarding = false; + + InputStream in = null; + OutputStream out = null; + + static Random random; + + Buffer buf; + Packet packet; + + SocketFactory socket_factory = null; + + private Hashtable config = null; + + private Proxy proxy = null; + private UserInfo userinfo; + + private String hostKeyAlias = null; + private int serverAliveInterval = 0; + private int serverAliveCountMax = 1; + + private IdentityRepository identityRepository = null; + private HostKeyRepository hostkeyRepository = null; + private volatile String[] serverSigAlgs = null; + private volatile boolean sshBugSigType74 = false; + + protected boolean daemon_thread = false; + + private long kex_start_time = 0L; + + int max_auth_tries = 6; + int auth_failures = 0; + + String host = "127.0.0.1"; + String org_host = "127.0.0.1"; + int port = 22; + + String username = null; + byte[] password = null; + + JSch jsch; + Logger logger; + + Session(JSch jsch, String username, String host, int port) throws JSchException { + super(); + this.jsch = jsch; + buf = new Buffer(); + packet = new Packet(buf); + this.username = username; + this.org_host = this.host = host; + this.port = port; + + applyConfig(); + + if (this.username == null) { + this.username = Util.getSystemProperty("user.name"); + } + + if (this.username == null) { + throw new JSchException("username is not given."); + } + } + + public void connect() throws JSchException { + connect(timeout); + } + + public void connect(int connectTimeout) throws JSchException { + if (isConnected) { + throw new JSchException("session is already connected"); + } + initialKex = true; + + io = new IO(); + if (random == null) { + try { + Class c = Class.forName(getConfig("random")).asSubclass(Random.class); + random = c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + } + Packet.setRandom(random); + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "Connecting to " + host + " port " + port); + } + + try { + int i, j; + + if (proxy == null) { + InputStream in; + OutputStream out; + if (socket_factory == null) { + socket = Util.createSocket(host, port, connectTimeout); + in = socket.getInputStream(); + out = socket.getOutputStream(); + } else { + socket = socket_factory.createSocket(host, port); + in = socket_factory.getInputStream(socket); + out = socket_factory.getOutputStream(socket); + } + // if(timeout>0){ socket.setSoTimeout(timeout); } + socket.setTcpNoDelay(true); + io.setInputStream(in); + io.setOutputStream(out); + } else { + synchronized (proxy) { + proxy.connect(socket_factory, host, port, connectTimeout); + io.setInputStream(proxy.getInputStream()); + io.setOutputStream(proxy.getOutputStream()); + socket = proxy.getSocket(); + } + } + + if (connectTimeout > 0 && socket != null) { + socket.setSoTimeout(connectTimeout); + } + + isConnected = true; + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "Connection established"); + } + + jsch.addSession(this); + + { + // Some Cisco devices will miss to read '\n' if it is sent separately. + byte[] foo = new byte[V_C.length + 2]; + System.arraycopy(V_C, 0, foo, 0, V_C.length); + foo[foo.length - 2] = (byte) '\r'; + foo[foo.length - 1] = (byte) '\n'; + io.put(foo, 0, foo.length); + } + + while (true) { + i = 0; + j = 0; + while (i < buf.buffer.length) { + j = io.getByte(); + if (j < 0) + break; + buf.buffer[i] = (byte) j; + i++; + if (j == 10) + break; + } + if (j < 0) { + throw new JSchException("connection is closed by foreign host"); + } + + if (buf.buffer[i - 1] == 10) { // 0x0a + i--; + if (i > 0 && buf.buffer[i - 1] == 13) { // 0x0d + i--; + } + } + + if (i <= 3 || ((i != buf.buffer.length) && (buf.buffer[0] != 'S' || buf.buffer[1] != 'S' + || buf.buffer[2] != 'H' || buf.buffer[3] != '-'))) { + // It must not start with 'SSH-' + // System.err.println(new String(buf.buffer, 0, i); + continue; + } + + if (i == buf.buffer.length || i < 7 || // SSH-1.99 or SSH-2.0 + (buf.buffer[4] == '1' && buf.buffer[6] != '9') // SSH-1.5 + ) { + throw new JSchException("invalid server's version string"); + } + break; + } + + V_S = new byte[i]; + System.arraycopy(buf.buffer, 0, V_S, 0, i); + // System.err.println("V_S: ("+i+") ["+new String(V_S)+"]"); + String _v_s = Util.byte2str(V_S); + sshBugSigType74 = _v_s.startsWith("SSH-2.0-OpenSSH_7.4"); + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "Remote version string: " + _v_s); + getLogger().log(Logger.INFO, "Local version string: " + Util.byte2str(V_C)); + } + + enable_server_sig_algs = getConfig("enable_server_sig_algs").equals("yes"); + enable_ext_info_in_auth = getConfig("enable_ext_info_in_auth").equals("yes"); + enable_strict_kex = getConfig("enable_strict_kex").equals("yes"); + require_strict_kex = getConfig("require_strict_kex").equals("yes"); + send_kexinit(); + + buf = read(buf); + if (buf.getCommand() != SSH_MSG_KEXINIT) { + in_kex = false; + throw new JSchException("invalid protocol: " + buf.getCommand()); + } + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "SSH_MSG_KEXINIT received"); + } + + KeyExchange kex = receive_kexinit(buf); + + while (true) { + buf = read(buf); + if (kex.getState() == buf.getCommand()) { + kex_start_time = System.currentTimeMillis(); + boolean result = kex.next(buf); + if (!result) { + // System.err.println("verify: "+result); + in_kex = false; + throw new JSchException("verify: " + result); + } + } else { + in_kex = false; + throw new JSchException("invalid protocol(kex): " + buf.getCommand()); + } + if (kex.getState() == KeyExchange.STATE_END) { + break; + } + } + + try { + long tmp = System.currentTimeMillis(); + in_prompt = true; + checkHost(host, port, kex); + in_prompt = false; + kex_start_time += (System.currentTimeMillis() - tmp); + } catch (JSchException ee) { + in_kex = false; + in_prompt = false; + throw ee; + } + + send_newkeys(); + + // receive SSH_MSG_NEWKEYS(21) + buf = read(buf); + // System.err.println("read: 21 ? "+buf.getCommand()); + if (buf.getCommand() == SSH_MSG_NEWKEYS) { + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "SSH_MSG_NEWKEYS received"); + } + + receive_newkeys(buf, kex); + initialKex = false; + } else { + in_kex = false; + throw new JSchException("invalid protocol(newkeys): " + buf.getCommand()); + } + + if (enable_server_sig_algs && enable_ext_info_in_auth && doExtInfo) { + send_extinfo(); + } + + try { + String s = getConfig("MaxAuthTries"); + if (s != null) { + max_auth_tries = Integer.parseInt(s); + } + } catch (NumberFormatException e) { + throw new JSchException("MaxAuthTries: " + getConfig("MaxAuthTries"), e); + } + + boolean auth = false; + boolean auth_cancel = false; + + UserAuthNone uan = null; + try { + Class c = + Class.forName(getConfig("userauth.none")).asSubclass(UserAuthNone.class); + uan = c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + + auth = uan.start(this); + + String cmethods = getConfig("PreferredAuthentications"); + + String[] cmethoda = Util.split(cmethods, ","); + + String smethods = null; + if (!auth) { + smethods = uan.getMethods(); + if (smethods != null) { + smethods = smethods.toLowerCase(Locale.ROOT); + } else { + // methods: publickey,password,keyboard-interactive + // smethods = "publickey,password,keyboard-interactive"; + smethods = cmethods; + } + } + + String[] smethoda = Util.split(smethods, ","); + + int methodi = 0; + + loop: while (true) { + + while (!auth && cmethoda != null && methodi < cmethoda.length) { + + String method = cmethoda[methodi++]; + boolean acceptable = false; + for (int k = 0; k < smethoda.length; k++) { + if (smethoda[k].equals(method)) { + acceptable = true; + break; + } + } + if (!acceptable) { + continue; + } + + // System.err.println(" method: " + method); + + if (getLogger().isEnabled(Logger.INFO)) { + String str = "Authentications that can continue: "; + for (int k = methodi - 1; k < cmethoda.length; k++) { + str += cmethoda[k]; + if (k + 1 < cmethoda.length) + str += ","; + } + getLogger().log(Logger.INFO, str); + getLogger().log(Logger.INFO, "Next authentication method: " + method); + } + + UserAuth ua = null; + try { + Class c = null; + if (getConfig("userauth." + method) != null) { + c = Class.forName(getConfig("userauth." + method)).asSubclass(UserAuth.class); + ua = c.getDeclaredConstructor().newInstance(); + } + } catch (Exception e) { + if (getLogger().isEnabled(Logger.WARN)) { + getLogger().log(Logger.WARN, "failed to load " + method + " method"); + } + } + + if (ua != null) { + auth_cancel = false; + try { + auth = ua.start(this); + if (auth && getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "Authentication succeeded (" + method + ")."); + } + } catch (JSchAuthCancelException ee) { + auth_cancel = true; + } catch (JSchPartialAuthException ee) { + String tmp = smethods; + smethods = ee.getMethods(); + smethoda = Util.split(smethods, ","); + if (!tmp.equals(smethods)) { + methodi = 0; + } + // System.err.println("PartialAuth: " + methods); + auth_cancel = false; + continue loop; + } catch (RuntimeException ee) { + throw ee; + } catch (JSchException ee) { + throw ee; + } catch (Exception ee) { + // SSH_MSG_DISCONNECT: Too many authentication failures + // System.err.println("ee: " + ee); + if (getLogger().isEnabled(Logger.WARN)) { + getLogger().log(Logger.WARN, + "an exception during authentication\n" + ee.toString()); + } + break loop; + } + } + } + break; + } + + if (!auth) { + if (auth_failures >= max_auth_tries) { + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "Login trials exceeds " + max_auth_tries); + } + } + throw new JSchException( + (auth_cancel ? "Auth cancel" : "Auth fail") + " for methods '" + smethods + "'"); + } + + if (socket != null && (connectTimeout > 0 || timeout > 0)) { + socket.setSoTimeout(timeout); + } + + isAuthed = true; + + synchronized (lock) { + if (isConnected) { + connectThread = new Thread(this::run); + connectThread.setName("Connect thread " + host + " session"); + if (daemon_thread) { + connectThread.setDaemon(daemon_thread); + } + connectThread.start(); + + requestPortForwarding(); + } else { + // The session has been already down and + // we don't have to start new thread. + } + } + } catch (Exception e) { + in_kex = false; + try { + if (isConnected) { + String message = e.toString(); + packet.reset(); + buf.checkFreeSize(1 + 4 * 3 + message.length() + 2 + getBufferMargin()); + buf.putByte((byte) SSH_MSG_DISCONNECT); + buf.putInt(3); + buf.putString(Util.str2byte(message)); + buf.putString(Util.str2byte("en")); + write(packet); + } + } catch (Exception ee) { + } + try { + disconnect(); + } catch (Exception ee) { + } + isConnected = false; + // e.printStackTrace(); + if (e instanceof RuntimeException) + throw (RuntimeException) e; + if (e instanceof JSchException) + throw (JSchException) e; + throw new JSchException("Session.connect: " + e, e); + } finally { + Util.bzero(this.password); + this.password = null; + } + } + + private KeyExchange receive_kexinit(Buffer buf) throws Exception { + int j = buf.getInt(); + if (j != buf.getLength()) { // packet was compressed and + buf.getByte(); // j is the size of deflated packet. + I_S = new byte[buf.index - 5]; + } else { + I_S = new byte[j - 1 - buf.getByte()]; + } + System.arraycopy(buf.buffer, buf.s, I_S, 0, I_S.length); + + if (initialKex) { + if (enable_strict_kex || require_strict_kex) { + doStrictKex = checkServerStrictKex(); + if (doStrictKex) { + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "Doing strict KEX"); + } + + if (seqi != 1) { + throw new JSchStrictKexException("KEXINIT not first packet from server"); + } + } else if (require_strict_kex) { + throw new JSchStrictKexException("Strict KEX not supported by server"); + } + } + + if (enable_server_sig_algs) { + doExtInfo = checkServerExtInfo(); + if (doExtInfo && getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "ext-info messaging supported by server"); + } + } + } + + if (!in_kex) { // We are in rekeying activated by the remote! + send_kexinit(); + } + + guess = KeyExchange.guess(this, I_S, I_C); + + if (guess[KeyExchange.PROPOSAL_KEX_ALGS].equals("ext-info-c") + || guess[KeyExchange.PROPOSAL_KEX_ALGS].equals("ext-info-s") + || guess[KeyExchange.PROPOSAL_KEX_ALGS].equals("kex-strict-c-v00@openssh.com") + || guess[KeyExchange.PROPOSAL_KEX_ALGS].equals("kex-strict-s-v00@openssh.com")) { + throw new JSchException("Invalid Kex negotiated: " + guess[KeyExchange.PROPOSAL_KEX_ALGS]); + } + + if (!isAuthed && (guess[KeyExchange.PROPOSAL_ENC_ALGS_CTOS].equals("none") + || (guess[KeyExchange.PROPOSAL_ENC_ALGS_STOC].equals("none")))) { + throw new JSchException( + "NONE Cipher should not be chosen before authentification is successed."); + } + + KeyExchange kex = null; + try { + Class c = Class + .forName(getConfig(guess[KeyExchange.PROPOSAL_KEX_ALGS])).asSubclass(KeyExchange.class); + kex = c.getDeclaredConstructor().newInstance(); + } catch (Exception | NoClassDefFoundError e) { + throw new JSchException(e.toString(), e); + } + + kex.doInit(this, V_S, V_C, I_S, I_C); + return kex; + } + + private boolean checkServerStrictKex() { + Buffer sb = new Buffer(I_S); + sb.setOffSet(17); + byte[] sp = sb.getString(); // server proposal + + int l = 0; + int m = 0; + while (l < sp.length) { + while (l < sp.length && sp[l] != ',') + l++; + if (m == l) + continue; + if ("kex-strict-s-v00@openssh.com".equals(Util.byte2str(sp, m, l - m))) { + return true; + } + l++; + m = l; + } + + return false; + } + + private boolean checkServerExtInfo() { + Buffer sb = new Buffer(I_S); + sb.setOffSet(17); + byte[] sp = sb.getString(); // server proposal + + int l = 0; + int m = 0; + while (l < sp.length) { + while (l < sp.length && sp[l] != ',') + l++; + if (m == l) + continue; + if ("ext-info-s".equals(Util.byte2str(sp, m, l - m))) { + return true; + } + l++; + m = l; + } + + return false; + } + + private volatile boolean in_kex = false; + private volatile boolean in_prompt = false; + private volatile String[] not_available_shks = null; + + public String[] getUnavailableSignatures() { + return not_available_shks; + } + + public void rekey() throws Exception { + send_kexinit(); + } + + private void send_kexinit() throws Exception { + if (in_kex) + return; + + String cipherc2s = getConfig("cipher.c2s"); + String ciphers2c = getConfig("cipher.s2c"); + String[] not_available_ciphers = checkCiphers(getConfig("CheckCiphers")); + if (not_available_ciphers != null && not_available_ciphers.length > 0) { + if (getLogger().isEnabled(Logger.DEBUG)) { + getLogger().log(Logger.DEBUG, + "cipher.c2s proposal before removing unavailable algos is: " + cipherc2s); + getLogger().log(Logger.DEBUG, + "cipher.s2c proposal before removing unavailable algos is: " + ciphers2c); + } + + cipherc2s = Util.diffString(cipherc2s, not_available_ciphers); + ciphers2c = Util.diffString(ciphers2c, not_available_ciphers); + if (cipherc2s == null || ciphers2c == null) { + throw new JSchException("There are not any available ciphers."); + } + + if (getLogger().isEnabled(Logger.DEBUG)) { + getLogger().log(Logger.DEBUG, + "cipher.c2s proposal after removing unavailable algos is: " + cipherc2s); + getLogger().log(Logger.DEBUG, + "cipher.s2c proposal after removing unavailable algos is: " + ciphers2c); + } + } + + String macc2s = getConfig("mac.c2s"); + String macs2c = getConfig("mac.s2c"); + String[] not_available_macs = checkMacs(getConfig("CheckMacs")); + if (not_available_macs != null && not_available_macs.length > 0) { + if (getLogger().isEnabled(Logger.DEBUG)) { + getLogger().log(Logger.DEBUG, + "mac.c2s proposal before removing unavailable algos is: " + macc2s); + getLogger().log(Logger.DEBUG, + "mac.s2c proposal before removing unavailable algos is: " + macs2c); + } + + macc2s = Util.diffString(macc2s, not_available_macs); + macs2c = Util.diffString(macs2c, not_available_macs); + if (macc2s == null || macs2c == null) { + throw new JSchException("There are not any available macs."); + } + + if (getLogger().isEnabled(Logger.DEBUG)) { + getLogger().log(Logger.DEBUG, + "mac.c2s proposal after removing unavailable algos is: " + macc2s); + getLogger().log(Logger.DEBUG, + "mac.s2c proposal after removing unavailable algos is: " + macs2c); + } + } + + String kex = getConfig("kex"); + String[] not_available_kexes = checkKexes(getConfig("CheckKexes")); + if (not_available_kexes != null && not_available_kexes.length > 0) { + if (getLogger().isEnabled(Logger.DEBUG)) { + getLogger().log(Logger.DEBUG, "kex proposal before removing unavailable algos is: " + kex); + } + + kex = Util.diffString(kex, not_available_kexes); + if (kex == null) { + throw new JSchException("There are not any available kexes."); + } + + if (getLogger().isEnabled(Logger.DEBUG)) { + getLogger().log(Logger.DEBUG, "kex proposal after removing unavailable algos is: " + kex); + } + } + + if (enable_server_sig_algs && !isAuthed) { + kex += ",ext-info-c"; + } + + if ((enable_strict_kex || require_strict_kex) && initialKex) { + kex += ",kex-strict-c-v00@openssh.com"; + } + + String server_host_key = getConfig("server_host_key"); + String[] not_available_shks = checkSignatures(getConfig("CheckSignatures")); + // Cache for UserAuthPublicKey + this.not_available_shks = not_available_shks; + if (not_available_shks != null && not_available_shks.length > 0) { + if (getLogger().isEnabled(Logger.DEBUG)) { + getLogger().log(Logger.DEBUG, + "server_host_key proposal before removing unavailable algos is: " + server_host_key); + } + + server_host_key = Util.diffString(server_host_key, not_available_shks); + if (server_host_key == null) { + throw new JSchException("There are not any available sig algorithm."); + } + + if (getLogger().isEnabled(Logger.DEBUG)) { + getLogger().log(Logger.DEBUG, + "server_host_key proposal after removing unavailable algos is: " + server_host_key); + } + } + + String prefer_hkr = getConfig("prefer_known_host_key_types"); + if (prefer_hkr.equals("yes")) { + if (getLogger().isEnabled(Logger.DEBUG)) { + getLogger().log(Logger.DEBUG, + "server_host_key proposal before known_host reordering is: " + server_host_key); + } + + HostKeyRepository hkr = getHostKeyRepository(); + String chost = host; + if (hostKeyAlias != null) { + chost = hostKeyAlias; + } + if (hostKeyAlias == null && port != 22) { + chost = ("[" + chost + "]:" + port); + } + HostKey[] hks = hkr.getHostKey(chost, null); + if (hks != null && hks.length > 0) { + List pref_shks = new ArrayList<>(); + List shks = new ArrayList<>(Arrays.asList(Util.split(server_host_key, ","))); + Iterator it = shks.iterator(); + while (it.hasNext()) { + String algo = it.next(); + String type = algo; + if (type.equals("rsa-sha2-256") || type.equals("rsa-sha2-512") + || type.equals("ssh-rsa-sha224@ssh.com") || type.equals("ssh-rsa-sha256@ssh.com") + || type.equals("ssh-rsa-sha384@ssh.com") || type.equals("ssh-rsa-sha512@ssh.com")) { + type = "ssh-rsa"; + } + for (HostKey hk : hks) { + if (hk.getType().equals(type)) { + pref_shks.add(algo); + it.remove(); + break; + } + } + } + if (pref_shks.size() > 0) { + pref_shks.addAll(shks); + server_host_key = String.join(",", pref_shks); + } + } + + if (getLogger().isEnabled(Logger.DEBUG)) { + getLogger().log(Logger.DEBUG, + "server_host_key proposal after known_host reordering is: " + server_host_key); + } + } + + in_kex = true; + kex_start_time = System.currentTimeMillis(); + + // byte SSH_MSG_KEXINIT(20) + // byte[16] cookie (random bytes) + // string kex_algorithms + // string server_host_key_algorithms + // string encryption_algorithms_client_to_server + // string encryption_algorithms_server_to_client + // string mac_algorithms_client_to_server + // string mac_algorithms_server_to_client + // string compression_algorithms_client_to_server + // string compression_algorithms_server_to_client + // string languages_client_to_server + // string languages_server_to_client + Buffer buf = new Buffer(); // send_kexinit may be invoked + Packet packet = new Packet(buf); // by user thread. + packet.reset(); + buf.putByte((byte) SSH_MSG_KEXINIT); + synchronized (random) { + random.fill(buf.buffer, buf.index, 16); + buf.skip(16); + } + buf.putString(Util.str2byte(kex)); + buf.putString(Util.str2byte(server_host_key)); + buf.putString(Util.str2byte(cipherc2s)); + buf.putString(Util.str2byte(ciphers2c)); + buf.putString(Util.str2byte(getConfig("mac.c2s"))); + buf.putString(Util.str2byte(getConfig("mac.s2c"))); + buf.putString(Util.str2byte(getConfig("compression.c2s"))); + buf.putString(Util.str2byte(getConfig("compression.s2c"))); + buf.putString(Util.str2byte(getConfig("lang.c2s"))); + buf.putString(Util.str2byte(getConfig("lang.s2c"))); + buf.putByte((byte) 0); + buf.putInt(0); + + buf.setOffSet(5); + I_C = new byte[buf.getLength()]; + buf.getByte(I_C); + + write(packet); + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "SSH_MSG_KEXINIT sent"); + } + } + + private void send_newkeys() throws Exception { + // send SSH_MSG_NEWKEYS(21) + packet.reset(); + buf.putByte((byte) SSH_MSG_NEWKEYS); + write(packet); + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "SSH_MSG_NEWKEYS sent"); + } + } + + private void send_extinfo() throws Exception { + // send SSH_MSG_EXT_INFO(7) + packet.reset(); + buf.putByte((byte) SSH_MSG_EXT_INFO); + buf.putInt(1); + buf.putString(Util.str2byte("ext-info-in-auth@openssh.com")); + buf.putString(Util.str2byte("0")); + write(packet); + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "SSH_MSG_EXT_INFO sent"); + } + } + + private void checkHost(String chost, int port, KeyExchange kex) throws JSchException { + String shkc = getConfig("StrictHostKeyChecking"); + + if (hostKeyAlias != null) { + chost = hostKeyAlias; + } + + // System.err.println("shkc: "+shkc); + + byte[] K_S = kex.getHostKey(); + String key_type = kex.getKeyType(); + String key_fprint = kex.getFingerPrint(); + + if (hostKeyAlias == null && port != 22) { + chost = ("[" + chost + "]:" + port); + } + + HostKeyRepository hkr = getHostKeyRepository(); + + String hkh = getConfig("HashKnownHosts"); + if (hkh.equals("yes") && (hkr instanceof KnownHosts)) { + hostkey = ((KnownHosts) hkr).createHashedHostKey(chost, K_S); + } else { + hostkey = new HostKey(chost, K_S); + } + + int i = 0; + synchronized (hkr) { + i = hkr.check(chost, K_S); + } + + boolean insert = false; + if ((shkc.equals("ask") || shkc.equals("yes")) && i == HostKeyRepository.CHANGED) { + String file = null; + synchronized (hkr) { + file = hkr.getKnownHostsRepositoryID(); + } + if (file == null) { + file = "known_hosts"; + } + + boolean b = false; + + if (userinfo != null) { + String message = "WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!\n" + + "IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n" + + "Someone could be eavesdropping on you right now (man-in-the-middle attack)!\n" + + "It is also possible that the " + key_type + " host key has just been changed.\n" + + "The fingerprint for the " + key_type + " key sent by the remote host " + chost + + " is\n" + key_fprint + ".\n" + "Please contact your system administrator.\n" + + "Add correct host key in " + file + " to get rid of this message."; + + if (shkc.equals("ask")) { + b = userinfo + .promptYesNo(message + "\nDo you want to delete the old key and insert the new key?"); + } else { // shkc.equals("yes") + userinfo.showMessage(message); + } + } + + if (!b) { + throw new JSchException("HostKey has been changed: " + chost); + } + + synchronized (hkr) { + hkr.remove(chost, kex.getKeyAlgorithName(), null); + insert = true; + } + } + + if ((shkc.equals("ask") || shkc.equals("yes")) && (i != HostKeyRepository.OK) && !insert) { + if (shkc.equals("yes")) { + throw new JSchException("reject HostKey: " + chost); + } + // System.err.println("finger-print: "+key_fprint); + if (userinfo != null) { + boolean foo = userinfo.promptYesNo("The authenticity of host '" + chost + + "' can't be established.\n" + key_type + " key fingerprint is " + key_fprint + ".\n" + + "Are you sure you want to continue connecting?"); + if (!foo) { + throw new JSchException("reject HostKey: " + chost); + } + insert = true; + } else { + if (i == HostKeyRepository.NOT_INCLUDED) + throw new JSchUnknownHostKeyException( + "UnknownHostKey: " + chost + ". " + key_type + " key fingerprint is " + key_fprint); + else + throw new JSchChangedHostKeyException("HostKey has been changed: " + chost); + } + } + + if (shkc.equals("no") && HostKeyRepository.NOT_INCLUDED == i) { + insert = true; + } + + if (i == HostKeyRepository.OK) { + HostKey[] keys = hkr.getHostKey(chost, kex.getKeyAlgorithName()); + String _key = Util.byte2str(Util.toBase64(K_S, 0, K_S.length, true)); + for (int j = 0; j < keys.length; j++) { + if (keys[j].getKey().equals(_key) && keys[j].getMarker().equals("@revoked")) { + if (userinfo != null) { + userinfo.showMessage("The " + key_type + " host key for " + chost + + " is marked as revoked.\n" + "This could mean that a stolen key is being used to " + + "impersonate this host."); + } + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "Host '" + chost + "' has provided revoked key."); + } + throw new JSchRevokedHostKeyException("revoked HostKey: " + chost); + } + } + } + + if (i == HostKeyRepository.OK && getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, + "Host '" + chost + "' is known and matches the " + key_type + " host key"); + } + + if (insert && getLogger().isEnabled(Logger.WARN)) { + getLogger().log(Logger.WARN, + "Permanently added '" + chost + "' (" + key_type + ") to the list of known hosts."); + } + + if (insert) { + synchronized (hkr) { + hkr.add(hostkey, userinfo); + } + } + } + + // public void start(){ (new Thread(this)).start(); } + + public Channel openChannel(String type) throws JSchException { + if (!isConnected) { + throw new JSchException("session is down"); + } + try { + Channel channel = Channel.getChannel(type, this); + addChannel(channel); + channel.init(); + if (channel instanceof ChannelSession) { + applyConfigChannel((ChannelSession) channel); + } + return channel; + } catch (Exception e) { + // e.printStackTrace(); + } + return null; + } + + // encode will bin invoked in write with synchronization. + void encode(Packet packet) throws Exception { + // System.err.println("encode: "+packet.buffer.getCommand()); + // System.err.println(" "+packet.buffer.index); + // if(packet.buffer.getCommand()==96){ + // Thread.dumpStack(); + // } + if (deflater != null) { + compress_len[0] = packet.buffer.index; + packet.buffer.buffer = deflater.compress(packet.buffer.buffer, 5, compress_len); + packet.buffer.index = compress_len[0]; + } + int bsize = 8; + if (c2scipher != null) { + // bsize=c2scipher.getIVSize(); + bsize = c2scipher_size; + } + boolean isChaCha20 = (c2scipher != null && c2scipher.isChaCha20()); + boolean isAEAD = (c2scipher != null && c2scipher.isAEAD()); + boolean isEtM = + (!isChaCha20 && !isAEAD && c2scipher != null && c2smac != null && c2smac.isEtM()); + packet.padding(bsize, !(isChaCha20 || isAEAD || isEtM)); + + byte[] buf = packet.buffer.buffer; + if (isChaCha20) { + // init cipher with seq number + c2scipher.update(seqo); + // encrypt packet length field + c2scipher.update(buf, 0, 4, buf, 0); + // encrypt rest of packet & add tag + c2scipher.doFinal(buf, 0, packet.buffer.index, buf, 0); + packet.buffer.skip(c2scipher.getTagSize()); + } else if (isAEAD) { + c2scipher.updateAAD(buf, 0, 4); + c2scipher.doFinal(buf, 4, packet.buffer.index - 4, buf, 4); + packet.buffer.skip(c2scipher.getTagSize()); + } else if (isEtM) { + c2scipher.update(buf, 4, packet.buffer.index - 4, buf, 4); + c2smac.update(seqo); + c2smac.update(packet.buffer.buffer, 0, packet.buffer.index); + c2smac.doFinal(packet.buffer.buffer, packet.buffer.index); + packet.buffer.skip(c2smac.getBlockSize()); + } else { + if (c2smac != null) { + c2smac.update(seqo); + c2smac.update(packet.buffer.buffer, 0, packet.buffer.index); + c2smac.doFinal(packet.buffer.buffer, packet.buffer.index); + } + if (c2scipher != null) { + c2scipher.update(buf, 0, packet.buffer.index, buf, 0); + } + if (c2smac != null) { + packet.buffer.skip(c2smac.getBlockSize()); + } + } + } + + int[] uncompress_len = new int[1]; + int[] compress_len = new int[1]; + + private int s2ccipher_size = 8; + private int c2scipher_size = 8; + + Buffer read(Buffer buf) throws Exception { + int j = 0; + boolean isChaCha20 = (s2ccipher != null && s2ccipher.isChaCha20()); + boolean isAEAD = (s2ccipher != null && s2ccipher.isAEAD()); + boolean isEtM = + (!isChaCha20 && !isAEAD && s2ccipher != null && s2cmac != null && s2cmac.isEtM()); + while (true) { + buf.reset(); + if (isChaCha20) { + // read encrypted packet length field + io.getByte(buf.buffer, buf.index, 4); + buf.index += 4; + // init cipher with seq number + s2ccipher.update(seqi); + // decrypt packet length field + byte[] tmp = new byte[4]; + s2ccipher.update(buf.buffer, 0, 4, tmp, 0); + j = ((tmp[0] << 24) & 0xff000000) | ((tmp[1] << 16) & 0x00ff0000) + | ((tmp[2] << 8) & 0x0000ff00) | ((tmp[3]) & 0x000000ff); + // RFC 4253 6.1. Maximum Packet Length + if (j < 5 || j > PACKET_MAX_SIZE) { + start_discard(buf, s2ccipher, s2cmac, 0, PACKET_MAX_SIZE); + } + j += s2ccipher.getTagSize(); + if ((buf.index + j) > buf.buffer.length) { + byte[] foo = new byte[buf.index + j]; + System.arraycopy(buf.buffer, 0, foo, 0, buf.index); + buf.buffer = foo; + } + + if ((j % s2ccipher_size) != 0) { + String message = "Bad packet length " + j; + if (getLogger().isEnabled(Logger.FATAL)) { + getLogger().log(Logger.FATAL, message); + } + start_discard(buf, s2ccipher, s2cmac, 0, PACKET_MAX_SIZE - s2ccipher_size); + } + + io.getByte(buf.buffer, buf.index, j); + // subtract tag size now that whole packet has been fetched + j -= s2ccipher.getTagSize(); + buf.index += (j); + try { + s2ccipher.doFinal(buf.buffer, 0, j + 4, buf.buffer, 0); + } catch (AEADBadTagException e) { + throw new JSchException("Packet corrupt", e); + } + // overwrite encrypted packet length field with decrypted version + System.arraycopy(tmp, 0, buf.buffer, 0, 4); + } else if (isAEAD || isEtM) { + io.getByte(buf.buffer, buf.index, 4); + buf.index += 4; + j = ((buf.buffer[0] << 24) & 0xff000000) | ((buf.buffer[1] << 16) & 0x00ff0000) + | ((buf.buffer[2] << 8) & 0x0000ff00) | ((buf.buffer[3]) & 0x000000ff); + // RFC 4253 6.1. Maximum Packet Length + if (j < 5 || j > PACKET_MAX_SIZE) { + start_discard(buf, s2ccipher, s2cmac, 0, PACKET_MAX_SIZE); + } + if (isAEAD) { + j += s2ccipher.getTagSize(); + } + if ((buf.index + j) > buf.buffer.length) { + byte[] foo = new byte[buf.index + j]; + System.arraycopy(buf.buffer, 0, foo, 0, buf.index); + buf.buffer = foo; + } + + if ((j % s2ccipher_size) != 0) { + String message = "Bad packet length " + j; + if (getLogger().isEnabled(Logger.FATAL)) { + getLogger().log(Logger.FATAL, message); + } + start_discard(buf, s2ccipher, s2cmac, 0, PACKET_MAX_SIZE - s2ccipher_size); + } + + io.getByte(buf.buffer, buf.index, j); + buf.index += (j); + + if (isAEAD) { + try { + s2ccipher.updateAAD(buf.buffer, 0, 4); + s2ccipher.doFinal(buf.buffer, 4, j, buf.buffer, 4); + } catch (AEADBadTagException e) { + throw new JSchException("Packet corrupt", e); + } + // don't include AEAD tag size in buf so that decompression works below + buf.index -= s2ccipher.getTagSize(); + } else { + s2cmac.update(seqi); + s2cmac.update(buf.buffer, 0, buf.index); + s2cmac.doFinal(s2cmac_result1, 0); + + io.getByte(s2cmac_result2, 0, s2cmac_result2.length); + if (!Util.arraysequals(s2cmac_result1, s2cmac_result2)) { + throw new JSchException("Packet corrupt"); + } + s2ccipher.update(buf.buffer, 4, j, buf.buffer, 4); + } + } else { + io.getByte(buf.buffer, buf.index, s2ccipher_size); + buf.index += s2ccipher_size; + if (s2ccipher != null) { + s2ccipher.update(buf.buffer, 0, s2ccipher_size, buf.buffer, 0); + } + j = ((buf.buffer[0] << 24) & 0xff000000) | ((buf.buffer[1] << 16) & 0x00ff0000) + | ((buf.buffer[2] << 8) & 0x0000ff00) | ((buf.buffer[3]) & 0x000000ff); + // RFC 4253 6.1. Maximum Packet Length + if (j < 5 || j > PACKET_MAX_SIZE) { + start_discard(buf, s2ccipher, s2cmac, 0, PACKET_MAX_SIZE); + } + int need = j + 4 - s2ccipher_size; + // if(need<0){ + // throw new IOException("invalid data"); + // } + if ((buf.index + need) > buf.buffer.length) { + byte[] foo = new byte[buf.index + need]; + System.arraycopy(buf.buffer, 0, foo, 0, buf.index); + buf.buffer = foo; + } + + if ((need % s2ccipher_size) != 0) { + String message = "Bad packet length " + need; + if (getLogger().isEnabled(Logger.FATAL)) { + getLogger().log(Logger.FATAL, message); + } + start_discard(buf, s2ccipher, s2cmac, 0, PACKET_MAX_SIZE - s2ccipher_size); + } + + if (need > 0) { + io.getByte(buf.buffer, buf.index, need); + buf.index += (need); + if (s2ccipher != null) { + s2ccipher.update(buf.buffer, s2ccipher_size, need, buf.buffer, s2ccipher_size); + } + } + + if (s2cmac != null) { + s2cmac.update(seqi); + s2cmac.update(buf.buffer, 0, buf.index); + s2cmac.doFinal(s2cmac_result1, 0); + + io.getByte(s2cmac_result2, 0, s2cmac_result2.length); + if (!Util.arraysequals(s2cmac_result1, s2cmac_result2)) { + if (need + s2ccipher_size > PACKET_MAX_SIZE) { + throw new IOException("MAC Error"); + } + start_discard(buf, s2ccipher, s2cmac, buf.index, + PACKET_MAX_SIZE - need - s2ccipher_size); + continue; + } + } + } + + if (++seqi == 0 && (enable_strict_kex || require_strict_kex) && initialKex) { + throw new JSchStrictKexException("incoming sequence number wrapped during initial KEX"); + } + + if (inflater != null) { + // inflater.uncompress(buf); + int pad = buf.buffer[4]; + uncompress_len[0] = buf.index - 5 - pad; + byte[] foo = inflater.uncompress(buf.buffer, 5, uncompress_len); + if (foo != null) { + buf.buffer = foo; + buf.index = 5 + uncompress_len[0]; + } else { + if (getLogger().isEnabled(Logger.ERROR)) { + getLogger().log(Logger.ERROR, "fail in inflater"); + } + break; + } + } + + int type = buf.getCommand() & 0xff; + // System.err.println("read: "+type); + if (type == SSH_MSG_DISCONNECT) { + buf.rewind(); + buf.getInt(); + buf.getShort(); + int reason_code = buf.getInt(); + byte[] description_array = buf.getString(); + byte[] language_tag_array = buf.getString(); + String description = Util.byte2str(description_array); + String language_tag = Util.byte2str(language_tag_array); + throw new JSchSessionDisconnectException( + "SSH_MSG_DISCONNECT: " + reason_code + " " + description + " " + language_tag, + reason_code, description, language_tag); + // break; + } else if (initialKex && doStrictKex) { + break; + } else if (type == SSH_MSG_IGNORE) { + } else if (type == SSH_MSG_UNIMPLEMENTED) { + buf.rewind(); + buf.getInt(); + buf.getShort(); + int reason_id = buf.getInt(); + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "Received SSH_MSG_UNIMPLEMENTED for " + reason_id); + } + } else if (type == SSH_MSG_DEBUG) { + buf.rewind(); + buf.getInt(); + buf.getShort(); + /* + * byte always_display=(byte)buf.getByte(); byte[] message=buf.getString(); byte[] + * language_tag=buf.getString(); System.err.println("SSH_MSG_DEBUG:"+ + * " "+Util.byte2str(message)+ " "+Util.byte2str(language_tag)); + */ + } else if (type == SSH_MSG_CHANNEL_WINDOW_ADJUST) { + buf.rewind(); + buf.getInt(); + buf.getShort(); + Channel c = Channel.getChannel(buf.getInt(), this); + if (c == null) { + } else { + c.addRemoteWindowSize(buf.getUInt()); + } + } else if (type == SSH_MSG_EXT_INFO) { + buf.rewind(); + buf.getInt(); + buf.getShort(); + boolean ignore = false; + if (!enable_server_sig_algs) { + ignore = true; + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, + "Ignoring SSH_MSG_EXT_INFO while enable_server_sig_algs != yes"); + } + } else if (isAuthed) { + ignore = true; + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, + "Ignoring SSH_MSG_EXT_INFO received after SSH_MSG_USERAUTH_SUCCESS"); + } + } else if (in_kex) { + ignore = true; + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, + "Ignoring SSH_MSG_EXT_INFO received before SSH_MSG_NEWKEYS"); + } + } else { + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "SSH_MSG_EXT_INFO received"); + } + } + long num_extensions = buf.getUInt(); + for (long i = 0; i < num_extensions; i++) { + byte[] ext_name = buf.getString(); + byte[] ext_value = buf.getString(); + if (!ignore && Util.byte2str(ext_name).equals("server-sig-algs")) { + String foo = Util.byte2str(ext_value); + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "server-sig-algs=<" + foo + ">"); + } + if (sshBugSigType74) { + if (!foo.isEmpty()) { + foo += ",rsa-sha2-256,rsa-sha2-512"; + } else { + foo = "rsa-sha2-256,rsa-sha2-512"; + } + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, + "OpenSSH 7.4 detected: adding rsa-sha2-256 & rsa-sha2-512 to server-sig-algs"); + } + } + serverSigAlgs = Util.split(foo, ","); + } + } + } else if (type == UserAuth.SSH_MSG_USERAUTH_SUCCESS) { + isAuthed = true; + if (inflater == null && deflater == null) { + String method; + method = guess[KeyExchange.PROPOSAL_COMP_ALGS_CTOS]; + initDeflater(method); + method = guess[KeyExchange.PROPOSAL_COMP_ALGS_STOC]; + initInflater(method); + } + break; + } else { + break; + } + } + buf.rewind(); + return buf; + } + + private void start_discard(Buffer buf, Cipher cipher, MAC mac, int mac_already, int discard) + throws JSchException { + if (!cipher.isCBC() || (mac != null && mac.isEtM())) { + throw new JSchException("Packet corrupt"); + } + + if (mac != null) { + mac.update(seqi); + mac.update(buf.buffer, 0, mac_already); + } + + IOException ioe = null; + try { + while (discard > 0) { + buf.reset(); + int len = discard > buf.buffer.length ? buf.buffer.length : discard; + io.getByte(buf.buffer, 0, len); + if (mac != null) { + mac.update(buf.buffer, 0, len); + } + discard -= len; + } + } catch (IOException e) { + ioe = e; + if (getLogger().isEnabled(Logger.ERROR)) { + getLogger().log(Logger.ERROR, "start_discard finished early due to " + e.getMessage(), e); + } + } + + if (mac != null) { + mac.doFinal(buf.buffer, 0); + } + + JSchException e = new JSchException("Packet corrupt"); + if (ioe != null) { + e.addSuppressed(ioe); + } + throw e; + } + + byte[] getSessionId() { + return session_id; + } + + private void receive_newkeys(Buffer buf, KeyExchange kex) throws Exception { + try { + updateKeys(kex); + } finally { + kex.clearK(); + } + in_kex = false; + if (doStrictKex) { + seqi = 0; + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, + "Reset incoming sequence number after receiving SSH_MSG_NEWKEYS for strict KEX"); + } + } + } + + private void updateKeys(KeyExchange kex) throws Exception { + byte[] K = kex.getK(); + byte[] H = kex.getH(); + HASH hash = kex.getHash(); + + if (session_id == null) { + session_id = new byte[H.length]; + System.arraycopy(H, 0, session_id, 0, H.length); + } + + /* + * Initial IV client to server: HASH (K || H || "A" || session_id) Initial IV server to client: + * HASH (K || H || "B" || session_id) Encryption key client to server: HASH (K || H || "C" || + * session_id) Encryption key server to client: HASH (K || H || "D" || session_id) Integrity key + * client to server: HASH (K || H || "E" || session_id) Integrity key server to client: HASH (K + * || H || "F" || session_id) + */ + + buf.reset(); + buf.putByte(K); + buf.putByte(H); + buf.putByte((byte) 0x41); + buf.putByte(session_id); + hash.update(buf.buffer, 0, buf.index); + IVc2s = hash.digest(); + + int j = buf.index - session_id.length - 1; + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + IVs2c = hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + Ec2s = hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + Es2c = hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + MACc2s = hash.digest(); + + buf.buffer[j]++; + hash.update(buf.buffer, 0, buf.index); + MACs2c = hash.digest(); + + try { + Class cc; + Class cm; + String method; + + method = guess[KeyExchange.PROPOSAL_ENC_ALGS_STOC]; + cc = Class.forName(getConfig(method)).asSubclass(Cipher.class); + s2ccipher = cc.getDeclaredConstructor().newInstance(); + while (s2ccipher.getBlockSize() > Es2c.length) { + buf.reset(); + buf.putByte(K); + buf.putByte(H); + buf.putByte(Es2c); + hash.update(buf.buffer, 0, buf.index); + byte[] foo = hash.digest(); + byte[] bar = new byte[Es2c.length + foo.length]; + System.arraycopy(Es2c, 0, bar, 0, Es2c.length); + System.arraycopy(foo, 0, bar, Es2c.length, foo.length); + Es2c = bar; + } + s2ccipher.init(Cipher.DECRYPT_MODE, Es2c, IVs2c); + s2ccipher_size = s2ccipher.getIVSize(); + + if (!s2ccipher.isAEAD()) { + method = guess[KeyExchange.PROPOSAL_MAC_ALGS_STOC]; + cm = Class.forName(getConfig(method)).asSubclass(MAC.class); + s2cmac = cm.getDeclaredConstructor().newInstance(); + MACs2c = expandKey(buf, K, H, MACs2c, hash, s2cmac.getBlockSize()); + s2cmac.init(MACs2c); + // mac_buf=new byte[s2cmac.getBlockSize()]; + s2cmac_result1 = new byte[s2cmac.getBlockSize()]; + s2cmac_result2 = new byte[s2cmac.getBlockSize()]; + } + + method = guess[KeyExchange.PROPOSAL_ENC_ALGS_CTOS]; + cc = Class.forName(getConfig(method)).asSubclass(Cipher.class); + c2scipher = cc.getDeclaredConstructor().newInstance(); + while (c2scipher.getBlockSize() > Ec2s.length) { + buf.reset(); + buf.putByte(K); + buf.putByte(H); + buf.putByte(Ec2s); + hash.update(buf.buffer, 0, buf.index); + byte[] foo = hash.digest(); + byte[] bar = new byte[Ec2s.length + foo.length]; + System.arraycopy(Ec2s, 0, bar, 0, Ec2s.length); + System.arraycopy(foo, 0, bar, Ec2s.length, foo.length); + Ec2s = bar; + } + c2scipher.init(Cipher.ENCRYPT_MODE, Ec2s, IVc2s); + c2scipher_size = c2scipher.getIVSize(); + + if (!c2scipher.isAEAD()) { + method = guess[KeyExchange.PROPOSAL_MAC_ALGS_CTOS]; + cm = Class.forName(getConfig(method)).asSubclass(MAC.class); + c2smac = cm.getDeclaredConstructor().newInstance(); + MACc2s = expandKey(buf, K, H, MACc2s, hash, c2smac.getBlockSize()); + c2smac.init(MACc2s); + } + + method = guess[KeyExchange.PROPOSAL_COMP_ALGS_CTOS]; + initDeflater(method); + + method = guess[KeyExchange.PROPOSAL_COMP_ALGS_STOC]; + initInflater(method); + } catch (Exception | NoClassDefFoundError e) { + if (e instanceof JSchException) + throw e; + throw new JSchException(e.toString(), e); + // System.err.println("updatekeys: "+e); + } + } + + /* + * RFC 4253 7.2. Output from Key Exchange If the key length needed is longer than the output of + * the HASH, the key is extended by computing HASH of the concatenation of K and H and the entire + * key so far, and appending the resulting bytes (as many as HASH generates) to the key. This + * process is repeated until enough key material is available; the key is taken from the beginning + * of this value. In other words: K1 = HASH(K || H || X || session_id) (X is e.g., "A") K2 = + * HASH(K || H || K1) K3 = HASH(K || H || K1 || K2) ... key = K1 || K2 || K3 || ... + */ + private byte[] expandKey(Buffer buf, byte[] K, byte[] H, byte[] key, HASH hash, + int required_length) throws Exception { + byte[] result = key; + int size = hash.getBlockSize(); + while (result.length < required_length) { + buf.reset(); + buf.putByte(K); + buf.putByte(H); + buf.putByte(result); + hash.update(buf.buffer, 0, buf.index); + byte[] tmp = new byte[result.length + size]; + System.arraycopy(result, 0, tmp, 0, result.length); + System.arraycopy(hash.digest(), 0, tmp, result.length, size); + Util.bzero(result); + result = tmp; + } + return result; + } + + /* synchronized */ void write(Packet packet, Channel c, int length) throws Exception { + long t = getTimeout(); + while (true) { + if (in_kex) { + if (t > 0L && (System.currentTimeMillis() - kex_start_time) > t) { + throw new JSchException("timeout in waiting for rekeying process."); + } + try { + Thread.sleep(10); + } catch (InterruptedException e) { + } ; + continue; + } + synchronized (c) { + if (c.rwsize < length) { + try { + c.notifyme++; + c.wait(100); + } catch (InterruptedException e) { + } finally { + c.notifyme--; + } + } + + if (in_kex) { + continue; + } + + if (c.rwsize >= length) { + c.rwsize -= length; + break; + } + } + if (c.close || !c.isConnected()) { + throw new IOException("channel is broken"); + } + + boolean sendit = false; + int s = 0; + byte command = 0; + int recipient = -1; + synchronized (c) { + if (c.rwsize > 0) { + long len = c.rwsize; + if (len > length) { + len = length; + } + if (len != length) { + s = packet.shift((int) len, (c2scipher != null ? c2scipher_size : 8), + (c2smac != null ? c2smac.getBlockSize() : 0)); + } + command = packet.buffer.getCommand(); + recipient = c.getRecipient(); + length -= (int) len; + c.rwsize -= len; + sendit = true; + } + } + if (sendit) { + _write(packet); + if (length == 0) { + return; + } + packet.unshift(command, recipient, s, length); + } + + synchronized (c) { + if (in_kex) { + continue; + } + if (c.rwsize >= length) { + c.rwsize -= length; + break; + } + + // try{ + // System.out.println("1wait: "+c.rwsize); + // c.notifyme++; + // c.wait(100); + // } + // catch(InterruptedException e){ + // } + // finally{ + // c.notifyme--; + // } + } + } + _write(packet); + } + + void write(Packet packet) throws Exception { + // System.err.println("in_kex="+in_kex+" "+(packet.buffer.getCommand())); + long t = getTimeout(); + while (in_kex) { + if (t > 0L && (System.currentTimeMillis() - kex_start_time) > t && !in_prompt) { + throw new JSchException("timeout in waiting for rekeying process."); + } + byte command = packet.buffer.getCommand(); + // System.err.println("command: "+command); + if (command == SSH_MSG_KEXINIT || command == SSH_MSG_NEWKEYS || command == SSH_MSG_KEXDH_INIT + || command == SSH_MSG_KEXDH_REPLY || command == SSH_MSG_KEX_DH_GEX_GROUP + || command == SSH_MSG_KEX_DH_GEX_INIT || command == SSH_MSG_KEX_DH_GEX_REPLY + || command == SSH_MSG_KEX_DH_GEX_REQUEST || command == SSH_MSG_DISCONNECT) { + break; + } + try { + Thread.sleep(10); + } catch (InterruptedException e) { + } ; + } + _write(packet); + } + + private void _write(Packet packet) throws Exception { + boolean initialKex = this.initialKex; + boolean doStrictKex = this.doStrictKex; + boolean enable_strict_kex = this.enable_strict_kex; + boolean require_strict_kex = this.require_strict_kex; + boolean resetSeqo = packet.buffer.getCommand() == SSH_MSG_NEWKEYS && doStrictKex; + + synchronized (lock) { + encode(packet); + if (io != null) { + io.put(packet); + if (++seqo == 0 && (enable_strict_kex || require_strict_kex) && initialKex) { + throw new JSchStrictKexException("outgoing sequence number wrapped during initial KEX"); + } + if (resetSeqo) { + seqo = 0; + } + } + } + + if (resetSeqo && io != null && getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, + "Reset outgoing sequence number after sending SSH_MSG_NEWKEYS for strict KEX"); + } + } + + Runnable thread; + + void run() { + thread = this::run; + + byte[] foo; + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + int i = 0; + Channel channel; + int[] start = new int[1]; + int[] length = new int[1]; + KeyExchange kex = null; + + int stimeout = 0; + try { + while (isConnected && thread != null) { + try { + buf = read(buf); + stimeout = 0; + } catch (InterruptedIOException /* SocketTimeoutException */ ee) { + if (!in_kex && stimeout < serverAliveCountMax) { + sendKeepAliveMsg(); + stimeout++; + continue; + } else if (in_kex && stimeout < serverAliveCountMax) { + stimeout++; + continue; + } + throw ee; + } + + int msgType = buf.getCommand() & 0xff; + + if (kex != null && kex.getState() == msgType) { + kex_start_time = System.currentTimeMillis(); + boolean result = kex.next(buf); + if (!result) { + throw new JSchException("verify: " + result); + } + continue; + } + + switch (msgType) { + case SSH_MSG_KEXINIT: + // System.err.println("KEXINIT"); + kex = receive_kexinit(buf); + break; + + case SSH_MSG_NEWKEYS: + // System.err.println("NEWKEYS"); + send_newkeys(); + receive_newkeys(buf, kex); + kex = null; + break; + + case SSH_MSG_CHANNEL_DATA: + buf.getInt(); + buf.getByte(); + buf.getByte(); + i = buf.getInt(); + channel = Channel.getChannel(i, this); + foo = buf.getString(start, length); + if (channel == null) { + break; + } + + if (length[0] == 0) { + break; + } + + try { + channel.write(foo, start[0], length[0]); + } catch (Exception e) { + // System.err.println(e); + try { + channel.disconnect(); + } catch (Exception ee) { + } + break; + } + int len = length[0]; + channel.setLocalWindowSize(channel.lwsize - len); + if (channel.lwsize < channel.lwsize_max / 2) { + packet.reset(); + buf.putByte((byte) SSH_MSG_CHANNEL_WINDOW_ADJUST); + buf.putInt(channel.getRecipient()); + buf.putInt(channel.lwsize_max - channel.lwsize); + synchronized (channel) { + if (!channel.close) + write(packet); + } + channel.setLocalWindowSize(channel.lwsize_max); + } + break; + + case SSH_MSG_CHANNEL_EXTENDED_DATA: + buf.getInt(); + buf.getShort(); + i = buf.getInt(); + channel = Channel.getChannel(i, this); + buf.getInt(); // data_type_code == 1 + foo = buf.getString(start, length); + // System.err.println("stderr: "+new String(foo,start[0],length[0])); + if (channel == null) { + break; + } + + if (length[0] == 0) { + break; + } + + channel.write_ext(foo, start[0], length[0]); + + len = length[0]; + channel.setLocalWindowSize(channel.lwsize - len); + if (channel.lwsize < channel.lwsize_max / 2) { + packet.reset(); + buf.putByte((byte) SSH_MSG_CHANNEL_WINDOW_ADJUST); + buf.putInt(channel.getRecipient()); + buf.putInt(channel.lwsize_max - channel.lwsize); + synchronized (channel) { + if (!channel.close) + write(packet); + } + channel.setLocalWindowSize(channel.lwsize_max); + } + break; + + case SSH_MSG_CHANNEL_WINDOW_ADJUST: + buf.getInt(); + buf.getShort(); + i = buf.getInt(); + channel = Channel.getChannel(i, this); + if (channel == null) { + break; + } + channel.addRemoteWindowSize(buf.getUInt()); + break; + + case SSH_MSG_CHANNEL_EOF: + buf.getInt(); + buf.getShort(); + i = buf.getInt(); + channel = Channel.getChannel(i, this); + if (channel != null) { + // channel.eof_remote=true; + // channel.eof(); + channel.eof_remote(); + } + /* + * packet.reset(); buf.putByte((byte)SSH_MSG_CHANNEL_EOF); + * buf.putInt(channel.getRecipient()); write(packet); + */ + break; + case SSH_MSG_CHANNEL_CLOSE: + buf.getInt(); + buf.getShort(); + i = buf.getInt(); + channel = Channel.getChannel(i, this); + if (channel != null) { + // channel.close(); + channel.disconnect(); + } + /* + * if(Channel.pool.size()==0){ thread=null; } + */ + break; + case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: + buf.getInt(); + buf.getShort(); + i = buf.getInt(); + channel = Channel.getChannel(i, this); + int r = buf.getInt(); + long rws = buf.getUInt(); + int rps = buf.getInt(); + if (channel != null) { + channel.setRemoteWindowSize(rws); + channel.setRemotePacketSize(rps); + channel.open_confirmation = true; + channel.setRecipient(r); + } + break; + case SSH_MSG_CHANNEL_OPEN_FAILURE: + buf.getInt(); + buf.getShort(); + i = buf.getInt(); + channel = Channel.getChannel(i, this); + if (channel != null) { + int reason_code = buf.getInt(); + // foo=buf.getString(); // additional textual information + // foo=buf.getString(); // language tag + channel.setExitStatus(reason_code); + channel.close = true; + channel.eof_remote = true; + channel.setRecipient(0); + } + break; + case SSH_MSG_CHANNEL_REQUEST: + buf.getInt(); + buf.getShort(); + i = buf.getInt(); + foo = buf.getString(); + boolean reply = (buf.getByte() != 0); + channel = Channel.getChannel(i, this); + if (channel != null) { + byte reply_type = (byte) SSH_MSG_CHANNEL_FAILURE; + if ((Util.byte2str(foo)).equals("exit-status")) { + i = buf.getInt(); // exit-status + channel.setExitStatus(i); + reply_type = (byte) SSH_MSG_CHANNEL_SUCCESS; + } + if (reply) { + packet.reset(); + buf.putByte(reply_type); + buf.putInt(channel.getRecipient()); + write(packet); + } + } else { + } + break; + case SSH_MSG_CHANNEL_OPEN: + buf.getInt(); + buf.getShort(); + foo = buf.getString(); + String ctyp = Util.byte2str(foo); + if (!"forwarded-tcpip".equals(ctyp) && !("x11".equals(ctyp) && x11_forwarding) + && !("auth-agent@openssh.com".equals(ctyp) && agent_forwarding)) { + // System.err.println("Session.run: CHANNEL OPEN "+ctyp); + // throw new IOException("Session.run: CHANNEL OPEN "+ctyp); + packet.reset(); + buf.putByte((byte) SSH_MSG_CHANNEL_OPEN_FAILURE); + buf.putInt(buf.getInt()); + buf.putInt(Channel.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED); + buf.putString(Util.empty); + buf.putString(Util.empty); + write(packet); + } else { + channel = Channel.getChannel(ctyp, this); + addChannel(channel); + channel.getData(buf); + channel.init(); + + Thread tmp = new Thread(channel::run); + tmp.setName("Channel " + ctyp + " " + host); + if (daemon_thread) { + tmp.setDaemon(daemon_thread); + } + tmp.start(); + } + break; + case SSH_MSG_CHANNEL_SUCCESS: + buf.getInt(); + buf.getShort(); + i = buf.getInt(); + channel = Channel.getChannel(i, this); + if (channel == null) { + break; + } + channel.reply = 1; + break; + case SSH_MSG_CHANNEL_FAILURE: + buf.getInt(); + buf.getShort(); + i = buf.getInt(); + channel = Channel.getChannel(i, this); + if (channel == null) { + break; + } + channel.reply = 0; + break; + case SSH_MSG_GLOBAL_REQUEST: + buf.getInt(); + buf.getShort(); + foo = buf.getString(); // request name + reply = (buf.getByte() != 0); + if (reply) { + packet.reset(); + buf.putByte((byte) SSH_MSG_REQUEST_FAILURE); + write(packet); + } + break; + case SSH_MSG_REQUEST_FAILURE: + case SSH_MSG_REQUEST_SUCCESS: + Thread t = grr.getThread(); + if (t != null) { + grr.setReply(msgType == SSH_MSG_REQUEST_SUCCESS ? 1 : 0); + if (msgType == SSH_MSG_REQUEST_SUCCESS && grr.getPort() == 0) { + buf.getInt(); + buf.getShort(); + grr.setPort(buf.getInt()); + } + t.interrupt(); + } + break; + default: + // System.err.println("Session.run: unsupported type "+msgType); + throw new IOException("Unknown SSH message type " + msgType); + } + } + } catch (Exception e) { + in_kex = false; + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, + "Caught an exception, leaving main loop due to " + e.getMessage()); + } + // System.err.println("# Session.run"); + // e.printStackTrace(); + } + try { + disconnect(); + } catch (NullPointerException e) { + // System.err.println("@1"); + // e.printStackTrace(); + } catch (Exception e) { + // System.err.println("@2"); + // e.printStackTrace(); + } + isConnected = false; + } + + public void disconnect() { + if (!isConnected) + return; + // System.err.println(this+": disconnect"); + // Thread.dumpStack(); + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "Disconnecting from " + host + " port " + port); + } + /* + * for(int i=0; ilport is + * 0, the tcp port will be allocated. + * + * @param lport local port for local port forwarding + * @param host host address for local port forwarding + * @param rport remote port number for local port forwarding + * @return an allocated local TCP port number + * @see #setPortForwardingL(String bind_address, int lport, String host, int rport, + * ServerSocketFactory ssf, int connectTimeout) + */ + public int setPortForwardingL(int lport, String host, int rport) throws JSchException { + return setPortForwardingL("127.0.0.1", lport, host, rport); + } + + /** + * Registers the local port forwarding. If bind_address is an empty string or '*', + * the port should be available from all interfaces. If bind_address is + * "localhost" or null, the listening port will be bound for local use + * only. If lport is 0, the tcp port will be allocated. + * + * @param bind_address bind address for local port forwarding + * @param lport local port for local port forwarding + * @param host host address for local port forwarding + * @param rport remote port number for local port forwarding + * @return an allocated local TCP port number + * @see #setPortForwardingL(String bind_address, int lport, String host, int rport, + * ServerSocketFactory ssf, int connectTimeout) + */ + public int setPortForwardingL(String bind_address, int lport, String host, int rport) + throws JSchException { + return setPortForwardingL(bind_address, lport, host, rport, null); + } + + /** + * Registers the local port forwarding. If bind_address is an empty string or + * "*", the port should be available from all interfaces. If + * bind_address is "localhost" or null, the listening port + * will be bound for local use only. If lport is 0, the tcp port will be + * allocated. + * + * @param bind_address bind address for local port forwarding + * @param lport local port for local port forwarding + * @param host host address for local port forwarding + * @param rport remote port number for local port forwarding + * @param ssf socket factory + * @return an allocated local TCP port number + * @see #setPortForwardingL(String bind_address, int lport, String host, int rport, + * ServerSocketFactory ssf, int connectTimeout) + */ + public int setPortForwardingL(String bind_address, int lport, String host, int rport, + ServerSocketFactory ssf) throws JSchException { + return setPortForwardingL(bind_address, lport, host, rport, ssf, 0); + } + + /** + * Registers the local port forwarding. If bind_address is an empty string or + * "*", the port should be available from all interfaces. If + * bind_address is "localhost" or null, the listening port + * will be bound for local use only. If lport is 0, the tcp port will be + * allocated. + * + * @param bind_address bind address for local port forwarding + * @param lport local port for local port forwarding + * @param host host address for local port forwarding + * @param rport remote port number for local port forwarding + * @param ssf socket factory + * @param connectTimeout timeout for establishing port connection + * @return an allocated local TCP port number + */ + public int setPortForwardingL(String bind_address, int lport, String host, int rport, + ServerSocketFactory ssf, int connectTimeout) throws JSchException { + PortWatcher pw = PortWatcher.addPort(this, bind_address, lport, host, rport, ssf); + pw.setConnectTimeout(connectTimeout); + Thread tmp = new Thread(pw::run); + tmp.setName("PortWatcher Thread for " + host); + if (daemon_thread) { + tmp.setDaemon(daemon_thread); + } + tmp.start(); + return pw.lport; + } + + public int setSocketForwardingL(String bindAddress, int lport, String socketPath, + ServerSocketFactory ssf, int connectTimeout) throws JSchException { + PortWatcher pw = PortWatcher.addSocket(this, bindAddress, lport, socketPath, ssf); + pw.setConnectTimeout(connectTimeout); + Thread tmp = new Thread(pw::run); + tmp.setName("PortWatcher Thread for " + host); + if (daemon_thread) { + tmp.setDaemon(daemon_thread); + } + tmp.start(); + return pw.lport; + } + + /** + * Cancels the local port forwarding assigned at local TCP port lport on loopback + * interface. + * + * @param lport local TCP port + */ + public void delPortForwardingL(int lport) throws JSchException { + delPortForwardingL("127.0.0.1", lport); + } + + /** + * Cancels the local port forwarding assigned at local TCP port lport on + * bind_address interface. + * + * @param bind_address bind_address of network interfaces + * @param lport local TCP port + */ + public void delPortForwardingL(String bind_address, int lport) throws JSchException { + PortWatcher.delPort(this, bind_address, lport); + } + + /** + * Lists the registered local port forwarding. + * + * @return a list of "lport:host:hostport" + */ + public String[] getPortForwardingL() throws JSchException { + return PortWatcher.getPortForwarding(this); + } + + /** + * Registers the remote port forwarding for the loopback interface of the remote. + * + * @param rport remote port + * @param host host address + * @param lport local port + * @see #setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory + * sf) + */ + public void setPortForwardingR(int rport, String host, int lport) throws JSchException { + setPortForwardingR(null, rport, host, lport, (SocketFactory) null); + } + + /** + * Registers the remote port forwarding. If bind_address is an empty string or + * "*", the port should be available from all interfaces. If + * bind_address is "localhost" or is not given, the listening port will + * be bound for local use only. Note that if GatewayPorts is "no" on the + * remote, "localhost" is always used as a bind_address. + * + * @param bind_address bind address + * @param rport remote port + * @param host host address + * @param lport local port + * @see #setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory + * sf) + */ + public void setPortForwardingR(String bind_address, int rport, String host, int lport) + throws JSchException { + setPortForwardingR(bind_address, rport, host, lport, (SocketFactory) null); + } + + /** + * Registers the remote port forwarding for the loopback interface of the remote. + * + * @param rport remote port + * @param host host address + * @param lport local port + * @param sf socket factory + * @see #setPortForwardingR(String bind_address, int rport, String host, int lport, SocketFactory + * sf) + */ + public void setPortForwardingR(int rport, String host, int lport, SocketFactory sf) + throws JSchException { + setPortForwardingR(null, rport, host, lport, sf); + } + + // TODO: This method should return the integer value as the assigned port. + /** + * Registers the remote port forwarding. If bind_address is an empty string or + * "*", the port should be available from all interfaces. If + * bind_address is "localhost" or is not given, the listening port will + * be bound for local use only. Note that if GatewayPorts is "no" on the + * remote, "localhost" is always used as a bind_address. If rport is + * 0, the TCP port will be allocated on the remote. + * + * @param bind_address bind address + * @param rport remote port + * @param host host address + * @param lport local port + * @param sf socket factory + */ + public void setPortForwardingR(String bind_address, int rport, String host, int lport, + SocketFactory sf) throws JSchException { + int allocated = _setPortForwardingR(bind_address, rport); + ChannelForwardedTCPIP.addPort(this, bind_address, rport, allocated, host, lport, sf); + } + + /** + * Registers the remote port forwarding for the loopback interface of the remote. The TCP + * connection to rport on the remote will be forwarded to an instance of the class + * daemon. The class specified by daemon must implement + * ForwardedTCPIPDaemon. + * + * @param rport remote port + * @param daemon class name, which implements "ForwardedTCPIPDaemon" + * @see #setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) + */ + public void setPortForwardingR(int rport, String daemon) throws JSchException { + setPortForwardingR(null, rport, daemon, null); + } + + /** + * Registers the remote port forwarding for the loopback interface of the remote. The TCP + * connection to rport on the remote will be forwarded to an instance of the class + * daemon with the argument arg. The class specified by + * daemon must implement ForwardedTCPIPDaemon. + * + * @param rport remote port + * @param daemon class name, which implements "ForwardedTCPIPDaemon" + * @param arg arguments for "daemon" + * @see #setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) + */ + public void setPortForwardingR(int rport, String daemon, Object[] arg) throws JSchException { + setPortForwardingR(null, rport, daemon, arg); + } + + /** + * Registers the remote port forwarding. If bind_address is an empty string or + * "*", the port should be available from all interfaces. If + * bind_address is "localhost" or is not given, the listening port will + * be bound for local use only. Note that if GatewayPorts is "no" on the + * remote, "localhost" is always used as a bind_address. The TCP connection to + * rport on the remote will be forwarded to an instance of the class + * daemon with the argument arg. The class specified by + * daemon must implement ForwardedTCPIPDaemon. + * + * @param bind_address bind address + * @param rport remote port + * @param daemon class name, which implements "ForwardedTCPIPDaemon" + * @param arg arguments for "daemon" + * @see #setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) + */ + public void setPortForwardingR(String bind_address, int rport, String daemon, Object[] arg) + throws JSchException { + int allocated = _setPortForwardingR(bind_address, rport); + ChannelForwardedTCPIP.addPort(this, bind_address, rport, allocated, daemon, arg); + } + + /** + * Lists the registered remote port forwarding. + * + * @return a list of "rport:host:hostport" + */ + public String[] getPortForwardingR() throws JSchException { + return ChannelForwardedTCPIP.getPortForwarding(this); + } + + static class Forwarding { + String bind_address = null; + int port = -1; + String host = null; + int hostport = -1; + String socketPath = null; + } + + /** + * The given argument may be "[bind_address:]port:host:hostport" or "[bind_address:]port + * host:hostport", which is from LocalForward command of ~/.ssh/config . Also allows + * "[bind_address:]port:socketPath" or "[bind_address:]port socketPath" for socket forwarding. + */ + Forwarding parseForwarding(String conf) throws JSchException { + String[] tmp = conf.split(" "); + if (tmp.length > 1) { // "[bind_address:]port host:hostport" + Vector foo = new Vector<>(); + for (int i = 0; i < tmp.length; i++) { + if (tmp[i].length() == 0) + continue; + foo.addElement(tmp[i].trim()); + } + StringBuilder sb = new StringBuilder(); // join + for (int i = 0; i < foo.size(); i++) { + sb.append(foo.elementAt(i)); + if (i + 1 < foo.size()) + sb.append(":"); + } + conf = sb.toString(); + } + + String org = conf; + Forwarding f = new Forwarding(); + try { + if (conf.lastIndexOf(":") == -1) + throw new JSchException("parseForwarding: " + org); + try { + f.hostport = Integer.parseInt(conf.substring(conf.lastIndexOf(":") + 1)); + conf = conf.substring(0, conf.lastIndexOf(":")); + if (conf.lastIndexOf(":") == -1) + throw new JSchException("parseForwarding: " + org); + f.host = conf.substring(conf.lastIndexOf(":") + 1); + } catch (NumberFormatException e) { + f.socketPath = conf.substring(conf.lastIndexOf(":") + 1); + } + conf = conf.substring(0, conf.lastIndexOf(":")); + if (conf.lastIndexOf(":") != -1) { + f.port = Integer.parseInt(conf.substring(conf.lastIndexOf(":") + 1)); + conf = conf.substring(0, conf.lastIndexOf(":")); + if (conf.length() == 0 || conf.equals("*")) + conf = "0.0.0.0"; + if (conf.equals("localhost")) + conf = "127.0.0.1"; + f.bind_address = conf; + } else { + f.port = Integer.parseInt(conf); + f.bind_address = "127.0.0.1"; + } + } catch (NumberFormatException e) { + throw new JSchException("parseForwarding: " + e.toString(), e); + } + return f; + } + + /** + * Registers the local port forwarding. The argument should be in the format like + * "[bind_address:]port:host:hostport". If bind_address is an empty string or + * "*", the port should be available from all interfaces. If + * bind_address is "localhost" or is not given, the listening port will + * be bound for local use only. + * + * @param conf configuration of local port forwarding + * @return an assigned port number + * @see #setPortForwardingL(String bind_address, int lport, String host, int rport) + */ + public int setPortForwardingL(String conf) throws JSchException { + Forwarding f = parseForwarding(conf); + return setPortForwardingL(f.bind_address, f.port, f.host, f.hostport); + } + + /** + * Registers the remote port forwarding. The argument should be in the format like + * "[bind_address:]port:host:hostport". If the bind_address is not given, the default is to only + * bind to loopback addresses. If the bind_address is "*" or an empty string, then + * the forwarding is requested to listen on all interfaces. Note that if GatewayPorts + * is "no" on the remote, "localhost" is always used for bind_address. + * If the specified remote is "0", the TCP port will be allocated on the remote. + * + * @param conf configuration of remote port forwarding + * @return an allocated TCP port on the remote. + * @see #setPortForwardingR(String bind_address, int rport, String host, int rport) + */ + public int setPortForwardingR(String conf) throws JSchException { + Forwarding f = parseForwarding(conf); + int allocated = _setPortForwardingR(f.bind_address, f.port); + ChannelForwardedTCPIP.addPort(this, f.bind_address, f.port, allocated, f.host, f.hostport, + null); + return allocated; + } + + /** + * Instantiates an instance of stream-forwarder to host:port. Set I/O + * stream to the given channel, and then invoke Channel#connect() method. + * + * @param host remote host, which the given stream will be plugged to. + * @param port remote port, which the given stream will be plugged to. + */ + public Channel getStreamForwarder(String host, int port) throws JSchException { + ChannelDirectTCPIP channel = new ChannelDirectTCPIP(); + channel.init(); + this.addChannel(channel); + channel.setHost(host); + channel.setPort(port); + return channel; + } + + private static class GlobalRequestReply { + private Thread thread = null; + private int reply = -1; + private int port = 0; + + void setThread(Thread thread) { + this.thread = thread; + this.reply = -1; + } + + Thread getThread() { + return thread; + } + + void setReply(int reply) { + this.reply = reply; + } + + int getReply() { + return this.reply; + } + + int getPort() { + return this.port; + } + + void setPort(int port) { + this.port = port; + } + } + + private GlobalRequestReply grr = new GlobalRequestReply(); + + private int _setPortForwardingR(String bind_address, int rport) throws JSchException { + synchronized (grr) { + Buffer buf = new Buffer(200); // ?? + Packet packet = new Packet(buf); + + String address_to_bind = ChannelForwardedTCPIP.normalize(bind_address); + + grr.setThread(Thread.currentThread()); + grr.setPort(rport); + + try { + // byte SSH_MSG_GLOBAL_REQUEST 80 + // string "tcpip-forward" + // boolean want_reply + // string address_to_bind + // uint32 port number to bind + packet.reset(); + buf.putByte((byte) SSH_MSG_GLOBAL_REQUEST); + buf.putString(Util.str2byte("tcpip-forward")); + buf.putByte((byte) 1); + buf.putString(Util.str2byte(address_to_bind)); + buf.putInt(rport); + write(packet); + } catch (Exception e) { + grr.setThread(null); + throw new JSchException(e.toString(), e); + } + + int count = 0; + int reply = grr.getReply(); + while (count < 10 && reply == -1) { + try { + Thread.sleep(1000); + } catch (Exception e) { + } + count++; + reply = grr.getReply(); + } + grr.setThread(null); + if (reply != 1) { + throw new JSchException("remote port forwarding failed for listen port " + rport); + } + rport = grr.getPort(); + } + return rport; + } + + /** + * Cancels the remote port forwarding assigned at remote TCP port rport. + * + * @param rport remote TCP port + */ + public void delPortForwardingR(int rport) throws JSchException { + this.delPortForwardingR(null, rport); + } + + /** + * Cancels the remote port forwarding assigned at remote TCP port rport bound on the + * interface at bind_address. + * + * @param bind_address bind address of the interface on the remote + * @param rport remote TCP port + */ + public void delPortForwardingR(String bind_address, int rport) throws JSchException { + ChannelForwardedTCPIP.delPort(this, bind_address, rport); + } + + private void initDeflater(String method) throws JSchException { + Compression odeflater = deflater; + if (method.equals("none")) { + deflater = null; + if (odeflater != null) { + odeflater.end(); + } + return; + } + String foo = getConfig(method); + if (foo != null) { + if (method.equals("zlib") || (isAuthed && method.equals("zlib@openssh.com"))) { + try { + Class c = Class.forName(foo).asSubclass(Compression.class); + deflater = c.getDeclaredConstructor().newInstance(); + int level = 6; + try { + level = Integer.parseInt(getConfig("compression_level")); + } catch (Exception ee) { + } + deflater.init(Compression.DEFLATER, level, this); + } catch (Exception ee) { + throw new JSchException(ee.toString(), ee); + // System.err.println(foo+" isn't accessible."); + } finally { + if (odeflater != null) { + odeflater.end(); + } + } + } + } + } + + private void initInflater(String method) throws JSchException { + Compression oinflater = inflater; + if (method.equals("none")) { + inflater = null; + if (oinflater != null) { + oinflater.end(); + } + return; + } + String foo = getConfig(method); + if (foo != null) { + if (method.equals("zlib") || (isAuthed && method.equals("zlib@openssh.com"))) { + try { + Class c = Class.forName(foo).asSubclass(Compression.class); + inflater = c.getDeclaredConstructor().newInstance(); + inflater.init(Compression.INFLATER, 0, this); + } catch (Exception ee) { + throw new JSchException(ee.toString(), ee); + // System.err.println(foo+" isn't accessible."); + } finally { + if (oinflater != null) { + oinflater.end(); + } + } + } + } + } + + void addChannel(Channel channel) { + channel.setSession(this); + } + + String[] getServerSigAlgs() { + return serverSigAlgs; + } + + public void setProxy(Proxy proxy) { + this.proxy = proxy; + } + + public void setHost(String host) { + this.host = host; + } + + public void setPort(int port) { + this.port = port; + } + + void setUserName(String username) { + this.username = username; + } + + public void setUserInfo(UserInfo userinfo) { + this.userinfo = userinfo; + } + + public UserInfo getUserInfo() { + return userinfo; + } + + public void setInputStream(InputStream in) { + this.in = in; + } + + public void setOutputStream(OutputStream out) { + this.out = out; + } + + public void setX11Host(String host) { + ChannelX11.setHost(host); + } + + public void setX11Port(int port) { + ChannelX11.setPort(port); + } + + public void setX11Cookie(String cookie) { + ChannelX11.setCookie(cookie); + } + + public void setPassword(String password) { + if (password != null) + this.password = Util.str2byte(password); + } + + public void setPassword(byte[] password) { + if (password != null) { + this.password = new byte[password.length]; + System.arraycopy(password, 0, this.password, 0, password.length); + } + } + + public void setConfig(Properties newconf) { + Hashtable foo = new Hashtable<>(); + for (String key : newconf.stringPropertyNames()) { + foo.put(key, newconf.getProperty(key)); + } + setConfig(foo); + } + + public void setConfig(Hashtable newconf) { + synchronized (lock) { + if (config == null) + config = new Hashtable<>(); + for (Enumeration e = newconf.keys(); e.hasMoreElements();) { + String newkey = e.nextElement(); + String key = + (newkey.equals("PubkeyAcceptedKeyTypes") ? "PubkeyAcceptedAlgorithms" : newkey); + String value = newconf.get(newkey); + config.put(key, value); + } + } + } + + public void setConfig(String key, String value) { + synchronized (lock) { + if (config == null) { + config = new Hashtable<>(); + } + if (key.equals("PubkeyAcceptedKeyTypes")) { + config.put("PubkeyAcceptedAlgorithms", value); + } else { + config.put(key, value); + } + } + } + + public String getConfig(String key) { + if (key.equals("PubkeyAcceptedKeyTypes")) { + key = "PubkeyAcceptedAlgorithms"; + } + Object foo = null; + if (config != null) { + foo = config.get(key); + if (foo instanceof String) + return (String) foo; + } + foo = JSch.getConfig(key); + if (foo instanceof String) + return (String) foo; + return null; + } + + public void setSocketFactory(SocketFactory sfactory) { + socket_factory = sfactory; + } + + public boolean isConnected() { + return isConnected; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) throws JSchException { + if (socket == null) { + if (timeout < 0) { + throw new JSchException("invalid timeout value"); + } + this.timeout = timeout; + return; + } + try { + socket.setSoTimeout(timeout); + this.timeout = timeout; + } catch (Exception e) { + throw new JSchException(e.toString(), e); + } + } + + public String getServerVersion() { + return Util.byte2str(V_S); + } + + public String getClientVersion() { + return Util.byte2str(V_C); + } + + public void setClientVersion(String cv) { + V_C = Util.str2byte(cv); + } + + public void sendIgnore() throws Exception { + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + packet.reset(); + buf.putByte((byte) SSH_MSG_IGNORE); + write(packet); + } + + private static final byte[] keepalivemsg = Util.str2byte("keepalive@jcraft.com"); + + public void sendKeepAliveMsg() throws Exception { + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + packet.reset(); + buf.putByte((byte) SSH_MSG_GLOBAL_REQUEST); + buf.putString(keepalivemsg); + buf.putByte((byte) 1); + write(packet); + } + + private static final byte[] nomoresessions = Util.str2byte("no-more-sessions@openssh.com"); + + public void noMoreSessionChannels() throws Exception { + Buffer buf = new Buffer(); + Packet packet = new Packet(buf); + packet.reset(); + buf.putByte((byte) SSH_MSG_GLOBAL_REQUEST); + buf.putString(nomoresessions); + buf.putByte((byte) 0); + write(packet); + } + + private HostKey hostkey = null; + + public HostKey getHostKey() { + return hostkey; + } + + public String getHost() { + return host; + } + + public String getUserName() { + return username; + } + + public int getPort() { + return port; + } + + public void setHostKeyAlias(String hostKeyAlias) { + this.hostKeyAlias = hostKeyAlias; + } + + public String getHostKeyAlias() { + return hostKeyAlias; + } + + /** + * Sets the interval to send a keep-alive message. If zero is specified, any keep-alive message + * must not be sent. The default interval is zero. + * + * @param interval the specified interval, in milliseconds. + * @see #getServerAliveInterval() + */ + public void setServerAliveInterval(int interval) throws JSchException { + setTimeout(interval); + this.serverAliveInterval = interval; + } + + /** + * Returns setting for the interval to send a keep-alive message. + * + * @see #setServerAliveInterval(int) + */ + public int getServerAliveInterval() { + return this.serverAliveInterval; + } + + /** + * Sets the number of keep-alive messages which may be sent without receiving any messages back + * from the server. If this threshold is reached while keep-alive messages are being sent, the + * connection will be disconnected. The default value is one. + * + * @param count the specified count + * @see #getServerAliveCountMax() + */ + public void setServerAliveCountMax(int count) { + this.serverAliveCountMax = count; + } + + /** + * Returns setting for the threshold to send keep-alive messages. + * + * @see #setServerAliveCountMax(int) + */ + public int getServerAliveCountMax() { + return this.serverAliveCountMax; + } + + public void setDaemonThread(boolean enable) { + this.daemon_thread = enable; + } + + private String[] checkCiphers(String ciphers) { + if (ciphers == null || ciphers.length() == 0) + return null; + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "CheckCiphers: " + ciphers); + } + + String cipherc2s = getConfig("cipher.c2s"); + String ciphers2c = getConfig("cipher.s2c"); + + Vector result = new Vector<>(); + String[] _ciphers = Util.split(ciphers, ","); + for (int i = 0; i < _ciphers.length; i++) { + String cipher = _ciphers[i]; + if (ciphers2c.indexOf(cipher) == -1 && cipherc2s.indexOf(cipher) == -1) + continue; + if (!checkCipher(getConfig(cipher))) { + result.addElement(cipher); + } + } + if (result.size() == 0) + return null; + String[] foo = new String[result.size()]; + System.arraycopy(result.toArray(), 0, foo, 0, result.size()); + + if (getLogger().isEnabled(Logger.INFO)) { + for (int i = 0; i < foo.length; i++) { + getLogger().log(Logger.INFO, foo[i] + " is not available."); + } + } + + return foo; + } + + static boolean checkCipher(String cipher) { + try { + Class c = Class.forName(cipher).asSubclass(Cipher.class); + Cipher _c = c.getDeclaredConstructor().newInstance(); + _c.init(Cipher.ENCRYPT_MODE, new byte[_c.getBlockSize()], new byte[_c.getIVSize()]); + return true; + } catch (Exception | NoClassDefFoundError e) { + return false; + } + } + + private String[] checkMacs(String macs) { + if (macs == null || macs.length() == 0) + return null; + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "CheckMacs: " + macs); + } + + String macc2s = getConfig("mac.c2s"); + String macs2c = getConfig("mac.s2c"); + + Vector result = new Vector<>(); + String[] _macs = Util.split(macs, ","); + for (int i = 0; i < _macs.length; i++) { + String mac = _macs[i]; + if (macs2c.indexOf(mac) == -1 && macc2s.indexOf(mac) == -1) + continue; + if (!checkMac(getConfig(mac))) { + result.addElement(mac); + } + } + if (result.size() == 0) + return null; + String[] foo = new String[result.size()]; + System.arraycopy(result.toArray(), 0, foo, 0, result.size()); + + if (getLogger().isEnabled(Logger.INFO)) { + for (int i = 0; i < foo.length; i++) { + getLogger().log(Logger.INFO, foo[i] + " is not available."); + } + } + + return foo; + } + + static boolean checkMac(String mac) { + try { + Class c = Class.forName(mac).asSubclass(MAC.class); + MAC _c = c.getDeclaredConstructor().newInstance(); + _c.init(new byte[_c.getBlockSize()]); + return true; + } catch (Exception | NoClassDefFoundError e) { + return false; + } + } + + private String[] checkKexes(String kexes) { + if (kexes == null || kexes.length() == 0) + return null; + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "CheckKexes: " + kexes); + } + + Vector result = new Vector<>(); + String[] _kexes = Util.split(kexes, ","); + for (int i = 0; i < _kexes.length; i++) { + if (!checkKex(this, getConfig(_kexes[i]))) { + result.addElement(_kexes[i]); + } + } + if (result.size() == 0) + return null; + String[] foo = new String[result.size()]; + System.arraycopy(result.toArray(), 0, foo, 0, result.size()); + + if (getLogger().isEnabled(Logger.INFO)) { + for (int i = 0; i < foo.length; i++) { + getLogger().log(Logger.INFO, foo[i] + " is not available."); + } + } + + return foo; + } + + static boolean checkKex(Session s, String kex) { + try { + Class c = Class.forName(kex).asSubclass(KeyExchange.class); + KeyExchange _c = c.getDeclaredConstructor().newInstance(); + _c.doInit(s, null, null, null, null); + return true; + } catch (Exception | NoClassDefFoundError e) { + return false; + } + } + + private String[] checkSignatures(String sigs) { + if (sigs == null || sigs.length() == 0) + return null; + + if (getLogger().isEnabled(Logger.INFO)) { + getLogger().log(Logger.INFO, "CheckSignatures: " + sigs); + } + + Vector result = new Vector<>(); + String[] _sigs = Util.split(sigs, ","); + for (int i = 0; i < _sigs.length; i++) { + try { + Class c = + Class.forName(JSch.getConfig(_sigs[i])).asSubclass(Signature.class); + final Signature sig = c.getDeclaredConstructor().newInstance(); + sig.init(); + } catch (Exception | NoClassDefFoundError e) { + result.addElement(_sigs[i]); + } + } + if (result.size() == 0) + return null; + String[] foo = new String[result.size()]; + System.arraycopy(result.toArray(), 0, foo, 0, result.size()); + if (getLogger().isEnabled(Logger.INFO)) { + for (int i = 0; i < foo.length; i++) { + getLogger().log(Logger.INFO, foo[i] + " is not available."); + } + } + return foo; + } + + /** + * Sets the identityRepository, which will be referred in the public key authentication. The + * default value is null. + * + * @param identityRepository + * @see #getIdentityRepository() + */ + public void setIdentityRepository(IdentityRepository identityRepository) { + this.identityRepository = identityRepository; + } + + /** + * Gets the identityRepository. If this.identityRepository is null, + * JSch#getIdentityRepository() will be invoked. + * + * @see JSch#getIdentityRepository() + */ + IdentityRepository getIdentityRepository() { + if (identityRepository == null) + return jsch.getIdentityRepository(); + return identityRepository; + } + + /** + * Sets the hostkeyRepository, which will be referred in checking host keys. + * + * @param hostkeyRepository + * @see #getHostKeyRepository() + */ + public void setHostKeyRepository(HostKeyRepository hostkeyRepository) { + this.hostkeyRepository = hostkeyRepository; + } + + /** + * Gets the hostkeyRepository. If this.hostkeyRepository is null, + * JSch#getHostKeyRepository() will be invoked. + * + * @see JSch#getHostKeyRepository() + */ + public HostKeyRepository getHostKeyRepository() { + if (hostkeyRepository == null) + return jsch.getHostKeyRepository(); + return hostkeyRepository; + } + + /* + * // setProxyCommand("ssh -l user2 host2 -o 'ProxyCommand ssh user1@host1 nc host2 22' nc %h %p") + * public void setProxyCommand(String command){ setProxy(new ProxyCommand(command)); } + * + * class ProxyCommand implements Proxy { String command; Process p = null; InputStream in = null; + * OutputStream out = null; ProxyCommand(String command){ this.command = command; } public void + * connect(SocketFactory socket_factory, String host, int port, int timeout) throws Exception { + * String _command = command.replace("%h", host); _command = _command.replace("%p", new + * Integer(port).toString()); p = Runtime.getRuntime().exec(_command); in = p.getInputStream(); + * out = p.getOutputStream(); } public Socket getSocket() { return null; } public InputStream + * getInputStream() { return in; } public OutputStream getOutputStream() { return out; } public + * void close() { try{ if(p!=null){ p.getErrorStream().close(); p.getOutputStream().close(); + * p.getInputStream().close(); p.destroy(); p=null; } } catch(IOException e){ } } } + */ + + private void applyConfig() throws JSchException { + ConfigRepository configRepository = jsch.getConfigRepository(); + if (configRepository == null) { + return; + } + + ConfigRepository.Config config = configRepository.getConfig(org_host); + + String value = null; + + if (username == null) { + value = config.getUser(); + if (value != null) + username = value; + } + + value = config.getHostname(); + if (value != null) + host = value; + + int port = config.getPort(); + if (port != -1) + this.port = port; + + checkConfig(config, "kex"); + checkConfig(config, "server_host_key"); + checkConfig(config, "prefer_known_host_key_types"); + checkConfig(config, "enable_server_sig_algs"); + checkConfig(config, "enable_ext_info_in_auth"); + checkConfig(config, "enable_strict_kex"); + checkConfig(config, "require_strict_kex"); + checkConfig(config, "enable_pubkey_auth_query"); + checkConfig(config, "try_additional_pubkey_algorithms"); + checkConfig(config, "enable_auth_none"); + checkConfig(config, "use_sftp_write_flush_workaround"); + + checkConfig(config, "cipher.c2s"); + checkConfig(config, "cipher.s2c"); + checkConfig(config, "mac.c2s"); + checkConfig(config, "mac.s2c"); + checkConfig(config, "compression.c2s"); + checkConfig(config, "compression.s2c"); + checkConfig(config, "compression_level"); + + checkConfig(config, "StrictHostKeyChecking"); + checkConfig(config, "HashKnownHosts"); + checkConfig(config, "PreferredAuthentications"); + checkConfig(config, "PubkeyAcceptedAlgorithms"); + checkConfig(config, "FingerprintHash"); + checkConfig(config, "MaxAuthTries"); + checkConfig(config, "ClearAllForwardings"); + + value = config.getValue("HostKeyAlias"); + if (value != null) + this.setHostKeyAlias(value); + + value = config.getValue("UserKnownHostsFile"); + if (value != null) { + KnownHosts kh = new KnownHosts(jsch); + kh.setKnownHosts(value); + this.setHostKeyRepository(kh); + } + + String[] values = config.getValues("IdentityFile"); + if (values != null) { + String[] global = configRepository.getConfig("").getValues("IdentityFile"); + if (global != null) { + for (int i = 0; i < global.length; i++) { + jsch.addIdentity(global[i]); + } + } else { + global = new String[0]; + } + if (values.length - global.length > 0) { + IdentityRepositoryWrapper ir = + new IdentityRepositoryWrapper(jsch.getIdentityRepository(), true); + for (int i = 0; i < values.length; i++) { + String ifile = values[i]; + for (int j = 0; j < global.length; j++) { + if (!ifile.equals(global[j])) + continue; + ifile = null; + break; + } + if (ifile == null) + continue; + Identity identity = IdentityFile.newInstance(ifile, null, jsch.instLogger); + ir.add(identity); + } + this.setIdentityRepository(ir); + } + } + + value = config.getValue("ServerAliveInterval"); + if (value != null) { + try { + this.setServerAliveInterval(Integer.parseInt(value)); + } catch (NumberFormatException e) { + } + } + + value = config.getValue("ConnectTimeout"); + if (value != null) { + try { + setTimeout(Integer.parseInt(value)); + } catch (NumberFormatException e) { + } + } + + value = config.getValue("MaxAuthTries"); + if (value != null) { + setConfig("MaxAuthTries", value); + } + + value = config.getValue("ClearAllForwardings"); + if (value != null) { + setConfig("ClearAllForwardings", value); + } + } + + private void applyConfigChannel(ChannelSession channel) throws JSchException { + ConfigRepository configRepository = jsch.getConfigRepository(); + if (configRepository == null) { + return; + } + + ConfigRepository.Config config = configRepository.getConfig(org_host); + + String value = null; + + value = config.getValue("ForwardAgent"); + if (value != null) { + channel.setAgentForwarding(value.equals("yes")); + } + + value = config.getValue("RequestTTY"); + if (value != null) { + channel.setPty(value.equals("yes")); + } + } + + private void requestPortForwarding() throws JSchException { + + if (getConfig("ClearAllForwardings").equals("yes")) + return; + + ConfigRepository configRepository = jsch.getConfigRepository(); + if (configRepository == null) { + return; + } + + ConfigRepository.Config config = configRepository.getConfig(org_host); + + String[] values = config.getValues("LocalForward"); + if (values != null) { + for (int i = 0; i < values.length; i++) { + setPortForwardingL(values[i]); + } + } + + values = config.getValues("RemoteForward"); + if (values != null) { + for (int i = 0; i < values.length; i++) { + setPortForwardingR(values[i]); + } + } + } + + private void checkConfig(ConfigRepository.Config config, String key) { + String value = config.getValue(key); + if (value == null && key.equals("PubkeyAcceptedAlgorithms")) + value = config.getValue("PubkeyAcceptedKeyTypes"); + if (value != null) + this.setConfig(key, value); + } + + /** + * Returns the logger being used by this instance of Session. If no particular logger has been + * set, the instance logger of the jsch instance is returned this session belongs to. + * + * @return The logger + */ + public Logger getLogger() { + if (logger != null) { + return logger; + } + return jsch.getInstanceLogger(); + } + + /** + * Sets the logger being used by this instance of Session + * + * @param logger The logger or null if the instance logger of this instance's jsch + * instance should be used + */ + public void setLogger(Logger logger) { + this.logger = logger; + } + + int getBufferMargin() { + int buffer_margin = 32 + // maximum padding length + 32; // margin for deflater; deflater may inflate data + + Cipher _c2scipher = c2scipher; + MAC _c2smac = c2smac; + + // maximum mac length + int mac_length = 20; + if (_c2scipher != null && (_c2scipher.isChaCha20() || _c2scipher.isAEAD())) { + if (_c2scipher.getTagSize() > mac_length) { + mac_length = _c2scipher.getTagSize(); + } + } else if (_c2smac != null) { + if (_c2smac.getBlockSize() > mac_length) { + mac_length = _c2smac.getBlockSize(); + } + } + buffer_margin += mac_length; + + return buffer_margin; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SftpATTRS.java b/files-jsch/src/main/java/com/jcraft/jsch/SftpATTRS.java new file mode 100644 index 0000000..cff4c66 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SftpATTRS.java @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +/* + * uint32 flags uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE uint32 uid present only if + * flag SSH_FILEXFER_ATTR_UIDGID uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID uint32 + * permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS uint32 atime present only if flag + * SSH_FILEXFER_ACMODTIME uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME uint32 + * extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED string extended_type string + * extended_data ... more extended data (extended_type - extended_data pairs), so that number of + * pairs equals extended_count + */ +public class SftpATTRS { + + static final int S_ISUID = 04000; // set user ID on execution + static final int S_ISGID = 02000; // set group ID on execution + static final int S_ISVTX = 01000; // sticky bit ****** NOT DOCUMENTED ***** + + static final int S_IRUSR = 00400; // read by owner + static final int S_IWUSR = 00200; // write by owner + static final int S_IXUSR = 00100; // execute/search by owner + static final int S_IREAD = 00400; // read by owner + static final int S_IWRITE = 00200; // write by owner + static final int S_IEXEC = 00100; // execute/search by owner + + static final int S_IRGRP = 00040; // read by group + static final int S_IWGRP = 00020; // write by group + static final int S_IXGRP = 00010; // execute/search by group + + static final int S_IROTH = 00004; // read by others + static final int S_IWOTH = 00002; // write by others + static final int S_IXOTH = 00001; // execute/search by others + + private static final int pmask = 0xFFF; + + public String getPermissionsString() { + StringBuilder buf = new StringBuilder(10); + + if (isDir()) + buf.append('d'); + else if (isLink()) + buf.append('l'); + else + buf.append('-'); + + if ((permissions & S_IRUSR) != 0) + buf.append('r'); + else + buf.append('-'); + + if ((permissions & S_IWUSR) != 0) + buf.append('w'); + else + buf.append('-'); + + if ((permissions & S_ISUID) != 0) + buf.append('s'); + else if ((permissions & S_IXUSR) != 0) + buf.append('x'); + else + buf.append('-'); + + if ((permissions & S_IRGRP) != 0) + buf.append('r'); + else + buf.append('-'); + + if ((permissions & S_IWGRP) != 0) + buf.append('w'); + else + buf.append('-'); + + if ((permissions & S_ISGID) != 0) + buf.append('s'); + else if ((permissions & S_IXGRP) != 0) + buf.append('x'); + else + buf.append('-'); + + if ((permissions & S_IROTH) != 0) + buf.append('r'); + else + buf.append('-'); + + if ((permissions & S_IWOTH) != 0) + buf.append('w'); + else + buf.append('-'); + + if ((permissions & S_IXOTH) != 0) + buf.append('x'); + else + buf.append('-'); + return (buf.toString()); + } + + public String getAtimeString() { + return toDateString(Integer.toUnsignedLong(atime)); + } + + public String getMtimeString() { + return toDateString(Integer.toUnsignedLong(mtime)); + } + + private static DateTimeFormatter DTF = + DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy", Locale.ROOT); + + static String toDateString(long epochSeconds) { + Instant instant = Instant.ofEpochSecond(epochSeconds); + ZonedDateTime zdt = ZonedDateTime.ofInstant(instant, ZoneId.systemDefault()); + return DTF.format(zdt); + } + + public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001; + public static final int SSH_FILEXFER_ATTR_UIDGID = 0x00000002; + public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004; + public static final int SSH_FILEXFER_ATTR_ACMODTIME = 0x00000008; + public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000; + + static final int S_IFMT = 0xf000; + static final int S_IFIFO = 0x1000; + static final int S_IFCHR = 0x2000; + static final int S_IFDIR = 0x4000; + static final int S_IFBLK = 0x6000; + static final int S_IFREG = 0x8000; + static final int S_IFLNK = 0xa000; + static final int S_IFSOCK = 0xc000; + + int flags = 0; + long size; + int uid; + int gid; + int permissions; + int atime; + int mtime; + String[] extended = null; + + private SftpATTRS() {} + + static SftpATTRS getATTR(Buffer buf) { + SftpATTRS attr = new SftpATTRS(); + attr.flags = buf.getInt(); + if ((attr.flags & SSH_FILEXFER_ATTR_SIZE) != 0) { + attr.size = buf.getLong(); + } + if ((attr.flags & SSH_FILEXFER_ATTR_UIDGID) != 0) { + attr.uid = buf.getInt(); + attr.gid = buf.getInt(); + } + if ((attr.flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) { + attr.permissions = buf.getInt(); + } + if ((attr.flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) { + attr.atime = buf.getInt(); + } + if ((attr.flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) { + attr.mtime = buf.getInt(); + } + if ((attr.flags & SSH_FILEXFER_ATTR_EXTENDED) != 0) { + int count = buf.getInt(); + if (count > 0) { + attr.extended = new String[count * 2]; + for (int i = 0; i < count; i++) { + attr.extended[i * 2] = Util.byte2str(buf.getString()); + attr.extended[i * 2 + 1] = Util.byte2str(buf.getString()); + } + } + } + return attr; + } + + int length() { + int len = 4; + + if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) { + len += 8; + } + if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) { + len += 8; + } + if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) { + len += 4; + } + if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) { + len += 8; + } + if ((flags & SSH_FILEXFER_ATTR_EXTENDED) != 0) { + len += 4; + int count = extended.length / 2; + if (count > 0) { + for (int i = 0; i < count; i++) { + len += 4; + len += extended[i * 2].length(); + len += 4; + len += extended[i * 2 + 1].length(); + } + } + } + return len; + } + + void dump(Buffer buf) { + buf.putInt(flags); + if ((flags & SSH_FILEXFER_ATTR_SIZE) != 0) { + buf.putLong(size); + } + if ((flags & SSH_FILEXFER_ATTR_UIDGID) != 0) { + buf.putInt(uid); + buf.putInt(gid); + } + if ((flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0) { + buf.putInt(permissions); + } + if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) { + buf.putInt(atime); + } + if ((flags & SSH_FILEXFER_ATTR_ACMODTIME) != 0) { + buf.putInt(mtime); + } + if ((flags & SSH_FILEXFER_ATTR_EXTENDED) != 0) { + int count = extended.length / 2; + if (count > 0) { + for (int i = 0; i < count; i++) { + buf.putString(Util.str2byte(extended[i * 2])); + buf.putString(Util.str2byte(extended[i * 2 + 1])); + } + } + } + } + + void setFLAGS(int flags) { + this.flags = flags; + } + + public void setSIZE(long size) { + flags |= SSH_FILEXFER_ATTR_SIZE; + this.size = size; + } + + public void setUIDGID(int uid, int gid) { + flags |= SSH_FILEXFER_ATTR_UIDGID; + this.uid = uid; + this.gid = gid; + } + + public void setACMODTIME(int atime, int mtime) { + flags |= SSH_FILEXFER_ATTR_ACMODTIME; + this.atime = atime; + this.mtime = mtime; + } + + public void setPERMISSIONS(int permissions) { + flags |= SSH_FILEXFER_ATTR_PERMISSIONS; + permissions = (this.permissions & ~pmask) | (permissions & pmask); + this.permissions = permissions; + } + + private boolean isType(int mask) { + return (flags & SSH_FILEXFER_ATTR_PERMISSIONS) != 0 && (permissions & S_IFMT) == mask; + } + + public boolean isReg() { + return isType(S_IFREG); + } + + public boolean isDir() { + return isType(S_IFDIR); + } + + public boolean isChr() { + return isType(S_IFCHR); + } + + public boolean isBlk() { + return isType(S_IFBLK); + } + + public boolean isFifo() { + return isType(S_IFIFO); + } + + public boolean isLink() { + return isType(S_IFLNK); + } + + public boolean isSock() { + return isType(S_IFSOCK); + } + + public int getFlags() { + return flags; + } + + public long getSize() { + return size; + } + + public int getUId() { + return uid; + } + + public int getGId() { + return gid; + } + + public int getPermissions() { + return permissions; + } + + public int getATime() { + return atime; + } + + public int getMTime() { + return mtime; + } + + public String[] getExtended() { + return extended; + } + + @Override + public String toString() { + return (getPermissionsString() + " " + getUId() + " " + getGId() + " " + getSize() + " " + + getMtimeString()); + } + /* + * public String toString(){ return (((flags&SSH_FILEXFER_ATTR_SIZE)!=0) ? ("size:"+size+" ") : + * "")+ (((flags&SSH_FILEXFER_ATTR_UIDGID)!=0) ? ("uid:"+uid+",gid:"+gid+" ") : "")+ + * (((flags&SSH_FILEXFER_ATTR_PERMISSIONS)!=0) ? + * ("permissions:0x"+Integer.toHexString(permissions)+" ") : "")+ + * (((flags&SSH_FILEXFER_ATTR_ACMODTIME)!=0) ? ("atime:"+atime+",mtime:"+mtime+" ") : "")+ + * (((flags&SSH_FILEXFER_ATTR_EXTENDED)!=0) ? ("extended:?"+" ") : ""); } + */ +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SftpException.java b/files-jsch/src/main/java/com/jcraft/jsch/SftpException.java new file mode 100644 index 0000000..89c2451 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SftpException.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class SftpException extends Exception { + private static final long serialVersionUID = -1L; + public int id; + + public SftpException(int id, String message) { + super(message); + this.id = id; + } + + public SftpException(int id, String message, Throwable e) { + super(message, e); + this.id = id; + } + + @Override + public String toString() { + return id + ": " + getMessage(); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SftpProgressMonitor.java b/files-jsch/src/main/java/com/jcraft/jsch/SftpProgressMonitor.java new file mode 100644 index 0000000..de54a02 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SftpProgressMonitor.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface SftpProgressMonitor { + public static final int PUT = 0; + public static final int GET = 1; + public static final long UNKNOWN_SIZE = -1L; + + void init(int op, String src, String dest, long max); + + boolean count(long count); + + void end(); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SftpStatVFS.java b/files-jsch/src/main/java/com/jcraft/jsch/SftpStatVFS.java new file mode 100644 index 0000000..3054d6d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SftpStatVFS.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class SftpStatVFS { + + /* + * It seems data is serializsed according to sys/statvfs.h; for example, + * http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/statvfs.h.html + */ + + private long bsize; + private long frsize; + private long blocks; + private long bfree; + private long bavail; + private long files; + private long ffree; + private long favail; + private long fsid; + private long flag; + private long namemax; + + int flags = 0; + long size; + int uid; + int gid; + int permissions; + int atime; + int mtime; + String[] extended = null; + + private SftpStatVFS() {} + + static SftpStatVFS getStatVFS(Buffer buf) { + SftpStatVFS statvfs = new SftpStatVFS(); + + statvfs.bsize = buf.getLong(); + statvfs.frsize = buf.getLong(); + statvfs.blocks = buf.getLong(); + statvfs.bfree = buf.getLong(); + statvfs.bavail = buf.getLong(); + statvfs.files = buf.getLong(); + statvfs.ffree = buf.getLong(); + statvfs.favail = buf.getLong(); + statvfs.fsid = buf.getLong(); + int flag = (int) buf.getLong(); + statvfs.namemax = buf.getLong(); + + statvfs.flag = (flag & 1 /* SSH2_FXE_STATVFS_ST_RDONLY */) != 0 ? 1 /* ST_RDONLY */ : 0; + statvfs.flag |= (flag & 2 /* SSH2_FXE_STATVFS_ST_NOSUID */) != 0 ? 2 /* ST_NOSUID */ : 0; + + return statvfs; + } + + public long getBlockSize() { + return bsize; + } + + public long getFragmentSize() { + return frsize; + } + + public long getBlocks() { + return blocks; + } + + public long getFreeBlocks() { + return bfree; + } + + public long getAvailBlocks() { + return bavail; + } + + public long getINodes() { + return files; + } + + public long getFreeINodes() { + return ffree; + } + + public long getAvailINodes() { + return favail; + } + + public long getFileSystemID() { + return fsid; + } + + public long getMountFlag() { + return flag; + } + + public long getMaximumFilenameLength() { + return namemax; + } + + public long getSize() { + return getFragmentSize() * getBlocks() / 1024; + } + + public long getUsed() { + return getFragmentSize() * (getBlocks() - getFreeBlocks()) / 1024; + } + + public long getAvailForNonRoot() { + return getFragmentSize() * getAvailBlocks() / 1024; + } + + public long getAvail() { + return getFragmentSize() * getFreeBlocks() / 1024; + } + + public int getCapacity() { + return (int) (100 * (getBlocks() - getFreeBlocks()) / getBlocks()); + } + + // public String toString() { return ""; } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Signature.java b/files-jsch/src/main/java/com/jcraft/jsch/Signature.java new file mode 100644 index 0000000..be107ea --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Signature.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface Signature { + void init() throws Exception; + + void update(byte[] H) throws Exception; + + boolean verify(byte[] sig) throws Exception; + + byte[] sign() throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SignatureDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/SignatureDSA.java new file mode 100644 index 0000000..cf82e4d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SignatureDSA.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface SignatureDSA extends Signature { + void setPubKey(byte[] y, byte[] p, byte[] q, byte[] g) throws Exception; + + void setPrvKey(byte[] x, byte[] p, byte[] q, byte[] g) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SignatureECDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/SignatureECDSA.java new file mode 100644 index 0000000..2a1ae57 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SignatureECDSA.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface SignatureECDSA extends Signature { + void setPubKey(byte[] r, byte[] s) throws Exception; + + void setPrvKey(byte[] s) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SignatureEdDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/SignatureEdDSA.java new file mode 100644 index 0000000..0c7818a --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SignatureEdDSA.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface SignatureEdDSA extends Signature { + void setPubKey(byte[] y_arr) throws Exception; + + void setPrvKey(byte[] bytes) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SignatureRSA.java b/files-jsch/src/main/java/com/jcraft/jsch/SignatureRSA.java new file mode 100644 index 0000000..bd7ed21 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SignatureRSA.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface SignatureRSA extends Signature { + void setPubKey(byte[] e, byte[] n) throws Exception; + + void setPrvKey(byte[] d, byte[] n) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/SocketFactory.java b/files-jsch/src/main/java/com/jcraft/jsch/SocketFactory.java new file mode 100644 index 0000000..e31f340 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/SocketFactory.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; +import java.net.UnknownHostException; + +public interface SocketFactory { + public Socket createSocket(String host, int port) throws IOException, UnknownHostException; + + public InputStream getInputStream(Socket socket) throws IOException; + + public OutputStream getOutputStream(Socket socket) throws IOException; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/UIKeyboardInteractive.java b/files-jsch/src/main/java/com/jcraft/jsch/UIKeyboardInteractive.java new file mode 100644 index 0000000..5288632 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/UIKeyboardInteractive.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface UIKeyboardInteractive { + String[] promptKeyboardInteractive(String destination, String name, String instruction, + String[] prompt, boolean[] echo); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/USocketFactory.java b/files-jsch/src/main/java/com/jcraft/jsch/USocketFactory.java new file mode 100644 index 0000000..761337e --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/USocketFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.file.Path; + +public interface USocketFactory { + SocketChannel connect(Path path) throws IOException; + + ServerSocketChannel bind(Path path) throws IOException; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/UnixDomainSocketFactory.java b/files-jsch/src/main/java/com/jcraft/jsch/UnixDomainSocketFactory.java new file mode 100644 index 0000000..cb0f42f --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/UnixDomainSocketFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.nio.file.Path; + +public class UnixDomainSocketFactory implements USocketFactory { + + public UnixDomainSocketFactory() throws AgentProxyException {} + + @Override + public SocketChannel connect(Path path) throws IOException { + UnixDomainSocketAddress sockAddr = UnixDomainSocketAddress.of(path); + SocketChannel sock = SocketChannel.open(StandardProtocolFamily.UNIX); + sock.configureBlocking(true); + sock.connect(sockAddr); + return sock; + } + + @Override + public ServerSocketChannel bind(Path path) throws IOException { + UnixDomainSocketAddress sockAddr = UnixDomainSocketAddress.of(path); + ServerSocketChannel sock = ServerSocketChannel.open(StandardProtocolFamily.UNIX); + sock.configureBlocking(true); + sock.bind(sockAddr); + return sock; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/UserAuth.java b/files-jsch/src/main/java/com/jcraft/jsch/UserAuth.java new file mode 100644 index 0000000..5ddf503 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/UserAuth.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public abstract class UserAuth { + protected static final int SSH_MSG_USERAUTH_REQUEST = 50; + protected static final int SSH_MSG_USERAUTH_FAILURE = 51; + protected static final int SSH_MSG_USERAUTH_SUCCESS = 52; + protected static final int SSH_MSG_USERAUTH_BANNER = 53; + protected static final int SSH_MSG_USERAUTH_INFO_REQUEST = 60; + protected static final int SSH_MSG_USERAUTH_INFO_RESPONSE = 61; + protected static final int SSH_MSG_USERAUTH_PK_OK = 60; + + protected UserInfo userinfo; + protected Packet packet; + protected Buffer buf; + protected String username; + + public boolean start(Session session) throws Exception { + this.userinfo = session.getUserInfo(); + this.packet = session.packet; + this.buf = packet.getBuffer(); + this.username = session.getUserName(); + return true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java b/files-jsch/src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java new file mode 100644 index 0000000..92e9cd7 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/UserAuthGSSAPIWithMIC.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class UserAuthGSSAPIWithMIC extends UserAuth { + private static final int SSH_MSG_USERAUTH_GSSAPI_RESPONSE = 60; + private static final int SSH_MSG_USERAUTH_GSSAPI_TOKEN = 61; + private static final int SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE = 63; + private static final int SSH_MSG_USERAUTH_GSSAPI_ERROR = 64; + private static final int SSH_MSG_USERAUTH_GSSAPI_ERRTOK = 65; + private static final int SSH_MSG_USERAUTH_GSSAPI_MIC = 66; + + private static final byte[][] supported_oid = { + // OID 1.2.840.113554.1.2.2 in DER + {(byte) 0x6, (byte) 0x9, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, + (byte) 0x12, (byte) 0x1, (byte) 0x2, (byte) 0x2}}; + + private static final String[] supported_method = {"gssapi-with-mic.krb5"}; + + @Override + public boolean start(Session session) throws Exception { + super.start(session); + + byte[] _username = Util.str2byte(username); + + packet.reset(); + + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name(in ISO-10646 UTF-8 encoding) + // string service name(in US-ASCII) + // string "gssapi"(US-ASCII) + // uint32 n, the number of OIDs client supports + // string[n] mechanism OIDS + buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("gssapi-with-mic")); + buf.putInt(supported_oid.length); + for (int i = 0; i < supported_oid.length; i++) { + buf.putString(supported_oid[i]); + } + session.write(packet); + + String method = null; + int command; + while (true) { + buf = session.read(buf); + command = buf.getCommand() & 0xff; + + if (command == SSH_MSG_USERAUTH_FAILURE) { + return false; + } + + if (command == SSH_MSG_USERAUTH_GSSAPI_RESPONSE) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] message = buf.getString(); + + for (int i = 0; i < supported_oid.length; i++) { + if (Util.array_equals(message, supported_oid[i])) { + method = supported_method[i]; + break; + } + } + + if (method == null) { + return false; + } + + break; // success + } + + if (command == SSH_MSG_USERAUTH_BANNER) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] _message = buf.getString(); + byte[] lang = buf.getString(); + String message = Util.byte2str(_message); + if (userinfo != null) { + userinfo.showMessage(message); + } + continue; + } + return false; + } + + GSSContext context = null; + try { + Class c = + Class.forName(session.getConfig(method)).asSubclass(GSSContext.class); + context = c.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + return false; + } + + try { + context.create(username, session.host); + } catch (JSchException e) { + return false; + } + + byte[] token = new byte[0]; + + while (!context.isEstablished()) { + try { + token = context.init(token, 0, token.length); + } catch (JSchException e) { + // TODO + // ERRTOK should be sent? + // byte SSH_MSG_USERAUTH_GSSAPI_ERRTOK + // string error token + return false; + } + + if (token != null) { + packet.reset(); + buf.putByte((byte) SSH_MSG_USERAUTH_GSSAPI_TOKEN); + buf.putString(token); + session.write(packet); + } + + if (!context.isEstablished()) { + buf = session.read(buf); + command = buf.getCommand() & 0xff; + if (command == SSH_MSG_USERAUTH_GSSAPI_ERROR) { + // uint32 major_status + // uint32 minor_status + // string message + // string language tag + + buf = session.read(buf); + command = buf.getCommand() & 0xff; + // return false; + } else if (command == SSH_MSG_USERAUTH_GSSAPI_ERRTOK) { + // string error token + + buf = session.read(buf); + command = buf.getCommand() & 0xff; + // return false; + } + + if (command == SSH_MSG_USERAUTH_FAILURE) { + return false; + } + + buf.getInt(); + buf.getByte(); + buf.getByte(); + token = buf.getString(); + } + } + + Buffer mbuf = new Buffer(); + // string session identifier + // byte SSH_MSG_USERAUTH_REQUEST + // string user name + // string service + // string "gssapi-with-mic" + mbuf.putString(session.getSessionId()); + mbuf.putByte((byte) SSH_MSG_USERAUTH_REQUEST); + mbuf.putString(_username); + mbuf.putString(Util.str2byte("ssh-connection")); + mbuf.putString(Util.str2byte("gssapi-with-mic")); + + byte[] mic = context.getMIC(mbuf.buffer, 0, mbuf.getLength()); + + if (mic == null) { + return false; + } + + packet.reset(); + buf.putByte((byte) SSH_MSG_USERAUTH_GSSAPI_MIC); + buf.putString(mic); + session.write(packet); + + context.dispose(); + + buf = session.read(buf); + command = buf.getCommand() & 0xff; + + if (command == SSH_MSG_USERAUTH_SUCCESS) { + return true; + } else if (command == SSH_MSG_USERAUTH_FAILURE) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] foo = buf.getString(); + int partial_success = buf.getByte(); + // System.err.println(new String(foo)+ + // " partial_success:"+(partial_success!=0)); + if (partial_success != 0) { + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + } + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java b/files-jsch/src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java new file mode 100644 index 0000000..2fbabdb --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/UserAuthKeyboardInteractive.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.Locale; + +class UserAuthKeyboardInteractive extends UserAuth { + @Override + public boolean start(Session session) throws Exception { + super.start(session); + + if (userinfo != null && !(userinfo instanceof UIKeyboardInteractive)) { + return false; + } + + String dest = username + "@" + session.host; + if (session.port != 22) { + dest += (":" + session.port); + } + byte[] password = session.password; + + boolean cancel = false; + + byte[] _username = null; + _username = Util.str2byte(username); + + while (true) { + + if (session.auth_failures >= session.max_auth_tries) { + return false; + } + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name (ISO-10646 UTF-8, as defined in [RFC-2279]) + // string service name (US-ASCII) "ssh-userauth" ? "ssh-connection" + // string "keyboard-interactive" (US-ASCII) + // string language tag (as defined in [RFC-3066]) + // string submethods (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + // buf.putString("ssh-userauth".getBytes()); + buf.putString(Util.str2byte("keyboard-interactive")); + buf.putString(Util.empty); + buf.putString(Util.empty); + session.write(packet); + + boolean firsttime = true; + loop: while (true) { + buf = session.read(buf); + int command = buf.getCommand() & 0xff; + + if (command == SSH_MSG_USERAUTH_SUCCESS) { + return true; + } + if (command == SSH_MSG_USERAUTH_BANNER) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] _message = buf.getString(); + byte[] lang = buf.getString(); + String message = Util.byte2str(_message); + if (userinfo != null) { + userinfo.showMessage(message); + } + continue loop; + } + if (command == SSH_MSG_USERAUTH_FAILURE) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] foo = buf.getString(); + int partial_success = buf.getByte(); + // System.err.println(new String(foo)+ + // " partial_success:"+(partial_success!=0)); + + if (partial_success != 0) { + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + + if (firsttime) { + return false; + // throw new JSchException("USERAUTH KI is not supported"); + // cancel=true; // ?? + } + session.auth_failures++; + break; + } + if (command == SSH_MSG_USERAUTH_INFO_REQUEST) { + firsttime = false; + buf.getInt(); + buf.getByte(); + buf.getByte(); + String name = Util.byte2str(buf.getString()); + String instruction = Util.byte2str(buf.getString()); + String languate_tag = Util.byte2str(buf.getString()); + int num = buf.getInt(); + String[] prompt = new String[num]; + boolean[] echo = new boolean[num]; + for (int i = 0; i < num; i++) { + prompt[i] = Util.byte2str(buf.getString()); + echo[i] = (buf.getByte() != 0); + } + + byte[][] response = null; + + if (password != null && prompt.length == 1 && !echo[0] + && prompt[0].toLowerCase(Locale.ROOT).indexOf("password:") >= 0) { + response = new byte[1][]; + response[0] = password; + password = null; + } else if (num > 0 || (name.length() > 0 || instruction.length() > 0)) { + if (userinfo != null) { + UIKeyboardInteractive kbi = (UIKeyboardInteractive) userinfo; + String[] _response = + kbi.promptKeyboardInteractive(dest, name, instruction, prompt, echo); + if (_response != null) { + response = new byte[_response.length][]; + for (int i = 0; i < _response.length; i++) { + response[i] = _response[i] != null ? Util.str2byte(_response[i]) : Util.empty; + } + } + } + } + + // byte SSH_MSG_USERAUTH_INFO_RESPONSE(61) + // int num-responses + // string response[1] (ISO-10646 UTF-8) + // ... + // string response[num-responses] (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte) SSH_MSG_USERAUTH_INFO_RESPONSE); + if (num > 0 && (response == null || // cancel + num != response.length)) { + + if (response == null) { + // working around the bug in OpenSSH ;-< + buf.putInt(num); + for (int i = 0; i < num; i++) { + buf.putString(Util.empty); + } + } else { + buf.putInt(0); + } + + if (response == null) + cancel = true; + } else { + buf.putInt(num); + for (int i = 0; i < num; i++) { + buf.putString(response[i]); + } + } + session.write(packet); + /* + * if(cancel) break; + */ + continue loop; + } + // throw new JSchException("USERAUTH fail ("+command+")"); + return false; + } + if (cancel) { + throw new JSchAuthCancelException("keyboard-interactive"); + // break; + } + } + // return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/UserAuthNone.java b/files-jsch/src/main/java/com/jcraft/jsch/UserAuthNone.java new file mode 100644 index 0000000..a306a5c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/UserAuthNone.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public class UserAuthNone extends UserAuth { + protected static final int SSH_MSG_SERVICE_REQUEST = 5; + protected static final int SSH_MSG_SERVICE_ACCEPT = 6; + + private String methods; + + @Override + public boolean start(Session session) throws Exception { + super.start(session); + + // send + // byte SSH_MSG_SERVICE_REQUEST(5) + // string service name "ssh-userauth" + packet.reset(); + buf.putByte((byte) SSH_MSG_SERVICE_REQUEST); + buf.putString(Util.str2byte("ssh-userauth")); + session.write(packet); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "SSH_MSG_SERVICE_REQUEST sent"); + } + + // receive + // byte SSH_MSG_SERVICE_ACCEPT(6) + // string service name + buf = session.read(buf); + int command = buf.getCommand(); + + boolean result = (command == SSH_MSG_SERVICE_ACCEPT); + + if (session.getLogger().isEnabled(Logger.INFO)) { + session.getLogger().log(Logger.INFO, "SSH_MSG_SERVICE_ACCEPT received"); + } + if (!result) + return false; + + if (!session.getConfig("enable_auth_none").equals("yes")) + return false; + + byte[] _username = null; + _username = Util.str2byte(username); + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "none" + packet.reset(); + buf.putByte((byte) UserAuth.SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("none")); + session.write(packet); + + loop: while (true) { + buf = session.read(buf); + command = buf.getCommand() & 0xff; + + if (command == UserAuth.SSH_MSG_USERAUTH_SUCCESS) { + return true; + } + if (command == UserAuth.SSH_MSG_USERAUTH_BANNER) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] _message = buf.getString(); + byte[] lang = buf.getString(); + String message = Util.byte2str(_message); + if (userinfo != null) { + try { + userinfo.showMessage(message); + } catch (RuntimeException ee) { + } + } + continue loop; + } + if (command == UserAuth.SSH_MSG_USERAUTH_FAILURE) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] foo = buf.getString(); + int partial_success = buf.getByte(); + setMethods(Util.byte2str(foo)); + // System.err.println("UserAuthNone: " + methods + " partial_success:" + (partial_success != + // 0)); + // if (partial_success != 0) { + // throw new JSchPartialAuthException(new String(foo)); + // } + + break; + } else { + // System.err.println("USERAUTH fail (" + command + ")"); + throw new JSchException("USERAUTH fail (" + command + ")"); + } + } + // throw new JSchException("USERAUTH fail"); + return false; + } + + protected String getMethods() { + return methods; + } + + protected void setMethods(String methods) { + this.methods = methods; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/UserAuthPassword.java b/files-jsch/src/main/java/com/jcraft/jsch/UserAuthPassword.java new file mode 100644 index 0000000..375f11e --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/UserAuthPassword.java @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +class UserAuthPassword extends UserAuth { + private final int SSH_MSG_USERAUTH_PASSWD_CHANGEREQ = 60; + + @Override + public boolean start(Session session) throws Exception { + super.start(session); + + byte[] password = session.password; + String dest = username + "@" + session.host; + if (session.port != 22) { + dest += (":" + session.port); + } + + try { + + while (true) { + + if (session.auth_failures >= session.max_auth_tries) { + return false; + } + + if (password == null) { + if (userinfo == null) { + // throw new JSchException("USERAUTH fail"); + return false; + } + if (!userinfo.promptPassword("Password for " + dest)) { + throw new JSchAuthCancelException("password"); + // break; + } + + String _password = userinfo.getPassword(); + if (_password == null) { + throw new JSchAuthCancelException("password"); + // break; + } + password = Util.str2byte(_password); + } + + byte[] _username = null; + _username = Util.str2byte(username); + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "password" + // boolen FALSE + // string plaintext password (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("password")); + buf.putByte((byte) 0); + buf.putString(password); + session.write(packet); + + loop: while (true) { + buf = session.read(buf); + int command = buf.getCommand() & 0xff; + + if (command == SSH_MSG_USERAUTH_SUCCESS) { + return true; + } + if (command == SSH_MSG_USERAUTH_BANNER) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] _message = buf.getString(); + byte[] lang = buf.getString(); + String message = Util.byte2str(_message); + if (userinfo != null) { + userinfo.showMessage(message); + } + continue loop; + } + if (command == SSH_MSG_USERAUTH_PASSWD_CHANGEREQ) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] instruction = buf.getString(); + byte[] tag = buf.getString(); + if (userinfo == null || !(userinfo instanceof UIKeyboardInteractive)) { + if (userinfo != null) { + userinfo.showMessage("Password must be changed."); + } + return false; + } + + UIKeyboardInteractive kbi = (UIKeyboardInteractive) userinfo; + String[] response; + String name = "Password Change Required"; + String[] prompt = {"New Password: "}; + boolean[] echo = {false}; + response = + kbi.promptKeyboardInteractive(dest, name, Util.byte2str(instruction), prompt, echo); + if (response == null) { + throw new JSchAuthCancelException("password"); + } + + byte[] newpassword = response[0] != null ? Util.str2byte(response[0]) : Util.empty; + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "password" + // boolen TRUE + // string plaintext old password (ISO-10646 UTF-8) + // string plaintext new password (ISO-10646 UTF-8) + packet.reset(); + buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("password")); + buf.putByte((byte) 1); + buf.putString(password); + buf.putString(newpassword); + Util.bzero(newpassword); + response = null; + session.write(packet); + continue loop; + } + if (command == SSH_MSG_USERAUTH_FAILURE) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] foo = buf.getString(); + int partial_success = buf.getByte(); + // System.err.println(new String(foo)+ + // " partial_success:"+(partial_success!=0)); + if (partial_success != 0) { + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + session.auth_failures++; + break; + } else { + // System.err.println("USERAUTH fail ("+buf.getCommand()+")"); + // throw new JSchException("USERAUTH fail ("+buf.getCommand()+")"); + return false; + } + } + + if (password != null) { + Util.bzero(password); + password = null; + } + } + + } finally { + if (password != null) { + Util.bzero(password); + password = null; + } + } + + // throw new JSchException("USERAUTH fail"); + // return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java b/files-jsch/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java new file mode 100644 index 0000000..0b5f01b --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/UserAuthPublicKey.java @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Vector; + +class UserAuthPublicKey extends UserAuth { + + @Override + public boolean start(Session session) throws Exception { + super.start(session); + + Vector identities = session.getIdentityRepository().getIdentities(); + + synchronized (identities) { + if (identities.size() <= 0) { + return false; + } + + String pkmethodstr = session.getConfig("PubkeyAcceptedAlgorithms"); + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, "PubkeyAcceptedAlgorithms = " + pkmethodstr); + } + + String[] not_available_pka = session.getUnavailableSignatures(); + List not_available_pks = (not_available_pka != null && not_available_pka.length > 0 + ? Arrays.asList(not_available_pka) + : Collections.emptyList()); + if (!not_available_pks.isEmpty()) { + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, + "Signature algorithms unavailable for non-agent identities = " + not_available_pks); + } + } + + List pkmethods = Arrays.asList(Util.split(pkmethodstr, ",")); + if (pkmethods.isEmpty()) { + return false; + } + + String[] server_sig_algs = session.getServerSigAlgs(); + if (server_sig_algs != null && server_sig_algs.length > 0) { + List _known = new ArrayList<>(); + List _unknown = new ArrayList<>(); + for (String pkmethod : pkmethods) { + boolean add = false; + for (String server_sig_alg : server_sig_algs) { + if (pkmethod.equals(server_sig_alg)) { + add = true; + break; + } + } + + if (add) { + _known.add(pkmethod); + } else { + _unknown.add(pkmethod); + } + } + + if (!_known.isEmpty()) { + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, + "PubkeyAcceptedAlgorithms in server-sig-algs = " + _known); + } + } + + if (!_unknown.isEmpty()) { + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, + "PubkeyAcceptedAlgorithms not in server-sig-algs = " + _unknown); + } + } + + if (!_known.isEmpty() && !_unknown.isEmpty()) { + boolean success = _start(session, identities, _known, not_available_pks); + if (success) { + return true; + } + + return _start(session, identities, _unknown, not_available_pks); + } + } else { + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, + "No server-sig-algs found, using PubkeyAcceptedAlgorithms = " + pkmethods); + } + } + + return _start(session, identities, pkmethods, not_available_pks); + } + } + + private boolean _start(Session session, List identities, List pkmethods, + List not_available_pks) throws Exception { + if (session.auth_failures >= session.max_auth_tries) { + return false; + } + + boolean use_pk_auth_query = session.getConfig("enable_pubkey_auth_query").equals("yes"); + boolean try_other_pkmethods = + session.getConfig("try_additional_pubkey_algorithms").equals("yes"); + + List rsamethods = new ArrayList<>(); + List nonrsamethods = new ArrayList<>(); + for (String pkmethod : pkmethods) { + if (pkmethod.equals("ssh-rsa") || pkmethod.equals("rsa-sha2-256") + || pkmethod.equals("rsa-sha2-512") || pkmethod.equals("ssh-rsa-sha224@ssh.com") + || pkmethod.equals("ssh-rsa-sha256@ssh.com") || pkmethod.equals("ssh-rsa-sha384@ssh.com") + || pkmethod.equals("ssh-rsa-sha512@ssh.com")) { + rsamethods.add(pkmethod); + } else { + nonrsamethods.add(pkmethod); + } + } + + byte[] _username = Util.str2byte(username); + + int command; + + iloop: for (Identity identity : identities) { + + if (session.auth_failures >= session.max_auth_tries) { + return false; + } + + // System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted()); + decryptKey(session, identity); + // System.err.println("UserAuthPublicKey: identity.isEncrypted()="+identity.isEncrypted()); + + String _ipkmethod = identity.getAlgName(); + List ipkmethods = null; + if (_ipkmethod.equals("ssh-rsa")) { + ipkmethods = new ArrayList<>(rsamethods); + } else if (nonrsamethods.contains(_ipkmethod)) { + ipkmethods = new ArrayList<>(1); + ipkmethods.add(_ipkmethod); + } + if (ipkmethods == null || ipkmethods.isEmpty()) { + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, + _ipkmethod + " cannot be used as public key type for identity " + identity.getName()); + } + continue iloop; + } + + ipkmethodloop: while (!ipkmethods.isEmpty() + && session.auth_failures < session.max_auth_tries) { + byte[] pubkeyblob = identity.getPublicKeyBlob(); + List pkmethodsuccesses = null; + + if (pubkeyblob != null && use_pk_auth_query) { + command = SSH_MSG_USERAUTH_FAILURE; + Iterator it = ipkmethods.iterator(); + loop3: while (it.hasNext()) { + String ipkmethod = it.next(); + it.remove(); + if (not_available_pks.contains(ipkmethod) && !(identity instanceof AgentIdentity)) { + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, + ipkmethod + " not available for identity " + identity.getName()); + } + continue loop3; + } + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "publickey" + // boolen FALSE + // string public key algorithm name + // string public key blob + packet.reset(); + buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("publickey")); + buf.putByte((byte) 0); + buf.putString(Util.str2byte(ipkmethod)); + buf.putString(pubkeyblob); + session.write(packet); + + loop1: while (true) { + buf = session.read(buf); + command = buf.getCommand() & 0xff; + + if (command == SSH_MSG_USERAUTH_PK_OK) { + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, ipkmethod + " preauth success"); + } + pkmethodsuccesses = new ArrayList<>(1); + pkmethodsuccesses.add(ipkmethod); + break loop3; + } else if (command == SSH_MSG_USERAUTH_FAILURE) { + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, ipkmethod + " preauth failure"); + } + continue loop3; + } else if (command == SSH_MSG_USERAUTH_BANNER) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] _message = buf.getString(); + byte[] lang = buf.getString(); + String message = Util.byte2str(_message); + if (userinfo != null) { + userinfo.showMessage(message); + } + continue loop1; + } else { + // System.err.println("USERAUTH fail ("+command+")"); + // throw new JSchException("USERAUTH fail ("+command+")"); + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, + ipkmethod + " preauth failure command (" + command + ")"); + } + continue loop3; + } + } + } + + if (command != SSH_MSG_USERAUTH_PK_OK) { + continue iloop; + } + } + + if (identity.isEncrypted()) + continue iloop; + if (pubkeyblob == null) + pubkeyblob = identity.getPublicKeyBlob(); + + // System.err.println("UserAuthPublicKey: pubkeyblob="+pubkeyblob); + + if (pubkeyblob == null) + continue iloop; + if (pkmethodsuccesses == null) + pkmethodsuccesses = ipkmethods; + if (pkmethodsuccesses.isEmpty()) + continue iloop; + + Iterator it = pkmethodsuccesses.iterator(); + loop4: while (it.hasNext() && session.auth_failures < session.max_auth_tries) { + String pkmethodsuccess = it.next(); + it.remove(); + if (not_available_pks.contains(pkmethodsuccess) && !(identity instanceof AgentIdentity)) { + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, + pkmethodsuccess + " not available for identity " + identity.getName()); + } + continue loop4; + } + + // send + // byte SSH_MSG_USERAUTH_REQUEST(50) + // string user name + // string service name ("ssh-connection") + // string "publickey" + // boolen TRUE + // string public key algorithm name + // string public key blob + // string signature + packet.reset(); + buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST); + buf.putString(_username); + buf.putString(Util.str2byte("ssh-connection")); + buf.putString(Util.str2byte("publickey")); + buf.putByte((byte) 1); + buf.putString(Util.str2byte(pkmethodsuccess)); + buf.putString(pubkeyblob); + + // byte[] tmp=new byte[buf.index-5]; + // System.arraycopy(buf.buffer, 5, tmp, 0, tmp.length); + // buf.putString(signature); + + byte[] sid = session.getSessionId(); + int sidlen = sid.length; + byte[] tmp = new byte[4 + sidlen + buf.index - 5]; + tmp[0] = (byte) (sidlen >>> 24); + tmp[1] = (byte) (sidlen >>> 16); + tmp[2] = (byte) (sidlen >>> 8); + tmp[3] = (byte) (sidlen); + System.arraycopy(sid, 0, tmp, 4, sidlen); + System.arraycopy(buf.buffer, 5, tmp, 4 + sidlen, buf.index - 5); + byte[] signature = identity.getSignature(tmp, pkmethodsuccess); + if (signature == null) { // for example, too long key length. + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, pkmethodsuccess + " signature failure"); + } + continue loop4; + } + buf.putString(signature); + session.write(packet); + + loop2: while (true) { + buf = session.read(buf); + command = buf.getCommand() & 0xff; + + if (command == SSH_MSG_USERAUTH_SUCCESS) { + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, pkmethodsuccess + " auth success"); + } + return true; + } else if (command == SSH_MSG_USERAUTH_BANNER) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] _message = buf.getString(); + byte[] lang = buf.getString(); + String message = Util.byte2str(_message); + if (userinfo != null) { + userinfo.showMessage(message); + } + continue loop2; + } else if (command == SSH_MSG_USERAUTH_FAILURE) { + buf.getInt(); + buf.getByte(); + buf.getByte(); + byte[] foo = buf.getString(); + int partial_success = buf.getByte(); + // System.err.println(new String(foo)+ + // " partial_success:"+(partial_success!=0)); + if (partial_success != 0) { + throw new JSchPartialAuthException(Util.byte2str(foo)); + } + session.auth_failures++; + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, pkmethodsuccess + " auth failure"); + } + + if (session.auth_failures >= session.max_auth_tries) { + return false; + } else if (try_other_pkmethods) { + break loop2; + } else { + continue iloop; + } + } + // System.err.println("USERAUTH fail ("+command+")"); + // throw new JSchException("USERAUTH fail ("+command+")"); + if (session.getLogger().isEnabled(Logger.DEBUG)) { + session.getLogger().log(Logger.DEBUG, + pkmethodsuccess + " auth failure command (" + command + ")"); + } + break loop2; + } + } + } + } + return false; + } + + private void decryptKey(Session session, Identity identity) throws JSchException { + byte[] passphrase = null; + int count = 5; + while (true) { + if ((identity.isEncrypted() && passphrase == null)) { + if (userinfo == null) + throw new JSchException("USERAUTH fail"); + if (identity.isEncrypted() + && !userinfo.promptPassphrase("Passphrase for " + identity.getName())) { + throw new JSchAuthCancelException("publickey"); + // throw new JSchException("USERAUTH cancel"); + // break; + } + String _passphrase = userinfo.getPassphrase(); + if (_passphrase != null) { + passphrase = Util.str2byte(_passphrase); + } + } + + if (!identity.isEncrypted() || passphrase != null) { + if (identity.setPassphrase(passphrase)) { + if (passphrase != null + && (session.getIdentityRepository() instanceof IdentityRepositoryWrapper)) { + ((IdentityRepositoryWrapper) session.getIdentityRepository()).check(); + } + break; + } + } + Util.bzero(passphrase); + passphrase = null; + count--; + if (count == 0) + break; + } + + Util.bzero(passphrase); + passphrase = null; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/UserInfo.java b/files-jsch/src/main/java/com/jcraft/jsch/UserInfo.java new file mode 100644 index 0000000..7dbb47d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/UserInfo.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface UserInfo { + String getPassphrase(); + + String getPassword(); + + boolean promptPassword(String message); + + boolean promptPassphrase(String message); + + boolean promptYesNo(String message); + + void showMessage(String message); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Util.java b/files-jsch/src/main/java/com/jcraft/jsch/Util.java new file mode 100644 index 0000000..b7ef747 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Util.java @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketTimeoutException; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Vector; + +class Util { + + private static final byte[] b64 = + Util.str2byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="); + + private static byte val(byte foo) { + if (foo == '=') + return 0; + for (int j = 0; j < b64.length; j++) { + if (foo == b64[j]) + return (byte) j; + } + return 0; + } + + static byte[] fromBase64(byte[] buf, int start, int length) throws JSchException { + try { + byte[] foo = new byte[length]; + int j = 0; + for (int i = start; i < start + length; i += 4) { + foo[j] = (byte) ((val(buf[i]) << 2) | ((val(buf[i + 1]) & 0x30) >>> 4)); + if (buf[i + 2] == (byte) '=') { + j++; + break; + } + foo[j + 1] = (byte) (((val(buf[i + 1]) & 0x0f) << 4) | ((val(buf[i + 2]) & 0x3c) >>> 2)); + if (buf[i + 3] == (byte) '=') { + j += 2; + break; + } + foo[j + 2] = (byte) (((val(buf[i + 2]) & 0x03) << 6) | (val(buf[i + 3]) & 0x3f)); + j += 3; + } + byte[] bar = new byte[j]; + System.arraycopy(foo, 0, bar, 0, j); + return bar; + } catch (ArrayIndexOutOfBoundsException e) { + throw new JSchException("fromBase64: invalid base64 data", e); + } + } + + static byte[] toBase64(byte[] buf, int start, int length, boolean include_pad) { + + byte[] tmp = new byte[length * 2]; + int i, j, k; + + int foo = (length / 3) * 3 + start; + i = 0; + for (j = start; j < foo; j += 3) { + k = (buf[j] >>> 2) & 0x3f; + tmp[i++] = b64[k]; + k = (buf[j] & 0x03) << 4 | (buf[j + 1] >>> 4) & 0x0f; + tmp[i++] = b64[k]; + k = (buf[j + 1] & 0x0f) << 2 | (buf[j + 2] >>> 6) & 0x03; + tmp[i++] = b64[k]; + k = buf[j + 2] & 0x3f; + tmp[i++] = b64[k]; + } + + foo = (start + length) - foo; + if (foo == 1) { + k = (buf[j] >>> 2) & 0x3f; + tmp[i++] = b64[k]; + k = ((buf[j] & 0x03) << 4) & 0x3f; + tmp[i++] = b64[k]; + if (include_pad) { + tmp[i++] = (byte) '='; + tmp[i++] = (byte) '='; + } + } else if (foo == 2) { + k = (buf[j] >>> 2) & 0x3f; + tmp[i++] = b64[k]; + k = (buf[j] & 0x03) << 4 | (buf[j + 1] >>> 4) & 0x0f; + tmp[i++] = b64[k]; + k = ((buf[j + 1] & 0x0f) << 2) & 0x3f; + tmp[i++] = b64[k]; + if (include_pad) { + tmp[i++] = (byte) '='; + } + } + byte[] bar = new byte[i]; + System.arraycopy(tmp, 0, bar, 0, i); + return bar; + + // return sun.misc.BASE64Encoder().encode(buf); + } + + static String[] split(String foo, String split) { + if (foo == null) + return null; + byte[] buf = Util.str2byte(foo); + Vector bar = new Vector<>(); + int start = 0; + int index; + while (true) { + index = foo.indexOf(split, start); + if (index >= 0) { + bar.addElement(Util.byte2str(buf, start, index - start)); + start = index + 1; + continue; + } + bar.addElement(Util.byte2str(buf, start, buf.length - start)); + break; + } + String[] result = new String[bar.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = bar.elementAt(i); + } + return result; + } + + static boolean glob(byte[] pattern, byte[] name) { + return glob0(pattern, 0, name, 0); + } + + private static boolean glob0(byte[] pattern, int pattern_index, byte[] name, int name_index) { + if (name.length > 0 && name[0] == '.') { + if (pattern.length > 0 && pattern[0] == '.') { + if (pattern.length == 2 && pattern[1] == '*') + return true; + return glob(pattern, pattern_index + 1, name, name_index + 1); + } + return false; + } + return glob(pattern, pattern_index, name, name_index); + } + + private static boolean glob(byte[] pattern, int pattern_index, byte[] name, int name_index) { + // System.err.println("glob: "+new String(pattern)+", "+pattern_index+" "+new String(name)+", + // "+name_index); + + int patternlen = pattern.length; + if (patternlen == 0) + return false; + + int namelen = name.length; + int i = pattern_index; + int j = name_index; + + while (i < patternlen && j < namelen) { + if (pattern[i] == '\\') { + if (i + 1 == patternlen) + return false; + i++; + if (pattern[i] != name[j]) + return false; + i += skipUTF8Char(pattern[i]); + j += skipUTF8Char(name[j]); + continue; + } + + if (pattern[i] == '*') { + while (i < patternlen) { + if (pattern[i] == '*') { + i++; + continue; + } + break; + } + if (patternlen == i) + return true; + + byte foo = pattern[i]; + if (foo == '?') { + while (j < namelen) { + if (glob(pattern, i, name, j)) { + return true; + } + j += skipUTF8Char(name[j]); + } + return false; + } else if (foo == '\\') { + if (i + 1 == patternlen) + return false; + i++; + foo = pattern[i]; + while (j < namelen) { + if (foo == name[j]) { + if (glob(pattern, i + skipUTF8Char(foo), name, j + skipUTF8Char(name[j]))) { + return true; + } + } + j += skipUTF8Char(name[j]); + } + return false; + } + + while (j < namelen) { + if (foo == name[j]) { + if (glob(pattern, i, name, j)) { + return true; + } + } + j += skipUTF8Char(name[j]); + } + return false; + } + + if (pattern[i] == '?') { + i++; + j += skipUTF8Char(name[j]); + continue; + } + + if (pattern[i] != name[j]) + return false; + + i += skipUTF8Char(pattern[i]); + j += skipUTF8Char(name[j]); + + if (!(j < namelen)) { // name is end + if (!(i < patternlen)) { // pattern is end + return true; + } + if (pattern[i] == '*') { + break; + } + } + continue; + } + + if (i == patternlen && j == namelen) + return true; + + if (!(j < namelen) && // name is end + pattern[i] == '*') { + boolean ok = true; + while (i < patternlen) { + if (pattern[i++] != '*') { + ok = false; + break; + } + } + return ok; + } + + return false; + } + + static String quote(String path) { + byte[] _path = str2byte(path); + int count = 0; + for (int i = 0; i < _path.length; i++) { + byte b = _path[i]; + if (b == '\\' || b == '?' || b == '*') + count++; + } + if (count == 0) + return path; + byte[] _path2 = new byte[_path.length + count]; + for (int i = 0, j = 0; i < _path.length; i++) { + byte b = _path[i]; + if (b == '\\' || b == '?' || b == '*') { + _path2[j++] = '\\'; + } + _path2[j++] = b; + } + return byte2str(_path2); + } + + static String unquote(String path) { + byte[] foo = str2byte(path); + byte[] bar = unquote(foo); + if (foo.length == bar.length) + return path; + return byte2str(bar); + } + + static byte[] unquote(byte[] path) { + int pathlen = path.length; + int i = 0; + while (i < pathlen) { + if (path[i] == '\\') { + if (i + 1 == pathlen) + break; + System.arraycopy(path, i + 1, path, i, path.length - (i + 1)); + pathlen--; + i++; + continue; + } + i++; + } + if (pathlen == path.length) + return path; + byte[] foo = new byte[pathlen]; + System.arraycopy(path, 0, foo, 0, pathlen); + return foo; + } + + private static String[] chars = + {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; + + static String getFingerPrint(HASH hash, byte[] data, boolean include_prefix, boolean force_hex) { + try { + hash.init(); + hash.update(data, 0, data.length); + byte[] foo = hash.digest(); + StringBuilder sb = new StringBuilder(); + if (include_prefix) { + sb.append(hash.name()); + sb.append(":"); + } + if (force_hex || hash.name().equals("MD5")) { + int bar; + for (int i = 0; i < foo.length; i++) { + bar = foo[i] & 0xff; + sb.append(chars[(bar >>> 4) & 0xf]); + sb.append(chars[(bar) & 0xf]); + if (i + 1 < foo.length) + sb.append(":"); + } + } else { + byte[] b64str = toBase64(foo, 0, foo.length, false); + sb.append(byte2str(b64str, 0, b64str.length)); + } + return sb.toString(); + } catch (Exception e) { + return "???"; + } + } + + static boolean array_equals(byte[] foo, byte bar[]) { + int i = foo.length; + if (i != bar.length) + return false; + for (int j = 0; j < i; j++) { + if (foo[j] != bar[j]) + return false; + } + // try{while(true){i--; if(foo[i]!=bar[i])return false;}}catch(Exception e){} + return true; + } + + static Socket createSocket(String host, int port, int timeout) throws JSchException { + Socket socket = new Socket(); + try { + socket.connect(new InetSocketAddress(host, port), timeout); + return socket; + } catch (Exception e) { + try { + socket.close(); + } catch (Exception ignore) { + } + + String message = + e instanceof SocketTimeoutException ? "timeout: socket is not established" : e.toString(); + throw new JSchException(message, e); + } + } + + static byte[] str2byte(String str, Charset encoding) { + if (str == null) + return null; + return str.getBytes(encoding); + } + + static byte[] str2byte(String str) { + return str2byte(str, StandardCharsets.UTF_8); + } + + static String byte2str(byte[] str, Charset encoding) { + return byte2str(str, 0, str.length, encoding); + } + + static String byte2str(byte[] str, int s, int l, Charset encoding) { + return new String(str, s, l, encoding); + } + + static String byte2str(byte[] str) { + return byte2str(str, 0, str.length, StandardCharsets.UTF_8); + } + + static String byte2str(byte[] str, int s, int l) { + return byte2str(str, s, l, StandardCharsets.UTF_8); + } + + static String toHex(byte[] str) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length; i++) { + String foo = Integer.toHexString(str[i] & 0xff); + sb.append("0x" + (foo.length() == 1 ? "0" : "") + foo); + if (i + 1 < str.length) + sb.append(":"); + } + return sb.toString(); + } + + static final byte[] empty = str2byte(""); + + /* + * static byte[] char2byte(char[] foo){ int len=0; for(int i=0; i>>8); bar[j++]=(byte)foo[i]; } } return bar; } + */ + static void bzero(byte[] foo) { + if (foo == null) + return; + for (int i = 0; i < foo.length; i++) + foo[i] = 0; + } + + static String diffString(String str, String[] not_available) { + String[] stra = Util.split(str, ","); + String result = null; + loop: for (int i = 0; i < stra.length; i++) { + for (int j = 0; j < not_available.length; j++) { + if (stra[i].equals(not_available[j])) { + continue loop; + } + } + if (result == null) { + result = stra[i]; + } else { + result = result + "," + stra[i]; + } + } + return result; + } + + static String checkTilde(String str) { + try { + if (str.startsWith("~")) { + str = str.replace("~", System.getProperty("user.home")); + } + } catch (SecurityException e) { + } + return str; + } + + private static int skipUTF8Char(byte b) { + if ((byte) (b & 0x80) == 0) + return 1; + if ((byte) (b & 0xe0) == (byte) 0xc0) + return 2; + if ((byte) (b & 0xf0) == (byte) 0xe0) + return 3; + return 1; + } + + static byte[] fromFile(String _file) throws IOException { + _file = checkTilde(_file); + File file = new File(_file); + try (InputStream fis = new FileInputStream(_file)) { + byte[] result = new byte[(int) (file.length())]; + int len = 0; + while (true) { + int i = fis.read(result, len, result.length - len); + if (i <= 0) + break; + len += i; + } + return result; + } + } + + static boolean arraysequals(byte[] a, byte[] b) { + if (a.length != b.length) + return false; + int res = 0; + for (int i = 0; i < a.length; i++) { + res |= a[i] ^ b[i]; + } + return res == 0; + } + + static String getSystemEnv(String name) { + try { + return System.getenv(name); + } catch (SecurityException e) { + return null; + } + } + + static String getSystemProperty(String key) { + try { + return System.getProperty(key); + } catch (SecurityException e) { + return null; + } + } + + static String getSystemProperty(String key, String def) { + try { + return System.getProperty(key, def); + } catch (SecurityException e) { + return def; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/Version.java b/files-jsch/src/main/java/com/jcraft/jsch/Version.java new file mode 100644 index 0000000..929b97d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/Version.java @@ -0,0 +1,10 @@ +package com.jcraft.jsch; + +final class Version { + + private static final String VERSION = "0.2.18"; + + static String getVersion() { + return VERSION; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/XDH.java b/files-jsch/src/main/java/com/jcraft/jsch/XDH.java new file mode 100644 index 0000000..57a290d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/XDH.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch; + +public interface XDH { + void init(String name, int keylen) throws Exception; + + byte[] getSecret(byte[] u) throws Exception; + + byte[] getQ() throws Exception; + + boolean validate(byte[] u) throws Exception; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/annotations/SuppressForbiddenApi.java b/files-jsch/src/main/java/com/jcraft/jsch/annotations/SuppressForbiddenApi.java new file mode 100644 index 0000000..fb9a356 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/annotations/SuppressForbiddenApi.java @@ -0,0 +1,13 @@ +package com.jcraft.jsch.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, + ElementType.PARAMETER, ElementType.TYPE}) +public @interface SuppressForbiddenApi { + String[] value(); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jbcrypt/BCrypt.java b/files-jsch/src/main/java/com/jcraft/jsch/jbcrypt/BCrypt.java new file mode 100644 index 0000000..332f879 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jbcrypt/BCrypt.java @@ -0,0 +1,718 @@ +// Copyright (c) 2006 Damien Miller +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package com.jcraft.jsch.jbcrypt; + +import java.nio.charset.StandardCharsets; +import java.security.DigestException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + * BCrypt implements OpenBSD-style Blowfish password hashing using the scheme described in "A + * Future-Adaptable Password Scheme" by Niels Provos and David Mazieres. + * + *

+ * This password hashing system tries to thwart off-line password cracking using a + * computationally-intensive hashing algorithm, based on Bruce Schneier's Blowfish cipher. The work + * factor of the algorithm is parameterised, so it can be increased as computers get faster. + * + *

+ * Usage is really simple. To hash a password for the first time, call the hashpw method with a + * random salt, like this: + * + *

+ * + * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
+ *
+ * + *

+ * To check whether a plaintext password matches one that has been hashed previously, use the + * checkpw method: + * + *

+ * + * if (BCrypt.checkpw(candidate_password, stored_hash))
+ *     System.out.println("It matches");
+ * else
+ *     System.out.println("It does not match");
+ *
+ * + *

+ * The gensalt() method takes an optional parameter (log_rounds) that determines the computational + * complexity of the hashing: + * + *

+ * + * String strong_salt = BCrypt.gensalt(10)
+ * String stronger_salt = BCrypt.gensalt(12)
+ *
+ * + *

+ * The amount of work increases exponentially (2**log_rounds), so each increment is twice as much + * work. The default log_rounds is 10, and the valid range is 4 to 30. + * + * @author Damien Miller + * @version 0.2 + */ +public class BCrypt { + // BCrypt parameters + private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10; + private static final int BCRYPT_SALT_LEN = 16; + + // Blowfish parameters + private static final int BLOWFISH_NUM_ROUNDS = 16; + + // Initial contents of key schedule + private static final int P_orig[] = {0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, + 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b}; + private static final int S_orig[] = {0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, + 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, + 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, + 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, + 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, + 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, + 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, + 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, + 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, + 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, + 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, + 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, + 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, + 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, + 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, + 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, + 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, + 0x8c4f5573, 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, + 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, + 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, + 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, + 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, + 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, + 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, + 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, + 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, + 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, + 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 0x4b7a70e9, + 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, + 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, + 0x4d2d38e6, 0xf0255dc1, 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, + 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, + 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9, 0x7ca92ff6, + 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 0xa9446146, + 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, + 0xbcaf89af, 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, + 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, + 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6, + 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, + 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, + 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, + 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, + 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, + 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, + 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, + 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19, + 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, + 0x1ac24696, 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, + 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, + 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, + 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, + 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 0x3372f092, + 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, + 0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, + 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, + 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, + 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, + 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, + 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, + 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, + 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, + 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, + 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, + 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, + 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, + 0xcad18115, 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, + 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, + 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b, + 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, + 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, + 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, + 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, + 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, + 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, + 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, + 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, + 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, + 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, + 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, + 0xc0f586e0, 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, + 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, + 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, + 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, + 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, + 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, + 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, + 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, + 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 0x80e4a915, + 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, + 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, + 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, + 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, + 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, + 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, + 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, + 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, + 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, + 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, + 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, + 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, + 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, + 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, + 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, + 0xe0d392df, 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, + 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, + 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292, + 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, + 0xcd769c2b, 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, + 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, + 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6}; + + // OpenBSD IV: "OxychromaticBlowfishSwatDynamite" in big endian + private static final int[] openbsd_iv = new int[] {0x4f787963, 0x68726f6d, 0x61746963, 0x426c6f77, + 0x66697368, 0x53776174, 0x44796e61, 0x6d697465,}; + + // bcrypt IV: "OrpheanBeholderScryDoubt". The C implementation calls + // this "ciphertext", but it is really plaintext or an IV. We keep + // the name to make code comparison easier. + private static final int bf_crypt_ciphertext[] = + {0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274}; + + // Table for Base64 encoding + private static final char base64_code[] = {'.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', + 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; + + // Table for Base64 decoding + private static final byte index_64[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, + -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1}; + + // Expanded Blowfish key + private int P[]; + private int S[]; + + /** + * Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note that this is + * *not* compatible with the standard MIME-base64 encoding. + * + * @param d the byte array to encode + * @param len the number of bytes to encode + * @return base64-encoded string + * @exception IllegalArgumentException if the length is invalid + */ + private static String encode_base64(byte d[], int len) throws IllegalArgumentException { + int off = 0; + StringBuilder rs = new StringBuilder(); + int c1, c2; + + if (len <= 0 || len > d.length) + throw new IllegalArgumentException("Invalid len"); + + while (off < len) { + c1 = d[off++] & 0xff; + rs.append(base64_code[(c1 >> 2) & 0x3f]); + c1 = (c1 & 0x03) << 4; + if (off >= len) { + rs.append(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 4) & 0x0f; + rs.append(base64_code[c1 & 0x3f]); + c1 = (c2 & 0x0f) << 2; + if (off >= len) { + rs.append(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 6) & 0x03; + rs.append(base64_code[c1 & 0x3f]); + rs.append(base64_code[c2 & 0x3f]); + } + return rs.toString(); + } + + /** + * Look up the 3 bits base64-encoded by the specified character, range-checking againt conversion + * table + * + * @param x the base64-encoded value + * @return the decoded value of x + */ + private static byte char64(char x) { + if ((int) x < 0 || (int) x > index_64.length) + return -1; + return index_64[(int) x]; + } + + /** + * Decode a string encoded using bcrypt's base64 scheme to a byte array. Note that this is *not* + * compatible with the standard MIME-base64 encoding. + * + * @param s the string to decode + * @param maxolen the maximum number of bytes to decode + * @return an array containing the decoded bytes + * @throws IllegalArgumentException if maxolen is invalid + */ + private static byte[] decode_base64(String s, int maxolen) throws IllegalArgumentException { + StringBuilder rs = new StringBuilder(); + int off = 0, slen = s.length(), olen = 0; + byte ret[]; + byte c1, c2, c3, c4, o; + + if (maxolen <= 0) + throw new IllegalArgumentException("Invalid maxolen"); + + while (off < slen - 1 && olen < maxolen) { + c1 = char64(s.charAt(off++)); + c2 = char64(s.charAt(off++)); + if (c1 == -1 || c2 == -1) + break; + o = (byte) (c1 << 2); + o |= (byte) ((c2 & 0x30) >> 4); + rs.append((char) o); + if (++olen >= maxolen || off >= slen) + break; + c3 = char64(s.charAt(off++)); + if (c3 == -1) + break; + o = (byte) ((c2 & 0x0f) << 4); + o |= (byte) ((c3 & 0x3c) >> 2); + rs.append((char) o); + if (++olen >= maxolen || off >= slen) + break; + c4 = char64(s.charAt(off++)); + o = (byte) ((c3 & 0x03) << 6); + o |= c4; + rs.append((char) o); + ++olen; + } + + ret = new byte[olen]; + for (off = 0; off < olen; off++) + ret[off] = (byte) rs.charAt(off); + return ret; + } + + /** + * Blowfish encipher a single 64-bit block encoded as two 32-bit halves + * + * @param lr an array containing the two 32-bit half blocks + * @param off the position in the array of the blocks + */ + private final void encipher(int lr[], int off) { + int i, n, l = lr[off], r = lr[off + 1]; + + l ^= P[0]; + for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) { + // Feistel substitution on left word + n = S[(l >> 24) & 0xff]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[++i]; + + // Feistel substitution on right word + n = S[(r >> 24) & 0xff]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[++i]; + } + lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; + lr[off + 1] = l; + } + + /** + * Cycically extract a word of key material + * + * @param data the string to extract the data from + * @param offp a "pointer" (as a one-entry array) to the current offset into data + * @return the next word of material from data + */ + private static int streamtoword(byte data[], int offp[]) { + int i; + int word = 0; + int off = offp[0]; + + for (i = 0; i < 4; i++) { + word = (word << 8) | (data[off] & 0xff); + off = (off + 1) % data.length; + } + + offp[0] = off; + return word; + } + + /** Initialise the Blowfish key schedule */ + private void init_key() { + P = P_orig.clone(); + S = S_orig.clone(); + } + + /** + * Key the Blowfish cipher + * + * @param key an array containing the key + */ + private void key(byte key[]) { + int i; + int koffp[] = {0}; + int lr[] = {0, 0}; + int plen = P.length, slen = S.length; + + for (i = 0; i < plen; i++) + P[i] = P[i] ^ streamtoword(key, koffp); + + for (i = 0; i < plen; i += 2) { + encipher(lr, 0); + P[i] = lr[0]; + P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) { + encipher(lr, 0); + S[i] = lr[0]; + S[i + 1] = lr[1]; + } + } + + /** + * Perform the "enhanced key schedule" step described by Provos and Mazieres in "A + * Future-Adaptable Password Scheme" http://www.openbsd.org/papers/bcrypt-paper.ps + * + * @param data salt information + * @param key password information + */ + private void ekskey(byte data[], byte key[]) { + int i; + int koffp[] = {0}, doffp[] = {0}; + int lr[] = {0, 0}; + int plen = P.length, slen = S.length; + + for (i = 0; i < plen; i++) + P[i] = P[i] ^ streamtoword(key, koffp); + + for (i = 0; i < plen; i += 2) { + lr[0] ^= streamtoword(data, doffp); + lr[1] ^= streamtoword(data, doffp); + encipher(lr, 0); + P[i] = lr[0]; + P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) { + lr[0] ^= streamtoword(data, doffp); + lr[1] ^= streamtoword(data, doffp); + encipher(lr, 0); + S[i] = lr[0]; + S[i + 1] = lr[1]; + } + } + + /** Compatibility with new OpenBSD function. */ + public void hash(byte[] hpass, byte[] hsalt, byte[] output) { + init_key(); + ekskey(hsalt, hpass); + for (int i = 0; i < 64; i++) { + key(hsalt); + key(hpass); + } + + int[] buf = new int[openbsd_iv.length]; + System.arraycopy(openbsd_iv, 0, buf, 0, openbsd_iv.length); + for (int i = 0; i < 8; i += 2) { + for (int j = 0; j < 64; j++) { + encipher(buf, i); + } + } + + for (int i = 0, j = 0; i < buf.length; i++) { + // Output of this is little endian + output[j++] = (byte) (buf[i] & 0xff); + output[j++] = (byte) ((buf[i] >> 8) & 0xff); + output[j++] = (byte) ((buf[i] >> 16) & 0xff); + output[j++] = (byte) ((buf[i] >> 24) & 0xff); + } + } + + /** Compatibility with new OpenBSD function. */ + public void pbkdf(byte[] password, byte[] salt, int rounds, byte[] output) { + try { + MessageDigest sha512 = MessageDigest.getInstance("SHA-512"); + + int nblocks = (output.length + 31) / 32; + byte[] hpass = sha512.digest(password); + + byte[] hsalt = new byte[64]; + byte[] block_b = new byte[4]; + byte[] out = new byte[32]; + byte[] tmp = new byte[32]; + for (int block = 1; block <= nblocks; block++) { + // Block count is in big endian + block_b[0] = (byte) ((block >> 24) & 0xFF); + block_b[1] = (byte) ((block >> 16) & 0xFF); + block_b[2] = (byte) ((block >> 8) & 0xFF); + block_b[3] = (byte) (block & 0xFF); + + sha512.reset(); + sha512.update(salt); + sha512.update(block_b); + sha512.digest(hsalt, 0, hsalt.length); + + hash(hpass, hsalt, out); + System.arraycopy(out, 0, tmp, 0, out.length); + + for (int round = 1; round < rounds; round++) { + sha512.reset(); + sha512.update(tmp); + sha512.digest(hsalt, 0, hsalt.length); + + hash(hpass, hsalt, tmp); + + for (int i = 0; i < tmp.length; i++) { + out[i] ^= tmp[i]; + } + } + + for (int i = 0; i < out.length; i++) { + int idx = i * nblocks + (block - 1); + if (idx < output.length) { + output[idx] = out[i]; + } + } + } + } catch (DigestException e) { + throw new RuntimeException(e); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + /** + * Perform the central password hashing step in the bcrypt scheme + * + * @param password the password to hash + * @param salt the binary salt to hash with the password + * @param log_rounds the binary logarithm of the number of rounds of hashing to apply + * @param cdata the plaintext to encrypt + * @return an array containing the binary hashed password + */ + public byte[] crypt_raw(byte password[], byte salt[], int log_rounds, int cdata[]) { + int rounds, i, j; + int clen = cdata.length; + byte ret[]; + + if (log_rounds < 4 || log_rounds > 30) + throw new IllegalArgumentException("Bad number of rounds"); + rounds = 1 << log_rounds; + if (salt.length != BCRYPT_SALT_LEN) + throw new IllegalArgumentException("Bad salt length"); + + init_key(); + ekskey(salt, password); + for (i = 0; i != rounds; i++) { + key(password); + key(salt); + } + + for (i = 0; i < 64; i++) { + for (j = 0; j < (clen >> 1); j++) + encipher(cdata, j << 1); + } + + ret = new byte[clen * 4]; + for (i = 0, j = 0; i < clen; i++) { + ret[j++] = (byte) ((cdata[i] >> 24) & 0xff); + ret[j++] = (byte) ((cdata[i] >> 16) & 0xff); + ret[j++] = (byte) ((cdata[i] >> 8) & 0xff); + ret[j++] = (byte) (cdata[i] & 0xff); + } + return ret; + } + + /** + * Hash a password using the OpenBSD bcrypt scheme + * + * @param password the password to hash + * @param salt the salt to hash with (perhaps generated using BCrypt.gensalt) + * @return the hashed password + */ + public static String hashpw(String password, String salt) { + BCrypt B; + String real_salt; + byte passwordb[], saltb[], hashed[]; + char minor = (char) 0; + int rounds, off = 0; + StringBuilder rs = new StringBuilder(); + + if (salt.charAt(0) != '$' || salt.charAt(1) != '2') + throw new IllegalArgumentException("Invalid salt version"); + if (salt.charAt(2) == '$') + off = 3; + else { + minor = salt.charAt(2); + if (minor != 'a' || salt.charAt(3) != '$') + throw new IllegalArgumentException("Invalid salt revision"); + off = 4; + } + + // Extract number of rounds + if (salt.charAt(off + 2) > '$') + throw new IllegalArgumentException("Missing salt rounds"); + rounds = Integer.parseInt(salt.substring(off, off + 2)); + + real_salt = salt.substring(off + 3, off + 25); + passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes(StandardCharsets.UTF_8); + + saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); + + B = new BCrypt(); + hashed = B.crypt_raw(passwordb, saltb, rounds, bf_crypt_ciphertext.clone()); + + rs.append("$2"); + if (minor >= 'a') + rs.append(minor); + rs.append("$"); + if (rounds < 10) + rs.append("0"); + if (rounds > 30) { + throw new IllegalArgumentException("rounds exceeds maximum (30)"); + } + rs.append(Integer.toString(rounds)); + rs.append("$"); + rs.append(encode_base64(saltb, saltb.length)); + rs.append(encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1)); + return rs.toString(); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method + * + * @param log_rounds the log2 of the number of rounds of hashing to apply - the work factor + * therefore increases as 2**log_rounds. + * @param random an instance of SecureRandom to use + * @return an encoded salt value + */ + public static String gensalt(int log_rounds, SecureRandom random) { + StringBuilder rs = new StringBuilder(); + byte rnd[] = new byte[BCRYPT_SALT_LEN]; + + random.nextBytes(rnd); + + rs.append("$2a$"); + if (log_rounds < 10) + rs.append("0"); + if (log_rounds > 30) { + throw new IllegalArgumentException("log_rounds exceeds maximum (30)"); + } + rs.append(Integer.toString(log_rounds)); + rs.append("$"); + rs.append(encode_base64(rnd, rnd.length)); + return rs.toString(); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method + * + * @param log_rounds the log2 of the number of rounds of hashing to apply - the work factor + * therefore increases as 2**log_rounds. + * @return an encoded salt value + */ + public static String gensalt(int log_rounds) { + return gensalt(log_rounds, new SecureRandom()); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method, selecting a reasonable default for the + * number of hashing rounds to apply + * + * @return an encoded salt value + */ + public static String gensalt() { + return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS); + } + + /** + * Check that a plaintext password matches a previously hashed one + * + * @param plaintext the plaintext password to verify + * @param hashed the previously-hashed password + * @return true if the passwords match, false otherwise + */ + public static boolean checkpw(String plaintext, String hashed) { + String try_pw = hashpw(plaintext, hashed); + byte hashed_bytes[] = hashed.getBytes(StandardCharsets.UTF_8); + byte try_bytes[] = try_pw.getBytes(StandardCharsets.UTF_8); + if (hashed_bytes.length != try_bytes.length) + return false; + byte ret = 0; + for (int i = 0; i < try_bytes.length; i++) + ret |= (byte) (hashed_bytes[i] ^ try_bytes[i]); + return ret == 0; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jbcrypt/JBCrypt.java b/files-jsch/src/main/java/com/jcraft/jsch/jbcrypt/JBCrypt.java new file mode 100644 index 0000000..4b1c0a8 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jbcrypt/JBCrypt.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jbcrypt; + +public class JBCrypt implements com.jcraft.jsch.BCrypt { + private BCrypt bcrypt; + private byte[] salt; + private int iteration; + + @Override + public void init(byte[] salt, int iteration) throws Exception { + bcrypt = new BCrypt(); + this.salt = salt; + this.iteration = iteration; + } + + @Override + public byte[] getKey(byte[] pass, int size) { + byte[] key = new byte[size]; + bcrypt.pbkdf(pass, salt, iteration, key); + return key; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/AES128CBC.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES128CBC.java new file mode 100644 index 0000000..860f457 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES128CBC.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class AES128CBC implements com.jcraft.jsch.Cipher { + private static final int ivsize = 16; + private static final int bsize = 16; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > ivsize) { + tmp = new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + + try { + SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); + cipher = Cipher.getInstance("AES/CBC/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/AES128CTR.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES128CTR.java new file mode 100644 index 0000000..1cd5dd8 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES128CTR.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class AES128CTR implements com.jcraft.jsch.Cipher { + private static final int ivsize = 16; + private static final int bsize = 16; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > ivsize) { + tmp = new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + + try { + SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); + cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/AES128GCM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES128GCM.java new file mode 100644 index 0000000..d5255cb --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES128GCM.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class AES128GCM extends AESGCM { + // Actually the key size, not block size + private static final int bsize = 16; + + @Override + public int getBlockSize() { + return bsize; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/AES192CBC.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES192CBC.java new file mode 100644 index 0000000..1d1e16b --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES192CBC.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class AES192CBC implements com.jcraft.jsch.Cipher { + private static final int ivsize = 16; + private static final int bsize = 24; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > ivsize) { + tmp = new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + try { + SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); + cipher = Cipher.getInstance("AES/CBC/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/AES192CTR.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES192CTR.java new file mode 100644 index 0000000..8d26639 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES192CTR.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class AES192CTR implements com.jcraft.jsch.Cipher { + private static final int ivsize = 16; + private static final int bsize = 24; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > ivsize) { + tmp = new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + try { + SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); + cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/AES256CBC.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES256CBC.java new file mode 100644 index 0000000..9a736c4 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES256CBC.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2005-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class AES256CBC implements com.jcraft.jsch.Cipher { + private static final int ivsize = 16; + private static final int bsize = 32; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > ivsize) { + tmp = new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + try { + SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); + cipher = Cipher.getInstance("AES/CBC/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/AES256CTR.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES256CTR.java new file mode 100644 index 0000000..c1f72dc --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES256CTR.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class AES256CTR implements com.jcraft.jsch.Cipher { + private static final int ivsize = 16; + private static final int bsize = 32; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > ivsize) { + tmp = new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + try { + SecretKeySpec keyspec = new SecretKeySpec(key, "AES"); + cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + keyspec, new IvParameterSpec(iv)); + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/AES256GCM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES256GCM.java new file mode 100644 index 0000000..66156ba --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/AES256GCM.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class AES256GCM extends AESGCM { + // Actually the key size, not block size + private static final int bsize = 32; + + @Override + public int getBlockSize() { + return bsize; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/AESGCM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/AESGCM.java new file mode 100644 index 0000000..ae94e7c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/AESGCM.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import java.nio.ByteBuffer; +import javax.crypto.Cipher; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +abstract class AESGCM implements com.jcraft.jsch.Cipher { + // Actually the block size, not IV size + private static final int ivsize = 16; + private static final int tagsize = 16; + private Cipher cipher; + private SecretKeySpec keyspec; + private int mode; + private ByteBuffer iv; + private long initcounter; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getTagSize() { + return tagsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > 12) { + tmp = new byte[12]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + int bsize = getBlockSize(); + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + this.mode = + ((mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE) ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE); + this.iv = ByteBuffer.wrap(iv); + this.initcounter = this.iv.getLong(4); + try { + keyspec = new SecretKeySpec(key, "AES"); + cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(this.mode, keyspec, new GCMParameterSpec(tagsize * 8, iv)); + } catch (Exception e) { + cipher = null; + keyspec = null; + this.iv = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public void updateAAD(byte[] foo, int s1, int len) throws Exception { + cipher.updateAAD(foo, s1, len); + } + + @Override + public void doFinal(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.doFinal(foo, s1, len, bar, s2); + long newcounter = iv.getLong(4) + 1; + if (newcounter == initcounter) { + throw new IllegalStateException("GCM IV would be reused"); + } + iv.putLong(4, newcounter); + cipher.init(mode, keyspec, new GCMParameterSpec(tagsize * 8, iv.array())); + } + + @Override + public boolean isCBC() { + return false; + } + + @Override + public boolean isAEAD() { + return true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/ARCFOUR.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/ARCFOUR.java new file mode 100644 index 0000000..5b6ea72 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/ARCFOUR.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +public class ARCFOUR implements com.jcraft.jsch.Cipher { + private static final int ivsize = 8; + private static final int bsize = 16; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + + try { + cipher = Cipher.getInstance("RC4"); + SecretKeySpec _key = new SecretKeySpec(key, "RC4"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + _key); + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java new file mode 100644 index 0000000..7d10faf --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/ARCFOUR128.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +public class ARCFOUR128 implements com.jcraft.jsch.Cipher { + private static final int ivsize = 8; + private static final int bsize = 16; + private static final int skip = 1536; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + try { + cipher = Cipher.getInstance("RC4"); + SecretKeySpec _key = new SecretKeySpec(key, "RC4"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + _key); + byte[] foo = new byte[1]; + for (int i = 0; i < skip; i++) { + cipher.update(foo, 0, 1, foo, 0); + } + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/ARCFOUR256.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/ARCFOUR256.java new file mode 100644 index 0000000..5f822a1 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/ARCFOUR256.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +public class ARCFOUR256 implements com.jcraft.jsch.Cipher { + private static final int ivsize = 8; + private static final int bsize = 32; + private static final int skip = 1536; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + try { + cipher = Cipher.getInstance("RC4"); + SecretKeySpec _key = new SecretKeySpec(key, "RC4"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + _key); + byte[] foo = new byte[1]; + for (int i = 0; i < skip; i++) { + cipher.update(foo, 0, 1, foo, 0); + } + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/BlowfishCBC.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/BlowfishCBC.java new file mode 100644 index 0000000..443993e --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/BlowfishCBC.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class BlowfishCBC implements com.jcraft.jsch.Cipher { + private static final int ivsize = 8; + private static final int bsize = 16; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > ivsize) { + tmp = new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + try { + SecretKeySpec skeySpec = new SecretKeySpec(key, "Blowfish"); + cipher = Cipher.getInstance("Blowfish/CBC/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + skeySpec, new IvParameterSpec(iv)); + } catch (Exception e) { + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/BlowfishCTR.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/BlowfishCTR.java new file mode 100644 index 0000000..40b52d1 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/BlowfishCTR.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class BlowfishCTR implements com.jcraft.jsch.Cipher { + private static final int ivsize = 8; + private static final int bsize = 32; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > ivsize) { + tmp = new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + try { + SecretKeySpec skeySpec = new SecretKeySpec(key, "Blowfish"); + cipher = Cipher.getInstance("Blowfish/CTR/NoPadding"); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + skeySpec, new IvParameterSpec(iv)); + } catch (Exception e) { + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/DH.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/DH.java new file mode 100644 index 0000000..c9673e6 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/DH.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.JSchException; +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import javax.crypto.KeyAgreement; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.DHPublicKeySpec; + +public class DH implements com.jcraft.jsch.DH { + BigInteger p; + BigInteger g; + BigInteger e; // my public key + byte[] e_array; + BigInteger f; // your public key + + private KeyPairGenerator myKpairGen; + private KeyAgreement myKeyAgree; + + @Override + public void init() throws Exception { + myKpairGen = KeyPairGenerator.getInstance("DH"); + myKeyAgree = KeyAgreement.getInstance("DH"); + } + + @Override + public byte[] getE() throws Exception { + if (e == null) { + DHParameterSpec dhSkipParamSpec = new DHParameterSpec(p, g); + myKpairGen.initialize(dhSkipParamSpec); + KeyPair myKpair = myKpairGen.generateKeyPair(); + myKeyAgree.init(myKpair.getPrivate()); + e = ((DHPublicKey) (myKpair.getPublic())).getY(); + e_array = e.toByteArray(); + } + return e_array; + } + + @Override + public byte[] getK() throws Exception { + KeyFactory myKeyFac = KeyFactory.getInstance("DH"); + DHPublicKeySpec keySpec = new DHPublicKeySpec(f, p, g); + PublicKey yourPubKey = myKeyFac.generatePublic(keySpec); + myKeyAgree.doPhase(yourPubKey, true); + return myKeyAgree.generateSecret(); + } + + @Override + public void setP(byte[] p) { + setP(new BigInteger(1, p)); + } + + @Override + public void setG(byte[] g) { + setG(new BigInteger(1, g)); + } + + @Override + public void setF(byte[] f) { + setF(new BigInteger(1, f)); + } + + void setP(BigInteger p) { + this.p = p; + } + + void setG(BigInteger g) { + this.g = g; + } + + void setF(BigInteger f) { + this.f = f; + } + + // e, f must be in [1, p-1]. + @Override + public void checkRange() throws Exception { + /* + * checkRange(e); checkRange(f); + */ + } + + private void checkRange(BigInteger tmp) throws Exception { + BigInteger one = BigInteger.ONE; + BigInteger p_1 = p.subtract(one); + // !(1 bsize) { + byte[] tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, bsize); + key = tmp; + } + SecretKeySpec skey = new SecretKeySpec(key, algorithm); + mac = Mac.getInstance(algorithm); + mac.init(skey); + } + + private final byte[] tmp = new byte[4]; + + @Override + public void update(int i) { + tmp[0] = (byte) (i >>> 24); + tmp[1] = (byte) (i >>> 16); + tmp[2] = (byte) (i >>> 8); + tmp[3] = (byte) i; + update(tmp, 0, 4); + } + + @Override + public void update(byte foo[], int s, int l) { + mac.update(foo, s, l); + } + + @Override + public void doFinal(byte[] buf, int offset) { + try { + mac.doFinal(buf, offset); + } catch (ShortBufferException e) { + if (JSch.getLogger().isEnabled(Logger.ERROR)) { + JSch.getLogger().log(Logger.ERROR, e.getMessage(), e); + } + } + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isEtM() { + return etm; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD5.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD5.java new file mode 100644 index 0000000..681d3ba --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD5.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACMD5 extends HMAC { + public HMACMD5() { + name = "hmac-md5"; + bsize = 16; + algorithm = "HmacMD5"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD596.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD596.java new file mode 100644 index 0000000..78b36a9 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD596.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACMD596 extends HMACMD5 { + public HMACMD596() { + name = "hmac-md5-96"; + } + + @Override + public int getBlockSize() { + return 12; + }; + + private final byte[] _buf16 = new byte[16]; + + @Override + public void doFinal(byte[] buf, int offset) { + super.doFinal(_buf16, 0); + System.arraycopy(_buf16, 0, buf, offset, 12); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD596ETM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD596ETM.java new file mode 100644 index 0000000..7c800f5 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD596ETM.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACMD596ETM extends HMACMD596 { + public HMACMD596ETM() { + name = "hmac-md5-96-etm@openssh.com"; + etm = true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD5ETM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD5ETM.java new file mode 100644 index 0000000..e490fad --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACMD5ETM.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACMD5ETM extends HMACMD5 { + public HMACMD5ETM() { + name = "hmac-md5-etm@openssh.com"; + etm = true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA1.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA1.java new file mode 100644 index 0000000..e764bf7 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA1.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA1 extends HMAC { + public HMACSHA1() { + name = "hmac-sha1"; + bsize = 20; + algorithm = "HmacSHA1"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA196.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA196.java new file mode 100644 index 0000000..06a586c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA196.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA196 extends HMACSHA1 { + + public HMACSHA196() { + name = "hmac-sha1-96"; + } + + @Override + public int getBlockSize() { + return 12; + }; + + private final byte[] _buf20 = new byte[20]; + + @Override + public void doFinal(byte[] buf, int offset) { + super.doFinal(_buf20, 0); + System.arraycopy(_buf20, 0, buf, offset, 12); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA196ETM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA196ETM.java new file mode 100644 index 0000000..084c9f8 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA196ETM.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA196ETM extends HMACSHA196 { + + public HMACSHA196ETM() { + name = "hmac-sha1-96-etm@openssh.com"; + etm = true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA1ETM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA1ETM.java new file mode 100644 index 0000000..7698aee --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA1ETM.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA1ETM extends HMACSHA1 { + public HMACSHA1ETM() { + name = "hmac-sha1-etm@openssh.com"; + etm = true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA224SSHCOM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA224SSHCOM.java new file mode 100644 index 0000000..da7f075 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA224SSHCOM.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA224SSHCOM extends HMAC { + public HMACSHA224SSHCOM() { + name = "hmac-sha224@ssh.com"; + bsize = 28; + algorithm = "HmacSHA224"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA256.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA256.java new file mode 100644 index 0000000..b6c526b --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA256.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA256 extends HMAC { + public HMACSHA256() { + name = "hmac-sha2-256"; + bsize = 32; + algorithm = "HmacSHA256"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA2562SSHCOM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA2562SSHCOM.java new file mode 100644 index 0000000..d136f7c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA2562SSHCOM.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA2562SSHCOM extends HMAC { + public HMACSHA2562SSHCOM() { + name = "hmac-sha256-2@ssh.com"; + bsize = 32; + algorithm = "HmacSHA256"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA256ETM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA256ETM.java new file mode 100644 index 0000000..5b77628 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA256ETM.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA256ETM extends HMACSHA256 { + public HMACSHA256ETM() { + name = "hmac-sha2-256-etm@openssh.com"; + etm = true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA256SSHCOM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA256SSHCOM.java new file mode 100644 index 0000000..a657266 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA256SSHCOM.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +// This MAC appears to use a shortened keysize of 16 bytes instead of 32 bytes. +// See discussion at https://github.com/ronf/asyncssh/issues/399. +public class HMACSHA256SSHCOM extends HMAC { + public HMACSHA256SSHCOM() { + name = "hmac-sha256@ssh.com"; + bsize = 16; + algorithm = "HmacSHA256"; + } + + @Override + public int getBlockSize() { + return 32; + }; +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA384SSHCOM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA384SSHCOM.java new file mode 100644 index 0000000..7849ede --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA384SSHCOM.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA384SSHCOM extends HMAC { + public HMACSHA384SSHCOM() { + name = "hmac-sha384@ssh.com"; + bsize = 48; + algorithm = "HmacSHA384"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA512.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA512.java new file mode 100644 index 0000000..426a327 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA512.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA512 extends HMAC { + public HMACSHA512() { + name = "hmac-sha2-512"; + bsize = 64; + algorithm = "HmacSHA512"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA512ETM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA512ETM.java new file mode 100644 index 0000000..94abe7c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA512ETM.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA512ETM extends HMACSHA512 { + public HMACSHA512ETM() { + name = "hmac-sha2-512-etm@openssh.com"; + etm = true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA512SSHCOM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA512SSHCOM.java new file mode 100644 index 0000000..1edf740 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/HMACSHA512SSHCOM.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class HMACSHA512SSHCOM extends HMAC { + public HMACSHA512SSHCOM() { + name = "hmac-sha512@ssh.com"; + bsize = 64; + algorithm = "HmacSHA512"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java new file mode 100644 index 0000000..29c06c9 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenDSA.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.interfaces.DSAKey; +import java.security.interfaces.DSAParams; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; + +public class KeyPairGenDSA implements com.jcraft.jsch.KeyPairGenDSA { + byte[] x; // private + byte[] y; // public + byte[] p; + byte[] q; + byte[] g; + + @Override + public void init(int key_size) throws Exception { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA"); + keyGen.initialize(key_size, new SecureRandom()); + KeyPair pair = keyGen.generateKeyPair(); + PublicKey pubKey = pair.getPublic(); + PrivateKey prvKey = pair.getPrivate(); + + x = ((DSAPrivateKey) prvKey).getX().toByteArray(); + y = ((DSAPublicKey) pubKey).getY().toByteArray(); + + DSAParams params = ((DSAKey) prvKey).getParams(); + p = params.getP().toByteArray(); + q = params.getQ().toByteArray(); + g = params.getG().toByteArray(); + } + + @Override + public byte[] getX() { + return x; + } + + @Override + public byte[] getY() { + return y; + } + + @Override + public byte[] getP() { + return p; + } + + @Override + public byte[] getQ() { + return q; + } + + @Override + public byte[] getG() { + return g; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java new file mode 100644 index 0000000..ac67849 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenECDSA.java @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.JSchException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; + +public class KeyPairGenECDSA implements com.jcraft.jsch.KeyPairGenECDSA { + byte[] d; + byte[] r; + byte[] s; + ECPublicKey pubKey; + ECPrivateKey prvKey; + ECParameterSpec params; + + @Override + public void init(int key_size) throws Exception { + String name = null; + if (key_size == 256) + name = "secp256r1"; + else if (key_size == 384) + name = "secp384r1"; + else if (key_size == 521) + name = "secp521r1"; + else + throw new JSchException("unsupported key size: " + key_size); + + for (int i = 0; i < 1000; i++) { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); + ECGenParameterSpec ecsp = new ECGenParameterSpec(name); + kpg.initialize(ecsp); + KeyPair kp = kpg.genKeyPair(); + prvKey = (ECPrivateKey) kp.getPrivate(); + pubKey = (ECPublicKey) kp.getPublic(); + params = pubKey.getParams(); + d = prvKey.getS().toByteArray(); + ECPoint w = pubKey.getW(); + r = w.getAffineX().toByteArray(); + s = w.getAffineY().toByteArray(); + + if (r.length != s.length) + continue; + if (key_size == 256 && r.length == 32) + break; + if (key_size == 384 && r.length == 48) + break; + if (key_size == 521 && r.length == 66) + break; + } + if (d.length < r.length) { + d = insert0(d); + } + } + + @Override + public byte[] getD() { + return d; + } + + @Override + public byte[] getR() { + return r; + } + + @Override + public byte[] getS() { + return s; + } + + ECPublicKey getPublicKey() { + return pubKey; + } + + ECPrivateKey getPrivateKey() { + return prvKey; + } + + private byte[] insert0(byte[] buf) { + // if ((buf[0] & 0x80) == 0) return buf; + byte[] tmp = new byte[buf.length + 1]; + System.arraycopy(buf, 0, tmp, 1, buf.length); + Util.bzero(buf); + return tmp; + } + + private byte[] chop0(byte[] buf) { + if (buf[0] != 0 || (buf[1] & 0x80) == 0) + return buf; + byte[] tmp = new byte[buf.length - 1]; + System.arraycopy(buf, 1, tmp, 0, tmp.length); + Util.bzero(buf); + return tmp; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenEdDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenEdDSA.java new file mode 100644 index 0000000..fa02abb --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenEdDSA.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.EdECPrivateKey; +import java.security.interfaces.EdECPublicKey; +import java.security.spec.EdECPoint; +import java.util.Arrays; + +public class KeyPairGenEdDSA implements com.jcraft.jsch.KeyPairGenEdDSA { + byte[] prv; // private + byte[] pub; // public + int keylen; + + @Override + public void init(String name, int keylen) throws Exception { + this.keylen = keylen; + + KeyPairGenerator keyGen = KeyPairGenerator.getInstance(name); + KeyPair pair = keyGen.generateKeyPair(); + + EdECPublicKey pubKey = (EdECPublicKey) pair.getPublic(); + EdECPrivateKey prvKey = (EdECPrivateKey) pair.getPrivate(); + EdECPoint point = pubKey.getPoint(); + + prv = prvKey.getBytes().get(); + pub = rotate(point.getY().toByteArray()); + if (point.isXOdd()) { + pub[pub.length - 1] |= (byte) 0x80; + } + } + + @Override + public byte[] getPrv() { + return prv; + } + + @Override + public byte[] getPub() { + return pub; + } + + private byte[] rotate(byte[] in) { + int len = in.length; + byte[] out = new byte[len]; + + for (int i = 0; i < len; i++) { + out[i] = in[len - i - 1]; + } + + return Arrays.copyOf(out, keylen); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenRSA.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenRSA.java new file mode 100644 index 0000000..5a04940 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/KeyPairGenRSA.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +public class KeyPairGenRSA implements com.jcraft.jsch.KeyPairGenRSA { + byte[] d; // private + byte[] e; // public + byte[] n; + + byte[] c; // coefficient + byte[] ep; // exponent p + byte[] eq; // exponent q + byte[] p; // prime p + byte[] q; // prime q + + @Override + public void init(int key_size) throws Exception { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); + keyGen.initialize(key_size, new SecureRandom()); + KeyPair pair = keyGen.generateKeyPair(); + + PublicKey pubKey = pair.getPublic(); + PrivateKey prvKey = pair.getPrivate(); + + d = ((RSAPrivateKey) prvKey).getPrivateExponent().toByteArray(); + e = ((RSAPublicKey) pubKey).getPublicExponent().toByteArray(); + n = ((RSAPrivateKey) prvKey).getModulus().toByteArray(); + + c = ((RSAPrivateCrtKey) prvKey).getCrtCoefficient().toByteArray(); + ep = ((RSAPrivateCrtKey) prvKey).getPrimeExponentP().toByteArray(); + eq = ((RSAPrivateCrtKey) prvKey).getPrimeExponentQ().toByteArray(); + p = ((RSAPrivateCrtKey) prvKey).getPrimeP().toByteArray(); + q = ((RSAPrivateCrtKey) prvKey).getPrimeQ().toByteArray(); + } + + @Override + public byte[] getD() { + return d; + } + + @Override + public byte[] getE() { + return e; + } + + @Override + public byte[] getN() { + return n; + } + + @Override + public byte[] getC() { + return c; + } + + @Override + public byte[] getEP() { + return ep; + } + + @Override + public byte[] getEQ() { + return eq; + } + + @Override + public byte[] getP() { + return p; + } + + @Override + public byte[] getQ() { + return q; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/MD5.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/MD5.java new file mode 100644 index 0000000..21afabb --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/MD5.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.HASH; +import java.security.MessageDigest; + +public class MD5 implements HASH { + MessageDigest md; + + @Override + public int getBlockSize() { + return 16; + } + + @Override + public void init() throws Exception { + md = MessageDigest.getInstance("MD5"); + } + + @Override + public void update(byte[] foo, int start, int len) throws Exception { + md.update(foo, start, len); + } + + @Override + public byte[] digest() throws Exception { + return md.digest(); + } + + @Override + public String name() { + return "MD5"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF.java new file mode 100644 index 0000000..2cfb574 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +/** Use PBKDF2 instead. */ +@Deprecated +public class PBKDF implements com.jcraft.jsch.PBKDF { + @Override + public byte[] getKey(byte[] _pass, byte[] salt, int iterations, int size) { + char[] pass = new char[_pass.length]; + for (int i = 0; i < _pass.length; i++) { + pass[i] = (char) (_pass[i] & 0xff); + } + try { + PBEKeySpec spec = new PBEKeySpec(pass, salt, iterations, size * 8); + SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); + byte[] key = skf.generateSecret(spec).getEncoded(); + return key; + } catch (InvalidKeySpecException e) { + } catch (NoSuchAlgorithmException e) { + } + return null; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2.java new file mode 100644 index 0000000..8279d69 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import java.security.spec.InvalidKeySpecException; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; + +abstract class PBKDF2 implements com.jcraft.jsch.PBKDF2 { + private SecretKeyFactory skf; + private byte[] salt; + private int iterations; + + abstract String getName(); + + @Override + public void init(byte[] salt, int iterations) throws Exception { + skf = SecretKeyFactory.getInstance(getName()); + this.salt = salt; + this.iterations = iterations; + } + + @Override + public byte[] getKey(byte[] _pass, int size) { + char[] pass = new char[_pass.length]; + for (int i = 0; i < _pass.length; i++) { + pass[i] = (char) (_pass[i] & 0xff); + } + try { + PBEKeySpec spec = new PBEKeySpec(pass, salt, iterations, size * 8); + byte[] key = skf.generateSecret(spec).getEncoded(); + return key; + } catch (InvalidKeySpecException e) { + } + return null; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA1.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA1.java new file mode 100644 index 0000000..ebc0bcd --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA1.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class PBKDF2HMACSHA1 extends PBKDF2 { + @Override + String getName() { + return "PBKDF2WithHmacSHA1"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA224.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA224.java new file mode 100644 index 0000000..22daf9f --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA224.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class PBKDF2HMACSHA224 extends PBKDF2 { + @Override + String getName() { + return "PBKDF2WithHmacSHA224"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA256.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA256.java new file mode 100644 index 0000000..56df6dc --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA256.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class PBKDF2HMACSHA256 extends PBKDF2 { + @Override + String getName() { + return "PBKDF2WithHmacSHA256"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA384.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA384.java new file mode 100644 index 0000000..0f94482 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA384.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class PBKDF2HMACSHA384 extends PBKDF2 { + @Override + String getName() { + return "PBKDF2WithHmacSHA384"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512.java new file mode 100644 index 0000000..b4e3d8e --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class PBKDF2HMACSHA512 extends PBKDF2 { + @Override + String getName() { + return "PBKDF2WithHmacSHA512"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512224.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512224.java new file mode 100644 index 0000000..de9a04b --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512224.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class PBKDF2HMACSHA512224 extends PBKDF2 { + @Override + String getName() { + return "PBKDF2WithHmacSHA512/224"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512256.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512256.java new file mode 100644 index 0000000..cf1db90 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/PBKDF2HMACSHA512256.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2013-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class PBKDF2HMACSHA512256 extends PBKDF2 { + @Override + String getName() { + return "PBKDF2WithHmacSHA512/256"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/Random.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/Random.java new file mode 100644 index 0000000..e85f776 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/Random.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import java.security.SecureRandom; + +public class Random implements com.jcraft.jsch.Random { + private byte[] tmp = new byte[16]; + private SecureRandom random = null; + + public Random() { + + // We hope that 'new SecureRandom()' will use NativePRNG algorithm + // on Sun's Java5 for GNU/Linux and Solaris. + // It seems NativePRNG refers to /dev/urandom and it must not be blocked, + // but NativePRNG is slower than SHA1PRNG ;-< + // TIPS: By adding option '-Djava.security.egd=file:/dev/./urandom' + // SHA1PRNG will be used instead of NativePRNG. + // On MacOSX, 'new SecureRandom()' will use NativePRNG algorithm and + // it is also slower than SHA1PRNG. + // On Windows, 'new SecureRandom()' will use SHA1PRNG algorithm. + random = new SecureRandom(); + + /* + * try{ random=SecureRandom.getInstance("SHA1PRNG"); return; } catch(NoSuchAlgorithmException + * e){ // System.err.println(e); } + * + * // The following code is for IBM's JCE try{ + * random=SecureRandom.getInstance("IBMSecureRandom"); return; } catch(NoSuchAlgorithmException + * ee){ //System.err.println(ee); } + */ + } + + @Override + public void fill(byte[] foo, int start, int len) { + /* + * // This case will not become true in our usage. if(start==0 && foo.length==len){ + * random.nextBytes(foo); return; } + */ + if (len > tmp.length) { + tmp = new byte[len]; + } + random.nextBytes(tmp); + System.arraycopy(tmp, 0, foo, start, len); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA1.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA1.java new file mode 100644 index 0000000..4796781 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA1.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.HASH; +import java.security.MessageDigest; + +public class SHA1 implements HASH { + MessageDigest md; + + @Override + public int getBlockSize() { + return 20; + } + + @Override + public void init() throws Exception { + md = MessageDigest.getInstance("SHA-1"); + } + + @Override + public void update(byte[] foo, int start, int len) throws Exception { + md.update(foo, start, len); + } + + @Override + public byte[] digest() throws Exception { + return md.digest(); + } + + @Override + public String name() { + return "SHA1"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA224.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA224.java new file mode 100644 index 0000000..7eef4fa --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA224.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.HASH; +import java.security.MessageDigest; + +public class SHA224 implements HASH { + MessageDigest md; + + @Override + public int getBlockSize() { + return 28; + } + + @Override + public void init() throws Exception { + md = MessageDigest.getInstance("SHA-224"); + } + + @Override + public void update(byte[] foo, int start, int len) throws Exception { + md.update(foo, start, len); + } + + @Override + public byte[] digest() throws Exception { + return md.digest(); + } + + @Override + public String name() { + return "SHA224"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA256.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA256.java new file mode 100644 index 0000000..67288a1 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA256.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.HASH; +import java.security.MessageDigest; + +public class SHA256 implements HASH { + MessageDigest md; + + @Override + public int getBlockSize() { + return 32; + } + + @Override + public void init() throws Exception { + md = MessageDigest.getInstance("SHA-256"); + } + + @Override + public void update(byte[] foo, int start, int len) throws Exception { + md.update(foo, start, len); + } + + @Override + public byte[] digest() throws Exception { + return md.digest(); + } + + @Override + public String name() { + return "SHA256"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA384.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA384.java new file mode 100644 index 0000000..662f3e0 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA384.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.HASH; +import java.security.MessageDigest; + +public class SHA384 implements HASH { + MessageDigest md; + + @Override + public int getBlockSize() { + return 48; + } + + @Override + public void init() throws Exception { + md = MessageDigest.getInstance("SHA-384"); + } + + @Override + public void update(byte[] foo, int start, int len) throws Exception { + md.update(foo, start, len); + } + + @Override + public byte[] digest() throws Exception { + return md.digest(); + } + + @Override + public String name() { + return "SHA384"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA512.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA512.java new file mode 100644 index 0000000..5c2df9c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SHA512.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.HASH; +import java.security.MessageDigest; + +public class SHA512 implements HASH { + MessageDigest md; + + @Override + public int getBlockSize() { + return 64; + } + + @Override + public void init() throws Exception { + md = MessageDigest.getInstance("SHA-512"); + } + + @Override + public void update(byte[] foo, int start, int len) throws Exception { + md.update(foo, start, len); + } + + @Override + public byte[] digest() throws Exception { + return md.digest(); + } + + @Override + public String name() { + return "SHA512"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureDSA.java new file mode 100644 index 0000000..5f6a687 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureDSA.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Buffer; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.DSAPrivateKeySpec; +import java.security.spec.DSAPublicKeySpec; + +public class SignatureDSA implements com.jcraft.jsch.SignatureDSA { + + Signature signature; + KeyFactory keyFactory; + + @Override + public void init() throws Exception { + signature = Signature.getInstance("SHA1withDSA"); + keyFactory = KeyFactory.getInstance("DSA"); + } + + @Override + public void setPubKey(byte[] y, byte[] p, byte[] q, byte[] g) throws Exception { + DSAPublicKeySpec dsaPubKeySpec = new DSAPublicKeySpec(new BigInteger(y), new BigInteger(p), + new BigInteger(q), new BigInteger(g)); + PublicKey pubKey = keyFactory.generatePublic(dsaPubKeySpec); + signature.initVerify(pubKey); + } + + @Override + public void setPrvKey(byte[] x, byte[] p, byte[] q, byte[] g) throws Exception { + DSAPrivateKeySpec dsaPrivKeySpec = new DSAPrivateKeySpec(new BigInteger(x), new BigInteger(p), + new BigInteger(q), new BigInteger(g)); + PrivateKey prvKey = keyFactory.generatePrivate(dsaPrivKeySpec); + signature.initSign(prvKey); + } + + @Override + public byte[] sign() throws Exception { + byte[] sig = signature.sign(); + /* + * System.err.print("sign["+sig.length+"] "); for(int i=0; i 1 && secret[0] == 0 && (secret[1] & 0x80) == 0) { + byte[] tmp = new byte[secret.length - 1]; + System.arraycopy(secret, 1, tmp, 0, tmp.length); + return normalize(tmp); + } else { + return secret; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSA256.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSA256.java new file mode 100644 index 0000000..bddf2cc --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSA256.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureECDSA256 extends SignatureECDSAN { + @Override + String getName() { + return "ecdsa-sha2-nistp256"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSA384.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSA384.java new file mode 100644 index 0000000..46e0a85 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSA384.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureECDSA384 extends SignatureECDSAN { + @Override + String getName() { + return "ecdsa-sha2-nistp384"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSA521.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSA521.java new file mode 100644 index 0000000..3af49c5 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSA521.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureECDSA521 extends SignatureECDSAN { + @Override + String getName() { + return "ecdsa-sha2-nistp521"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java new file mode 100644 index 0000000..307a043 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureECDSAN.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Buffer; +import com.jcraft.jsch.SignatureECDSA; +import java.math.BigInteger; +import java.security.AlgorithmParameters; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.ECGenParameterSpec; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; + +abstract class SignatureECDSAN implements SignatureECDSA { + + Signature signature; + KeyFactory keyFactory; + + abstract String getName(); + + @Override + public void init() throws Exception { + String name = getName(); + String foo = "SHA256withECDSA"; + if (name.equals("ecdsa-sha2-nistp384")) + foo = "SHA384withECDSA"; + else if (name.equals("ecdsa-sha2-nistp521")) + foo = "SHA512withECDSA"; + signature = Signature.getInstance(foo); + keyFactory = KeyFactory.getInstance("EC"); + } + + @Override + public void setPubKey(byte[] r, byte[] s) throws Exception { + + // r and s must be unsigned values. + r = insert0(r); + s = insert0(s); + + String name = "secp256r1"; + if (r.length >= 64) + name = "secp521r1"; + else if (r.length >= 48) + name = "secp384r1"; + + AlgorithmParameters param = AlgorithmParameters.getInstance("EC"); + param.init(new ECGenParameterSpec(name)); + ECParameterSpec ecparam = param.getParameterSpec(ECParameterSpec.class); + ECPoint w = new ECPoint(new BigInteger(1, r), new BigInteger(1, s)); + PublicKey pubKey = keyFactory.generatePublic(new ECPublicKeySpec(w, ecparam)); + signature.initVerify(pubKey); + } + + @Override + public void setPrvKey(byte[] d) throws Exception { + + // d must be unsigned value. + d = insert0(d); + + String name = "secp256r1"; + if (d.length >= 64) + name = "secp521r1"; + else if (d.length >= 48) + name = "secp384r1"; + + AlgorithmParameters param = AlgorithmParameters.getInstance("EC"); + param.init(new ECGenParameterSpec(name)); + ECParameterSpec ecparam = param.getParameterSpec(ECParameterSpec.class); + BigInteger _d = new BigInteger(1, d); + PrivateKey prvKey = keyFactory.generatePrivate(new ECPrivateKeySpec(_d, ecparam)); + signature.initSign(prvKey); + } + + @Override + public byte[] sign() throws Exception { + byte[] sig = signature.sign(); + + // It seems that the output from SunEC is in ASN.1, + // so we have to convert it. + if (sig[0] == 0x30 && // in ASN.1 + ((sig[1] + 2 == sig.length) + || ((sig[1] & 0x80) != 0 && (sig[2] & 0xff) + 3 == sig.length))) { // 2bytes for len + + int index = 3; + if ((sig[1] & 0x80) != 0 && (sig[2] & 0xff) + 3 == sig.length) + index = 4; + + byte[] r = new byte[sig[index]]; + byte[] s = new byte[sig[index + 2 + sig[index]]]; + System.arraycopy(sig, index + 1, r, 0, r.length); + System.arraycopy(sig, index + 3 + sig[index], s, 0, s.length); + + r = chop0(r); + s = chop0(s); + + Buffer buf = new Buffer(); + buf.putMPInt(r); + buf.putMPInt(s); + + sig = new byte[buf.getLength()]; + buf.setOffSet(0); + buf.getByte(sig); + } + + return sig; + } + + @Override + public void update(byte[] foo) throws Exception { + signature.update(foo); + } + + @Override + public boolean verify(byte[] sig) throws Exception { + + // It seems that SunEC expects ASN.1 data, + // so we have to convert it. + if (!(sig[0] == 0x30 && // not in ASN.1 + ((sig[1] + 2 == sig.length) + || ((sig[1] & 0x80) != 0 && (sig[2] & 0xff) + 3 == sig.length)))) { + Buffer b = new Buffer(sig); + + b.getString(); // ecdsa-sha2-nistp256 + b.getInt(); + + byte[] r = b.getMPInt(); + byte[] s = b.getMPInt(); + + r = trimLeadingZeros(insert0(r)); + s = trimLeadingZeros(insert0(s)); + + byte[] asn1 = null; + if (r.length < 64) { + asn1 = new byte[6 + r.length + s.length]; + asn1[0] = (byte) 0x30; + asn1[1] = (byte) (4 + r.length + s.length); + asn1[2] = (byte) 0x02; + asn1[3] = (byte) r.length; + System.arraycopy(r, 0, asn1, 4, r.length); + asn1[r.length + 4] = (byte) 0x02; + asn1[r.length + 5] = (byte) s.length; + System.arraycopy(s, 0, asn1, (6 + r.length), s.length); + } else { + asn1 = new byte[6 + r.length + s.length + 1]; + asn1[0] = (byte) 0x30; + asn1[1] = (byte) 0x81; + asn1[2] = (byte) (4 + r.length + s.length); + asn1[3] = (byte) 0x02; + asn1[4] = (byte) r.length; + System.arraycopy(r, 0, asn1, 5, r.length); + asn1[r.length + 5] = (byte) 0x02; + asn1[r.length + 6] = (byte) s.length; + System.arraycopy(s, 0, asn1, (7 + r.length), s.length); + } + sig = asn1; + } + + return signature.verify(sig); + } + + private static byte[] insert0(byte[] buf) { + if ((buf[0] & 0x80) == 0) + return buf; + byte[] tmp = new byte[buf.length + 1]; + System.arraycopy(buf, 0, tmp, 1, buf.length); + Util.bzero(buf); + return tmp; + } + + private static byte[] chop0(byte[] buf) { + if (buf[0] != 0) + return buf; + byte[] tmp = new byte[buf.length - 1]; + System.arraycopy(buf, 1, tmp, 0, tmp.length); + Util.bzero(buf); + return tmp; + } + + private static byte[] trimLeadingZeros(byte[] buf) { + if (buf.length < 2) + return buf; + + int i = 0; + while (i < buf.length - 1) { + if (buf[i] == 0 && (buf[i + 1] & 0x80) == 0) + i++; + else + break; + } + + if (i == 0) + return buf; + + byte[] tmp = new byte[buf.length - i]; + System.arraycopy(buf, i, tmp, 0, tmp.length); + Util.bzero(buf); + return tmp; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureEd25519.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureEd25519.java new file mode 100644 index 0000000..b9303f2 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureEd25519.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureEd25519 extends SignatureEdDSA { + @Override + String getName() { + return "ssh-ed25519"; + } + + @Override + String getAlgo() { + return "Ed25519"; + } + + @Override + int getKeylen() { + return 32; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureEd448.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureEd448.java new file mode 100644 index 0000000..620aa13 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureEd448.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureEd448 extends SignatureEdDSA { + @Override + String getName() { + return "ssh-ed448"; + } + + @Override + String getAlgo() { + return "Ed448"; + } + + @Override + int getKeylen() { + return 57; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureEdDSA.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureEdDSA.java new file mode 100644 index 0000000..2386e20 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureEdDSA.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Buffer; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.EdECPoint; +import java.security.spec.EdECPrivateKeySpec; +import java.security.spec.EdECPublicKeySpec; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; + +abstract class SignatureEdDSA implements com.jcraft.jsch.SignatureEdDSA { + + Signature signature; + KeyFactory keyFactory; + + abstract String getName(); + + abstract String getAlgo(); + + abstract int getKeylen(); + + @Override + public void init() throws Exception { + signature = Signature.getInstance("EdDSA"); + keyFactory = KeyFactory.getInstance("EdDSA"); + } + + // RFC 8032, + // 5.1. Ed25519ph, Ed25519ctx, and Ed25519 + // ... + // 5.1.3. Decoding + // Decoding a point, given as a 32-octet string, is a little more + // complicated. + // + // 1. First, interpret the string as an integer in little-endian + // representation. Bit 255 of this number is the least significant + // bit of the x-coordinate and denote this value x_0. The + // y-coordinate is recovered simply by clearing this bit. If the + // resulting value is >= p, decoding fails. + // + // 5.2. Ed448ph and Ed448 + // ... + // 5.2.3. Decoding + // Decoding a point, given as a 57-octet string, is a little more + // complicated. + // + // 1. First, interpret the string as an integer in little-endian + // representation. Bit 455 of this number is the least significant + // bit of the x-coordinate and denote this value x_0. The + // y-coordinate is recovered simply by clearing this bit. If the + // resulting value is >= p, decoding fails. + @Override + public void setPubKey(byte[] y_arr) throws Exception { + y_arr = rotate(y_arr); + boolean xOdd = (y_arr[0] & 0x80) != 0; + y_arr[0] &= 0x7f; + BigInteger y = new BigInteger(y_arr); + + NamedParameterSpec paramSpec = new NamedParameterSpec(getAlgo()); + EdECPublicKeySpec pubSpec = new EdECPublicKeySpec(paramSpec, new EdECPoint(xOdd, y)); + PublicKey pubKey = keyFactory.generatePublic(pubSpec); + signature.initVerify(pubKey); + } + + @Override + public void setPrvKey(byte[] bytes) throws Exception { + NamedParameterSpec paramSpec = new NamedParameterSpec(getAlgo()); + EdECPrivateKeySpec privSpec = new EdECPrivateKeySpec(paramSpec, bytes); + PrivateKey prvKey = keyFactory.generatePrivate(privSpec); + signature.initSign(prvKey); + } + + @Override + public byte[] sign() throws Exception { + byte[] sig = signature.sign(); + return sig; + } + + @Override + public void update(byte[] foo) throws Exception { + signature.update(foo); + } + + @Override + public boolean verify(byte[] sig) throws Exception { + int i = 0; + int j = 0; + byte[] tmp; + Buffer buf = new Buffer(sig); + + String foo = new String(buf.getString(), StandardCharsets.UTF_8); + if (foo.equals(getName())) { + j = buf.getInt(); + i = buf.getOffSet(); + tmp = new byte[j]; + System.arraycopy(sig, i, tmp, 0, j); + sig = tmp; + } + + return signature.verify(sig); + } + + private byte[] rotate(byte[] in) { + int len = in.length; + byte[] out = new byte[len]; + + for (int i = 0; i < len; i++) { + out[i] = in[len - i - 1]; + } + + return Arrays.copyOf(out, getKeylen()); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSA.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSA.java new file mode 100644 index 0000000..85860dd --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSA.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureRSA extends SignatureRSAN { + @Override + String getName() { + return "ssh-rsa"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSAN.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSAN.java new file mode 100644 index 0000000..e1f5f39 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSAN.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import com.jcraft.jsch.Buffer; +import com.jcraft.jsch.SignatureRSA; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.security.spec.RSAPrivateKeySpec; +import java.security.spec.RSAPublicKeySpec; + +abstract class SignatureRSAN implements SignatureRSA { + + Signature signature; + KeyFactory keyFactory; + + abstract String getName(); + + @Override + public void init() throws Exception { + String name = getName(); + String foo = "SHA1withRSA"; + if (name.equals("rsa-sha2-256") || name.equals("ssh-rsa-sha256@ssh.com")) + foo = "SHA256withRSA"; + else if (name.equals("rsa-sha2-512") || name.equals("ssh-rsa-sha512@ssh.com")) + foo = "SHA512withRSA"; + else if (name.equals("ssh-rsa-sha384@ssh.com")) + foo = "SHA384withRSA"; + else if (name.equals("ssh-rsa-sha224@ssh.com")) + foo = "SHA224withRSA"; + signature = Signature.getInstance(foo); + keyFactory = KeyFactory.getInstance("RSA"); + } + + @Override + public void setPubKey(byte[] e, byte[] n) throws Exception { + RSAPublicKeySpec rsaPubKeySpec = new RSAPublicKeySpec(new BigInteger(n), new BigInteger(e)); + PublicKey pubKey = keyFactory.generatePublic(rsaPubKeySpec); + signature.initVerify(pubKey); + } + + @Override + public void setPrvKey(byte[] d, byte[] n) throws Exception { + RSAPrivateKeySpec rsaPrivKeySpec = new RSAPrivateKeySpec(new BigInteger(n), new BigInteger(d)); + PrivateKey prvKey = keyFactory.generatePrivate(rsaPrivKeySpec); + signature.initSign(prvKey); + } + + @Override + public byte[] sign() throws Exception { + byte[] sig = signature.sign(); + return sig; + } + + @Override + public void update(byte[] foo) throws Exception { + signature.update(foo); + } + + @Override + public boolean verify(byte[] sig) throws Exception { + int i = 0; + int j = 0; + byte[] tmp; + Buffer buf = new Buffer(sig); + + String foo = new String(buf.getString(), StandardCharsets.UTF_8); + if (foo.equals("ssh-rsa") || foo.equals("rsa-sha2-256") || foo.equals("rsa-sha2-512") + || foo.equals("ssh-rsa-sha224@ssh.com") || foo.equals("ssh-rsa-sha256@ssh.com") + || foo.equals("ssh-rsa-sha384@ssh.com") || foo.equals("ssh-rsa-sha512@ssh.com")) { + if (!foo.equals(getName())) + return false; + j = buf.getInt(); + i = buf.getOffSet(); + tmp = new byte[j]; + System.arraycopy(sig, i, tmp, 0, j); + sig = tmp; + } + + return signature.verify(sig); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA224SSHCOM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA224SSHCOM.java new file mode 100644 index 0000000..1677fa8 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA224SSHCOM.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureRSASHA224SSHCOM extends SignatureRSAN { + @Override + String getName() { + return "ssh-rsa-sha224@ssh.com"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA256.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA256.java new file mode 100644 index 0000000..1b1e895 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA256.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureRSASHA256 extends SignatureRSAN { + @Override + String getName() { + return "rsa-sha2-256"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA256SSHCOM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA256SSHCOM.java new file mode 100644 index 0000000..d83c10c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA256SSHCOM.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureRSASHA256SSHCOM extends SignatureRSAN { + @Override + String getName() { + return "ssh-rsa-sha256@ssh.com"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA384SSHCOM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA384SSHCOM.java new file mode 100644 index 0000000..8197f65 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA384SSHCOM.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureRSASHA384SSHCOM extends SignatureRSAN { + @Override + String getName() { + return "ssh-rsa-sha384@ssh.com"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA512.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA512.java new file mode 100644 index 0000000..d7e985c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA512.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureRSASHA512 extends SignatureRSAN { + @Override + String getName() { + return "rsa-sha2-512"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA512SSHCOM.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA512SSHCOM.java new file mode 100644 index 0000000..3c0fc06 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/SignatureRSASHA512SSHCOM.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +public class SignatureRSASHA512SSHCOM extends SignatureRSAN { + @Override + String getName() { + return "ssh-rsa-sha512@ssh.com"; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/TripleDESCBC.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/TripleDESCBC.java new file mode 100644 index 0000000..3515085 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/TripleDESCBC.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; + +public class TripleDESCBC implements com.jcraft.jsch.Cipher { + private static final int ivsize = 8; + private static final int bsize = 24; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > ivsize) { + tmp = new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + + try { + cipher = Cipher.getInstance("DESede/CBC/NoPadding"); + /* + * // The following code does not work on IBM's JDK 1.4.1 SecretKeySpec skeySpec = new + * SecretKeySpec(key, "DESede"); cipher.init((mode==com.jcraft.jsch.Cipher.ENCRYPT_MODE? + * Cipher.ENCRYPT_MODE: Cipher.DECRYPT_MODE), skeySpec, new IvParameterSpec(iv)); + */ + DESedeKeySpec keyspec = new DESedeKeySpec(key); + SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede"); + SecretKey _key = keyfactory.generateSecret(keyspec); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + _key, new IvParameterSpec(iv)); + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return true; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java new file mode 100644 index 0000000..de10ca3 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/TripleDESCTR.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2008-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESedeKeySpec; +import javax.crypto.spec.IvParameterSpec; + +public class TripleDESCTR implements com.jcraft.jsch.Cipher { + private static final int ivsize = 8; + private static final int bsize = 24; + private Cipher cipher; + + @Override + public int getIVSize() { + return ivsize; + } + + @Override + public int getBlockSize() { + return bsize; + } + + @Override + public void init(int mode, byte[] key, byte[] iv) throws Exception { + byte[] tmp; + if (iv.length > ivsize) { + tmp = new byte[ivsize]; + System.arraycopy(iv, 0, tmp, 0, tmp.length); + iv = tmp; + } + if (key.length > bsize) { + tmp = new byte[bsize]; + System.arraycopy(key, 0, tmp, 0, tmp.length); + key = tmp; + } + + try { + cipher = Cipher.getInstance("DESede/CTR/NoPadding"); + /* + * // The following code does not work on IBM's JDK 1.4.1 SecretKeySpec skeySpec = new + * SecretKeySpec(key, "DESede"); cipher.init((mode==com.jcraft.jsch.Cipher.ENCRYPT_MODE? + * Cipher.ENCRYPT_MODE: Cipher.DECRYPT_MODE), skeySpec, new IvParameterSpec(iv)); + */ + DESedeKeySpec keyspec = new DESedeKeySpec(key); + SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede"); + SecretKey _key = keyfactory.generateSecret(keyspec); + cipher.init( + (mode == com.jcraft.jsch.Cipher.ENCRYPT_MODE ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE), + _key, new IvParameterSpec(iv)); + } catch (Exception e) { + cipher = null; + throw e; + } + } + + @Override + public void update(byte[] foo, int s1, int len, byte[] bar, int s2) throws Exception { + cipher.update(foo, s1, len, bar, s2); + } + + @Override + public boolean isCBC() { + return false; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/Util.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/Util.java new file mode 100644 index 0000000..f7219ed --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/Util.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +class Util { + static void bzero(byte[] foo) { + if (foo == null) + return; + for (int i = 0; i < foo.length; i++) + foo[i] = 0; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jce/XDH.java b/files-jsch/src/main/java/com/jcraft/jsch/jce/XDH.java new file mode 100644 index 0000000..4c07a54 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jce/XDH.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2015-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jce; + +import java.math.BigInteger; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.interfaces.XECPublicKey; +import java.security.spec.NamedParameterSpec; +import java.security.spec.XECPublicKeySpec; +import java.util.Arrays; +import javax.crypto.KeyAgreement; + +public class XDH implements com.jcraft.jsch.XDH { + byte[] Q_array; + XECPublicKey publicKey; + int keylen; + + private KeyAgreement myKeyAgree; + + @Override + public void init(String name, int keylen) throws Exception { + this.keylen = keylen; + myKeyAgree = KeyAgreement.getInstance("XDH"); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH"); + NamedParameterSpec paramSpec = new NamedParameterSpec(name); + kpg.initialize(paramSpec); + KeyPair kp = kpg.genKeyPair(); + publicKey = (XECPublicKey) kp.getPublic(); + Q_array = rotate(publicKey.getU().toByteArray()); + myKeyAgree.init(kp.getPrivate()); + } + + @Override + public byte[] getQ() throws Exception { + return Q_array; + } + + @Override + public byte[] getSecret(byte[] Q) throws Exception { + // The u coordinate in BigInteger format needs to be a positive value. + // So zero extend the little-endian input before rotating into big-endian. + // This should ensure that we end up with a positive BigInteger value. + Q = rotate(Q); + byte[] u = new byte[keylen + 1]; + System.arraycopy(Q, 0, u, 1, keylen); + XECPublicKeySpec spec = new XECPublicKeySpec(publicKey.getParams(), new BigInteger(u)); + KeyFactory kf = KeyFactory.getInstance("XDH"); + PublicKey theirPublicKey = kf.generatePublic(spec); + myKeyAgree.doPhase(theirPublicKey, true); + return myKeyAgree.generateSecret(); + } + + // https://cr.yp.to/ecdh.html#validate + // RFC 8731, + // 3. Key Exchange Methods + // Clients and servers MUST + // also abort if the length of the received public keys are not the + // expected lengths. An abort for these purposes is defined as a + // disconnect (SSH_MSG_DISCONNECT) of the session and SHOULD use the + // SSH_DISCONNECT_KEY_EXCHANGE_FAILED reason for the message + // [IANA-REASON]. No further validation is required beyond what is + // described in [RFC7748]. + @Override + public boolean validate(byte[] u) throws Exception { + return u.length == keylen; + } + + // RFC 7748, + // 5. The X25519 and X448 Functions + // The u-coordinates are elements of the underlying field GF(2^255 - 19) + // or GF(2^448 - 2^224 - 1) and are encoded as an array of bytes, u, in + // little-endian order such that u[0] + 256*u[1] + 256^2*u[2] + ... + + // 256^(n-1)*u[n-1] is congruent to the value modulo p and u[n-1] is + // minimal. When receiving such an array, implementations of X25519 + // (but not X448) MUST mask the most significant bit in the final byte. + // This is done to preserve compatibility with point formats that + // reserve the sign bit for use in other protocols and to increase + // resistance to implementation fingerprinting. + private byte[] rotate(byte[] in) { + int len = in.length; + byte[] out = new byte[len]; + + for (int i = 0; i < len; i++) { + out[i] = in[len - i - 1]; + } + + return Arrays.copyOf(out, keylen); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java b/files-jsch/src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java new file mode 100644 index 0000000..689da9c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jgss/GSSContextKrb5.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2006-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jgss; + +import com.jcraft.jsch.JSchException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import org.ietf.jgss.GSSContext; +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSException; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.MessageProp; +import org.ietf.jgss.Oid; + +public class GSSContextKrb5 implements com.jcraft.jsch.GSSContext { + + private static final String pUseSubjectCredsOnly = "javax.security.auth.useSubjectCredsOnly"; + private static String useSubjectCredsOnly = getSystemProperty(pUseSubjectCredsOnly); + + private GSSContext context = null; + + @Override + public void create(String user, String host) throws JSchException { + try { + // RFC 1964 + Oid krb5 = new Oid("1.2.840.113554.1.2.2"); + // Kerberos Principal Name Form + Oid principalName = new Oid("1.2.840.113554.1.2.2.1"); + + GSSManager mgr = GSSManager.getInstance(); + + GSSCredential crd = null; + /* + * try{ GSSName _user=mgr.createName(user, principalName); crd=mgr.createCredential(_user, + * GSSCredential.DEFAULT_LIFETIME, krb5, GSSCredential.INITIATE_ONLY); } catch(GSSException + * crdex){ } + */ + + String cname = host; + try { + cname = InetAddress.getByName(cname).getCanonicalHostName(); + } catch (UnknownHostException e) { + } + GSSName _host = mgr.createName("host/" + cname, principalName); + + context = mgr.createContext(_host, krb5, crd, GSSContext.DEFAULT_LIFETIME); + + // RFC4462 3.4. GSS-API Session + // + // When calling GSS_Init_sec_context(), the client MUST set + // integ_req_flag to "true" to request that per-message integrity + // protection be supported for this context. In addition, + // deleg_req_flag MAY be set to "true" to request access delegation, if + // requested by the user. + // + // Since the user authentication process by its nature authenticates + // only the client, the setting of mutual_req_flag is not needed for + // this process. This flag SHOULD be set to "false". + + // TODO: OpenSSH's sshd does accepts 'false' for mutual_req_flag + // context.requestMutualAuth(false); + context.requestMutualAuth(true); + context.requestConf(true); + context.requestInteg(true); // for MIC + context.requestCredDeleg(true); + context.requestAnonymity(false); + + return; + } catch (GSSException ex) { + throw new JSchException(ex.toString(), ex); + } + } + + @Override + public boolean isEstablished() { + return context.isEstablished(); + } + + @Override + public byte[] init(byte[] token, int s, int l) throws JSchException { + try { + // Without setting "javax.security.auth.useSubjectCredsOnly" to "false", + // Sun's JVM for Un*x will show messages to stderr in + // processing context.initSecContext(). + // This hack is not thread safe ;-<. + // If that property is explicitly given as "true" or "false", + // this hack must not be invoked. + if (useSubjectCredsOnly == null) { + setSystemProperty(pUseSubjectCredsOnly, "false"); + } + return context.initSecContext(token, 0, l); + } catch (GSSException ex) { + throw new JSchException(ex.toString(), ex); + } catch (SecurityException ex) { + throw new JSchException(ex.toString(), ex); + } finally { + if (useSubjectCredsOnly == null) { + // By the default, it must be "true". + setSystemProperty(pUseSubjectCredsOnly, "true"); + } + } + } + + @Override + public byte[] getMIC(byte[] message, int s, int l) { + try { + MessageProp prop = new MessageProp(0, true); + return context.getMIC(message, s, l, prop); + } catch (GSSException ex) { + return null; + } + } + + @Override + public void dispose() { + try { + context.dispose(); + } catch (GSSException ex) { + } + } + + private static String getSystemProperty(String key) { + try { + return System.getProperty(key); + } catch (Exception e) { + // We are not allowed to get the System properties. + return null; + } + } + + private static void setSystemProperty(String key, String value) { + try { + System.setProperty(key, value); + } catch (Exception e) { + // We are not allowed to set the System properties. + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/juz/Compression.java b/files-jsch/src/main/java/com/jcraft/jsch/juz/Compression.java new file mode 100644 index 0000000..990c40e --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/juz/Compression.java @@ -0,0 +1,135 @@ +package com.jcraft.jsch.juz; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Logger; +import com.jcraft.jsch.Session; +import java.util.function.Supplier; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; + +/** + * This example demonstrates the packet compression without using jzlib[1]. + * + *

+ * The ssh protocol adopts zlib[2] for the packet compression. Fortunately, JDK has provided wrapper + * classes for zlib(j.u.z.{Deflater, Inflater}), but it does not expose enough functionality of + * zlib, unfortunately; it must not allow to compress data with SYNC_FLUSH. So, JSch has been using + * jzlib by the default. After 12 years of bug parade entry[3] filing, Java7 has revised + * j.u.z.Deflater, and SYNC_FLUSH has been supported at last. This example shows how to enable the + * packet compression by using JDK's java.util.zip package. + * + *

+ * [1] http://www.jcraft.com/jzlib/ [2] http://www.zlib.net/ [3] + * https://bugs.openjdk.java.net/browse/JDK-4206909 + */ +public class Compression implements com.jcraft.jsch.Compression { + private static final int BUF_SIZE = 4096; + private final int buffer_margin = 32 + 20; // AES256 + HMACSHA1 + private Deflater deflater; + private Inflater inflater; + private byte[] tmpbuf = new byte[BUF_SIZE]; + private byte[] inflated_buf; + private Session session; + + public Compression() {} + + private void logMessage(int level, Supplier message) { + Logger logger = session == null ? JSch.getLogger() : session.getLogger(); + if (!logger.isEnabled(level)) { + return; + } + logger.log(level, message.get()); + } + + @Override + public void end() { + inflated_buf = null; + if (inflater != null) { + inflater.end(); + inflater = null; + } + if (deflater != null) { + deflater.end(); + deflater = null; + } + session = null; + } + + @Override + public void init(int type, int level, Session session) { + this.session = session; + init(type, level); + } + + @Override + public void init(int type, int level) { + if (type == DEFLATER) { + deflater = new Deflater(level); + } else if (type == INFLATER) { + inflater = new Inflater(); + inflated_buf = new byte[BUF_SIZE]; + } + logMessage(Logger.DEBUG, () -> "zlib using " + this.getClass().getCanonicalName()); + } + + @Override + public byte[] compress(byte[] buf, int start, int[] end) { + + // There may be a bug in j.u.z.Deflater. + // It seems to me that if the size of buffer for Deflater#deflate() is + // not enough, that method will return weird value ;-( + if (tmpbuf.length < end[0]) { + tmpbuf = new byte[end[0] * 2]; + } + + deflater.setInput(buf, start, end[0] - start); + + byte[] obuf = buf; // output buffer + int obuflen = start; // length of output buffer + do { + int result = deflater.deflate(tmpbuf, 0, tmpbuf.length, Deflater.SYNC_FLUSH); + // deflation of delfated data may inflate it. + if (obuf.length < obuflen + result + buffer_margin) { + byte[] tmp = new byte[(obuflen + result + buffer_margin) * 2]; + System.arraycopy(obuf, 0, tmp, 0, obuf.length); + obuf = tmp; + } + System.arraycopy(tmpbuf, 0, obuf, obuflen, result); + obuflen += result; + } while (!deflater.needsInput()); + + end[0] = obuflen; + return obuf; + } + + @Override + public byte[] uncompress(byte[] buf, int start, int[] len) { + inflater.setInput(buf, start, len[0]); + + int inflated_end = 0; + try { + do { + int result = inflater.inflate(tmpbuf, 0, tmpbuf.length); + if (inflated_buf.length < inflated_end + result) { + byte[] tmp = new byte[inflated_end + result]; + System.arraycopy(inflated_buf, 0, tmp, 0, inflated_end); + inflated_buf = tmp; + } + System.arraycopy(tmpbuf, 0, inflated_buf, inflated_end, result); + inflated_end += result; + } while (inflater.getRemaining() > 0); + } catch (DataFormatException e) { + logMessage(Logger.WARN, () -> "an exception during uncompress\n" + e.toString()); + } + + if (buf.length < inflated_buf.length + start) { + byte[] tmp = new byte[inflated_buf.length + start]; + System.arraycopy(buf, 0, tmp, 0, start); + buf = tmp; + } + System.arraycopy(inflated_buf, 0, buf, start, inflated_end); + len[0] = inflated_end; + return buf; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Adler32.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Adler32.java new file mode 100644 index 0000000..a20b890 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Adler32.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class Adler32 implements Checksum { + + // largest prime smaller than 65536 + private static final int BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private static final int NMAX = 5552; + + private long s1 = 1L; + private long s2 = 0L; + + @Override + public void reset(long init) { + s1 = init & 0xffff; + s2 = (init >> 16) & 0xffff; + } + + @Override + public void reset() { + s1 = 1L; + s2 = 0L; + } + + @Override + public long getValue() { + return ((s2 << 16) | s1); + } + + @Override + public void update(byte[] buf, int index, int len) { + + if (len == 1) { + s1 += buf[index++] & 0xff; + s2 += s1; + s1 %= BASE; + s2 %= BASE; + return; + } + + int len1 = len / NMAX; + int len2 = len % NMAX; + while (len1-- > 0) { + int k = NMAX; + len -= k; + while (k-- > 0) { + s1 += buf[index++] & 0xff; + s2 += s1; + } + s1 %= BASE; + s2 %= BASE; + } + + int k = len2; + len -= k; + while (k-- > 0) { + s1 += buf[index++] & 0xff; + s2 += s1; + } + s1 %= BASE; + s2 %= BASE; + } + + @Override + public Adler32 copy() { + Adler32 foo = new Adler32(); + foo.s1 = this.s1; + foo.s2 = this.s2; + return foo; + } + + // The following logic has come from zlib.1.2. + static long combine(long adler1, long adler2, long len2) { + long BASEL = (long) BASE; + long sum1; + long sum2; + long rem; // unsigned int + + rem = len2 % BASEL; + sum1 = adler1 & 0xffffL; + sum2 = rem * sum1; + sum2 %= BASEL; // MOD(sum2); + sum1 += (adler2 & 0xffffL) + BASEL - 1; + sum2 += ((adler1 >> 16) & 0xffffL) + ((adler2 >> 16) & 0xffffL) + BASEL - rem; + if (sum1 >= BASEL) + sum1 -= BASEL; + if (sum1 >= BASEL) + sum1 -= BASEL; + if (sum2 >= (BASEL << 1)) + sum2 -= (BASEL << 1); + if (sum2 >= BASEL) + sum2 -= BASEL; + return sum1 | (sum2 << 16); + } + + /* + * private java.util.zip.Adler32 adler=new java.util.zip.Adler32(); void update(byte[] buf, int + * index, int len){ if(buf==null) {adler.reset();} else{adler.update(buf, index, len);} } void + * reset(){ adler.reset(); } void reset(long init){ if(init==1L){ adler.reset(); } else{ + * System.err.println("unsupported operation"); } } long getValue(){ return adler.getValue(); } + */ +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/CRC32.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/CRC32.java new file mode 100644 index 0000000..6e9591e --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/CRC32.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class CRC32 implements Checksum { + + /* + * The following logic has come from RFC1952. + */ + private int v = 0; + private static int[] crc_table = null; + + static { + crc_table = new int[256]; + for (int n = 0; n < 256; n++) { + int c = n; + for (int k = 8; --k >= 0;) { + if ((c & 1) != 0) + c = 0xedb88320 ^ (c >>> 1); + else + c = c >>> 1; + } + crc_table[n] = c; + } + } + + @Override + public void update(byte[] buf, int index, int len) { + int c = ~v; + while (--len >= 0) + c = crc_table[(c ^ buf[index++]) & 0xff] ^ (c >>> 8); + v = ~c; + } + + @Override + public void reset() { + v = 0; + } + + @Override + public void reset(long vv) { + v = (int) (vv & 0xffffffffL); + } + + @Override + public long getValue() { + return v & 0xffffffffL; + } + + // The following logic has come from zlib.1.2. + private static final int GF2_DIM = 32; + + static long combine(long crc1, long crc2, long len2) { + long row; + long[] even = new long[GF2_DIM]; + long[] odd = new long[GF2_DIM]; + + // degenerate case (also disallow negative lengths) + if (len2 <= 0) + return crc1; + + // put operator for one zero bit in odd + odd[0] = 0xedb88320L; // CRC-32 polynomial + row = 1; + for (int n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + // put operator for two zero bits in even + gf2_matrix_square(even, odd); + + // put operator for four zero bits in odd + gf2_matrix_square(odd, even); + + // apply len2 zeros to crc1 (first square will put the operator for one + // zero byte, eight zero bits, in even) + do { + // apply zeros operator for this bit of len2 + gf2_matrix_square(even, odd); + if ((len2 & 1) != 0) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + // if no more bits set, then done + if (len2 == 0) + break; + + // another iteration of the loop with odd and even swapped + gf2_matrix_square(odd, even); + if ((len2 & 1) != 0) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + // if no more bits set, then done + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; + } + + private static long gf2_matrix_times(long[] mat, long vec) { + long sum = 0; + int index = 0; + while (vec != 0) { + if ((vec & 1) != 0) + sum ^= mat[index]; + vec >>= 1; + index++; + } + return sum; + } + + static final void gf2_matrix_square(long[] square, long[] mat) { + for (int n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); + } + + /* + * private java.util.zip.CRC32 crc32 = new java.util.zip.CRC32(); + * + * void update(byte[] buf, int index, int len){ if(buf==null) {crc32.reset();} + * else{crc32.update(buf, index, len);} } void reset(){ crc32.reset(); } void reset(long init){ + * if(init==0L){ crc32.reset(); } else{ System.err.println("unsupported operation"); } } long + * getValue(){ return crc32.getValue(); } + */ + @Override + public CRC32 copy() { + CRC32 foo = new CRC32(); + foo.v = this.v; + return foo; + } + + static int[] getCRC32Table() { + int[] tmp = new int[crc_table.length]; + System.arraycopy(crc_table, 0, tmp, 0, tmp.length); + return tmp; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Checksum.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Checksum.java new file mode 100644 index 0000000..cc00c02 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Checksum.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +interface Checksum { + void update(byte[] buf, int index, int len); + + void reset(); + + void reset(long init); + + long getValue(); + + Checksum copy(); +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Compression.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Compression.java new file mode 100644 index 0000000..279ee18 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Compression.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2002-2018 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jzlib; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Logger; +import com.jcraft.jsch.Session; +import java.io.UncheckedIOException; +import java.util.function.Supplier; + +public class Compression implements com.jcraft.jsch.Compression { + private static final int BUF_SIZE = 4096; + private final int buffer_margin = 32 + 20; // AES256 + HMACSHA1 + private Deflater deflater; + private Inflater inflater; + private byte[] tmpbuf = new byte[BUF_SIZE]; + private byte[] inflated_buf; + private Session session; + + public Compression() {} + + private void logMessage(int level, Supplier message) { + Logger logger = session == null ? JSch.getLogger() : session.getLogger(); + if (!logger.isEnabled(level)) { + return; + } + logger.log(level, message.get()); + } + + @Override + public void end() { + inflated_buf = null; + if (inflater != null) { + inflater.end(); + inflater = null; + } + if (deflater != null) { + deflater.end(); + deflater = null; + } + session = null; + } + + @Override + public void init(int type, int level, Session session) { + this.session = session; + init(type, level); + } + + public void init(int type, int level) throws UncheckedIOException { + if (type == DEFLATER) { + try { + deflater = new Deflater(level); + } catch (GZIPException e) { + throw new UncheckedIOException(e); + } + } else if (type == INFLATER) { + inflater = new Inflater(); + inflated_buf = new byte[BUF_SIZE]; + } + logMessage(Logger.DEBUG, () -> "zlib using " + this.getClass().getCanonicalName()); + } + + @Override + public byte[] compress(byte[] buf, int start, int[] len) { + deflater.next_in = buf; + deflater.next_in_index = start; + deflater.avail_in = len[0] - start; + int outputlen = start; + byte[] outputbuf = buf; + int tmp = 0; + + do { + deflater.next_out = tmpbuf; + deflater.next_out_index = 0; + deflater.avail_out = BUF_SIZE; + int status = deflater.deflate(JZlib.Z_PARTIAL_FLUSH); + switch (status) { + case JZlib.Z_OK: + tmp = BUF_SIZE - deflater.avail_out; + if (outputbuf.length < outputlen + tmp + buffer_margin) { + byte[] foo = new byte[(outputlen + tmp + buffer_margin) * 2]; + System.arraycopy(outputbuf, 0, foo, 0, outputbuf.length); + outputbuf = foo; + } + System.arraycopy(tmpbuf, 0, outputbuf, outputlen, tmp); + outputlen += tmp; + break; + default: + logMessage(Logger.WARN, () -> "compress: deflate returnd " + status); + } + } while (deflater.avail_out == 0); + + len[0] = outputlen; + return outputbuf; + } + + @Override + public byte[] uncompress(byte[] buffer, int start, int[] length) { + int inflated_end = 0; + + inflater.next_in = buffer; + inflater.next_in_index = start; + inflater.avail_in = length[0]; + + while (true) { + inflater.next_out = tmpbuf; + inflater.next_out_index = 0; + inflater.avail_out = BUF_SIZE; + int status = inflater.inflate(JZlib.Z_PARTIAL_FLUSH); + switch (status) { + case JZlib.Z_OK: + if (inflated_buf.length < inflated_end + BUF_SIZE - inflater.avail_out) { + int len = inflated_buf.length * 2; + if (len < inflated_end + BUF_SIZE - inflater.avail_out) + len = inflated_end + BUF_SIZE - inflater.avail_out; + byte[] foo = new byte[len]; + System.arraycopy(inflated_buf, 0, foo, 0, inflated_end); + inflated_buf = foo; + } + System.arraycopy(tmpbuf, 0, inflated_buf, inflated_end, BUF_SIZE - inflater.avail_out); + inflated_end += (BUF_SIZE - inflater.avail_out); + length[0] = inflated_end; + break; + case JZlib.Z_BUF_ERROR: + if (inflated_end > buffer.length - start) { + byte[] foo = new byte[inflated_end + start]; + System.arraycopy(buffer, 0, foo, 0, start); + System.arraycopy(inflated_buf, 0, foo, start, inflated_end); + buffer = foo; + } else { + System.arraycopy(inflated_buf, 0, buffer, start, inflated_end); + } + length[0] = inflated_end; + return buffer; + default: + logMessage(Logger.WARN, () -> "compress: deflate returnd " + status); + return null; + } + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Deflate.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Deflate.java new file mode 100644 index 0000000..4e15b04 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Deflate.java @@ -0,0 +1,1761 @@ +/* + * Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class Deflate implements Cloneable { + + private static final int MAX_MEM_LEVEL = 9; + + private static final int Z_DEFAULT_COMPRESSION = -1; + + private static final int MAX_WBITS = 15; // 32K LZ77 window + private static final int DEF_MEM_LEVEL = 8; + + static class Config { + int good_length; // reduce lazy search above this match length + int max_lazy; // do not perform lazy search above this match length + int nice_length; // quit search above this match length + int max_chain; + int func; + + Config(int good_length, int max_lazy, int nice_length, int max_chain, int func) { + this.good_length = good_length; + this.max_lazy = max_lazy; + this.nice_length = nice_length; + this.max_chain = max_chain; + this.func = func; + } + } + + private static final int STORED = 0; + private static final int FAST = 1; + private static final int SLOW = 2; + private static final Config[] config_table; + + static { + config_table = new Config[10]; + // good lazy nice chain + config_table[0] = new Config(0, 0, 0, 0, STORED); + config_table[1] = new Config(4, 4, 8, 4, FAST); + config_table[2] = new Config(4, 5, 16, 8, FAST); + config_table[3] = new Config(4, 6, 32, 32, FAST); + + config_table[4] = new Config(4, 4, 16, 16, SLOW); + config_table[5] = new Config(8, 16, 32, 32, SLOW); + config_table[6] = new Config(8, 16, 128, 128, SLOW); + config_table[7] = new Config(8, 32, 128, 256, SLOW); + config_table[8] = new Config(32, 128, 258, 1024, SLOW); + config_table[9] = new Config(32, 258, 258, 4096, SLOW); + } + + private static final String[] z_errmsg = {"need dictionary", // Z_NEED_DICT 2 + "stream end", // Z_STREAM_END 1 + "", // Z_OK 0 + "file error", // Z_ERRNO (-1) + "stream error", // Z_STREAM_ERROR (-2) + "data error", // Z_DATA_ERROR (-3) + "insufficient memory", // Z_MEM_ERROR (-4) + "buffer error", // Z_BUF_ERROR (-5) + "incompatible version", // Z_VERSION_ERROR (-6) + ""}; + + // block not completed, need more input or more output + private static final int NeedMore = 0; + + // block flush performed + private static final int BlockDone = 1; + + // finish started, need only more output at next deflate + private static final int FinishStarted = 2; + + // finish done, accept no more input or output + private static final int FinishDone = 3; + + // preset dictionary flag in zlib header + private static final int PRESET_DICT = 0x20; + + private static final int Z_FILTERED = 1; + private static final int Z_HUFFMAN_ONLY = 2; + private static final int Z_DEFAULT_STRATEGY = 0; + + private static final int Z_NO_FLUSH = 0; + private static final int Z_PARTIAL_FLUSH = 1; + private static final int Z_SYNC_FLUSH = 2; + private static final int Z_FULL_FLUSH = 3; + private static final int Z_FINISH = 4; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + private static final int INIT_STATE = 42; + private static final int BUSY_STATE = 113; + private static final int FINISH_STATE = 666; + + // The deflate compression method + private static final int Z_DEFLATED = 8; + + private static final int STORED_BLOCK = 0; + private static final int STATIC_TREES = 1; + private static final int DYN_TREES = 2; + + // The three kinds of block type + private static final int Z_BINARY = 0; + private static final int Z_ASCII = 1; + private static final int Z_UNKNOWN = 2; + + private static final int Buf_size = 8 * 2; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + private static final int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + private static final int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + private static final int REPZ_11_138 = 18; + + private static final int MIN_MATCH = 3; + private static final int MAX_MATCH = 258; + private static final int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private static final int MAX_BITS = 15; + private static final int D_CODES = 30; + private static final int BL_CODES = 19; + private static final int LENGTH_CODES = 29; + private static final int LITERALS = 256; + private static final int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private static final int HEAP_SIZE = (2 * L_CODES + 1); + + private static final int END_BLOCK = 256; + + ZStream strm; // pointer back to this zlib stream + int status; // as the name implies + byte[] pending_buf; // output still pending + int pending_buf_size; // size of pending_buf + int pending_out; // next pending byte to output to the stream + int pending; // nb of bytes in the pending buffer + int wrap = 1; + byte data_type; // UNKNOWN, BINARY or ASCII + byte method; // STORED (for zip only) or DEFLATED + int last_flush; // value of flush param for previous deflate call + + int w_size; // LZ77 window size (32K by default) + int w_bits; // log2(w_size) (8..16) + int w_mask; // w_size - 1 + + byte[] window; + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least wSize + // bytes. With this organization, matches are limited to a distance of + // wSize-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. Also, it limits + // the window size to 64K, which is quite useful on MSDOS. + // To do: use the user input buffer as sliding window. + + int window_size; + // Actual size of window: 2*wSize, except when the user input buffer + // is directly used as sliding window. + + short[] prev; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + + short[] head; // Heads of the hash chains or NIL. + + int ins_h; // hash index of string to be inserted + int hash_size; // number of elements in hash table + int hash_bits; // log2(hash_size) + int hash_mask; // hash_size-1 + + // Number of bits by which ins_h must be shifted at each input + // step. It must be such that after MIN_MATCH steps, the oldest + // byte no longer takes part in the hash key, that is: + // hash_shift * MIN_MATCH >= hash_bits + int hash_shift; + + // Window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + int block_start; + + int match_length; // length of best match + int prev_match; // previous match + int match_available; // set if previous match exists + int strstart; // start of string to insert + int match_start; // start of matching string + int lookahead; // number of valid bytes ahead in window + + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + int prev_length; + + // To speed up deflation, hash chains are never searched beyond this + // length. A higher limit improves compression ratio but degrades the speed. + int max_chain_length; + + // Attempt to find a better match only when the current match is strictly + // smaller than this value. This mechanism is used only for compression + // levels >= 4. + int max_lazy_match; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + int level; // compression level (1..9) + int strategy; // favor or force Huffman coding + + // Use a faster search when the previous match is longer than this + int good_match; + + // Stop searching when current match exceeds this + int nice_match; + + short[] dyn_ltree; // literal and length tree + short[] dyn_dtree; // distance tree + short[] bl_tree; // Huffman tree for bit lengths + + Tree l_desc = new Tree(); // desc for literal tree + Tree d_desc = new Tree(); // desc for distance tree + Tree bl_desc = new Tree(); // desc for bit length tree + + // number of codes at each bit length for an optimal tree + short[] bl_count = new short[MAX_BITS + 1]; + // working area to be used in Tree#gen_codes() + short[] next_code = new short[MAX_BITS + 1]; + + // heap used to build the Huffman trees + int[] heap = new int[2 * L_CODES + 1]; + + int heap_len; // number of elements in the heap + int heap_max; // element of largest frequency + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + // Depth of each subtree used as tie breaker for trees of equal frequency + byte[] depth = new byte[2 * L_CODES + 1]; + + byte[] l_buf; // index for literals or lengths */ + + // Size of match buffer for literals/lengths. There are 4 reasons for + // limiting lit_bufsize to 64K: + // - frequencies can be kept in 16 bit counters + // - if compression is not successful for the first block, all input + // data is still in the window so we can still emit a stored block even + // when input comes from standard input. (This can also be done for + // all blocks if lit_bufsize is not greater than 32K.) + // - if compression is not successful for a file smaller than 64K, we can + // even emit a stored file instead of a stored block (saving 5 bytes). + // This is applicable only for zip (not gzip or zlib). + // - creating new Huffman trees less frequently may not provide fast + // adaptation to changes in the input data statistics. (Take for + // example a binary file with poorly compressible code followed by + // a highly compressible string table.) Smaller buffer sizes give + // fast adaptation but have of course the overhead of transmitting + // trees more frequently. + // - I can't count above 4 + int lit_bufsize; + + int last_lit; // running index in l_buf + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + int d_buf; // index of pendig_buf + + int opt_len; // bit length of current block with optimal trees + int static_len; // bit length of current block with static trees + int matches; // number of string matches in current block + int last_eob_len; // bit length of EOB code for last block + + // Output buffer. bits are inserted starting at the bottom (least + // significant bits). + short bi_buf; + + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + int bi_valid; + + GZIPHeader gheader = null; + + Deflate(ZStream strm) { + this.strm = strm; + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + void lm_init() { + window_size = 2 * w_size; + + head[hash_size - 1] = 0; + for (int i = 0; i < hash_size - 1; i++) { + head[i] = 0; + } + + // Set the default configuration parameters: + max_lazy_match = Deflate.config_table[level].max_lazy; + good_match = Deflate.config_table[level].good_length; + nice_match = Deflate.config_table[level].nice_length; + max_chain_length = Deflate.config_table[level].max_chain; + + strstart = 0; + block_start = 0; + lookahead = 0; + match_length = prev_length = MIN_MATCH - 1; + match_available = 0; + ins_h = 0; + } + + // Initialize the tree data structures for a new zlib stream. + void tr_init() { + + l_desc.dyn_tree = dyn_ltree; + l_desc.stat_desc = StaticTree.static_l_desc; + + d_desc.dyn_tree = dyn_dtree; + d_desc.stat_desc = StaticTree.static_d_desc; + + bl_desc.dyn_tree = bl_tree; + bl_desc.stat_desc = StaticTree.static_bl_desc; + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + init_block(); + } + + void init_block() { + // Initialize the trees. + for (int i = 0; i < L_CODES; i++) + dyn_ltree[i * 2] = 0; + for (int i = 0; i < D_CODES; i++) + dyn_dtree[i * 2] = 0; + for (int i = 0; i < BL_CODES; i++) + bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + void pqdownheap(short[] tree, // the tree to restore + int k // node to move down + ) { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) { + // Set j to the smallest of the two sons: + if (j < heap_len && smaller(tree, heap[j + 1], heap[j], depth)) { + j++; + } + // Exit if v is smaller than both sons + if (smaller(tree, v, heap[j], depth)) + break; + + // Exchange v with the smallest son + heap[k] = heap[j]; + k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + static boolean smaller(short[] tree, int n, int m, byte[] depth) { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || (tn2 == tm2 && depth[n] <= depth[m])); + } + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + void scan_tree(short[] tree, // the tree to be scanned + int max_code // and its largest code of non zero frequency + ) { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + tree[(max_code + 1) * 2 + 1] = (short) 0xffff; // guard + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + bl_tree[curlen * 2] += (short) count; + } else if (curlen != 0) { + if (curlen != prevlen) + bl_tree[curlen * 2]++; + bl_tree[REP_3_6 * 2]++; + } else if (count <= 10) { + bl_tree[REPZ_3_10 * 2]++; + } else { + bl_tree[REPZ_11_138 * 2]++; + } + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + int build_bl_tree() { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, l_desc.max_code); + scan_tree(dyn_dtree, d_desc.max_code); + + // Build the bit length tree: + bl_desc.build_tree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) { + if (bl_tree[Tree.bl_order[max_blindex] * 2 + 1] != 0) + break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + void send_all_trees(int lcodes, int dcodes, int blcodes) { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) { + send_bits(bl_tree[Tree.bl_order[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + void send_tree(short[] tree, // the tree to be sent + int max_code // and its largest code of non zero frequency + ) { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { + send_code(curlen, bl_tree); + } while (--count != 0); + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(curlen, bl_tree); + count--; + } + send_code(REP_3_6, bl_tree); + send_bits(count - 3, 2); + } else if (count <= 10) { + send_code(REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } else { + send_code(REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; + prevlen = curlen; + if (nextlen == 0) { + max_count = 138; + min_count = 3; + } else if (curlen == nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } + } + + // Output a byte on the stream. + // IN assertion: there is enough room in pending_buf. + final void put_byte(byte[] p, int start, int len) { + System.arraycopy(p, start, pending_buf, pending, len); + pending += len; + } + + final void put_byte(byte c) { + pending_buf[pending++] = c; + } + + final void put_short(int w) { + put_byte((byte) (w /* &0xff */)); + put_byte((byte) (w >>> 8)); + } + + final void putShortMSB(int b) { + put_byte((byte) (b >> 8)); + put_byte((byte) (b /* &0xff */)); + } + + final void send_code(int c, short[] tree) { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + void send_bits(int value, int length) { + int len = length; + if (bi_valid > Buf_size - len) { + int val = value; + // bi_buf |= (val << bi_valid); + bi_buf |= (short) ((val << bi_valid) & 0xffff); + put_short(bi_buf); + bi_buf = (short) (val >>> (Buf_size - bi_valid)); + bi_valid += len - Buf_size; + } else { + // bi_buf |= (value) << bi_valid; + bi_buf |= (short) (((value) << bi_valid) & 0xffff); + bi_valid += len; + } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + void _tr_align() { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, StaticTree.static_ltree); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, StaticTree.static_ltree); + bi_flush(); + } + last_eob_len = 7; + } + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + boolean _tr_tally(int dist, // distance of matched string + int lc // match length-MIN_MATCH or unmatched char (if dist==0) + ) { + + pending_buf[d_buf + last_lit * 2] = (byte) (dist >>> 8); + pending_buf[d_buf + last_lit * 2 + 1] = (byte) dist; + + l_buf[last_lit] = (byte) lc; + last_lit++; + + if (dist == 0) { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } else { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Tree._length_code[lc] + LITERALS + 1) * 2]++; + dyn_dtree[Tree.d_code(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && level > 2) { + // Compute an upper bound for the compressed length + int out_length = last_lit * 8; + int in_length = strstart - block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (int) dyn_dtree[dcode * 2] * (5 + Tree.extra_dbits[dcode]); + } + out_length >>>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) + return true; + } + + return (last_lit == lit_bufsize - 1); + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + // Send the block data compressed using the given Huffman trees + void compress_block(short[] ltree, short[] dtree) { + int dist; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) { + do { + dist = ((pending_buf[d_buf + lx * 2] << 8) & 0xff00) + | (pending_buf[d_buf + lx * 2 + 1] & 0xff); + lc = (l_buf[lx]) & 0xff; + lx++; + + if (dist == 0) { + send_code(lc, ltree); // send a literal byte + } else { + // Here, lc is the match length - MIN_MATCH + code = Tree._length_code[lc]; + + send_code(code + LITERALS + 1, ltree); // send the length code + extra = Tree.extra_lbits[code]; + if (extra != 0) { + lc -= Tree.base_length[code]; + send_bits(lc, extra); // send the extra length bits + } + dist--; // dist is now the match distance - 1 + code = Tree.d_code(dist); + + send_code(code, dtree); // send the distance code + extra = Tree.extra_dbits[code]; + if (extra != 0) { + dist -= Tree.base_dist[code]; + send_bits(dist, extra); // send the extra distance bits + } + } // literal or match pair ? + + // Check that the overlay between pending_buf and d_buf+l_buf is ok: + } while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + void set_data_type() { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) { + bin_freq += dyn_ltree[n * 2]; + n++; + } + while (n < 128) { + ascii_freq += dyn_ltree[n * 2]; + n++; + } + while (n < LITERALS) { + bin_freq += dyn_ltree[n * 2]; + n++; + } + data_type = (byte) (bin_freq > (ascii_freq >>> 2) ? Z_BINARY : Z_ASCII); + } + + // Flush the bit buffer, keeping at most 7 bits in it. + void bi_flush() { + if (bi_valid == 16) { + put_short(bi_buf); + bi_buf = 0; + bi_valid = 0; + } else if (bi_valid >= 8) { + put_byte((byte) bi_buf); + bi_buf >>>= 8; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + void bi_windup() { + if (bi_valid > 8) { + put_short(bi_buf); + } else if (bi_valid > 0) { + put_byte((byte) bi_buf); + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + void copy_block(int buf, // the input data + int len, // its length + boolean header // true if block header must be written + ) { + int index = 0; + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) { + put_short((short) len); + put_short((short) ~len); + } + + // while(len--!=0) { + // put_byte(window[buf+index]); + // index++; + // } + put_byte(window, buf, len); + } + + void flush_block_only(boolean eof) { + _tr_flush_block(block_start >= 0 ? block_start : -1, strstart - block_start, eof); + block_start = strstart; + strm.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + int deflate_stored(int flush) { + // Stored blocks are limited to 0xffff bytes, pending_buf is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > pending_buf_size - 5) { + max_block_size = pending_buf_size - 5; + } + + // Copy as much as possible from input to output: + while (true) { + // Fill the window as much as possible: + if (lookahead <= 1) { + fill_window(); + if (lookahead == 0 && flush == Z_NO_FLUSH) + return NeedMore; + if (lookahead == 0) + break; // flush the current block + } + + strstart += lookahead; + lookahead = 0; + + // Emit a stored block if pending_buf will be full: + max_start = block_start + max_block_size; + if (strstart == 0 || strstart >= max_start) { + // strstart == 0 is possible when wraparound on 16-bit machine + lookahead = strstart - max_start; + strstart = max_start; + + flush_block_only(false); + if (strm.avail_out == 0) + return NeedMore; + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (strstart - block_start >= w_size - MIN_LOOKAHEAD) { + flush_block_only(false); + if (strm.avail_out == 0) + return NeedMore; + } + } + + flush_block_only(flush == Z_FINISH); + if (strm.avail_out == 0) + return (flush == Z_FINISH) ? FinishStarted : NeedMore; + + return flush == Z_FINISH ? FinishDone : BlockDone; + } + + // Send a stored block + void _tr_stored_block(int buf, // input block + int stored_len, // length of input block + boolean eof // true if this is the last block for a file + ) { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + void _tr_flush_block(int buf, // input block, or NULL if too old + int stored_len, // length of input block + boolean eof // true if this is the last block for a file + ) { + int opt_lenb, static_lenb; // opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (level > 0) { + // Check if the file is ascii or binary + if (data_type == Z_UNKNOWN) + set_data_type(); + + // Construct the literal and distance trees + l_desc.build_tree(this); + + d_desc.build_tree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >>> 3; + static_lenb = (static_len + 3 + 7) >>> 3; + + if (static_lenb <= opt_lenb) + opt_lenb = static_lenb; + } else { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } else if (static_lenb == opt_lenb) { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + compress_block(StaticTree.static_ltree, StaticTree.static_dtree); + } else { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(l_desc.max_code + 1, d_desc.max_code + 1, max_blindex + 1); + compress_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + init_block(); + + if (eof) { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + void fill_window() { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do { + more = (window_size - lookahead - strstart); + + // Deal with !@#$% 64K limit: + if (more == 0 && strstart == 0 && lookahead == 0) { + more = w_size; + } else if (more == -1) { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) { + System.arraycopy(window, w_size, window, 0, w_size); + match_start -= w_size; + strstart -= w_size; // we now have strstart >= MAX_DIST + block_start -= w_size; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = hash_size; + p = n; + do { + m = (head[--p] & 0xffff); + head[p] = (m >= w_size ? (short) (m - w_size) : 0); + } while (--n != 0); + + n = w_size; + p = n; + do { + m = (prev[--p] & 0xffff); + prev[p] = (m >= w_size ? (short) (m - w_size) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } while (--n != 0); + more += w_size; + } + + if (strm.avail_in == 0) + return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = strm.read_buf(window, strstart + lookahead, more); + lookahead += n; + + // Initialize the hash value now that we have some input: + if (lookahead >= MIN_MATCH) { + ins_h = window[strstart] & 0xff; + ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } while (lookahead < MIN_LOOKAHEAD && strm.avail_in != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + int deflate_fast(int flush) { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + boolean bflush; // set if current block must be flushed + + while (true) { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (lookahead < MIN_LOOKAHEAD) { + fill_window(); + if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return NeedMore; + } + if (lookahead == 0) + break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (lookahead >= MIN_MATCH) { + ins_h = + (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = (short) strstart; + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (strategy != Z_HUFFMAN_ONLY) { + match_length = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (match_length >= MIN_MATCH) { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH); + + lookahead -= match_length; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (match_length <= max_lazy_match && lookahead >= MIN_MATCH) { + match_length--; // string at strstart already in hash table + do { + strstart++; + + ins_h = + ((ins_h << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = (short) strstart; + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } while (--match_length != 0); + strstart++; + } else { + strstart += match_length; + match_length = 0; + ins_h = window[strstart] & 0xff; + + ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } else { + // No match, output a literal byte + + bflush = _tr_tally(0, window[strstart] & 0xff); + lookahead--; + strstart++; + } + if (bflush) { + + flush_block_only(false); + if (strm.avail_out == 0) + return NeedMore; + } + } + + flush_block_only(flush == Z_FINISH); + if (strm.avail_out == 0) { + if (flush == Z_FINISH) + return FinishStarted; + else + return NeedMore; + } + return flush == Z_FINISH ? FinishDone : BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + int deflate_slow(int flush) { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + boolean bflush; // set if current block must be flushed + + // Process the input block. + while (true) { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (lookahead < MIN_LOOKAHEAD) { + fill_window(); + if (lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return NeedMore; + } + if (lookahead == 0) + break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (lookahead >= MIN_MATCH) { + ins_h = + (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = (short) strstart; + } + + // Find the longest match, discarding those <= prev_length. + prev_length = match_length; + prev_match = match_start; + match_length = MIN_MATCH - 1; + + if (hash_head != 0 && prev_length < max_lazy_match + && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (strategy != Z_HUFFMAN_ONLY) { + match_length = longest_match(hash_head); + } + // longest_match() sets match_start + + if (match_length <= 5 && (strategy == Z_FILTERED + || (match_length == MIN_MATCH && strstart - match_start > 4096))) { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + match_length = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (prev_length >= MIN_MATCH && match_length <= prev_length) { + int max_insert = strstart + lookahead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + lookahead -= prev_length - 1; + prev_length -= 2; + do { + if (++strstart <= max_insert) { + ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) + & hash_mask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = (short) strstart; + } + } while (--prev_length != 0); + match_available = 0; + match_length = MIN_MATCH - 1; + strstart++; + + if (bflush) { + flush_block_only(false); + if (strm.avail_out == 0) + return NeedMore; + } + } else if (match_available != 0) { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, window[strstart - 1] & 0xff); + + if (bflush) { + flush_block_only(false); + } + strstart++; + lookahead--; + if (strm.avail_out == 0) + return NeedMore; + } else { + // There is no previous match to compare with, wait for + // the next step to decide. + + match_available = 1; + strstart++; + lookahead--; + } + } + + if (match_available != 0) { + bflush = _tr_tally(0, window[strstart - 1] & 0xff); + match_available = 0; + } + flush_block_only(flush == Z_FINISH); + + if (strm.avail_out == 0) { + if (flush == Z_FINISH) + return FinishStarted; + else + return NeedMore; + } + + return flush == Z_FINISH ? FinishDone : BlockDone; + } + + int longest_match(int cur_match) { + int chain_length = max_chain_length; // max hash chain length + int scan = strstart; // current string + int match; // matched string + int len; // length of current match + int best_len = prev_length; // best match length so far + int limit = strstart > (w_size - MIN_LOOKAHEAD) ? strstart - (w_size - MIN_LOOKAHEAD) : 0; + int nice_match = this.nice_match; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = w_mask; + + int strend = strstart + MAX_MATCH; + byte scan_end1 = window[scan + best_len - 1]; + byte scan_end = window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (prev_length >= good_match) { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (nice_match > lookahead) + nice_match = lookahead; + + do { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (window[match + best_len] != scan_end || window[match + best_len - 1] != scan_end1 + || window[match] != window[scan] || window[++match] != window[scan + 1]) + continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; + match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do { + } while (window[++scan] == window[++match] && window[++scan] == window[++match] + && window[++scan] == window[++match] && window[++scan] == window[++match] + && window[++scan] == window[++match] && window[++scan] == window[++match] + && window[++scan] == window[++match] && window[++scan] == window[++match] + && scan < strend); + + len = MAX_MATCH - strend - scan; + scan = strend - MAX_MATCH; + + if (len > best_len) { + match_start = cur_match; + best_len = len; + if (len >= nice_match) + break; + scan_end1 = window[scan + best_len - 1]; + scan_end = window[scan + best_len]; + } + + } while ((cur_match = (prev[cur_match & wmask] & 0xffff)) > limit && --chain_length != 0); + + if (best_len <= lookahead) + return best_len; + return lookahead; + } + + int deflateInit(int level, int bits, int memlevel) { + return deflateInit(level, Z_DEFLATED, bits, memlevel, Z_DEFAULT_STRATEGY); + } + + int deflateInit(int level, int bits) { + return deflateInit(level, Z_DEFLATED, bits, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + } + + int deflateInit(int level) { + return deflateInit(level, MAX_WBITS); + } + + private int deflateInit(int level, int method, int windowBits, int memLevel, int strategy) { + int wrap = 1; + // byte[] my_version=ZLIB_VERSION; + + // + // if (version == null || version[0] != my_version[0] + // || stream_size != sizeof(z_stream)) { + // return Z_VERSION_ERROR; + // } + + strm.msg = null; + + if (level == Z_DEFAULT_COMPRESSION) + level = 6; + + if (windowBits < 0) { // undocumented feature: suppress zlib header + wrap = 0; + windowBits = -windowBits; + } else if (windowBits > 15) { + wrap = 2; + windowBits -= 16; + strm.adler = new CRC32(); + } + + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || windowBits < 9 + || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + + strm.dstate = this; + + this.wrap = wrap; + w_bits = windowBits; + w_size = 1 << w_bits; + w_mask = w_size - 1; + + hash_bits = memLevel + 7; + hash_size = 1 << hash_bits; + hash_mask = hash_size - 1; + hash_shift = ((hash_bits + MIN_MATCH - 1) / MIN_MATCH); + + window = new byte[w_size * 2]; + prev = new short[w_size]; + head = new short[hash_size]; + + lit_bufsize = 1 << (memLevel + 6); // 16K elements by default + + // We overlay pending_buf and d_buf+l_buf. This works since the average + // output size for (length,distance) codes is <= 24 bits. + pending_buf = new byte[lit_bufsize * 3]; + pending_buf_size = lit_bufsize * 3; + + d_buf = lit_bufsize; + l_buf = new byte[lit_bufsize]; + + this.level = level; + + this.strategy = strategy; + this.method = (byte) method; + + return deflateReset(); + } + + int deflateReset() { + strm.total_in = strm.total_out = 0; + strm.msg = null; // + strm.data_type = Z_UNKNOWN; + + pending = 0; + pending_out = 0; + + if (wrap < 0) { + wrap = -wrap; + } + status = (wrap == 0) ? BUSY_STATE : INIT_STATE; + strm.adler.reset(); + + last_flush = Z_NO_FLUSH; + + tr_init(); + lm_init(); + return Z_OK; + } + + int deflateEnd() { + if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending_buf = null; + l_buf = null; + head = null; + prev = null; + window = null; + // free + // dstate=null; + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; + } + + int deflateParams(int _level, int _strategy) { + int err = Z_OK; + + if (_level == Z_DEFAULT_COMPRESSION) { + _level = 6; + } + if (_level < 0 || _level > 9 || _strategy < 0 || _strategy > Z_HUFFMAN_ONLY) { + return Z_STREAM_ERROR; + } + + if (config_table[level].func != config_table[_level].func && strm.total_in != 0) { + // Flush the last buffer: + err = strm.deflate(Z_PARTIAL_FLUSH); + } + + if (level != _level) { + level = _level; + max_lazy_match = config_table[level].max_lazy; + good_match = config_table[level].good_length; + nice_match = config_table[level].nice_length; + max_chain_length = config_table[level].max_chain; + } + strategy = _strategy; + return err; + } + + int deflateSetDictionary(byte[] dictionary, int dictLength) { + int length = dictLength; + int index = 0; + + if (dictionary == null || status != INIT_STATE) + return Z_STREAM_ERROR; + + strm.adler.update(dictionary, 0, dictLength); + + if (length < MIN_MATCH) + return Z_OK; + if (length > w_size - MIN_LOOKAHEAD) { + length = w_size - MIN_LOOKAHEAD; + index = dictLength - length; // use the tail of the dictionary + } + System.arraycopy(dictionary, index, window, 0, length); + strstart = length; + block_start = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + ins_h = window[0] & 0xff; + ins_h = (((ins_h) << hash_shift) ^ (window[1] & 0xff)) & hash_mask; + + for (int n = 0; n <= length - MIN_MATCH; n++) { + ins_h = (((ins_h) << hash_shift) ^ (window[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + prev[n & w_mask] = head[ins_h]; + head[ins_h] = (short) n; + } + return Z_OK; + } + + int deflate(int flush) { + int old_flush; + + if (flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + + if (strm.next_out == null || (strm.next_in == null && strm.avail_in != 0) + || (status == FINISH_STATE && flush != Z_FINISH)) { + strm.msg = z_errmsg[Z_NEED_DICT - (Z_STREAM_ERROR)]; + return Z_STREAM_ERROR; + } + if (strm.avail_out == 0) { + strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + old_flush = last_flush; + last_flush = flush; + + // Write the zlib header + if (status == INIT_STATE) { + if (wrap == 2) { + getGZIPHeader().put(this); + status = BUSY_STATE; + strm.adler.reset(); + } else { + int header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8; + int level_flags = ((level - 1) & 0xff) >> 1; + + if (level_flags > 3) + level_flags = 3; + header |= (level_flags << 6); + if (strstart != 0) + header |= PRESET_DICT; + header += 31 - (header % 31); + + status = BUSY_STATE; + putShortMSB(header); + + // Save the adler32 of the preset dictionary: + if (strstart != 0) { + long adler = strm.adler.getValue(); + putShortMSB((int) (adler >>> 16)); + putShortMSB((int) (adler & 0xffff)); + } + strm.adler.reset(); + } + } + + // Flush as much pending output as possible + if (pending != 0) { + strm.flush_pending(); + if (strm.avail_out == 0) { + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = -1; + return Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } else if (strm.avail_in == 0 && flush <= old_flush && flush != Z_FINISH) { + strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + // User must not provide more input after the first FINISH: + if (status == FINISH_STATE && strm.avail_in != 0) { + strm.msg = z_errmsg[Z_NEED_DICT - (Z_BUF_ERROR)]; + return Z_BUF_ERROR; + } + + // Start a new block or continue the current one. + if (strm.avail_in != 0 || lookahead != 0 || (flush != Z_NO_FLUSH && status != FINISH_STATE)) { + int bstate = -1; + switch (config_table[level].func) { + case STORED: + bstate = deflate_stored(flush); + break; + case FAST: + bstate = deflate_fast(flush); + break; + case SLOW: + bstate = deflate_slow(flush); + break; + default: + } + + if (bstate == FinishStarted || bstate == FinishDone) { + status = FINISH_STATE; + } + if (bstate == NeedMore || bstate == FinishStarted) { + if (strm.avail_out == 0) { + last_flush = -1; // avoid BUF_ERROR next call, see above + } + return Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockDone) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(); + } else { // FULL_FLUSH or SYNC_FLUSH + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == Z_FULL_FLUSH) { + // state.head[s.hash_size-1]=0; + for (int i = 0; i < hash_size /*-1*/; i++) // forget history + head[i] = 0; + } + } + strm.flush_pending(); + if (strm.avail_out == 0) { + last_flush = -1; // avoid BUF_ERROR at next call, see above + return Z_OK; + } + } + } + + if (flush != Z_FINISH) + return Z_OK; + if (wrap <= 0) + return Z_STREAM_END; + + if (wrap == 2) { + long adler = strm.adler.getValue(); + put_byte((byte) (adler & 0xff)); + put_byte((byte) ((adler >> 8) & 0xff)); + put_byte((byte) ((adler >> 16) & 0xff)); + put_byte((byte) ((adler >> 24) & 0xff)); + put_byte((byte) (strm.total_in & 0xff)); + put_byte((byte) ((strm.total_in >> 8) & 0xff)); + put_byte((byte) ((strm.total_in >> 16) & 0xff)); + put_byte((byte) ((strm.total_in >> 24) & 0xff)); + + getGZIPHeader().setCRC(adler); + } else { + // Write the zlib trailer (adler32) + long adler = strm.adler.getValue(); + putShortMSB((int) (adler >>> 16)); + putShortMSB((int) (adler & 0xffff)); + } + + strm.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + + if (wrap > 0) + wrap = -wrap; // write the trailer only once! + return pending != 0 ? Z_OK : Z_STREAM_END; + } + + static int deflateCopy(ZStream dest, ZStream src) { + + if (src.dstate == null) { + return Z_STREAM_ERROR; + } + + if (src.next_in != null) { + dest.next_in = new byte[src.next_in.length]; + System.arraycopy(src.next_in, 0, dest.next_in, 0, src.next_in.length); + } + dest.next_in_index = src.next_in_index; + dest.avail_in = src.avail_in; + dest.total_in = src.total_in; + + if (src.next_out != null) { + dest.next_out = new byte[src.next_out.length]; + System.arraycopy(src.next_out, 0, dest.next_out, 0, src.next_out.length); + } + + dest.next_out_index = src.next_out_index; + dest.avail_out = src.avail_out; + dest.total_out = src.total_out; + + dest.msg = src.msg; + dest.data_type = src.data_type; + dest.adler = src.adler.copy(); + + try { + dest.dstate = (Deflate) src.dstate.clone(); + dest.dstate.strm = dest; + } catch (CloneNotSupportedException e) { + // + } + return Z_OK; + } + + @Override + public Object clone() throws CloneNotSupportedException { + Deflate dest = (Deflate) super.clone(); + + dest.pending_buf = dup(dest.pending_buf); + dest.l_buf = dup(dest.l_buf); + dest.window = dup(dest.window); + + dest.prev = dup(dest.prev); + dest.head = dup(dest.head); + dest.dyn_ltree = dup(dest.dyn_ltree); + dest.dyn_dtree = dup(dest.dyn_dtree); + dest.bl_tree = dup(dest.bl_tree); + + dest.bl_count = dup(dest.bl_count); + dest.next_code = dup(dest.next_code); + dest.heap = dup(dest.heap); + dest.depth = dup(dest.depth); + + dest.l_desc.dyn_tree = dest.dyn_ltree; + dest.d_desc.dyn_tree = dest.dyn_dtree; + dest.bl_desc.dyn_tree = dest.bl_tree; + + /* + * dest.l_desc.stat_desc = StaticTree.static_l_desc; dest.d_desc.stat_desc = + * StaticTree.static_d_desc; dest.bl_desc.stat_desc = StaticTree.static_bl_desc; + */ + + if (dest.gheader != null) { + dest.gheader = (GZIPHeader) dest.gheader.clone(); + } + + return dest; + } + + private byte[] dup(byte[] buf) { + byte[] foo = new byte[buf.length]; + System.arraycopy(buf, 0, foo, 0, foo.length); + return foo; + } + + private short[] dup(short[] buf) { + short[] foo = new short[buf.length]; + System.arraycopy(buf, 0, foo, 0, foo.length); + return foo; + } + + private int[] dup(int[] buf) { + int[] foo = new int[buf.length]; + System.arraycopy(buf, 0, foo, 0, foo.length); + return foo; + } + + synchronized GZIPHeader getGZIPHeader() { + if (gheader == null) { + gheader = new GZIPHeader(); + } + return gheader; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Deflater.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Deflater.java new file mode 100644 index 0000000..0941a21 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Deflater.java @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class Deflater extends ZStream { + + private static final int MAX_WBITS = 15; // 32K LZ77 window + private static final int DEF_WBITS = MAX_WBITS; + + private static final int Z_NO_FLUSH = 0; + private static final int Z_PARTIAL_FLUSH = 1; + private static final int Z_SYNC_FLUSH = 2; + private static final int Z_FULL_FLUSH = 3; + private static final int Z_FINISH = 4; + + private static final int MAX_MEM_LEVEL = 9; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + private boolean finished = false; + + Deflater() { + super(); + } + + Deflater(int level) throws GZIPException { + this(level, MAX_WBITS); + } + + Deflater(int level, boolean nowrap) throws GZIPException { + this(level, MAX_WBITS, nowrap); + } + + Deflater(int level, int bits) throws GZIPException { + this(level, bits, false); + } + + Deflater(int level, int bits, boolean nowrap) throws GZIPException { + super(); + int ret = init(level, bits, nowrap); + if (ret != Z_OK) + throw new GZIPException(ret + ": " + msg); + } + + Deflater(int level, int bits, int memlevel, JZlib.WrapperType wrapperType) throws GZIPException { + super(); + int ret = init(level, bits, memlevel, wrapperType); + if (ret != Z_OK) + throw new GZIPException(ret + ": " + msg); + } + + Deflater(int level, int bits, int memlevel) throws GZIPException { + super(); + int ret = init(level, bits, memlevel); + if (ret != Z_OK) + throw new GZIPException(ret + ": " + msg); + } + + int init(int level) { + return init(level, MAX_WBITS); + } + + int init(int level, boolean nowrap) { + return init(level, MAX_WBITS, nowrap); + } + + int init(int level, int bits) { + return init(level, bits, false); + } + + int init(int level, int bits, int memlevel, JZlib.WrapperType wrapperType) { + if (bits < 9 || bits > 15) { + return Z_STREAM_ERROR; + } + if (wrapperType == JZlib.W_NONE) { + bits *= -1; + } else if (wrapperType == JZlib.W_GZIP) { + bits += 16; + } else if (wrapperType == JZlib.W_ANY) { + return Z_STREAM_ERROR; + } else if (wrapperType == JZlib.W_ZLIB) { + } + return init(level, bits, memlevel); + } + + int init(int level, int bits, int memlevel) { + finished = false; + dstate = new Deflate(this); + return dstate.deflateInit(level, bits, memlevel); + } + + int init(int level, int bits, boolean nowrap) { + finished = false; + dstate = new Deflate(this); + return dstate.deflateInit(level, nowrap ? -bits : bits); + } + + @Override + int deflate(int flush) { + if (dstate == null) { + return Z_STREAM_ERROR; + } + int ret = dstate.deflate(flush); + if (ret == Z_STREAM_END) + finished = true; + return ret; + } + + @Override + int end() { + finished = true; + if (dstate == null) + return Z_STREAM_ERROR; + int ret = dstate.deflateEnd(); + dstate = null; + free(); + return ret; + } + + int params(int level, int strategy) { + if (dstate == null) + return Z_STREAM_ERROR; + return dstate.deflateParams(level, strategy); + } + + int setDictionary(byte[] dictionary, int dictLength) { + if (dstate == null) + return Z_STREAM_ERROR; + return dstate.deflateSetDictionary(dictionary, dictLength); + } + + @Override + boolean finished() { + return finished; + } + + int copy(Deflater src) { + this.finished = src.finished; + return Deflate.deflateCopy(this, src); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/DeflaterOutputStream.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/DeflaterOutputStream.java new file mode 100644 index 0000000..ce4be27 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/DeflaterOutputStream.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jzlib; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +final class DeflaterOutputStream extends FilterOutputStream { + + protected final Deflater deflater; + + protected byte[] buffer; + + private boolean closed = false; + + private boolean syncFlush = false; + + private final byte[] buf1 = new byte[1]; + + protected boolean mydeflater = false; + + private boolean close_out = true; + + protected static final int DEFAULT_BUFSIZE = 512; + + DeflaterOutputStream(OutputStream out) throws IOException { + this(out, new Deflater(JZlib.Z_DEFAULT_COMPRESSION), DEFAULT_BUFSIZE, true); + mydeflater = true; + } + + DeflaterOutputStream(OutputStream out, Deflater def) throws IOException { + this(out, def, DEFAULT_BUFSIZE, true); + } + + DeflaterOutputStream(OutputStream out, Deflater deflater, int size) throws IOException { + this(out, deflater, size, true); + } + + DeflaterOutputStream(OutputStream out, Deflater deflater, int size, boolean close_out) + throws IOException { + super(out); + if (out == null || deflater == null) { + throw new NullPointerException(); + } else if (size <= 0) { + throw new IllegalArgumentException("buffer size must be greater than 0"); + } + this.deflater = deflater; + buffer = new byte[size]; + this.close_out = close_out; + } + + @Override + public void write(int b) throws IOException { + buf1[0] = (byte) (b & 0xff); + write(buf1, 0, 1); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + if (deflater.finished()) { + throw new IOException("finished"); + } else if (off < 0 || len < 0 || off + len > b.length) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } else { + int flush = syncFlush ? JZlib.Z_SYNC_FLUSH : JZlib.Z_NO_FLUSH; + deflater.setInput(b, off, len, true); + while (deflater.avail_in > 0) { + int err = deflate(flush); + if (err == JZlib.Z_STREAM_END) + break; + } + } + } + + void finish() throws IOException { + while (!deflater.finished()) { + deflate(JZlib.Z_FINISH); + } + } + + @Override + public void close() throws IOException { + if (!closed) { + finish(); + if (mydeflater) { + deflater.end(); + } + if (close_out) + out.close(); + closed = true; + } + } + + @SuppressWarnings("fallthrough") + protected int deflate(int flush) throws IOException { + deflater.setOutput(buffer, 0, buffer.length); + int err = deflater.deflate(flush); + switch (err) { + case JZlib.Z_OK: + case JZlib.Z_STREAM_END: + break; + case JZlib.Z_BUF_ERROR: + if (deflater.avail_in <= 0 && flush != JZlib.Z_FINISH) { + // flush() without any data + break; + } + default: + throw new IOException( + "failed to deflate: error=" + err + " avail_out=" + deflater.avail_out); + } + int len = deflater.next_out_index; + if (len > 0) { + out.write(buffer, 0, len); + } + return err; + } + + @Override + public void flush() throws IOException { + if (syncFlush && !deflater.finished()) { + while (true) { + int err = deflate(JZlib.Z_SYNC_FLUSH); + if (deflater.next_out_index < buffer.length) + break; + if (err == JZlib.Z_STREAM_END) + break; + } + } + out.flush(); + } + + long getTotalIn() { + return deflater.getTotalIn(); + } + + long getTotalOut() { + return deflater.getTotalOut(); + } + + void setSyncFlush(boolean syncFlush) { + this.syncFlush = syncFlush; + } + + boolean getSyncFlush() { + return this.syncFlush; + } + + Deflater getDeflater() { + return deflater; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/GZIPException.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/GZIPException.java new file mode 100644 index 0000000..81204ea --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/GZIPException.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +import java.io.IOException; + +final class GZIPException extends IOException { + private static final long serialVersionUID = -1L; + + GZIPException() { + super(); + } + + GZIPException(String s) { + super(s); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/GZIPHeader.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/GZIPHeader.java new file mode 100644 index 0000000..8c69a1c --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/GZIPHeader.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +import java.nio.charset.StandardCharsets; + +/** + * @see http://www.ietf.org/rfc/rfc1952.txt + */ +final class GZIPHeader implements Cloneable { + + static final byte OS_MSDOS = (byte) 0x00; + static final byte OS_AMIGA = (byte) 0x01; + static final byte OS_VMS = (byte) 0x02; + static final byte OS_UNIX = (byte) 0x03; + static final byte OS_ATARI = (byte) 0x05; + static final byte OS_OS2 = (byte) 0x06; + static final byte OS_MACOS = (byte) 0x07; + static final byte OS_TOPS20 = (byte) 0x0a; + static final byte OS_WIN32 = (byte) 0x0b; + static final byte OS_VMCMS = (byte) 0x04; + static final byte OS_ZSYSTEM = (byte) 0x08; + static final byte OS_CPM = (byte) 0x09; + static final byte OS_QDOS = (byte) 0x0c; + static final byte OS_RISCOS = (byte) 0x0d; + static final byte OS_UNKNOWN = (byte) 0xff; + + boolean text = false; + private boolean fhcrc = false; + int xflags; + int os = 255; + byte[] extra; + byte[] name; + byte[] comment; + int hcrc; + long crc; + boolean done = false; + long mtime = 0; + + void setModifiedTime(long mtime) { + this.mtime = mtime; + } + + long getModifiedTime() { + return mtime; + } + + void setOS(int os) { + if ((0 <= os && os <= 13) || os == 255) + this.os = os; + else + throw new IllegalArgumentException("os: " + os); + } + + int getOS() { + return os; + } + + void setName(String name) { + this.name = name.getBytes(StandardCharsets.ISO_8859_1); + } + + String getName() { + if (name == null) + return ""; + return new String(name, StandardCharsets.ISO_8859_1); + } + + void setComment(String comment) { + this.comment = comment.getBytes(StandardCharsets.ISO_8859_1); + } + + String getComment() { + if (comment == null) + return ""; + return new String(comment, StandardCharsets.ISO_8859_1); + } + + void setCRC(long crc) { + this.crc = crc; + } + + long getCRC() { + return crc; + } + + void put(Deflate d) { + int flag = 0; + if (text) { + flag |= 1; // FTEXT + } + if (fhcrc) { + flag |= 2; // FHCRC + } + if (extra != null) { + flag |= 4; // FEXTRA + } + if (name != null) { + flag |= 8; // FNAME + } + if (comment != null) { + flag |= 16; // FCOMMENT + } + int xfl = 0; + if (d.level == JZlib.Z_BEST_SPEED) { + xfl |= 4; + } else if (d.level == JZlib.Z_BEST_COMPRESSION) { + xfl |= 2; + } + + d.put_short((short) 0x8b1f); // ID1 ID2 + d.put_byte((byte) 8); // CM(Compression Method) + d.put_byte((byte) flag); + d.put_byte((byte) mtime); + d.put_byte((byte) (mtime >> 8)); + d.put_byte((byte) (mtime >> 16)); + d.put_byte((byte) (mtime >> 24)); + d.put_byte((byte) xfl); + d.put_byte((byte) os); + + if (extra != null) { + d.put_byte((byte) extra.length); + d.put_byte((byte) (extra.length >> 8)); + d.put_byte(extra, 0, extra.length); + } + + if (name != null) { + d.put_byte(name, 0, name.length); + d.put_byte((byte) 0); + } + + if (comment != null) { + d.put_byte(comment, 0, comment.length); + d.put_byte((byte) 0); + } + } + + @Override + public Object clone() throws CloneNotSupportedException { + GZIPHeader gheader = (GZIPHeader) super.clone(); + byte[] tmp; + if (gheader.extra != null) { + tmp = new byte[gheader.extra.length]; + System.arraycopy(gheader.extra, 0, tmp, 0, tmp.length); + gheader.extra = tmp; + } + + if (gheader.name != null) { + tmp = new byte[gheader.name.length]; + System.arraycopy(gheader.name, 0, tmp, 0, tmp.length); + gheader.name = tmp; + } + + if (gheader.comment != null) { + tmp = new byte[gheader.comment.length]; + System.arraycopy(gheader.comment, 0, tmp, 0, tmp.length); + gheader.comment = tmp; + } + + return gheader; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InfBlocks.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InfBlocks.java new file mode 100644 index 0000000..458ff5d --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InfBlocks.java @@ -0,0 +1,690 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class InfBlocks { + private static final int MANY = 1440; + + // And'ing with mask[n] masks the lower n bits + private static final int[] inflate_mask = {0x00000000, 0x00000001, 0x00000003, 0x00000007, + 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, + 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff}; + + // Table for deflate from PKZIP's appnote.txt. + static final int[] border = { // Order of the bit length code lengths + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + private static final int TYPE = 0; // get type bits (3, including end bit) + private static final int LENS = 1; // get lengths for stored + private static final int STORED = 2; // processing stored block + private static final int TABLE = 3; // get table lengths + private static final int BTREE = 4; // get bit lengths tree for a dynamic block + private static final int DTREE = 5; // get length, distance trees for a dynamic block + private static final int CODES = 6; // processing fixed or dynamic block + private static final int DRY = 7; // output remaining window bytes + private static final int DONE = 8; // finished last block, done + private static final int BAD = 9; // ot a data error--stuck here + + int mode; // current inflate_block mode + + int left; // if STORED, bytes left to copy + + int table; // table lengths (14 bits) + int index; // index into blens (or border) + int[] blens; // bit lengths of codes + int[] bb = new int[1]; // bit length tree depth + int[] tb = new int[1]; // bit length decoding tree + + int[] bl = new int[1]; + int[] bd = new int[1]; + + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + int[] tli = new int[1]; // tl_index + int[] tdi = new int[1]; // td_index + + private final InfCodes codes; // if CODES, current state + + int last; // true if this block is the last block + + // mode independent information + int bitk; // bits in bit buffer + int bitb; // bit buffer + int[] hufts; // single malloc for tree space + byte[] window; // sliding window + int end; // one byte after sliding window + int read; // window read pointer + int write; // window write pointer + private boolean check; + + private final InfTree inftree = new InfTree(); + + private final ZStream z; + + InfBlocks(ZStream z, int w) { + this.z = z; + this.codes = new InfCodes(this.z, this); + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.check = (z.istate.wrap == 0) ? false : true; + mode = TYPE; + reset(); + } + + void reset() { + if (mode == BTREE || mode == DTREE) { + } + if (mode == CODES) { + codes.free(z); + } + mode = TYPE; + bitk = 0; + bitb = 0; + read = write = 0; + if (check) { + z.adler.reset(); + } + } + + @SuppressWarnings("fallthrough") + int proc(int r) { + int t; // temporary storage + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + { + p = z.next_in_index; + n = z.avail_in; + b = bitb; + k = bitk; + } + { + q = write; + m = (q < read ? read - q - 1 : end - q); + } + + // process input based on current state + while (true) { + switch (mode) { + case TYPE: + while (k < (3)) { + if (n != 0) { + r = Z_OK; + } else { + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } ; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + t = b & 7; + last = t & 1; + + switch (t >>> 1) { + case 0: // stored + { + b >>>= (3); + k -= (3); + } + t = k & 7; // go to byte boundary + + { + b >>>= (t); + k -= (t); + } + mode = LENS; // get length of stored block + break; + case 1: // fixed + InfTree.inflate_trees_fixed(bl, bd, tl, td, z); + codes.init(bl[0], bd[0], tl[0], 0, td[0], 0); + + { + b >>>= (3); + k -= (3); + } + + mode = CODES; + break; + case 2: // dynamic + { + b >>>= (3); + k -= (3); + } + + mode = TABLE; + break; + case 3: // illegal + { + b >>>= (3); + k -= (3); + } + mode = BAD; + z.msg = "invalid block type"; + r = Z_DATA_ERROR; + + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } + break; + case LENS: + while (k < (32)) { + if (n != 0) { + r = Z_OK; + } else { + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } ; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if ((((~b) >>> 16) & 0xffff) != (b & 0xffff)) { + mode = BAD; + z.msg = "invalid stored block lengths"; + r = Z_DATA_ERROR; + + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? STORED : (last != 0 ? DRY : TYPE); + break; + case STORED: + if (n == 0) { + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } + + if (m == 0) { + if (q == end && read != 0) { + q = 0; + m = (q < read ? read - q - 1 : end - q); + } + if (m == 0) { + write = q; + r = inflate_flush(r); + q = write; + m = (q < read ? read - q - 1 : end - q); + if (q == end && read != 0) { + q = 0; + m = (q < read ? read - q - 1 : end - q); + } + if (m == 0) { + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } + } + } + r = Z_OK; + + t = left; + if (t > n) + t = n; + if (t > m) + t = m; + System.arraycopy(z.next_in, p, window, q, t); + p += t; + n -= t; + q += t; + m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? DRY : TYPE; + break; + case TABLE: + while (k < (14)) { + if (n != 0) { + r = Z_OK; + } else { + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } ; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) { + mode = BAD; + z.msg = "too many length or distance symbols"; + r = Z_DATA_ERROR; + + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.length < t) { + blens = new int[t]; + } else { + for (int i = 0; i < t; i++) { + blens[i] = 0; + } + } + + { + b >>>= (14); + k -= (14); + } + + index = 0; + mode = BTREE; + case BTREE: + while (index < 4 + (table >>> 10)) { + while (k < (3)) { + if (n != 0) { + r = Z_OK; + } else { + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } ; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + + { + b >>>= (3); + k -= (3); + } + } + + while (index < 19) { + blens[border[index++]] = 0; + } + + bb[0] = 7; + t = inftree.inflate_trees_bits(blens, bb, tb, hufts, z); + if (t != Z_OK) { + r = t; + if (r == Z_DATA_ERROR) { + blens = null; + mode = BAD; + } + + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } + + index = 0; + mode = DTREE; + case DTREE: + while (true) { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) { + break; + } + + int[] h; + int i, j, c; + + t = bb[0]; + + while (k < (t)) { + if (n != 0) { + r = Z_OK; + } else { + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } ; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + if (tb[0] == -1) { + // System.err.println("null..."); + } + + t = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & inflate_mask[t])) * 3 + 2]; + + if (c < 16) { + b >>>= (t); + k -= (t); + blens[index++] = c; + } else { // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) { + if (n != 0) { + r = Z_OK; + } else { + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } ; + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + b >>>= (t); + k -= (t); + + j += (b & inflate_mask[i]); + + b >>>= (i); + k -= (i); + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) { + blens = null; + mode = BAD; + z.msg = "invalid bit length repeat"; + r = Z_DATA_ERROR; + + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } + + c = c == 16 ? blens[i - 1] : 0; + do { + blens[i++] = c; + } while (--j != 0); + index = i; + } + } + + tb[0] = -1; { + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions + t = table; + t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), blens, bl, bd, + tli, tdi, hufts, z); + + if (t != Z_OK) { + if (t == Z_DATA_ERROR) { + blens = null; + mode = BAD; + } + r = t; + + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } + codes.init(bl[0], bd[0], hufts, tli[0], hufts, tdi[0]); + } + mode = CODES; + case CODES: + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + + if ((r = codes.proc(r)) != Z_STREAM_END) { + return inflate_flush(r); + } + r = Z_OK; + codes.free(z); + + p = z.next_in_index; + n = z.avail_in; + b = bitb; + k = bitk; + q = write; + m = (q < read ? read - q - 1 : end - q); + + if (last == 0) { + mode = TYPE; + break; + } + mode = DRY; + case DRY: + write = q; + r = inflate_flush(r); + q = write; + m = (q < read ? read - q - 1 : end - q); + if (read != write) { + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } + mode = DONE; + case DONE: + r = Z_STREAM_END; + + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + case BAD: + r = Z_DATA_ERROR; + + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + + default: + r = Z_STREAM_ERROR; + + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(r); + } + } + } + + void free() { + reset(); + window = null; + hufts = null; + // ZFREE(z, s); + } + + void set_dictionary(byte[] d, int start, int n) { + System.arraycopy(d, start, window, 0, n); + read = write = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or Z_FULL_FLUSH. + int sync_point() { + return mode == LENS ? 1 : 0; + } + + // copy as much as possible from the sliding window to the output area + int inflate_flush(int r) { + int n; + int p; + int q; + + // local copies of source and destination pointers + p = z.next_out_index; + q = read; + + // compute number of bytes to copy as far as end of window + n = ((q <= write ? write : end) - q); + if (n > z.avail_out) + n = z.avail_out; + if (n != 0 && r == Z_BUF_ERROR) + r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (check && n > 0) { + z.adler.update(window, q, n); + } + + // copy as far as end of window + System.arraycopy(window, q, z.next_out, p, n); + p += n; + q += n; + + // see if more to copy at beginning of window + if (q == end) { + // wrap pointers + q = 0; + if (write == end) + write = 0; + + // compute bytes to copy + n = write - q; + if (n > z.avail_out) + n = z.avail_out; + if (n != 0 && r == Z_BUF_ERROR) + r = Z_OK; + + // update counters + z.avail_out -= n; + z.total_out += n; + + // update check information + if (check && n > 0) { + z.adler.update(window, q, n); + } + + // copy + System.arraycopy(window, q, z.next_out, p, n); + p += n; + q += n; + } + + // update pointers + z.next_out_index = p; + read = q; + + // done + return r; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InfCodes.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InfCodes.java new file mode 100644 index 0000000..edfd089 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InfCodes.java @@ -0,0 +1,708 @@ +/* + * Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class InfCodes { + + private static final int[] inflate_mask = {0x00000000, 0x00000001, 0x00000003, 0x00000007, + 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, + 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff}; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + private static final int START = 0; // x: set up for LEN + private static final int LEN = 1; // i: get length/literal/eob next + private static final int LENEXT = 2; // i: getting length extra (have base) + private static final int DIST = 3; // i: get distance next + private static final int DISTEXT = 4; // i: getting distance extra + private static final int COPY = 5; // o: copying bytes in window, waiting for space + private static final int LIT = 6; // o: got literal, waiting for output space + private static final int WASH = 7; // o: got eob, possibly still output waiting + private static final int END = 8; // x: got eob and all data flushed + private static final int BADCODE = 9; // x: got error + + int mode; // current inflate_codes mode + + // mode dependent information + int len; + + int[] tree; // pointer into tree + int tree_index = 0; + int need; // bits needed + + int lit; + + // if EXT or COPY, where and how much + int get; // bits to get for extra + int dist; // distance back to copy from + + byte lbits; // ltree bits decoded per branch + byte dbits; // dtree bits decoder per branch + int[] ltree; // literal/length/eob tree + int ltree_index; // literal/length/eob tree + int[] dtree; // distance tree + int dtree_index; // distance tree + + private final ZStream z; + private final InfBlocks s; + + InfCodes(ZStream z, InfBlocks s) { + this.z = z; + this.s = s; + } + + void init(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index) { + mode = START; + lbits = (byte) bl; + dbits = (byte) bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + @SuppressWarnings("fallthrough") + int proc(int r) { + int j; // temporary storage + int[] t; // temporary pointer + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + // copy input/output information to locals (UPDATE macro restores) + p = z.next_in_index; + n = z.avail_in; + b = s.bitb; + k = s.bitk; + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + // process input and output based on current state + while (true) { + switch (mode) { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN + if (m >= 258 && n >= 10) { + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + r = inflate_fast(lbits, dbits, ltree, ltree_index, dtree, dtree_index, s, z); + + p = z.next_in_index; + n = z.avail_in; + b = s.bitb; + k = s.bitk; + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + if (r != Z_OK) { + mode = r == Z_STREAM_END ? WASH : BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = LEN; + case LEN: // i: get length/literal/eob next + j = need; + + while (k < (j)) { + if (n != 0) + r = Z_OK; + else { + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) { // literal + lit = tree[tindex + 2]; + mode = LIT; + break; + } + if ((e & 16) != 0) { // length + get = e & 15; + len = tree[tindex + 2]; + mode = LENEXT; + break; + } + if ((e & 64) == 0) { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) { // end of block + mode = WASH; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid literal/length code"; + r = Z_DATA_ERROR; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + + case LENEXT: // i: getting length extra (have base) + j = get; + + while (k < (j)) { + if (n != 0) + r = Z_OK; + else { + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + len += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = DIST; + case DIST: // i: get distance next + j = need; + + while (k < (j)) { + if (n != 0) + r = Z_OK; + else { + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & inflate_mask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 16) != 0) { // distance + get = e & 15; + dist = tree[tindex + 2]; + mode = DISTEXT; + break; + } + if ((e & 64) == 0) { // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = BADCODE; // invalid code + z.msg = "invalid distance code"; + r = Z_DATA_ERROR; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + + case DISTEXT: // i: getting distance extra + j = get; + + while (k < (j)) { + if (n != 0) + r = Z_OK; + else { + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + } + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + dist += (b & inflate_mask[j]); + + b >>= j; + k -= j; + + mode = COPY; + case COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) { // modulo window size-"while" instead + f += s.end; // of "if" handles invalid distances + } + while (len != 0) { + + if (m == 0) { + if (q == s.end && s.read != 0) { + q = 0; + m = q < s.read ? s.read - q - 1 : s.end - q; + } + if (m == 0) { + s.write = q; + r = s.inflate_flush(r); + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { + q = 0; + m = q < s.read ? s.read - q - 1 : s.end - q; + } + + if (m == 0) { + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + } + } + } + + s.window[q++] = s.window[f++]; + m--; + + if (f == s.end) + f = 0; + len--; + } + mode = START; + break; + case LIT: // o: got literal, waiting for output space + if (m == 0) { + if (q == s.end && s.read != 0) { + q = 0; + m = q < s.read ? s.read - q - 1 : s.end - q; + } + if (m == 0) { + s.write = q; + r = s.inflate_flush(r); + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + if (q == s.end && s.read != 0) { + q = 0; + m = q < s.read ? s.read - q - 1 : s.end - q; + } + if (m == 0) { + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + } + } + } + r = Z_OK; + + s.window[q++] = (byte) lit; + m--; + + mode = START; + break; + case WASH: // o: got eob, possibly more output + if (k > 7) { // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + s.write = q; + r = s.inflate_flush(r); + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + if (s.read != s.write) { + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + } + mode = END; + case END: + r = Z_STREAM_END; + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + + case BADCODE: // x: got error + r = Z_DATA_ERROR; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + + default: + r = Z_STREAM_ERROR; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + return s.inflate_flush(r); + } + } + } + + void free(ZStream z) { + // ZFREE(z, c); + } + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + int inflate_fast(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, InfBlocks s, + ZStream z) { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.next_in_index; + n = z.avail_in; + b = s.bitb; + k = s.bitk; + q = s.write; + m = q < s.read ? s.read - q - 1 : s.end - q; + + // initialize masks + ml = inflate_mask[bl]; + md = inflate_mask[bd]; + + // do until not enough input or output space for fast loop + do { // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) { // max bits for literal/length code + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) { + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte) tp[tp_index_t_3 + 2]; + m--; + continue; + } + do { + + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) { + e &= 15; + c = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= e; + k -= e; + + // decode distance base of block to copy + while (k < (15)) { // max bits for distance code + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do { + + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) { + // get extra bits to add to distance base + e &= 15; + while (k < (e)) { // get extra bits (up to 13) + n--; + b |= (z.next_in[p++] & 0xff) << k; + k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & inflate_mask[e]); + + b >>= (e); + k -= (e); + + // do the copy + m -= c; + if (q >= d) { // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } else { + System.arraycopy(s.window, r, s.window, q, 2); + q += 2; + r += 2; + c -= 2; + } + } else { // else offset after destination + r = q - d; + do { + r += s.end; // force pointer in window + } while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) { // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) { + do { + s.window[q++] = s.window[r++]; + } while (--e != 0); + } else { + System.arraycopy(s.window, r, s.window, q, e); + q += e; + r += e; + e = 0; + } + r = 0; // copy rest from start of window + } + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) { + do { + s.window[q++] = s.window[r++]; + } while (--c != 0); + } else { + System.arraycopy(s.window, r, s.window, q, c); + q += c; + r += c; + c = 0; + } + break; + } else if ((e & 64) == 0) { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } else { + z.msg = "invalid distance code"; + + c = z.avail_in - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= c << 3; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + + return Z_DATA_ERROR; + } + } while (true); + break; + } + + if ((e & 64) == 0) { + t += tp[tp_index_t_3 + 2]; + t += (b & inflate_mask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) { + + b >>= (tp[tp_index_t_3 + 1]); + k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte) tp[tp_index_t_3 + 2]; + m--; + break; + } + } else if ((e & 32) != 0) { + + c = z.avail_in - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= c << 3; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + + return Z_STREAM_END; + } else { + z.msg = "invalid literal/length code"; + + c = z.avail_in - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= c << 3; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + + return Z_DATA_ERROR; + } + } while (true); + } while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.avail_in - n; + c = (k >> 3) < c ? k >> 3 : c; + n += c; + p -= c; + k -= c << 3; + + s.bitb = b; + s.bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + s.write = q; + + return Z_OK; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InfTree.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InfTree.java new file mode 100644 index 0000000..26b6fa6 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InfTree.java @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class InfTree { + + private static final int MANY = 1440; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + static final int fixed_bl = 9; + static final int fixed_bd = 5; + + static final int[] fixed_tl = {96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, + 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0, 0, 8, 128, 0, 8, 64, + 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, + 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, + 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13, 0, 8, + 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, 7, 8, 0, 8, 92, 0, 8, + 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, + 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, + 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, 2, + 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 148, 84, 7, 67, 0, 8, 122, + 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, + 0, 9, 244, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, + 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 236, 80, 7, 9, + 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 220, 82, 7, 27, 0, 8, + 110, 0, 8, 46, 0, 9, 188, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, + 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97, 0, 8, 33, + 0, 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 146, + 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, + 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, + 117, 0, 8, 53, 0, 9, 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, + 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, + 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, + 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, + 8, 99, 0, 8, 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230, 80, 7, 7, 0, 8, 91, 0, 8, + 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, + 9, 182, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, + 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174, 0, 8, 7, 0, + 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, + 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, + 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 193, + 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, + 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, + 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, + 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, + 9, 169, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 153, 84, + 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 185, 0, 8, 12, 0, + 8, 140, 0, 8, 76, 0, 9, 249, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, + 0, 8, 50, 0, 9, 197, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8, 130, 0, 8, 66, + 0, 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 213, + 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 181, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, + 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8, + 102, 0, 8, 38, 0, 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, + 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 221, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, + 9, 189, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, + 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, + 0, 8, 129, 0, 8, 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 147, 83, 7, 59, 0, 8, 121, + 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, 9, 0, 8, 137, 0, 8, 73, + 0, 9, 243, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, + 203, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 235, 80, + 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, 23, 0, + 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 251, 80, 7, 3, 0, 8, 83, 0, + 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, + 0, 9, 167, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, + 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 183, 0, 8, + 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, + 119, 0, 8, 55, 0, 9, 207, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, + 71, 0, 9, 239, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, + 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 255}; + static final int[] fixed_td = {80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, + 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5, 8193, 82, 5, 9, 90, 5, + 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, 25, 91, 5, 6145, 81, 5, 7, 89, + 5, 1537, 85, 5, 97, 93, 5, 24577, 80, 5, 4, 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, + 90, 5, 3073, 86, 5, 193, 192, 5, 24577}; + + // Tables for deflate from PKZIP's appnote.txt. + static final int[] cplens = { // Copy lengths for literal codes 257..285 + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, + 163, 195, 227, 258, 0, 0}; + + // see note #13 above about 258 + static final int[] cplext = { // Extra bits for literal codes 257..285 + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, + 112 // 112==invalid + }; + + static final int[] cpdist = { // Copy offsets for distance codes 0..29 + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, + 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; + + static final int[] cpdext = { // Extra bits for distance codes + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, + 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + static final int BMAX = 15; // maximum bit length of any code + + int[] hn = null; // hufts used in space + int[] v = null; // work area for huft_build + int[] c = null; // bit length count table + int[] r = null; // table entry for structure assignment + int[] u = null; // table stack + int[] x = null; // bit offsets, then code stack + + private int huft_build(int[] b, // code lengths in bits (all assumed <= BMAX) + int bindex, int n, // number of codes (assumed <= 288) + int s, // number of simple-valued codes (0..s-1) + int[] d, // list of base values for non-simple codes + int[] e, // list of extra bits for non-simple codes + int[] t, // result: starting table + int[] m, // maximum lookup bits, returns actual + int[] hp, // space for trees + int[] hn, // hufts used in space + int[] v // working area: values in order of bit length + ) { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; + i = n; + do { + c[b[bindex + p]]++; + p++; + i--; // assume all entries <= BMAX + } while (i != 0); + + if (c[0] == n) { // null input--all zero length codes + t[0] = -1; + m[0] = 0; + return Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) + break; + k = j; // minimum code length + if (l < j) { + l = j; + } + for (i = BMAX; i != 0; i--) { + if (c[i] != 0) + break; + } + g = i; // maximum code length + if (l > i) { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) { + if ((y -= c[j]) < 0) { + return Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) { + return Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; + xp = 2; + while (--i != 0) { // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; + p = 0; + do { + if ((j = b[bindex + p]) != 0) { + v[x[j]++] = i; + } + p++; + } while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = -1; // no tables yet--level -1 + w = -l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) { + a = c[k]; + while (a-- != 0) { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l) ? l : z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) { // try a k-w bit table + // too few codes for k-w bit table + f -= a + 1; // deduct codes from patterns left + xp = k; + if (j < z) { + while (++j < z) { // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) { // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = /* hp+ */ hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) { + x[h] = i; // save pattern for backing up + r[0] = (byte) j; // bits in this table + r[1] = (byte) l; // bits to dump before this table + j = i >>> (w - l); + r[2] = q - u[h - 1] - j; // offset to this table + System.arraycopy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } else { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (byte) (k - w); + if (p >= n) { + r[0] = 128 + 64; // out of values--invalid code + } else if (v[p] < s) { + r[0] = (byte) (v[p] < 256 ? 0 : 32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } else { + r[0] = (byte) (e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = i >>> w; j < z; j += f) { + System.arraycopy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j >>>= 1) { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK; + } + + int inflate_trees_bits(int[] c, // 19 code lengths + int[] bb, // bits tree desired/actual depth + int[] tb, // bits tree result + int[] hp, // space for trees + ZStream z // for messages + ) { + int result; + initWorkArea(19); + hn[0] = 0; + result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == Z_DATA_ERROR) { + z.msg = "oversubscribed dynamic bit lengths tree"; + } else if (result == Z_BUF_ERROR || bb[0] == 0) { + z.msg = "incomplete dynamic bit lengths tree"; + result = Z_DATA_ERROR; + } + return result; + } + + int inflate_trees_dynamic(int nl, // number of literal/length codes + int nd, // number of distance codes + int[] c, // that many (total) code lengths + int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[] tl, // literal/length tree result + int[] td, // distance tree result + int[] hp, // space for trees + ZStream z // for messages + ) { + int result; + + // build literal/length tree + initWorkArea(288); + hn[0] = 0; + result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != Z_OK || bl[0] == 0) { + if (result == Z_DATA_ERROR) { + z.msg = "oversubscribed literal/length tree"; + } else if (result != Z_MEM_ERROR) { + z.msg = "incomplete literal/length tree"; + result = Z_DATA_ERROR; + } + return result; + } + + // build distance tree + initWorkArea(288); + result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != Z_OK || (bd[0] == 0 && nl > 257)) { + if (result == Z_DATA_ERROR) { + z.msg = "oversubscribed distance tree"; + } else if (result == Z_BUF_ERROR) { + z.msg = "incomplete distance tree"; + result = Z_DATA_ERROR; + } else if (result != Z_MEM_ERROR) { + z.msg = "empty distance tree with lengths"; + result = Z_DATA_ERROR; + } + return result; + } + + return Z_OK; + } + + static int inflate_trees_fixed(int[] bl, // literal desired/actual bit depth + int[] bd, // distance desired/actual bit depth + int[][] tl, // literal/length tree result + int[][] td, // distance tree result + ZStream z // for memory allocation + ) { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return Z_OK; + } + + private void initWorkArea(int vsize) { + if (hn == null) { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + if (v.length < vsize) { + v = new int[vsize]; + } + for (int i = 0; i < vsize; i++) { + v[i] = 0; + } + for (int i = 0; i < BMAX + 1; i++) { + c[i] = 0; + } + for (int i = 0; i < 3; i++) { + r[i] = 0; + } + System.arraycopy(c, 0, u, 0, BMAX); + System.arraycopy(c, 0, x, 0, BMAX + 1); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Inflate.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Inflate.java new file mode 100644 index 0000000..c937d6a --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Inflate.java @@ -0,0 +1,793 @@ +/* + * Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +import java.io.ByteArrayOutputStream; + +final class Inflate { + + private static final int MAX_WBITS = 15; // 32K LZ77 window + + // preset dictionary flag in zlib header + private static final int PRESET_DICT = 0x20; + + static final int Z_NO_FLUSH = 0; + static final int Z_PARTIAL_FLUSH = 1; + static final int Z_SYNC_FLUSH = 2; + static final int Z_FULL_FLUSH = 3; + static final int Z_FINISH = 4; + + private static final int Z_DEFLATED = 8; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + private static final int METHOD = 0; // waiting for method byte + private static final int FLAG = 1; // waiting for flag byte + private static final int DICT4 = 2; // four dictionary check bytes to go + private static final int DICT3 = 3; // three dictionary check bytes to go + private static final int DICT2 = 4; // two dictionary check bytes to go + private static final int DICT1 = 5; // one dictionary check byte to go + private static final int DICT0 = 6; // waiting for inflateSetDictionary + private static final int BLOCKS = 7; // decompressing blocks + private static final int CHECK4 = 8; // four check bytes to go + private static final int CHECK3 = 9; // three check bytes to go + private static final int CHECK2 = 10; // two check bytes to go + private static final int CHECK1 = 11; // one check byte to go + private static final int DONE = 12; // finished check, done + private static final int BAD = 13; // got an error--stay here + + private static final int HEAD = 14; + private static final int LENGTH = 15; + private static final int TIME = 16; + private static final int OS = 17; + private static final int EXLEN = 18; + private static final int EXTRA = 19; + private static final int NAME = 20; + private static final int COMMENT = 21; + private static final int HCRC = 22; + private static final int FLAGS = 23; + + static final int INFLATE_ANY = 0x40000000; + + int mode; // current inflate mode + + // mode dependent information + int method; // if FLAGS, method byte + + // if CHECK, check values to compare + long was = -1; // computed check value + long need; // stream check value + + // if BAD, inflateSync's marker bytes count + int marker; + + // mode independent information + int wrap; // flag for no wrapper + // 0: no wrapper + // 1: zlib header + // 2: gzip header + // 4: auto detection + + int wbits; // log2(window size) (8..15, defaults to 15) + + InfBlocks blocks; // current inflate_blocks state + + private final ZStream z; + + private int flags; + + private int need_bytes = -1; + private byte[] crcbuf = new byte[4]; + + GZIPHeader gheader = null; + + int inflateReset() { + if (z == null) + return Z_STREAM_ERROR; + + z.total_in = z.total_out = 0; + z.msg = null; + this.mode = HEAD; + this.need_bytes = -1; + this.blocks.reset(); + return Z_OK; + } + + int inflateEnd() { + if (blocks != null) { + blocks.free(); + } + return Z_OK; + } + + Inflate(ZStream z) { + this.z = z; + } + + int inflateInit(int w) { + z.msg = null; + blocks = null; + + // handle undocumented wrap option (no zlib header or check) + wrap = 0; + if (w < 0) { + w = -w; + } else if ((w & INFLATE_ANY) != 0) { + wrap = 4; + w &= ~INFLATE_ANY; + if (w < 48) + w &= 15; + } else if ((w & ~31) != 0) { // for example, DEF_WBITS + 32 + wrap = 4; // zlib and gzip wrapped data should be accepted. + w &= 15; + } else { + wrap = (w >> 4) + 1; + if (w < 48) + w &= 15; + } + + if (w < 8 || w > 15) { + inflateEnd(); + return Z_STREAM_ERROR; + } + if (blocks != null && wbits != w) { + blocks.free(); + blocks = null; + } + + // set window size + wbits = w; + + this.blocks = new InfBlocks(z, 1 << w); + + // reset state + inflateReset(); + + return Z_OK; + } + + @SuppressWarnings("fallthrough") + int inflate(int f) { + int hold = 0; + + int r; + int b; + + if (z == null || z.next_in == null) { + if (f == Z_FINISH && this.mode == HEAD) + return Z_OK; + return Z_STREAM_ERROR; + } + + f = f == Z_FINISH ? Z_BUF_ERROR : Z_OK; + r = Z_BUF_ERROR; + while (true) { + + switch (this.mode) { + case HEAD: + if (wrap == 0) { + this.mode = BLOCKS; + break; + } + + try { + r = readBytes(2, r, f); + } catch (Return e) { + return e.r; + } + + if ((wrap == 4 || (wrap & 2) != 0) && this.need == 0x8b1fL) { // gzip header + if (wrap == 4) { + wrap = 2; + } + z.adler = new CRC32(); + checksum(2, this.need); + + if (gheader == null) + gheader = new GZIPHeader(); + + this.mode = FLAGS; + break; + } + + if ((wrap & 2) != 0) { + this.mode = BAD; + z.msg = "incorrect header check"; + break; + } + + flags = 0; + + this.method = ((int) this.need) & 0xff; + b = ((int) (this.need >> 8)) & 0xff; + + if (((wrap & 1) == 0 || // check if zlib header allowed + (((this.method << 8) + b) % 31) != 0) && (this.method & 0xf) != Z_DEFLATED) { + if (wrap == 4) { + z.next_in_index -= 2; + z.avail_in += 2; + z.total_in -= 2; + wrap = 0; + this.mode = BLOCKS; + break; + } + this.mode = BAD; + z.msg = "incorrect header check"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + * this.marker = 5; // can't try inflateSync + */ + break; + } + + if ((this.method & 0xf) != Z_DEFLATED) { + this.mode = BAD; + z.msg = "unknown compression method"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + * this.marker = 5; // can't try inflateSync + */ + break; + } + + if (wrap == 4) { + wrap = 1; + } + + if ((this.method >> 4) + 8 > this.wbits) { + this.mode = BAD; + z.msg = "invalid window size"; + // since zlib 1.2, it is allowted to inflateSync for this case. + /* + * this.marker = 5; // can't try inflateSync + */ + break; + } + + z.adler = new Adler32(); + + if ((b & PRESET_DICT) == 0) { + this.mode = BLOCKS; + break; + } + this.mode = DICT4; + case DICT4: + if (z.avail_in == 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + this.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & 0xff000000L; + this.mode = DICT3; + case DICT3: + if (z.avail_in == 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + this.need += ((z.next_in[z.next_in_index++] & 0xff) << 16) & 0xff0000L; + this.mode = DICT2; + case DICT2: + if (z.avail_in == 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + this.need += ((z.next_in[z.next_in_index++] & 0xff) << 8) & 0xff00L; + this.mode = DICT1; + case DICT1: + if (z.avail_in == 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + this.need += (z.next_in[z.next_in_index++] & 0xffL); + z.adler.reset(this.need); + this.mode = DICT0; + return Z_NEED_DICT; + case DICT0: + this.mode = BAD; + z.msg = "need dictionary"; + this.marker = 0; // can try inflateSync + return Z_STREAM_ERROR; + case BLOCKS: + r = this.blocks.proc(r); + if (r == Z_DATA_ERROR) { + this.mode = BAD; + this.marker = 0; // can try inflateSync + break; + } + if (r == Z_OK) { + r = f; + } + if (r != Z_STREAM_END) { + return r; + } + r = f; + this.was = z.adler.getValue(); + this.blocks.reset(); + if (this.wrap == 0) { + this.mode = DONE; + break; + } + this.mode = CHECK4; + case CHECK4: + if (z.avail_in == 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + this.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & 0xff000000L; + this.mode = CHECK3; + case CHECK3: + if (z.avail_in == 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + this.need += ((z.next_in[z.next_in_index++] & 0xff) << 16) & 0xff0000L; + this.mode = CHECK2; + case CHECK2: + if (z.avail_in == 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + this.need += ((z.next_in[z.next_in_index++] & 0xff) << 8) & 0xff00L; + this.mode = CHECK1; + case CHECK1: + if (z.avail_in == 0) + return r; + r = f; + + z.avail_in--; + z.total_in++; + this.need += (z.next_in[z.next_in_index++] & 0xffL); + + if (flags != 0) { // gzip + this.need = ((this.need & 0xff000000) >> 24 | (this.need & 0x00ff0000) >> 8 + | (this.need & 0x0000ff00) << 8 | (this.need & 0x0000ffff) << 24) & 0xffffffffL; + } + + if (((int) (this.was)) != ((int) (this.need))) { + z.msg = "incorrect data check"; + // chack is delayed + /* + * this.mode = BAD; this.marker = 5; // can't try inflateSync break; + */ + } else if (flags != 0 && gheader != null) { + gheader.crc = this.need; + } + + this.mode = LENGTH; + case LENGTH: + if (wrap != 0 && flags != 0) { + + try { + r = readBytes(4, r, f); + } catch (Return e) { + return e.r; + } + + if (z.msg != null && z.msg.equals("incorrect data check")) { + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + } + + if (this.need != (z.total_out & 0xffffffffL)) { + z.msg = "incorrect length check"; + this.mode = BAD; + break; + } + z.msg = null; + } else { + if (z.msg != null && z.msg.equals("incorrect data check")) { + this.mode = BAD; + this.marker = 5; // can't try inflateSync + break; + } + } + + this.mode = DONE; + case DONE: + return Z_STREAM_END; + case BAD: + return Z_DATA_ERROR; + + case FLAGS: + try { + r = readBytes(2, r, f); + } catch (Return e) { + return e.r; + } + + flags = ((int) this.need) & 0xffff; + + if ((flags & 0xff) != Z_DEFLATED) { + z.msg = "unknown compression method"; + this.mode = BAD; + break; + } + if ((flags & 0xe000) != 0) { + z.msg = "unknown header flags set"; + this.mode = BAD; + break; + } + + if ((flags & 0x0200) != 0) { + checksum(2, this.need); + } + + this.mode = TIME; + + case TIME: + try { + r = readBytes(4, r, f); + } catch (Return e) { + return e.r; + } + if (gheader != null) { + gheader.setModifiedTime(this.need); + } + if ((flags & 0x0200) != 0) { + checksum(4, this.need); + } + this.mode = OS; + case OS: + try { + r = readBytes(2, r, f); + } catch (Return e) { + return e.r; + } + if (gheader != null) { + gheader.xflags = ((int) this.need) & 0xff; + gheader.os = (((int) this.need) >> 8) & 0xff; + } + if ((flags & 0x0200) != 0) { + checksum(2, this.need); + } + this.mode = EXLEN; + case EXLEN: + if ((flags & 0x0400) != 0) { + try { + r = readBytes(2, r, f); + } catch (Return e) { + return e.r; + } + if (gheader != null) { + gheader.extra = new byte[((int) this.need) & 0xffff]; + } + if ((flags & 0x0200) != 0) { + checksum(2, this.need); + } + } else if (gheader != null) { + gheader.extra = null; + } + this.mode = EXTRA; + + case EXTRA: + if ((flags & 0x0400) != 0) { + try { + r = readBytes(r, f); + if (gheader != null) { + byte[] foo = tmp_string.toByteArray(); + tmp_string = null; + if (foo.length == gheader.extra.length) { + System.arraycopy(foo, 0, gheader.extra, 0, foo.length); + } else { + z.msg = "bad extra field length"; + this.mode = BAD; + break; + } + } + } catch (Return e) { + return e.r; + } + } else if (gheader != null) { + gheader.extra = null; + } + this.mode = NAME; + case NAME: + if ((flags & 0x0800) != 0) { + try { + r = readString(r, f); + if (gheader != null) { + gheader.name = tmp_string.toByteArray(); + } + tmp_string = null; + } catch (Return e) { + return e.r; + } + } else if (gheader != null) { + gheader.name = null; + } + this.mode = COMMENT; + case COMMENT: + if ((flags & 0x1000) != 0) { + try { + r = readString(r, f); + if (gheader != null) { + gheader.comment = tmp_string.toByteArray(); + } + tmp_string = null; + } catch (Return e) { + return e.r; + } + } else if (gheader != null) { + gheader.comment = null; + } + this.mode = HCRC; + case HCRC: + if ((flags & 0x0200) != 0) { + try { + r = readBytes(2, r, f); + } catch (Return e) { + return e.r; + } + if (gheader != null) { + gheader.hcrc = (int) (this.need & 0xffff); + } + if (this.need != (z.adler.getValue() & 0xffffL)) { + this.mode = BAD; + z.msg = "header crc mismatch"; + this.marker = 5; // can't try inflateSync + break; + } + } + z.adler = new CRC32(); + + this.mode = BLOCKS; + break; + default: + return Z_STREAM_ERROR; + } + } + } + + int inflateSetDictionary(byte[] dictionary, int dictLength) { + if (z == null || (this.mode != DICT0 && this.wrap != 0)) { + return Z_STREAM_ERROR; + } + + int index = 0; + int length = dictLength; + + if (this.mode == DICT0) { + long adler_need = z.adler.getValue(); + z.adler.reset(); + z.adler.update(dictionary, 0, dictLength); + if (z.adler.getValue() != adler_need) { + return Z_DATA_ERROR; + } + } + + z.adler.reset(); + + if (length >= (1 << this.wbits)) { + length = (1 << this.wbits) - 1; + index = dictLength - length; + } + this.blocks.set_dictionary(dictionary, index, length); + this.mode = BLOCKS; + return Z_OK; + } + + private static byte[] mark = {(byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff}; + + int inflateSync() { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (z == null) + return Z_STREAM_ERROR; + if (this.mode != BAD) { + this.mode = BAD; + this.marker = 0; + } + if ((n = z.avail_in) == 0) + return Z_BUF_ERROR; + + p = z.next_in_index; + m = this.marker; + // search + while (n != 0 && m < 4) { + if (z.next_in[p] == mark[m]) { + m++; + } else if (z.next_in[p] != 0) { + m = 0; + } else { + m = 4 - m; + } + p++; + n--; + } + + // restore + z.total_in += p - z.next_in_index; + z.next_in_index = p; + z.avail_in = n; + this.marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) { + return Z_DATA_ERROR; + } + r = z.total_in; + w = z.total_out; + inflateReset(); + z.total_in = r; + z.total_out = w; + this.mode = BLOCKS; + + return Z_OK; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + // implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH + // but removes the length bytes of the resulting empty stored block. When + // decompressing, PPP checks that at the end of input packet, inflate is + // waiting for these length bytes. + int inflateSyncPoint() { + if (z == null || this.blocks == null) + return Z_STREAM_ERROR; + return this.blocks.sync_point(); + } + + private int readBytes(int n, int r, int f) throws Return { + if (need_bytes == -1) { + need_bytes = n; + this.need = 0; + } + while (need_bytes > 0) { + if (z.avail_in == 0) { + throw new Return(r); + } ; + r = f; + z.avail_in--; + z.total_in++; + this.need = this.need | ((z.next_in[z.next_in_index++] & 0xff) << ((n - need_bytes) * 8)); + need_bytes--; + } + if (n == 2) { + this.need &= 0xffffL; + } else if (n == 4) { + this.need &= 0xffffffffL; + } + need_bytes = -1; + return r; + } + + static class Return extends Exception { + private static final long serialVersionUID = -1L; + int r; + + Return(int r) { + this.r = r; + } + } + + private ByteArrayOutputStream tmp_string = null; + + private int readString(int r, int f) throws Return { + if (tmp_string == null) { + tmp_string = new ByteArrayOutputStream(); + } + int b = 0; + do { + if (z.avail_in == 0) { + throw new Return(r); + } ; + r = f; + z.avail_in--; + z.total_in++; + b = z.next_in[z.next_in_index]; + if (b != 0) + tmp_string.write(z.next_in, z.next_in_index, 1); + z.adler.update(z.next_in, z.next_in_index, 1); + z.next_in_index++; + } while (b != 0); + return r; + } + + private int readBytes(int r, int f) throws Return { + if (tmp_string == null) { + tmp_string = new ByteArrayOutputStream(); + } + int b = 0; + while (this.need > 0) { + if (z.avail_in == 0) { + throw new Return(r); + } ; + r = f; + z.avail_in--; + z.total_in++; + b = z.next_in[z.next_in_index]; + tmp_string.write(z.next_in, z.next_in_index, 1); + z.adler.update(z.next_in, z.next_in_index, 1); + z.next_in_index++; + this.need--; + } + return r; + } + + private void checksum(int n, long v) { + for (int i = 0; i < n; i++) { + crcbuf[i] = (byte) (v & 0xff); + v >>= 8; + } + z.adler.update(crcbuf, 0, n); + } + + GZIPHeader getGZIPHeader() { + return gheader; + } + + boolean inParsingHeader() { + switch (mode) { + case HEAD: + case DICT4: + case DICT3: + case DICT2: + case DICT1: + case FLAGS: + case TIME: + case OS: + case EXLEN: + case EXTRA: + case NAME: + case COMMENT: + case HCRC: + return true; + default: + return false; + } + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Inflater.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Inflater.java new file mode 100644 index 0000000..d4aede8 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Inflater.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class Inflater extends ZStream { + + private static final int MAX_WBITS = 15; // 32K LZ77 window + private static final int DEF_WBITS = MAX_WBITS; + + private static final int Z_NO_FLUSH = 0; + private static final int Z_PARTIAL_FLUSH = 1; + private static final int Z_SYNC_FLUSH = 2; + private static final int Z_FULL_FLUSH = 3; + private static final int Z_FINISH = 4; + + private static final int MAX_MEM_LEVEL = 9; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + private int param_w = -1; + private JZlib.WrapperType param_wrapperType = null; + private boolean param_nowrap = false; + + Inflater() { + super(); + init(); + } + + Inflater(JZlib.WrapperType wrapperType) throws GZIPException { + this(DEF_WBITS, wrapperType); + } + + Inflater(int w, JZlib.WrapperType wrapperType) throws GZIPException { + super(); + param_w = w; + param_wrapperType = wrapperType; + int ret = init(w, wrapperType); + if (ret != Z_OK) + throw new GZIPException(ret + ": " + msg); + } + + Inflater(int w) throws GZIPException { + this(w, false); + } + + Inflater(boolean nowrap) throws GZIPException { + this(DEF_WBITS, nowrap); + } + + Inflater(int w, boolean nowrap) throws GZIPException { + super(); + param_w = w; + param_nowrap = nowrap; + int ret = init(w, nowrap); + if (ret != Z_OK) + throw new GZIPException(ret + ": " + msg); + } + + void reset() { + finished = false; + if (param_wrapperType != null) { + init(param_w, param_wrapperType); + } else { + init(param_w, param_nowrap); + } + } + + private boolean finished = false; + + int init() { + return init(DEF_WBITS); + } + + int init(JZlib.WrapperType wrapperType) { + return init(DEF_WBITS, wrapperType); + } + + int init(int w, JZlib.WrapperType wrapperType) { + boolean nowrap = false; + if (wrapperType == JZlib.W_NONE) { + nowrap = true; + } else if (wrapperType == JZlib.W_GZIP) { + w += 16; + } else if (wrapperType == JZlib.W_ANY) { + w |= Inflate.INFLATE_ANY; + } else if (wrapperType == JZlib.W_ZLIB) { + } + return init(w, nowrap); + } + + int init(boolean nowrap) { + return init(DEF_WBITS, nowrap); + } + + int init(int w) { + return init(w, false); + } + + int init(int w, boolean nowrap) { + finished = false; + istate = new Inflate(this); + return istate.inflateInit(nowrap ? -w : w); + } + + @Override + int inflate(int f) { + if (istate == null) + return Z_STREAM_ERROR; + int ret = istate.inflate(f); + if (ret == Z_STREAM_END) + finished = true; + return ret; + } + + @Override + int end() { + finished = true; + if (istate == null) + return Z_STREAM_ERROR; + int ret = istate.inflateEnd(); + // istate = null; + return ret; + } + + int sync() { + if (istate == null) + return Z_STREAM_ERROR; + return istate.inflateSync(); + } + + int syncPoint() { + if (istate == null) + return Z_STREAM_ERROR; + return istate.inflateSyncPoint(); + } + + int setDictionary(byte[] dictionary, int dictLength) { + if (istate == null) + return Z_STREAM_ERROR; + return istate.inflateSetDictionary(dictionary, dictLength); + } + + @Override + boolean finished() { + return istate.mode == 12 /* DONE */; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InflaterInputStream.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InflaterInputStream.java new file mode 100644 index 0000000..28e9236 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/InflaterInputStream.java @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.jcraft.jsch.jzlib; + +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +final class InflaterInputStream extends FilterInputStream { + protected final Inflater inflater; + protected byte[] buf; + + private boolean closed = false; + + protected boolean eof = false; + + private boolean close_in = true; + + protected static final int DEFAULT_BUFSIZE = 512; + + InflaterInputStream(InputStream in) throws IOException { + this(in, false); + } + + InflaterInputStream(InputStream in, boolean nowrap) throws IOException { + this(in, new Inflater(nowrap)); + myinflater = true; + } + + InflaterInputStream(InputStream in, Inflater inflater) throws IOException { + this(in, inflater, DEFAULT_BUFSIZE); + } + + InflaterInputStream(InputStream in, Inflater inflater, int size) throws IOException { + this(in, inflater, size, true); + } + + InflaterInputStream(InputStream in, Inflater inflater, int size, boolean close_in) + throws IOException { + super(in); + if (in == null || inflater == null) { + throw new NullPointerException(); + } else if (size <= 0) { + throw new IllegalArgumentException("buffer size must be greater than 0"); + } + this.inflater = inflater; + buf = new byte[size]; + this.close_in = close_in; + } + + protected boolean myinflater = false; + + private byte[] byte1 = new byte[1]; + + @Override + public int read() throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + return read(byte1, 0, 1) == -1 ? -1 : byte1[0] & 0xff; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } else if (eof) { + return -1; + } + + int n = 0; + inflater.setOutput(b, off, len); + while (!eof) { + if (inflater.avail_in == 0) + fill(); + int err = inflater.inflate(JZlib.Z_NO_FLUSH); + n += inflater.next_out_index - off; + off = inflater.next_out_index; + switch (err) { + case JZlib.Z_DATA_ERROR: + throw new IOException(inflater.msg); + case JZlib.Z_STREAM_END: + case JZlib.Z_NEED_DICT: + eof = true; + if (err == JZlib.Z_NEED_DICT) + return -1; + break; + default: + } + if (inflater.avail_out == 0) + break; + } + return n; + } + + @Override + public int available() throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + if (eof) { + return 0; + } else { + return 1; + } + } + + private byte[] b = new byte[512]; + + @Override + public long skip(long n) throws IOException { + if (n < 0) { + throw new IllegalArgumentException("negative skip length"); + } + + if (closed) { + throw new IOException("Stream closed"); + } + + int max = (int) Math.min(n, Integer.MAX_VALUE); + int total = 0; + while (total < max) { + int len = max - total; + if (len > b.length) { + len = b.length; + } + len = read(b, 0, len); + if (len == -1) { + eof = true; + break; + } + total += len; + } + return total; + } + + @Override + public void close() throws IOException { + if (!closed) { + if (myinflater) + inflater.end(); + if (close_in) + in.close(); + closed = true; + } + } + + protected void fill() throws IOException { + if (closed) { + throw new IOException("Stream closed"); + } + int len = in.read(buf, 0, buf.length); + if (len == -1) { + if (inflater.istate.wrap == 0 && !inflater.finished()) { + buf[0] = 0; + len = 1; + } else if (inflater.istate.was != -1) { // in reading trailer + throw new IOException("footer is not found"); + } else { + throw new EOFException("Unexpected end of ZLIB input stream"); + } + } + inflater.setInput(buf, 0, len, true); + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public synchronized void mark(int readlimit) {} + + @Override + public synchronized void reset() throws IOException { + throw new IOException("mark/reset not supported"); + } + + long getTotalIn() { + return inflater.getTotalIn(); + } + + long getTotalOut() { + return inflater.getTotalOut(); + } + + byte[] getAvailIn() { + if (inflater.avail_in <= 0) + return null; + byte[] tmp = new byte[inflater.avail_in]; + System.arraycopy(inflater.next_in, inflater.next_in_index, tmp, 0, inflater.avail_in); + return tmp; + } + + void readHeader() throws IOException { + + byte[] empty = "".getBytes(StandardCharsets.UTF_8); + inflater.setInput(empty, 0, 0, false); + inflater.setOutput(empty, 0, 0); + + int err = inflater.inflate(JZlib.Z_NO_FLUSH); + if (!inflater.istate.inParsingHeader()) { + return; + } + + byte[] b1 = new byte[1]; + do { + int i = in.read(b1); + if (i <= 0) + throw new IOException("no input"); + inflater.setInput(b1); + err = inflater.inflate(JZlib.Z_NO_FLUSH); + if (err != 0 /* Z_OK */) + throw new IOException(inflater.msg); + } while (inflater.istate.inParsingHeader()); + } + + Inflater getInflater() { + return inflater; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/JZlib.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/JZlib.java new file mode 100644 index 0000000..c9c60d8 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/JZlib.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class JZlib { + private static final String version = "1.1.3"; + + static String version() { + return version; + } + + static final int MAX_WBITS = 15; // 32K LZ77 window + static final int DEF_WBITS = MAX_WBITS; + + enum WrapperType { + NONE, ZLIB, GZIP, ANY + } + + static final WrapperType W_NONE = WrapperType.NONE; + static final WrapperType W_ZLIB = WrapperType.ZLIB; + static final WrapperType W_GZIP = WrapperType.GZIP; + static final WrapperType W_ANY = WrapperType.ANY; + + // compression levels + static final int Z_NO_COMPRESSION = 0; + static final int Z_BEST_SPEED = 1; + static final int Z_BEST_COMPRESSION = 9; + static final int Z_DEFAULT_COMPRESSION = (-1); + + // compression strategy + static final int Z_FILTERED = 1; + static final int Z_HUFFMAN_ONLY = 2; + static final int Z_DEFAULT_STRATEGY = 0; + + static final int Z_NO_FLUSH = 0; + static final int Z_PARTIAL_FLUSH = 1; + static final int Z_SYNC_FLUSH = 2; + static final int Z_FULL_FLUSH = 3; + static final int Z_FINISH = 4; + + static final int Z_OK = 0; + static final int Z_STREAM_END = 1; + static final int Z_NEED_DICT = 2; + static final int Z_ERRNO = -1; + static final int Z_STREAM_ERROR = -2; + static final int Z_DATA_ERROR = -3; + static final int Z_MEM_ERROR = -4; + static final int Z_BUF_ERROR = -5; + static final int Z_VERSION_ERROR = -6; + + // The three kinds of block type + static final byte Z_BINARY = 0; + static final byte Z_ASCII = 1; + static final byte Z_UNKNOWN = 2; + + static long adler32_combine(long adler1, long adler2, long len2) { + return Adler32.combine(adler1, adler2, len2); + } + + static long crc32_combine(long crc1, long crc2, long len2) { + return CRC32.combine(crc1, crc2, len2); + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/StaticTree.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/StaticTree.java new file mode 100644 index 0000000..12a1f86 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/StaticTree.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class StaticTree { + private static final int MAX_BITS = 15; + + private static final int BL_CODES = 19; + private static final int D_CODES = 30; + private static final int LITERALS = 256; + private static final int LENGTH_CODES = 29; + private static final int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + // Bit length codes must not exceed MAX_BL_BITS bits + static final int MAX_BL_BITS = 7; + + static final short[] static_ltree = {12, 8, 140, 8, 76, 8, 204, 8, 44, 8, 172, 8, 108, 8, 236, 8, + 28, 8, 156, 8, 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, 252, 8, 2, 8, 130, 8, 66, 8, 194, 8, 34, + 8, 162, 8, 98, 8, 226, 8, 18, 8, 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, 114, 8, 242, 8, 10, 8, + 138, 8, 74, 8, 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, + 186, 8, 122, 8, 250, 8, 6, 8, 134, 8, 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, 230, 8, 22, 8, + 150, 8, 86, 8, 214, 8, 54, 8, 182, 8, 118, 8, 246, 8, 14, 8, 142, 8, 78, 8, 206, 8, 46, 8, + 174, 8, 110, 8, 238, 8, 30, 8, 158, 8, 94, 8, 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, 1, 8, + 129, 8, 65, 8, 193, 8, 33, 8, 161, 8, 97, 8, 225, 8, 17, 8, 145, 8, 81, 8, 209, 8, 49, 8, 177, + 8, 113, 8, 241, 8, 9, 8, 137, 8, 73, 8, 201, 8, 41, 8, 169, 8, 105, 8, 233, 8, 25, 8, 153, 8, + 89, 8, 217, 8, 57, 8, 185, 8, 121, 8, 249, 8, 5, 8, 133, 8, 69, 8, 197, 8, 37, 8, 165, 8, 101, + 8, 229, 8, 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, 181, 8, 117, 8, 245, 8, 13, 8, 141, 8, 77, 8, + 205, 8, 45, 8, 173, 8, 109, 8, 237, 8, 29, 8, 157, 8, 93, 8, 221, 8, 61, 8, 189, 8, 125, 8, + 253, 8, 19, 9, 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, 211, 9, 467, 9, 51, 9, 307, 9, 179, 9, + 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, 331, 9, 203, 9, + 459, 9, 43, 9, 299, 9, 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, 491, 9, 27, 9, 283, 9, 155, 9, + 411, 9, 91, 9, 347, 9, 219, 9, 475, 9, 59, 9, 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, 251, 9, + 507, 9, 7, 9, 263, 9, 135, 9, 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, 39, 9, 295, 9, 167, 9, + 423, 9, 103, 9, 359, 9, 231, 9, 487, 9, 23, 9, 279, 9, 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, + 471, 9, 55, 9, 311, 9, 183, 9, 439, 9, 119, 9, 375, 9, 247, 9, 503, 9, 15, 9, 271, 9, 143, 9, + 399, 9, 79, 9, 335, 9, 207, 9, 463, 9, 47, 9, 303, 9, 175, 9, 431, 9, 111, 9, 367, 9, 239, 9, + 495, 9, 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, 351, 9, 223, 9, 479, 9, 63, 9, 319, 9, 191, 9, + 447, 9, 127, 9, 383, 9, 255, 9, 511, 9, 0, 7, 64, 7, 32, 7, 96, 7, 16, 7, 80, 7, 48, 7, 112, + 7, 8, 7, 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, 56, 7, 120, 7, 4, 7, 68, 7, 36, 7, 100, 7, 20, 7, + 84, 7, 52, 7, 116, 7, 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, 163, 8, 99, 8, 227, 8}; + + static final short[] static_dtree = {0, 5, 16, 5, 8, 5, 24, 5, 4, 5, 20, 5, 12, 5, 28, 5, 2, 5, + 18, 5, 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, 30, 5, 1, 5, 17, 5, 9, 5, 25, 5, 5, 5, 21, 5, 13, 5, + 29, 5, 3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5}; + + static StaticTree static_l_desc = + new StaticTree(static_ltree, Tree.extra_lbits, LITERALS + 1, L_CODES, MAX_BITS); + + static StaticTree static_d_desc = + new StaticTree(static_dtree, Tree.extra_dbits, 0, D_CODES, MAX_BITS); + + static StaticTree static_bl_desc = + new StaticTree(null, Tree.extra_blbits, 0, BL_CODES, MAX_BL_BITS); + + short[] static_tree; // static tree or null + int[] extra_bits; // extra bits for each code or null + int extra_base; // base index for extra_bits + int elems; // max number of elements in the tree + int max_length; // max bit length for the codes + + private StaticTree(short[] static_tree, int[] extra_bits, int extra_base, int elems, + int max_length) { + this.static_tree = static_tree; + this.extra_bits = extra_bits; + this.extra_base = extra_base; + this.elems = elems; + this.max_length = max_length; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Tree.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Tree.java new file mode 100644 index 0000000..9668bdf --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/Tree.java @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +final class Tree { + private static final int MAX_BITS = 15; + private static final int BL_CODES = 19; + private static final int D_CODES = 30; + private static final int LITERALS = 256; + private static final int LENGTH_CODES = 29; + private static final int L_CODES = (LITERALS + 1 + LENGTH_CODES); + private static final int HEAP_SIZE = (2 * L_CODES + 1); + + // Bit length codes must not exceed MAX_BL_BITS bits + static final int MAX_BL_BITS = 7; + + // end of block literal code + static final int END_BLOCK = 256; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + static final int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + static final int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + static final int REPZ_11_138 = 18; + + // extra bits for each length code + static final int[] extra_lbits = + {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0}; + + // extra bits for each distance code + static final int[] extra_dbits = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, + 9, 10, 10, 11, 11, 12, 12, 13, 13}; + + // extra bits for each bit length code + static final int[] extra_blbits = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7}; + + static final byte[] bl_order = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + // The lengths of the bit length codes are sent in order of decreasing + // probability, to avoid transmitting the lengths for unused bit + // length codes. + + static final int Buf_size = 8 * 2; + + // see definition of array dist_code below + static final int DIST_CODE_LEN = 512; + + static final byte[] _dist_code = {0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, + 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29}; + + static final byte[] _length_code = {0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, + 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 17, + 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 28}; + + static final int[] base_length = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, + 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0}; + + static final int[] base_dist = {0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, + 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576}; + + // Mapping from a distance to a distance code. dist is the distance - 1 and + // must not have side effects. _dist_code[256] and _dist_code[257] are never + // used. + static int d_code(int dist) { + return ((dist) < 256 ? _dist_code[dist] : _dist_code[256 + ((dist) >>> 7)]); + } + + short[] dyn_tree; // the dynamic tree + int max_code; // largest code with non zero frequency + StaticTree stat_desc; // the corresponding static tree + + // Compute the optimal bit lengths for a tree and update the total bit length + // for the current block. + // IN assertion: the fields freq and dad are set, heap[heap_max] and + // above are the tree nodes sorted by increasing frequency. + // OUT assertions: the field len is set to the optimal bit length, the + // array bl_count contains the frequencies for each bit length. + // The length opt_len is updated; static_len is also updated if stree is + // not null. + void gen_bitlen(Deflate s) { + short[] tree = dyn_tree; + short[] stree = stat_desc.static_tree; + int[] extra = stat_desc.extra_bits; + int base = stat_desc.extra_base; + int max_length = stat_desc.max_length; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= MAX_BITS; bits++) + s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) { + bits = max_length; + overflow++; + } + tree[n * 2 + 1] = (short) bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > max_code) + continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= base) + xbits = extra[n - base]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) + s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) + return; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do { + bits = max_length - 1; + while (s.bl_count[bits] == 0) + bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] += 2; // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) { + n = s.bl_count[bits]; + while (n != 0) { + m = s.heap[--h]; + if (m > max_code) + continue; + if (tree[m * 2 + 1] != bits) { + s.opt_len += (int) (((long) bits - (long) tree[m * 2 + 1]) * (long) tree[m * 2]); + tree[m * 2 + 1] = (short) bits; + } + n--; + } + } + } + + // Construct one Huffman tree and assigns the code bit strings and lengths. + // Update the total bit length for the current block. + // IN assertion: the field freq is set for all tree elements. + // OUT assertions: the fields len and code are set to the optimal bit length + // and corresponding code. The length opt_len is updated; static_len is + // also updated if stree is not null. The field max_code is set. + void build_tree(Deflate s) { + short[] tree = dyn_tree; + short[] stree = stat_desc.static_tree; + int elems = stat_desc.elems; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n * 2] != 0) { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } else { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) { + node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; + if (stree != null) + s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.max_code = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = (short) (tree[n * 2] + tree[m * 2]); + s.depth[node] = (byte) (Math.max(s.depth[n], s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short) node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + gen_bitlen(s); + + // The field len is now set, we can generate the bit codes + gen_codes(tree, max_code, s.bl_count, s.next_code); + } + + // Generate the codes for a given tree and bit counts (which need not be + // optimal). + // IN assertion: the array bl_count contains the bit length statistics for + // the given tree and the field len is set for all tree elements. + // OUT assertion: the field code is set for all tree elements of non + // zero code length. + private static final void gen_codes(short[] tree, // the tree to decorate + int max_code, // largest code with non zero frequency + short[] bl_count, // number of codes at each bit length + short[] next_code) { + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + next_code[0] = 0; + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (short) ((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + // Assert (code + bl_count[MAX_BITS]-1 == (1<>>= 1; + res <<= 1; + } while (--len > 0); + return res >>> 1; + } +} diff --git a/files-jsch/src/main/java/com/jcraft/jsch/jzlib/ZStream.java b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/ZStream.java new file mode 100644 index 0000000..bffc093 --- /dev/null +++ b/files-jsch/src/main/java/com/jcraft/jsch/jzlib/ZStream.java @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2000-2011 ymnk, JCraft,Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. The names of the authors may not be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * This program is based on zlib-1.1.3, so all credit should go authors Jean-loup + * Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) and contributors of zlib. + */ + +package com.jcraft.jsch.jzlib; + +class ZStream { + + private static final int MAX_WBITS = 15; // 32K LZ77 window + private static final int DEF_WBITS = MAX_WBITS; + + private static final int Z_NO_FLUSH = 0; + private static final int Z_PARTIAL_FLUSH = 1; + private static final int Z_SYNC_FLUSH = 2; + private static final int Z_FULL_FLUSH = 3; + private static final int Z_FINISH = 4; + + private static final int MAX_MEM_LEVEL = 9; + + private static final int Z_OK = 0; + private static final int Z_STREAM_END = 1; + private static final int Z_NEED_DICT = 2; + private static final int Z_ERRNO = -1; + private static final int Z_STREAM_ERROR = -2; + private static final int Z_DATA_ERROR = -3; + private static final int Z_MEM_ERROR = -4; + private static final int Z_BUF_ERROR = -5; + private static final int Z_VERSION_ERROR = -6; + + byte[] next_in; // next input byte + int next_in_index; + int avail_in; // number of bytes available at next_in + long total_in; // total nb of input bytes read so far + + byte[] next_out; // next output byte should be put there + int next_out_index; + int avail_out; // remaining free space at next_out + long total_out; // total nb of bytes output so far + + String msg; + + Deflate dstate; + Inflate istate; + + int data_type; // best guess about the data type: ascii or binary + + Checksum adler; + + ZStream() { + this(new Adler32()); + } + + ZStream(Checksum adler) { + this.adler = adler; + } + + int inflateInit() { + return inflateInit(DEF_WBITS); + } + + int inflateInit(boolean nowrap) { + return inflateInit(DEF_WBITS, nowrap); + } + + int inflateInit(int w) { + return inflateInit(w, false); + } + + int inflateInit(JZlib.WrapperType wrapperType) { + return inflateInit(DEF_WBITS, wrapperType); + } + + int inflateInit(int w, JZlib.WrapperType wrapperType) { + boolean nowrap = false; + if (wrapperType == JZlib.W_NONE) { + nowrap = true; + } else if (wrapperType == JZlib.W_GZIP) { + w += 16; + } else if (wrapperType == JZlib.W_ANY) { + w |= Inflate.INFLATE_ANY; + } else if (wrapperType == JZlib.W_ZLIB) { + } + return inflateInit(w, nowrap); + } + + int inflateInit(int w, boolean nowrap) { + istate = new Inflate(this); + return istate.inflateInit(nowrap ? -w : w); + } + + int inflate(int f) { + if (istate == null) + return Z_STREAM_ERROR; + return istate.inflate(f); + } + + int inflateEnd() { + if (istate == null) + return Z_STREAM_ERROR; + int ret = istate.inflateEnd(); + // istate = null; + return ret; + } + + int inflateSync() { + if (istate == null) + return Z_STREAM_ERROR; + return istate.inflateSync(); + } + + int inflateSyncPoint() { + if (istate == null) + return Z_STREAM_ERROR; + return istate.inflateSyncPoint(); + } + + int inflateSetDictionary(byte[] dictionary, int dictLength) { + if (istate == null) + return Z_STREAM_ERROR; + return istate.inflateSetDictionary(dictionary, dictLength); + } + + boolean inflateFinished() { + return istate.mode == 12 /* DONE */; + } + + int deflateInit(int level) { + return deflateInit(level, MAX_WBITS); + } + + int deflateInit(int level, boolean nowrap) { + return deflateInit(level, MAX_WBITS, nowrap); + } + + int deflateInit(int level, int bits) { + return deflateInit(level, bits, false); + } + + int deflateInit(int level, int bits, int memlevel, JZlib.WrapperType wrapperType) { + if (bits < 9 || bits > 15) { + return Z_STREAM_ERROR; + } + if (wrapperType == JZlib.W_NONE) { + bits *= -1; + } else if (wrapperType == JZlib.W_GZIP) { + bits += 16; + } else if (wrapperType == JZlib.W_ANY) { + return Z_STREAM_ERROR; + } else if (wrapperType == JZlib.W_ZLIB) { + } + return this.deflateInit(level, bits, memlevel); + } + + int deflateInit(int level, int bits, int memlevel) { + dstate = new Deflate(this); + return dstate.deflateInit(level, bits, memlevel); + } + + int deflateInit(int level, int bits, boolean nowrap) { + dstate = new Deflate(this); + return dstate.deflateInit(level, nowrap ? -bits : bits); + } + + int deflate(int flush) { + if (dstate == null) { + return Z_STREAM_ERROR; + } + return dstate.deflate(flush); + } + + int deflateEnd() { + if (dstate == null) + return Z_STREAM_ERROR; + int ret = dstate.deflateEnd(); + dstate = null; + return ret; + } + + int deflateParams(int level, int strategy) { + if (dstate == null) + return Z_STREAM_ERROR; + return dstate.deflateParams(level, strategy); + } + + int deflateSetDictionary(byte[] dictionary, int dictLength) { + if (dstate == null) + return Z_STREAM_ERROR; + return dstate.deflateSetDictionary(dictionary, dictLength); + } + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + void flush_pending() { + int len = dstate.pending; + + if (len > avail_out) + len = avail_out; + if (len == 0) + return; + + if (dstate.pending_buf.length <= dstate.pending_out || next_out.length <= next_out_index + || dstate.pending_buf.length < (dstate.pending_out + len) + || next_out.length < (next_out_index + len)) { + // System.out.println(dstate.pending_buf.length+", "+dstate.pending_out+ + // ", "+next_out.length+", "+next_out_index+", "+len); + // System.out.println("avail_out="+avail_out); + } + + System.arraycopy(dstate.pending_buf, dstate.pending_out, next_out, next_out_index, len); + + next_out_index += len; + dstate.pending_out += len; + total_out += len; + avail_out -= len; + dstate.pending -= len; + if (dstate.pending == 0) { + dstate.pending_out = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + int read_buf(byte[] buf, int start, int size) { + int len = avail_in; + + if (len > size) + len = size; + if (len == 0) + return 0; + + avail_in -= len; + + if (dstate.wrap != 0) { + adler.update(next_in, next_in_index, len); + } + System.arraycopy(next_in, next_in_index, buf, start, len); + next_in_index += len; + total_in += len; + return len; + } + + long getAdler() { + return adler.getValue(); + } + + void free() { + next_in = null; + next_out = null; + msg = null; + } + + void setOutput(byte[] buf) { + setOutput(buf, 0, buf.length); + } + + void setOutput(byte[] buf, int off, int len) { + next_out = buf; + next_out_index = off; + avail_out = len; + } + + void setInput(byte[] buf) { + setInput(buf, 0, buf.length, false); + } + + void setInput(byte[] buf, boolean append) { + setInput(buf, 0, buf.length, append); + } + + void setInput(byte[] buf, int off, int len, boolean append) { + if (len <= 0 && append && next_in != null) + return; + + if (avail_in > 0 && append) { + byte[] tmp = new byte[avail_in + len]; + System.arraycopy(next_in, next_in_index, tmp, 0, avail_in); + System.arraycopy(buf, off, tmp, avail_in, len); + next_in = tmp; + next_in_index = 0; + avail_in += len; + } else { + next_in = buf; + next_in_index = off; + avail_in = len; + } + } + + byte[] getNextIn() { + return next_in; + } + + void setNextIn(byte[] next_in) { + this.next_in = next_in; + } + + int getNextInIndex() { + return next_in_index; + } + + void setNextInIndex(int next_in_index) { + this.next_in_index = next_in_index; + } + + int getAvailIn() { + return avail_in; + } + + void setAvailIn(int avail_in) { + this.avail_in = avail_in; + } + + byte[] getNextOut() { + return next_out; + } + + void setNextOut(byte[] next_out) { + this.next_out = next_out; + } + + int getNextOutIndex() { + return next_out_index; + } + + void setNextOutIndex(int next_out_index) { + this.next_out_index = next_out_index; + } + + int getAvailOut() { + return avail_out; + } + + void setAvailOut(int avail_out) { + this.avail_out = avail_out; + } + + long getTotalOut() { + return total_out; + } + + long getTotalIn() { + return total_in; + } + + String getMessage() { + return msg; + } + + // Those methods are expected to be override by Inflater and Deflater. + // In the future, they will become abstract methods. + int end() { + return Z_OK; + } + + boolean finished() { + return false; + } +} diff --git a/files-jsch/src/main/java/module-info.java b/files-jsch/src/main/java/module-info.java new file mode 100644 index 0000000..c9409db --- /dev/null +++ b/files-jsch/src/main/java/module-info.java @@ -0,0 +1,5 @@ +module org.xbib.files.jsch { + exports com.jcraft.jsch; + requires java.logging; + requires java.security.jgss; +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/AbstractBufferMargin.java b/files-jsch/src/test/java/com/jcraft/jsch/test/AbstractBufferMargin.java new file mode 100644 index 0000000..e8321c0 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/AbstractBufferMargin.java @@ -0,0 +1,290 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelExec; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.input.BoundedInputStream; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public abstract class AbstractBufferMargin { + + // Python can be slow for DH group 18 + private static final int timeout = 10000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(AbstractBufferMargin.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd; + + protected AbstractBufferMargin(int maxPktSize) { + sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("asyncsshd.py", "docker/asyncsshd.py") + .withFileFromClasspath("ssh_host_ed448_key", "docker/ssh_host_ed448_key") + .withFileFromClasspath("ssh_host_ed448_key.pub", "docker/ssh_host_ed448_key.pub") + .withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.asyncssh") + .withBuildArg("MAX_PKTSIZE", Integer.toString(maxPktSize))) + .withExposedPorts(22); + } + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + protected void doTestSftp(String cipher, String mac, String compression) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("cipher.s2c", cipher); + session.setConfig("cipher.c2s", cipher); + session.setConfig("mac.s2c", mac); + session.setConfig("mac.c2s", mac); + session.setConfig("compression.s2c", compression); + session.setConfig("compression.c2s", compression); + doSftp(session, true); + } + + protected void doTestScp(String cipher, String mac, String compression) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("cipher.s2c", cipher); + session.setConfig("cipher.c2s", cipher); + session.setConfig("mac.s2c", mac); + session.setConfig("mac.c2s", mac); + session.setConfig("compression.s2c", compression); + session.setConfig("compression.c2s", compression); + doScp(session, true); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void doScp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelExec scp; + + scp = (ChannelExec) session.openChannel("exec"); + try (InputStream is = scp.getInputStream()) { + try (OutputStream os = scp.getOutputStream()) { + scp.setCommand("scp -t /root/test"); + scp.connect(timeout); + checkAck(is); + String cmd = "C0644 102400 test\n"; + os.write(cmd.getBytes(UTF_8)); + os.flush(); + checkAck(is); + Files.copy(in, os); + os.flush(); + sendAck(os); + checkAck(is); + } + } + while (scp.isConnected()) { + Thread.sleep(100L); + } + + scp = (ChannelExec) session.openChannel("exec"); + try (OutputStream os = scp.getOutputStream()) { + try (InputStream is = scp.getInputStream()) { + scp.setCommand("scp -f /root/test"); + scp.connect(timeout); + sendAck(os); + int c = checkAck(is); + if (c == 'C') { + byte[] buf = new byte[17]; + is.read(buf, 0, 17); + sendAck(os); + Files.copy( + BoundedInputStream.builder().setMaxCount(100L * 1024L).setInputStream(is).get(), + out); + checkAck(is); + sendAck(os); + } + } + } + while (scp.isConnected()) { + Thread.sleep(100L); + } + + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private static int checkAck(InputStream is) throws IOException { + int b = is.read(); + if (b == 0) { + return b; + } else if (b == -1) { + throw new IOException("no response"); + } + + StringBuilder sb = new StringBuilder(); + if (b == 1 || b == 2) { + int c = is.read(); + while (c > 0 && c != '\n') { + sb.append((char) c); + c = is.read(); + } + } + + switch (b) { + case 1: + throw new IOException("error: " + sb); + case 2: + throw new IOException("fatal error: " + sb); + default: + return b; + } + } + + private static void sendAck(OutputStream os) throws IOException { + byte[] ack = new byte[1]; + ack[0] = 0; + os.write(ack); + os.flush(); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/Algorithms2IT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/Algorithms2IT.java new file mode 100644 index 0000000..7a623cf --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/Algorithms2IT.java @@ -0,0 +1,397 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.condition.JRE.JAVA_11; +import static org.junit.jupiter.api.condition.JRE.JAVA_15; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class Algorithms2IT { + + // Python can be slow for DH group 18 + private static final int timeout = 10000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = TestLoggerFactory.getTestLogger(Algorithms2IT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("asyncsshd.py", "docker/asyncsshd.py") + .withFileFromClasspath("ssh_host_ed448_key", "docker/ssh_host_ed448_key") + .withFileFromClasspath("ssh_host_ed448_key.pub", "docker/ssh_host_ed448_key.pub") + .withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.asyncssh")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + @EnabledForJreRange(min = JAVA_11) + public void testJava11KEXs() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("xdh", "com.jcraft.jsch.jce.XDH"); + session.setConfig("kex", "curve448-sha512"); + doSftp(session, true); + + String expected = "kex: algorithm: curve448-sha512.*"; + checkLogs(expected); + } + + @Test + public void testBCKEXs() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("xdh", "com.jcraft.jsch.bc.XDH"); + session.setConfig("kex", "curve448-sha512"); + doSftp(session, true); + + String expected = "kex: algorithm: curve448-sha512.*"; + checkLogs(expected); + } + + @ParameterizedTest + @ValueSource(strings = {"curve448-sha512", "diffie-hellman-group17-sha512", + "diffie-hellman-group15-sha512", "diffie-hellman-group18-sha512@ssh.com", + "diffie-hellman-group16-sha512@ssh.com", "diffie-hellman-group16-sha384@ssh.com", + "diffie-hellman-group15-sha384@ssh.com", "diffie-hellman-group15-sha256@ssh.com", + "diffie-hellman-group14-sha256@ssh.com", "diffie-hellman-group14-sha224@ssh.com", + "diffie-hellman-group-exchange-sha512@ssh.com", + "diffie-hellman-group-exchange-sha384@ssh.com", + "diffie-hellman-group-exchange-sha224@ssh.com"}) + public void testKEXs(String kex) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("kex", kex); + doSftp(session, true); + + String expected = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); + checkLogs(expected); + } + + @ParameterizedTest + @CsvSource(value = {"diffie-hellman-group-exchange-sha256,1024", + "diffie-hellman-group-exchange-sha1,1024", + "diffie-hellman-group-exchange-sha512@ssh.com,1024", + "diffie-hellman-group-exchange-sha512@ssh.com,2048", + "diffie-hellman-group-exchange-sha512@ssh.com,4096", + "diffie-hellman-group-exchange-sha512@ssh.com,6144", + "diffie-hellman-group-exchange-sha512@ssh.com,8192", + "diffie-hellman-group-exchange-sha384@ssh.com,1024", + "diffie-hellman-group-exchange-sha384@ssh.com,2048", + "diffie-hellman-group-exchange-sha384@ssh.com,4096", + "diffie-hellman-group-exchange-sha384@ssh.com,6144", + "diffie-hellman-group-exchange-sha384@ssh.com,8192", + "diffie-hellman-group-exchange-sha224@ssh.com,1024", + "diffie-hellman-group-exchange-sha224@ssh.com,2048", + "diffie-hellman-group-exchange-sha224@ssh.com,4096", + "diffie-hellman-group-exchange-sha224@ssh.com,6144", + "diffie-hellman-group-exchange-sha224@ssh.com,8192"}) + public void testDHGEXSizes(String kex, String size) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("kex", kex); + session.setConfig("dhgex_min", size); + session.setConfig("dhgex_max", size); + session.setConfig("dhgex_preferred", size); + doSftp(session, true); + + String expectedKex = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); + String expectedSizes = String.format(Locale.ROOT, + "SSH_MSG_KEX_DH_GEX_REQUEST\\(%s<%s<%s\\) sent", size, size, size); + checkLogs(expectedKex); + checkLogs(expectedSizes); + } + + @Test + @EnabledForJreRange(min = JAVA_15) + public void testJava15Ed448() throws Exception { + JSch ssh = createEd448Identity(); + Session session = createSession(ssh); + session.setConfig("keypairgen.eddsa", "com.jcraft.jsch.jce.KeyPairGenEdDSA"); + session.setConfig("ssh-ed448", "com.jcraft.jsch.jce.SignatureEd448"); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-ed448"); + session.setConfig("server_host_key", "ssh-ed448"); + doSftp(session, true); + + String expected = "kex: host key algorithm: ssh-ed448.*"; + checkLogs(expected); + } + + @Test + public void testBCEd448() throws Exception { + JSch ssh = createEd448Identity(); + Session session = createSession(ssh); + session.setConfig("keypairgen.eddsa", "com.jcraft.jsch.bc.KeyPairGenEdDSA"); + session.setConfig("ssh-ed448", "com.jcraft.jsch.bc.SignatureEd448"); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-ed448"); + session.setConfig("server_host_key", "ssh-ed448"); + doSftp(session, true); + + String expected = "kex: host key algorithm: ssh-ed448.*"; + checkLogs(expected); + } + + @Test + public void testEd448() throws Exception { + JSch ssh = createEd448Identity(); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-ed448"); + session.setConfig("server_host_key", "ssh-ed448"); + doSftp(session, true); + + String expected = "kex: host key algorithm: ssh-ed448.*"; + checkLogs(expected); + } + + @ParameterizedTest + @ValueSource(strings = {"ssh-rsa-sha512@ssh.com", "ssh-rsa-sha384@ssh.com", + "ssh-rsa-sha256@ssh.com", "ssh-rsa-sha224@ssh.com"}) + public void testRSA(String keyType) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", keyType); + session.setConfig("server_host_key", keyType); + doSftp(session, true); + + String expected = String.format(Locale.ROOT, "kex: host key algorithm: %s.*", keyType); + checkLogs(expected); + } + + @ParameterizedTest + @CsvSource(value = {"seed-cbc@ssh.com,none", "seed-cbc@ssh.com,zlib@openssh.com"}) + public void testCiphers(String cipher, String compression) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("cipher.s2c", cipher); + session.setConfig("cipher.c2s", cipher); + session.setConfig("compression.s2c", compression); + session.setConfig("compression.c2s", compression); + doSftp(session, true); + + String expectedS2C = String.format(Locale.ROOT, "kex: server->client cipher: %s.*", cipher); + String expectedC2S = String.format(Locale.ROOT, "kex: client->server cipher: %s.*", cipher); + checkLogs(expectedS2C); + checkLogs(expectedC2S); + } + + @ParameterizedTest + @CsvSource(value = {"hmac-sha512@ssh.com,none", "hmac-sha512@ssh.com,zlib@openssh.com", + "hmac-sha384@ssh.com,none", "hmac-sha384@ssh.com,zlib@openssh.com", + "hmac-sha256-2@ssh.com,none", "hmac-sha256-2@ssh.com,zlib@openssh.com", + "hmac-sha256@ssh.com,none", "hmac-sha256@ssh.com,zlib@openssh.com", + "hmac-sha224@ssh.com,none", "hmac-sha224@ssh.com,zlib@openssh.com"}) + public void testMACs(String mac, String compression) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("mac.s2c", mac); + session.setConfig("mac.c2s", mac); + session.setConfig("compression.s2c", compression); + session.setConfig("compression.c2s", compression); + // Make sure a non-AEAD cipher is used + session.setConfig("cipher.s2c", "aes128-ctr"); + session.setConfig("cipher.c2s", "aes128-ctr"); + doSftp(session, true); + + String expectedS2C = String.format(Locale.ROOT, "kex: server->client .* MAC: %s.*", mac); + String expectedC2S = String.format(Locale.ROOT, "kex: client->server .* MAC: %s.*", mac); + checkLogs(expectedS2C); + checkLogs(expectedC2S); + } + + @Test + public void testCompressions() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("compression.s2c", "zlib"); + session.setConfig("compression.c2s", "zlib"); + doSftp(session, true); + + String expectedS2C = "kex: server->client .* compression: zlib.*"; + String expectedC2S = "kex: client->server .* compression: zlib.*"; + checkLogs(expectedS2C); + checkLogs(expectedC2S); + } + + @ParameterizedTest + @ValueSource(strings = {"com.jcraft.jsch.juz.Compression", "com.jcraft.jsch.jzlib.Compression"}) + public void testCompressionImpls(String impl) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("compression.s2c", "zlib"); + session.setConfig("compression.c2s", "zlib"); + session.setConfig("zlib", impl); + doSftp(session, true); + + String expectedImpl = String.format(Locale.ROOT, "zlib using %s", impl); + String expectedS2C = "kex: server->client .* compression: zlib.*"; + String expectedC2S = "kex: client->server .* compression: zlib.*"; + checkLogs(expectedImpl); + checkLogs(expectedS2C); + checkLogs(expectedC2S); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createEd448Identity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_ed448_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_ed448"), getResourceFile("docker/id_ed448.pub"), + null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/Algorithms3IT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/Algorithms3IT.java new file mode 100644 index 0000000..bc8bed5 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/Algorithms3IT.java @@ -0,0 +1,183 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class Algorithms3IT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = TestLoggerFactory.getTestLogger(Algorithms3IT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>(new ImageFromDockerfile() + .withFileFromClasspath("dropbear_rsa_host_key", "docker/dropbear_rsa_host_key") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.dropbear")).withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @ParameterizedTest + @CsvSource(value = {"3des-ctr,none", "3des-ctr,zlib@openssh.com"}) + public void testCiphers(String cipher, String compression) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("cipher.s2c", cipher); + session.setConfig("cipher.c2s", cipher); + session.setConfig("compression.s2c", compression); + session.setConfig("compression.c2s", compression); + doSftp(session, true); + + String expectedS2C = String.format(Locale.ROOT, "kex: server->client cipher: %s.*", cipher); + String expectedC2S = String.format(Locale.ROOT, "kex: client->server cipher: %s.*", cipher); + checkLogs(expectedS2C); + checkLogs(expectedC2S); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + // Dropbear does not support rsa-sha2-512/rsa-sha2-256, so add ssh-rsa + String serverHostKey = session.getConfig("server_host_key") + ",ssh-rsa"; + String pubkeyAcceptedAlgorithms = session.getConfig("PubkeyAcceptedAlgorithms") + ",ssh-rsa"; + session.setConfig("server_host_key", serverHostKey); + session.setConfig("PubkeyAcceptedAlgorithms", pubkeyAcceptedAlgorithms); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/Algorithms4IT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/Algorithms4IT.java new file mode 100644 index 0000000..e2eaf71 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/Algorithms4IT.java @@ -0,0 +1,194 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class Algorithms4IT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = TestLoggerFactory.getTestLogger(Algorithms4IT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config.openssh96") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.openssh96")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @ParameterizedTest + @ValueSource(strings = {"sntrup761x25519-sha512@openssh.com"}) + public void testBCKEXs(String kex) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("kex", kex); + doSftp(session, true); + + String expected = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); + checkLogs(expected); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + // Skip OpenSSH log checks, as log output from Docker falls behind and these assertions + // frequently run before they are output + // Optional actualSshd = + // sshdLogger.getAllLoggingEvents().stream() + // .map(LoggingEvent::getFormattedMessage) + // .filter(msg -> msg.matches("STDERR: debug1: " + expected)) + // .findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + // assertTrue(actualSshd.isPresent(), () -> "sshd: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/AlgorithmsIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/AlgorithmsIT.java new file mode 100644 index 0000000..a2facc1 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/AlgorithmsIT.java @@ -0,0 +1,536 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.stream.Collectors.toList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.condition.JRE.JAVA_11; +import static org.junit.jupiter.api.condition.JRE.JAVA_15; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class AlgorithmsIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = TestLoggerFactory.getTestLogger(AlgorithmsIT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @ParameterizedTest + @ValueSource(strings = {"curve25519-sha256", "curve25519-sha256@libssh.org"}) + @EnabledForJreRange(min = JAVA_11) + public void testJava11KEXs(String kex) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("xdh", "com.jcraft.jsch.jce.XDH"); + session.setConfig("kex", kex); + doSftp(session, true); + + String expected = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); + checkLogs(expected); + } + + @ParameterizedTest + @ValueSource(strings = {"curve25519-sha256", "curve25519-sha256@libssh.org"}) + public void testBCKEXs(String kex) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("xdh", "com.jcraft.jsch.bc.XDH"); + session.setConfig("kex", kex); + doSftp(session, true); + + String expected = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); + checkLogs(expected); + } + + @ParameterizedTest + @ValueSource(strings = {"curve25519-sha256", "curve25519-sha256@libssh.org", "ecdh-sha2-nistp521", + "ecdh-sha2-nistp384", "ecdh-sha2-nistp256", "diffie-hellman-group18-sha512", + "diffie-hellman-group16-sha512", "diffie-hellman-group14-sha256", + "diffie-hellman-group-exchange-sha256", "diffie-hellman-group-exchange-sha1", + "diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1"}) + public void testKEXs(String kex) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("kex", kex); + doSftp(session, true); + + String expected = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); + checkLogs(expected); + } + + @ParameterizedTest + @CsvSource(value = {"diffie-hellman-group-exchange-sha256,2048", + "diffie-hellman-group-exchange-sha256,3072", "diffie-hellman-group-exchange-sha256,4096", + "diffie-hellman-group-exchange-sha256,6144", "diffie-hellman-group-exchange-sha256,8192", + "diffie-hellman-group-exchange-sha1,2048", "diffie-hellman-group-exchange-sha1,3072", + "diffie-hellman-group-exchange-sha1,4096", "diffie-hellman-group-exchange-sha1,6144", + "diffie-hellman-group-exchange-sha1,8192"}) + public void testDHGEXSizes(String kex, String size) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("kex", kex); + session.setConfig("dhgex_min", size); + session.setConfig("dhgex_max", size); + session.setConfig("dhgex_preferred", size); + doSftp(session, true); + + String expectedKex = String.format(Locale.ROOT, "kex: algorithm: %s.*", kex); + String expectedSizes = String.format(Locale.ROOT, + "SSH_MSG_KEX_DH_GEX_REQUEST\\(%s<%s<%s\\) sent", size, size, size); + checkLogs(expectedKex); + checkLogs(expectedSizes); + } + + @Test + @EnabledForJreRange(min = JAVA_15) + public void testJava15Ed25519() throws Exception { + JSch ssh = createEd25519Identity(); + Session session = createSession(ssh); + session.setConfig("keypairgen.eddsa", "com.jcraft.jsch.jce.KeyPairGenEdDSA"); + session.setConfig("ssh-ed25519", "com.jcraft.jsch.jce.SignatureEd25519"); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-ed25519"); + session.setConfig("server_host_key", "ssh-ed25519"); + doSftp(session, true); + + String expected = "kex: host key algorithm: ssh-ed25519.*"; + checkLogs(expected); + } + + @Test + public void testBCEd25519() throws Exception { + JSch ssh = createEd25519Identity(); + Session session = createSession(ssh); + session.setConfig("keypairgen.eddsa", "com.jcraft.jsch.bc.KeyPairGenEdDSA"); + session.setConfig("ssh-ed25519", "com.jcraft.jsch.bc.SignatureEd25519"); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-ed25519"); + session.setConfig("server_host_key", "ssh-ed25519"); + doSftp(session, true); + + String expected = "kex: host key algorithm: ssh-ed25519.*"; + checkLogs(expected); + } + + @Test + public void testEd25519() throws Exception { + JSch ssh = createEd25519Identity(); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-ed25519"); + session.setConfig("server_host_key", "ssh-ed25519"); + doSftp(session, true); + + String expected = "kex: host key algorithm: ssh-ed25519.*"; + checkLogs(expected); + } + + @Test + public void testECDSA521() throws Exception { + JSch ssh = createECDSA521Identity(); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ecdsa-sha2-nistp521"); + session.setConfig("server_host_key", "ecdsa-sha2-nistp521"); + doSftp(session, true); + + String expected = "kex: host key algorithm: ecdsa-sha2-nistp521.*"; + checkLogs(expected); + } + + @Test + public void testECDSA384() throws Exception { + JSch ssh = createECDSA384Identity(); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ecdsa-sha2-nistp384"); + session.setConfig("server_host_key", "ecdsa-sha2-nistp384"); + doSftp(session, true); + + String expected = "kex: host key algorithm: ecdsa-sha2-nistp384.*"; + checkLogs(expected); + } + + @Test + public void testECDSA256() throws Exception { + JSch ssh = createECDSA256Identity(); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ecdsa-sha2-nistp256"); + session.setConfig("server_host_key", "ecdsa-sha2-nistp256"); + doSftp(session, true); + + String expected = "kex: host key algorithm: ecdsa-sha2-nistp256.*"; + checkLogs(expected); + } + + @ParameterizedTest + @ValueSource(strings = {"rsa-sha2-512", "rsa-sha2-256", "ssh-rsa"}) + public void testRSA(String keyType) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", keyType); + session.setConfig("server_host_key", keyType); + doSftp(session, true); + + String expected = String.format(Locale.ROOT, "kex: host key algorithm: %s.*", keyType); + checkLogs(expected); + } + + @Test + public void testDSA() throws Exception { + JSch ssh = createDSAIdentity(); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-dss"); + session.setConfig("server_host_key", "ssh-dss"); + doSftp(session, true); + + String expected = "kex: host key algorithm: ssh-dss.*"; + checkLogs(expected); + } + + @ParameterizedTest + @CsvSource(value = {"chacha20-poly1305@openssh.com,none", + "chacha20-poly1305@openssh.com,zlib@openssh.com", "aes256-gcm@openssh.com,none", + "aes256-gcm@openssh.com,zlib@openssh.com", "aes128-gcm@openssh.com,none", + "aes128-gcm@openssh.com,zlib@openssh.com", "aes256-ctr,none", "aes256-ctr,zlib@openssh.com", + "aes192-ctr,none", "aes192-ctr,zlib@openssh.com", "aes128-ctr,none", + "aes128-ctr,zlib@openssh.com", "aes256-cbc,none", "aes256-cbc,zlib@openssh.com", + "aes192-cbc,none", "aes192-cbc,zlib@openssh.com", "aes128-cbc,none", + "aes128-cbc,zlib@openssh.com", "3des-cbc,none", "3des-cbc,zlib@openssh.com", + "blowfish-cbc,none", "blowfish-cbc,zlib@openssh.com", "arcfour,none", + "arcfour,zlib@openssh.com", "arcfour256,none", "arcfour256,zlib@openssh.com", + "arcfour128,none", "arcfour128,zlib@openssh.com", "rijndael-cbc@lysator.liu.se,none", + "rijndael-cbc@lysator.liu.se,zlib@openssh.com", "cast128-cbc,none", + "cast128-cbc,zlib@openssh.com"}) + public void testCiphers(String cipher, String compression) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("cipher.s2c", cipher); + session.setConfig("cipher.c2s", cipher); + session.setConfig("compression.s2c", compression); + session.setConfig("compression.c2s", compression); + doSftp(session, true); + + String expectedS2C = String.format(Locale.ROOT, "kex: server->client cipher: %s.*", cipher); + String expectedC2S = String.format(Locale.ROOT, "kex: client->server cipher: %s.*", cipher); + checkLogs(expectedS2C); + checkLogs(expectedC2S); + } + + @ParameterizedTest + @CsvSource(value = {"hmac-sha2-512-etm@openssh.com,none", + "hmac-sha2-512-etm@openssh.com,zlib@openssh.com", "hmac-sha2-256-etm@openssh.com,none", + "hmac-sha2-256-etm@openssh.com,zlib@openssh.com", "hmac-sha1-etm@openssh.com,none", + "hmac-sha1-etm@openssh.com,zlib@openssh.com", "hmac-sha1-96-etm@openssh.com,none", + "hmac-sha1-96-etm@openssh.com,zlib@openssh.com", "hmac-md5-etm@openssh.com,none", + "hmac-md5-etm@openssh.com,zlib@openssh.com", "hmac-md5-96-etm@openssh.com,none", + "hmac-md5-96-etm@openssh.com,zlib@openssh.com", "hmac-sha2-512,none", + "hmac-sha2-512,zlib@openssh.com", "hmac-sha2-256,none", "hmac-sha2-256,zlib@openssh.com", + "hmac-sha1,none", "hmac-sha1,zlib@openssh.com", "hmac-sha1-96,none", + "hmac-sha1-96,zlib@openssh.com", "hmac-md5,none", "hmac-md5,zlib@openssh.com", + "hmac-md5-96,none", "hmac-md5-96,zlib@openssh.com", "hmac-ripemd160,none", + "hmac-ripemd160,zlib@openssh.com", "hmac-ripemd160@openssh.com,none", + "hmac-ripemd160@openssh.com,zlib@openssh.com", "hmac-ripemd160-etm@openssh.com,none", + "hmac-ripemd160-etm@openssh.com,zlib@openssh.com"}) + public void testMACs(String mac, String compression) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("mac.s2c", mac); + session.setConfig("mac.c2s", mac); + session.setConfig("compression.s2c", compression); + session.setConfig("compression.c2s", compression); + // Make sure a non-AEAD cipher is used + session.setConfig("cipher.s2c", "aes128-ctr"); + session.setConfig("cipher.c2s", "aes128-ctr"); + doSftp(session, true); + + String expectedS2C = String.format(Locale.ROOT, "kex: server->client .* MAC: %s.*", mac); + String expectedC2S = String.format(Locale.ROOT, "kex: client->server .* MAC: %s.*", mac); + checkLogs(expectedS2C); + checkLogs(expectedC2S); + } + + @ParameterizedTest + @ValueSource(strings = {"zlib@openssh.com", "none"}) + public void testCompressions(String compression) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("compression.s2c", compression); + session.setConfig("compression.c2s", compression); + doSftp(session, true); + + String expectedS2C = + String.format(Locale.ROOT, "kex: server->client .* compression: %s.*", compression); + String expectedC2S = + String.format(Locale.ROOT, "kex: client->server .* compression: %s.*", compression); + checkLogs(expectedS2C); + checkLogs(expectedC2S); + } + + @ParameterizedTest + @ValueSource(strings = {"com.jcraft.jsch.juz.Compression", "com.jcraft.jsch.jzlib.Compression"}) + public void testCompressionImpls(String impl) throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("compression.s2c", "zlib@openssh.com"); + session.setConfig("compression.c2s", "zlib@openssh.com"); + session.setConfig("zlib@openssh.com", impl); + doSftp(session, true); + + String expectedImpl = String.format(Locale.ROOT, "zlib using %s", impl); + String expectedS2C = "kex: server->client .* compression: zlib@openssh\\.com.*"; + String expectedC2S = "kex: client->server .* compression: zlib@openssh\\.com.*"; + checkLogs(expectedImpl); + checkLogs(expectedS2C); + checkLogs(expectedC2S); + } + + @ParameterizedTest + @ValueSource(strings = { + "SHA512:EyyvMhUehzuELz3ySpqMw2UggtNqVmWnTSrQy2x4FLT7aF1lmqKC30oF+VUOLhvTmFHYaDLLN9UnpuGphIltKQ", + "SHA384:CMxHNJ/xzOfsmNqw4g6Be+ltVZX3ixtplON7nOspNlji0iMnWzM7X4SelzcpP7Ap", + "SHA256:iqNO6JDjrpga8TvgBKGReaKEnGoF/1csoxWp/DV5xJ0", + "SHA224:mJNHjKtQuiRHioFZIGj1g/+fcKMOsKmzcokU2w", "SHA1:FO2EB514+YMk4jTFmNGOwscY2Pk", + "MD5:3b:50:5b:c5:53:66:8c:2c:98:9b:ee:3f:19:0a:ff:29"}) + public void testFingerprintHashes(String fingerprint) throws Exception { + String[] split = fingerprint.split(":"); + String hash = split[0]; + MockUserInfo userInfo = new MockUserInfo(); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-rsa"); + session.setConfig("server_host_key", "ssh-rsa"); + session.setConfig("StrictHostKeyChecking", "ask"); + session.setConfig("FingerprintHash", hash); + session.setUserInfo(userInfo); + try { + doSftp(session, false); + } catch (JSchException expected) { + } + + String expected = String.format(Locale.ROOT, "RSA key fingerprint is %s.", fingerprint); + List msgs = userInfo.getMessages().stream().map(msg -> msg.split("\n")) + .flatMap(Arrays::stream).collect(toList()); + Optional actual = msgs.stream().filter(msg -> msg.equals(expected)).findFirst(); + + if (!actual.isPresent()) { + msgs.forEach(System.out::println); + printInfo(); + } + + assertTrue(actual.isPresent()); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createECDSA256Identity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_ecdsa256_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_ecdsa256"), + getResourceFile("docker/id_ecdsa256.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createECDSA384Identity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_ecdsa384_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_ecdsa384"), + getResourceFile("docker/id_ecdsa384.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createECDSA521Identity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_ecdsa521_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_ecdsa521"), + getResourceFile("docker/id_ecdsa521.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createDSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_dsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_dsa"), getResourceFile("docker/id_dsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createEd25519Identity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_ed25519_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_ed25519"), getResourceFile("docker/id_ed25519.pub"), + null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + // Skip OpenSSH log checks, as log output from Docker falls behind and these assertions + // frequently run before they are output + // Optional actualSshd = + // sshdLogger.getAllLoggingEvents().stream() + // .map(LoggingEvent::getFormattedMessage) + // .filter(msg -> msg.matches("STDERR: debug1: " + expected)) + // .findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + // assertTrue(actualSshd.isPresent(), () -> "sshd: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/ExtInfoInAuthIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/ExtInfoInAuthIT.java new file mode 100644 index 0000000..cda8a42 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/ExtInfoInAuthIT.java @@ -0,0 +1,240 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class ExtInfoInAuthIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(ServerSigAlgsIT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config.ExtInfoInAuthIT") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.ExtInfoInAuthIT")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + public void testExtInfoInAuthYes() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh, "rsa"); + session.setConfig("enable_ext_info_in_auth", "yes"); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-rsa"); + doSftp(session, "rsa", true); + + String expectedServerKex = "server proposal: KEX algorithms: .*,ext-info-s,.*"; + String expectedClientKex = "client proposal: KEX algorithms: .*,ext-info-c,.*"; + String expected1 = "ext-info messaging supported by server"; + String expected2 = "SSH_MSG_EXT_INFO sent"; + String expectedServerSigAlgs1 = "server-sig-algs="; + String expectedServerSigAlgs2 = "server-sig-algs=<.*ssh-rsa.*>"; + String expectedServerSigAlgs3 = "server-sig-algs=<.*ecdsa.*>"; + checkLogs(expectedServerKex); + checkLogs(expectedClientKex); + checkLogs(expected1); + checkLogs(expected2); + checkLogs(expectedServerSigAlgs1); + checkLogs(expectedServerSigAlgs2); + checkNoLogs(expectedServerSigAlgs3); + } + + @Test + public void testExtInfoInAuthNo() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh, "ecdsa"); + session.setConfig("enable_ext_info_in_auth", "no"); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-rsa"); + session.setTimeout(timeout); + + Assertions.assertThrows(JSchException.class, session::connect, "Auth fail for methods 'publickey'"); + + String expectedServerKex = "server proposal: KEX algorithms: .*,ext-info-s,.*"; + String expectedClientKex = "client proposal: KEX algorithms: .*,ext-info-c,.*"; + String expected1 = "ext-info messaging supported by server"; + String expected2 = "SSH_MSG_EXT_INFO sent"; + String expectedServerSigAlgs1 = "server-sig-algs="; + String expectedServerSigAlgs2 = "server-sig-algs=<.*ssh-rsa.*>"; + String expectedServerSigAlgs3 = "server-sig-algs=<.*ecdsa.*>"; + checkLogs(expectedServerKex); + checkLogs(expectedClientKex); + checkLogs(expected1); + checkNoLogs(expected2); + checkLogs(expectedServerSigAlgs1); + checkNoLogs(expectedServerSigAlgs2); + checkNoLogs(expectedServerSigAlgs3); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh, String username) throws Exception { + Session session = ssh.getSession(username, sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, String username, boolean debugException) throws Exception { + String testFile = String.format(Locale.ROOT, "/%s/test", username); + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), testFile); + sftp.get(testFile, out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private void checkNoLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertFalse(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/JSchAlgoNegoFailExceptionIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/JSchAlgoNegoFailExceptionIT.java new file mode 100644 index 0000000..7a95801 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/JSchAlgoNegoFailExceptionIT.java @@ -0,0 +1,106 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchAlgoNegoFailException; +import com.jcraft.jsch.Session; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class JSchAlgoNegoFailExceptionIT { + + private static final int timeout = 2000; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile")) + .withExposedPorts(22); + + @ParameterizedTest + @CsvSource(delimiter = '|', value = { + "kex|curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group14-sha256,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1", + "server_host_key|ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss", + "cipher.c2s|chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc,blowfish-cbc,arcfour,arcfour256,arcfour128,rijndael-cbc@lysator.liu.se,cast128-cbc", + "cipher.s2c|chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc,blowfish-cbc,arcfour,arcfour256,arcfour128,rijndael-cbc@lysator.liu.se,cast128-cbc", + "mac.c2s|hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-96-etm@openssh.com,hmac-sha1-96,hmac-md5-etm@openssh.com,hmac-md5,hmac-md5-96-etm@openssh.com,hmac-md5-96,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-ripemd160-etm@openssh.com", + "mac.s2c|hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-96-etm@openssh.com,hmac-sha1-96,hmac-md5-etm@openssh.com,hmac-md5,hmac-md5-96-etm@openssh.com,hmac-md5-96,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-ripemd160-etm@openssh.com", + "compression.c2s|none,zlib@openssh.com", "compression.s2c|none,zlib@openssh.com", + "lang.c2s|''", "lang.s2c|''"}) + public void testJSchAlgoNegoFailException(String algorithmName, String serverProposal) + throws Exception { + String jschProposal = "foo"; + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig(algorithmName, jschProposal); + session.setTimeout(timeout); + + JSchAlgoNegoFailException e = assertThrows(JSchAlgoNegoFailException.class, session::connect); + + if (algorithmName.equals("kex")) { + jschProposal += ",ext-info-c,kex-strict-c-v00@openssh.com"; + } + String message = String.format(Locale.ROOT, + "Algorithm negotiation fail: algorithmName=\"%s\" jschProposal=\"%s\" serverProposal=\"%s\"", + algorithmName, jschProposal, serverProposal); + + assertEquals(message, e.getMessage()); + assertEquals(algorithmName, e.getAlgorithmName()); + assertEquals(jschProposal, e.getJSchProposal()); + assertEquals(serverProposal, e.getServerProposal()); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/JSchStrictKexExceptionIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/JSchStrictKexExceptionIT.java new file mode 100644 index 0000000..282f81a --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/JSchStrictKexExceptionIT.java @@ -0,0 +1,96 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchStrictKexException; +import com.jcraft.jsch.Session; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class JSchStrictKexExceptionIT { + + private static final int timeout = 2000; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile")) + .withExposedPorts(22); + + @Test + public void testEnableStrictKexRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "yes"); + session.setConfig("require_strict_kex", "yes"); + session.setTimeout(timeout); + + Assertions.assertThrows(JSchStrictKexException.class, session::connect, + "Strict KEX not supported by server"); + } + + @Test + public void testNoEnableStrictKexRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "no"); + session.setConfig("require_strict_kex", "yes"); + session.setTimeout(timeout); + + assertThrows(JSchStrictKexException.class, session::connect, + "Strict KEX not supported by server"); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/JSchTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/JSchTest.java new file mode 100644 index 0000000..155738a --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/JSchTest.java @@ -0,0 +1,81 @@ +package com.jcraft.jsch.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Logger; +import java.util.Hashtable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class JSchTest { + + @BeforeEach + void resetJsch() { + JSch.setLogger(null); + } + + @Test + void getPubkeyAcceptedKeyTypes() throws JSchException { + JSch.setConfig("PubkeyAcceptedAlgorithms", "JSchTest111"); + assertEquals("JSchTest111", JSch.getConfig("PubkeyAcceptedKeyTypes")); + assertEquals("JSchTest111", JSch.getConfig("PubkeyAcceptedAlgorithms")); + } + + @Test + void setPubkeyAcceptedKeyTypes() throws JSchException { + JSch.setConfig("PubkeyAcceptedKeyTypes", "JSchTest222"); + assertEquals("JSchTest222", JSch.getConfig("PubkeyAcceptedKeyTypes")); + assertEquals("JSchTest222", JSch.getConfig("PubkeyAcceptedAlgorithms")); + } + + @Test + void setPubkeyAcceptedKeyTypesHashtable() throws JSchException { + Hashtable newconf = new Hashtable<>(); + newconf.put("PubkeyAcceptedKeyTypes", "JSchTest333"); + JSch.setConfig(newconf); + assertEquals("JSchTest333", JSch.getConfig("PubkeyAcceptedKeyTypes")); + assertEquals("JSchTest333", JSch.getConfig("PubkeyAcceptedAlgorithms")); + } + + @Test + void checkLoggerBehavior() throws Exception { + assertSame(JSch.DEVNULL, JSch.logger, "initial static value of logger should be DEVNULL"); + + JSch jsch = new JSch(); + assertSame(JSch.DEVNULL, jsch.getInstanceLogger(), "instance logger should be DEVNULL"); + + TestLogger staticLogger = new TestLogger(); + TestLogger instanceLogger = new TestLogger(); + + JSch.setLogger(staticLogger); + assertSame(staticLogger, JSch.logger, "mismatch with static logger"); + assertSame(staticLogger, jsch.getInstanceLogger(), "instance should return static logger"); + + jsch.setInstanceLogger(instanceLogger); + assertSame(staticLogger, JSch.logger, "mismatch with static logger"); + assertSame(instanceLogger, jsch.getInstanceLogger(), "instance should return static logger"); + + jsch.setInstanceLogger(null); + assertSame(staticLogger, JSch.logger, "mismatch with static logger"); + assertSame(staticLogger, jsch.getInstanceLogger(), "instance should return static logger"); + + JSch.setLogger(null); + assertSame(JSch.DEVNULL, JSch.logger, "static logger should be DEVNULL"); + assertSame(JSch.DEVNULL, jsch.getInstanceLogger(), "instance logger should be DEVNULL"); + } + + static final class TestLogger implements Logger { + @Override + public boolean isEnabled(int level) { + return true; + } + + @Override + public void log(int level, String message) { + // empty + } + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/JulLoggerTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/JulLoggerTest.java new file mode 100644 index 0000000..9dca725 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/JulLoggerTest.java @@ -0,0 +1,203 @@ +package com.jcraft.jsch.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JulLogger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class JulLoggerTest { + + private static final Logger logger = Logger.getLogger(JSch.class.getName()); + private static final ListHandler handler = new ListHandler(); + + private final Exception testException = new Exception("dummy exception"); + + @BeforeAll + public static void beforeAll() { + LogManager.getLogManager().reset(); + Arrays.stream(logger.getHandlers()).forEach(logger::removeHandler); + logger.addHandler(handler); + logger.setLevel(Level.ALL); + logger.setUseParentHandlers(false); + Logger.getLogger("").setLevel(Level.OFF); + } + + @BeforeEach + public void beforeEach() { + handler.clear(); + } + + @AfterAll + public static void afterAll() { + LogManager.getLogManager().reset(); + } + + @Test + public void testGetLevel() { + Assertions.assertEquals(Level.FINER, JulLogger.getLevel(-1)); + + assertEquals(Level.FINE, JulLogger.getLevel(com.jcraft.jsch.Logger.DEBUG)); + assertEquals(Level.SEVERE, JulLogger.getLevel(com.jcraft.jsch.Logger.ERROR)); + assertEquals(Level.SEVERE, JulLogger.getLevel(com.jcraft.jsch.Logger.FATAL)); + assertEquals(Level.INFO, JulLogger.getLevel(com.jcraft.jsch.Logger.INFO)); + assertEquals(Level.WARNING, JulLogger.getLevel(com.jcraft.jsch.Logger.WARN)); + + assertEquals(Level.FINER, JulLogger.getLevel(Integer.MAX_VALUE)); + } + + @Test + public void testIsEnabled() { + JulLogger jl = new JulLogger(); + + logger.setLevel(Level.FINEST); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); + assertTrue(jl.isEnabled(-1), "trace should be enabled"); + + logger.setLevel(Level.FINE); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); + assertFalse(jl.isEnabled(-1), "trace should not be enabled"); + + logger.setLevel(Level.SEVERE); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); + assertFalse(jl.isEnabled(-1), "trace should not be enabled"); + + logger.setLevel(Level.INFO); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); + assertFalse(jl.isEnabled(-1), "trace should not be enabled"); + + logger.setLevel(Level.OFF); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should not be enabled"); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should not be enabled"); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should not be enabled"); + assertFalse(jl.isEnabled(-1), "trace should not be enabled"); + + logger.setLevel(Level.FINER); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); + assertTrue(jl.isEnabled(-1), "trace should be enabled"); + + logger.setLevel(Level.WARNING); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.DEBUG), "debug should not be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.ERROR), "error should be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.FATAL), "fatal should be enabled"); + assertFalse(jl.isEnabled(com.jcraft.jsch.Logger.INFO), "info should not be enabled"); + assertTrue(jl.isEnabled(com.jcraft.jsch.Logger.WARN), "warn should be enabled"); + assertFalse(jl.isEnabled(-1), "trace should not be enabled"); + } + + @Test + public void testLogging() { + JulLogger jl = new JulLogger(); + + List expectedMessages = + Arrays.asList("debug message", "debug message with null cause", "debug message with cause"); + List> expectedExceptions = + Arrays.asList(Optional.empty(), Optional.ofNullable(null), Optional.of(testException)); + + logger.setLevel(Level.ALL); + jl.log(-1, "debug message"); + jl.log(-1, "debug message with null cause", null); + jl.log(-1, "debug message with cause", testException); + checkMessages(expectedMessages, expectedExceptions); + + logger.setLevel(Level.ALL); + jl.log(com.jcraft.jsch.Logger.FATAL, "debug message"); + jl.log(com.jcraft.jsch.Logger.FATAL, "debug message with null cause", null); + jl.log(com.jcraft.jsch.Logger.FATAL, "debug message with cause", testException); + checkMessages(expectedMessages, expectedExceptions); + + logger.setLevel(Level.SEVERE); + jl.log(-1, "debug message"); + jl.log(-1, "debug message with null cause", null); + jl.log(-1, "debug message with cause", testException); + checkMessages(Collections.emptyList(), Collections.emptyList()); + + logger.setLevel(Level.SEVERE); + jl.log(com.jcraft.jsch.Logger.FATAL, "debug message"); + jl.log(com.jcraft.jsch.Logger.FATAL, "debug message with null cause", null); + jl.log(com.jcraft.jsch.Logger.FATAL, "debug message with cause", testException); + checkMessages(expectedMessages, expectedExceptions); + } + + private void checkMessages(List expectedMessages, + List> expectedExceptions) { + List records = handler.getRecords(); + handler.clear(); + List actualMessages = + records.stream().map(LogRecord::getMessage).collect(Collectors.toList()); + List> actualExceptions = records.stream().map(LogRecord::getThrown) + .map(Optional::ofNullable).collect(Collectors.toList()); + assertEquals(expectedMessages, actualMessages, "mismatch in logged messages"); + assertEquals(expectedExceptions, actualExceptions, "mismatch in logged exceptions"); + } + + public static class ListHandler extends Handler { + private final List records; + + public ListHandler() { + super(); + records = Collections.synchronizedList(new ArrayList<>()); + } + + @Override + public void publish(LogRecord record) { + records.add(record); + } + + @Override + public void flush() {} + + @Override + public void close() throws SecurityException {} + + public List getRecords() { + return Collections.unmodifiableList(new ArrayList<>(records)); + } + + public void clear() { + synchronized (records) { + records.clear(); + setLevel(Level.ALL); + } + } + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/KeyExchangeTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/KeyExchangeTest.java new file mode 100644 index 0000000..1876f3c --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/KeyExchangeTest.java @@ -0,0 +1,240 @@ +package com.jcraft.jsch.test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +import com.jcraft.jsch.Buffer; +import com.jcraft.jsch.KeyExchange; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.Util; +import java.util.Arrays; +import java.util.Random; +import org.junit.jupiter.api.Test; + +public class KeyExchangeTest { + + private final Random random = new Random(); + private final KeyExchange kex = new TestKex(); + + @Test + public void testNormalize0() { + byte[] secret = new byte[0]; + doNormalize(secret); + } + + @Test + public void testNormalize1() { + byte[] secret = new byte[1]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + doNormalize(secret); + } + } + + @Test + public void testNormalize2() { + byte[] secret = new byte[2]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + doNormalize(secret); + } + } + } + + @Test + public void testNormalize3() { + byte[] secret = new byte[3]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + for (int k = 0; k <= 0xff; k++) { + secret[2] = (byte) k; + doNormalize(secret); + } + } + } + } + + @Test + public void testNormalizeRandom() { + for (int i = 0; i < 1000000; i++) { + byte[] secret = new byte[64]; + random.nextBytes(secret); + doNormalize(secret); + } + } + + @Test + public void testEncodeAsMPInt1() { + byte[] secret = new byte[1]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + doEncodeAsMPInt(secret); + } + } + + @Test + public void testEncodeAsMPInt2() { + byte[] secret = new byte[2]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + doEncodeAsMPInt(secret); + } + } + } + + @Test + public void testEncodeAsMPInt3() { + byte[] secret = new byte[3]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + for (int k = 0; k <= 0xff; k++) { + secret[2] = (byte) k; + doEncodeAsMPInt(secret); + } + } + } + } + + @Test + public void testEncodeAsMPIntRandom() { + for (int i = 0; i < 1000000; i++) { + byte[] secret = new byte[64]; + random.nextBytes(secret); + doEncodeAsMPInt(secret); + } + } + + @Test + public void testEncodeAsString0() { + byte[] secret = new byte[0]; + doEncodeAsString(secret); + } + + @Test + public void testEncodeAsString1() { + byte[] secret = new byte[1]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + doEncodeAsString(secret); + } + } + + @Test + public void testEncodeAsString2() { + byte[] secret = new byte[2]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + doEncodeAsString(secret); + } + } + } + + @Test + public void testEncodeAsString3() { + byte[] secret = new byte[3]; + for (int i = 0; i <= 0xff; i++) { + secret[0] = (byte) i; + for (int j = 0; j <= 0xff; j++) { + secret[1] = (byte) j; + for (int k = 0; k <= 0xff; k++) { + secret[2] = (byte) k; + doEncodeAsString(secret); + } + } + } + } + + @Test + public void testEncodeAsStringRandom() { + for (int i = 0; i < 1000000; i++) { + byte[] secret = new byte[64]; + random.nextBytes(secret); + doEncodeAsString(secret); + } + } + + private void doNormalize(byte[] secret) { + byte[] expected = normalize(Arrays.copyOf(secret, secret.length)); + byte[] actual = kex.normalize(Arrays.copyOf(secret, secret.length)); + try { + assertArrayEquals(expected, actual); + } catch (Exception e) { + System.out.println(" secret = " + Arrays.toString(secret)); + System.out.println("expected = " + Arrays.toString(expected)); + System.out.println(" actual = " + Arrays.toString(actual)); + throw e; + } + } + + // Copy of old implementation + private static byte[] normalize(byte[] secret) { + if (secret.length > 1 && secret[0] == 0 && (secret[1] & 0x80) == 0) { + byte[] tmp = new byte[secret.length - 1]; + System.arraycopy(secret, 1, tmp, 0, tmp.length); + Util.bzero(secret); + return normalize(tmp); + } else { + return secret; + } + } + + private void doEncodeAsMPInt(byte[] secret) { + Buffer b = new Buffer(); + b.putMPInt(secret); + byte[] expected = new byte[b.getLength()]; + b.getByte(expected); + byte[] actual = kex.encodeAsMPInt(Arrays.copyOf(secret, secret.length)); + try { + assertArrayEquals(expected, actual); + } catch (Throwable t) { + System.out.println(" secret = " + Arrays.toString(secret)); + System.out.println("expected = " + Arrays.toString(expected)); + System.out.println(" actual = " + Arrays.toString(actual)); + throw t; + } + } + + private void doEncodeAsString(byte[] secret) { + Buffer b = new Buffer(); + b.putString(secret); + byte[] expected = new byte[b.getLength()]; + b.getByte(expected); + byte[] actual = kex.encodeAsString(Arrays.copyOf(secret, secret.length)); + try { + assertArrayEquals(expected, actual); + } catch (Throwable t) { + System.out.println(" secret = " + Arrays.toString(secret)); + System.out.println("expected = " + Arrays.toString(expected)); + System.out.println(" actual = " + Arrays.toString(actual)); + throw t; + } + } + + static class TestKex extends KeyExchange { + + @Override + public void init(Session session, byte[] V_S, byte[] V_C, byte[] I_S, byte[] I_C) + throws Exception { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public boolean next(Buffer buf) throws Exception { + throw new UnsupportedOperationException("Not supported"); + } + + @Override + public int getState() { + throw new UnsupportedOperationException("Not supported"); + } + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPair2IT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPair2IT.java new file mode 100644 index 0000000..f6e15b5 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPair2IT.java @@ -0,0 +1,136 @@ +package com.jcraft.jsch.test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UserInfo; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class KeyPair2IT { + + // Python can be slow for DH group 18 + private static final int timeout = 10000; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("asyncsshd.py", "docker/asyncsshd.py") + .withFileFromClasspath("ssh_host_ed448_key", "docker/ssh_host_ed448_key") + .withFileFromClasspath("ssh_host_ed448_key.pub", "docker/ssh_host_ed448_key.pub") + .withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys.KeyPairIT") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.asyncssh")) + .withExposedPorts(22); + + @ParameterizedTest + @MethodSource("com.jcraft.jsch.KeyPair2Test#keyArgs") + void connectWithPublicKey(String path, String password, String keyType) throws Exception { + + final JSch jSch = createIdentity(path, password); + + Session session = createSession(jSch); + + if (keyType != null) { + session.setConfig("PubkeyAcceptedAlgorithms", keyType); + } + try { + session.connect(timeout); + assertTrue(session.isConnected()); + } finally { + session.disconnect(); + } + } + + @ParameterizedTest + @MethodSource("com.jcraft.jsch.KeyPair2Test#keyArgs") + void connectWithPublicKeyAndUserInfo(String path, String password, String keyType) + throws Exception { + + final JSch jSch = new JSch(); + + jSch.addIdentity( + Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath()); + + Session session = createSession(jSch); + session.setUserInfo(new UserInfo() { + @Override + public String getPassphrase() { + return password; + } + + @Override + public String getPassword() { + return null; + } + + @Override + public boolean promptPassword(String message) { + return false; + } + + @Override + public boolean promptPassphrase(String message) { + return true; + } + + @Override + public boolean promptYesNo(String message) { + return false; + } + + @Override + public void showMessage(String message) {} + }); + + if (keyType != null) { + session.setConfig("PubkeyAcceptedAlgorithms", keyType); + } + try { + session.connect(timeout); + assertTrue(session.isConnected()); + } finally { + session.disconnect(); + } + } + + private JSch createIdentity(String path, String password) + throws JSchException, URISyntaxException { + JSch ssh = new JSch(); + if (password != null) { + ssh.addIdentity( + Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath(), + password); + } else { + ssh.addIdentity( + Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath()); + } + return ssh; + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "no"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPair2Test.java b/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPair2Test.java new file mode 100644 index 0000000..7cb3914 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPair2Test.java @@ -0,0 +1,58 @@ +package com.jcraft.jsch.test; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import java.io.File; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Stream; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class KeyPair2Test { + + @TempDir + public Path tmpDir; + + static Stream keyArgs() { + return Stream.of( + // PuTTY v2 keys + Arguments.of("ppkv2_ed448_unix.ppk", null, "ssh-ed448"), + Arguments.of("ppkv2_ed448_unix_encrypted.ppk", "secret123", "ssh-ed448"), + Arguments.of("ppkv2_ed448_windows.ppk", null, "ssh-ed448"), + Arguments.of("ppkv2_ed448_windows_encrypted.ppk", "secret123", "ssh-ed448"), + // PuTTY v3 keys + Arguments.of("ppkv3_ed448_unix.ppk", null, "ssh-ed448"), + Arguments.of("ppkv3_ed448_unix_encrypted.ppk", "secret123", "ssh-ed448"), + Arguments.of("ppkv3_ed448_windows.ppk", null, "ssh-ed448"), + Arguments.of("ppkv3_ed448_windows_encrypted.ppk", "secret123", "ssh-ed448"), + // PKCS8 keys + Arguments.of("pkcs8_ed448", null, "ssh-ed448"), + Arguments.of("pkcs8_ed448_encrypted_scrypt", "secret123", "ssh-ed448")); + } + + @ParameterizedTest + @MethodSource("keyArgs") + void loadKey(String path, String password, String keyType) + throws URISyntaxException, JSchException { + final JSch jSch = new JSch(); + final String prvkey = + Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath(); + assertTrue(new File(prvkey).exists()); + assertDoesNotThrow(() -> { + if (null != password) { + jSch.addIdentity(prvkey, password); + } else { + jSch.addIdentity(prvkey); + } + }); + assertEquals(keyType, jSch.getIdentityRepository().getIdentities().get(0).getAlgName()); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPairIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPairIT.java new file mode 100644 index 0000000..9aacbdf --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPairIT.java @@ -0,0 +1,119 @@ +package com.jcraft.jsch.test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UserInfo; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class KeyPairIT { + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("sshd_config", "docker/sshd_config") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys.KeyPairIT") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.KeyPairIT")) + .withExposedPorts(22); + + @ParameterizedTest + @MethodSource("com.jcraft.jsch.KeyPairTest#keyArgs") + void connectWithPublicKey(String path, String password, String keyType) throws Exception { + + final JSch jSch = createIdentity(path, password); + + Session session = createSession(jSch); + + if (keyType != null) { + session.setConfig("PubkeyAcceptedAlgorithms", keyType); + } + try { + session.connect(2000); + assertTrue(session.isConnected()); + } finally { + session.disconnect(); + } + } + + @ParameterizedTest + @MethodSource("com.jcraft.jsch.KeyPairTest#keyArgs") + void connectWithPublicKeyAndUserInfo(String path, String password, String keyType) + throws Exception { + + final JSch jSch = new JSch(); + + jSch.addIdentity( + Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath()); + + Session session = createSession(jSch); + session.setUserInfo(new UserInfo() { + @Override + public String getPassphrase() { + return password; + } + + @Override + public String getPassword() { + return null; + } + + @Override + public boolean promptPassword(String message) { + return false; + } + + @Override + public boolean promptPassphrase(String message) { + return true; + } + + @Override + public boolean promptYesNo(String message) { + return false; + } + + @Override + public void showMessage(String message) {} + }); + + if (keyType != null) { + session.setConfig("PubkeyAcceptedAlgorithms", keyType); + } + try { + session.connect(2000); + assertTrue(session.isConnected()); + } finally { + session.disconnect(); + } + } + + private JSch createIdentity(String path, String password) + throws JSchException, URISyntaxException { + JSch ssh = new JSch(); + if (password != null) { + ssh.addIdentity( + Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath(), + password); + } else { + ssh.addIdentity( + Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath()); + } + return ssh; + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "no"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPairTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPairTest.java new file mode 100644 index 0000000..f1f6dc4 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/KeyPairTest.java @@ -0,0 +1,185 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.jcraft.jsch.IdentityFile; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.KeyPair; +import java.io.File; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; + +class KeyPairTest { + + @TempDir + public Path tmpDir; + + static Stream keyArgs() { + return Stream.of( + // docker/id_rsa rsa + Arguments.of("docker/id_rsa", null, "ssh-rsa"), + // docker/ssh_host_rsa_key openssh format + Arguments.of("docker/ssh_host_rsa_key", null, "ssh-rsa"), + // encrypted_openssh_private_key_rsa + Arguments.of("encrypted_openssh_private_key_rsa", "secret123", "ssh-rsa"), + // docker/id_dsa + Arguments.of("docker/id_dsa", null, "ssh-dss"), + // dsa openssh format + Arguments.of("docker/ssh_host_dsa_key", null, "ssh-dss"), + // encrypted dsa + Arguments.of("encrypted_openssh_private_key_dsa", "secret123", "ssh-dss"), + // unencrypted RSA with windows (\r\n) line endings + Arguments.of("issue362_rsa", null, "ssh-rsa"), + Arguments.of("issue_369_rsa_opensshv1", null, "ssh-rsa"), + Arguments.of("issue_369_rsa_pem", null, "ssh-rsa"), + Arguments.of("encrypted_issue_369_rsa_opensshv1", "secret123", "ssh-rsa"), + Arguments.of("encrypted_issue_369_rsa_pem", "secret123", "ssh-rsa"), + // ecdsa EC private key format + Arguments.of("docker/id_ecdsa256", null, "ecdsa-sha2-nistp256"), // + Arguments.of("docker/id_ecdsa384", null, "ecdsa-sha2-nistp384"), // + Arguments.of("docker/id_ecdsa521", null, "ecdsa-sha2-nistp521"), + Arguments.of("docker/ssh_host_ecdsa256_key", null, "ecdsa-sha2-nistp256"), + Arguments.of("docker/ssh_host_ecdsa384_key", null, "ecdsa-sha2-nistp384"), + Arguments.of("docker/ssh_host_ecdsa521_key", null, "ecdsa-sha2-nistp521"), + // encrypted ecdsa + Arguments.of("encrypted_openssh_private_key_ecdsa", "secret123", "ecdsa-sha2-nistp256"), + // PuTTY v2 keys + Arguments.of("ppkv2_dsa_unix.ppk", null, "ssh-dss"), + Arguments.of("ppkv2_dsa_unix_encrypted.ppk", "secret123", "ssh-dss"), + Arguments.of("ppkv2_dsa_windows.ppk", null, "ssh-dss"), + Arguments.of("ppkv2_dsa_windows_encrypted.ppk", "secret123", "ssh-dss"), + Arguments.of("ppkv2_rsa_unix.ppk", null, "ssh-rsa"), + Arguments.of("ppkv2_rsa_unix_encrypted.ppk", "secret123", "ssh-rsa"), + Arguments.of("ppkv2_rsa_windows.ppk", null, "ssh-rsa"), + Arguments.of("ppkv2_rsa_windows_encrypted.ppk", "secret123", "ssh-rsa"), + Arguments.of("ppkv2_ecdsa256_unix.ppk", null, "ecdsa-sha2-nistp256"), + Arguments.of("ppkv2_ecdsa256_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp256"), + Arguments.of("ppkv2_ecdsa384_unix.ppk", null, "ecdsa-sha2-nistp384"), + Arguments.of("ppkv2_ecdsa384_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp384"), + Arguments.of("ppkv2_ecdsa521_unix.ppk", null, "ecdsa-sha2-nistp521"), + Arguments.of("ppkv2_ecdsa521_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp521"), + Arguments.of("ppkv2_ecdsa256_windows.ppk", null, "ecdsa-sha2-nistp256"), + Arguments.of("ppkv2_ecdsa256_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp256"), + Arguments.of("ppkv2_ecdsa384_windows.ppk", null, "ecdsa-sha2-nistp384"), + Arguments.of("ppkv2_ecdsa384_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp384"), + Arguments.of("ppkv2_ecdsa521_windows.ppk", null, "ecdsa-sha2-nistp521"), + Arguments.of("ppkv2_ecdsa521_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp521"), + Arguments.of("ppkv2_ed25519_unix.ppk", null, "ssh-ed25519"), + Arguments.of("ppkv2_ed25519_unix_encrypted.ppk", "secret123", "ssh-ed25519"), + Arguments.of("ppkv2_ed25519_windows.ppk", null, "ssh-ed25519"), + Arguments.of("ppkv2_ed25519_windows_encrypted.ppk", "secret123", "ssh-ed25519"), + // PuTTY v3 keys + Arguments.of("ppkv3_dsa_unix.ppk", null, "ssh-dss"), + Arguments.of("ppkv3_dsa_unix_encrypted.ppk", "secret123", "ssh-dss"), + Arguments.of("ppkv3_dsa_windows.ppk", null, "ssh-dss"), + Arguments.of("ppkv3_dsa_windows_encrypted.ppk", "secret123", "ssh-dss"), + Arguments.of("ppkv3_rsa_unix.ppk", null, "ssh-rsa"), + Arguments.of("ppkv3_rsa_unix_encrypted.ppk", "secret123", "ssh-rsa"), + Arguments.of("ppkv3_rsa_windows.ppk", null, "ssh-rsa"), + Arguments.of("ppkv3_rsa_windows_encrypted.ppk", "secret123", "ssh-rsa"), + Arguments.of("ppkv3_ecdsa256_unix.ppk", null, "ecdsa-sha2-nistp256"), + Arguments.of("ppkv3_ecdsa256_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp256"), + Arguments.of("ppkv3_ecdsa384_unix.ppk", null, "ecdsa-sha2-nistp384"), + Arguments.of("ppkv3_ecdsa384_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp384"), + Arguments.of("ppkv3_ecdsa521_unix.ppk", null, "ecdsa-sha2-nistp521"), + Arguments.of("ppkv3_ecdsa521_unix_encrypted.ppk", "secret123", "ecdsa-sha2-nistp521"), + Arguments.of("ppkv3_ecdsa256_windows.ppk", null, "ecdsa-sha2-nistp256"), + Arguments.of("ppkv3_ecdsa256_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp256"), + Arguments.of("ppkv3_ecdsa384_windows.ppk", null, "ecdsa-sha2-nistp384"), + Arguments.of("ppkv3_ecdsa384_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp384"), + Arguments.of("ppkv3_ecdsa521_windows.ppk", null, "ecdsa-sha2-nistp521"), + Arguments.of("ppkv3_ecdsa521_windows_encrypted.ppk", "secret123", "ecdsa-sha2-nistp521"), + Arguments.of("ppkv3_ed25519_unix.ppk", null, "ssh-ed25519"), + Arguments.of("ppkv3_ed25519_unix_encrypted.ppk", "secret123", "ssh-ed25519"), + Arguments.of("ppkv3_ed25519_windows.ppk", null, "ssh-ed25519"), + Arguments.of("ppkv3_ed25519_windows_encrypted.ppk", "secret123", "ssh-ed25519"), + // PKCS8 keys + Arguments.of("pkcs8_dsa", null, "ssh-dss"), + Arguments.of("pkcs8_dsa_encrypted_hmacsha1", "secret123", "ssh-dss"), + Arguments.of("pkcs8_dsa_encrypted_hmacsha256", "secret123", "ssh-dss"), + Arguments.of("pkcs8_rsa", null, "ssh-rsa"), + Arguments.of("pkcs8_rsa_encrypted_hmacsha1", "secret123", "ssh-rsa"), + Arguments.of("pkcs8_rsa_encrypted_hmacsha256", "secret123", "ssh-rsa"), + Arguments.of("pkcs8_ecdsa256", null, "ecdsa-sha2-nistp256"), + Arguments.of("pkcs8_ecdsa256_encrypted_scrypt", "secret123", "ecdsa-sha2-nistp256"), + Arguments.of("pkcs8_ecdsa384", null, "ecdsa-sha2-nistp384"), + Arguments.of("pkcs8_ecdsa384_encrypted_scrypt", "secret123", "ecdsa-sha2-nistp384"), + Arguments.of("pkcs8_ecdsa521", null, "ecdsa-sha2-nistp521"), + Arguments.of("pkcs8_ecdsa521_encrypted_scrypt", "secret123", "ecdsa-sha2-nistp521"), + Arguments.of("pkcs8_ed25519", null, "ssh-ed25519"), + Arguments.of("pkcs8_ed25519_encrypted_scrypt", "secret123", "ssh-ed25519")); + } + + @ParameterizedTest + @MethodSource("keyArgs") + void loadKey(String path, String password, String keyType) + throws URISyntaxException, JSchException { + final JSch jSch = new JSch(); + final String prvkey = + Paths.get(ClassLoader.getSystemResource(path).toURI()).toFile().getAbsolutePath(); + assertTrue(new File(prvkey).exists()); + assertDoesNotThrow(() -> { + if (null != password) { + jSch.addIdentity(prvkey, password); + } else { + jSch.addIdentity(prvkey); + } + }); + assertEquals(keyType, jSch.getIdentityRepository().getIdentities().get(0).getAlgName()); + } + + @Test + void genKeypair() { + final JSch jSch = new JSch(); + assertDoesNotThrow(() -> { + KeyPair kpair = KeyPair.genKeyPair(jSch, KeyPair.RSA, 1024); + kpair.writePrivateKey(tmpDir.resolve("my-private-key").toString()); + }); + } + + @Test + void genKeypairEncrypted() { + final JSch jSch = new JSch(); + assertDoesNotThrow(() -> { + KeyPair kpair = KeyPair.genKeyPair(jSch, KeyPair.RSA, 1024); + kpair.writePrivateKey(tmpDir.resolve("my-private-key-encrypted").toString(), + "my-password".getBytes(UTF_8)); + }); + } + + @ParameterizedTest + @ValueSource(strings = {"encrypted_openssh_private_key_rsa", "encrypted_openssh_private_key_dsa", + "encrypted_openssh_private_key_ecdsa"}) + void decryptEncryptedOpensshKey(String keyFile) throws URISyntaxException, JSchException { + final JSch jSch = new JSch(); + final String prvkey = + Paths.get(ClassLoader.getSystemResource(keyFile).toURI()).toFile().getAbsolutePath(); + assertTrue(new File(prvkey).exists()); + IdentityFile identity = IdentityFile.newInstance(prvkey, null, jSch.instLogger); + + // Decrypt the key file + assertTrue(identity.getKeyPair().decrypt("secret123")); + + // From now on, the pair now longer counts as encrypted + assertFalse(identity.getKeyPair().isEncrypted()); + assertNotNull(identity.getKeyPair().getPrivateKey()); + // An unencrypted key pair should allow #decrypt(null) + // com.jcraft.jsch.UserAuthPublicKey relies on this + assertTrue(identity.getKeyPair().decrypt((byte[]) null)); + assertTrue(identity.getKeyPair().decrypt((String) null)); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/KnownHostsTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/KnownHostsTest.java new file mode 100644 index 0000000..63a8eaa --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/KnownHostsTest.java @@ -0,0 +1,1152 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.KnownHosts; +import com.jcraft.jsch.KnownHosts.HashedHostKey; +import com.jcraft.jsch.Logger; +import com.jcraft.jsch.MAC; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.UserInfo; +import com.jcraft.jsch.Util; +import com.jcraft.jsch.jce.HMACSHA256; +import com.jcraft.jsch.jce.HMACSHA512; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.LinkedList; +import java.util.Properties; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class KnownHostsTest { + private static final String rsaKey = + "AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkc" + + "cKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81e" + + "FzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpI" + + "oaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G" + + "3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ=="; + private static final String hashValue = + "|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg="; + private static final String hostLine = "ssh.example.com,192.168.1.61"; + private static final byte[] dsaKeyBytes = Util.str2byte(" ssh-dsa"); + private static final byte[] rsaKeyBytes = Util.str2byte(" ssh-rsa"); + private LinkedList messages; + private JSch jsch; + private Hashtable orgConfig; + private Properties orgProps; + + @BeforeEach + void setupTest() { + orgProps = System.getProperties(); + Properties myProps = new Properties(orgProps); + System.setProperties(myProps); + orgConfig = new Hashtable<>(JSch.config); + messages = new LinkedList<>(); + jsch = new JSch(); + jsch.setInstanceLogger(new TestLogger(messages)); + } + + @AfterEach + void tearDownTest() { + System.setProperties(orgProps); + JSch.setConfig(orgConfig); + Session.random = null; + } + + @Test + void testInstantiationValues() throws Exception { + KnownHosts kh = new KnownHosts(jsch); + + assertNull(kh.getKnownHostsFile(), "check known_hosts filename"); + assertNull(kh.getKnownHostsRepositoryID(), "check repository id"); + assertNotNull(kh.hmacsha1, "hmac instance not expected to be null"); + assertSame(kh.hmacsha1, kh.getHMACSHA1(), + "same instance should be returned with call of getHMACSHA1"); + assertSame(kh.getHMACSHA1(), kh.getHMACSHA1(), + "same instance should be returned with call of getHMACSHA1"); + + KnownHosts kh2 = new KnownHosts(jsch); + assertNotNull(kh2.hmacsha1, "hmac instance not expected to be null"); + assertNotSame(kh.hmacsha1, kh2.hmacsha1, + "hmac instances should be different in different KH-instances"); + } + + @Test + void testSetKnownHostsByFilename() throws Exception { + KnownHosts kh = new KnownHosts(jsch) { + @Override + void setKnownHosts(InputStream input) throws JSchException { + messages.add("set known hosts by stream"); + StringBuilder sb = new StringBuilder(); + int read; + try { + while ((read = input.read()) != -1) { + sb.append((char) read); + } + messages.add(sb.toString()); + } catch (IOException ioe) { + throw new JSchException("error while reading hosts file", ioe); + } + } + }; + + File hostFile = File.createTempFile("setknownhosts", ".txt"); + try { + try (FileOutputStream fos = new FileOutputStream(hostFile)) { + fos.write("some host data".getBytes(ISO_8859_1)); + } + System.setProperty("user.home", hostFile.getParentFile().getAbsolutePath()); + kh.setKnownHosts("some_filename:that can't exist"); + assertEquals("some_filename:that can't exist", kh.getKnownHostsFile(), + "check known_hosts filename"); + assertEquals("some_filename:that can't exist", kh.getKnownHostsRepositoryID(), + "check repository id"); + + assertEquals(0, messages.size(), "expected no messages till now"); + + kh.setKnownHosts(hostFile.getAbsolutePath()); + assertEquals(hostFile.getAbsolutePath(), kh.getKnownHostsFile(), + "check known_hosts filename"); + assertEquals(hostFile.getAbsolutePath(), kh.getKnownHostsRepositoryID(), + "check repository id"); + assertEquals(2, messages.size(), "expected no messages after setting"); + assertEquals("set known hosts by stream", messages.removeFirst(), "check message"); + assertEquals("some host data", messages.removeFirst(), "check message"); + + File userDirFile = new File("~", hostFile.getName()); + assertEquals(hostFile.getAbsolutePath(), Util.checkTilde(userDirFile.getPath()), + "check result of userTilde"); + kh.setKnownHosts(userDirFile.getPath()); + assertEquals(userDirFile.getPath(), kh.getKnownHostsFile(), "check known_hosts filename"); + assertEquals(userDirFile.getPath(), kh.getKnownHostsRepositoryID(), "check repository id"); + assertEquals(2, messages.size(), "expected 2 messages after setting"); + assertEquals("set known hosts by stream", messages.removeFirst(), "check message"); + assertEquals("some host data", messages.removeFirst(), "check message"); + } finally { + hostFile.delete(); + } + } + + @Test + void testCreateAndGetHMACSHA1() throws Exception { + JSch.setConfig("hmac-sha1", "my.hmac.sha1.class.Name"); + KnownHosts kh = new KnownHosts(jsch) { + @Override + MAC createHMAC(String hmacClassname) { + messages.add("create hmac instance of class " + hmacClassname); + return null; + } + }; + + assertNull(kh.hmacsha1, "hmac instance should be null"); + assertEquals("create hmac instance of class my.hmac.sha1.class.Name", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + + try { + new KnownHosts(jsch); + fail("exception expected"); + } catch (IllegalArgumentException iae) { + assertEquals("instantiation of my.hmac.sha1.class.Name lead to an error", iae.getMessage(), + "check exception message"); + Throwable cause = iae.getCause(); + assertNotNull(cause, "cause should not be null"); + assertEquals(ClassNotFoundException.class.getName(), cause.getClass().getName(), + "unexpected cause"); + } + assertEquals( + "M(3): unable to instantiate HMAC-class my.hmac.sha1.class.Name\r\n" + + " java.lang.ClassNotFoundException: my.hmac.sha1.class.Name", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + + // it's not SHA-1 but for this test any hashing class will do + JSch.setConfig("hmac-sha1", HMACSHA256.class.getName()); + kh = new KnownHosts(jsch); + assertNotNull(kh.hmacsha1, "hmac instance should not be null"); + assertSame(kh.hmacsha1, kh.getHMACSHA1(), "instance shouldn't change"); + assertEquals(HMACSHA256.class.getName(), kh.hmacsha1.getClass().getName(), + "hmac class mismatch"); + + MAC currentMAC = kh.hmacsha1; + JSch.setConfig("hmac-sha1", HMACSHA512.class.getName()); + assertSame(currentMAC, kh.getHMACSHA1(), + "instance shouldn't change even with now correct config-entry"); + + kh = new KnownHosts(jsch); + assertNotNull(kh.hmacsha1, "hmac instance should not be null"); + assertSame(kh.hmacsha1, kh.getHMACSHA1(), "instance shouldn't change"); + assertEquals(HMACSHA512.class.getName(), kh.hmacsha1.getClass().getName(), + "hmac class mismatch"); + assertEquals("", messages.stream().collect(Collectors.joining("\r\n"))); + } + + @Test + void testSetKnownHostsHashedHost() throws Exception { + KnownHosts kh = new KnownHosts(jsch); + // comment with umlaut to check used charset for dump + kh.setKnownHosts( + new ByteArrayInputStream((hashValue + " " + "ssh-rsa " + rsaKey + " some comment\r\n" + + "# 192.168.1.61 ssh-rsa MYRSAKEY some other commänt").getBytes(UTF_8))); + + assertEquals(0, messages.size(), "no messages expected"); + HostKey[] keys = kh.getHostKey(); + checkResultForKeyResult(keys, rsaKey, hashValue, ""); + + keys = kh.getHostKey("192.168.1.61", "ssh-rsa"); + checkResultForKeyResult(keys, rsaKey, hashValue, ""); + + keys = kh.getHostKey("192.168.1.62", "ssh-rsa"); + assertNotNull(keys, "actual keys expected"); + assertEquals(0, keys.length, "0 keys expected"); + keys = kh.getHostKey("192.168.1.61", "ssh-dsa"); + assertNotNull(keys, "actual keys expected"); + assertEquals(0, keys.length, "0 keys expected"); + + ByteArrayOutputStream dump = new ByteArrayOutputStream(); + kh.dump(dump); + assertEquals( + hashValue + " ssh-rsa " + rsaKey + " some comment\n" + + "# 192.168.1.61 ssh-rsa MYRSAKEY some other commänt\n" + "", + dump.toString("UTF8"), "dump mismatch"); + } + + @Test + void testSetKnownHostsDirectHost() throws Exception { + KnownHosts kh = new KnownHosts(jsch); + kh.setKnownHosts(new ByteArrayInputStream( + ("@cert-authority " + hostLine + " " + "ssh-rsa " + rsaKey + " some comment") + .getBytes(ISO_8859_1))); + + assertEquals(0, messages.size(), "no messages expected"); + HostKey[] keys = kh.getHostKey(); + checkResultForKeyResult(keys, rsaKey, hostLine, "@cert-authority"); + + keys = kh.getHostKey("192.168.1.61", "ssh-rsa"); + checkResultForKeyResult(keys, rsaKey, hostLine, "@cert-authority"); + keys = kh.getHostKey("ssh.example.com", "ssh-rsa"); + checkResultForKeyResult(keys, rsaKey, hostLine, "@cert-authority"); + + keys = kh.getHostKey("192.168.1.62", "ssh-rsa"); + assertNotNull(keys, "actual keys expected"); + assertEquals(0, keys.length, "0 keys expected"); + keys = kh.getHostKey("192.168.1.61", "ssh-dsa"); + assertNotNull(keys, "actual keys expected"); + assertEquals(0, keys.length, "0 keys expected"); + + ByteArrayOutputStream dump = new ByteArrayOutputStream(); + kh.dump(dump); + assertEquals("@cert-authority " + hostLine + " ssh-rsa " + rsaKey + " some comment\n" + "", + dump.toString("8859_1"), "dump mismatch"); + + kh.setKnownHosts(new ByteArrayInputStream( + ("!ssh.example.com,!192.168.1.61 " + "ssh-rsa " + rsaKey + " some comment") + .getBytes(ISO_8859_1))); + assertEquals(0, messages.size(), "no messages expected"); + keys = kh.getHostKey(); + checkResultForKeyResult(keys, rsaKey, "!ssh.example.com,!192.168.1.61", ""); + + keys = kh.getHostKey("192.168.1.61", "ssh-rsa"); + assertNotNull(keys, "actual keys expected"); + assertEquals(0, keys.length, "0 keys expected"); + keys = kh.getHostKey("ssh.example.com", "ssh-rsa"); + assertNotNull(keys, "actual keys expected"); + assertEquals(0, keys.length, "0 keys expected"); + + keys = kh.getHostKey("192.168.1.62", "ssh-rsa"); + assertNotNull(keys, "actual keys expected"); + assertEquals(0, keys.length, "0 keys expected"); + keys = kh.getHostKey("192.168.1.61", "ssh-dsa"); + assertNotNull(keys, "actual keys expected"); + assertEquals(0, keys.length, "0 keys expected"); + + dump.reset(); + kh.dump(dump); + assertEquals("!ssh.example.com,!192.168.1.61" + " ssh-rsa " + rsaKey + " some comment\n" + "", + dump.toString("8859_1"), "dump mismatch"); + } + + @Test + void testkSyncDump() throws Exception { + KnownHosts kh = new KnownHosts(jsch) { + @Override + synchronized void sync(String foo) throws IOException { + messages.add("sync with file '" + foo + "'"); + super.sync(foo); + } + }; + File tempFile = File.createTempFile("checksyncdump", ".txt"); + try { + System.setProperty("user.home", tempFile.getParentFile().getAbsolutePath()); + assertNull(kh.getKnownHostsFile(), "known_hosts expected to be null"); + kh.sync(); + assertEquals(0, messages.size(), "no messages expected"); + kh.setKnownHosts(tempFile.getAbsolutePath()); + assertEquals(0, messages.size(), "no messages expected"); + kh.sync(); + assertEquals("sync with file '" + tempFile.getAbsolutePath() + "'", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + + kh = new KnownHosts(jsch) { + @Override + void dump(OutputStream out) { + messages.add("stream based dump called with stream: " + out.getClass().getName()); + try { + out.write("some dump data".getBytes(ISO_8859_1)); + } catch (IOException ioe) { + Assertions.fail("exception occurred while trying to write dump to tream", ioe); + } + } + }; + kh.sync(null); + assertEquals(0, messages.size(), "no messages expected"); + kh.sync(tempFile.getAbsolutePath()); + assertEquals("stream based dump called with stream: java.io.FileOutputStream", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + assertEquals("some dump data", getContent(tempFile.getAbsolutePath())); + assertTrue(tempFile.delete(), "unable to delete '" + tempFile.getAbsolutePath() + "'"); + + String userPath = new File("~", tempFile.getName()).getPath(); + kh.sync(userPath); + assertEquals("stream based dump called with stream: java.io.FileOutputStream", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + assertEquals("some dump data", getContent(tempFile.getAbsolutePath())); + assertTrue(tempFile.delete(), "unable to delete '" + tempFile.getAbsolutePath() + "'"); + + assertTrue(tempFile.mkdir(), "unable to create '" + tempFile.getAbsolutePath() + "'"); + try { + kh.sync(tempFile.getAbsolutePath()); + fail("exception expected"); + } catch (FileNotFoundException fnfe) { + // expected, details are OS-dependent, so no check of message, etc. + } + assertEquals(0, messages.size(), "no messages expected"); + + kh = new KnownHosts(jsch) { + @Override + void dumpHostKey(OutputStream out, HostKey hk) throws IOException { + if (out == null) { + throw new NullPointerException("out is null"); + } + messages.add("dump host key for host " + hk.getHost()); + } + }; + + kh.add(kh.new HashedHostKey("host1.example.com", HostKey.SSHRSA, new byte[1]), null); + kh.add(kh.new HashedHostKey("host2.example.com", HostKey.SSHRSA, new byte[1]), null); + + kh.dump(null); + assertEquals( + "M(3): unable to dump known hosts\r\n" + " java.lang.NullPointerException: out is null", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + + kh.dump(new ByteArrayOutputStream()); + assertEquals( + "dump host key for host host1.example.com\r\n" + + "dump host key for host host2.example.com", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + } finally { + tempFile.delete(); + } + } + + @Test + void testDumpHostKey() throws Exception { + ByteArrayOutputStream sink = new ByteArrayOutputStream(); + KnownHosts kh = new KnownHosts(jsch); + + kh.dumpHostKey(sink, + new HostKey("", "hostwithoutmarker", HostKey.SSHRSA, "rsakey".getBytes(ISO_8859_1), null)); + assertEquals("hostwithoutmarker ssh-rsa cnNha2V5\n", sink.toString("utf8"), "check dumped key"); + sink.reset(); + kh.dumpHostKey(sink, new HostKey("@somemarker", "hostwithmarker", HostKey.SSHRSA, + "rsakey".getBytes(ISO_8859_1), null)); + assertEquals("@somemarker hostwithmarker ssh-rsa cnNha2V5\n", sink.toString("utf8"), + "check dumped key"); + sink.reset(); + + kh.dumpHostKey(sink, new HostKey("", "hostwithoutmarker", HostKey.SSHRSA, + "rsakey".getBytes(ISO_8859_1), "some commänt")); + assertEquals("hostwithoutmarker ssh-rsa cnNha2V5 some commänt\n", sink.toString("utf8"), + "check dumped key"); + sink.reset(); + kh.dumpHostKey(sink, new HostKey("@somemarker", "hostwithmarker", HostKey.SSHRSA, + "rsakey".getBytes(ISO_8859_1), "some commänt")); + assertEquals("@somemarker hostwithmarker ssh-rsa cnNha2V5 some commänt\n", + sink.toString("utf8"), "check dumped key"); + sink.reset(); + + kh.dumpHostKey(sink, new HostKey("", "hostwithoutmarker", HostKey.UNKNOWN, + "rsakey".getBytes(ISO_8859_1), "some commänt")); + assertEquals("hostwithoutmarker\n", sink.toString("utf8"), "check dumped key"); + sink.reset(); + } + + @Test + void testDeleteSubstring() { + KnownHosts kh = new KnownHosts(jsch); + assertEquals("host1,host2", kh.deleteSubString("todelete,host1,host2", "todelete"), + "check result"); + assertEquals("host1,host2", kh.deleteSubString("host1,todelete,host2", "todelete"), + "check result"); + assertEquals("host1,host2,todelete", + kh.deleteSubString("host1,todelete,host2,todelete", "todelete"), "check result"); + assertEquals("host1,host2", kh.deleteSubString("host1,host2,todelete", "todelete"), + "check result"); + assertEquals("host1,host2,host3", kh.deleteSubString("host1,host2,host3", "todelete"), + "check result"); + assertEquals("host1,host2,host3,", kh.deleteSubString("host1,host2,host3,", "todelete"), + "check result"); + assertEquals("", kh.deleteSubString("todelete", "todelete"), "check result"); + assertEquals("nottodelete", kh.deleteSubString("nottodelete", "todelete"), "check result"); + } + + @Test + void testCreateHashedKey() throws Exception { + Session.random = new NotSoRandomRandom(); + KnownHosts kh = new KnownHosts(jsch); + kh.hmacsha1 = null; // this will lead to an NPE if the creation uses this instance + + try { + kh.createHashedHostKey("host.example.com", " ssh-rsa".getBytes(ISO_8859_1)); + fail("exception expected"); + } catch (NullPointerException npe) { + // expected but messages differ between java versions, so we don't check the message + assertEquals("hash", npe.getStackTrace()[0].getMethodName(), "check hash threw exception"); + } + + kh.hmacsha1 = new HMACSHA256(); + HostKey hostKey = + kh.createHashedHostKey("host.example.com", " ssh-rsa".getBytes(ISO_8859_1)); + assertNotNull(hostKey, "returned host key shouldn't be null"); + assertEquals(HashedHostKey.class.getName(), hostKey.getClass().getName(), + "check type of returned host key"); + HashedHostKey hhk = (HashedHostKey) hostKey; + + assertEquals( + "|1|AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=|mie6rcAf1aPGk6d+HxnkpvO4HaOAH/Y6YWegs+Xog/s=", + hhk.getHost(), "host mismatch"); + assertEquals("", hhk.getMarker(), "marker mismatch"); + assertEquals( + "9c:fb:7f:99:79:01:6d:46:68:87:39:15:4f:f5:cc:9d:71:7a:8b:5a:4a:c1:c7:4b:9c:20:a5:91:c2:6a:ff:5a", + hhk.getFingerPrint(jsch)); + assertEquals(null, hhk.getComment(), "comment mismatch"); + assertEquals("ICAgIHNzaC1yc2E=", hhk.getKey(), "key mismatch"); + assertEquals("ssh-rsa", hhk.getType(), "type mismatch"); + } + + @Test + void testHashedKeyCreation() throws Exception { + Session.random = new NotSoRandomRandom(); + KnownHosts kh = new KnownHosts(jsch); + HashedHostKey hhk; + + hhk = kh.new HashedHostKey("host.example.com", dsaKeyBytes); + checkUnhashedHostKey(hhk, "", "host.example.com", "ssh-dss", null); + + hhk.hash(); + checkSHA1HashResult(hhk, "", "ssh-dss", null); + + hhk.hash(); + checkSHA1HashResult(hhk, "", "ssh-dss", null); + + hhk = kh.new HashedHostKey("|1|AAECAwQFBgcICQoLDA0ODxAREhM=|/pE4peaossRYDRp6bEWa348eFLI=", + dsaKeyBytes); + checkSHA1HashResult(hhk, "", "ssh-dss", null); + + hhk = kh.new HashedHostKey("|1|AAECAwQFBgcICQoLDA0ODxAREhM=/pE4peaossRYDRp6bEWa348eFLI=", + dsaKeyBytes); + checkUnhashedHostKey(hhk, "", "|1|AAECAwQFBgcICQoLDA0ODxAREhM=/pE4peaossRYDRp6bEWa348eFLI=", + "ssh-dss", null); + + hhk = kh.new HashedHostKey("|1|AAAA|ABCD", dsaKeyBytes); + checkUnhashedHostKey(hhk, "", "|1|AAAA|ABCD", "ssh-dss", null); + + hhk = + kh.new HashedHostKey("|1|AAECAwQFBgcICQoLDA0ODxAREhM=|ABCD", HostKey.ED25519, dsaKeyBytes); + checkUnhashedHostKey(hhk, "", "|1|AAECAwQFBgcICQoLDA0ODxAREhM=|ABCD", "ssh-ed25519", null); + + Mac mac = Mac.getInstance("HMACSHA1", new BouncyCastleProvider()); + kh.hmacsha1 = new BCHMACSHA1(mac); + hhk = kh.new HashedHostKey("@somemarker", "host.example.com", HostKey.ED448, dsaKeyBytes, + "some commänt"); + checkUnhashedHostKey(hhk, "@somemarker", "host.example.com", "ssh-ed448", "some commänt"); + + hhk.hash(); + checkSHA1HashResult(hhk, "@somemarker", "ssh-ed448", "some commänt"); + + hhk.hash(); // should have no effect + checkSHA1HashResult(hhk, "@somemarker", "ssh-ed448", "some commänt"); + + hhk = kh.new HashedHostKey(hhk.getHost(), HostKey.ED448, dsaKeyBytes); + checkSHA1HashResult(hhk, "", "ssh-ed448", null); + } + + @Test + void testHashedHostKeyHashIsMatch() throws Exception { + String heyKey = + "0x00:0x01:0x02:0x03:0x04:0x05:0x06:0x07:0x08:0x09:0x0a:0x0b:0x0c:0x0d:0x0e:0x0f:0x10:0x11:0x12:0x13"; + Session.random = new NotSoRandomRandom() { + @Override + public void fill(byte[] foo, int start, int len) { + messages.add("fill in random called"); + super.fill(foo, start, len); + } + }; + KnownHosts kh = new KnownHosts(jsch); + boolean[] throwException = new boolean[1]; + Mac mac = Mac.getInstance("HMACSHA1", new BouncyCastleProvider()); + kh.hmacsha1 = new BCHMACSHA1(mac) { + @Override + public void init(byte[] key) throws Exception { + messages.add("init in mac called with key " + Util.toHex(key)); + if (throwException[0]) { + throw new IOException("dummy ioe"); + } + super.init(key); + } + }; + HashedHostKey hhk = kh.new HashedHostKey("@somemarker", "host.example.com", HostKey.ED448, + dsaKeyBytes, "some commänt"); + checkUnhashedHostKey(hhk, "@somemarker", "host.example.com", "ssh-ed448", "some commänt"); + assertEquals(0, messages.size(), "expected no messages"); + assertTrue(hhk.isMatched("host.example.com"), "match expected"); + assertFalse(hhk.isMatched("otherhost.example.com"), "no match expected"); + + hhk.hash(); + checkSHA1HashResult(hhk, "@somemarker", "ssh-ed448", "some commänt"); + assertEquals("fill in random called\r\n" + "init in mac called with key " + heyKey, + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + assertTrue(hhk.isMatched("host.example.com"), "match expected"); + assertEquals("init in mac called with key " + heyKey, + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + assertFalse(hhk.isMatched("otherhost.example.com"), "no match expected"); + assertEquals("init in mac called with key " + heyKey, + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + + hhk.hash(); + assertEquals(0, messages.size(), "expected no messages"); + + hhk = kh.new HashedHostKey("@somemarker", "host.example.com", HostKey.ED448, dsaKeyBytes, + "some commänt"); + hhk.salt = new byte[mac.getMacLength()]; + Session.random.fill(hhk.salt, 0, hhk.salt.length); + messages.clear(); + hhk.hash(); + checkSHA1HashResult(hhk, "@somemarker", "ssh-ed448", "some commänt"); + assertEquals("init in mac called with key " + heyKey, + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + + hhk = kh.new HashedHostKey("@somemarker", "host.example.com", HostKey.ED448, dsaKeyBytes, + "some commänt"); + throwException[0] = true; + checkUnhashedHostKey(hhk, "@somemarker", "host.example.com", "ssh-ed448", "some commänt"); + hhk.hash(); + checkUnhashedHostKey(hhk, "@somemarker", "host.example.com", "ssh-ed448", "some commänt"); + assertEquals("fill in random called\r\n" + "init in mac called with key " + heyKey + "\r\n" + + "M(3): an error occurred while trying to calculate the hash for host host.example.com\r\n" + + " java.io.IOException: dummy ioe", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + + throwException[0] = false; + hhk.hash(); + checkSHA1HashResult(hhk, "@somemarker", "ssh-ed448", "some commänt"); + messages.clear(); + throwException[0] = true; + assertFalse(hhk.isMatched("host.example.com"), "no match expected"); + assertEquals( + "init in mac called with key " + heyKey + "\r\n" + + "M(3): an error occurred while trying to check hash for host host.example.com\r\n" + + " java.io.IOException: dummy ioe", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + } + + @Test + void testSyncKnownHostsFile() throws Exception { + boolean[] throwException = new boolean[1]; + KnownHosts kh = new KnownHosts(jsch) { + @Override + void sync() throws IOException { + messages.add("sync() called"); + throw new IOException("shouldn't be called"); + } + + @Override + synchronized void sync(String filename) throws IOException { + messages.add("sync called with file " + filename); + if (throwException[0]) { + throw new RuntimeException("dummy re"); + } + } + }; + LinkedList> promptHandlers = new LinkedList<>(); + UserInfo checkUI = new UserInfo() { + @Override + public void showMessage(String message) { + messages.add("UIM: " + message); + } + + @Override + public boolean promptYesNo(String message) { + messages.add("UIPYN: " + message); + if (promptHandlers.isEmpty()) { + return false; + } + Function function = promptHandlers.removeFirst(); + return function.apply(message).booleanValue(); + } + + @Override + public boolean promptPassword(String message) { + throw new RuntimeException("promptPassword shouldn't be called"); + } + + @Override + public boolean promptPassphrase(String message) { + throw new RuntimeException("promptPassphrase shouldn't be called"); + } + + @Override + public String getPassword() { + throw new RuntimeException("getPassword shouldn't be called"); + } + + @Override + public String getPassphrase() { + throw new RuntimeException("getPassphrase shouldn't be called"); + } + }; + + File tempFile = File.createTempFile("syncknownhostfile", ".dir"); + assertTrue(tempFile.delete(), "unable to delete " + tempFile.getAbsolutePath()); + File subDirFile = new File(tempFile, "subdir/known_hosts"); + String subdirParentAbsPath = subDirFile.getParentFile().getAbsolutePath(); + + try { + assertNull(kh.getKnownHostsRepositoryID(), "repository id should be null"); + kh.syncKnownHostsFile(null); + assertEquals(0, messages.size(), "no messages expected"); + + kh.setKnownHosts(tempFile.getAbsolutePath()); + assertEquals(tempFile.getAbsolutePath(), kh.getKnownHostsRepositoryID(), + "repository id should be the temp file's absolute path"); + kh.syncKnownHostsFile(null); + assertEquals(0, messages.size(), "no messages expected"); + + promptHandlers.add((message) -> checkMessageAndReturn( + tempFile.getAbsolutePath() + " does not exist.\n" + "Are you sure you want to create it?", + message, false)); + kh.syncKnownHostsFile(checkUI); + assertEquals( + "UIPYN: " + tempFile.getAbsolutePath() + " does not exist.\n" + + "Are you sure you want to create it?", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + assertFalse(tempFile.exists(), "file shouldn't exist after call"); + + kh.setKnownHosts(subDirFile.getAbsolutePath()); + promptHandlers.add((message) -> checkMessageAndReturn(subDirFile.getAbsolutePath() + + " does not exist.\n" + "Are you sure you want to create it?", message, true)); + promptHandlers + .add((message) -> checkMessageAndReturn("The parent directory " + subdirParentAbsPath + + " does not exist.\n" + "Are you sure you want to create it?", message, false)); + kh.syncKnownHostsFile(checkUI); + assertEquals( + "UIPYN: " + subDirFile.getAbsolutePath() + " does not exist.\n" + + "Are you sure you want to create it?\r\n" + "UIPYN: The parent directory " + + subdirParentAbsPath + " does not exist.\n" + "Are you sure you want to create it?", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + assertFalse(subDirFile.exists(), "file shouldn't exist after call"); + assertFalse(tempFile.exists(), "subdir shouldn't exist after call"); + + assertTrue(tempFile.createNewFile(), "unable to create " + tempFile.getAbsolutePath()); + promptHandlers.add((message) -> checkMessageAndReturn(subDirFile.getAbsolutePath() + + " does not exist.\n" + "Are you sure you want to create it?", message, true)); + promptHandlers + .add((message) -> checkMessageAndReturn("The parent directory " + subdirParentAbsPath + + " does not exist.\n" + "Are you sure you want to create it?", message, true)); + kh.syncKnownHostsFile(checkUI); + assertEquals("UIPYN: " + subDirFile.getAbsolutePath() + " does not exist.\n" + + "Are you sure you want to create it?\r\n" + "UIPYN: The parent directory " + + subdirParentAbsPath + " does not exist.\n" + "Are you sure you want to create it?\r\n" + + "UIM: " + subdirParentAbsPath + " has not been created.", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + assertFalse(subDirFile.exists(), "file shouldn't exist after call"); + assertTrue(tempFile.isFile(), "subdir should exist as file after call"); + + assertTrue(tempFile.delete(), "unable to delete " + tempFile.getAbsolutePath()); + promptHandlers.add((message) -> checkMessageAndReturn(subDirFile.getAbsolutePath() + + " does not exist.\n" + "Are you sure you want to create it?", message, true)); + promptHandlers + .add((message) -> checkMessageAndReturn("The parent directory " + subdirParentAbsPath + + " does not exist.\n" + "Are you sure you want to create it?", message, true)); + kh.syncKnownHostsFile(checkUI); + assertEquals( + "UIPYN: " + subDirFile.getAbsolutePath() + " does not exist.\n" + + "Are you sure you want to create it?\r\n" + "UIPYN: The parent directory " + + subdirParentAbsPath + " does not exist.\n" + + "Are you sure you want to create it?\r\n" + "UIM: " + subdirParentAbsPath + + " has been succesfully created.\n" + "Please check its access permission.\r\n" + + "sync called with file " + subDirFile.getAbsolutePath(), + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + assertFalse(subDirFile.exists(), "file shouldn't exist after call"); + assertTrue(tempFile.isDirectory(), "subdir should exist as directory after call"); + + assertTrue(subDirFile.createNewFile(), "unable to create " + subDirFile.getAbsolutePath()); + kh.syncKnownHostsFile(checkUI); + assertEquals("sync called with file " + subDirFile.getAbsolutePath(), + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + assertTrue(subDirFile.exists(), "file should exist (we've created it)"); + assertTrue(tempFile.isDirectory(), "subdir should exist as directory after call"); + + throwException[0] = true; + kh.syncKnownHostsFile(checkUI); + assertEquals( + "sync called with file " + subDirFile.getAbsolutePath() + "\r\n" + + "M(3): unable to sync known host file " + subDirFile.getAbsolutePath() + "\r\n" + + " java.lang.RuntimeException: dummy re", + messages.stream().collect(Collectors.joining("\r\n"))); + messages.clear(); + } finally { + if (tempFile != null) { + subDirFile.delete(); + subDirFile.getParentFile().delete(); + tempFile.delete(); + } + } + } + + @Test + public void testCheck() throws Exception { + KnownHosts kh = new KnownHosts(jsch); + String expectedExceptionMessage = ""; + try { + new HostKey("host.example.com", HostKey.GUESS, new byte[0]); + fail("exception expected"); + } catch (Exception e) { + expectedExceptionMessage = e.getMessage(); + } + + assertEquals(KnownHosts.NOT_INCLUDED, kh.check(null, new byte[0]), + "null host should return NOT_INCLUDED"); + assertEquals(0, messages.size(), + "no messages expected: " + messages.stream().collect(Collectors.joining("\r\n"))); + assertEquals(KnownHosts.NOT_INCLUDED, kh.check("host.example.com", new byte[0]), + "empty key should return NOT_INCLUDED"); + assertEquals(1, messages.size(), + "only one message: " + messages.stream().collect(Collectors.joining("\r\n"))); + assertEquals( + "M(0): exception while trying to read key while checking host 'host.example.com'\r\n" + + " java.lang.ArrayIndexOutOfBoundsException: " + expectedExceptionMessage, + messages.removeFirst(), "unexpected message"); + + addHosts(kh); + assertEquals(KnownHosts.NOT_INCLUDED, kh.check("host.example.com", dsaKeyBytes), + "type mismatch should return NOT_INCLUDED"); + assertEquals(KnownHosts.OK, kh.check("host.example.com", rsaKeyBytes), + "fitting key should return OK"); + assertEquals(KnownHosts.OK, kh.check("192.277.325.3", rsaKeyBytes), + "fitting key should return OK"); + assertEquals(KnownHosts.OK, kh.check("192.277.325.5", dsaKeyBytes), + "fitting key should return OK"); + assertEquals(KnownHosts.OK, kh.check("[192.277.325.5]:123", dsaKeyBytes), + "fitting key should return OK"); + assertEquals(KnownHosts.NOT_INCLUDED, kh.check("[192.277.325.5:123]", dsaKeyBytes), + "invalid syntax should return NOT_INCLUDED"); + assertEquals(KnownHosts.NOT_INCLUDED, kh.check("[]:123", dsaKeyBytes), + "invalid syntax should return NOT_INCLUDED"); + + assertEquals(KnownHosts.CHANGED, + kh.check("host.example.com", " ssh-rsa1234".getBytes(ISO_8859_1)), + "changed key should return CHANGED"); + assertEquals(KnownHosts.NOT_INCLUDED, kh.check("host2.example.com", rsaKeyBytes), + "host mismatch should return NOT_INCLUDED"); + + assertEquals(KnownHosts.NOT_INCLUDED, kh.check("192.277.325.5", rsaKeyBytes), + "wrong key should return NOT_INCLUDED"); + assertEquals(KnownHosts.OK, kh.check("[192.277.325.5]:123", dsaKeyBytes), + "fitting key should return OK"); + assertEquals(KnownHosts.OK, kh.check("[192.277.325.5]:123", rsaKeyBytes), + "fitting key should return OK"); + + assertEquals(0, messages.size(), "no messages expected: " + getMessagesAsString()); + } + + @Test + public void testAddGetRemoveHostKeys() throws Exception { + boolean[] throwException = new boolean[1]; + Session.random = new NotSoRandomRandom(); + KnownHosts kh = new KnownHosts(jsch) { + @Override + void sync() throws IOException { + messages.add("sync"); + if (throwException[0]) { + messages.add("throw exception"); + throw new RuntimeException("dummy re"); + } + } + }; + HostKey[] hosts; + + addHosts(kh); + + hosts = kh.getHostKey(); + assertEquals(3, hosts.length, "unexpected number of host keys"); + assertEquals( + "host.example.com,192.277.325.3: key type ssh-rsa\r\n" + + "192.277.325.5: key type ssh-dss\r\n" + "[192.277.325.5]:123: key type ssh-rsa", + getHostKeysString(hosts), "unexpected hosts"); + + hosts = kh.getHostKey("nohost.example.com", null); + assertEquals(0, hosts.length, "unexpected number of host keys"); + + hosts = kh.getHostKey("host.example.com", null); + assertEquals(1, hosts.length, "unexpected number of host keys"); + assertEquals("host.example.com,192.277.325.3: key type ssh-rsa", getHostKeysString(hosts), + "unexpected hosts"); + + hosts = kh.getHostKey("192.277.325.5", null); + assertEquals(1, hosts.length, "unexpected number of host keys"); + assertEquals("192.277.325.5: key type ssh-dss", getHostKeysString(hosts), "unexpected hosts"); + + hosts = kh.getHostKey("[192.277.325.5]:123", null); + assertEquals(2, hosts.length, "unexpected number of host keys"); + assertEquals("[192.277.325.5]:123: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss", + getHostKeysString(hosts), "unexpected hosts"); + + hosts = kh.getHostKey("[192.277.325.5]:123", "ssh-dss"); + assertEquals(1, hosts.length, "unexpected number of host keys"); + assertEquals("192.277.325.5: key type ssh-dss", getHostKeysString(hosts), "unexpected hosts"); + + hosts = kh.getHostKey("[192.277.325.5:123]", "ssh-dss"); + assertEquals(0, hosts.length, "unexpected number of host keys"); + + assertEquals(0, messages.size(), "unexpected number of messages: " + getMessagesAsString()); + + kh.remove(null, null); + hosts = kh.getHostKey(); + assertEquals(0, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals("sync", getMessagesAsString(), "unexpected messages"); + + addHosts(kh); + kh.remove(null, "ssh-dsa"); + hosts = kh.getHostKey(); + assertEquals(0, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals("sync", getMessagesAsString(), "unexpected messages"); + + addHosts(kh); + kh.remove("host.example.com", null); + hosts = kh.getHostKey(); + assertEquals(3, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals( + "192.277.325.3: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss\r\n" + + "[192.277.325.5]:123: key type ssh-rsa", + getHostKeysString(hosts), "unexpected hosts"); + assertEquals("sync", getMessagesAsString(), "unexpected messages"); + + kh.remove("[192.277.325.5]:123", "ssh-dsa"); + hosts = kh.getHostKey(); + assertEquals(3, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals( + "192.277.325.3: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss\r\n" + + "[192.277.325.5]:123: key type ssh-rsa", + getHostKeysString(hosts), "unexpected hosts"); + assertEquals("", getMessagesAsString(), "unexpected messages"); + + kh.remove("[192.277.325.5]:123", "ssh-rsa", " ssh-rsa1234".getBytes(ISO_8859_1)); + hosts = kh.getHostKey(); + assertEquals(3, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals( + "192.277.325.3: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss\r\n" + + "[192.277.325.5]:123: key type ssh-rsa", + getHostKeysString(hosts), "unexpected hosts"); + assertEquals("", getMessagesAsString(), "unexpected messages"); + + kh.remove("[192.277.325.5]:123", "ssh-rsa", rsaKeyBytes); + hosts = kh.getHostKey(); + assertEquals(2, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals("192.277.325.3: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss", + getHostKeysString(hosts), "unexpected hosts"); + assertEquals("sync", getMessagesAsString(), "unexpected messages"); + + HashedHostKey hhk = kh.new HashedHostKey("hashed.example.com", rsaKeyBytes); + kh.add(hhk, null); + hosts = kh.getHostKey(); + assertEquals(3, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals("192.277.325.3: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss\r\n" + + "hashed.example.com: key type ssh-rsa", getHostKeysString(hosts), "unexpected hosts"); + assertEquals("", getMessagesAsString(), "unexpected messages"); + + kh.remove("nothashed.example.com", "ssh-rsa"); + hosts = kh.getHostKey(); + assertEquals(3, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals("192.277.325.3: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss\r\n" + + "hashed.example.com: key type ssh-rsa", getHostKeysString(hosts), "unexpected hosts"); + assertEquals("", getMessagesAsString(), "unexpected messages"); + + kh.remove("hashed.example.com", "ssh-rsa"); + hosts = kh.getHostKey(); + assertEquals(2, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals("192.277.325.3: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss", + getHostKeysString(hosts), "unexpected hosts"); + assertEquals("sync", getMessagesAsString(), "unexpected messages"); + + hhk.hash(); + kh.add(hhk, null); + hosts = kh.getHostKey(); + assertEquals(3, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals( + "192.277.325.3: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss\r\n" + + "|1|AAECAwQFBgcICQoLDA0ODxAREhM=|tfTk2zfUwEOJq8/nQE8s/gLfc58=: key type ssh-rsa", + getHostKeysString(hosts), "unexpected hosts"); + assertEquals("", getMessagesAsString(), "unexpected messages"); + + kh.remove("nothashed.example.com", "ssh-rsa"); + hosts = kh.getHostKey(); + assertEquals(3, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals( + "192.277.325.3: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss\r\n" + + "|1|AAECAwQFBgcICQoLDA0ODxAREhM=|tfTk2zfUwEOJq8/nQE8s/gLfc58=: key type ssh-rsa", + getHostKeysString(hosts), "unexpected hosts"); + assertEquals("", getMessagesAsString(), "unexpected messages"); + + kh.remove("hashed.example.com", "ssh-rsa"); + hosts = kh.getHostKey(); + assertEquals(2, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals("192.277.325.3: key type ssh-rsa\r\n" + "192.277.325.5: key type ssh-dss", + getHostKeysString(hosts), "unexpected hosts"); + assertEquals("sync", getMessagesAsString(), "unexpected messages"); + + throwException[0] = true; + kh.remove("192.277.325.5", "ssh-dss"); + hosts = kh.getHostKey(); + assertEquals(1, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals("192.277.325.3: key type ssh-rsa", getHostKeysString(hosts), "unexpected hosts"); + assertEquals("sync\r\n" + "throw exception", getMessagesAsString(), "unexpected messages"); + + kh.remove(null, null); + addHosts(kh); + hosts = kh.getHostKey(); + assertEquals(3, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals( + "host.example.com,192.277.325.3: key type ssh-rsa\r\n" + + "192.277.325.5: key type ssh-dss\r\n" + "[192.277.325.5]:123: key type ssh-rsa", + getHostKeysString(hosts), "unexpected hosts"); + assertEquals("sync\r\n" + "throw exception", getMessagesAsString(), "unexpected messages"); + + kh.remove("192.277.325.5", null); + hosts = kh.getHostKey("[192.277.325.5]:123", null); + assertEquals(1, hosts.length, "unexpected number of host keys: " + getHostKeysString(hosts)); + assertEquals("[192.277.325.5]:123: key type ssh-rsa", getHostKeysString(hosts), + "unexpected hosts"); + } + + private String getMessagesAsString() { + try { + return messages.stream().collect(Collectors.joining("\r\n")); + } finally { + messages.clear(); + } + } + + private void addHosts(KnownHosts kh) throws JSchException { + kh.add(new HostKey("host.example.com,192.277.325.3", rsaKeyBytes), null); + kh.add(new HostKey("192.277.325.5", dsaKeyBytes), null); + kh.add(new HostKey("[192.277.325.5]:123", rsaKeyBytes), null); + } + + private String getHostKeysString(HostKey[] hosts) { + return Arrays.stream(hosts).map(host -> host.getHost() + ": key type " + host.getType()) + .collect(Collectors.joining("\r\n")); + } + + private Boolean checkMessageAndReturn(String expectedMessge, String actual, boolean ret) { + assertEquals(expectedMessge, actual, "prompted message mismatch"); + return Boolean.valueOf(ret); + } + + private void checkUnhashedHostKey(HashedHostKey hhk, String expectedMarker, String expctedHost, + String expectedType, String expectedComment) { + assertEquals(expctedHost, hhk.getHost(), "host mismatch"); + assertEquals(expectedMarker, hhk.getMarker(), "marker mismatch"); + assertEquals( + "1e:b5:70:92:65:6e:6a:f9:d6:7a:a9:43:00:40:a2:e7:c8:51:35:df:ee:60:19:b7:4b:18:1d:eb:46:48:28:4b", + hhk.getFingerPrint(jsch)); + assertEquals(expectedComment, hhk.getComment(), "comment mismatch"); + assertEquals("ICAgIHNzaC1kc2E=", hhk.getKey(), "key mismatch"); + assertEquals(expectedType, hhk.getType(), "type mismatch"); + assertFalse(hhk.isHashed(), "key should report itself unhashed"); + assertNull(hhk.salt, "salt should be null"); + assertNull(hhk.hash, "hash should be null"); + } + + private void checkSHA1HashResult(HashedHostKey hhk, String expectedMarker, String expectedType, + String expectedComment) throws UnsupportedEncodingException { + assertEquals("|1|AAECAwQFBgcICQoLDA0ODxAREhM=|/pE4peaossRYDRp6bEWa348eFLI=", hhk.getHost(), + "host mismatch"); + assertEquals(expectedMarker, hhk.getMarker(), "marker mismatch"); + assertEquals( + "1e:b5:70:92:65:6e:6a:f9:d6:7a:a9:43:00:40:a2:e7:c8:51:35:df:ee:60:19:b7:4b:18:1d:eb:46:48:28:4b", + hhk.getFingerPrint(jsch)); + assertEquals(expectedComment, hhk.getComment(), "comment mismatch"); + assertEquals("ICAgIHNzaC1kc2E=", hhk.getKey(), "key mismatch"); + assertEquals(expectedType, hhk.getType(), "type mismatch"); + assertTrue(hhk.isHashed(), "key should report itself hashed"); + assertEquals("AAECAwQFBgcICQoLDA0ODxAREhM=", + new String(Util.toBase64(hhk.salt, 0, hhk.salt.length, true), ISO_8859_1), + "salt should be null"); + assertEquals("/pE4peaossRYDRp6bEWa348eFLI=", + new String(Util.toBase64(hhk.hash, 0, hhk.hash.length, true), ISO_8859_1), + "salt should be null"); + } + + private String getContent(String filename) throws IOException { + try (FileInputStream fis = new FileInputStream(filename)) { + StringBuilder sb = new StringBuilder(); + int read; + while ((read = fis.read()) != -1) { + sb.append((char) read); + } + return sb.toString(); + } + } + + private void checkResultForKeyResult(HostKey[] keys, String rsaKey, String expectedHostResult, + String expectedMarker) { + assertNotNull(keys, "actual keys expected"); + assertEquals(1, keys.length, "1 key expected"); + HostKey key = keys[0]; + assertEquals("some comment", key.getComment(), "comment mismatch"); + assertEquals( + "9d:38:5b:83:a9:17:52:92:56:1a:5e:c4:d4:81:8e:0a:ca:51:a2:64:f1:74:20:11:2e:f8:8a:c3:a1:39:49:8f", + key.getFingerPrint(jsch), "fingerprint mismatch"); + assertEquals(expectedHostResult, key.getHost(), "host mismatch"); + assertEquals(rsaKey, key.getKey(), "key mismatch"); + assertEquals(expectedMarker, key.getMarker(), "marker mismatch"); + assertEquals("ssh-rsa", key.getType(), "type mismatch"); + } + + private static class BCHMACSHA1 implements MAC { + private final Mac mac; + + private BCHMACSHA1(Mac mac) { + this.mac = mac; + } + + @Override + public void update(int foo) { + mac.update((byte) foo); + } + + @Override + public void update(byte[] foo, int start, int len) { + mac.update(foo, start, len); + } + + @Override + public void init(byte[] key) throws Exception { + SecretKey k = new SecretKeySpec(key, "hmacsha1"); + mac.init(k); + } + + @Override + public String getName() { + return mac.getAlgorithm(); + } + + @Override + public int getBlockSize() { + return mac.getMacLength(); + } + + @Override + public void doFinal(byte[] buf, int offset) { + try { + mac.doFinal(buf, offset); + } catch (ShortBufferException sbe) { + throw new RuntimeException("unable to do final", sbe); + } + } + } + + private static class NotSoRandomRandom extends com.jcraft.jsch.jce.Random { + @Override + public void fill(byte[] foo, int start, int len) { + for (int i = 0; i < len; i++) { + foo[i + start] = (byte) i; + } + } + } + + private static class TestLogger implements Logger { + private final LinkedList messages; + + private TestLogger(LinkedList messages) { + this.messages = messages; + } + + @Override + public void log(int level, String message) { + messages.add("M(" + level + "): " + message); + } + + @Override + public void log(int level, String message, Throwable cause) { + if (cause != null) { + message += "\r\n " + cause.getClass().getName() + ": " + cause.getMessage(); + } + messages.add("M(" + level + "): " + message); + } + + @Override + public boolean isEnabled(int level) { + return true; + } + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/LoggerTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/LoggerTest.java new file mode 100644 index 0000000..9fd9c20 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/LoggerTest.java @@ -0,0 +1,58 @@ +package com.jcraft.jsch.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.jcraft.jsch.Logger; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +public class LoggerTest { + + private final Exception testException = new Exception("dummy exception"); + + @Test + public void testLogging() { + List actualMessages = new ArrayList<>(); + boolean[] enabledResult = new boolean[1]; + Logger logger = new Logger() { + @Override + public void log(int level, String message) { + if (isEnabled(level)) { + actualMessages.add(level + ":" + message); + } + } + + @Override + public boolean isEnabled(int level) { + return enabledResult[0]; + } + }; + + actualMessages.clear(); + enabledResult[0] = false; + logger.log(Logger.ERROR, "debug message"); + logger.log(Logger.ERROR, "debug message with null cause", null); + logger.log(Logger.ERROR, "debug message with cause", testException); + assertEquals(Collections.emptyList(), actualMessages, "mismatch in logged messages"); + + StringWriter sw = new StringWriter(); + try (PrintWriter pw = new PrintWriter(sw, true)) { + testException.printStackTrace(pw); + } + List expectedMessages = Arrays.asList(Logger.ERROR + ":debug message", + Logger.ERROR + ":debug message with null cause", + Logger.ERROR + ":debug message with cause" + System.lineSeparator() + sw); + + actualMessages.clear(); + enabledResult[0] = true; + logger.log(Logger.ERROR, "debug message"); + logger.log(Logger.ERROR, "debug message with null cause", null); + logger.log(Logger.ERROR, "debug message with cause", testException); + assertEquals(expectedMessages, actualMessages, "mismatch in logged messages"); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/MockUserInfo.java b/files-jsch/src/test/java/com/jcraft/jsch/test/MockUserInfo.java new file mode 100644 index 0000000..7f941f7 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/MockUserInfo.java @@ -0,0 +1,51 @@ +package com.jcraft.jsch.test; + +import com.jcraft.jsch.UserInfo; +import java.util.ArrayList; +import java.util.List; + +public class MockUserInfo implements UserInfo { + + private final List messages; + + public MockUserInfo() { + messages = new ArrayList<>(); + } + + @Override + public String getPassphrase() { + return ""; + } + + @Override + public String getPassword() { + return ""; + } + + @Override + public boolean promptPassword(String message) { + messages.add(message); + return false; + } + + @Override + public boolean promptPassphrase(String message) { + messages.add(message); + return false; + } + + @Override + public boolean promptYesNo(String message) { + messages.add(message); + return false; + } + + @Override + public void showMessage(String message) { + messages.add(message); + } + + public List getMessages() { + return messages; + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/OpenSSH74ServerSigAlgsIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/OpenSSH74ServerSigAlgsIT.java new file mode 100644 index 0000000..94d9e5c --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/OpenSSH74ServerSigAlgsIT.java @@ -0,0 +1,208 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@EnabledOnOs(architectures = "x86_64") +@Testcontainers +public class OpenSSH74ServerSigAlgsIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(OpenSSH74ServerSigAlgsIT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.openssh74")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + public void testServerSigAlgs() throws Exception { + String algos = + "ssh-rsa-sha512@ssh.com,ssh-rsa-sha384@ssh.com,ssh-rsa-sha256@ssh.com,ssh-rsa-sha224@ssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", algos); + session.setConfig("server_host_key", algos); + doSftp(session, true); + + String expectedKex = "kex: host key algorithm: rsa-sha2-512"; + String expectedExtInfo = "SSH_MSG_EXT_INFO received"; + String expectedServerSigAlgs = + "server-sig-algs="; + String expectedOpenSSH74Bug = + "OpenSSH 7.4 detected: adding rsa-sha2-256 & rsa-sha2-512 to server-sig-algs"; + String expectedPubkeysKnown = + "PubkeyAcceptedAlgorithms in server-sig-algs = \\[rsa-sha2-512, rsa-sha2-256, ssh-rsa\\]"; + String expectedPubkeysUnknown = + "PubkeyAcceptedAlgorithms not in server-sig-algs = \\[ssh-rsa-sha512@ssh.com, ssh-rsa-sha384@ssh.com, ssh-rsa-sha256@ssh.com, ssh-rsa-sha224@ssh.com\\]"; + String expectedPreauth = "rsa-sha2-512 preauth success"; + String expectedAuth = "rsa-sha2-512 auth success"; + checkLogs(expectedKex); + checkLogs(expectedExtInfo); + checkLogs(expectedServerSigAlgs); + checkLogs(expectedOpenSSH74Bug); + checkLogs(expectedPubkeysKnown); + checkLogs(expectedPubkeysUnknown); + checkLogs(expectedPreauth); + checkLogs(expectedAuth); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/OpenSSHConfigTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/OpenSSHConfigTest.java new file mode 100644 index 0000000..94fc7fe --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/OpenSSHConfigTest.java @@ -0,0 +1,115 @@ +package com.jcraft.jsch.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import com.jcraft.jsch.ConfigRepository; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.OpenSSHConfig; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class OpenSSHConfigTest { + + Map keyMap = OpenSSHConfig.getKeymap().entrySet().stream().collect(Collectors + .toMap(entry -> entry.getValue().toUpperCase(Locale.ROOT), Map.Entry::getKey, (s, s2) -> s2)); + + @Test + void parseFile() throws IOException, URISyntaxException { + final String configFile = + Paths.get(ClassLoader.getSystemResource("config").toURI()).toFile().getAbsolutePath(); + final OpenSSHConfig openSSHConfig = OpenSSHConfig.parseFile(configFile); + final ConfigRepository.Config config = openSSHConfig.getConfig("host2"); + assertNotNull(config); + assertEquals("foobar", config.getUser()); + assertEquals("host2.somewhere.edu", config.getHostname()); + assertEquals("~/.ssh/old_keys/host2_key", config.getValue("IdentityFile")); + } + + @ParameterizedTest + @ValueSource(strings = {"MACs", "Macs"}) + void parseMacsCaseInsensitive(String key) throws IOException { + OpenSSHConfig parse = OpenSSHConfig.parse(key + " someValue"); + ConfigRepository.Config config = parse.getConfig(""); + assertEquals("someValue", config.getValue("mac.c2s")); + assertEquals("someValue", config.getValue("mac.s2c")); + } + + @Test + void appendKexAlgorithms() throws IOException { + OpenSSHConfig parse = OpenSSHConfig.parse("KexAlgorithms +diffie-hellman-group1-sha1"); + ConfigRepository.Config kex = parse.getConfig(""); + assertEquals(JSch.getConfig("kex") + "," + "diffie-hellman-group1-sha1", kex.getValue("kex")); + } + + @ParameterizedTest + @ValueSource(strings = {"KexAlgorithms", "Ciphers", "HostKeyAlgorithms", "MACs", + "PubkeyAcceptedAlgorithms", "PubkeyAcceptedKeyTypes"}) + void appendAlgorithms(String key) throws IOException { + OpenSSHConfig parse = OpenSSHConfig.parse(key + " +someValue,someValue1"); + ConfigRepository.Config config = parse.getConfig(""); + String mappedKey = Optional.ofNullable(keyMap.get(key.toUpperCase(Locale.ROOT))).orElse(key); + assertEquals(JSch.getConfig(mappedKey) + "," + "someValue,someValue1", + config.getValue(mappedKey)); + } + + @ParameterizedTest + @ValueSource(strings = {"KexAlgorithms", "Ciphers", "HostKeyAlgorithms", "MACs", + "PubkeyAcceptedAlgorithms", "PubkeyAcceptedKeyTypes"}) + void prependAlgorithms(String key) throws IOException { + OpenSSHConfig parse = OpenSSHConfig.parse(key + " ^someValue,someValue1"); + ConfigRepository.Config config = parse.getConfig(""); + String mappedKey = Optional.ofNullable(keyMap.get(key.toUpperCase(Locale.ROOT))).orElse(key); + assertEquals("someValue,someValue1," + JSch.getConfig(mappedKey), config.getValue(mappedKey)); + } + + @Test + void prependKexAlgorithms() throws IOException { + OpenSSHConfig parse = OpenSSHConfig.parse("KexAlgorithms ^diffie-hellman-group1-sha1"); + ConfigRepository.Config kex = parse.getConfig(""); + assertEquals("diffie-hellman-group1-sha1," + JSch.getConfig("kex"), kex.getValue("kex")); + } + + @Test + void removeKexAlgorithm() throws IOException { + OpenSSHConfig parse = OpenSSHConfig.parse("KexAlgorithms -ecdh-sha2-nistp256"); + ConfigRepository.Config kex = parse.getConfig(""); + assertEquals(JSch.getConfig("kex").replaceAll(",ecdh-sha2-nistp256", ""), kex.getValue("kex")); + } + + @Test + void replaceKexAlgorithms() throws IOException { + OpenSSHConfig parse = OpenSSHConfig.parse("KexAlgorithms diffie-hellman-group1-sha1"); + ConfigRepository.Config kex = parse.getConfig(""); + assertEquals("diffie-hellman-group1-sha1", kex.getValue("kex")); + } + + @Test + void parseFileWithNegations() throws IOException, URISyntaxException { + final String configFile = + Paths.get(ClassLoader.getSystemResource("config_with_negations").toURI()).toFile() + .getAbsolutePath(); + final OpenSSHConfig openSSHConfig = OpenSSHConfig.parseFile(configFile); + + assertUserEquals(openSSHConfig, "my.example.com", "u1"); + assertUserEquals(openSSHConfig, "my-jump.example.com", "jump-u1"); + assertUserEquals(openSSHConfig, "my-proxy.example.com", "proxy-u1"); + assertUserEquals(openSSHConfig, "my.example.org", "u2"); + } + + private void assertUserEquals(OpenSSHConfig openSSHConfig, String host, String expected) { + final ConfigRepository.Config config = openSSHConfig.getConfig(host); + assertNotNull(config); + String actual = config.getUser(); + assertEquals(expected, actual, String.format(Locale.ROOT, + "Expected user for host %s to be %s, but was %s", host, expected, actual)); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize128IT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize128IT.java new file mode 100644 index 0000000..b491575 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize128IT.java @@ -0,0 +1,41 @@ +package com.jcraft.jsch.test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class PktSize128IT extends AbstractBufferMargin { + + public PktSize128IT() { + super(128); + } + + @ParameterizedTest + @CsvSource(value = { + // 16 byte tag (MAC doesn't matter) + "aes128-gcm@openssh.com,hmac-sha1,none", "aes128-gcm@openssh.com,hmac-sha1,zlib@openssh.com", + // 12 byte MAC + "aes128-ctr,hmac-md5-96,none", "aes128-ctr,hmac-md5-96,zlib@openssh.com", + // 16 byte MAC + "aes128-ctr,hmac-md5,none", "aes128-ctr,hmac-md5,zlib@openssh.com", + // 20 byte MAC + "aes128-ctr,hmac-sha1,none", "aes128-ctr,hmac-sha1,zlib@openssh.com"}) + public void testSftp(String cipher, String mac, String compression) throws Exception { + doTestSftp(cipher, mac, compression); + } + + @ParameterizedTest + @CsvSource(value = { + // 16 byte tag (MAC doesn't matter) + "aes128-gcm@openssh.com,hmac-sha1,none", "aes128-gcm@openssh.com,hmac-sha1,zlib@openssh.com", + // 12 byte MAC + "aes128-ctr,hmac-md5-96,none", "aes128-ctr,hmac-md5-96,zlib@openssh.com", + // 16 byte MAC + "aes128-ctr,hmac-md5,none", "aes128-ctr,hmac-md5,zlib@openssh.com", + // 20 byte MAC + "aes128-ctr,hmac-sha1,none", "aes128-ctr,hmac-sha1,zlib@openssh.com", + // 32 byte MAC + "aes128-ctr,hmac-sha2-256,none", "aes128-ctr,hmac-sha2-256,zlib@openssh.com"}) + public void testScp(String cipher, String mac, String compression) throws Exception { + doTestScp(cipher, mac, compression); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize160IT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize160IT.java new file mode 100644 index 0000000..600f4f5 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize160IT.java @@ -0,0 +1,27 @@ +package com.jcraft.jsch.test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class PktSize160IT extends AbstractBufferMargin { + + public PktSize160IT() { + super(160); + } + + @ParameterizedTest + @CsvSource(value = { + // 32 byte MAC + "aes128-ctr,hmac-sha2-256,none", "aes128-ctr,hmac-sha2-256,zlib@openssh.com"}) + public void testSftp(String cipher, String mac, String compression) throws Exception { + doTestSftp(cipher, mac, compression); + } + + @ParameterizedTest + @CsvSource(value = { + // 64 byte MAC + "aes128-ctr,hmac-sha2-512,none", "aes128-ctr,hmac-sha2-512,zlib@openssh.com"}) + public void testScp(String cipher, String mac, String compression) throws Exception { + doTestScp(cipher, mac, compression); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize192IT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize192IT.java new file mode 100644 index 0000000..67e9b08 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize192IT.java @@ -0,0 +1,19 @@ +package com.jcraft.jsch.test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class PktSize192IT extends AbstractBufferMargin { + + public PktSize192IT() { + super(192); + } + + @ParameterizedTest + @CsvSource(value = { + // 64 byte MAC + "aes128-ctr,hmac-sha2-512,none", "aes128-ctr,hmac-sha2-512,zlib@openssh.com"}) + public void testSftp(String cipher, String mac, String compression) throws Exception { + doTestSftp(cipher, mac, compression); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize32768IT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize32768IT.java new file mode 100644 index 0000000..408da43 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/PktSize32768IT.java @@ -0,0 +1,47 @@ +package com.jcraft.jsch.test; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public class PktSize32768IT extends AbstractBufferMargin { + + public PktSize32768IT() { + super(32768); + } + + @ParameterizedTest + @CsvSource(value = { + // 16 byte tag (MAC doesn't matter) + "aes128-gcm@openssh.com,hmac-sha1,none", "aes128-gcm@openssh.com,hmac-sha1,zlib@openssh.com", + // 12 byte MAC + "aes128-ctr,hmac-md5-96,none", "aes128-ctr,hmac-md5-96,zlib@openssh.com", + // 16 byte MAC + "aes128-ctr,hmac-md5,none", "aes128-ctr,hmac-md5,zlib@openssh.com", + // 20 byte MAC + "aes128-ctr,hmac-sha1,none", "aes128-ctr,hmac-sha1,zlib@openssh.com", + // 32 byte MAC + "aes128-ctr,hmac-sha2-256,none", "aes128-ctr,hmac-sha2-256,zlib@openssh.com", + // 64 byte MAC + "aes128-ctr,hmac-sha2-512,none", "aes128-ctr,hmac-sha2-512,zlib@openssh.com"}) + public void testSftp(String cipher, String mac, String compression) throws Exception { + doTestSftp(cipher, mac, compression); + } + + @ParameterizedTest + @CsvSource(value = { + // 16 byte tag (MAC doesn't matter) + "aes128-gcm@openssh.com,hmac-sha1,none", "aes128-gcm@openssh.com,hmac-sha1,zlib@openssh.com", + // 12 byte MAC + "aes128-ctr,hmac-md5-96,none", "aes128-ctr,hmac-md5-96,zlib@openssh.com", + // 16 byte MAC + "aes128-ctr,hmac-md5,none", "aes128-ctr,hmac-md5,zlib@openssh.com", + // 20 byte MAC + "aes128-ctr,hmac-sha1,none", "aes128-ctr,hmac-sha1,zlib@openssh.com", + // 32 byte MAC + "aes128-ctr,hmac-sha2-256,none", "aes128-ctr,hmac-sha2-256,zlib@openssh.com", + // 64 byte MAC + "aes128-ctr,hmac-sha2-512,none", "aes128-ctr,hmac-sha2-512,zlib@openssh.com"}) + public void testScp(String cipher, String mac, String compression) throws Exception { + doTestScp(cipher, mac, compression); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/ResourceUtil.java b/files-jsch/src/test/java/com/jcraft/jsch/test/ResourceUtil.java new file mode 100644 index 0000000..5c08a04 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/ResourceUtil.java @@ -0,0 +1,13 @@ +package com.jcraft.jsch.test; + +import java.io.File; + +public class ResourceUtil { + public static String getResourceFile(Class clazz, String fileName) { + String path = clazz.getClassLoader().getResource(fileName).getFile(); + // Note: on Windows the returned path can be in the form: /C:/ + // to strip the initial / in a platform independent way we need to + // create a java.io.File and take it's absolute path + return new File(path).getAbsolutePath(); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/SSHAgentIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/SSHAgentIT.java new file mode 100644 index 0000000..65d8b82 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/SSHAgentIT.java @@ -0,0 +1,415 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.condition.JRE.JAVA_16; +import static org.junit.jupiter.api.condition.OS.LINUX; +import static org.testcontainers.containers.BindMode.READ_WRITE; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.AgentIdentityRepository; +import com.jcraft.jsch.AgentProxy; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.IdentityRepository; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.SSHAgentConnector; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.USocketFactory; +import com.jcraft.jsch.UnixDomainSocketFactory; +import com.sun.jna.platform.unix.LibC; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledForJreRange; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@EnabledOnOs(LINUX) +@Testcontainers +public class SSHAgentIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = TestLoggerFactory.getTestLogger(SSHAgentIT.class); + private static final TestLogger sshAgentLogger = + TestLoggerFactory.getTestLogger(AgentProxy.class); + @TempDir + public static Path tmpDir; + private static String testuid; + private static String testgid; + private static Path sshAgentSock; + + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + private Slf4jLogConsumer sshAgentLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile")) + .withExposedPorts(22); + + @Container + public GenericContainer sshAgent = new GenericContainer<>( + new ImageFromDockerfile().withBuildArg("testuid", testuid).withBuildArg("testgid", testgid) + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.sshagent")) + .withFileSystemBind(sshAgentSock.getParent().toString(), "/testuser", READ_WRITE); + + @BeforeAll + public static void beforeAll() throws IOException { + JSch.setLogger(new Slf4jLogger()); + LibC libc = LibC.INSTANCE; + testuid = Integer.toString(libc.getuid()); + testgid = Integer.toString(libc.getgid()); + Path temp = Files.createTempDirectory(tmpDir, "sshagent"); + sshAgentSock = temp.resolve("sock"); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + if (sshAgentLogConsumer == null) { + sshAgentLogConsumer = new Slf4jLogConsumer(sshAgentLogger); + sshAgent.followOutput(sshAgentLogConsumer); + } + + Path temp = Files.createTempDirectory(tmpDir, "sshd"); + in = temp.resolve("in"); + out = temp.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + sshAgentLogger.clearAll(); + } + + @AfterEach + public void afterEach() { + try { + Files.deleteIfExists(sshAgentSock); + } catch (IOException ignore) { + } + + try { + Files.deleteIfExists(out); + } catch (IOException ignore) { + } + + try { + Files.deleteIfExists(in); + } catch (IOException ignore) { + } + + try { + Files.deleteIfExists(in.getParent()); + } catch (IOException ignore) { + } + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + sshAgentLogger.clearAll(); + + try { + Files.deleteIfExists(sshAgentSock.getParent()); + } catch (IOException ignore) { + } + } + + @Test + public void testEd25519JUnixSocketFactory() throws Exception { + JSch ssh = createEd25519Identity(new JUnixSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-ed25519"); + doSftp(session, true); + } + + @Test + public void testECDSA521JUnixSocketFactory() throws Exception { + JSch ssh = createECDSA521Identity(new JUnixSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ecdsa-sha2-nistp521"); + doSftp(session, true); + } + + @Test + public void testECDSA384JUnixSocketFactory() throws Exception { + JSch ssh = createECDSA384Identity(new JUnixSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ecdsa-sha2-nistp384"); + doSftp(session, true); + } + + @Test + public void testECDSA256JUnixSocketFactory() throws Exception { + JSch ssh = createECDSA256Identity(new JUnixSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ecdsa-sha2-nistp256"); + doSftp(session, true); + } + + @ParameterizedTest + @ValueSource(strings = {"rsa-sha2-512", "rsa-sha2-256", "ssh-rsa"}) + public void testRSAJUnixSocketFactory(String keyType) throws Exception { + JSch ssh = createRSAIdentity(new JUnixSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", keyType); + doSftp(session, true); + } + + @Test + public void testDSAJUnixSocketFactory() throws Exception { + JSch ssh = createDSAIdentity(new JUnixSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-dss"); + doSftp(session, true); + } + + @Test + @EnabledForJreRange(min = JAVA_16) + public void testEd25519UnixDomainSocketFactory() throws Exception { + JSch ssh = createEd25519Identity(new UnixDomainSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-ed25519"); + doSftp(session, true); + } + + @Test + @EnabledForJreRange(min = JAVA_16) + public void testECDSA521UnixDomainSocketFactory() throws Exception { + JSch ssh = createECDSA521Identity(new UnixDomainSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ecdsa-sha2-nistp521"); + doSftp(session, true); + } + + @Test + @EnabledForJreRange(min = JAVA_16) + public void testECDSA384UnixDomainSocketFactory() throws Exception { + JSch ssh = createECDSA384Identity(new UnixDomainSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ecdsa-sha2-nistp384"); + doSftp(session, true); + } + + @Test + @EnabledForJreRange(min = JAVA_16) + public void testECDSA256UnixDomainSocketFactory() throws Exception { + JSch ssh = createECDSA256Identity(new UnixDomainSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ecdsa-sha2-nistp256"); + doSftp(session, true); + } + + @ParameterizedTest + @ValueSource(strings = {"rsa-sha2-512", "rsa-sha2-256", "ssh-rsa"}) + @EnabledForJreRange(min = JAVA_16) + public void testRSAUnixDomainSocketFactory(String keyType) throws Exception { + JSch ssh = createRSAIdentity(new UnixDomainSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", keyType); + doSftp(session, true); + } + + @Test + @EnabledForJreRange(min = JAVA_16) + public void testDSAUnixDomainSocketFactory() throws Exception { + JSch ssh = createDSAIdentity(new UnixDomainSocketFactory()); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", "ssh-dss"); + doSftp(session, true); + } + + private JSch createRSAIdentity(USocketFactory factory) throws Exception { + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); + + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.setIdentityRepository(ir); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + assertEquals(1, ir.getIdentities().size()); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createECDSA256Identity(USocketFactory factory) throws Exception { + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); + + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.setIdentityRepository(ir); + ssh.addIdentity(getResourceFile("docker/id_ecdsa256"), + getResourceFile("docker/id_ecdsa256.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createECDSA384Identity(USocketFactory factory) throws Exception { + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); + + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.setIdentityRepository(ir); + ssh.addIdentity(getResourceFile("docker/id_ecdsa384"), + getResourceFile("docker/id_ecdsa384.pub"), null); + assertEquals(1, ir.getIdentities().size()); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createECDSA521Identity(USocketFactory factory) throws Exception { + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); + + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.setIdentityRepository(ir); + ssh.addIdentity(getResourceFile("docker/id_ecdsa521"), + getResourceFile("docker/id_ecdsa521.pub"), null); + assertEquals(1, ir.getIdentities().size()); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createDSAIdentity(USocketFactory factory) throws Exception { + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); + + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.setIdentityRepository(ir); + ssh.addIdentity(getResourceFile("docker/id_dsa"), getResourceFile("docker/id_dsa.pub"), null); + assertEquals(1, ir.getIdentities().size()); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private JSch createEd25519Identity(USocketFactory factory) throws Exception { + IdentityRepository ir = + new AgentIdentityRepository(new SSHAgentConnector(factory, sshAgentSock)); + assertTrue(ir.getIdentities().isEmpty(), "ssh-agent empty"); + + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.setIdentityRepository(ir); + ssh.addIdentity(getResourceFile("docker/id_ed25519"), getResourceFile("docker/id_ed25519.pub"), + null); + assertEquals(1, ir.getIdentities().size()); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshAgentLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/ServerSigAlgsIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/ServerSigAlgsIT.java new file mode 100644 index 0000000..f09a565 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/ServerSigAlgsIT.java @@ -0,0 +1,232 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class ServerSigAlgsIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(ServerSigAlgsIT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + public void testServerSigAlgs() throws Exception { + String algos = + "ssh-rsa-sha512@ssh.com,ssh-rsa-sha384@ssh.com,ssh-rsa-sha256@ssh.com,ssh-rsa-sha224@ssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("PubkeyAcceptedAlgorithms", algos); + session.setConfig("server_host_key", algos); + doSftp(session, true); + + String expectedKex = "kex: host key algorithm: rsa-sha2-512"; + String expectedExtInfo = "SSH_MSG_EXT_INFO received"; + String expectedServerSigAlgs = + "server-sig-algs="; + String expectedPubkeysKnown = + "PubkeyAcceptedAlgorithms in server-sig-algs = \\[rsa-sha2-512, rsa-sha2-256, ssh-rsa\\]"; + String expectedPubkeysUnknown = + "PubkeyAcceptedAlgorithms not in server-sig-algs = \\[ssh-rsa-sha512@ssh.com, ssh-rsa-sha384@ssh.com, ssh-rsa-sha256@ssh.com, ssh-rsa-sha224@ssh.com\\]"; + String expectedPreauth = "rsa-sha2-512 preauth success"; + String expectedAuth = "rsa-sha2-512 auth success"; + checkLogs(expectedKex); + checkLogs(expectedExtInfo); + checkLogs(expectedServerSigAlgs); + checkLogs(expectedPubkeysKnown); + checkLogs(expectedPubkeysUnknown); + checkLogs(expectedPreauth); + checkLogs(expectedAuth); + } + + @Test + public void testNoServerSigAlgs() throws Exception { + String algos = + "ssh-rsa-sha512@ssh.com,ssh-rsa-sha384@ssh.com,ssh-rsa-sha256@ssh.com,ssh-rsa-sha224@ssh.com,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_server_sig_algs", "no"); + session.setConfig("PubkeyAcceptedAlgorithms", algos); + session.setConfig("server_host_key", algos); + doSftp(session, true); + + String expectedKex = "kex: host key algorithm: rsa-sha2-512"; + // String expectedPubkeysNoServerSigs = String.format(Locale.ROOT, + // "No server-sig-algs found, using PubkeyAcceptedAlgorithms = %s", algos); + String expectedPreauthFail1 = "ssh-rsa-sha512@ssh.com preauth failure"; + String expectedPreauthFail2 = "ssh-rsa-sha384@ssh.com preauth failure"; + String expectedPreauthFail3 = "ssh-rsa-sha256@ssh.com preauth failure"; + String expectedPreauthFail4 = "ssh-rsa-sha224@ssh.com preauth failure"; + String expectedPreauth = "rsa-sha2-512 preauth success"; + String expectedAuth = "rsa-sha2-512 auth success"; + checkLogs(expectedKex); + checkLogs(expectedPreauthFail1); + checkLogs(expectedPreauthFail2); + checkLogs(expectedPreauthFail3); + checkLogs(expectedPreauthFail4); + checkLogs(expectedPreauth); + checkLogs(expectedAuth); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/SessionReconnectIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/SessionReconnectIT.java new file mode 100644 index 0000000..652dc07 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/SessionReconnectIT.java @@ -0,0 +1,167 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchAlgoNegoFailException; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class SessionReconnectIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(SessionReconnectIT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>(new ImageFromDockerfile() + .withFileFromClasspath("dropbear_rsa_host_key", "docker/dropbear_rsa_host_key") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.dropbear")).withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + public void testReconnectWithExtraAlgorithms() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + try { + doSftp(session, false); + fail("exception expected"); + } catch (JSchAlgoNegoFailException e) { + // Dropbear does not support rsa-sha2-512/rsa-sha2-256, so add ssh-rsa + String serverHostKey = session.getConfig("server_host_key") + ",ssh-rsa"; + String pubkeyAcceptedAlgorithms = session.getConfig("PubkeyAcceptedAlgorithms") + ",ssh-rsa"; + session.setConfig("server_host_key", serverHostKey); + session.setConfig("PubkeyAcceptedAlgorithms", pubkeyAcceptedAlgorithms); + doSftp(session, true); + } + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/SessionTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/SessionTest.java new file mode 100644 index 0000000..f8934ea --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/SessionTest.java @@ -0,0 +1,91 @@ +package com.jcraft.jsch.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; + +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.JSchException; +import com.jcraft.jsch.Logger; +import com.jcraft.jsch.Session; +import com.jcraft.jsch.test.JSchTest.TestLogger; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class SessionTest { + + static JSch jsch = new JSch(); + + @ParameterizedTest + @MethodSource("sshConfigs") + void parseForwarding(String sshConfig, String expectedBindAddress, String expectedHost, + int expectedHostPort, String expectedSocket) throws JSchException { + final Session session = new Session(jsch, null, null, 0); + final Session.Forwarding forwarding = session.parseForwarding(sshConfig); + assertEquals(expectedBindAddress, forwarding.bind_address); + assertEquals(42, forwarding.port); + assertEquals(expectedHost, forwarding.host); + assertEquals(expectedHostPort, forwarding.hostport); + assertEquals(expectedSocket, forwarding.socketPath); + } + + private static Stream sshConfigs() { + return Stream.of(Arguments.of("bind_address:42:host:99", "bind_address", "host", 99, null), // colon + Arguments.of("bind_address:42 host:99", "bind_address", "host", 99, null), // blank + Arguments.of("42:host:99", "127.0.0.1", "host", 99, null), // colon wo bind + Arguments.of("42 host:99", "127.0.0.1", "host", 99, null), // blank wo bind + Arguments.of("localhost:42 host:99", "127.0.0.1", "host", 99, null), // blank + Arguments.of(":42 host:99", "0.0.0.0", "host", 99, null), // bind is empty + Arguments.of("*:42 host:99", "0.0.0.0", "host", 99, null), // bind is asterisk + Arguments.of("bind_adress:42 socket", "bind_adress", null, -1, "socket"), // socket + Arguments.of("42 socket", "127.0.0.1", null, -1, "socket") // socket wo bind + ); + } + + @Test + void getPubkeyAcceptedKeyTypes() throws JSchException { + Session session = new Session(jsch, null, null, 0); + session.setConfig("PubkeyAcceptedAlgorithms", "SessionTest111"); + assertEquals("SessionTest111", session.getConfig("PubkeyAcceptedKeyTypes")); + assertEquals("SessionTest111", session.getConfig("PubkeyAcceptedAlgorithms")); + } + + @Test + void setPubkeyAcceptedKeyTypes() throws JSchException { + Session session = new Session(jsch, null, null, 0); + session.setConfig("PubkeyAcceptedKeyTypes", "SessionTest222"); + assertEquals("SessionTest222", session.getConfig("PubkeyAcceptedKeyTypes")); + assertEquals("SessionTest222", session.getConfig("PubkeyAcceptedAlgorithms")); + } + + @Test + void checkLoggerFunctionality() throws Exception { + Logger orgLogger = JSch.getLogger(); + try { + JSch.setLogger(null); + TestLogger staticLogger = new TestLogger(); + TestLogger jschInstanceLogger = new TestLogger(); + TestLogger sessionLogger = new TestLogger(); + + Session session = new Session(jsch, null, null, 0); + + assertSame(JSch.DEVNULL, session.getLogger(), "DEVNULL logger expected after creation"); + + JSch.setLogger(staticLogger); + assertSame(staticLogger, session.getLogger(), + "static logger expected after setting static logger"); + + jsch.setInstanceLogger(jschInstanceLogger); + assertSame(jschInstanceLogger, session.getLogger(), + "static logger expected after setting instance logger"); + + session.setLogger(sessionLogger); + assertSame(sessionLogger, session.getLogger(), + "static logger expected after setting session logger"); + } finally { + JSch.setLogger(orgLogger); + } + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/SftpATTRSTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/SftpATTRSTest.java new file mode 100644 index 0000000..c10e398 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/SftpATTRSTest.java @@ -0,0 +1,39 @@ +package com.jcraft.jsch.test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.jcraft.jsch.SftpATTRS; +import java.util.Date; +import java.util.Random; +import org.junit.jupiter.api.Test; + +public class SftpATTRSTest { + + private final Random random = new Random(); + + @Test + public void testToDateString0() { + String expected = new Date(0L).toString(); + String actual = SftpATTRS.toDateString(0L); + assertEquals(expected, actual); + } + + @Test + public void testToDateStringNow() { + long now = System.currentTimeMillis() / 1000L; + String expected = new Date(now * 1000L).toString(); + String actual = SftpATTRS.toDateString(now); + assertEquals(expected, actual); + } + + @Test + public void testToDateStringRandom() { + for (int i = 0; i < 1000000; i++) { + int j = random.ints(Integer.MIN_VALUE, Integer.MAX_VALUE).findFirst().getAsInt(); + long l = Integer.toUnsignedLong(j); + String expected = new Date(l * 1000L).toString(); + String actual = SftpATTRS.toDateString(l); + assertEquals(expected, actual); + } + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/StrictKexIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/StrictKexIT.java new file mode 100644 index 0000000..a2cd543 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/StrictKexIT.java @@ -0,0 +1,274 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class StrictKexIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(ServerSigAlgsIT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config.openssh96") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile.openssh96")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + public void testEnableStrictKexNoRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "yes"); + session.setConfig("require_strict_kex", "no"); + doSftp(session, true); + + String expectedServerKex = "server proposal: KEX algorithms: .*,kex-strict-s-v00@openssh.com"; + String expectedClientKex = "client proposal: KEX algorithms: .*,kex-strict-c-v00@openssh.com"; + String expected1 = "Doing strict KEX"; + String expected2 = + "Reset outgoing sequence number after sending SSH_MSG_NEWKEYS for strict KEX"; + String expected3 = + "Reset incoming sequence number after receiving SSH_MSG_NEWKEYS for strict KEX"; + checkLogs(expectedServerKex); + checkLogs(expectedClientKex); + checkLogs(expected1); + checkLogs(expected2); + checkLogs(expected3); + } + + @Test + public void testEnableStrictKexRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "yes"); + session.setConfig("require_strict_kex", "yes"); + doSftp(session, true); + + String expectedServerKex = "server proposal: KEX algorithms: .*,kex-strict-s-v00@openssh.com"; + String expectedClientKex = "client proposal: KEX algorithms: .*,kex-strict-c-v00@openssh.com"; + String expected1 = "Doing strict KEX"; + String expected2 = + "Reset outgoing sequence number after sending SSH_MSG_NEWKEYS for strict KEX"; + String expected3 = + "Reset incoming sequence number after receiving SSH_MSG_NEWKEYS for strict KEX"; + checkLogs(expectedServerKex); + checkLogs(expectedClientKex); + checkLogs(expected1); + checkLogs(expected2); + checkLogs(expected3); + } + + @Test + public void testNoEnableStrictKexRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "no"); + session.setConfig("require_strict_kex", "yes"); + doSftp(session, true); + + String expectedServerKex = "server proposal: KEX algorithms: .*,kex-strict-s-v00@openssh.com"; + String expectedClientKex = "client proposal: KEX algorithms: .*,kex-strict-c-v00@openssh.com"; + String expected1 = "Doing strict KEX"; + String expected2 = + "Reset outgoing sequence number after sending SSH_MSG_NEWKEYS for strict KEX"; + String expected3 = + "Reset incoming sequence number after receiving SSH_MSG_NEWKEYS for strict KEX"; + checkLogs(expectedServerKex); + checkLogs(expectedClientKex); + checkLogs(expected1); + checkLogs(expected2); + checkLogs(expected3); + } + + @Test + public void testNoEnableStrictKexNoRequireStrictKex() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_strict_kex", "no"); + session.setConfig("require_strict_kex", "no"); + doSftp(session, true); + + String expectedServerKex = "server proposal: KEX algorithms: .*,kex-strict-s-v00@openssh.com"; + String expectedClientKex = "client proposal: KEX algorithms: .*,kex-strict-c-v00@openssh.com"; + String expected1 = "Doing strict KEX"; + String expected2 = + "Reset outgoing sequence number after sending SSH_MSG_NEWKEYS for strict KEX"; + String expected3 = + "Reset incoming sequence number after receiving SSH_MSG_NEWKEYS for strict KEX"; + checkLogs(expectedServerKex); + checkNoLogs(expectedClientKex); + checkNoLogs(expected1); + checkNoLogs(expected2); + checkNoLogs(expected3); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private void checkLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertTrue(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private void checkNoLogs(String expected) { + Optional actualJsch = jschLogger.getAllLoggingEvents().stream() + .map(LoggingEvent::getFormattedMessage).filter(msg -> msg.matches(expected)).findFirst(); + try { + assertFalse(actualJsch.isPresent(), () -> "JSch: " + expected); + } catch (AssertionError e) { + printInfo(); + throw e; + } + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/UserAuthIT.java b/files-jsch/src/test/java/com/jcraft/jsch/test/UserAuthIT.java new file mode 100644 index 0000000..cef62fa --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/UserAuthIT.java @@ -0,0 +1,200 @@ +package com.jcraft.jsch.test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.github.valfirst.slf4jtest.LoggingEvent; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.jcraft.jsch.ChannelSftp; +import com.jcraft.jsch.HostKey; +import com.jcraft.jsch.JSch; +import com.jcraft.jsch.Session; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Base64; +import java.util.List; +import java.util.Locale; +import java.util.Random; +import org.apache.commons.codec.digest.DigestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Testcontainers +public class UserAuthIT { + + private static final int timeout = 2000; + private static final DigestUtils sha256sum = new DigestUtils(DigestUtils.getSha256Digest()); + private static final TestLogger jschLogger = TestLoggerFactory.getTestLogger(JSch.class); + private static final TestLogger sshdLogger = + TestLoggerFactory.getTestLogger(ServerSigAlgsIT.class); + + @TempDir + public Path tmpDir; + private Path in; + private Path out; + private String hash; + private Slf4jLogConsumer sshdLogConsumer; + + @Container + public GenericContainer sshd = new GenericContainer<>( + new ImageFromDockerfile().withFileFromClasspath("ssh_host_rsa_key", "docker/ssh_host_rsa_key") + .withFileFromClasspath("ssh_host_rsa_key.pub", "docker/ssh_host_rsa_key.pub") + .withFileFromClasspath("ssh_host_ecdsa256_key", "docker/ssh_host_ecdsa256_key") + .withFileFromClasspath("ssh_host_ecdsa256_key.pub", "docker/ssh_host_ecdsa256_key.pub") + .withFileFromClasspath("ssh_host_ecdsa384_key", "docker/ssh_host_ecdsa384_key") + .withFileFromClasspath("ssh_host_ecdsa384_key.pub", "docker/ssh_host_ecdsa384_key.pub") + .withFileFromClasspath("ssh_host_ecdsa521_key", "docker/ssh_host_ecdsa521_key") + .withFileFromClasspath("ssh_host_ecdsa521_key.pub", "docker/ssh_host_ecdsa521_key.pub") + .withFileFromClasspath("ssh_host_ed25519_key", "docker/ssh_host_ed25519_key") + .withFileFromClasspath("ssh_host_ed25519_key.pub", "docker/ssh_host_ed25519_key.pub") + .withFileFromClasspath("ssh_host_dsa_key", "docker/ssh_host_dsa_key") + .withFileFromClasspath("ssh_host_dsa_key.pub", "docker/ssh_host_dsa_key.pub") + .withFileFromClasspath("sshd_config", "docker/sshd_config") + .withFileFromClasspath("authorized_keys", "docker/authorized_keys") + .withFileFromClasspath("Dockerfile", "docker/Dockerfile")) + .withExposedPorts(22); + + @BeforeAll + public static void beforeAll() { + JSch.setLogger(new Slf4jLogger()); + } + + @BeforeEach + public void beforeEach() throws IOException { + if (sshdLogConsumer == null) { + sshdLogConsumer = new Slf4jLogConsumer(sshdLogger); + sshd.followOutput(sshdLogConsumer); + } + + in = tmpDir.resolve("in"); + out = tmpDir.resolve("out"); + Files.createFile(in); + try (OutputStream os = Files.newOutputStream(in)) { + byte[] data = new byte[1024]; + for (int i = 0; i < 1024 * 100; i += 1024) { + new Random().nextBytes(data); + os.write(data); + } + } + hash = sha256sum.digestAsHex(in); + + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @AfterAll + public static void afterAll() { + JSch.setLogger(null); + jschLogger.clearAll(); + sshdLogger.clearAll(); + } + + @Test + public void testAuthNoneEnabledPubkeyAuthQueryEnabled() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_auth_none", "yes"); + session.setConfig("enable_pubkey_auth_query", "yes"); + doSftp(session, true); + } + + @Test + public void testAuthNoneEnabledPubkeyAuthQueryDisabled() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_auth_none", "yes"); + session.setConfig("enable_pubkey_auth_query", "no"); + doSftp(session, true); + } + + @Test + public void testAuthNoneDisabledPubkeyAuthQueryEnabled() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_auth_none", "no"); + session.setConfig("enable_pubkey_auth_query", "yes"); + doSftp(session, true); + } + + @Test + public void testAuthNoneDisabledPubkeyAuthQueryDisabled() throws Exception { + JSch ssh = createRSAIdentity(); + Session session = createSession(ssh); + session.setConfig("enable_auth_none", "no"); + session.setConfig("enable_pubkey_auth_query", "no"); + doSftp(session, true); + } + + private JSch createRSAIdentity() throws Exception { + HostKey hostKey = readHostKey(getResourceFile("docker/ssh_host_rsa_key.pub")); + JSch ssh = new JSch(); + ssh.addIdentity(getResourceFile("docker/id_rsa"), getResourceFile("docker/id_rsa.pub"), null); + ssh.getHostKeyRepository().add(hostKey, null); + return ssh; + } + + private HostKey readHostKey(String fileName) throws Exception { + List lines = Files.readAllLines(Paths.get(fileName), UTF_8); + String[] split = lines.get(0).split("\\s+"); + String hostname = + String.format(Locale.ROOT, "[%s]:%d", sshd.getHost(), sshd.getFirstMappedPort()); + return new HostKey(hostname, Base64.getDecoder().decode(split[1])); + } + + private Session createSession(JSch ssh) throws Exception { + Session session = ssh.getSession("root", sshd.getHost(), sshd.getFirstMappedPort()); + session.setConfig("StrictHostKeyChecking", "yes"); + session.setConfig("PreferredAuthentications", "publickey"); + return session; + } + + private void doSftp(Session session, boolean debugException) throws Exception { + assertDoesNotThrow(() -> { + try { + session.setTimeout(timeout); + session.connect(); + ChannelSftp sftp = (ChannelSftp) session.openChannel("sftp"); + sftp.connect(timeout); + sftp.put(in.toString(), "/root/test"); + sftp.get("/root/test", out.toString()); + sftp.disconnect(); + session.disconnect(); + } catch (Exception e) { + if (debugException) { + printInfo(); + } + throw e; + } + }); + + assertEquals(1024L * 100L, Files.size(out)); + assertEquals(hash, sha256sum.digestAsHex(out)); + } + + private void printInfo() { + jschLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + sshdLogger.getAllLoggingEvents().stream().map(LoggingEvent::getFormattedMessage) + .forEach(System.out::println); + System.out.println(""); + System.out.println(""); + System.out.println(""); + } + + private String getResourceFile(String fileName) { + return ResourceUtil.getResourceFile(getClass(), fileName); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/jbcrypt/BCryptTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/jbcrypt/BCryptTest.java new file mode 100644 index 0000000..4895f9b --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/jbcrypt/BCryptTest.java @@ -0,0 +1,275 @@ +// Copyright (c) 2006 Damien Miller +// +// Permission to use, copy, modify, and distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +package com.jcraft.jsch.test.jbcrypt; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.jcraft.jsch.jbcrypt.BCrypt; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +/** + * JUnit unit tests for BCrypt routines + * + * @author Damien Miller + * @version 0.2 + */ +public class BCryptTest { + String test_vectors[][] = { + {"", "$2a$06$DCq7YPn5Rq63x1Lad4cll.", + "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."}, + {"", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.", + "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye"}, + {"", "$2a$10$k1wbIrmNyFAPwPVPSVa/ze", + "$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW"}, + {"", "$2a$12$k42ZFHFWqBp3vWli.nIn8u", + "$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO"}, + {"a", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO", + "$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe"}, + {"a", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfe", + "$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V."}, + {"a", "$2a$10$k87L/MF28Q673VKh8/cPi.", + "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"}, + {"a", "$2a$12$8NJH3LsPrANStV6XtBakCe", + "$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS"}, + {"abc", "$2a$06$If6bvum7DFjUnE9p2uDeDu", + "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i"}, + {"abc", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7O", + "$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm"}, + {"abc", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.", + "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"}, + {"abc", "$2a$12$EXRkfkdmXn2gzds2SSitu.", + "$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q"}, + {"abcdefghijklmnopqrstuvwxyz", "$2a$06$.rCVZVOThsIa97pEDOxvGu", + "$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC"}, + {"abcdefghijklmnopqrstuvwxyz", "$2a$08$aTsUwsyowQuzRrDqFflhge", + "$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz."}, + {"abcdefghijklmnopqrstuvwxyz", "$2a$10$fVH8e28OQRj9tqiDXs1e1u", + "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq"}, + {"abcdefghijklmnopqrstuvwxyz", "$2a$12$D4G5f18o7aMMfwasBL7Gpu", + "$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG"}, + {"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$06$fPIsBO8qRqkjj273rfaOI.", + "$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO"}, + {"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$08$Eq2r4G/76Wv39MzSX262hu", + "$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW"}, + {"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe", + "$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS"}, + {"~!@#$%^&*() ~!@#$%^&*()PNBFRD", "$2a$12$WApznUOJfkEGSmYRfnkrPO", + "$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC"},}; + + /** Test method for 'BCrypt.hashpw(String, String)' */ + @Test + public void testHashpw() { + // System.out.print("BCrypt.hashpw(): "); + for (int i = 0; i < test_vectors.length; i++) { + String plain = test_vectors[i][0]; + String salt = test_vectors[i][1]; + String expected = test_vectors[i][2]; + String hashed = BCrypt.hashpw(plain, salt); + assertEquals(hashed, expected); + // System.out.print("."); + } + // System.out.println(""); + } + + /** Test method for 'BCrypt.gensalt(int)' */ + @Test + public void testGensaltInt() { + // System.out.print("BCrypt.gensalt(log_rounds):"); + for (int i = 4; i <= 12; i++) { + // System.out.print(" " + Integer.toString(i) + ":"); + for (int j = 0; j < test_vectors.length; j += 4) { + String plain = test_vectors[j][0]; + String salt = BCrypt.gensalt(i); + String hashed1 = BCrypt.hashpw(plain, salt); + String hashed2 = BCrypt.hashpw(plain, hashed1); + assertEquals(hashed1, hashed2); + // System.out.print("."); + } + } + // System.out.println(""); + } + + /** Test method for 'BCrypt.gensalt()' */ + @Test + public void testGensalt() { + // System.out.print("BCrypt.gensalt(): "); + for (int i = 0; i < test_vectors.length; i += 4) { + String plain = test_vectors[i][0]; + String salt = BCrypt.gensalt(); + String hashed1 = BCrypt.hashpw(plain, salt); + String hashed2 = BCrypt.hashpw(plain, hashed1); + assertEquals(hashed1, hashed2); + // System.out.print("."); + } + // System.out.println(""); + } + + /** Test method for 'BCrypt.checkpw(String, String)' expecting success */ + @Test + public void testCheckpw_success() { + // System.out.print("BCrypt.checkpw w/ good passwords: "); + for (int i = 0; i < test_vectors.length; i++) { + String plain = test_vectors[i][0]; + String expected = test_vectors[i][2]; + assertTrue(BCrypt.checkpw(plain, expected)); + // System.out.print("."); + } + // System.out.println(""); + } + + /** Test method for 'BCrypt.checkpw(String, String)' expecting failure */ + @Test + public void testCheckpw_failure() { + // System.out.print("BCrypt.checkpw w/ bad passwords: "); + for (int i = 0; i < test_vectors.length; i++) { + int broken_index = (i + 4) % test_vectors.length; + String plain = test_vectors[i][0]; + String expected = test_vectors[broken_index][2]; + assertFalse(BCrypt.checkpw(plain, expected)); + // System.out.print("."); + } + // System.out.println(""); + } + + /** Test for correct hashing of non-US-ASCII passwords */ + @Test + public void testInternationalChars() { + // System.out.print("BCrypt.hashpw w/ international chars: "); + String pw1 = "\u2605\u2605\u2605\u2605\u2605\u2605\u2605\u2605"; + String pw2 = "????????"; + + String h1 = BCrypt.hashpw(pw1, BCrypt.gensalt()); + assertFalse(BCrypt.checkpw(pw2, h1)); + // System.out.print("."); + + String h2 = BCrypt.hashpw(pw2, BCrypt.gensalt()); + assertFalse(BCrypt.checkpw(pw1, h2)); + // System.out.print("."); + // System.out.println(""); + } + + private static class BCryptHashTV { + private final byte[] pass; + private final byte[] salt; + private final byte[] out; + + public BCryptHashTV(byte[] pass, byte[] salt, byte[] out) { + this.pass = pass; + this.salt = salt; + this.out = out; + } + } + + BCryptHashTV[] bcrypt_hash_test_vectors = new BCryptHashTV[] {new BCryptHashTV( + new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}, + new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}, + new byte[] {(byte) 0x46, (byte) 0x02, (byte) 0x86, (byte) 0xe9, (byte) 0x72, (byte) 0xfa, + (byte) 0x83, (byte) 0x3f, (byte) 0x8b, (byte) 0x12, (byte) 0x83, (byte) 0xad, (byte) 0x8f, + (byte) 0xa9, (byte) 0x19, (byte) 0xfa, (byte) 0x29, (byte) 0xbd, (byte) 0xe2, (byte) 0x0e, + (byte) 0x23, (byte) 0x32, (byte) 0x9e, (byte) 0x77, (byte) 0x4d, (byte) 0x84, (byte) 0x22, + (byte) 0xba, (byte) 0xc0, (byte) 0xa7, (byte) 0x92, (byte) 0x6c,}), + new BCryptHashTV( + new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,}, + new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,}, + new byte[] {(byte) 0xc6, (byte) 0xa9, (byte) 0x5f, (byte) 0xe6, (byte) 0x41, (byte) 0x31, + (byte) 0x15, (byte) 0xfb, (byte) 0x57, (byte) 0xe9, (byte) 0x9f, (byte) 0x75, + (byte) 0x74, (byte) 0x98, (byte) 0xe8, (byte) 0x5d, (byte) 0xa3, (byte) 0xc6, + (byte) 0xe1, (byte) 0xdf, (byte) 0x0c, (byte) 0x3c, (byte) 0x93, (byte) 0xaa, + (byte) 0x97, (byte) 0x5c, (byte) 0x54, (byte) 0x8a, (byte) 0x34, (byte) 0x43, + (byte) 0x26, (byte) 0xf8,}),}; + + @Test + public void testBCryptHashTestVectors() throws Exception { + // System.out.print("BCrypt.hash w/ known vectors: "); + for (BCryptHashTV tv : bcrypt_hash_test_vectors) { + byte[] output = new byte[tv.out.length]; + new BCrypt().hash(tv.pass, tv.salt, output); + assertEquals(Arrays.toString(tv.out), Arrays.toString(output)); + // System.out.print("."); + } + // System.out.println(""); + } + + private static class BCryptPbkdfTV { + private final byte[] pass; + private final byte[] salt; + private final int rounds; + private final byte[] out; + + public BCryptPbkdfTV(byte[] pass, byte[] salt, int rounds, byte[] out) { + this.pass = pass; + this.salt = salt; + this.rounds = rounds; + this.out = out; + } + } + + BCryptPbkdfTV[] bcrypt_pbkdf_test_vectors = new BCryptPbkdfTV[] { + new BCryptPbkdfTV("password".getBytes(UTF_8), "salt".getBytes(UTF_8), 4, + new byte[] {(byte) 0x5b, (byte) 0xbf, (byte) 0x0c, (byte) 0xc2, (byte) 0x93, (byte) 0x58, + (byte) 0x7f, (byte) 0x1c, (byte) 0x36, (byte) 0x35, (byte) 0x55, (byte) 0x5c, + (byte) 0x27, (byte) 0x79, (byte) 0x65, (byte) 0x98, (byte) 0xd4, (byte) 0x7e, + (byte) 0x57, (byte) 0x90, (byte) 0x71, (byte) 0xbf, (byte) 0x42, (byte) 0x7e, + (byte) 0x9d, (byte) 0x8f, (byte) 0xbe, (byte) 0x84, (byte) 0x2a, (byte) 0xba, + (byte) 0x34, (byte) 0xd9,}), + new BCryptPbkdfTV("password".getBytes(UTF_8), "salt".getBytes(UTF_8), 8, + new byte[] {(byte) 0xe1, (byte) 0x36, (byte) 0x7e, (byte) 0xc5, (byte) 0x15, (byte) 0x1a, + (byte) 0x33, (byte) 0xfa, (byte) 0xac, (byte) 0x4c, (byte) 0xc1, (byte) 0xc1, + (byte) 0x44, (byte) 0xcd, (byte) 0x23, (byte) 0xfa, (byte) 0x15, (byte) 0xd5, + (byte) 0x54, (byte) 0x84, (byte) 0x93, (byte) 0xec, (byte) 0xc9, (byte) 0x9b, + (byte) 0x9b, (byte) 0x5d, (byte) 0x9c, (byte) 0x0d, (byte) 0x3b, (byte) 0x27, + (byte) 0xbe, (byte) 0xc7, (byte) 0x62, (byte) 0x27, (byte) 0xea, (byte) 0x66, + (byte) 0x08, (byte) 0x8b, (byte) 0x84, (byte) 0x9b, (byte) 0x20, (byte) 0xab, + (byte) 0x7a, (byte) 0xa4, (byte) 0x78, (byte) 0x01, (byte) 0x02, (byte) 0x46, + (byte) 0xe7, (byte) 0x4b, (byte) 0xba, (byte) 0x51, (byte) 0x72, (byte) 0x3f, + (byte) 0xef, (byte) 0xa9, (byte) 0xf9, (byte) 0x47, (byte) 0x4d, (byte) 0x65, + (byte) 0x08, (byte) 0x84, (byte) 0x5e, (byte) 0x8d}), + new BCryptPbkdfTV("password".getBytes(UTF_8), "salt".getBytes(UTF_8), 42, + new byte[] {(byte) 0x83, (byte) 0x3c, (byte) 0xf0, (byte) 0xdc, (byte) 0xf5, (byte) 0x6d, + (byte) 0xb6, (byte) 0x56, (byte) 0x08, (byte) 0xe8, (byte) 0xf0, (byte) 0xdc, + (byte) 0x0c, (byte) 0xe8, (byte) 0x82, (byte) 0xbd}),}; + + @Test + public void testBCryptPbkdfTestVectors() throws Exception { + // System.out.print("BCrypt.pbkdf w/ known vectors: "); + for (BCryptPbkdfTV tv : bcrypt_pbkdf_test_vectors) { + byte[] output = new byte[tv.out.length]; + new BCrypt().pbkdf(tv.pass, tv.salt, tv.rounds, output); + assertEquals(Arrays.toString(tv.out), Arrays.toString(output)); + // System.out.print("."); + } + // System.out.println(""); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/Adler32Test.java b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/Adler32Test.java new file mode 100644 index 0000000..26565f8 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/Adler32Test.java @@ -0,0 +1,75 @@ +package com.jcraft.jsch.test.jzlib; + +import static com.jcraft.jsch.test.jzlib.Package.randombuf; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.jcraft.jsch.jzlib.Adler32; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class Adler32Test { + private Adler32 adler; + + @BeforeEach + public void before() { + adler = new Adler32(); + } + + @AfterEach + public void after() {} + + @Test + public void testAdler32IsCompatibleWithJavaUtilZipAdler32() { + byte[] buf1 = randombuf(1024); + java.util.zip.Adler32 juza = new java.util.zip.Adler32(); + juza.update(buf1, 0, buf1.length); + long expected = juza.getValue(); + long actual = getValue(Arrays.asList(buf1)); + + assertEquals(expected, actual); + } + + @Test + public void testAdler32CanCopyItself() { + byte[] buf1 = randombuf(1024); + byte[] buf2 = randombuf(1024); + + Adler32 adler1 = new Adler32(); + + adler1.update(buf1, 0, buf1.length); + + Adler32 adler2 = adler1.copy(); + + adler1.update(buf2, 0, buf1.length); + adler2.update(buf2, 0, buf1.length); + + long expected = adler1.getValue(); + long actual = adler2.getValue(); + + assertEquals(expected, actual); + } + + @Test + public void testAdler32CanCombineValues() { + + byte[] buf1 = randombuf(1024); + byte[] buf2 = randombuf(1024); + + long adler1 = getValue(Arrays.asList(buf1)); + long adler2 = getValue(Arrays.asList(buf2)); + long expected = getValue(Arrays.asList(buf1, buf2)); + + long actual = Adler32.combine(adler1, adler2, buf2.length); + + assertEquals(expected, actual); + } + + private synchronized long getValue(List buf) { + adler.reset(); + buf.forEach(b -> adler.update(b, 0, b.length)); + return adler.getValue(); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/CRC32Test.java b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/CRC32Test.java new file mode 100644 index 0000000..026d77c --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/CRC32Test.java @@ -0,0 +1,74 @@ +package com.jcraft.jsch.test.jzlib; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.jcraft.jsch.jzlib.CRC32; +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class CRC32Test { + private CRC32 crc; + + @BeforeEach + public void before() { + crc = new CRC32(); + } + + @AfterEach + public void after() {} + + @Test + public void testCRC32IsCompatibleWithJavaUtilZipCRC32() { + byte[] buf1 = Package.randombuf(1024); + java.util.zip.CRC32 juza = new java.util.zip.CRC32(); + juza.update(buf1, 0, buf1.length); + long expected = juza.getValue(); + long actual = getValue(Arrays.asList(buf1)); + + assertEquals(expected, actual); + } + + @Test + public void testCRC2CanCopyItself() { + byte[] buf1 = Package.randombuf(1024); + byte[] buf2 = Package.randombuf(1024); + + CRC32 crc1 = new CRC32(); + + crc1.update(buf1, 0, buf1.length); + + CRC32 crc2 = crc1.copy(); + + crc1.update(buf2, 0, buf1.length); + crc2.update(buf2, 0, buf1.length); + + long expected = crc1.getValue(); + long actual = crc2.getValue(); + + assertEquals(expected, actual); + } + + @Test + public void testCRC32CanCombineValues() { + + byte[] buf1 = Package.randombuf(1024); + byte[] buf2 = Package.randombuf(1024); + + long crc1 = getValue(Arrays.asList(buf1)); + long crc2 = getValue(Arrays.asList(buf2)); + long expected = getValue(Arrays.asList(buf1, buf2)); + + long actual = CRC32.combine(crc1, crc2, buf2.length); + + assertEquals(expected, actual); + } + + private synchronized long getValue(List buf) { + crc.reset(); + buf.forEach(b -> crc.update(b, 0, b.length)); + return crc.getValue(); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/DeflateInflateTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/DeflateInflateTest.java new file mode 100644 index 0000000..0ab7797 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/DeflateInflateTest.java @@ -0,0 +1,352 @@ +package com.jcraft.jsch.test.jzlib; + +import static com.jcraft.jsch.jzlib.JZlib.Z_BEST_COMPRESSION; +import static com.jcraft.jsch.jzlib.JZlib.Z_BEST_SPEED; +import static com.jcraft.jsch.jzlib.JZlib.Z_DATA_ERROR; +import static com.jcraft.jsch.jzlib.JZlib.Z_DEFAULT_COMPRESSION; +import static com.jcraft.jsch.jzlib.JZlib.Z_DEFAULT_STRATEGY; +import static com.jcraft.jsch.jzlib.JZlib.Z_FILTERED; +import static com.jcraft.jsch.jzlib.JZlib.Z_FINISH; +import static com.jcraft.jsch.jzlib.JZlib.Z_FULL_FLUSH; +import static com.jcraft.jsch.jzlib.JZlib.Z_NEED_DICT; +import static com.jcraft.jsch.jzlib.JZlib.Z_NO_COMPRESSION; +import static com.jcraft.jsch.jzlib.JZlib.Z_NO_FLUSH; +import static com.jcraft.jsch.jzlib.JZlib.Z_OK; +import static com.jcraft.jsch.jzlib.JZlib.Z_STREAM_END; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.jcraft.jsch.jzlib.Deflater; +import com.jcraft.jsch.jzlib.Inflater; +import com.jcraft.jsch.jzlib.JZlib; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class DeflateInflateTest { + private final int comprLen = 40000; + private final int uncomprLen = comprLen; + private byte[] compr; + private byte[] uncompr; + + private Deflater deflater; + private Inflater inflater; + private int err; + + @BeforeEach + public void before() { + compr = new byte[comprLen]; + uncompr = new byte[uncomprLen]; + + deflater = new Deflater(); + inflater = new Inflater(); + + err = Z_OK; + } + + @AfterEach + public void after() {} + + @Test + public void testDeflaterAndInflaterCanDeflateAndInflateDataInLargeBuffer() { + err = deflater.init(Z_BEST_SPEED); + assertEquals(Z_OK, err); + + deflater.setInput(uncompr); + deflater.setOutput(compr); + + err = deflater.deflate(Z_NO_FLUSH); + assertEquals(Z_OK, err); + + assertEquals(0, deflater.avail_in); + + deflater.params(Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + deflater.setInput(compr); + deflater.avail_in = comprLen / 2; + + err = deflater.deflate(Z_NO_FLUSH); + assertEquals(Z_OK, err); + + deflater.params(Z_BEST_COMPRESSION, Z_FILTERED); + deflater.setInput(uncompr); + deflater.avail_in = uncomprLen; + + err = deflater.deflate(Z_NO_FLUSH); + assertEquals(Z_OK, err); + + err = deflater.deflate(JZlib.Z_FINISH); + assertEquals(Z_STREAM_END, err); + + err = deflater.end(); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + + err = inflater.init(); + assertEquals(Z_OK, err); + + boolean loop = true; + while (loop) { + inflater.setOutput(uncompr); + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) + loop = false; + else + assertEquals(Z_OK, err); + } + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + + assertEquals(2 * uncomprLen + comprLen / 2, total_out); + } + + @Test + public void testDeflaterAndInflaterCanDeflateAndInflateDataInSmallBuffer() { + byte[] data = "hello, hello!".getBytes(UTF_8); + + err = deflater.init(Z_DEFAULT_COMPRESSION); + assertEquals(Z_OK, err); + + deflater.setInput(data); + deflater.setOutput(compr); + + while (deflater.total_in < data.length && deflater.total_out < comprLen) { + deflater.avail_in = 1; + deflater.avail_out = 1; + err = deflater.deflate(Z_NO_FLUSH); + assertEquals(Z_OK, err); + } + + do { + deflater.avail_out = 1; + err = deflater.deflate(Z_FINISH); + } while (err != Z_STREAM_END); + + err = deflater.end(); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + inflater.setOutput(uncompr); + + err = inflater.init(); + assertEquals(Z_OK, err); + + boolean loop = true; + while (inflater.total_out < uncomprLen && inflater.total_in < comprLen && loop) { + inflater.avail_in = 1; // force small buffers + inflater.avail_out = 1; // force small buffers + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) + loop = false; + else + assertEquals(Z_OK, err); + } + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + byte[] actual = new byte[total_out]; + System.arraycopy(uncompr, 0, actual, 0, total_out); + + assertArrayEquals(data, actual); + } + + @Test + public void testDeflaterAndInflaterSupportDictionary() { + byte[] hello = "hello".getBytes(UTF_8); + byte[] dictionary = "hello, hello!".getBytes(UTF_8); + + err = deflater.init(Z_DEFAULT_COMPRESSION); + assertEquals(Z_OK, err); + + deflater.setDictionary(dictionary, dictionary.length); + assertEquals(Z_OK, err); + + long dictID = deflater.getAdler(); + + deflater.setInput(hello); + deflater.setOutput(compr); + + err = deflater.deflate(Z_FINISH); + assertEquals(Z_STREAM_END, err); + + err = deflater.end(); + assertEquals(Z_OK, err); + + err = inflater.init(); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + inflater.setOutput(uncompr); + + boolean loop = true; + do { + err = inflater.inflate(JZlib.Z_NO_FLUSH); + switch (err) { + case Z_STREAM_END: + loop = false; + break; + case Z_NEED_DICT: + assertEquals(inflater.getAdler(), dictID); + err = inflater.setDictionary(dictionary, dictionary.length); + assertEquals(Z_OK, err); + break; + default: + assertEquals(Z_OK, err); + break; + } + } while (loop); + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + byte[] actual = new byte[total_out]; + System.arraycopy(uncompr, 0, actual, 0, total_out); + + assertArrayEquals(hello, actual); + } + + @Test + public void testDeflaterAndInflaterSupportSync() { + byte[] hello = "hello".getBytes(UTF_8); + + err = deflater.init(Z_DEFAULT_COMPRESSION); + assertEquals(Z_OK, err); + + deflater.setInput(hello); + deflater.avail_in = 3; + deflater.setOutput(compr); + + err = deflater.deflate(Z_FULL_FLUSH); + assertEquals(Z_OK, err); + + compr[3] = (byte) (compr[3] + 1); + deflater.avail_in = hello.length - 3; + + err = deflater.deflate(Z_FINISH); + assertEquals(Z_STREAM_END, err); + int comprLen = (int) deflater.total_out; + + err = deflater.end(); + assertEquals(Z_OK, err); + + err = inflater.init(); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + inflater.avail_in = 2; + + inflater.setOutput(uncompr); + + err = inflater.inflate(JZlib.Z_NO_FLUSH); + assertEquals(Z_OK, err); + + inflater.avail_in = comprLen - 2; + err = inflater.sync(); + + err = inflater.inflate(Z_FINISH); + assertEquals(Z_DATA_ERROR, err); + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + byte[] actual = new byte[total_out]; + System.arraycopy(uncompr, 0, actual, 0, total_out); + + assertEquals(new String(hello, UTF_8), "hel" + new String(actual, UTF_8)); + } + + @Test + public void testInflaterCanInflateGzipData() { + byte[] hello = "foo".getBytes(UTF_8); + byte[] data = {(byte) 0x1f, (byte) 0x8b, (byte) 0x08, (byte) 0x18, (byte) 0x08, (byte) 0xeb, + (byte) 0x7a, (byte) 0x0b, (byte) 0x00, (byte) 0x0b, (byte) 0x58, (byte) 0x00, (byte) 0x59, + (byte) 0x00, (byte) 0x4b, (byte) 0xcb, (byte) 0xcf, (byte) 0x07, (byte) 0x00, (byte) 0x21, + (byte) 0x65, (byte) 0x73, (byte) 0x8c, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00}; + + err = inflater.init(15 + 32); + assertEquals(Z_OK, err); + + inflater.setInput(data); + inflater.setOutput(uncompr); + + int comprLen = data.length; + + boolean loop = true; + while (inflater.total_out < uncomprLen && inflater.total_in < comprLen && loop) { + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) + loop = false; + else + assertEquals(Z_OK, err); + } + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + byte[] actual = new byte[total_out]; + System.arraycopy(uncompr, 0, actual, 0, total_out); + + assertArrayEquals(hello, actual); + } + + @Test + public void testInflaterAndDeflaterCanSupportGzipData() { + byte[] data = "hello, hello!".getBytes(UTF_8); + + err = deflater.init(Z_DEFAULT_COMPRESSION, 15 + 16); + assertEquals(Z_OK, err); + + deflater.setInput(data); + deflater.setOutput(compr); + + while (deflater.total_in < data.length && deflater.total_out < comprLen) { + deflater.avail_in = 1; + deflater.avail_out = 1; + err = deflater.deflate(Z_NO_FLUSH); + assertEquals(Z_OK, err); + } + + do { + deflater.avail_out = 1; + err = deflater.deflate(Z_FINISH); + } while (err != Z_STREAM_END); + + err = deflater.end(); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + inflater.setOutput(uncompr); + + err = inflater.init(15 + 32); + assertEquals(Z_OK, err); + + boolean loop = true; + while (inflater.total_out < uncomprLen && inflater.total_in < comprLen && loop) { + inflater.avail_in = 1; // force small buffers + inflater.avail_out = 1; // force small buffers + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) + loop = false; + else + assertEquals(Z_OK, err); + } + + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + byte[] actual = new byte[total_out]; + System.arraycopy(uncompr, 0, actual, 0, total_out); + + assertArrayEquals(data, actual); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/DeflaterInflaterStreamTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/DeflaterInflaterStreamTest.java new file mode 100644 index 0000000..e9f72cc --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/DeflaterInflaterStreamTest.java @@ -0,0 +1,126 @@ +package com.jcraft.jsch.test.jzlib; + +import static com.jcraft.jsch.test.jzlib.Package.randombuf; +import static com.jcraft.jsch.test.jzlib.Package.readArray; +import static com.jcraft.jsch.test.jzlib.Package.readIS; +import static com.jcraft.jsch.test.jzlib.Package.uncheckedConsumer; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.jcraft.jsch.jzlib.Deflater; +import com.jcraft.jsch.jzlib.DeflaterOutputStream; +import com.jcraft.jsch.jzlib.Inflater; +import com.jcraft.jsch.jzlib.InflaterInputStream; +import com.jcraft.jsch.jzlib.JZlib; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class DeflaterInflaterStreamTest { + + @BeforeEach + public void before() {} + + @AfterEach + public void after() {} + + @Test + public void testDeflaterAndInflaterCanDeflateAndInflateDataOneByOne() throws IOException { + byte[] data1 = randombuf(1024); + byte[] buf = new byte[1]; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DeflaterOutputStream gos = new DeflaterOutputStream(baos); + readArray(data1, gos, buf); + gos.close(); + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + readIS(new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray())), baos2, buf); + byte[] data2 = baos2.toByteArray(); + + assertEquals(data1.length, data2.length); + assertArrayEquals(data1, data2); + } + + @Test + public void testDeflaterOutputStreamAndInflaterInputStreamCanDeflateAndInflate() + throws IOException { + + for (int i = 1; i < 100; i += 3) { + + byte[] buf = new byte[i]; + + byte[] data1 = randombuf(10240); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DeflaterOutputStream gos = new DeflaterOutputStream(baos); + readArray(data1, gos, buf); + gos.close(); + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + readIS(new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray())), baos2, buf); + byte[] data2 = baos2.toByteArray(); + + assertEquals(data1.length, data2.length); + assertArrayEquals(data1, data2); + } + } + + @Test + public void testDeflaterAndInflaterCanDeflateAndInflateNowrapData() throws IOException { + + for (int i = 1; i < 100; i += 3) { + + byte[] buf = new byte[i]; + + byte[] data1 = randombuf(10240); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Deflater deflater = new Deflater(JZlib.Z_DEFAULT_COMPRESSION, JZlib.DEF_WBITS, true); + DeflaterOutputStream gos = new DeflaterOutputStream(baos, deflater); + readArray(data1, gos, buf); + gos.close(); + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + Inflater inflater = new Inflater(JZlib.DEF_WBITS, true); + readIS(new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray()), inflater), baos2, + buf); + byte[] data2 = baos2.toByteArray(); + + assertEquals(data1.length, data2.length); + assertArrayEquals(data1, data2); + } + } + + @Test + public void testDeflaterAndInflaterCanDeflateAndInflateNowrapDataWithMaxWbits() { + byte[] buf = new byte[100]; + + Arrays.asList(randombuf(10240), + "{\"color\":2,\"id\":\"EvLd4UG.CXjnk35o1e8LrYYQfHu0h.d*SqVJPoqmzXM::Ly::Snaps::Store::Commit\"}" + .getBytes(UTF_8)) + .forEach(uncheckedConsumer(data1 -> { + Deflater deflater = new Deflater(JZlib.Z_DEFAULT_COMPRESSION, JZlib.MAX_WBITS, true); + + Inflater inflater = new Inflater(JZlib.MAX_WBITS, true); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DeflaterOutputStream gos = new DeflaterOutputStream(baos, deflater); + readArray(data1, gos, buf); + gos.close(); + + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + readIS(new InflaterInputStream(new ByteArrayInputStream(baos.toByteArray()), inflater), + baos2, buf); + byte[] data2 = baos2.toByteArray(); + + assertEquals(data1.length, data2.length); + assertArrayEquals(data1, data2); + })); + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/Package.java b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/Package.java new file mode 100644 index 0000000..e0e8230 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/Package.java @@ -0,0 +1,61 @@ +package com.jcraft.jsch.test.jzlib; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UncheckedIOException; +import java.util.Random; +import java.util.function.Consumer; +import java.util.function.Function; + +public class Package { + public static void readIS(InputStream is, OutputStream out, byte[] buf) throws IOException { + int i; + while ((i = is.read(buf)) != -1) { + out.write(buf, 0, i); + } + is.close(); + } + + public static void readArray(byte[] is, OutputStream out, byte[] buf) throws IOException { + readIS(new ByteArrayInputStream(is), out, buf); + } + + public static byte[] randombuf(int n) { + Random random = new Random(); + byte[] ret = new byte[n]; + random.nextBytes(ret); + return ret; + } + + public static Consumer uncheckedConsumer(IOConsumer consumer) { + return t -> { + try { + consumer.accept(t); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; + } + + public static Function uncheckedFunction(IOFunction function) { + return t -> { + try { + return function.apply(t); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }; + } + + @FunctionalInterface + public interface IOConsumer { + void accept(T t) throws IOException; + } + + @FunctionalInterface + public interface IOFunction { + R apply(T t) throws IOException; + } +} diff --git a/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/WrapperTypeTest.java b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/WrapperTypeTest.java new file mode 100644 index 0000000..20635d8 --- /dev/null +++ b/files-jsch/src/test/java/com/jcraft/jsch/test/jzlib/WrapperTypeTest.java @@ -0,0 +1,283 @@ +package com.jcraft.jsch.test.jzlib; + +import static com.jcraft.jsch.jzlib.JZlib.DEF_WBITS; +import static com.jcraft.jsch.jzlib.JZlib.W_ANY; +import static com.jcraft.jsch.jzlib.JZlib.W_GZIP; +import static com.jcraft.jsch.jzlib.JZlib.W_NONE; +import static com.jcraft.jsch.jzlib.JZlib.W_ZLIB; +import static com.jcraft.jsch.jzlib.JZlib.Z_BEST_SPEED; +import static com.jcraft.jsch.jzlib.JZlib.Z_DATA_ERROR; +import static com.jcraft.jsch.jzlib.JZlib.Z_DEFAULT_COMPRESSION; +import static com.jcraft.jsch.jzlib.JZlib.Z_NO_FLUSH; +import static com.jcraft.jsch.jzlib.JZlib.Z_OK; +import static com.jcraft.jsch.jzlib.JZlib.Z_STREAM_END; +import static com.jcraft.jsch.test.jzlib.Package.readArray; +import static com.jcraft.jsch.test.jzlib.Package.readIS; +import static com.jcraft.jsch.test.jzlib.Package.uncheckedConsumer; +import static com.jcraft.jsch.test.jzlib.Package.uncheckedFunction; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import com.jcraft.jsch.jzlib.Deflater; +import com.jcraft.jsch.jzlib.DeflaterOutputStream; +import com.jcraft.jsch.jzlib.Inflater; +import com.jcraft.jsch.jzlib.InflaterInputStream; +import com.jcraft.jsch.jzlib.JZlib; +import com.jcraft.jsch.jzlib.JZlib.WrapperType; +import com.jcraft.jsch.jzlib.ZStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class WrapperTypeTest { + private final byte[] data = "hello, hello!".getBytes(UTF_8); + + private final int comprLen = 40000; + private final int uncomprLen = comprLen; + private byte[] compr; + private byte[] uncompr; + private int err; + + private final List cases = Arrays.asList( + /* success fail */ + new Case(W_ZLIB, Arrays.asList(W_ZLIB, W_ANY), Arrays.asList(W_GZIP, W_NONE)), + new Case(W_GZIP, Arrays.asList(W_GZIP, W_ANY), Arrays.asList(W_ZLIB, W_NONE)), + new Case(W_NONE, Arrays.asList(W_NONE, W_ANY), Arrays.asList(W_ZLIB, W_GZIP))); + + @BeforeEach + public void before() { + compr = new byte[comprLen]; + uncompr = new byte[uncomprLen]; + + err = Z_OK; + } + + @AfterEach + public void after() {} + + @Test + public void testDeflaterCanDetectDataTypeOfInput() { + byte[] buf = compr; + + cases.forEach(uncheckedConsumer(c -> { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Deflater deflater = new Deflater(Z_DEFAULT_COMPRESSION, DEF_WBITS, 9, c.iflag); + DeflaterOutputStream gos = new DeflaterOutputStream(baos, deflater); + readArray(data, gos, buf); + gos.close(); + + byte[] deflated = baos.toByteArray(); + + c.good.stream().map(uncheckedFunction(w -> { + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + Inflater inflater = new Inflater(w); + readIS(new InflaterInputStream(new ByteArrayInputStream(deflated), inflater), baos2, buf); + byte[] data1 = baos2.toByteArray(); + assertEquals(data.length, data1.length); + assertArrayEquals(data, data1); + return new Tuple(inflater.avail_in, inflater.avail_out, inflater.total_in, + inflater.total_out); + })).reduce((x, y) -> { + assertEquals(y, x); + return x; + }); + + c.bad.forEach(uncheckedConsumer(w -> { + ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); + Inflater inflater = new Inflater(w); + assertThrows(IOException.class, + () -> readIS(new InflaterInputStream(new ByteArrayInputStream(deflated), inflater), + baos2, buf)); + })); + })); + } + + @Test + public void testZStreamCanDetectDataTypeOfInput() { + cases.forEach(c -> { + ZStream deflater = new ZStream(); + + err = deflater.deflateInit(Z_BEST_SPEED, DEF_WBITS, 9, c.iflag); + assertEquals(Z_OK, err); + + deflate(deflater, data, compr); + + c.good.forEach(w -> { + ZStream inflater = inflate(compr, uncompr, w); + int total_out = (int) inflater.total_out; + assertEquals(new String(data, UTF_8), new String(uncompr, 0, total_out, UTF_8)); + }); + + c.bad.forEach(w -> { + inflate_fail(compr, uncompr, w); + }); + }); + } + + @Test + public void testDeflaterCanSupportWbitsPlus32() { + + Deflater deflater = new Deflater(); + err = deflater.init(Z_BEST_SPEED, DEF_WBITS, 9); + assertEquals(Z_OK, err); + + deflate(deflater, data, compr); + + Inflater inflater = new Inflater(); + err = inflater.init(DEF_WBITS + 32); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + + boolean loop = true; + while (loop) { + inflater.setOutput(uncompr); + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) + loop = false; + else + assertEquals(Z_OK, err); + } + err = inflater.end(); + assertEquals(Z_OK, err); + + int total_out = (int) inflater.total_out; + assertEquals(new String(data, UTF_8), new String(uncompr, 0, total_out, UTF_8)); + + deflater = new Deflater(); + err = deflater.init(Z_BEST_SPEED, DEF_WBITS + 16, 9); + assertEquals(Z_OK, err); + + deflate(deflater, data, compr); + + inflater = new Inflater(); + err = inflater.init(DEF_WBITS + 32); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + + loop = true; + while (loop) { + inflater.setOutput(uncompr); + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) + loop = false; + else + assertEquals(Z_OK, err); + } + err = inflater.end(); + assertEquals(Z_OK, err); + + total_out = (int) inflater.total_out; + assertEquals(new String(data, UTF_8), new String(uncompr, 0, total_out, UTF_8)); + } + + private void deflate(ZStream deflater, byte[] data, byte[] compr) { + deflater.setInput(data); + deflater.setOutput(compr); + + err = deflater.deflate(JZlib.Z_FINISH); + assertEquals(Z_STREAM_END, err); + + err = deflater.end(); + assertEquals(Z_OK, err); + } + + private ZStream inflate(byte[] compr, byte[] uncompr, WrapperType w) { + ZStream inflater = new ZStream(); + err = inflater.inflateInit(w); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + + boolean loop = true; + while (loop) { + inflater.setOutput(uncompr); + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) + loop = false; + else + assertEquals(Z_OK, err); + } + err = inflater.end(); + assertEquals(Z_OK, err); + + return inflater; + } + + private void inflate_fail(byte[] compr, byte[] uncompr, WrapperType w) { + ZStream inflater = new ZStream(); + + err = inflater.inflateInit(w); + assertEquals(Z_OK, err); + + inflater.setInput(compr); + + boolean loop = true; + while (loop) { + inflater.setOutput(uncompr); + err = inflater.inflate(Z_NO_FLUSH); + if (err == Z_STREAM_END) + loop = false; + else { + assertEquals(Z_DATA_ERROR, err); + loop = false; + } + } + } + + static class Case { + final WrapperType iflag; + final List good; + final List bad; + + Case(WrapperType iflag, List good, List bad) { + this.iflag = iflag; + this.good = good; + this.bad = bad; + } + } + + static class Tuple { + private final int a; + private final int b; + private final long c; + private final long d; + + Tuple(int a, int b, long c, long d) { + this.a = a; + this.b = b; + this.c = c; + this.d = d; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Tuple)) + return false; + else if (a != ((Tuple) obj).a) + return false; + else if (b != ((Tuple) obj).b) + return false; + else if (c != ((Tuple) obj).c) + return false; + else if (d != ((Tuple) obj).d) + return false; + else + return true; + } + + @Override + public int hashCode() { + return Objects.hash(a, b, c, d); + } + } +} diff --git a/files-jsch/src/test/java/module-info.java b/files-jsch/src/test/java/module-info.java new file mode 100644 index 0000000..d63ed44 --- /dev/null +++ b/files-jsch/src/test/java/module-info.java @@ -0,0 +1,10 @@ +module org.xbib.files.jsch.test { + requires java.logging; + requires org.junit.jupiter.api; + requires org.xbib.files.jsch; + requires testcontainers; + requires junit.jupiter; + requires org.junit.jupiter.params; + exports com.jcraft.jsch.test; + opens com.jcraft.jsch.test to org.junit.platform.commons; +} diff --git a/files-jsch/src/test/resources/Log4j2LoggerTest.xml b/files-jsch/src/test/resources/Log4j2LoggerTest.xml new file mode 100644 index 0000000..65413df --- /dev/null +++ b/files-jsch/src/test/resources/Log4j2LoggerTest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/files-jsch/src/test/resources/config b/files-jsch/src/test/resources/config new file mode 100644 index 0000000..7140a03 --- /dev/null +++ b/files-jsch/src/test/resources/config @@ -0,0 +1,5 @@ +# some comment +Host host2 + HostName host2.somewhere.edu + User foobar + IdentityFile ~/.ssh/old_keys/host2_key \ No newline at end of file diff --git a/files-jsch/src/test/resources/config_with_negations b/files-jsch/src/test/resources/config_with_negations new file mode 100644 index 0000000..ff8ed38 --- /dev/null +++ b/files-jsch/src/test/resources/config_with_negations @@ -0,0 +1,11 @@ +Host *.example.com !*-jump.example.com !*-proxy.example.com + User u1 + +Host *-jump.example.com + User jump-u1 + +Host *-proxy.example.com + User proxy-u1 + +Host *.example.org + User u2 diff --git a/files-jsch/src/test/resources/docker/Dockerfile b/files-jsch/src/test/resources/docker/Dockerfile new file mode 100644 index 0000000..0a061ec --- /dev/null +++ b/files-jsch/src/test/resources/docker/Dockerfile @@ -0,0 +1,24 @@ +FROM alpine:3.7 +RUN apk update && \ + apk upgrade && \ + apk add openssh && \ + rm /var/cache/apk/* && \ + mkdir /root/.ssh && \ + chmod 700 /root/.ssh +COPY ssh_host_rsa_key /etc/ssh/ +COPY ssh_host_rsa_key.pub /etc/ssh/ +COPY ssh_host_ecdsa256_key /etc/ssh/ +COPY ssh_host_ecdsa256_key.pub /etc/ssh/ +COPY ssh_host_ecdsa384_key /etc/ssh/ +COPY ssh_host_ecdsa384_key.pub /etc/ssh/ +COPY ssh_host_ecdsa521_key /etc/ssh/ +COPY ssh_host_ecdsa521_key.pub /etc/ssh/ +COPY ssh_host_ed25519_key /etc/ssh/ +COPY ssh_host_ed25519_key.pub /etc/ssh/ +COPY ssh_host_dsa_key /etc/ssh/ +COPY ssh_host_dsa_key.pub /etc/ssh/ +COPY sshd_config /etc/ssh/ +COPY authorized_keys /root/.ssh/ +RUN chmod 600 /etc/ssh/ssh_*_key /root/.ssh/authorized_keys +RUN passwd -u root +ENTRYPOINT ["/usr/sbin/sshd", "-D", "-e"] diff --git a/files-jsch/src/test/resources/docker/Dockerfile.ExtInfoInAuthIT b/files-jsch/src/test/resources/docker/Dockerfile.ExtInfoInAuthIT new file mode 100644 index 0000000..06a3f9b --- /dev/null +++ b/files-jsch/src/test/resources/docker/Dockerfile.ExtInfoInAuthIT @@ -0,0 +1,36 @@ +FROM alpine:3.19 +RUN apk update && \ + apk upgrade && \ + apk add openssh && \ + rm /var/cache/apk/* && \ + addgroup -g 1000 rsa && \ + adduser -Du 1000 -G rsa -Hh /rsa -s /bin/sh -g rsa rsa && \ + mkdir -p /rsa/.ssh && \ + chown -R rsa:rsa /rsa && \ + chmod 700 /rsa /rsa/.ssh && \ + passwd -u rsa && \ + addgroup -g 1001 ecdsa && \ + adduser -Du 1001 -G ecdsa -Hh /ecdsa -s /bin/sh -g ecdsa ecdsa && \ + mkdir -p /ecdsa/.ssh && \ + chown -R ecdsa:ecdsa /ecdsa && \ + chmod 700 /ecdsa /ecdsa/.ssh && \ + passwd -u ecdsa +COPY ssh_host_rsa_key /etc/ssh/ +COPY ssh_host_rsa_key.pub /etc/ssh/ +COPY ssh_host_ecdsa256_key /etc/ssh/ +COPY ssh_host_ecdsa256_key.pub /etc/ssh/ +COPY ssh_host_ecdsa384_key /etc/ssh/ +COPY ssh_host_ecdsa384_key.pub /etc/ssh/ +COPY ssh_host_ecdsa521_key /etc/ssh/ +COPY ssh_host_ecdsa521_key.pub /etc/ssh/ +COPY ssh_host_ed25519_key /etc/ssh/ +COPY ssh_host_ed25519_key.pub /etc/ssh/ +COPY ssh_host_dsa_key /etc/ssh/ +COPY ssh_host_dsa_key.pub /etc/ssh/ +COPY sshd_config /etc/ssh/ +COPY authorized_keys /rsa/.ssh/ +COPY authorized_keys /ecdsa/.ssh/ +RUN chown rsa:rsa /rsa/.ssh/authorized_keys && \ + chown ecdsa:ecdsa /ecdsa/.ssh/authorized_keys && \ + chmod 600 /etc/ssh/ssh_*_key /rsa/.ssh/authorized_keys /ecdsa/.ssh/authorized_keys +ENTRYPOINT ["/usr/sbin/sshd", "-D", "-e"] diff --git a/files-jsch/src/test/resources/docker/Dockerfile.KeyPairIT b/files-jsch/src/test/resources/docker/Dockerfile.KeyPairIT new file mode 100644 index 0000000..02e6d66 --- /dev/null +++ b/files-jsch/src/test/resources/docker/Dockerfile.KeyPairIT @@ -0,0 +1,14 @@ +FROM alpine:3.7 +RUN apk update && \ + apk upgrade && \ + apk add openssh && \ + rm /var/cache/apk/* && \ + mkdir /root/.ssh && \ + chmod 700 /root/.ssh && \ + ssh-keygen -A +COPY sshd_config /etc/ssh/ +COPY authorized_keys /root/.ssh/ +RUN chmod 600 /etc/ssh/ssh_*_key /root/.ssh/authorized_keys +RUN passwd -u root +EXPOSE 22 +ENTRYPOINT ["/usr/sbin/sshd", "-D", "-e"] diff --git a/files-jsch/src/test/resources/docker/Dockerfile.asyncssh b/files-jsch/src/test/resources/docker/Dockerfile.asyncssh new file mode 100644 index 0000000..734a0e6 --- /dev/null +++ b/files-jsch/src/test/resources/docker/Dockerfile.asyncssh @@ -0,0 +1,25 @@ +FROM python:3.9 +ARG MAX_PKTSIZE=32768 +RUN pip install 'asyncssh[bcrypt]' && \ + mkdir /root/.ssh && \ + chmod 700 /root/.ssh +COPY asyncsshd.py / +COPY ssh_host_rsa_key /etc/ssh/ +COPY ssh_host_rsa_key.pub /etc/ssh/ +COPY ssh_host_ecdsa256_key /etc/ssh/ +COPY ssh_host_ecdsa256_key.pub /etc/ssh/ +COPY ssh_host_ecdsa384_key /etc/ssh/ +COPY ssh_host_ecdsa384_key.pub /etc/ssh/ +COPY ssh_host_ecdsa521_key /etc/ssh/ +COPY ssh_host_ecdsa521_key.pub /etc/ssh/ +COPY ssh_host_ed25519_key /etc/ssh/ +COPY ssh_host_ed25519_key.pub /etc/ssh/ +COPY ssh_host_ed448_key /etc/ssh/ +COPY ssh_host_ed448_key.pub /etc/ssh/ +COPY ssh_host_dsa_key /etc/ssh/ +COPY ssh_host_dsa_key.pub /etc/ssh/ +COPY authorized_keys /root/.ssh/ +RUN chmod 600 /etc/ssh/ssh_*_key /root/.ssh/authorized_keys +RUN passwd -u root +ENV MAX_PKTSIZE=${MAX_PKTSIZE} +ENTRYPOINT ["python", "/asyncsshd.py"] diff --git a/files-jsch/src/test/resources/docker/Dockerfile.dropbear b/files-jsch/src/test/resources/docker/Dockerfile.dropbear new file mode 100644 index 0000000..9c002ec --- /dev/null +++ b/files-jsch/src/test/resources/docker/Dockerfile.dropbear @@ -0,0 +1,13 @@ +FROM alpine:3.12 +RUN apk update && \ + apk upgrade && \ + apk add dropbear openssh && \ + rm /var/cache/apk/* && \ + mkdir /etc/dropbear && \ + mkdir /root/.ssh && \ + chmod 700 /root/.ssh +COPY dropbear_rsa_host_key /etc/dropbear/ +COPY authorized_keys /root/.ssh/ +RUN chmod 600 /etc/dropbear/dropbear_*_key /root/.ssh/authorized_keys +RUN passwd -u root +ENTRYPOINT ["/usr/sbin/dropbear", "-F", "-s", "-E"] diff --git a/files-jsch/src/test/resources/docker/Dockerfile.openssh74 b/files-jsch/src/test/resources/docker/Dockerfile.openssh74 new file mode 100644 index 0000000..255dbc5 --- /dev/null +++ b/files-jsch/src/test/resources/docker/Dockerfile.openssh74 @@ -0,0 +1,23 @@ +FROM alpine:3.5 +RUN apk update && \ + apk upgrade && \ + apk add openssh && \ + rm /var/cache/apk/* && \ + mkdir /root/.ssh && \ + chmod 700 /root/.ssh +COPY ssh_host_rsa_key /etc/ssh/ +COPY ssh_host_rsa_key.pub /etc/ssh/ +COPY ssh_host_ecdsa256_key /etc/ssh/ +COPY ssh_host_ecdsa256_key.pub /etc/ssh/ +COPY ssh_host_ecdsa384_key /etc/ssh/ +COPY ssh_host_ecdsa384_key.pub /etc/ssh/ +COPY ssh_host_ecdsa521_key /etc/ssh/ +COPY ssh_host_ecdsa521_key.pub /etc/ssh/ +COPY ssh_host_ed25519_key /etc/ssh/ +COPY ssh_host_ed25519_key.pub /etc/ssh/ +COPY ssh_host_dsa_key /etc/ssh/ +COPY ssh_host_dsa_key.pub /etc/ssh/ +COPY sshd_config /etc/ssh/ +COPY authorized_keys /root/.ssh/ +RUN chmod 600 /etc/ssh/ssh_*_key /root/.ssh/authorized_keys +ENTRYPOINT ["/usr/sbin/sshd", "-D", "-e"] diff --git a/files-jsch/src/test/resources/docker/Dockerfile.openssh96 b/files-jsch/src/test/resources/docker/Dockerfile.openssh96 new file mode 100644 index 0000000..474c928 --- /dev/null +++ b/files-jsch/src/test/resources/docker/Dockerfile.openssh96 @@ -0,0 +1,23 @@ +FROM alpine:3.19 +RUN apk update && \ + apk upgrade && \ + apk add openssh && \ + rm /var/cache/apk/* && \ + mkdir /root/.ssh && \ + chmod 700 /root/.ssh +COPY ssh_host_rsa_key /etc/ssh/ +COPY ssh_host_rsa_key.pub /etc/ssh/ +COPY ssh_host_ecdsa256_key /etc/ssh/ +COPY ssh_host_ecdsa256_key.pub /etc/ssh/ +COPY ssh_host_ecdsa384_key /etc/ssh/ +COPY ssh_host_ecdsa384_key.pub /etc/ssh/ +COPY ssh_host_ecdsa521_key /etc/ssh/ +COPY ssh_host_ecdsa521_key.pub /etc/ssh/ +COPY ssh_host_ed25519_key /etc/ssh/ +COPY ssh_host_ed25519_key.pub /etc/ssh/ +COPY ssh_host_dsa_key /etc/ssh/ +COPY ssh_host_dsa_key.pub /etc/ssh/ +COPY sshd_config /etc/ssh/ +COPY authorized_keys /root/.ssh/ +RUN chmod 600 /etc/ssh/ssh_*_key /root/.ssh/authorized_keys +ENTRYPOINT ["/usr/sbin/sshd", "-D", "-e"] diff --git a/files-jsch/src/test/resources/docker/Dockerfile.sshagent b/files-jsch/src/test/resources/docker/Dockerfile.sshagent new file mode 100644 index 0000000..b73d917 --- /dev/null +++ b/files-jsch/src/test/resources/docker/Dockerfile.sshagent @@ -0,0 +1,17 @@ +FROM alpine:3.7 +ARG testuid +ARG testgid +RUN apk update && \ + apk upgrade && \ + apk add openssh su-exec && \ + rm /var/cache/apk/* && \ + if [ "$testuid" -gt 0 ]; then if egrep "^[^:]+:x:$testuid:" /etc/passwd; then deluser "$(egrep "^[^:]+:x:$testuid:" /etc/passwd | cut -d: -f1)"; fi; fi && \ + if [ "$testgid" -gt 0 ]; then if egrep "^[^:]+:x:[^:]+:$testgid:" /etc/passwd; then deluser "$(egrep "^[^:]+:x:[^:]+:$testgid:" /etc/passwd | cut -d: -f1)"; fi; fi && \ + if [ "$testgid" -gt 0 ]; then if egrep "^[^:]+:x:$testgid:" /etc/group; then delgroup "$(egrep "^[^:]+:x:$testgid:" /etc/group | cut -d: -f1)"; fi; fi && \ + addgroup -g $testgid testuser && \ + adduser -Du $testuid -G testuser -Hh /testuser -s /bin/sh -g testuser testuser && \ + mkdir /testuser && \ + chown testuser:testuser /testuser && \ + chmod 700 /testuser && \ + passwd -u testuser +ENTRYPOINT ["/sbin/su-exec", "testuser:testuser", "/usr/bin/ssh-agent", "-d", "-a", "/testuser/sock"] diff --git a/files-jsch/src/test/resources/docker/asyncsshd.py b/files-jsch/src/test/resources/docker/asyncsshd.py new file mode 100755 index 0000000..a76843b --- /dev/null +++ b/files-jsch/src/test/resources/docker/asyncsshd.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +import asyncio +import asyncssh +import os +import sys + + +class MySFTPServer(asyncssh.SFTPServer): + def __init__(self, chan): + root = '/' + super().__init__(chan, chroot=root) + + +async def start_server(max_pktsize: int): + await asyncssh.listen('', 22, sftp_factory=MySFTPServer, allow_scp=True, max_pktsize=max_pktsize, + authorized_client_keys='/root/.ssh/authorized_keys', + server_host_keys=['/etc/ssh/ssh_host_ecdsa256_key', '/etc/ssh/ssh_host_ecdsa384_key', + '/etc/ssh/ssh_host_ecdsa521_key', '/etc/ssh/ssh_host_ed448_key', + '/etc/ssh/ssh_host_ed25519_key', '/etc/ssh/ssh_host_rsa_key', + '/etc/ssh/ssh_host_dsa_key'], + kex_algs=['curve448-sha512', 'curve25519-sha256', 'curve25519-sha256@libssh.org', + 'ecdh-sha2-nistp521', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp256', + 'diffie-hellman-group18-sha512', 'diffie-hellman-group17-sha512', + 'diffie-hellman-group16-sha512', 'diffie-hellman-group15-sha512', + 'diffie-hellman-group14-sha256', 'diffie-hellman-group-exchange-sha256', + 'diffie-hellman-group-exchange-sha1', 'diffie-hellman-group14-sha1', + 'diffie-hellman-group1-sha1', 'diffie-hellman-group14-sha224@ssh.com', + 'diffie-hellman-group14-sha256@ssh.com', 'diffie-hellman-group15-sha256@ssh.com', + 'diffie-hellman-group15-sha384@ssh.com', 'diffie-hellman-group16-sha384@ssh.com', + 'diffie-hellman-group16-sha512@ssh.com', 'diffie-hellman-group18-sha512@ssh.com', + 'diffie-hellman-group-exchange-sha224@ssh.com', + 'diffie-hellman-group-exchange-sha384@ssh.com', + 'diffie-hellman-group-exchange-sha512@ssh.com'], + signature_algs=['ecdsa-sha2-nistp521', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp256', + 'ssh-ed448', 'ssh-ed25519', 'rsa-sha2-512', 'rsa-sha2-256', 'ssh-rsa', + 'ssh-dss', 'ssh-rsa-sha224@ssh.com', 'ssh-rsa-sha256@ssh.com', + 'ssh-rsa-sha384@ssh.com', 'ssh-rsa-sha512@ssh.com'], + encryption_algs=['chacha20-poly1305@openssh.com', 'aes256-gcm@openssh.com', + 'aes128-gcm@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr', + 'aes256-cbc', 'aes192-cbc', 'aes128-cbc', '3des-cbc', 'blowfish-cbc', + 'arcfour', 'arcfour256', 'arcfour128', 'cast128-cbc', 'seed-cbc@ssh.com'], + mac_algs=['hmac-sha2-512-etm@openssh.com', 'hmac-sha2-256-etm@openssh.com', + 'hmac-sha1-etm@openssh.com', 'hmac-sha2-512', 'hmac-sha2-256', 'hmac-sha1', + 'hmac-sha1-96-etm@openssh.com', 'hmac-sha1-96', 'hmac-md5-etm@openssh.com', + 'hmac-md5', 'hmac-md5-96-etm@openssh.com', 'hmac-md5-96', + 'hmac-sha256-2@ssh.com', 'hmac-sha224@ssh.com', 'hmac-sha256@ssh.com', + 'hmac-sha384@ssh.com', 'hmac-sha512@ssh.com'], + compression_algs=['zlib@openssh.com', 'zlib', 'none']) + + +loop = asyncio.get_event_loop() + +try: + max_pktsize = int(os.getenv("MAX_PKTSIZE")) + loop.run_until_complete(start_server(max_pktsize)) +except (ValueError, OSError, asyncssh.Error) as exc: + sys.exit('Error starting server: ' + str(exc)) + +loop.run_forever() diff --git a/files-jsch/src/test/resources/docker/authorized_keys b/files-jsch/src/test/resources/docker/authorized_keys new file mode 100644 index 0000000..7b4779d --- /dev/null +++ b/files-jsch/src/test/resources/docker/authorized_keys @@ -0,0 +1,7 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEE5LImdpZHwScWm81gWgQo2WqeMmKTNIGbWfhFwMgP2YSBt0HnuQ7/Fa88S5Url7ufOwJl8fC7s6m+yjHn1NYgCM88EtAklBAoiZzJaq4ulvuZsbi+r287wxCJ7/z0za099RkD5azXOCupD+PYlGtDONOxOaHTB38oQpi8kFOKdxguuJ3oWL/gsgUbkG9umejTtfpxRV8TLqXKYLzr0SZ1K+BXTG73Ye/U9LitPaOgpM1zUP8xu4n2WIa6Ahhnow3/7Rpaf7CW/4dPhzqINlBLet1bIlNlNVC1WFPNDV79AAf5FjiKhpcH7bUvojTc3SKUIyDzDlc140X7EebzN+J test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIErkTdmv0GQWOykbxhJdU8Dz8CqMRdCS6BZN7rVgb6Qe test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADl9cFNgeHKLh3cPhjr2QpnAYIN5w1jmm4AHIvz8r9XcLxl9+wrH0JCHGXNCdKQ4Sz/11wMc1eV6WQA= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBcLHSACx0yr1S902vlRRnXA12YpaXIOkZUXoNFb01+CxQBgGpVTEVMuakLpIW/XcD9ltu+c6Czk5WnBKEoHNuI= test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPzeZvkHBTpjn1KGR0ZeW1mCEJhpL0TjvO3nei+16zUh6z3D6rs09rSbzwz9ovDzGd0SHN1vTeanm987Ebkpij4oIlypz3D6xSWXQZTQwKxB0JnOpccdeC7FxcLmHFFecg== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAG3XTlAFoQd4iIBt5KoTHJk46v1AdZSd+wfCSpc2/GCzHNGVqidrKCrypszpkQOW6vYG8riilyfF36rJa9ipjR+5gDApLIe/ix1Fknr8MU6nWoYv2+NBTsEcaS/VTeiUuMKftMtlO2wOaWFcbZBQUAlb3LDnpMyYKCqIXhhl4vqCxBukg== test +ssh-dss AAAAB3NzaC1kc3MAAACBAKCQzxCEIM7yb5fbSmuJCz/+v40WEbULx4JKCXBb7zmRtDtweBqUJXDM1bMjQ4VwGB1EkfMZJQR7rPrOAxfdMdCpkqgPR0+xlljZLVarSJHwt1KR9EudUjUdD2ZrPlvPlG1sAyvMVTK5KCx2QaS2Bn/gukxrvT7DkaqCFHg2P7rLAAAAFQCF+m0B12XxhF1P+3hF3ktGmfYpywAAAIBX+azef1TX90FwS7vyngCrdJsOYqWtj1zQ8MlrwrWL0jdbjcIG/0vcq1x84LTgs+hAPylDBCGGJa/td88kJ6jIUeWiebRUG0zEH8LM+nehQA5O6TuEV3U0Dg73I0Mb04ZzN2GqYqW+KwHdiaU5M8XQv3V/PCmY8d7R5WcMKKDCcQAAAIEAmy0FgQoaWyHdD84b7zD0YjziMsVELAbGWwdxNuNe6wtACcctCdIQigQYaC2HwGjlDKVMjQfd4RWArshg7p0PlEw7//HSgoVhwcBbDpmWGCS6TxSSQpDmdDgiVegERMYjo2BZoZQTuJIx/BhQggVISkELwZfsi1BnPC5Aecq7ILI= test diff --git a/files-jsch/src/test/resources/docker/authorized_keys.KeyPairIT b/files-jsch/src/test/resources/docker/authorized_keys.KeyPairIT new file mode 100644 index 0000000..555a29d --- /dev/null +++ b/files-jsch/src/test/resources/docker/authorized_keys.KeyPairIT @@ -0,0 +1,92 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEE5LImdpZHwScWm81gWgQo2WqeMmKTNIGbWfhFwMgP2YSBt0HnuQ7/Fa88S5Url7ufOwJl8fC7s6m+yjHn1NYgCM88EtAklBAoiZzJaq4ulvuZsbi+r287wxCJ7/z0za099RkD5azXOCupD+PYlGtDONOxOaHTB38oQpi8kFOKdxguuJ3oWL/gsgUbkG9umejTtfpxRV8TLqXKYLzr0SZ1K+BXTG73Ye/U9LitPaOgpM1zUP8xu4n2WIa6Ahhnow3/7Rpaf7CW/4dPhzqINlBLet1bIlNlNVC1WFPNDV79AAf5FjiKhpcH7bUvojTc3SKUIyDzDlc140X7EebzN+J test +ssh-dss AAAAB3NzaC1kc3MAAACBAKCQzxCEIM7yb5fbSmuJCz/+v40WEbULx4JKCXBb7zmRtDtweBqUJXDM1bMjQ4VwGB1EkfMZJQR7rPrOAxfdMdCpkqgPR0+xlljZLVarSJHwt1KR9EudUjUdD2ZrPlvPlG1sAyvMVTK5KCx2QaS2Bn/gukxrvT7DkaqCFHg2P7rLAAAAFQCF+m0B12XxhF1P+3hF3ktGmfYpywAAAIBX+azef1TX90FwS7vyngCrdJsOYqWtj1zQ8MlrwrWL0jdbjcIG/0vcq1x84LTgs+hAPylDBCGGJa/td88kJ6jIUeWiebRUG0zEH8LM+nehQA5O6TuEV3U0Dg73I0Mb04ZzN2GqYqW+KwHdiaU5M8XQv3V/PCmY8d7R5WcMKKDCcQAAAIEAmy0FgQoaWyHdD84b7zD0YjziMsVELAbGWwdxNuNe6wtACcctCdIQigQYaC2HwGjlDKVMjQfd4RWArshg7p0PlEw7//HSgoVhwcBbDpmWGCS6TxSSQpDmdDgiVegERMYjo2BZoZQTuJIx/BhQggVISkELwZfsi1BnPC5Aecq7ILI= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCmukZSkSImcmE/TQhe/pL/Bx3ZNwFIWFbdNy8tzjlN+TSxrVrb7kyH/4skSUleNAb8M69MTKTiospqq0E7bnZ21h8M0qQCTFcRnN5Jk6MLQzaWHG4dz3BSKWzf9wJhnGuFT0F3bhIG14LsewxcJyf2bVh4pAlt9rO0q77/HNSj4045XvUT2M/LwvNF68qXly90ZxsbTGmy6b3QT7wlJmdorThQRNTVp3MYq7X6oJb46QWxe35A3S3UfOKHBw9q2zjdG4UBE4UqkjTqWLyIu+BhG+K6y8/fbVGgX/L1sgxyzPhEIo++ZkXjzx8sTWH3DIE738jBS7o0/rz5zI/CRl8Z test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDNVEfQu64p2GczvWj0AlncuNG+PzPd0jZ1/hfx/JDz1dRVEalcXMtIGRSAH49LzicAMPV8jvwNCa+aj/UF7laDNqjIXvvC+sTA8l8/YilLm6BJ2G8B4sMo7/F8WOAcxrs+j1QT5tAMV88yaefoaXjh9EWTOGesW1OiRfGYnzfhNrx8m8y0qVpYKpFbA5hswEL2VJC6YZxpg2XlMMJSSxBDhJLWBOBlxh1oN5cqv8cDPmwGYlvBYGGcuz6h3krARdXcyHciafoBKZNBimiJ5VNvhmsa7aSkZgH+k6w4Cbddnuo78WtrEGvyf7HN3gBJZHGVFeclkRkeR0RR05HDqyanhOE/fO2HOHW3/x9SSwrnk9I/lZAQlyROP+zOJ3LI4m+iHZUqMfgtN/JkK40l+rNbClGszEFLW+OSbajGoEh+deL+tdwownsuVORpAckoRJdLo03RGam8WMxx8UmkHw8WJBf91bshH1kgQSTIV/lBbFllkwM7+LAWy7Ow1ZGfzsM= test +ssh-dss AAAAB3NzaC1kc3MAAACBAKCQzxCEIM7yb5fbSmuJCz/+v40WEbULx4JKCXBb7zmRtDtweBqUJXDM1bMjQ4VwGB1EkfMZJQR7rPrOAxfdMdCpkqgPR0+xlljZLVarSJHwt1KR9EudUjUdD2ZrPlvPlG1sAyvMVTK5KCx2QaS2Bn/gukxrvT7DkaqCFHg2P7rLAAAAFQCF+m0B12XxhF1P+3hF3ktGmfYpywAAAIBX+azef1TX90FwS7vyngCrdJsOYqWtj1zQ8MlrwrWL0jdbjcIG/0vcq1x84LTgs+hAPylDBCGGJa/td88kJ6jIUeWiebRUG0zEH8LM+nehQA5O6TuEV3U0Dg73I0Mb04ZzN2GqYqW+KwHdiaU5M8XQv3V/PCmY8d7R5WcMKKDCcQAAAIEAmy0FgQoaWyHdD84b7zD0YjziMsVELAbGWwdxNuNe6wtACcctCdIQigQYaC2HwGjlDKVMjQfd4RWArshg7p0PlEw7//HSgoVhwcBbDpmWGCS6TxSSQpDmdDgiVegERMYjo2BZoZQTuJIx/BhQggVISkELwZfsi1BnPC5Aecq7ILI= test +ssh-dss AAAAB3NzaC1kc3MAAACBAMKmaZwslPWE83wv+Ofl9oaMeOR03Yk0cyDc5fSAgjepXZQR5sps0KFORRufI6dLQHY4XLoyPc/Hr0ra+vY/l+p20cJVjQ07oosPU/d2eLdZ66rdjXCyrJPjFjnNJtumnBnJZqWGObKspnWk73vPOflReyGpUFe51PF1usur3uDLAAAAFQC3dSlxZZVNbvqjJpg8/oSMuG27/QAAAIEApkd3miYVc/Cl1QwdqwInIjby1yGNCfFsZALGBYc67lkd5lGBdNlL+fgi+BwC6UXu5OAqWB/b9OtvJR8NRcM77V252IER4t95t2ZdG66M1T5q1aOVL/ehPZFHf0oPXHJLcsybzqKFAtQj5hEbnrwJYW0fWi87C9LApplzPTVYQikAAACAUEu5tFmImzyQsUBMN/j1GWCCSVznjfRHkeGwx5koC0D2iK3mMphnF9avsoX2PrboEbGqy69JPCekKQiPPWxcQRTRqFS/ySkECJ5lJ2zJrW1whjLQEJbOq7WmusSML9UXCpNSwCqhJxPYx5a8Pq7lrrBP21jT/Z/A4m4c4wMkLXo= test +ssh-dss AAAAB3NzaC1kc3MAAACBAPmIBCP1AA8EiTL0VAdzau+1KuRmPkg+H2du4zt4ifhzEx4MscZ7cpBsUAM/EBC/ECWTHzDiWyFFuenvxDlfTi9XxySdOKM6XGLkY7oLsY1y5bShyLrycxDD7MRQ8HpUc9TPzE2PDH4Od6o98e+vdXP0+IDUZGbSuwEnaABX/1+ZAAAAFQDIB9KUd1/gnzM4s4FXCQb7zCnjVwAAAIBptrE9g/ooRnh4bOWiA3/StxbjMRmA9f9bJbM55CinL4yf/GdGmeBp6YjDBxDyk1oXmVJ2hLVXgW6kETbt+D01SouCTgLPlFwLFhqZQ+A4JQOKuLIBaM92hI7sAfTewkxbmAk/e1vyOeyfcQDhbncESzVBZrheXC1fTKv7O2PgbgAAAIBKhGEiNwKNA/pfYvkEbX90/jVoIEKZfqTk+PB2gbVMT98LWPaexrDwEGuCCK3KOtMncDai+0r1ujf1hzNJ4z43Z1n15tPvAIwQsObTL+eUQkVjHA8LXOq8n4yYyPidkjGzWcfBAm4Lm6vKkzFM3VRouQIHpWpjRl3wGTJZkOoH0Q== test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDY+326Z0bUOn8kYUc399P2mr7Eh0E7Nc9zPrv80B05ykfmsCM053SiGYQgwP5aBzZk83PdWdYIK12U9eO5WzTwT2po9PrZT0AvsBGnU7MkzSSlXX79e9ELbGYNWV4DafT/VHxVwCuoctG7XKUorGA3ZHxGbhNWyo/+6LNwyCMcxfpjpCelJWn4MTwFqs0DVme4R2Sy+LaVg3Nv5Vbbi27cIbA+6Orit2ajDUUCHJvcQlpGrM6x9w0zbt4pUI6yHPCHIIqZEPzj1285GCF6ZQzi0TMEf4wHtA8fN1Xgn1vBw+IEkrAAnQUeTWOR1nc6CDFEGTlyYlIuz634I4BwfhBl test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD1ub4yJ8TCXgPaTtASEA034K+85zo2MDlRCB7tHgpmIpK7btPxizYEPEPywVnBRGMJnhIyKKHc7AHzCos3tkGVYNBlDGmnoaZTeZqhmsRSqqxHyfIRQ1T+aGIN4scliZ+iW8RI/djYyfPlcAR3Kku+Q3xsR5K2yzzxZiYB/rD9XjvRRtc7cYZlHaMe2NBtQhh3hamQVSEJMjfSCpq4okULk0UytXhdbPtP/tLDvqeK7op+q97hFS0wRY9xTaNM+qUhuhaKNbhuexgGQBk7We45LcR6O3XwjAB+X7owa4JtKx9oKY93+3qtM+5+6nRnFnkB/GL5fdKPmIswQ1vOSgGeEhg/M0yzX0PEMx4qfczpUp2rFtMTNg9O6Xz9qJCnZy/I1EmK6FEXerpEY0x41mY2NBBor9vataM9IxzJ1VIra8JYjnPqG48V9LFhSEpFfi1CquqJu1fmtxtH4YNWWNQECKp0s+WmxKAu2ZAAr1wl+tNkkiZOaDSluT14qMkSEjU= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCze/5G1FPKvgcnLXqHV4jbUfO+gi75BUKUWKBowdg63kr9e3GdGVmwtBBWTJMyxEWVtJMrH8CARWoyViILQy9oFfbbcwuAnfhVjdPVNpK7RL1ZwCCrsg6UDpE+Vp4rTWzUcDXUyI2MZrePJCwU0+UWdvoEJXwE2Nitb+sS802IsdlwiRj9knzKJ5rVcpGpQ0Qkc0jbeNDfznRAE8ARawkDLcXEio9V6t4psAWf3BbHGYec9PZAYcdzCHcwmLmTT/DSOF+ut2fxvjBydWJzGHmKyozlFNjThgXt0i0fcedLG0oAHNNBL8UeEYQ7H0glbT9rsnMqOAtgBYD/nJGfMkMMjP7NRS0Y0jtkmY8PQ3l7RkHTFeKVkKpKtyD3+LsfuOZkh5A6rhOZgMP9+22AZhIXGho9kJZSSF7B9CWa+TCr5iZo9lLXbeMFRrk5Ltg7BNHkO5Rx8SGroGYGkLXt89+eL+Ht1I4jbq1fWF55YQYnTOovvnNVVCp4i0c8KlNAqSM= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC96PvllVEOgtQdHnaoga3gi8md+/lUwBG0SOv4co9QaJ2kvJZDTiSLmJc4euEuciiplvzKpKSWSVqaZLboXiVVt8MnPmIwIlp8bhIqJ+0431FNWWq6RA/4EszHiARxMIwUqw+svN2GMm5fGr+PK3FR6mXGjiVlv4/5Vb0BCUjgB0vIqq123DmKpxrT6S3Ik5vYCCn0zKtuWxfxp3hpCqC2TdqD7/rFTX/AstmlhTO4FRDEK/7JNBSWY8+9/rQS/c/HSSW7qMGEoGFkELKSYFSdwLcyAMS1HySzNR1SuyBLDsOSaN5iWm5cBqKIK175Uc3Xe3fIn6+C5ld0m/nN75bw6QJiDH7I2jMYDTBpDqztBiqdjcSwvNTogGQg/oJLC7aAz+E5gfkTsg/Q6bPf6quyPIQ+KKH5v2YVFQIw2Te0X7/OZACsqUnCbGiDXCixPTTGoRypcR0MQDJxR497y7zvO4kiqQpsdBoELuhWSW8+9BcovL4/jSyqUEGvGYoh/QM= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqGObM6DyGcra+/eBdm7mIvnLM9oUDUwpT3hRbF7S7V6bb64EuFlduGXJKK+i4/a/ls1Sm3GVwnWvwuRxrVXgv1497MYDzDcflfm+bQLUN38qNscVhn3jdtFuCTIc7VKmtEd9zn4HVzQhUYWq/oiqwOsDeP1QidWbNwgBthCxzhfdi9XUpdpT3Tm/mtLwEz8KZ2kP9VdfXBql6NI64COcI+B2nzE2YwxZsLGFj1orstRZ3WOLTJdLtQ1NhBQqTEwFD06Jhf50wo13xV84jWeZom+ylJQcj9njnfH8Kf7T+1eyBAqCPLvS6kM5Q9J6bGPj67Hkp96zPT/y3Gh1le80o3JrYjo20NqWzO+/OIXilrDoYdUKJbgoM7GXtMlxFNEnj7BIf6TLNZJY9bA6dw1yFs9wOl9LRhC1zUC6kESx8DIiqSpxrZTF0ScgvC/gKqX6K81ovJBklrAuTQpTqGKOW2eY5jqiRAVa7Gx8sPPtHkGqodD/sFOKurggVDf2Y1vs= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBcLHSACx0yr1S902vlRRnXA12YpaXIOkZUXoNFb01+CxQBgGpVTEVMuakLpIW/XcD9ltu+c6Czk5WnBKEoHNuI= test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPKyvhX/3Q32EEhZKF6s+fEzfQwabsSpXgFJ2LwpGxKFxzE2jw4Nvwe9W3YbPyrZU6K2MMA7p7ZdJwdDP/tQ9dhttOGGSkea9Cq4dcdgC9cdBuQ4aGb8crmY3Z5wc1dPxQ== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAG3XTlAFoQd4iIBt5KoTHJk46v1AdZSd+wfCSpc2/GCzHNGVqidrKCrypszpkQOW6vYG8riilyfF36rJa9ipjR+5gDApLIe/ix1Fknr8MU6nWoYv2+NBTsEcaS/VTeiUuMKftMtlO2wOaWFcbZBQUAlb3LDnpMyYKCqIXhhl4vqCxBukg== test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEGMIJlen0L+Zbjr0UfhTGUs1NXKTWqxmLuHwjLXK5HD2ktbu/wWIqWD4fdfZ/bnH2s8FZu2Pw58PM0a7ZpvoGw= test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPKyvhX/3Q32EEhZKF6s+fEzfQwabsSpXgFJ2LwpGxKFxzE2jw4Nvwe9W3YbPyrZU6K2MMA7p7ZdJwdDP/tQ9dhttOGGSkea9Cq4dcdgC9cdBuQ4aGb8crmY3Z5wc1dPxQ== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAEhmKgKa4VuG9k/oiIGUZMLKwIGJ1rwKhHHZ/m4ZGhaWbvBApeDPkSj8WG0uz4o+LnQHD/i9rzWiRr5sd27quHErAHFzzLbUUoRvKpVjGEKWt+5yWoK9kkj/shi4Rv5UOiwZRr7dcHX+5L9ff1KpkxsGUD9mq6dVSVttU4Bpygw5PP5tw== test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKHuAe5N1uLPUpY3t5kyYuISOxUobPZfK8H+CQaJTCALTMFrT63UDDYLyI2xroS67T2bWHkuhX1BHiTGP6JpwL8= test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPzeZvkHBTpjn1KGR0ZeW1mCEJhpL0TjvO3nei+16zUh6z3D6rs09rSbzwz9ovDzGd0SHN1vTeanm987Ebkpij4oIlypz3D6xSWXQZTQwKxB0JnOpccdeC7FxcLmHFFecg== test +ssh-dss AAAAB3NzaC1kc3MAAAEBAO8/3Ymo6sXoRb7gfTBqwAYPO+ck8aKsoBdQIn2oD9cX2EIjXaoUXv/gK8tVN4EN0C8XADEFXpE3AQU5WhVZ11bv0AXOqGOreoP381pN72aphh9o989Ee2PET+wEXZnZ4PmftMcuhtHWC8Pq0coht7CO6Xt56ItQuvj3nxMwYc0hvEFDOpHaFCyqiann9q3W5bLB9+ek2tJg5s1AVQKnlGCwn1fj+FXY1cRFDsp7AxGJZPDSNjq4awEZwECG/QLQYiWT0FVILFv68SxQeGk6OlOGip4dKkdXPIhMqHy0GT0diBUm//UweV44S9IXPF9Xsiidq1Dn0vDe9e5OYNmV8kcAAAAVAKpXVF5GLMgMtlhSvjICPOy/A+T7AAABAQDEFLE91TMvZkCGeAHHXHkjF02m3CafJ7qLej8RoTEeZxV8Op/XlRluKNn97XF5G3DHp/DWGqBgawbBwqEq7l3V97zRHESMlivPr0UuCvjSmFR/on5nFCbl3RtZJOHOUkamtv51zBfk124uw7+bpnm9aiDNUs+AHNC3SLSyvdBbz6+sgt3kqD+FZyk+7TC/sOm+zuNmducFDDQIjldI7gx78b8D4BBx33+P5qW12PSlKGrQ7E1Rnk+o9WMSJosq/D7lV56waSV6/AJE9lruQr3LGEwN28DrvbVWen8KtvivGlJUwl1OK/hMBS5u+QZTIzN5M6iGB2hfJjBbF+LUONDNAAABAHBA/s/1ShLjRiIfenBfnInfjl3a3DSF9M3/o1fuY6EFkNs8MR4P2aeOfk0zE4gmO3vlT7TFs960tdUdIHm4TFOo+PFp4pjvVSO3K93cAjzOTwMI4sPhB3rRJxrDmx1CWVdnBrWQKO78kLpLf63/QB71qEe7MirH4FXZRLFP39gRcWknBtHmez92CFlzKzqLQXAMfaSbZ95lPh/bwgQ8OCuYExuB3aw3QmoQQdzSJRfWfuCEsUMmJ3Aub/yUMkpfe+hmQwkG2v730MXp/uDXfDXhZKkoh+v7Mhm04wzpxtB/C1dYpmfSP/i5ews4+9Lo9me6l54xy3+k05PPBBhnYzw= test +ssh-dss AAAAB3NzaC1kc3MAAAEBAKU4cgWySBcfMzYy9aSOQwvH5JM1/M4UMNIB5BOZocC2FvJbEok0gTTbLCzGI9A0pe2bnr6EWx78v+aIH6p1GOmbhxZVY4+vkEUMH9Ee8v7XMYqz+CCefWWiAH2bXNa1v7LC9mImiuKmcDdHiVLJwrj/TH3c2EUYlcyQSUGRyOmiw5/auITPrAlmwhyB2ZXLC0X1qNKn+nJiwWxe+SsTXV6F38jbZo+fepbnVEqZ3wTUVnW2gpk0z+LRiLM+1mhASykpmRCyggxW7H2NyNGW+9jVTXD8WCqB/z0o+7PVCPS85qpyfNi6PhH+etXiYnyG8iGI19BWZxDmMCG8Sgr+AnEAAAAVALkJetYWk3MacexWEA0Xa78/o9RfAAABAQCeNE28P6R13v2OUqUtBe6oYQPHOHzMFdEk+qJoXyzKmz7QuQiCGuJ90YUST3fhvsBCOY6lPc6zGfeaVb/r2loa6zm3/0DoOXc/dnU4sbB0auohPlwmjFXDwTQF5BRyxuRWtvPuNgKlgwFd/GcnIwlgW/jRIF3j5wopM3m12+yhZnW+gh575hjvf9edpra7Y1u1VR/jYEQRg+GuXFDvcGE3OTWpphAe0zfjwqIOMYYvx/JQ4HMnB+FxJ4svs5aUOPwBXhMCT9YPWEWVr+1GBVf/VlKTrbapNODYEp0tuL2z8FvakgquJ2XGY74+6M1wbhrrDZ+LPd0Iuwcr7N0ghtVYAAABAEU47Jcr0BB3XEcmy/LsL+hSKudgmczV6QD+fxelS4mY3//8INn+wMRyQzgix4B5yGL6OzQ2RBGlqho2TdfLo5UOjIoQzOpZsjZu2TLZezS5Lpx85ZVuLWwqezx1NZqGDXhtDz2tdUV8eNbePWb5788WXYXOW43ya6Z5RMoywkO7sZRziHH4I+WwlorUpDMgzzfGs2j91bpejsBYcSnqcUDcHJedbMmcUQ53OWocK+/MwoYi8+F/UBuwAoZ8wYauasMZ/ph2k8ygzWHCv7NNteyVAY3o5OvrML4sfu9kmgBIZKKfGZHeFptjQdQcOj7fTI3tuqocwiWVohYBF7laKvI= test +ssh-dss AAAAB3NzaC1kc3MAAAEBAIRPWjGVTl5F+y9uOPu8PPtFyjFy+Qclpqww/vdKq7GVAYPU5zdDHPvmEBZKfklfcROB5Ody3Bf7miR3RoLmmK9SZLDqEint+FdbvSWEooFYexZmYukhS9FeGUwttMOz4K4JIAPMVBxbGMPOL2AzPhE45BT2PPUpXMNZTLft2YHYhSIg30nsx+gc7tK7dkdY38pJ3Qtazc3jpyHnxT9o5qA1OdrA09nYHs9osneC4O29uru4Qz+foNP8XbGbkl1XxfooweQiElA9kvoOX+IgtJGgX2fuexAl0VcRmeb8lYxC0KOo9QL/HRW84nC8ZvwhiOXwbvN6X+tmKqTpcYQXgOcAAAAVAKearN3tBfL6HPzkCHzgGrUbSK1zAAABABW+7UNnhWAvSwiHShVSWm+eHjY56rss8OoYCvVRxDTLYhnZs8GvjlHJTTvuPbNMiWYe/vqwKhBDkNJe2cw2on58GsJwLLmdhsZzGRuCZ5twFLusZsjjRImjBTY5ZNYhdbhM4/527a7bSRLT+QPsedmaEvJHeSMHwDYwEb/WMsYnL7XnQTaPRjetz1a64zWIYQU6PTHW+HRrQDT2Dh/knymQvcw3QC5/ZwNf9k1HS8FdE5HC9G9eQfPCGjiFGLfVfAKbq6HslLZO4Ea8WnFLm5CHz3+mFF+1fHXkahGxlOkPGMr/kSeIvcneiMAqUDINC7VcXShVAvATGyaVh324uwoAAAEBAIM8p/hq3+3N5V885fGyBuO6H9RFmmFsuA5c70CXm3A7CHLNh1+ql72WzaRs3DyTICexOtEy/m+2jFd2suKaSa4F74OMQv4KaeeXrGuC8+EGdWFMN0Tf5UkvR/3igCFdxUbhfEa7qbMJBi5fOkkY1/OCldoHHWwWmp7uJA9oY7AVaFcwVyzOOQtUxBL6rtAE8Bcff88H2TFT2vf0WX/fKMKss69uzbphIIxnqnk152lA/gVWcT/mhCWa6NBIGJqKTNppKExTcLBcLlglqmLFi616A7gm8Y1+gi3dnDVu/hWiyz8uOSE31LB4oJGX4fkhOcKgwMJbB00F2ZGPT7JM4JU= test +ssh-dss AAAAB3NzaC1kc3MAAAEBAOex/Q9Z8ZKXJbCIQnLHKuJdhGNpdIIOsmFYpNGCClPz4ZTZmk0Dbw7sn2KrshVAbi2RXAIz4gJn+MfHRF7okwgAgN94mMP6VG+D4B2ZpgyyqWRYSSdV5AmbKc2VDX+DVK9qKmHlEX3Wjm4Yfw3s1kGN1/eWwI5MrHB7w5tA8Fw8iUfsRLnwTyGPBXbR1ATqT/obLAic6vogcpGojg1s61k8Rlz2bVMaYRu/RQsIdxvUVi27jn00ClTaMGWKH1ZMhnmjedvN7qI7GcAUgNXFjOGa786aPXzJDC0JdNcmDoei9G/ynTJOpMMo8oy4bJLnVqL/kS8v5k+P4JeIhwvgp+MAAAAVALGpbWxdf1geBNR9INuHLoevUHjvAAABAQDijQTRDHymwb7TO5Lxy2P5MBvxlhTlRN4C+KIo5rnZMX+oFeEX5/hG18grUn7FYdx5/4AHP5EhK4GIcoKYJqnEjXvfSuvBvD2OxES8y9QI5Y0DZSGyAsV0p0/9N6a/khkdJ5Zs9S8iFeeJkevIVsCskLRkz6rrBbsuiXB/N3ojtetEQfFuznFVgEJyBNrk7QuVvu43nWnzYaF98ah2o4C4HdjAds57XzyhDks5kJsi1mryl5jvbCSxcf4iZW9OsOHYcjifwfuM+hz2oYYf87obEIIMdOtsvHjMEfTmBqechUqqsRP6wzv0u6g8rReMBldaoG2HFGpwjvMM+PGU/zdWAAABAAhLKqKGhmXzW3ZugZftycNwrxXFeVXFBywlC1qtDH8N/0aW4AvhWXBk2gw+rDXQudpNlVbtV9sT/+9JBM5iQ9txRfDjVeEzdOuXR2ch51p9Ep7KTYoSaBrBqL5KCxsz12G1+KJazMmlbpxQbdWYrAJrlRjTcHnYCDuhyUV/yCnrR+7VMAx5FyIduuRtYrCgo/+Z/Z9UklPU2596/pp7uozkCBR+RkOm7+9ldU+U9YiaUWymVmXhohFJvy9O6tqj3kZYQll2h7NnGpKVeIqp8wNW5BoCyVwV+fIuyCVsWg5vOKTBu5GR2Kc3AMc18Uq+WEeC/jsXZS53ItONrdNsQNk= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDsUXXjApfYYCtRK2LrymKvcZpS4cf6agyOj1/VW48g1GVtJM4g5kOjHWRqqsQZJCb8R9CvChCsJp4LPOS3tC36dSS52loeMoti7Z3GtKORjPGKDO/byt7V68l02KZdaO6U91D73a7y9v0g+JsLtR4xkZMd1zXLK3NnT43rtLg116YBsLxAxbAC8eeC1xBF7MNzdLbmg8Gp9dA6iNnDlUhDsf2/FJ8a43WbU8inaNJh6eGd6NtL1fb9iDWho+JEhw7WbyoNrBAf4YlqO+3Y0oO1A4ezOspv3Vx8CkoZTrtyCzVCPEIV/3wSK40VQaMxt4ptbPwQK9mSUI2dG3kuzkxV test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCXX5GPy2zMByhViM5aW+lVH3oPVBVvsfXjb7Afy2ldttHHkDMhoQKMNUScBTdyLwHrje6c0C99aYNkSl5TLXePWx4nIkNFrEyhOfooY8CHPTuhCjfy+4A/psxeE5aoI5YGfffSr0uOVOUdhV3X3p1SmQsybzSiFz0THULrtirxoCbf5a1UGNafYoXPvtSS3/qGJnsnbxE/gL2mTEvOyLI7o3ffFIgbfOranbuow/HSboX/Uc9i2FsbJR4i6K5A6KB3NIL3gFJHxzjumJB4QXt7mUXzo0FWnOXLORFrzYd1Rf26AVcUc24wwStVAOtXO3EIr/AylwgsqWqoStOzoRFB test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCdPnSRJDt/YuU5Oj1gq6JKYsJacWbgsdU+vpzAT7IiIVOoGB7oPi8EGcZSt1kIJBc9Lf70uRYCbCmq3v3EOLGpSdhV16oKQygSgSZawq2YwYHzHnonp6akLfQQXwE/nnubX3S6iYAaJSrJ50APhKfQXYsz4CH2alrzYb9Y2TDl4hKfxYABgMza3O02BFooK2tbxy2+0fbOKB/qjamGVE/CoMcv4ChB9FDGdUr0AVy7BYtfELAZ1dPrUh7lMCXa6JRRhFtdRWQGKmfM1nH/KpuzB+yZsl8kfHf47qiX/mwTQM87JMxm7HuV8pHHr7DNFs9g30CreF+hbY1Cx+IKh+eR test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCPJDCMNOM7Huv+NNeP626Slq54aPceMLEaoqlfvLJiQf11b8jXcKfFgLlp/f0Ltk10p+5hAGlzBw/nlYRU85i7Eq75kSb6LPcQ3SNY1/NkrNcZcFf4p/AKeC0s1qgbypvjWy7Zk8AUooPFrhIo/kKaPMouq4hcyENz2VKgpdJ21SzTxjDASj7zd9DZP8uc1Lsg3ftbr8Mi+MFFQRnFPLv/w2zIoLo6xwO0UplUIyAGAsRPDdRZQ50h9b5BFyFLnXUlwI/Jrpgs2J31Vg8J2j2Y3BFDgn/Mf/P9Rsvdo4fp/y+W195198VQ2gsH2+Az+3Id6ySsGhztBP5Pl2Z2K5Pt test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCo/5qxk4V6ZZGDFHDeVBr7ayBkc85+7SPuuY2OnBSrObqo06lw3nlH8dcloRzCt7qYvbmWTutlTujgSOJuvd3A= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAJ3Mot6VLEkkG5HscBLY3geI20FnwrgQi2wzAKiktPDhF39ZavVlOMzvkIGgQDxmo8WoQllY52ed4e8+7KQ76M= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCDseBAbWBVvO3A6gqqd2+Qm9uSTBPEFvvj/huI35w7iYhMmIjWAYwi9av867hF9GsRJaPGSkoWUIcA6QBXarAM= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKIrHi0RIW1gbd9+rJLTPJ5qAyXzNjEHidpxBzRnN22jDcnvBjqnAtfWPBjazs2KHSn/RP+KtyejY1Q+mMikDDk= test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBCTReLXxPo93rpl1xlrrx7yYZgD0DzRieTvFO9C3a8Z8blHuZHNy2iljsIYeCPOtK9lcivx+faOrp5FdE2VDroGPdDY2ZAuwF4/UxBiGNNEXWBhRFkupBcM6RbyjvCXaA== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBRylqfIuL/Bzq92j3g4UHd3TOlU2aG1U/2W76hrf1svwNPNe5d81xXA6j7Uh+AxOYfYrJRQr9KotRvd8WcxbikMZs5O/dKG+xx8WmTcgvu+jVW4lCbZrZk4S7/hqIlVjw== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPATBzcVXx8g+unFg+8RAEvjiY/ZSb98vuIQ6MAtFCkNU2zgUHI9m/Jn9FitFd/F7KlIh4G5/wMB2dGhks/FYSk+OxgAmhjif6lKLfD5veRCLhRnVbhTQm1fOmU0Sy+FSA== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGxZq9s9JfIvKluYcWj/f+OuiV0bJ9RLE8rT+8qF0syMvzZZVRqLmF+a9VxJ4uxxRVvmMQ1CXb5TGtLVdBv1SrviKWMoaBw/sosSMr8v3eoQ04f4bs32fx8/dN4JQbCZEg== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACuthNYwDJn4I/b0fdhJdD3ykWe5gMBS91H9qWt0NHh4QTYxVyu3npbLveRVr9AWoF36UYIVF0Z3WjFV4u4fOPapQFNlGDf1DNn8aipy/5iUKY02z+AxnWIIjToN0IPPizMw3LApwmy0UaBSw7sZyfxNHD0Nu8wXsP8A6HG+Y01zSMAqg== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAEUP4llO/qJcKrD5gZ6L+8+JzEU5RLICud1jgVh+QgtuuAZQUztdee3GEPmR+C9Mv5cAHg138OCNcyRI1eukjFNDgAq3xeh0V6BwjZZur+v5zyTnXQASBwuI+iofZZqS0z8lsM48eIluTZuo3siTlv6uvFLs4ovN/cg67QyGIpsKusESQ== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGIVlZVYKq71fKe0Uxj73A5WPmvTuohTARpvnJ0VB5zIHFEtHRuI7Qkn6pRGv8rbKDRKygv1kZc6rFZ2fgfhLxXcQHG1LwWM8x+XXoWMJM0C0kpCEErKd/tCZstQqg4v2JO0iG5+wmoZ90Cru912t24fhFGfVeFIbLN/xqznIm0dZTN/Q== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACcIEokqqdXr1cB4vJksHNPuIb4HhwaRqaCi22yZa1slGr5+KQhWc6TaRP2yEsXaskuwThVy5hNjfONP/uB5IMj6QCxnR3bA/knuB2n7YRFlbIICLmCxr6QI5bz271MxCB+ccJO1L8sSfEsS6IcxH7O3rYv7hDadwcY9t5VmsJ3t7A67w== test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINknjInRhIeQB0yCD/N+ilxpTNwD2OGA776pYHI8bOdZ test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFYLKsn61Pg/YYmumVe7oXik0o+Ca/P3vvN0Om3KCHqo test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHh90f1nUVIpoKQoYxtD66cTGgLZ33D3hCxVqS/N5R3u test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILhbcrIlE43dp+IlosYszkS0c4zpwC60ec1MRwZuxwvr test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADnTDrr21+87QbcPF+2Ayh61ot/Jtf0c9yTy126ki8Um/UUEW6uy87s/9gpS0OHyoH3GO3WgsUoe/AA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADnwdqOa0pIeeorEzsEY7kskGeyZfhpkYZtbA/iR5Nyp0cbPZnl9UFsfKQIYKJwPWwnim8BXTkEq6YA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADk5uIOJgx7OfFkbkRV43yMP37dcnIVemLPjlaH/WRKjLTwueFIgN5Mc4UYcIZtlbAaUhiiEvW7aYQA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADkVNtCwBIBWO41k+QYmuMromfii6QbjVKRxjxbTYGPDaw8u+gSCHAnqgaxi9qkc7HU0U01BJI5tJgA= test +ssh-dss AAAAB3NzaC1kc3MAAAEBAM6Xy9zencF2KDhq1r0gTXBdA4AL5Rtg5GYd4tx6qlSxk3ZX634ude5bV8Io2UrsgvIDQHSfj+1J4sDpWnhMtT12KucddwAzLhb+/5gYEr1UCZtPn1WlsvOXrJ9pmAt/MB1KppyG38UX3QK6Z04oWspqHWJBfO0oquUUGx5Ua9JSneJCrzk++Sd1GkhjZElR51Hm7MKV9KZejn/tUZQK2FRjpfP50GsIlAs1v31OkY1glhgs90/6v7PyV1ljp27Nw1aNL+CxOjA/wYnFo+jE5+EsW81oT8C06n4yrcFySWFR96wOfb2fAuCl4K9Ma4jN+7J0RLMY1++KzCsAtppi4KMAAAAVAMbFE/Q0VL6xGj975l/7AqEVxWH7AAABAQCbU609B2d7d5EipWyjlETAs64vaKBXOAREcQr8HQfAkWmSUBr//brBdNWeA4TVla4hyKL1CBiP1Hi6pDQ12lCB+J30ikRqdi45y2MvOR8OfEDy3sn94sLwswARnMwdfHWrPepfyVdqOnIlhsYWzLpc5EhfOThvL8YZ78tPTCqyui8COi3TznOFfW/do0ydEEmfQwJTlHi6dpFiokh+mpQVOlstehmgKzrolLEjccS/vnxSqsdWDwKErbtg0bYhhV3rztiMcMozoBblgCSDiVmvGBAO8GnIoyX5/J+UfuwWYQKYrtuZRfB4hRyRU4zKopPfQrZkc1Jummk959KgfeTIAAABAQCayyiO1dYHPHF6g21swekxrukaXix6sab4m+OLm0bNRa+7nI3A09wG+otKajznna/mdjcIaWuhdG5S4+l+aPypIUWTdM0krUu0A+9Pu9YpIgVgEFKVQX9PBcP5+WHanMaeYmLOCEP1WqFMJ5HiRCJmssOKwZJygH/KEMIGaDGhYnSmN6R+41j8sDAoY9/00WlSTKqpmVGTe7bJGWomnhT08RsATIyvM2i/J13qrh5UfGprj6GsaE9q+Zjg47oMdZIRSgyp4jw9coQoR68TemOmkODmXaoixfrGT1FC9LbqOmN/iDwv0QvLUen3RTbbB2lE3LwObBXJRpd44RjeMo7c test +ssh-dss AAAAB3NzaC1kc3MAAAEBALvd0uyCDZFuKcpphdcnMEYVXnoy16rW5XIFhlxmvVSUDDz3DAGyXISyhyQ43BhI8R0OeOr6VpivujFu0srXrcxcgsAy6yv/6uAvaeLfekc/JErS7OqVqACFmt5ISkEBcvWKnUseFRwoy2KCeiEo2GyKwyV00caLnYb1Th2CUNRFZcH5p5XLj3gwFbPf7EdkKV0PHWV4E5ptFVVT/aRijCJRQn//FlSNlG6iOfUF4IRUpz05qivVGaHLWNOw1fSXK1Pc53qQqCAaEict3arhQCwSEKl1xktSNBDVmeapLPj7LNcZIGvAA52Tpg3qR1rp/8a+bdtteNkbRg4Gfx2uBuMAAAAVAKFdLforDVztYQzh7pFjN8qAjogVAAABACTsvZOw0aPNwdQFiaQik1F08HQrjlp298vgbjIZFrgABE4xeuWMydEcxAKKEhy7Y9Go4l9shUddkHgk3Y+OLvAsifowqfc8Fpsqxh1WKLxnw9NO5rjenO+kNeiwhX/oWa1VBMOa/O+iGTvCy1XcW3u0E1LqGVAx3DdkP6EHV91ma3d1LWih/RR8ClERNaovnETash8NQCHAmKejIObneLtNRqyaiNohYK0t5EAtP4JutVwrsnj3ZoSKrCZct2wVrMUkkqCsKcKygCsr29uk1xpVYBkv9zUn6V199ZB/uXanRLcL/vq0UYWGV9nXU0shVanMwHNQaMk8oWnd6hTPB6EAAAEADwMN1uDvQqkyKel83xNG4Er3LsSCx6iARNWpaV1ZLNrtf2h6MnYCKdSdyVgRjIL5coBQB5yZb/+CXm9QgysH+K5FHXgnh4hjz8W8/Magj+qm3/SKDTmbLAsNvvvenQvayTWUGNQlfQET/tHvGRy7gz4r95phWGmxS8Zu1NkT0RHl6xxcSqWmvvjG/m2MuNo+5lfKFa2+j+Is59HwN3ZK5umpPlJl4uM5SkyFD+iUbptOVCXdVV4xRhIzYy4wllADQQHkbNQtNGJT7JEHuot92J3FWOCEQVw4F06j8NsdghKWIwGSlcHLHAbA8zZDq11R6vnhOFDwmfekxCwLXaBlGQ== test +ssh-dss AAAAB3NzaC1kc3MAAAEBAOGfXe0SXEr7JIMEZQo6rkTc6CfPIWsgqDYem72p84pn6KykGD6RonyqDEfDoS0XpwIY2sCZLmwauEhTQtBZpMyix4sD49w+SjaHUP1aoAT7iPFvsXXGNw9/jgK5a67NzWxmcl5XMQG/VDjtVLWzNfl9OXHMSNmGUKQmlPZKXRrm06SYVZMDsRbgSJgi6PNOatuVvfR+b2hK+ppMpQNKnHXe/vpgcblZ1ViPUTH4vG+04PkidIbwHIIl5Yv69lUYOcycxR3yzd8ZC3a7qzVkxet+tGWCD4/PzfWEP+/G2TWXbRuxV6YEaq9POmI0JuZa7CAZ8iA/OFtYua3TB4cUsSsAAAAVAJzJoUVy/fnPi6UuPLR8cCyhqB3HAAABAQDH61jRVXoAqKJRHgIWhBsrQMmAvOqVHyrALSlAchNT5cso4e7Rd9ajJFg+hVZGKu5PoQdhp/8ERk8mnABKue6r9qO/6qT8NvCRuI0Wb8Sxgw5q/4hzo4BVdx4UhLqTRl9rF7B5wLSjV75HIsqW61ZcbT9+VdYpRaNg8/Te1jfqwgrTndaD7jhkhU2P0Z7vHSRlzaeSksF6b+SRFu+bnQ6neBxZZTuMrSfKOSpp2ltLUvWrQ48GJ0I/uE1HbQehz9Tn0Q3VA2BjpfKgj8bLE5YFokveuqjFULF7EiL4CmAt6qhiQOl/f6zKR4gaLUjkVuvU/uyTr/da7/FfOJouETpFAAABAERldAO0JWHLr1nClDX+ZPN8XbbpjSaWO+fFLQO0A7QE/2gKJ8lt5qi39GOOhmUO10johHaOupqRqe4iCRnBxMJeLxXmS2KbcwDke+IBRaid81ccocpcPOrpov582DjJuwZ0394Akyrc7uMonjBy+JJaPwpXHQS4SXRe7NybuEE4S+DsV6DNx9WG5n5PFh4YELbzdP2oJZ3hyZ2MiZgo02ShziEDPI1vQ4BBAsknp6Ubzt40Gt1yjyNMbGpv8IIRZzu+H8CIRnzbCYKT8mRKe0HvTG48c6la0/wvvG62zfmbFYgkw0tQxgekpf3ojNpRJZ1vcQPgtvQ5IjkCYSF/ss4= test +ssh-dss AAAAB3NzaC1kc3MAAAEBAIYJqKE0jtG+HpbHurIu+kVIuQLrMrgXhL7woINHOHGfb/0O85u+3DsSy+s7f/M9bWUIqRtt2+JV2J/V28mDgF7IEeiP3OjSYspmoxnCy8d6g313NcYYoCqG9f/yWrzSXQtK3hD8+KNqm68GQx2L6lceyyHn3CgUFTwHw0MNntOfAk94PCoM5wOZCfqx9sZfTVk+uQb++aG8xcAqPoPQdTgaqrFa9lrp2Ul8RhM/eDpCmByg8I5tIsbk17IVzS0gLp9slfInhwr2UjX2wIOlVOnNmS8UHLAwhDtdtHcWYdFbEMpO9hFMJCz39JqnJ7g8qjct/bfMxAEqhwlGpgbYrz0AAAAVAL66GQDORVX2on1ic6drzCN/9f4ZAAABAA8eU3kzUacUj50ggFzpTLuBBztILvjIpNGnXy7SUG5EWCZXLvBPTjJf9idxL/0P42AlJqKpaZSsdDLeaPbyUsQ0B9R+eTVDKQd5po5kBmWcxqMJh0f1XU1CYFZgN2cvzmtyx9euekEka/j1mqGLuXHRpX0vak/wsy3T8mEPlqLr09c6hPUDG5U8NXEy3aWS5gRRj7wPDLAi1sFGo4ReUfjn6LJglbCi71tSirnV0dJJvVGpXA17RzetDgZmIjdDur6XdGz+6B07puj/OUTTCFSFzJC0wNKGS/OjY9+59ziFj+M7LCA7HUAlYeL4n83EsZp4fHI09JyuLJppPo/4xxAAAAEAU2ThrVHZ3fT8JuXT4dgJVtgoBTQVf1vbzKA/o5yrOCKj+pPLFv+mXSJxLWvdSUeHkaRZF76hCwSpy6K+Wt9sRSuL6qTPog8TT7Yhk0YgfMd/wXwYlhhoH6HqRIIQRHd1bIOn+n1DUWQHS2w6Ksd6jnVDd5cte+sBRNFeNjRJPUg2dzFqYQUgf5bfDu6/mhISduez7BctGb0cWtVPyjzQivJRUVnLiUJJE3V0saEC2FYOalknyW2x99tolUn4YwOLjUuQm1oKZ+BEbXlgGe4pKe/JxB3nYHT78wP7ArnhImPGK8b0+/JD4nAA15GEkFLRV/u9qyZJe/iwbpqKz2vB/Q== test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCclEvT+gGh6exRwmgPhsN+PLMpcNltztLQ1x8iKOsxCCbXWXRxfvvG/ye8lp4kEOWIZ1224UoG1401aNBVTGNUT7SVyAFSdnHG7AGgFnLwJcxrsEHLX/f+gj/m4syNJfAg1PrS4O8IeaEnTdql9x4cYkzd+Jpa/OHZLgltc8SHezhmxrc5Ylm1IMHu6gxJb0WwQ1mIZ44IAIaYvNflal7dP8v//bY+6kvS5iARk6hDfiR/wH3926r7U0pB3siNDdz5yq7rjvTg6igtOhQDSnd9Drp0yHjBCyQoiDqP1P2jhyfz1iDdf6u71CFhZK20rfbsUUHCjWilB00onvjjIDYJ test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCMTxeJiE+rerxDpf3UhJRVvaWulraY8VZryHhSPnHCODQw3MsQ0X6ECmTs8c3YeNRBRhiaB6zOQorUFqFbAwO2XIFQgDGk9qSJF1kwsVXKnReFqUQ4MQGjRt5odjHc4+00KnmfdLUubvLGJmzHYJ4Ia1+EEyzkUik4nVTxdreXrvFEIEej/QHJlhVSqsL2oKXKVMLAJ6OvGt9K1pFSJRCyedHVmR/z3tfsiFqpw0R2P2WhhkK9jXG47tJcG9v5HPrGJqd3D+W982/YUFNFvLMVkq5qU4wEDFVh1u7Z95dABtZm2TwzhrYfxdAIcYbZLpbFozWX+f2mEQRAbWws2YE7 test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCyu2eTD/3HRZnFhKMiy7YLGW7WUsT1uEgRCUufHFbjwZqSdUMXplB7wD5g/iombD36oZtFGpZWjiReT8hh2k2dR5pzF7Hg9yTZosuBu8YRSTjwicc/NHTcDfl5kQFSGz40azFXWn1xJqPRQesDGZ2rQVloCxW771AVS5hZvAybtM/qf6En7PxHJE4Eg7klvjH2bzhacDQch56u/ij+/ipwtqkWt40k6sMQ7xtRIQH4AUNlqQ+3qpZxKfAl+NCbJ/V8Mj9BM3WuyeD6DYPwBT1mHXug9KWi7aii2c/UvDv1xpF7u0mTRSW4Dkm7FTEbX8iPP1QF3ze87YEaF1qRkeMf test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnTnSVyVw7nyqnxy/WWUUuFswXUsdwvNtojMVuqltV3CTDHfX6+uMu+XBC6Gqr/AzHvwzOPvzR+/k+m5fV5zLvjfe3zTU1gs5EmOtu3YbOnn6il/IvVvU9ILq/5Dg89aIbSYG9R28iu8KciZxHOOpjgFpmoCiMAg8x4OWRHc6irKj5VKTXMJC+5pmb4QZlSKal7KPqKyAsKII6IxMjpPNJejZm/Omw3QZ19YIN02ttbM8rwoLuPLi1DoZiAUvF7J3XTEUcDAn5y5upltk1Dx+bWhuth/sYZ26qksxPr+KmtHwJ5R8pWJ4CXtENlwxQiAWftVWW8MeQlyN17qPW+Z9x test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLoiF7se+l2KwZh9oxhfTASh5diPDNvYbVgS98Q4yPE9uExiDDMeX2Nu9XLtnevuTWrmcAF6u6/ozI3BIkeHiBQ= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGrSiZUMuXa3aTNDF/H+rTZb4wUBapMIf9QXzfoJI6hOYPcCtHGH8KChdeZ2U9W3ohzX/WZIX8IQ9fpxcGyvpbc= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDMTBUib+twute16i5Rs2zKlVDMam5/RMWuFcXs2dvIs0kLP5N06diHYHBqvMPT9N336KMChz0/O4S/dXR1pwHE= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMQNLejYwJxwVlAGIVi55Lb0zfkz18CEAwqDtrr6xs2sz+vqRU1xadOgEzHeWovBqCXdC9wVja202PyupH5QPsg= test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBnR7wyGL+IOT7A7vKr4Do1OGlsMhBFN28GuKUohpkd5rvvZfe/9UNbdobFa38KmLWpxzAlL9Go3AK6+7OpWz6HE8JHdrJGyRYoS87XVv4oIIzmp8B8GThUYiDt8Qh8FmA== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGdfKrIhLUFtiMfBOFdF1LulPAF3OKNqlahGbA0bf/LXPf7tmbJUs+GvozsqYAbsMhed1vmwfBwpaQdIG97JRSK4MK4yIrCjDjGwqmakX/hg/WUQZPi17463t9zSzFZ8xA== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNShRribjCP+pkCkAMwdj2LjxNrZjIKHtylcTdohEgfHJTXzub4tbq6odNS3izPwgMEfX6M6Vl5NBIstk3c34+kDuJ/a+Cyn7lZ9HZPdDuaKE66PNp3zGK9linfzn3iFBA== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBKGDqZyyEHaKkg0ijDH9nRu86AiXmUNJcKWC05ksthEMheDRMyZtyqzIIwQI9Goum+Tktu3LeQS4fBh9aNHwMP2xN5uvj/uUYxN1dmQmLQ4NsQhpz+DY5TtxHJ/tUKvNpA== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADwW+/zw0OOBYVS2jEOym6OctYKqb1/uSTegImb7XOcklV6HZSnkGDrp89gzXL6AgoFWMQkySgR93l1j22kCwxGWQFQBLalq5ekPGpqBiE31QNot0QKPat2f4O8bw0DO5sUNPmH7ImKmyxAjHGiCPhi5egoIgrgzHoGnx1ynMr32Pntkg== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGCln1Y+fp/uQTIVhjoNHuuRGVSzHc8shdMTGUQz0q+TpDSEf+SViws/gS0+V73QggV5TcUqOIOra4uD8mGSieuUwHea4ieXw+cbcts9lpYdjWkFmgSGwYfIVyosFZ+aE07We7fNRqSUpH5gyBpdSpFKAiBTwtv73FOIwz88I82E4HVQw== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAA37JVxrRSMTbtG8SUUuPzzwjsrumeJD+100IbXoAcXK0mPbGei04TdyMTpfy1SDWL3YND5CSzcuHMOjApNKrxJ6wB8A5LE178vFKb7kvYo5SJq+YAbv7utQqlMH4sdKdywGwvdSTSZXCu+mzx9ebdoyRsmPS7vBrJYKcQPHiBDa9iVMA== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAKniAtk8UOBIVa/348YbbZAyiLgJKpbH+2rEB0tHG7YYf+fAAGAZJsE7K7bl68XzunBT1HguytUpcjzNgOS2hx2AAVJT1ObdU0IK8+B0DTXrntG8BMKpDjpHZJr/RQLEDljHt6kc355TLoKZygF7vUujNjceWFjSmBlFeCbr5Fl4KoXw== test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFrJ3kAPWlhD8uR/Mo1dqhqtJIYACuH6vIh7/oP+r8UY test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINnWA1NU5+6rlguJ4vDwB7Ro3wTO+ntaSPfmWIXjp384 test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGGj5a3dN4+iyqsAXenA/CzejFmBNyZinnYHhXXcjbBV test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEy7J6rEkUafMJmtqE8SpWZg/uYx6EcME5FH3E3HB3rR test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADkhFs87N5abAoXAcsLiYTDOCujVJngP6C9j5dSttY/EBgCQBMK60a0vt1lF7b7Drby5kqg7+IvkGgA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADldNmjRx57BM40rrc4DabuF0L+RhEkOcDC8/jAhtbdTO0X+sqgITai5gSk1JGlIrCRIF83Kvs0TKIA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADkVoUhABT+RHfYEGAMKySSyUSoJFj6HHcz87U+io4Dpe4ye88DGUU2Lnxfux/WQq61DPHWxQHo4GoA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADkdO1HUptQ2ksEmblqqTM8loY2Q/Ep003qRxW753XVaz8eh9CejJvTO2FYtyHNib5syBD814By7C4A= test +ssh-dss AAAAB3NzaC1kc3MAAACBAOGamTQ4i4AEWhMGssPosXwsaLBWIsldlycRQEfRCRlmi0hzdmYvuJJn+YQ2Jf1QJ49jTTxlCuFJLrDct+UxpittxRtDb+AdDpmhGp/zMeNdPBsfeLX9REfANyveQrfT9hqZ2gIbMOzJ0wRPgInmSRi22A0It7vxGaw47J5bAEf/AAAAFQD/Q9R8eCV6qL7aQr4E4aNOzZ5JdwAAAIAaUAApSPx+S6f+H49ml7sHdAQC4lVyKVQVh0RG55/rsIrd/dSYgo6LZ522vzNgxw2BmBeFJUpNZkQzIGmxN2FkTmlSgOxZkRH2oZA2j04vcskJgIiN8K1rhrpRUtJjcKXnF9YRn0eR4o1xph74ySPmIWX12GULB4R1j4nysqDmbwAAAIBL1zviIOohyjKKILovL9rPlTLHlcdXkyd5S3PZImq3CXDY5mQFlxH8r/hEnwMBBTAHmun7ptcdH/R5kZVB6v7xsAdJXDYiYOyoLI8yL4aWbjs3flriPXVmCb0hCh4n6pSKVR5IHLzsABABlJTULxSkvWcKwMms7arQm46WR6MPlQ== test +ssh-dss AAAAB3NzaC1kc3MAAACBAMui93cyTI7R14Prqeet0joIvIWWGSw09hk6qDZvgKW2zzs5X1BTScrnLMnaKOgYtwDbFN4OQsBLNZu+PzcAqfwJBeTD4mGLRVk1k1zK6jXpdPr3V1sqQP7OEVa6MF6XsIf/Yu3zFvqpwkoNnrCcSrZxLMnnW83Ya6NG5d7WGBw/AAAAFQCHbVl3CZvAlhbRo/yMAY4do6Gg3QAAAIBb2u2D16qhuFq8B8UjBt1HSo7R/KPjRI0YzrCpJNoYjB743wARQMl5yDU67IuiIAzjxFZBiK5u+mTKpuO1v7MeTc9IFQ3ObR6N9PO1wUAZ1uv0KSesnxjgbZNIHwGwzJ88T43LEbugZleVEifbbIZl7puEgQdFko9zEgqGhOu3VQAAAIBKDmTnJZliffrAxCElJnnrM2SLo+Dl/cdtCDdI+3RBG7wTPWqMF8C9VNC5ssbi8QLDwl+OPrSIERPb3VXtjkVxQna36JK3od0aSx90VZkv7RPeL1Gt6Q/F+Td6D7p5ZCGjHaQkEk4kfPjTUnLxKn5FGxmY1HV7ceXo17AElmfmyg== test +ssh-dss AAAAB3NzaC1kc3MAAACBALDWcFnpSWiwGbuyqVzo3ieAm2K9+BIUYjPlA69UCM1GulxzCj9ImbQDvMsuQ3tKpMH6925NoGU5G/atRnqXouJFNcOi43kqtHx8tE1vV9v8im3+gGsOF97pVgtBMHNQr8sjDN0FrtB5MvLMc9c5b/1lhrUj5PeupHIJzrhvAvsHAAAAFQCeztLdFSYoOE+Nq/XDQh0BuuuM+wAAAIEAh8p4L7O0vUW76jSJvE3v/yPG8bcBpZporUTvG2DqKP/rISyRblRoYlwmlaTzyrC0/HrUOqw/1fiyorr+KoxJVhOavlKVq/YSGpxmrgt4eS+VXr5BJyRGxDaPF4kTAu+2/FVMu4D1clK8BuR++phu9DWN32sWAzw2IYwOfhB0b+0AAACBAIUb/bDKE9DAGpG+zMyida0JTIIPHqWvkSa2dUXU9J+4iV+Bws+X3bEggZDeeDAKd9FmFTujek25ihIJD7tOu/CCFWEcUZ46aff/RQ5bfCKsJ2SyoSRvFGGal3541YvgK5A3DgzRjEGhlF5KfJ7HIcAdTq5CYA8uDBZngA8oeQfz test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCOSYk6+5VS4tJqK2Z095cPYHjlUT4Gqm2MM5xdzM2nK9bd4slUyz8fFYpWAIvu1G6Q5z9e/2Kfcme86lCjN/lidSNHQBpz6xW2WQTuTPfSPW2lBblRovlezhypdHTdMt1NB7B499py1BF1iLO5Y8URPCztTXawneMnUjvUp8g6dDKVDGNBssgwZ3TR8Fj/vrOkgafwtJAl/X4CJBXdt45dLre5eRXEoWDxTdW/GxUw/PtV/mW4Psheo3/RoIFequh2m7U8uSdXKvGqm0ZlpSTSoen7uELSd+9iY3E4ULdm03Pom/O3WO+DOCmRb5YUTNFKZUVop3DKizjEggy9mbb5W2iJp+6K31YWGWHCR/HhFr/61R6yQomXzHhbqAEGH1FsBmpRDa6c7a/b8Xl4w4kbBlOOs21G+tvL9l3hYqBfWpPE8A72WGdkzaOltvt9TEDroNI2yopNWTVXuUvJ2maFqiwf/4cYpGZqu3V4ccBpTJbhHQ3G3w5XVQ6Np42ez8= test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDIUGLfSbG/8i18Y53PL59UJ71RN4XTKJEvVljZM9K0HvFkc0wOlzTOXoOAEJiTIq6JXVWjq31BJoC/x20ySmaj6hSOs6NGrnyxXzSbM4E+PzJo74avh+NXZ6ojD3CKg7XuShgX5+PUqEiAffK3TdzRX4NlkIfiIaHi1O+Q5drSGwnoB1C5sNeimLDhO9IPo699OvQu1Obh06gygWh5pnu0NBuiv7rWwBx9S80fJvqhAQATxv2RWFDqcvn/T0WApJs5FCpyLxKozTpOenB9OGaEEz3mpg6H4KPL6UwhyC7YLr3rRKyjff08uELvdQzsO8q6tO8uOaQngLFhLe5lrlV test +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCszdi6vvJvN331KkQI2SVEtrwBHQunxWGfMP3HnSn4aH656Mcx/9a33V/W5KwKoSgVeRsHVnbjby10yP1PaU898hILUDFBazlJUyfxvMybiP6TC6LMomCFqoncaNJ+hR2BsEQmrIfmD+cvIFADz8zPtjDeOSAPQumP42C5uXJKNKYM8nmpCUrHo8qSvbVLTX/UC8mZweenxY21IxnKSZkkuLkRtqp7+p+xc6bYNfXcdb6LyrR/jmD2GsnEXSmWFPxvYGAErUZBSAMF2xYGG34l+lCB7Pzvpfv/mB0MK46YL+pdKC/B3JjDu2izt7cEBTlhd/TwzNah5OfsXtVJBNlYi9FU/Ee7A6+ylb5WnNasU165ngIVBBL5UNg8Hg/W6XJJmAVZg1PsRbikNNszKeaRrTC3X35PEr6uab5hlcORcAWV2FYoYWN8iIv0rFJyaE7InpxSHp/G3lE6XHddcJrq6BdmURez3jShrCYg7DdnE9Nx7dBX1rbJ46lLxKY5lQs= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGwG9ZwJjv29NIb0Cx1QsCboiidCStHoza+zQQMvs9nCBMRnABQL9ecKQeUGClj22ll8qRg6vWbHrQn8+ZUCgZg= test +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKjOhkR/tN6oSku7uJXEqDZh3HsYFeCi6F1rnDR1roSpLy2HQYGLPjalt+E+sQhayJBOLJd8j2idjIRNK9trrYA= test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBDTDzvrOOKrQoRhWJCf5ZyYk2ugd2wAayLt13XYBPJHArsgwCKMktyW6+dmKmskqnVDEPqQ42RBO0kcJ78y+3l2i1iNXr5dDSkJ/2J28L4YxzEOU6HhVn8wBA0wPYNpsgw== test +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGYBJ8F7NOBihGiGXVBApYIXiLlNVW+SSYANC3okjqsax5eFcwyfQBRSWSJxAXKA1aV5vtYMN/nBwvg5kEgftHi3L3Pye4VA1PKyoa73E4T7AsAJ1eRrJp31ecSXi1f9HQ== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBABdYMYEOU8AxcsHYPdHwtbndVLfJZsCFFQ8oMUt+xHi/MwISVkojGjk2FOtUeUnsc8SWXHEUC6GDSqy25+42GsxWQGL7/IyR2X0Iu9rUooKrKg4EsvQNEDfq9Joj7zuWOmAQBQK392fbXdYYILjSAaG5at9mhiw8Kr65ShK2IUv4n4dnA== test +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACWNfRwfrfQb+K8d5+VutrDWme+Pk6UyiDvbuS48bCg3e9ptaWHQVtQ6xm1yLYUeF/ZVWn20+s2DntLGKX8dFCaSwEHVmX4oj6hx2V5zprevf3GShfyz+HXt4qzoPzVMyOPY/po4d5oxM1RM7oWaqNwvuVhU9Zk5MbJSzKq3atWJv/+AQ== test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINKulAsCNhbGFNT4DlsMmQt57Y1/i40T2EWpeUWjiDL6 test +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2oQIAotMhMO8LyWecYBIXFlxSFRoxm8A0Hpbn1mEOJ test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADlrhnFj5z0wgxD0IY7ECoZE3/8v9cUWP7VJnyp0YPY0aLKBLnagj5/GMDV71bYW56Tl7AFx3EQvxYA= test +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADn6JKamPkXumrMxQnui53u1e7U0WkROtrmraifd6LEDfbEW78xEmNPSVZARg0ydaeNYzc+iEGG/cYA= test diff --git a/files-jsch/src/test/resources/docker/dropbear_rsa_host_key b/files-jsch/src/test/resources/docker/dropbear_rsa_host_key new file mode 100644 index 0000000..cbee9e8 Binary files /dev/null and b/files-jsch/src/test/resources/docker/dropbear_rsa_host_key differ diff --git a/files-jsch/src/test/resources/docker/id_dsa b/files-jsch/src/test/resources/docker/id_dsa new file mode 100644 index 0000000..3082f4d --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_dsa @@ -0,0 +1,12 @@ +-----BEGIN DSA PRIVATE KEY----- +MIIBuwIBAAKBgQCgkM8QhCDO8m+X20priQs//r+NFhG1C8eCSglwW+85kbQ7cHga +lCVwzNWzI0OFcBgdRJHzGSUEe6z6zgMX3THQqZKoD0dPsZZY2S1Wq0iR8LdSkfRL +nVI1HQ9maz5bz5RtbAMrzFUyuSgsdkGktgZ/4LpMa70+w5GqghR4Nj+6ywIVAIX6 +bQHXZfGEXU/7eEXeS0aZ9inLAoGAV/ms3n9U1/dBcEu78p4Aq3SbDmKlrY9c0PDJ +a8K1i9I3W43CBv9L3KtcfOC04LPoQD8pQwQhhiWv7XfPJCeoyFHlonm0VBtMxB/C +zPp3oUAOTuk7hFd1NA4O9yNDG9OGczdhqmKlvisB3YmlOTPF0L91fzwpmPHe0eVn +DCigwnECgYEAmy0FgQoaWyHdD84b7zD0YjziMsVELAbGWwdxNuNe6wtACcctCdIQ +igQYaC2HwGjlDKVMjQfd4RWArshg7p0PlEw7//HSgoVhwcBbDpmWGCS6TxSSQpDm +dDgiVegERMYjo2BZoZQTuJIx/BhQggVISkELwZfsi1BnPC5Aecq7ILICFE+sM/BQ +4c0QZhjod9UaZk7O2icH +-----END DSA PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/id_dsa.pub b/files-jsch/src/test/resources/docker/id_dsa.pub new file mode 100644 index 0000000..a7af401 --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_dsa.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAKCQzxCEIM7yb5fbSmuJCz/+v40WEbULx4JKCXBb7zmRtDtweBqUJXDM1bMjQ4VwGB1EkfMZJQR7rPrOAxfdMdCpkqgPR0+xlljZLVarSJHwt1KR9EudUjUdD2ZrPlvPlG1sAyvMVTK5KCx2QaS2Bn/gukxrvT7DkaqCFHg2P7rLAAAAFQCF+m0B12XxhF1P+3hF3ktGmfYpywAAAIBX+azef1TX90FwS7vyngCrdJsOYqWtj1zQ8MlrwrWL0jdbjcIG/0vcq1x84LTgs+hAPylDBCGGJa/td88kJ6jIUeWiebRUG0zEH8LM+nehQA5O6TuEV3U0Dg73I0Mb04ZzN2GqYqW+KwHdiaU5M8XQv3V/PCmY8d7R5WcMKKDCcQAAAIEAmy0FgQoaWyHdD84b7zD0YjziMsVELAbGWwdxNuNe6wtACcctCdIQigQYaC2HwGjlDKVMjQfd4RWArshg7p0PlEw7//HSgoVhwcBbDpmWGCS6TxSSQpDmdDgiVegERMYjo2BZoZQTuJIx/BhQggVISkELwZfsi1BnPC5Aecq7ILI= test diff --git a/files-jsch/src/test/resources/docker/id_ecdsa256 b/files-jsch/src/test/resources/docker/id_ecdsa256 new file mode 100644 index 0000000..7f52db7 --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_ecdsa256 @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIGO2c0qkiVbPaFdw2b7jABHbSmnWp8K3LGq9UOTQ9HcdoAoGCCqGSM49 +AwEHoUQDQgAEFwsdIALHTKvVL3Ta+VFGdcDXZilpcg6RlReg0VvTX4LFAGAalVMR +Uy5qQukhb9dwP2W275zoLOTlacEoSgc24g== +-----END EC PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/id_ecdsa256.pub b/files-jsch/src/test/resources/docker/id_ecdsa256.pub new file mode 100644 index 0000000..5da42fd --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_ecdsa256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBcLHSACx0yr1S902vlRRnXA12YpaXIOkZUXoNFb01+CxQBgGpVTEVMuakLpIW/XcD9ltu+c6Czk5WnBKEoHNuI= test diff --git a/files-jsch/src/test/resources/docker/id_ecdsa384 b/files-jsch/src/test/resources/docker/id_ecdsa384 new file mode 100644 index 0000000..57e53f5 --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_ecdsa384 @@ -0,0 +1,6 @@ +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDAltVKtxP2QrTdQJa3REnwY3hMd8qT9iz5w4l0fmG534ktEcDI4lTVA +rE+vgI/LN5ugBwYFK4EEACKhZANiAAT83mb5BwU6Y59ShkdGXltZghCYaS9E47zt +53ovtes1Ies9w+q7NPa0m88M/aLw8xndEhzdb03mp5vfOxG5KYo+KCJcqc9w+sUl +l0GU0MCsQdCZzqXHHXguxcXC5hxRXnI= +-----END EC PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/id_ecdsa384.pub b/files-jsch/src/test/resources/docker/id_ecdsa384.pub new file mode 100644 index 0000000..8692273 --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_ecdsa384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPzeZvkHBTpjn1KGR0ZeW1mCEJhpL0TjvO3nei+16zUh6z3D6rs09rSbzwz9ovDzGd0SHN1vTeanm987Ebkpij4oIlypz3D6xSWXQZTQwKxB0JnOpccdeC7FxcLmHFFecg== test diff --git a/files-jsch/src/test/resources/docker/id_ecdsa521 b/files-jsch/src/test/resources/docker/id_ecdsa521 new file mode 100644 index 0000000..7a018db --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_ecdsa521 @@ -0,0 +1,7 @@ +-----BEGIN EC PRIVATE KEY----- +MIHcAgEBBEIBe/WgCLfCZlZnwZ04GQOkIW4i0az4cizRHXV0PvVyqUceJnMPNDXE +nJeKsmYdfzAbB3llyY5f2SILMuEYRXzIdR+gBwYFK4EEACOhgYkDgYYABAG3XTlA +FoQd4iIBt5KoTHJk46v1AdZSd+wfCSpc2/GCzHNGVqidrKCrypszpkQOW6vYG8ri +ilyfF36rJa9ipjR+5gDApLIe/ix1Fknr8MU6nWoYv2+NBTsEcaS/VTeiUuMKftMt +lO2wOaWFcbZBQUAlb3LDnpMyYKCqIXhhl4vqCxBukg== +-----END EC PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/id_ecdsa521.pub b/files-jsch/src/test/resources/docker/id_ecdsa521.pub new file mode 100644 index 0000000..deda1e6 --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_ecdsa521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAG3XTlAFoQd4iIBt5KoTHJk46v1AdZSd+wfCSpc2/GCzHNGVqidrKCrypszpkQOW6vYG8riilyfF36rJa9ipjR+5gDApLIe/ix1Fknr8MU6nWoYv2+NBTsEcaS/VTeiUuMKftMtlO2wOaWFcbZBQUAlb3LDnpMyYKCqIXhhl4vqCxBukg== test diff --git a/files-jsch/src/test/resources/docker/id_ed25519 b/files-jsch/src/test/resources/docker/id_ed25519 new file mode 100644 index 0000000..b5a5d3e --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_ed25519 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBK5E3Zr9BkFjspG8YSXVPA8/AqjEXQkugWTe61YG+kHgAAAIjbWs6021rO +tAAAAAtzc2gtZWQyNTUxOQAAACBK5E3Zr9BkFjspG8YSXVPA8/AqjEXQkugWTe61YG+kHg +AAAEBIs55uel1B0GufLAbr1fLpT5/trE3ONzFutHRmQgbTE0rkTdmv0GQWOykbxhJdU8Dz +8CqMRdCS6BZN7rVgb6QeAAAABHRlc3QB +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/id_ed25519.pub b/files-jsch/src/test/resources/docker/id_ed25519.pub new file mode 100644 index 0000000..2b18299 --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIErkTdmv0GQWOykbxhJdU8Dz8CqMRdCS6BZN7rVgb6Qe test diff --git a/files-jsch/src/test/resources/docker/id_ed448 b/files-jsch/src/test/resources/docker/id_ed448 new file mode 100644 index 0000000..5d7244e --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_ed448 @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAASgAAAAlzc2gtZW +Q0NDgAAAA5fXBTYHhyi4d3D4Y69kKZwGCDecNY5puAByL8/K/V3C8ZffsKx9CQhxlzQnSk +OEs/9dcDHNXlelkAAAAA0G1DR+ptQ0fqAAAACXNzaC1lZDQ0OAAAADl9cFNgeHKLh3cPhj +r2QpnAYIN5w1jmm4AHIvz8r9XcLxl9+wrH0JCHGXNCdKQ4Sz/11wMc1eV6WQAAAAByxh+E +aQEVBsa+DYaC8NvVcVQuch3GAITIqyweByuntdsKU/UYVIgFyWG+LIsMkKsRYHXpetH3Mt +JhfXBTYHhyi4d3D4Y69kKZwGCDecNY5puAByL8/K/V3C8ZffsKx9CQhxlzQnSkOEs/9dcD +HNXlelkAAAAABHRlc3Q= +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/id_ed448.pub b/files-jsch/src/test/resources/docker/id_ed448.pub new file mode 100644 index 0000000..2f98392 --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_ed448.pub @@ -0,0 +1 @@ +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADl9cFNgeHKLh3cPhjr2QpnAYIN5w1jmm4AHIvz8r9XcLxl9+wrH0JCHGXNCdKQ4Sz/11wMc1eV6WQA= test diff --git a/files-jsch/src/test/resources/docker/id_rsa b/files-jsch/src/test/resources/docker/id_rsa new file mode 100644 index 0000000..04cc610 --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxBOSyJnaWR8EnFpvNYFoEKNlqnjJikzSBm1n4RcDID9mEgbd +B57kO/xWvPEuVK5e7nzsCZfHwu7Opvsox59TWIAjPPBLQJJQQKImcyWquLpb7mbG +4vq9vO8MQie/89M2tPfUZA+Ws1zgrqQ/j2JRrQzjTsTmh0wd/KEKYvJBTincYLri +d6Fi/4LIFG5Bvbpno07X6cUVfEy6lymC869EmdSvgV0xu92Hv1PS4rT2joKTNc1D +/MbuJ9liGugIYZ6MN/+0aWn+wlv+HT4c6iDZQS3rdWyJTZTVQtVhTzQ1e/QAH+RY +4ioaXB+21L6I03N0ilCMg8w5XNeNF+xHm8zfiQIDAQABAoIBAFcia6SB2qqoQiBJ +gApDC7/5HQD41HeFVABPNk6tL1JxWibArhDo53xQ0yr78x7mroFFOo+4x6k0/tjt +3F15zEwUBZTbJFX5r3hJltSs9IVLK/dx7QiJbTA8+zGbAI6WWkVprRUaaFok+CQk +EarignFovMsQbXHKvbi6yCbYiXT2FUhzI9sk1y6wedAAW9Bgw7AHwI8bq5pSlnmH +o58PprKi0tCeWCW+zuCSJeKoGwbYTMq5Glx7lqiLdi28xe/FctAfiQI4bLWtl8Aw +vm0UWBK9IHP0K0U2iErNs1l80Kc6av1PVIZTVLaRJfrLMRO+k/HGSndUlGhpl1ds +QB29axECgYEA9w+Fc3WeY5aBKZP8v4Mmn9+OIgfMYzxgoQuCpXGgLmi5r5of/COc +4+94XZ82o/s2InUKPLPwuox5dbRrbqp21dSpDtpFNKTEhHA+4Q8Vm7OMg7k+1NMj +lckUiDp7jqAqSGSfDyPU0hPS7ztFsfu3dDpSXHFfLR+Putuub31a5aMCgYEAyyvL +dWwMVG5jiQzIdgWWB622XkYj61T/v0nrT8obsZQ1SVuAx7gL2/fKPkQt3JZvCoQq +G6nTd3buKClsSgDAG72S1Kh7QeMiskSIQ7coBr8yD4JVuVF3Ej+CxYoDfRaMahDC +aAlvqSqtyAWdKSBgw4SJmFehFkBSaBQOqd2zwOMCgYEAjGXypZFyqA9waStjF4Y1 +XecSA37BBKfcFcjPvqsapyT2WLdSc9vdHZ8JnNKMn5VzxxMR9TsgkzKfF2enIaWX +kJ6gUAN0Dp2P299ZrFYBJpFbScPs088EyusRUvVBH+UfAjLEPTtUhumjFjZtvd5Y ++6DlCwmWV7M2365Vq5/mFtcCgYBKJrbAKWoqmotrZqX3JTzHmqf3qmPLrR8WQrVG +Tr5ixnPYTTQoMEzrwHko1ptiaeknDmuL4ySR8m8ja1Lhg3qZ5Uukn8U0m+B67bZJ ++Jv8MmXj8S62TzxFBbcFv8RcMBGZWHqLm/4L4M7CNQJSE0Ydtc6+MiVRyLIK2F8t +sgO7/wKBgEIXdAzaOBmw0Qk5BJTI5areYjQw7lFE7gi7EvjHmhZr/JjSg9fkVk4O +lNskEfC6DZKcE3KV3mDIi0xzsz+D5nIj1n+b/6Hd5rFwxpWoassA0BIP+6U/AyYc +TQxZKRN4RkFLYRMNegx71RRnyvNyU1dcam9J3wgVtlBz24QkEPZX +-----END RSA PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/id_rsa.pub b/files-jsch/src/test/resources/docker/id_rsa.pub new file mode 100644 index 0000000..3a82e75 --- /dev/null +++ b/files-jsch/src/test/resources/docker/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEE5LImdpZHwScWm81gWgQo2WqeMmKTNIGbWfhFwMgP2YSBt0HnuQ7/Fa88S5Url7ufOwJl8fC7s6m+yjHn1NYgCM88EtAklBAoiZzJaq4ulvuZsbi+r287wxCJ7/z0za099RkD5azXOCupD+PYlGtDONOxOaHTB38oQpi8kFOKdxguuJ3oWL/gsgUbkG9umejTtfpxRV8TLqXKYLzr0SZ1K+BXTG73Ye/U9LitPaOgpM1zUP8xu4n2WIa6Ahhnow3/7Rpaf7CW/4dPhzqINlBLet1bIlNlNVC1WFPNDV79AAf5FjiKhpcH7bUvojTc3SKUIyDzDlc140X7EebzN+J test diff --git a/files-jsch/src/test/resources/docker/ssh_host_dsa_key b/files-jsch/src/test/resources/docker/ssh_host_dsa_key new file mode 100644 index 0000000..c6e76d5 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_dsa_key @@ -0,0 +1,21 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABsgAAAAdzc2gtZH +NzAAAAgQDCpmmcLJT1hPN8L/jn5faGjHjkdN2JNHMg3OX0gII3qV2UEebKbNChTkUbnyOn +S0B2OFy6Mj3Px69K2vr2P5fqdtHCVY0NO6KLD1P3dni3Weuq3Y1wsqyT4xY5zSbbppwZyW +alhjmyrKZ1pO97zzn5UXshqVBXudTxdbrLq97gywAAABUAt3UpcWWVTW76oyaYPP6EjLht +u/0AAACBAKZHd5omFXPwpdUMHasCJyI28tchjQnxbGQCxgWHOu5ZHeZRgXTZS/n4IvgcAu +lF7uTgKlgf2/TrbyUfDUXDO+1dudiBEeLfebdmXRuujNU+atWjlS/3oT2RR39KD1xyS3LM +m86ihQLUI+YRG568CWFtH1ovOwvSwKaZcz01WEIpAAAAgFBLubRZiJs8kLFATDf49Rlggk +lc5430R5HhsMeZKAtA9oit5jKYZxfWr7KF9j626BGxqsuvSTwnpCkIjz1sXEEU0ahUv8kp +BAieZSdsya1tcIYy0BCWzqu1prrEjC/VFwqTUsAqoScT2MeWvD6u5a6wT9tY0/2fwOJuHO +MDJC16AAAB4Pd1QNz3dUDcAAAAB3NzaC1kc3MAAACBAMKmaZwslPWE83wv+Ofl9oaMeOR0 +3Yk0cyDc5fSAgjepXZQR5sps0KFORRufI6dLQHY4XLoyPc/Hr0ra+vY/l+p20cJVjQ07oo +sPU/d2eLdZ66rdjXCyrJPjFjnNJtumnBnJZqWGObKspnWk73vPOflReyGpUFe51PF1usur +3uDLAAAAFQC3dSlxZZVNbvqjJpg8/oSMuG27/QAAAIEApkd3miYVc/Cl1QwdqwInIjby1y +GNCfFsZALGBYc67lkd5lGBdNlL+fgi+BwC6UXu5OAqWB/b9OtvJR8NRcM77V252IER4t95 +t2ZdG66M1T5q1aOVL/ehPZFHf0oPXHJLcsybzqKFAtQj5hEbnrwJYW0fWi87C9LApplzPT +VYQikAAACAUEu5tFmImzyQsUBMN/j1GWCCSVznjfRHkeGwx5koC0D2iK3mMphnF9avsoX2 +PrboEbGqy69JPCekKQiPPWxcQRTRqFS/ySkECJ5lJ2zJrW1whjLQEJbOq7WmusSML9UXCp +NSwCqhJxPYx5a8Pq7lrrBP21jT/Z/A4m4c4wMkLXoAAAAUSIVjC6KPZkvx3kYnCstVPm2t +cIoAAAAEdGVzdAECAwQFBg== +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/ssh_host_dsa_key.pub b/files-jsch/src/test/resources/docker/ssh_host_dsa_key.pub new file mode 100644 index 0000000..6ec6ea5 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_dsa_key.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAMKmaZwslPWE83wv+Ofl9oaMeOR03Yk0cyDc5fSAgjepXZQR5sps0KFORRufI6dLQHY4XLoyPc/Hr0ra+vY/l+p20cJVjQ07oosPU/d2eLdZ66rdjXCyrJPjFjnNJtumnBnJZqWGObKspnWk73vPOflReyGpUFe51PF1usur3uDLAAAAFQC3dSlxZZVNbvqjJpg8/oSMuG27/QAAAIEApkd3miYVc/Cl1QwdqwInIjby1yGNCfFsZALGBYc67lkd5lGBdNlL+fgi+BwC6UXu5OAqWB/b9OtvJR8NRcM77V252IER4t95t2ZdG66M1T5q1aOVL/ehPZFHf0oPXHJLcsybzqKFAtQj5hEbnrwJYW0fWi87C9LApplzPTVYQikAAACAUEu5tFmImzyQsUBMN/j1GWCCSVznjfRHkeGwx5koC0D2iK3mMphnF9avsoX2PrboEbGqy69JPCekKQiPPWxcQRTRqFS/ySkECJ5lJ2zJrW1whjLQEJbOq7WmusSML9UXCpNSwCqhJxPYx5a8Pq7lrrBP21jT/Z/A4m4c4wMkLXo= test diff --git a/files-jsch/src/test/resources/docker/ssh_host_ecdsa256_key b/files-jsch/src/test/resources/docker/ssh_host_ecdsa256_key new file mode 100644 index 0000000..dc32d13 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_ecdsa256_key @@ -0,0 +1,8 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQRBjCCZXp9C/mW469FH4UxlLNTVyk1q +sZi7h8Iy1yuRw9pLW7v8FiKlg+H3X2f25x9rPBWbtj8OfDzNGu2ab6BsAAAAoLQlxre0Jc +a3AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEGMIJlen0L+Zbjr +0UfhTGUs1NXKTWqxmLuHwjLXK5HD2ktbu/wWIqWD4fdfZ/bnH2s8FZu2Pw58PM0a7ZpvoG +wAAAAhALqL7wU3p+D026Wt1/hUKAL0xMloZFq6/hk4qm1LbCmgAAAABHRlc3QBAgM= +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/ssh_host_ecdsa256_key.pub b/files-jsch/src/test/resources/docker/ssh_host_ecdsa256_key.pub new file mode 100644 index 0000000..3c2e892 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_ecdsa256_key.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEGMIJlen0L+Zbjr0UfhTGUs1NXKTWqxmLuHwjLXK5HD2ktbu/wWIqWD4fdfZ/bnH2s8FZu2Pw58PM0a7ZpvoGw= test diff --git a/files-jsch/src/test/resources/docker/ssh_host_ecdsa384_key b/files-jsch/src/test/resources/docker/ssh_host_ecdsa384_key new file mode 100644 index 0000000..8a6fb55 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_ecdsa384_key @@ -0,0 +1,10 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS +1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQTysr4V/90N9hBIWSherPnxM30MGm7E +qV4BSdi8KRsShccxNo8ODb8HvVt2Gz8q2VOitjDAO6e2XScHQz/7UPXYbbThhkpHmvQquH +XHYAvXHQbkOGhm/HK5mN2ecHNXT8UAAADQ/hL8wv4S/MIAAAATZWNkc2Etc2hhMi1uaXN0 +cDM4NAAAAAhuaXN0cDM4NAAAAGEE8rK+Ff/dDfYQSFkoXqz58TN9DBpuxKleAUnYvCkbEo +XHMTaPDg2/B71bdhs/KtlTorYwwDuntl0nB0M/+1D12G204YZKR5r0Krh1x2AL1x0G5Dho +ZvxyuZjdnnBzV0/FAAAAMQChI2+z1qVCRLNqJg9SPkA1YgSR3KrNTegm0FN+lRRTlj+bgl +AAum+tBb9q85G8M1oAAAAEdGVzdAECAw== +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/ssh_host_ecdsa384_key.pub b/files-jsch/src/test/resources/docker/ssh_host_ecdsa384_key.pub new file mode 100644 index 0000000..cf85613 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_ecdsa384_key.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPKyvhX/3Q32EEhZKF6s+fEzfQwabsSpXgFJ2LwpGxKFxzE2jw4Nvwe9W3YbPyrZU6K2MMA7p7ZdJwdDP/tQ9dhttOGGSkea9Cq4dcdgC9cdBuQ4aGb8crmY3Z5wc1dPxQ== test diff --git a/files-jsch/src/test/resources/docker/ssh_host_ecdsa521_key b/files-jsch/src/test/resources/docker/ssh_host_ecdsa521_key new file mode 100644 index 0000000..60947db --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_ecdsa521_key @@ -0,0 +1,12 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS +1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBIZioCmuFbhvZP6IiBlGTCysCBida +8CoRx2f5uGRoWlm7wQKXgz5Eo/FhtLs+KPi50Bw/4va81oka+bHdu6rhxKwBxc8y21FKEb +yqVYxhClrfuclqCvZJI/7IYuEb+VDosGUa+3XB1/uS/X39SqZMbBlA/ZqunVUlbbVOAaco +MOTz+bcAAAEIhI9q7oSPau4AAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ +AAAIUEASGYqAprhW4b2T+iIgZRkwsrAgYnWvAqEcdn+bhkaFpZu8ECl4M+RKPxYbS7Pij4 +udAcP+L2vNaJGvmx3buq4cSsAcXPMttRShG8qlWMYQpa37nJagr2SSP+yGLhG/lQ6LBlGv +t1wdf7kv19/UqmTGwZQP2arp1VJW21TgGnKDDk8/m3AAAAQgF8ZZM2aCmx9w1sEKrJVR0f +aoGPQYtuCFU3gHbBpbHDMzW7bmDZz/TVieEGY/Bv5JNpx6e9DjsNEr5LBCZYUlt87gAAAA +R0ZXN0AQIDBAUG +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/ssh_host_ecdsa521_key.pub b/files-jsch/src/test/resources/docker/ssh_host_ecdsa521_key.pub new file mode 100644 index 0000000..71b85e1 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_ecdsa521_key.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAEhmKgKa4VuG9k/oiIGUZMLKwIGJ1rwKhHHZ/m4ZGhaWbvBApeDPkSj8WG0uz4o+LnQHD/i9rzWiRr5sd27quHErAHFzzLbUUoRvKpVjGEKWt+5yWoK9kkj/shi4Rv5UOiwZRr7dcHX+5L9ff1KpkxsGUD9mq6dVSVttU4Bpygw5PP5tw== test diff --git a/files-jsch/src/test/resources/docker/ssh_host_ed25519_key b/files-jsch/src/test/resources/docker/ssh_host_ed25519_key new file mode 100644 index 0000000..2442fcc --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_ed25519_key @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBCPG6z5518A6IFxDrio2t6uZgQhgS2JHlkccUT7R204gAAAIgS2Q3NEtkN +zQAAAAtzc2gtZWQyNTUxOQAAACBCPG6z5518A6IFxDrio2t6uZgQhgS2JHlkccUT7R204g +AAAECdJnXoCSUoYHzHlcXkSeaH0GYzMhD88yjppCIIJZnd40I8brPnnXwDogXEOuKja3q5 +mBCGBLYkeWRxxRPtHbTiAAAABHRlc3QB +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/ssh_host_ed25519_key.pub b/files-jsch/src/test/resources/docker/ssh_host_ed25519_key.pub new file mode 100644 index 0000000..d2e1f5a --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_ed25519_key.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEI8brPnnXwDogXEOuKja3q5mBCGBLYkeWRxxRPtHbTi test diff --git a/files-jsch/src/test/resources/docker/ssh_host_ed448_key b/files-jsch/src/test/resources/docker/ssh_host_ed448_key new file mode 100644 index 0000000..9290493 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_ed448_key @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAASgAAAAlzc2gtZW +Q0NDgAAAA57hSdc570vS9YNRSe4cSTdxaEcvWEnreS89JtkrkMhAzqeDVPVgkAJUvUWhQU +8Au9wO7pDscv++QAAAAA0Of4PPrn+Dz6AAAACXNzaC1lZDQ0OAAAADnuFJ1znvS9L1g1FJ +7hxJN3FoRy9YSet5Lz0m2SuQyEDOp4NU9WCQAlS9RaFBTwC73A7ukOxy/75AAAAABy9ryb +m32A5PqfyePV4/Aml56Bj2P7cIu3PsKTXdpnDYTaMyaLhaoJ76GBfAaD6sEluVsl/oY2xM +8F7hSdc570vS9YNRSe4cSTdxaEcvWEnreS89JtkrkMhAzqeDVPVgkAJUvUWhQU8Au9wO7p +Dscv++QAAAAABHRlc3Q= +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/ssh_host_ed448_key.pub b/files-jsch/src/test/resources/docker/ssh_host_ed448_key.pub new file mode 100644 index 0000000..58e8097 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_ed448_key.pub @@ -0,0 +1 @@ +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADnuFJ1znvS9L1g1FJ7hxJN3FoRy9YSet5Lz0m2SuQyEDOp4NU9WCQAlS9RaFBTwC73A7ukOxy/75AA= test diff --git a/files-jsch/src/test/resources/docker/ssh_host_rsa_key b/files-jsch/src/test/resources/docker/ssh_host_rsa_key new file mode 100644 index 0000000..12cfe65 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_rsa_key @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEAprpGUpEiJnJhP00IXv6S/wcd2TcBSFhW3TcvLc45Tfk0sa1a2+5M +h/+LJElJXjQG/DOvTEyk4qLKaqtBO252dtYfDNKkAkxXEZzeSZOjC0M2lhxuHc9wUils3/ +cCYZxrhU9Bd24SBteC7HsMXCcn9m1YeKQJbfaztKu+/xzUo+NOOV71E9jPy8LzRevKl5cv +dGcbG0xpsum90E+8JSZnaK04UETU1adzGKu1+qCW+OkFsXt+QN0t1HzihwcPats43RuFAR +OFKpI06li8iLvgYRviusvP321RoF/y9bIMcsz4RCKPvmZF488fLE1h9wyBO9/IwUu6NP68 ++cyPwkZfGQAAA8CLb3Z8i292fAAAAAdzc2gtcnNhAAABAQCmukZSkSImcmE/TQhe/pL/Bx +3ZNwFIWFbdNy8tzjlN+TSxrVrb7kyH/4skSUleNAb8M69MTKTiospqq0E7bnZ21h8M0qQC +TFcRnN5Jk6MLQzaWHG4dz3BSKWzf9wJhnGuFT0F3bhIG14LsewxcJyf2bVh4pAlt9rO0q7 +7/HNSj4045XvUT2M/LwvNF68qXly90ZxsbTGmy6b3QT7wlJmdorThQRNTVp3MYq7X6oJb4 +6QWxe35A3S3UfOKHBw9q2zjdG4UBE4UqkjTqWLyIu+BhG+K6y8/fbVGgX/L1sgxyzPhEIo +++ZkXjzx8sTWH3DIE738jBS7o0/rz5zI/CRl8ZAAAAAwEAAQAAAQEAoUqCzU1p5BeyzDIR +bxSHz/9qeMAllX8gSVEhawadmTkEKP6zbx/RGZ+1U50yRNfACitV4lIjELy7ZQXXTU9Hex +8ZWqZL041Wjctbn4IbsMGNBRrrzBrpZhJ9KQ0kWJmhyo0poAfzmHrURUGka2HV3ydY9xx5 +os+OebHMS6/wZbnoD4CAtMwWzAhKI8wWOk1rNbcAMFt6yajYrOG6egBiv+mEkcWS/BS2jd +vXL2pjU758/gJw3xqcI87Y77dG/oXF90wjmbtXdjAZJxg2OFMxvM1vy4sMucOAqJSkTreR +PtoVeqjKQCeVR6wMlKP1t730wvScovMWP1VWhliftrHSgQAAAIA0FlTE7isLTUuAaoSWhR +ilWfv9ta4R4Q/jbrhUuKIesHzIeVlB1YG0D1tncp6ZDPnlA+yF1IROEcbyxhK3aHAd/5DZ +7B8Csdj9wzo+D55hPt5E1y9wZnf/E9KtYIb8OHT0B+e1z7W3+rRecc3MIKCQVM2j0MlJwj +9dkvzuCDSO5gAAAIEA0+6HmMGF/+xVO2RXsOXiS+fAuX9x8tkOwQjQSqGIU+W+U2hesC3x +ytEgFtK4xh+OpDtl2hdD5aMG2jtYBwCHcgxSbbjLNi5an4A7nKhE1Eg2TyRHBDJ4Wpyn/V +pnvR0tOC7F9/4pQjmxm4Y39F5RfBUNw8eFbrKAU2NjCoeJBvUAAACBAMlldN5RsLvxAUj4 +EYYscVJFWUAlSYI7r6CpJ999gC1JtSag/FDnQCEEYMzRpo2R3AqUrXgYXgaHJf/GA4KPrr +DkzgWGPlq8vmsZ3jWdTT0ylLQ1ulpF8TU+4EDsWtEZ8n4iWrtQAshcCLGtclJRbGB31JKw +pF9E8UH4Kf0TY3kVAAAABHRlc3QBAgMEBQY= +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/docker/ssh_host_rsa_key.pub b/files-jsch/src/test/resources/docker/ssh_host_rsa_key.pub new file mode 100644 index 0000000..93cd655 --- /dev/null +++ b/files-jsch/src/test/resources/docker/ssh_host_rsa_key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCmukZSkSImcmE/TQhe/pL/Bx3ZNwFIWFbdNy8tzjlN+TSxrVrb7kyH/4skSUleNAb8M69MTKTiospqq0E7bnZ21h8M0qQCTFcRnN5Jk6MLQzaWHG4dz3BSKWzf9wJhnGuFT0F3bhIG14LsewxcJyf2bVh4pAlt9rO0q77/HNSj4045XvUT2M/LwvNF68qXly90ZxsbTGmy6b3QT7wlJmdorThQRNTVp3MYq7X6oJb46QWxe35A3S3UfOKHBw9q2zjdG4UBE4UqkjTqWLyIu+BhG+K6y8/fbVGgX/L1sgxyzPhEIo++ZkXjzx8sTWH3DIE738jBS7o0/rz5zI/CRl8Z test diff --git a/files-jsch/src/test/resources/docker/sshd_config b/files-jsch/src/test/resources/docker/sshd_config new file mode 100644 index 0000000..a36327b --- /dev/null +++ b/files-jsch/src/test/resources/docker/sshd_config @@ -0,0 +1,21 @@ +ChallengeResponseAuthentication no +HostbasedAuthentication no +PasswordAuthentication no +PubkeyAuthentication yes +AuthenticationMethods publickey +PubkeyAcceptedKeyTypes ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +UseDNS no +PrintMotd no +PermitRootLogin yes +Subsystem sftp internal-sftp +HostKey /etc/ssh/ssh_host_ecdsa256_key +HostKey /etc/ssh/ssh_host_ecdsa384_key +HostKey /etc/ssh/ssh_host_ecdsa521_key +HostKey /etc/ssh/ssh_host_ed25519_key +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_dsa_key +KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group14-sha256,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1 +HostKeyAlgorithms ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-cbc,blowfish-cbc,arcfour,arcfour256,arcfour128,rijndael-cbc@lysator.liu.se,cast128-cbc +MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-96-etm@openssh.com,hmac-sha1-96,hmac-md5-etm@openssh.com,hmac-md5,hmac-md5-96-etm@openssh.com,hmac-md5-96,hmac-ripemd160,hmac-ripemd160@openssh.com,hmac-ripemd160-etm@openssh.com +LogLevel DEBUG3 diff --git a/files-jsch/src/test/resources/docker/sshd_config.ExtInfoInAuthIT b/files-jsch/src/test/resources/docker/sshd_config.ExtInfoInAuthIT new file mode 100644 index 0000000..73dd194 --- /dev/null +++ b/files-jsch/src/test/resources/docker/sshd_config.ExtInfoInAuthIT @@ -0,0 +1,25 @@ +ChallengeResponseAuthentication no +HostbasedAuthentication no +PasswordAuthentication no +PubkeyAuthentication yes +AuthenticationMethods publickey +PubkeyAcceptedAlgorithms ssh-ed25519 +UseDNS no +PrintMotd no +PermitRootLogin yes +Subsystem sftp internal-sftp +HostKey /etc/ssh/ssh_host_ecdsa256_key +HostKey /etc/ssh/ssh_host_ecdsa384_key +HostKey /etc/ssh/ssh_host_ecdsa521_key +HostKey /etc/ssh/ssh_host_ed25519_key +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_dsa_key +KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group14-sha256,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1 +HostKeyAlgorithms ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc +MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-96-etm@openssh.com,hmac-sha1-96,hmac-md5-etm@openssh.com,hmac-md5,hmac-md5-96-etm@openssh.com,hmac-md5-96 +LogLevel DEBUG3 +Match User rsa + PubkeyAcceptedAlgorithms rsa-sha2-512,rsa-sha2-256,ssh-rsa +Match User ecdsa + PubkeyAcceptedAlgorithms ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256 diff --git a/files-jsch/src/test/resources/docker/sshd_config.openssh96 b/files-jsch/src/test/resources/docker/sshd_config.openssh96 new file mode 100644 index 0000000..12c4064 --- /dev/null +++ b/files-jsch/src/test/resources/docker/sshd_config.openssh96 @@ -0,0 +1,21 @@ +ChallengeResponseAuthentication no +HostbasedAuthentication no +PasswordAuthentication no +PubkeyAuthentication yes +AuthenticationMethods publickey +PubkeyAcceptedAlgorithms ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +UseDNS no +PrintMotd no +PermitRootLogin yes +Subsystem sftp internal-sftp +HostKey /etc/ssh/ssh_host_ecdsa256_key +HostKey /etc/ssh/ssh_host_ecdsa384_key +HostKey /etc/ssh/ssh_host_ecdsa521_key +HostKey /etc/ssh/ssh_host_ed25519_key +HostKey /etc/ssh/ssh_host_rsa_key +HostKey /etc/ssh/ssh_host_dsa_key +KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group14-sha256,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1 +HostKeyAlgorithms ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,ecdsa-sha2-nistp256,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc +MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,hmac-sha1,hmac-sha1-96-etm@openssh.com,hmac-sha1-96,hmac-md5-etm@openssh.com,hmac-md5,hmac-md5-96-etm@openssh.com,hmac-md5-96 +LogLevel DEBUG3 diff --git a/files-jsch/src/test/resources/encrypted_issue_369_rsa_opensshv1 b/files-jsch/src/test/resources/encrypted_issue_369_rsa_opensshv1 new file mode 100644 index 0000000..bb9a270 --- /dev/null +++ b/files-jsch/src/test/resources/encrypted_issue_369_rsa_opensshv1 @@ -0,0 +1,39 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCd+5LZfU +vM8no5y5XUnD99AAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQC96PvllVEO +gtQdHnaoga3gi8md+/lUwBG0SOv4co9QaJ2kvJZDTiSLmJc4euEuciiplvzKpKSWSVqaZL +boXiVVt8MnPmIwIlp8bhIqJ+0431FNWWq6RA/4EszHiARxMIwUqw+svN2GMm5fGr+PK3FR +6mXGjiVlv4/5Vb0BCUjgB0vIqq123DmKpxrT6S3Ik5vYCCn0zKtuWxfxp3hpCqC2TdqD7/ +rFTX/AstmlhTO4FRDEK/7JNBSWY8+9/rQS/c/HSSW7qMGEoGFkELKSYFSdwLcyAMS1HySz +NR1SuyBLDsOSaN5iWm5cBqKIK175Uc3Xe3fIn6+C5ld0m/nN75bw6QJiDH7I2jMYDTBpDq +ztBiqdjcSwvNTogGQg/oJLC7aAz+E5gfkTsg/Q6bPf6quyPIQ+KKH5v2YVFQIw2Te0X7/O +ZACsqUnCbGiDXCixPTTGoRypcR0MQDJxR497y7zvO4kiqQpsdBoELuhWSW8+9BcovL4/jS +yqUEGvGYoh/QMAAAWQCCm+hXX3/lj0BTGT+TRll0G/c21MVlKHmh4pSbWuWcVsNYf0Xk9T +AzQL2Aw2iIlCR0nZAb6fbeWkO7FcZwdUWgEH+qhUE5jnzsrGs8dVbuMQcCo//PRK5wPqGu +z1Na6IUSvuvVUk+t+spmVLOjbgKVmXD8NMz3yRrlb92bP/u10OWvHS2lQf8L4qyG0SCbsR ++mfKuuhquOvL+0hqeMwaMjcIeIDqF7XM3lYStliF8atc0KiyX3jNh3xzHZof791WmXgz6f +kHa0vpY/Ds84hkT0CKa7q7v08V2RXisdwfqs36sMMJDTSJbF8f72mSrX3ZiTDCRVzxxa7K +JOt6eKJMBlobsJvDq9cE1paypIiThIwdrcVKTtdk9QjWiVzalF5xrvYAO5rM9jt1RMvEHi +8uAuAz4rOqGlnNU3ruxTtyynAIk+Ao+TFxRwUfX053oJGCvvUgpQvIhK1PPTAJQxKL299C +Hsid01jDXujRCKURcFcBkjczHMV/Y35tBhRm31E5oQofONaejPLyna5RoKlfmw7feOqh1U +GhJPl0kX0RcOG4WafyXjDP8a4mlkukka2HxoVnI4DTo5rvNNifa1VN27jDcVJG9EoC4KdQ +Jn9jwrQtGgpYUC2Mw1yz8IImIjhzaOwuofuEa8XpSCDWN9EIF1+QNRFb8e8s8PkYG2qB3T +abxXFrZlXjG5xOpv2Nx5FfjLpzRXWNzyjy3boxJ2DMamcKoHsWV0FZe0dyoSWz0pVQc55Z +LcVH3pFM/mfyv5S3bEsoQsDHIoJJ1yAOHoAAKzKj8J6xqvXa6j71JIce68cIYx3Qrs3W92 +xrOAjVlE6M476C4lPJdNBxTdkClvgdnulzv56BYNAoBWtbuymZiwB1TZetSGDsDSdtD2rX +wNGHHdUceau+OEh+rsZpmFzBfN0ezyogzy+5FFVrlKFrcUnIlPm+1cgEncLES9AZttwkjx +HzUxdBgm7rq1sCB22Kld6dN5akRXGFC2DpU3t91XPw1lavTqudGC3ovMFfoFrfJPy95eVt +yAA73aHS93t0xZTxCkXff6sP0E2C/ztq38LP2SpvT7SZa250DLD+v3pdRgz5wYUNlnTlH/ +wvRdqHkEnQWwcOyoO33E/iZS/u88EjfMDMu3JbS/LR+0S7iZS5MSh8O3KUtNR51/qhP/Em +Q6v4R/2+odWi7yr0PrCsuyURaq44o12unHE2pzHmas5mN48+agF6thTUmiMR0csoMs/yEW +TeAbdPTEjxNc3cxvrMmnWhVCLFDNYRScq+O/PEWsPN7ofoYagUUM+TbgGGs7kWOOcyo/oN +gxMowxWX3sraUevHEpl8Fgbapl18W/ppNd1eIT965SiI2cHPzUW6aTPYW6y6BKOBHhX3np +EPf7ApjnUhuHF8Brvikgtu2eHoAA9gKFs/X7sOD8KWIhsV5tIr6psCqalOA4QGdMsmZuaW +GCIfeaCsMM/HcTJZzj8OBBpLAOC0/lSs8Y9qRqDK8Aid12Tp9FrtJKlvt5DsnsR8an4zsJ +e4k9sN7Z+bxVxKyhQulc16hSSUBJTQdCJ3fbdlyklTgx45maL8Q/bQhYlQ2y/EBIASwOwq +Gq5gJw1RNm2CaLBP/tjD+qCjIZ4DsPuHsdv1a51BGiTjt9YqV/eqXdZ+Npz89jFQw7WGc8 +AStzRvxNE7fIAZgK3LtlNNDpgEX0+g6VCPfiPrl8cvz003x1SMtSMYftPjbvuKosJaMu4K +g3t7vdQjTObNpuJMOcuTKiY3pJDBbNAcF1OKbzGpTF25zspvzHou60BKt1mQD4ktuSkh9k +IfwiPacrs5zA3S680z93Pt17M11tfMXgYpN7z34DCzdj5COdwo7Ei9Et3dxdv/Nzj83kxh +KmpEq3lxqRzHG2mb2h1zHQW3RR0= +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/encrypted_issue_369_rsa_opensshv1.pub b/files-jsch/src/test/resources/encrypted_issue_369_rsa_opensshv1.pub new file mode 100644 index 0000000..2ba237d --- /dev/null +++ b/files-jsch/src/test/resources/encrypted_issue_369_rsa_opensshv1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC96PvllVEOgtQdHnaoga3gi8md+/lUwBG0SOv4co9QaJ2kvJZDTiSLmJc4euEuciiplvzKpKSWSVqaZLboXiVVt8MnPmIwIlp8bhIqJ+0431FNWWq6RA/4EszHiARxMIwUqw+svN2GMm5fGr+PK3FR6mXGjiVlv4/5Vb0BCUjgB0vIqq123DmKpxrT6S3Ik5vYCCn0zKtuWxfxp3hpCqC2TdqD7/rFTX/AstmlhTO4FRDEK/7JNBSWY8+9/rQS/c/HSSW7qMGEoGFkELKSYFSdwLcyAMS1HySzNR1SuyBLDsOSaN5iWm5cBqKIK175Uc3Xe3fIn6+C5ld0m/nN75bw6QJiDH7I2jMYDTBpDqztBiqdjcSwvNTogGQg/oJLC7aAz+E5gfkTsg/Q6bPf6quyPIQ+KKH5v2YVFQIw2Te0X7/OZACsqUnCbGiDXCixPTTGoRypcR0MQDJxR497y7zvO4kiqQpsdBoELuhWSW8+9BcovL4/jSyqUEGvGYoh/QM= test diff --git a/files-jsch/src/test/resources/encrypted_issue_369_rsa_pem b/files-jsch/src/test/resources/encrypted_issue_369_rsa_pem new file mode 100644 index 0000000..b646966 --- /dev/null +++ b/files-jsch/src/test/resources/encrypted_issue_369_rsa_pem @@ -0,0 +1,42 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,D5AFEF41BADEB0A6ADDCC294C4C34402 + +BgIiIqe5ABrapndv+KDz2EoUj/QWtEJ81cCVLBJT05GTA93ZPYmwFOOHzwDtZN2n +36qqSkiytcPcF4onogR50qziz/k3cN38Eiiqyrm1kOrIkxaCUogy0I2vQvDSYr8v +CuW7btHpHpv8TlbWWNO2OGawH3ZYplNZoU8lPkHrGd8s0SdwHWjGm5pvKtbNRMjP +G8UYgBO4259oCRKpXbJJK2J7cH44itedZeiHPhh2gukqyFIaiu5m8ZG8X+sCnxw3 +DYYdm9C24uaC55Pfh7DTaGdvKXlprR816FUnqqF30Len+JegHbT3EG68PYsakIwr +O2h/jJo7Io61/w5lzoFoGrh2MJ73sHpsLk+ytQIghfUEHc1pBZnI9YcHhiHqEhUb +GZH/knfhR/V+OjFL8eXD+Mv6RHilxe22Dma0kk45B9rtAHa7wCtkRpQPBerUQ/Rz +uD39LPc5hS9tdvMVVZHJRCx10B+yvRo+CutuTpyZo27Hr5EtamGCgKU8BIr5EYaH +FkRAMy7Q+27kiXgD0H+DViyZZQ1gM09gxItQuw/E4tjjaAqsy/xnZtGdmKARqqZe +3oPXIv/bmLWrO9c2Hq8xFB5iJYRrzgmmt03RPMyrQLPw8gfbGVrehn1F9F5TLfdc +J2Ao2K+Bv7bTTh7OunwGXF/UmAphvREcH2yhB4VOr04R54z8Vo81x8kRJtTlbP1g +fsYHRK/BSG3R8pTge7MJwWLYW4lyGyNQ1G3NWbhsafbC2sBxm43uP1sO32SR7moe +t4Um+DzxNeVx1zaSuw0XSc/W87F5GuXKpYRmeaKo/EkC3LLKI2UFK/5iv9m3//Jn +6XPMA1jfiavN/hZwKTHImCdHP7uqEn0VwfmoNnSovZUcVikuPjToRPoxOYblPed4 +RDmsT1DrEO3zRiZ4wY/al1/Qs5ivojoAzwpuJDbG9C9H4B82gDvXcqAr8LeJS2gZ +9w3hD+8XJJMf3wA112RaoDJn7xzJC/fTmyq5tiS0pMkGP0bH9RVAVTVPoKLjs70m +oW81L/3Z6FK7t+6vDonQITZqal79jU7HNwb0Jelv95kT4jUtkzeLjLGgsBGRGBqg +ACAwFqMKqAZAmqCI2aai8ky054aNSi0CCEN+kQEs29BRzs7/+RdlPUZCQKeY9siT +5htrC1g8xn+F/HudIzcue6Vg0KmONCn/PjJbSjK5lyQc3PN8W6Fa6POJRZgHpkR/ +vfN+IJlh9vmU7CqZcT08SZDp/2x/eIq58yoBCBDwu46vr477+Vc25rSpl1Wt+9sj +PZ5WKLqHmPy8vBmG5nOv1p3OKHv/AL7LvhvBk/B33oK7mDI0HhGaHDmMyPrikI1n +DcStuKhNgtZxV6V5YLVfbGI92Q6bggyN9eslb2AMcPg3Jusw1wQHKtGBYTveQC3I +zakD00bl6eyR8L8GM7NQFlBWO6QAQRAjwbrop9uixrXFqJ+kcsJ2EXikhHOKpW8B +3uhmGkIokzbCy2SLy8ErzBPNea58vc4kI+CJEFANsrhOhrkIw+9+MVz6XnOUafb7 +mk4k9Vu+EdAT8zwFpdo4oDH3gO6Y7E6fzFvCWblXmy34xf2C2G7uxl2jHJFbqPxY +3sCcKfFMOZACsdiq4SqtgilzLvnyxf6QY704hIS4Tl6QpLVxshIDHVNW6TUJ7qwg +I1fDyFWFponmtV0pS28yYAEM7Eb4aoUvXWEeQh6PE+CPB/s/6UQ++IgM0INAt4kn +LIljwtHAcWtKvoWup4CHelnBpdEqzWjWbiopW1/166m6/BFNJKarUNZtuuj3vorU +rAi/pwNxQ9a32pNW9ox0ir/C04Aqb2Jj58BD+X/dw2PbfilHPNraOJqdiMn8802S +2Vlj8R3VpbeETBc8J4zRibV0ZL2TsNRlBPXAaCVJhBgqUdYGAeaLqioK3hdQqHA0 +y/c+DsjFwODP+jtfVYHTgKIeAS4BnUt/3TwfzdSC7CgdZztk9aPc9bhQrFM1rdZN +olV2k2nTHLtflLPvQWckL5W0vBYqELjwBCJ4fUG3aNRwoml00uYWWtliOA7qABW7 +dkX4Xx0iUwql00tEiugxHHOgC2S5cDmSeC2+emGFb8ca55nrxIkCJWG0wOm+/gtH +UDBHmgxD01dk4QFG5wF77AAIBXWuFcYVLp9wzgAIhnEqGbYy5+hzoCxYTjjIpUv5 +MA5A1z2Yc9n0Qvu08swzO9YvrRpQPy/3Ys2nVhLF8MSI6qn20o/sDYy8DAum4GIr +RaVuIIo7cnqkDGXjnVTtganrRfdS0YzUh7jkoxN0us9YBMKyJnNNIDMEJW5SOU2i +t2qluYBIdfEdvB/LWOgJt0YDQUtk0ed7RCQqauBKDPWLvZ+Q8j4DOdZgW8makCS1 +-----END RSA PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/encrypted_issue_369_rsa_pem.pub b/files-jsch/src/test/resources/encrypted_issue_369_rsa_pem.pub new file mode 100644 index 0000000..92c85d9 --- /dev/null +++ b/files-jsch/src/test/resources/encrypted_issue_369_rsa_pem.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCqGObM6DyGcra+/eBdm7mIvnLM9oUDUwpT3hRbF7S7V6bb64EuFlduGXJKK+i4/a/ls1Sm3GVwnWvwuRxrVXgv1497MYDzDcflfm+bQLUN38qNscVhn3jdtFuCTIc7VKmtEd9zn4HVzQhUYWq/oiqwOsDeP1QidWbNwgBthCxzhfdi9XUpdpT3Tm/mtLwEz8KZ2kP9VdfXBql6NI64COcI+B2nzE2YwxZsLGFj1orstRZ3WOLTJdLtQ1NhBQqTEwFD06Jhf50wo13xV84jWeZom+ylJQcj9njnfH8Kf7T+1eyBAqCPLvS6kM5Q9J6bGPj67Hkp96zPT/y3Gh1le80o3JrYjo20NqWzO+/OIXilrDoYdUKJbgoM7GXtMlxFNEnj7BIf6TLNZJY9bA6dw1yFs9wOl9LRhC1zUC6kESx8DIiqSpxrZTF0ScgvC/gKqX6K81ovJBklrAuTQpTqGKOW2eY5jqiRAVa7Gx8sPPtHkGqodD/sFOKurggVDf2Y1vs= test diff --git a/files-jsch/src/test/resources/encrypted_openssh_private_key_dsa b/files-jsch/src/test/resources/encrypted_openssh_private_key_dsa new file mode 100644 index 0000000..cb477d2 --- /dev/null +++ b/files-jsch/src/test/resources/encrypted_openssh_private_key_dsa @@ -0,0 +1,22 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABBtwRvhD+ +SsnQUTeW/8JbaXAAAAEAAAAAEAAAGxAAAAB3NzaC1kc3MAAACBAPmIBCP1AA8EiTL0VAdz +au+1KuRmPkg+H2du4zt4ifhzEx4MscZ7cpBsUAM/EBC/ECWTHzDiWyFFuenvxDlfTi9Xxy +SdOKM6XGLkY7oLsY1y5bShyLrycxDD7MRQ8HpUc9TPzE2PDH4Od6o98e+vdXP0+IDUZGbS +uwEnaABX/1+ZAAAAFQDIB9KUd1/gnzM4s4FXCQb7zCnjVwAAAIBptrE9g/ooRnh4bOWiA3 +/StxbjMRmA9f9bJbM55CinL4yf/GdGmeBp6YjDBxDyk1oXmVJ2hLVXgW6kETbt+D01SouC +TgLPlFwLFhqZQ+A4JQOKuLIBaM92hI7sAfTewkxbmAk/e1vyOeyfcQDhbncESzVBZrheXC +1fTKv7O2PgbgAAAIBKhGEiNwKNA/pfYvkEbX90/jVoIEKZfqTk+PB2gbVMT98LWPaexrDw +EGuCCK3KOtMncDai+0r1ujf1hzNJ4z43Z1n15tPvAIwQsObTL+eUQkVjHA8LXOq8n4yYyP +idkjGzWcfBAm4Lm6vKkzFM3VRouQIHpWpjRl3wGTJZkOoH0QAAAfCw91NlezuvRtiR06mE +iiEUUjSLamkTQuC5zezJRYrdfYLE5nXGJG5I9Z0AN6bknf1AqZrSUtemnAxgArXm1f8CU5 +3b20z9lRKM/W3CJpmIV5YAnx2cmZe/Ls4hpauwTktY+47QMG1Egi14fJvLAI7vGmCkSI2R +JuuHpaQFZ1k57tfvIPllauOuvUZ8cEfXx3LvSabSfuUDpMlHuzlKJ76sq7WqPfP0mo1baX +lF4019kJHKoUar/G4CHgz+uuhpOvFD7vqP3CuEGa9E+hQhrzmHv9/+gO3se4qetPtuu825 +/7p3bzDaNZ0p0cnk4Ol2G0+1lDIRJslPlfqmmzIn19j9WYZbMd1/UJ7vWJ29gYIE7lX5VJ +SFlGG1AaW+3V+eviBVB09Tio60q7pUWz9xJE+Tp0cGBJYCZWV8c7vC4c5nCmYAWKeXlcgA +az2jUXyosqguSPxbWETzXaD7jgKmfrpNLRbBkws9LXHikv7Zd3JtjAYFTqK5h3Xh4OFRCG +X8XHiLOlQ0or2LgD4kWQp437xz5oKywbTabqeBCdlbYKuq91fM9DwILLBtphOcEKoIs+OD +3nmf1MoisQs1Oir9Nau8Kk4MRsqjx7JSF9q+q4vN97BGjrpERFlKm1XGDrcVYNq0miINBP +pJTZbcmjD3oMtF +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/encrypted_openssh_private_key_dsa.pub b/files-jsch/src/test/resources/encrypted_openssh_private_key_dsa.pub new file mode 100644 index 0000000..ca3c5b6 --- /dev/null +++ b/files-jsch/src/test/resources/encrypted_openssh_private_key_dsa.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAPmIBCP1AA8EiTL0VAdzau+1KuRmPkg+H2du4zt4ifhzEx4MscZ7cpBsUAM/EBC/ECWTHzDiWyFFuenvxDlfTi9XxySdOKM6XGLkY7oLsY1y5bShyLrycxDD7MRQ8HpUc9TPzE2PDH4Od6o98e+vdXP0+IDUZGbSuwEnaABX/1+ZAAAAFQDIB9KUd1/gnzM4s4FXCQb7zCnjVwAAAIBptrE9g/ooRnh4bOWiA3/StxbjMRmA9f9bJbM55CinL4yf/GdGmeBp6YjDBxDyk1oXmVJ2hLVXgW6kETbt+D01SouCTgLPlFwLFhqZQ+A4JQOKuLIBaM92hI7sAfTewkxbmAk/e1vyOeyfcQDhbncESzVBZrheXC1fTKv7O2PgbgAAAIBKhGEiNwKNA/pfYvkEbX90/jVoIEKZfqTk+PB2gbVMT98LWPaexrDwEGuCCK3KOtMncDai+0r1ujf1hzNJ4z43Z1n15tPvAIwQsObTL+eUQkVjHA8LXOq8n4yYyPidkjGzWcfBAm4Lm6vKkzFM3VRouQIHpWpjRl3wGTJZkOoH0Q== test diff --git a/files-jsch/src/test/resources/encrypted_openssh_private_key_ecdsa b/files-jsch/src/test/resources/encrypted_openssh_private_key_ecdsa new file mode 100644 index 0000000..ea65e4e --- /dev/null +++ b/files-jsch/src/test/resources/encrypted_openssh_private_key_ecdsa @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCnz7UO3z +wPva3ZeqAv3Fb3AAAAEAAAAAEAAABoAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlz +dHAyNTYAAABBBKHuAe5N1uLPUpY3t5kyYuISOxUobPZfK8H+CQaJTCALTMFrT63UDDYLyI +2xroS67T2bWHkuhX1BHiTGP6JpwL8AAACwwZ1jHlWJTZUwle+U8cXZx2Od0s4Y71qL9onX +/+g+UVxaBeAZq88S8fbIPc1netiue9VVo7Qiw4e4WEPUCWKykQ+mwBX9798q/QUbrK3UZl +509ZLFe/DN41mVGO6rU6NHy+1mVstStgEEle7Dc+JaZwa/iZRVeRNq58MXQ5HBAO8fi4Y5 +yhe050OWLkNylLtLOmRffRWo6eFg/DwCK64G5+qsSeTYVrt0nq/ffcvyovc= +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/encrypted_openssh_private_key_ecdsa.pub b/files-jsch/src/test/resources/encrypted_openssh_private_key_ecdsa.pub new file mode 100644 index 0000000..beb9abe --- /dev/null +++ b/files-jsch/src/test/resources/encrypted_openssh_private_key_ecdsa.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKHuAe5N1uLPUpY3t5kyYuISOxUobPZfK8H+CQaJTCALTMFrT63UDDYLyI2xroS67T2bWHkuhX1BHiTGP6JpwL8= test diff --git a/files-jsch/src/test/resources/encrypted_openssh_private_key_rsa b/files-jsch/src/test/resources/encrypted_openssh_private_key_rsa new file mode 100644 index 0000000..968db45 --- /dev/null +++ b/files-jsch/src/test/resources/encrypted_openssh_private_key_rsa @@ -0,0 +1,39 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABCg12tR7n +gz+3k/7bLxtc8TAAAAEAAAAAEAAAGXAAAAB3NzaC1yc2EAAAADAQABAAABgQDNVEfQu64p +2GczvWj0AlncuNG+PzPd0jZ1/hfx/JDz1dRVEalcXMtIGRSAH49LzicAMPV8jvwNCa+aj/ +UF7laDNqjIXvvC+sTA8l8/YilLm6BJ2G8B4sMo7/F8WOAcxrs+j1QT5tAMV88yaefoaXjh +9EWTOGesW1OiRfGYnzfhNrx8m8y0qVpYKpFbA5hswEL2VJC6YZxpg2XlMMJSSxBDhJLWBO +Blxh1oN5cqv8cDPmwGYlvBYGGcuz6h3krARdXcyHciafoBKZNBimiJ5VNvhmsa7aSkZgH+ +k6w4Cbddnuo78WtrEGvyf7HN3gBJZHGVFeclkRkeR0RR05HDqyanhOE/fO2HOHW3/x9SSw +rnk9I/lZAQlyROP+zOJ3LI4m+iHZUqMfgtN/JkK40l+rNbClGszEFLW+OSbajGoEh+deL+ +tdwownsuVORpAckoRJdLo03RGam8WMxx8UmkHw8WJBf91bshH1kgQSTIV/lBbFllkwM7+L +AWy7Ow1ZGfzsMAAAWQbzIlDprGTE9bLjJZr45mf6CCPHGX6uZxlQnFmfL0zKLwfZm0hEJm +7SZuaZZbCqD/dOknidKyvumtX1NPK8HIWELLLv9L/0jg5NdJPUZKeDJXAm5DHrNWFxCMMs +TxfM0R3E8NMC/qR+q0ECjBO9yZzJFTtx1V1uQF6Xw718vP9iHztSbpRQHOejASzDk2VWG3 +KTgF2fadRINabcH7/ecniETT5PvrYFasCNKtOdSA2flFkEjT97RAQi9C7ZdSxvqLUKjAah +gUv/YLTZFc6fW3iGky1rBSEP7XLf3qeh7GZF0TZ09CdcNl6I/hFR4AndlozFFCx+GISFB2 +Qw7zB8EsGjjD0CPbiwIJiJsWBo6/ck+QHpiydJFaetB9otpF6Q6wgCjofJh2VjZkJdU9W0 +Af22TurAj6OjSBEZeSsMPbmP4qO2jGWVYzBMTwprap09jgVfm0sVgTcD9yuchU/XDlI0l6 +qno/T74o58lOm8B5ZAKc2SCAzbOTrVQnY35j8A87rrUSMM6NyGc4GIvQiOBmgPqJOdxhI2 +JR63IZAq4mdqCMfqIr+itNxVCs3kZxzMXqIKVJO1w9sQaOEySqOQHVHpBJqqsb8SXQMWXd +rNpNQ31Jgpavz12BsQ09+AV4K0oSfHJ0IlEFeXAUCUYiCR5JF0ZD6yBBnQ/JUVGMfmf9Ms +73on47cRlGyNzI2vGXslktvApp77s0A/n3pXmKK7aPHeLCeGUy5jGjGWgPP6R9DNSy2bUU +FvKySiw2xGCsZsGBOATIBcJAYUj31NK/bpXDdmoX3033gWH8bCVmU+zglSExrzSKJSvOZI +p2fWue/dNj4Jin79YwW26ukxyguo8Kd1NkKrSPMGnnUK3DB9EJjjB5Mgwv1xyngAybIFZp +SdUvIDtLwNU6ZXxoMgA6FUlbn5WO9Ay5R5xVUB8d6HI/upgQyFch3Tuoj+jM1LSDRstQId +Ox4w7vpijNN9b/Opq6af+sDcbiejDQ0Z4ubSayBt4AjjzmMgAf1OPd5csyfclB8xwSIH0e +2wz5zZ1rj8inGgZM2D/2cY4HuLOxIiHpkwn515sEi6cIAThg9bAZ8VseQI7fwW+nBuc+qb +RkSaHW5YMVuCCoyq2z+HdMX6IPbIgy7Vm+AzeJfsF8TnKE9Etgse0EopqNgAgz8gwxbvvL +hYpRPJEHbkTntZsIPZG7oQesDiXev+2A+u/YD0oYokHn3UXKXpbIdjkgWBRnEh13d+94qt +cCHuZ81gFYz4mofYavLIBk7ESHGCx5/oGHlTHThgKTT5gDATcA3dxGPoegq4mEAGTHxolp +J947DPh32J6jSSRe46u8cL2LS3It8Tt8Lc2roAvTr9kyCcDMj3hXHQ7RcaXPynIdZqPHP8 +gOvcrtftAp2f2J1XHCe8Tn5VG80lJdpeiWVHi4sBYSnbJe8/e+P+uU0oerrOF5uI/R3L9h +5PVogMl2YF3l3xsvezLtWPo32/BkeswVdJa2RsClGQUAUsOuHJRudMlMVCmX9teqDEvJES +QZrY/XaS0X6nPwanzOU7UC0usIDy+Jdhp0doO4YGUtiX0XA1nF+kJlKB4Ad9bClcDDvUXy +kFOOV28ogmIMDAvk9i2eq06YNMHdN+jgeIrKadOX6giTJBns3coavrlVRUv+NCir0tAKLv +dpPE053uuKAzC6akQONfqRdJoVzQrURTaccpvxQ+0Scfedsl2RknNOx203oVGAz6/Xkrpx +T7g2k1Hmy4G3vFm1V2ecpOfmxEleexOra5nLEWSCHReh8uBi37reaSriQaLskjr2JZw34K +NyZewMEm6cLva2vYab7DWVXE2QMg2BIubmS0+SWJ9PHH5jUGvfHfKf9hIT3/kcF2bOb3yt +BWQjyzG8068U9T52Kp1ZANHh9yo= +-----END OPENSSH PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/encrypted_openssh_private_key_rsa.pub b/files-jsch/src/test/resources/encrypted_openssh_private_key_rsa.pub new file mode 100644 index 0000000..1221452 --- /dev/null +++ b/files-jsch/src/test/resources/encrypted_openssh_private_key_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDNVEfQu64p2GczvWj0AlncuNG+PzPd0jZ1/hfx/JDz1dRVEalcXMtIGRSAH49LzicAMPV8jvwNCa+aj/UF7laDNqjIXvvC+sTA8l8/YilLm6BJ2G8B4sMo7/F8WOAcxrs+j1QT5tAMV88yaefoaXjh9EWTOGesW1OiRfGYnzfhNrx8m8y0qVpYKpFbA5hswEL2VJC6YZxpg2XlMMJSSxBDhJLWBOBlxh1oN5cqv8cDPmwGYlvBYGGcuz6h3krARdXcyHciafoBKZNBimiJ5VNvhmsa7aSkZgH+k6w4Cbddnuo78WtrEGvyf7HN3gBJZHGVFeclkRkeR0RR05HDqyanhOE/fO2HOHW3/x9SSwrnk9I/lZAQlyROP+zOJ3LI4m+iHZUqMfgtN/JkK40l+rNbClGszEFLW+OSbajGoEh+deL+tdwownsuVORpAckoRJdLo03RGam8WMxx8UmkHw8WJBf91bshH1kgQSTIV/lBbFllkwM7+LAWy7Ow1ZGfzsM= test diff --git a/files-jsch/src/test/resources/issue362_rsa b/files-jsch/src/test/resources/issue362_rsa new file mode 100644 index 0000000..0c0e41d --- /dev/null +++ b/files-jsch/src/test/resources/issue362_rsa @@ -0,0 +1,3 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEA2Pt9umdG1Dp/JGFHN/fT9pq+xIdBOzXPcz67/NAdOcpH5rAjNOd0ohmEIMD+Wgc2ZPNz3VnWCCtdlPXjuVs08E9qaPT62U9AL7ARp1OzJM0kpV1+/XvRC2xmDVleA2n0/1R8VcArqHLRu1ylKKxgN2R8Rm4TVsqP/uizcMgjHMX6Y6QnpSVp+DE8BarNA1ZnuEdksvi2lYNzb+VW24tu3CGwPujq4rdmow1FAhyb3EJaRqzOsfcNM27eKVCOshzwhyCKmRD849dvORghemUM4tEzBH+MB7QPHzdV4J9bwcPiBJKwAJ0FHk1jkdZ3OggxRBk5cmJSLs+t+COAcH4QZQIDAQABAoIBAGcA5AuEEWyYJFkZ0Nwxyq6LgTn8VywLfGJiCo2WIfmYHA/X1666nXSCFmYSF+yW9exwYbVXezI/m9ol7CfGs1fM61/Nw/M7GuZId+jt4+H5fIb/3lPo3jDFEaEOpoGKYCKBcdCnPFJnx0ZhUYoAYmCJVDF++bE+0aKZxu0oJPr35Lda1hBeCfrb8fDReAAU71jIoOGyuSyOXlCVyT+OmBhVme0u2xDpdY8cd5t9YueTK7BFNjNt3sJOI4L+2wRskNwxqHCrECtuOXmzEyza7vqo8cjRGSx1HZfqhoKgiRpRkRmSi8p+X1+DZOVZuCxc3BTZ7WrPgVZhu7u9hsISKyECgYEA+C9ajUNsx7Hx9gOg6Zvr+zV461PjKI/0n9g0DTZ5RHdcAfeNs3ugmsiDK6b9SayUW08EVyveJ1OJjT/NPvdP0/KhCAjUt+kN0j9f9CPdHKEWoboDca8u67dayOAhC10mNhoAp3tFQryfgpb678GIzqnXkVjGgyWC/8Z81AN7uAkCgYEA39CcJAUVMc6FigTwoMOkdOgNK6qCxAxkNBTdfB/cBBtG3h8g49n0u/fAMjkfL6awdl53j3jVCP4pUZgSSqArLb0aGo84G2vP92iN3d3j/kPiox22w8KG2XR7tHLWcc+CFfk8mdiKdPFxaWVQaYJ6htGq9LGqY3sw5lVzmk0wlH0CgYEAuWim/WGhoo4NdPzA+cTCRqlr7GJ/EY558fBS8ov/jGafFdkawztYgEnLtJDMKH4FVzFwzK65CCggWqWPb7rSqERaiOYQBFTXPnqZ9InWZczyW1/bstJs+yu/ZtIJ3bN5GHHUi0pMM882Wxjv3q12xu2bXbo0k0Uy2GIwXzM6+gECgYBDByur0eXeC7aMdhxGWTEoXdKL8D3HTtq3ikQmhzgR9sVLglEMS9rybCkgIWFImQgh+vqdehd64Pso130q4jrsMMTfjWLFO42Fz8ck2e4M2PHH3f89M0XFXBAsI3Q7k2SnBgRzIpmcmi5X3SKu5oehVqt3KroXnu4vHQpI/LL+1QKBgD3zxf795zFXw53iZXtKkzYNQhEMVRzbx6dQE+tguB0YCb2vPvYaxfejg/02hCQ4twIMuMK18v0Y9W7E34C0m++lP8UbnHdR0Dsg9ryH7OlUMp4662060HWDnDy79swB/Nnk9dxf+BoPIXGny84bEpk6cLoiHuL6TRpQhuJ292DW +-----END RSA PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/issue362_rsa.pub b/files-jsch/src/test/resources/issue362_rsa.pub new file mode 100644 index 0000000..d378cbe --- /dev/null +++ b/files-jsch/src/test/resources/issue362_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDY+326Z0bUOn8kYUc399P2mr7Eh0E7Nc9zPrv80B05ykfmsCM053SiGYQgwP5aBzZk83PdWdYIK12U9eO5WzTwT2po9PrZT0AvsBGnU7MkzSSlXX79e9ELbGYNWV4DafT/VHxVwCuoctG7XKUorGA3ZHxGbhNWyo/+6LNwyCMcxfpjpCelJWn4MTwFqs0DVme4R2Sy+LaVg3Nv5Vbbi27cIbA+6Orit2ajDUUCHJvcQlpGrM6x9w0zbt4pUI6yHPCHIIqZEPzj1285GCF6ZQzi0TMEf4wHtA8fN1Xgn1vBw+IEkrAAnQUeTWOR1nc6CDFEGTlyYlIuz634I4BwfhBl test diff --git a/files-jsch/src/test/resources/issue_369_rsa_opensshv1 b/files-jsch/src/test/resources/issue_369_rsa_opensshv1 new file mode 100644 index 0000000..2422781 --- /dev/null +++ b/files-jsch/src/test/resources/issue_369_rsa_opensshv1 @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG4gIBAAKCAYEA9bm+MifEwl4D2k7QEhANN+CvvOc6NjA5UQge7R4KZiKSu27T +8Ys2BDxD8sFZwURjCZ4SMiih3OwB8wqLN7ZBlWDQZQxpp6GmU3maoZrEUqqsR8ny +EUNU/mhiDeLHJYmfolvESP3Y2Mnz5XAEdypLvkN8bEeStss88WYmAf6w/V470UbX +O3GGZR2jHtjQbUIYd4WpkFUhCTI30gqauKJFC5NFMrV4XWz7T/7Sw76niu6Kfqve +4RUtMEWPcU2jTPqlIboWijW4bnsYBkAZO1nuOS3Eejt18IwAfl+6MGuCbSsfaCmP +d/t6rTPufup0ZxZ5Afxi+X3Sj5iLMENbzkoBnhIYPzNMs19DxDMeKn3M6VKdqxbT +EzYPTul8/aiQp2cvyNRJiuhRF3q6RGNMeNZmNjQQaK/b2rWjPSMcydVSK2vCWI5z +6huPFfSxYUhKRX4tQqrqibtX5rcbR+GDVljUBAiqdLPlpsSgLtmQAK9cJfrTZJIm +Tmg0pbk9eKjJEhI1AgMBAAECggGAEOhyf2YAInWwoy1oIM4M8srZnNB2T3M7Bmne +Iue9xHBdk1sZZ1XyZhE1hbcrM2K+w9MmImBsXtS4f546nR9D3QD35fQYMwoq9TR5 +YORS3PNUfm4VY320E/tfv9/aXylcnCHfXDxnVudyileOXxrAcnuXTKYSINTUQTIL +rHh3ej+pMwnCVptFqaCD8GPv14zEPTkrxTwuVUEo2SGUqt6zjIvaJ5aYDSmqE0OQ +AhsU0Tj/u56c6/T2kos+xweWFH5siuTKHnC4GOxeFMlHOKohTVPUgkzzN9MbzuSA +YjZAknZeHTHcaWFtFFeSFBbmzVJZKfoDCC5ESpLYZLRgV9AqU0dsoDJUP5ET8lHx +uh4SNcCXR8acN3qkGSJRijAff2jdVQiLe0hqkMurhuJzArGHVpjphXWvDwxe8mLe +nrjheykKn6CuWkrJWoFUIrDUaCcY26p9dNFdxPrEZfgUpnzCiDCGDIgj1lJvt1U0 +ifx7ttUI45UC6URU/yR9EP2TvmRBAoHBAPwYRRjVacqINmAJFnU9+4eD22uOI1ed +bfMUqqwt4ivWWmKs0ZI+GJmXiYSWettuN+Rtv1KOK8SAZSz3OdSWcwLuz8dlh4O7 +cjjInaXTz9Tf4sqMaiC/IdZlmt6ngZ7ZG+r8zR9HhxUwy7Cs9GpYF5m3BJmZn1LZ +bP9TH3dqvZH5G+Wn8ltNl1pVKM6rC0ARKGBMa7Bo0rsv1rUvmEetaZu5iiah5r6h +XTmb5jPLEaMxklxkqfkOIHgairYegEip1QKBwQD5iDbu0QxiA3Uu7DeZx+6GHUY8 +ffKkYXcnKdXFNIbeBGFcHkWlmZeuA6IqcOBbFTHpSpKcRmVuzvmd3TtP4FcIQxuH +RqJ6TRMeXm+Y+GzBLeVKiOCrABsKapUy3TLTDtguAbFlTduuM1HOYmq5vCe3o2MK +tgpOKJEHaOWHzrznMRgehjoWTeq3WuuMZmbYfjDsDo7IleZslWkIokWD0U7SERBo +D05/x9x5fSNqyyjM8NZ/ik2ujPUR+WdCHtXqluECgcA/k59Zc/kKKvALqD8RsmAM +/SQJK/+dyQZBl6SzZ57yj0ycNhlkWGS714vG9GxnipRt93+YwmInXHonrPHYu1im +FLQyBVj3z/4uc+nOOGzhstTvYBojyBAwkc9M99GozfhMexUAHnnizjuQgw3hA/Zv +vchbNHMJ4eurOLtm0nScq8ZtVL26aQcSsQdpl5luvuT/5EYEZ2s96gKsDyTIbuOD +cnd05r/as7dfIAIebcg07/uJcZmsRfPKVmdFJswTh0ECgcBr8FU61ujRWQeOpZWj +is2N7Anezuhv3M1K/pi+9mrEjQaEb3/XE2p+VooGa89Q9wkhDiX/PaBQ320wsWsf +sT5Uj5rP2GkeGEsF3vnNJOD+a1j89dqhfak0x0gEuZRrocc3l3niBVzarM5dRUs/ +TrmrgRytnHM2veuGVgS7y10BcMYrJgrobQn0CHtNv1oLmgKVifKPp/AF1leZ6X/C +dn7u9XywVraxJYYkc1IntvvOMvvGLdBOiiDUhpr5Cheko2ECgcAEFU2CTCaTzV/s +xTySE3AK6flhNaxdwL0wjK0I8DUZhfpqrBJVnlfV35Y5dXWl09qR/f1r6ZQOg2Ew +HiUnsFq8XxAwvQ7SrrLvGY7OAxp4mSeUufP/uP9hNIZQoXof6gxosLxeTfDq4ddA +dWQCygbX8PQRrrg7BMqC1pRIGljwt7ijKy2H4RS8WteGlM+nTrps9iEImAqxoOAz +ky2V56s1enP1E+5C/1Hr6yyVSxBF46482Bpl2WvJG6BBQRV146E= +-----END RSA PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/issue_369_rsa_opensshv1.pub b/files-jsch/src/test/resources/issue_369_rsa_opensshv1.pub new file mode 100644 index 0000000..f85c3b0 --- /dev/null +++ b/files-jsch/src/test/resources/issue_369_rsa_opensshv1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD1ub4yJ8TCXgPaTtASEA034K+85zo2MDlRCB7tHgpmIpK7btPxizYEPEPywVnBRGMJnhIyKKHc7AHzCos3tkGVYNBlDGmnoaZTeZqhmsRSqqxHyfIRQ1T+aGIN4scliZ+iW8RI/djYyfPlcAR3Kku+Q3xsR5K2yzzxZiYB/rD9XjvRRtc7cYZlHaMe2NBtQhh3hamQVSEJMjfSCpq4okULk0UytXhdbPtP/tLDvqeK7op+q97hFS0wRY9xTaNM+qUhuhaKNbhuexgGQBk7We45LcR6O3XwjAB+X7owa4JtKx9oKY93+3qtM+5+6nRnFnkB/GL5fdKPmIswQ1vOSgGeEhg/M0yzX0PEMx4qfczpUp2rFtMTNg9O6Xz9qJCnZy/I1EmK6FEXerpEY0x41mY2NBBor9vataM9IxzJ1VIra8JYjnPqG48V9LFhSEpFfi1CquqJu1fmtxtH4YNWWNQECKp0s+WmxKAu2ZAAr1wl+tNkkiZOaDSluT14qMkSEjU= test diff --git a/files-jsch/src/test/resources/issue_369_rsa_pem b/files-jsch/src/test/resources/issue_369_rsa_pem new file mode 100644 index 0000000..bdd875b --- /dev/null +++ b/files-jsch/src/test/resources/issue_369_rsa_pem @@ -0,0 +1,39 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIG5AIBAAKCAYEAs3v+RtRTyr4HJy16h1eI21HzvoIu+QVClFigaMHYOt5K/Xtx +nRlZsLQQVkyTMsRFlbSTKx/AgEVqMlYiC0MvaBX223MLgJ34VY3T1TaSu0S9WcAg +q7IOlA6RPlaeK01s1HA11MiNjGa3jyQsFNPlFnb6BCV8BNjYrW/rEvNNiLHZcIkY +/ZJ8yiea1XKRqUNEJHNI23jQ3850QBPAEWsJAy3FxIqPVereKbAFn9wWxxmHnPT2 +QGHHcwh3MJi5k0/w0jhfrrdn8b4wcnVicxh5isqM5RTY04YF7dItH3HnSxtKABzT +QS/FHhGEOx9IJW0/a7JzKjgLYAWA/5yRnzJDDIz+zUUtGNI7ZJmPD0N5e0ZB0xXi +lZCqSrcg9/i7H7jmZIeQOq4TmYDD/fttgGYSFxoaPZCWUkhewfQlmvkwq+YmaPZS +123jBUa5OS7YOwTR5DuUcfEhq6BmBpC17fPfni/h7dSOI26tX1heeWEGJ0zqL75z +VVQqeItHPCpTQKkjAgMBAAECggGAHHtwvoyzP1koiW8OIqwha6x1oaXHDn1nM2Nd +EUKxraXZAKC2RtffA8uPTCKauVOsNzWQpSdExRY+4/4HKQJgY4QYyHpZOO/YmLsJ +AqzGXDFsWvHCPXzkE1q8ccgNEZIX0x57bGjnDYC/YFe9JxD5Kbd3tXC9XYeL2voH +s9ooU6jleJZJAPReGTZvu6+SremexqoExc9GVj3M9N4tfJYfuAFrgOT8GgZLexIT +0mp6st26R44YBd9+ZyLQx0V1LYthx+Z3uMA4BYyTc0P3TcgRiazIBjhFC7gcbFwR +kma/MFv9P4jvUeNYJuQIueF5Bb24/FvRYp5o6HGTiE2pGUv1DAGK1PvI711D1aDd +TAcLhwkzPog1C9KPyz3mAn6p6x8S8+Lk35AjGGBE7i3s6ZGJF17SJW7BwzKJ1vSe +XX9U3XLq3DErPx9AnQhCAiPihohx3Cm8Hy68NGSHjRdu5CeWJiczX0TKHUNSx6TM +Jo5Ew5Lplr01x5UsjDbwgEOPToEBAoHBAN5UGGcXgkQ2jn/pH/BOQ7FnHNtS0/lo +feBnUHLEGRnFylka3sRak0fF6bMj48NrGyiI9QFNv/NeHqwhjGYGVeK75HGJENPC +6Wlewifo6Nw/GxmBo0sewEZ8XEyqClIkpj1oOhIbkGyeT/dXZjbyvx+kdz0FTAyU +/fUB20Ai3rgS600r6GSQKloOs2iAnELugpkTYeB7Fiwe2613i4l7myVLowhwYhu3 +BBW5kg+ApasSTwloJlC454TO1RylJ/GwjwKBwQDOqslErBZfb2Lvm8DkequQ9kzp +dIw8pZoPLXkRsU+X2kifxCY9W6SWlgzMQptX/wWwI6QB7fMIwLB1Umtzuu7vwlzB ++NBgeflLdWlWT76/mc2GfbW9QsKvnAR2ar3z9GV+zmHp09NPddFn2bZdLlXl736p +4C8MfLTZlmSwwJm2MYyoGQB/NfMMrJ/yaAPJV+AdlE0hV3A+5a1cRim2n8rUVEHj +OJ4x7AqxA+xt88picKIAXEtTGyjsj6DQQdUBYC0CgcEAv00u/i3NSfKDpO4sLDK7 +rn8h5lobyQQvI5LiNw4i5vk4xnkHa37gMabLEvhzt6eGY9eMsYV7/+VhkQ0A6JzU +89Zml4av8vZIrwD5ISwYicLHB6hzoGSiX0QMi27YmJuuazIunXwYRk3mUtZiPi+b +Ype6fcf8Cut8pX/mbwZSC6ND0lBQk4800e7KUsYvLqxZtWtnEaf3iRk4PseZSkAQ +XAP8EXvZ/yz4F9VoJ2yzoEKNvXNfXJ/tnmn2F8LIXv9jAoHAccENwa/bLLKZyXt4 +xApFbygzE3kkS6l3UA1ei3+GaPYsbUxBJBrSUFTNPI0ZBmmHzvj/KFS6JkIxnpI8 +NNpa9DuOZPI4eDILJx68WVbRjpLwzqtZIpChqpl811VPsvz99LtSp6sBr8YQ+lGa +kFWV0Fdv579PBleKEA445BVPRjqlykzguiSO4JYQABSCqQumf4GGpuiDDwvKzXSN +N0ljElZCYfhjEuVyyRZ4x9iduGt4sCwdBeR4NSWlhZwGy5gNAoHBAMmDur+qZ5sL +bb+AVsIPOhS1Nk64QyXru6Bz0M6doY3pgZJRGmXPwqfpHFsVGrjFoYU0Xxz4zWJb +fY0UBJZfb2vgLcHGVYCUL6C6d+f0m6DFDB7iqY06hU7EirYzA6iAcKiIHgAbELB2 +7ux+jieXxWKaMsJWYgR7XQn/UbC5xuOPSazZ5ws3Hz8toUK1tsYjXe+RoZioij2f +Xm+9NXEFFGhLtOvepf9qw1fHlIIetCOJ/yrCJKVzFmzhUReGPAnmoA== +-----END RSA PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/issue_369_rsa_pem.pub b/files-jsch/src/test/resources/issue_369_rsa_pem.pub new file mode 100644 index 0000000..2178b42 --- /dev/null +++ b/files-jsch/src/test/resources/issue_369_rsa_pem.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCze/5G1FPKvgcnLXqHV4jbUfO+gi75BUKUWKBowdg63kr9e3GdGVmwtBBWTJMyxEWVtJMrH8CARWoyViILQy9oFfbbcwuAnfhVjdPVNpK7RL1ZwCCrsg6UDpE+Vp4rTWzUcDXUyI2MZrePJCwU0+UWdvoEJXwE2Nitb+sS802IsdlwiRj9knzKJ5rVcpGpQ0Qkc0jbeNDfznRAE8ARawkDLcXEio9V6t4psAWf3BbHGYec9PZAYcdzCHcwmLmTT/DSOF+ut2fxvjBydWJzGHmKyozlFNjThgXt0i0fcedLG0oAHNNBL8UeEYQ7H0glbT9rsnMqOAtgBYD/nJGfMkMMjP7NRS0Y0jtkmY8PQ3l7RkHTFeKVkKpKtyD3+LsfuOZkh5A6rhOZgMP9+22AZhIXGho9kJZSSF7B9CWa+TCr5iZo9lLXbeMFRrk5Ltg7BNHkO5Rx8SGroGYGkLXt89+eL+Ht1I4jbq1fWF55YQYnTOovvnNVVCp4i0c8KlNAqSM= test diff --git a/files-jsch/src/test/resources/pkcs8_dsa b/files-jsch/src/test/resources/pkcs8_dsa new file mode 100644 index 0000000..1b06e2b --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_dsa @@ -0,0 +1,9 @@ +-----BEGIN PRIVATE KEY----- +MIIBSgIBADCCASsGByqGSM44BAEwggEeAoGBAOGamTQ4i4AEWhMGssPosXwsaLBW +IsldlycRQEfRCRlmi0hzdmYvuJJn+YQ2Jf1QJ49jTTxlCuFJLrDct+UxpittxRtD +b+AdDpmhGp/zMeNdPBsfeLX9REfANyveQrfT9hqZ2gIbMOzJ0wRPgInmSRi22A0I +t7vxGaw47J5bAEf/AhUA/0PUfHgleqi+2kK+BOGjTs2eSXcCgYAaUAApSPx+S6f+ +H49ml7sHdAQC4lVyKVQVh0RG55/rsIrd/dSYgo6LZ522vzNgxw2BmBeFJUpNZkQz +IGmxN2FkTmlSgOxZkRH2oZA2j04vcskJgIiN8K1rhrpRUtJjcKXnF9YRn0eR4o1x +ph74ySPmIWX12GULB4R1j4nysqDmbwQWAhRf04nB8DdbuA1JAbbkaSwORY/Mdw== +-----END PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_dsa.pub b/files-jsch/src/test/resources/pkcs8_dsa.pub new file mode 100644 index 0000000..19adc14 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_dsa.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAOGamTQ4i4AEWhMGssPosXwsaLBWIsldlycRQEfRCRlmi0hzdmYvuJJn+YQ2Jf1QJ49jTTxlCuFJLrDct+UxpittxRtDb+AdDpmhGp/zMeNdPBsfeLX9REfANyveQrfT9hqZ2gIbMOzJ0wRPgInmSRi22A0It7vxGaw47J5bAEf/AAAAFQD/Q9R8eCV6qL7aQr4E4aNOzZ5JdwAAAIAaUAApSPx+S6f+H49ml7sHdAQC4lVyKVQVh0RG55/rsIrd/dSYgo6LZ522vzNgxw2BmBeFJUpNZkQzIGmxN2FkTmlSgOxZkRH2oZA2j04vcskJgIiN8K1rhrpRUtJjcKXnF9YRn0eR4o1xph74ySPmIWX12GULB4R1j4nysqDmbwAAAIBL1zviIOohyjKKILovL9rPlTLHlcdXkyd5S3PZImq3CXDY5mQFlxH8r/hEnwMBBTAHmun7ptcdH/R5kZVB6v7xsAdJXDYiYOyoLI8yL4aWbjs3flriPXVmCb0hCh4n6pSKVR5IHLzsABABlJTULxSkvWcKwMms7arQm46WR6MPlQ== test diff --git a/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha1 b/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha1 new file mode 100644 index 0000000..959a4c4 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha1 @@ -0,0 +1,11 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBnzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIILw8uFzY0qcCAggA +MB0GCWCGSAFlAwQBKgQQw6RQtTSUAB05u3PwE4Qt9wSCAVCIpjeuXs1WOfohxzCa +AZYsQQ0hvJFa1DN5UTu0YwL8No71FAb86zoOcjKcrx96Vu+D7VnCCiJuYVpFalL3 +OoYpOpHY3m2ri24C31bR+2BY60GfCncK2LqlQcJbgQck4JGKJL1laN+9+6x1qXnQ +72D0gEWDWSLHdgnB8lAwcuh/B6ZBwwqJYh4njOtjAwOWb7ynG1pKm8C4Vk55UwMi +JT5gTpRMVONuj7w4NtROJmIe1eLAzZn9nZWo2y2dVzafCH0RcOZaEBJ4nBEYi0hf +5eU0irwWXdoVzegPYR3j/xFSjVLSb14rR6XBjgF+kaH9KPIDTqKzCqXplrNK70+v +H/IZbIgAKZh3YMU7GrBOArEBD9N4qdxJqO3OvgQ+WJbXXE5gauxgVFMYdXpUJXCV +q+FdjhzDJAZzNlj4OsZx4YQqHroeixFV7sdg/T2/elypkCE= +-----END ENCRYPTED PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha1.pub b/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha1.pub new file mode 100644 index 0000000..be0e70c --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha1.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBAMui93cyTI7R14Prqeet0joIvIWWGSw09hk6qDZvgKW2zzs5X1BTScrnLMnaKOgYtwDbFN4OQsBLNZu+PzcAqfwJBeTD4mGLRVk1k1zK6jXpdPr3V1sqQP7OEVa6MF6XsIf/Yu3zFvqpwkoNnrCcSrZxLMnnW83Ya6NG5d7WGBw/AAAAFQCHbVl3CZvAlhbRo/yMAY4do6Gg3QAAAIBb2u2D16qhuFq8B8UjBt1HSo7R/KPjRI0YzrCpJNoYjB743wARQMl5yDU67IuiIAzjxFZBiK5u+mTKpuO1v7MeTc9IFQ3ObR6N9PO1wUAZ1uv0KSesnxjgbZNIHwGwzJ88T43LEbugZleVEifbbIZl7puEgQdFko9zEgqGhOu3VQAAAIBKDmTnJZliffrAxCElJnnrM2SLo+Dl/cdtCDdI+3RBG7wTPWqMF8C9VNC5ssbi8QLDwl+OPrSIERPb3VXtjkVxQna36JK3od0aSx90VZkv7RPeL1Gt6Q/F+Td6D7p5ZCGjHaQkEk4kfPjTUnLxKn5FGxmY1HV7ceXo17AElmfmyg== test diff --git a/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha256 b/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha256 new file mode 100644 index 0000000..e71e348 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha256 @@ -0,0 +1,12 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIf91MBHTU55UCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBCKkOxM17NPpYz8mDja3yvOBIIB +ULDh7/2bZa2i5p529GCVrTjLYFWT9wLkkN6TSAGbVHeyjHm5hasrDlfK7Gni93Qt +Hmaii/0PBvWkcf2mQPKujl0gwnPel+li28duaysYOCNwMgadlN9cTs2y/+yAXzoa +q7UrQLxckpuDB3/hxn6WWDO7GRhthW3QmR6yzh2E0CzKhB2YU0tTGK7KHzGp+cS7 +Z0bwYlRlDE3YnDgi3Rpt3O5BT+uA0WOeSw1xd07jm/+okfhB3xHmM+vfO97476um +2POcDsth0a66j3p1bZkNb4NHQEp8Ud+ST+4j90lb9Eu76bXGIOHubWRN+ytyITBn +2O014+oOIdn00b38/yA/ej17fZPumEYh1fQHOlmOGi3fV9kEiTZYtiRYYET4keY4 +nduV4MVBjAFvtRWArEnkdeUqAqyEFbIzWADFxRZtfPCv6Pxr1So5DQatpe/UJiQC +qA== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha256.pub b/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha256.pub new file mode 100644 index 0000000..b69d6df --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_dsa_encrypted_hmacsha256.pub @@ -0,0 +1 @@ +ssh-dss AAAAB3NzaC1kc3MAAACBALDWcFnpSWiwGbuyqVzo3ieAm2K9+BIUYjPlA69UCM1GulxzCj9ImbQDvMsuQ3tKpMH6925NoGU5G/atRnqXouJFNcOi43kqtHx8tE1vV9v8im3+gGsOF97pVgtBMHNQr8sjDN0FrtB5MvLMc9c5b/1lhrUj5PeupHIJzrhvAvsHAAAAFQCeztLdFSYoOE+Nq/XDQh0BuuuM+wAAAIEAh8p4L7O0vUW76jSJvE3v/yPG8bcBpZporUTvG2DqKP/rISyRblRoYlwmlaTzyrC0/HrUOqw/1fiyorr+KoxJVhOavlKVq/YSGpxmrgt4eS+VXr5BJyRGxDaPF4kTAu+2/FVMu4D1clK8BuR++phu9DWN32sWAzw2IYwOfhB0b+0AAACBAIUb/bDKE9DAGpG+zMyida0JTIIPHqWvkSa2dUXU9J+4iV+Bws+X3bEggZDeeDAKd9FmFTujek25ihIJD7tOu/CCFWEcUZ46aff/RQ5bfCKsJ2SyoSRvFGGal3541YvgK5A3DgzRjEGhlF5KfJ7HIcAdTq5CYA8uDBZngA8oeQfz test diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa256 b/files-jsch/src/test/resources/pkcs8_ecdsa256 new file mode 100644 index 0000000..e753677 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa256 @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFQCSuRDPQE9G+4NK +nZDeTFNJ0lJUy0aqUr3/hToG8yyhRANCAARsBvWcCY79vTSG9AsdULAm6IonQkrR +6M2vs0EDL7PZwgTEZwAUC/XnCkHlBgpY9tpZfKkYOr1mx60J/PmVAoGY +-----END PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa256.pub b/files-jsch/src/test/resources/pkcs8_ecdsa256.pub new file mode 100644 index 0000000..359fcf5 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGwG9ZwJjv29NIb0Cx1QsCboiidCStHoza+zQQMvs9nCBMRnABQL9ecKQeUGClj22ll8qRg6vWbHrQn8+ZUCgZg= test diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt b/files-jsch/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt new file mode 100644 index 0000000..4b5fe13 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt @@ -0,0 +1,7 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIHkME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAhlStjRMGQ7hwICQAAC +AQgCAQEwHQYJYIZIAWUDBAECBBAdI6Cy/NhVCMbKL+x3uaw9BIGQgUJxrLtS+ygm +BYp4b24PS568lpErhJy38FXl82atJimDxQhEECU8ZsMj5mdnGocGEyR9WvwFQ45Q +MAuuPQO24WgvWcDLQXBF2c1ZtyJwrFLNEZ1eW2XxZqr3UEqyRgIPtUcjpOftTeH2 +BKksRQ1tixUBL4TuFX1ph1uuhO57pf4QDD/3cUeg829dg85qG9U1 +-----END ENCRYPTED PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt.pub b/files-jsch/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt.pub new file mode 100644 index 0000000..4f629d8 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa256_encrypted_scrypt.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKjOhkR/tN6oSku7uJXEqDZh3HsYFeCi6F1rnDR1roSpLy2HQYGLPjalt+E+sQhayJBOLJd8j2idjIRNK9trrYA= test diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa384 b/files-jsch/src/test/resources/pkcs8_ecdsa384 new file mode 100644 index 0000000..1eb4c26 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa384 @@ -0,0 +1,6 @@ +-----BEGIN PRIVATE KEY----- +MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDArsAu86PviKZkcTLOV +i7Xch/I8gwEH+RPFb5VTH+N5USzfRqDzFbjSBQDyl8+uteOhZANiAAQ0w876zjiq +0KEYViQn+WcmJNroHdsAGsi7dd12ATyRwK7IMAijJLcluvnZiprJKp1QxD6kONkQ +TtJHCe/Mvt5dotYjV6+XQ0pCf9idvC+GMcxDlOh4VZ/MAQNMD2DabIM= +-----END PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa384.pub b/files-jsch/src/test/resources/pkcs8_ecdsa384.pub new file mode 100644 index 0000000..fb1380f --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBDTDzvrOOKrQoRhWJCf5ZyYk2ugd2wAayLt13XYBPJHArsgwCKMktyW6+dmKmskqnVDEPqQ42RBO0kcJ78y+3l2i1iNXr5dDSkJ/2J28L4YxzEOU6HhVn8wBA0wPYNpsgw== test diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt b/files-jsch/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt new file mode 100644 index 0000000..2827d46 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt @@ -0,0 +1,8 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBFDBPBgkqhkiG9w0BBQ0wQjAhBgkrBgEEAdpHBAswFAQIAICpko7c3GsCAkAA +AgEIAgEBMB0GCWCGSAFlAwQBFgQQYR9mSZAzK41nexevamz0sQSBwG37I4qWSkb7 +wpldjYIYd3YMX0h+E+9uGgxZeBW74bQ/yM2F2GoWjDnw38YEJZceNh+RA/COh6b4 +MqD+cIjmi5aXhtDKnnqyuYeEAZtRRpWEKO8xCwIkY0kG5dNBb4r9mJRbDGJU68zt +bJGT2j5YXebwgszCt3UUhTXqF1bObYDdwx1SMOOMpkjn5PfGS9//EAF+xXBEXMLJ +h802rFX81ZRe2d6WhW5ypZ6ZAmusbgdtlY00R/iKLvzznx/zkHeH3w== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt.pub b/files-jsch/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt.pub new file mode 100644 index 0000000..0b2aa22 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa384_encrypted_scrypt.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGYBJ8F7NOBihGiGXVBApYIXiLlNVW+SSYANC3okjqsax5eFcwyfQBRSWSJxAXKA1aV5vtYMN/nBwvg5kEgftHi3L3Pye4VA1PKyoa73E4T7AsAJ1eRrJp31ecSXi1f9HQ== test diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa521 b/files-jsch/src/test/resources/pkcs8_ecdsa521 new file mode 100644 index 0000000..48da0fb --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa521 @@ -0,0 +1,8 @@ +-----BEGIN PRIVATE KEY----- +MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAu4Fi4AfgUdlwwYC3 +9ciPeVaUz8vZzqC1bXVtpPsRO5hCbtFrAQBdIQGyJ3chfDt3mi6UHToPXPEMGvSL +weiJ+0KhgYkDgYYABABdYMYEOU8AxcsHYPdHwtbndVLfJZsCFFQ8oMUt+xHi/MwI +SVkojGjk2FOtUeUnsc8SWXHEUC6GDSqy25+42GsxWQGL7/IyR2X0Iu9rUooKrKg4 +EsvQNEDfq9Joj7zuWOmAQBQK392fbXdYYILjSAaG5at9mhiw8Kr65ShK2IUv4n4d +nA== +-----END PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa521.pub b/files-jsch/src/test/resources/pkcs8_ecdsa521.pub new file mode 100644 index 0000000..add64a9 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBABdYMYEOU8AxcsHYPdHwtbndVLfJZsCFFQ8oMUt+xHi/MwISVkojGjk2FOtUeUnsc8SWXHEUC6GDSqy25+42GsxWQGL7/IyR2X0Iu9rUooKrKg4EsvQNEDfq9Joj7zuWOmAQBQK392fbXdYYILjSAaG5at9mhiw8Kr65ShK2IUv4n4dnA== test diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt b/files-jsch/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt new file mode 100644 index 0000000..ee46d85 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt @@ -0,0 +1,10 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIBVTBPBgkqhkiG9w0BBQ0wQjAhBgkrBgEEAdpHBAswFAQIf54qi5gx2/wCAkAA +AgEIAgEBMB0GCWCGSAFlAwQBKgQQKNwzbVYlv85zx1qRSDIuNwSCAQA6KsoUMRQS +BHjCTbclDicOr16QQ5G0weLTjfy2xxaefnWlU0Kb8bZ795Z69+UfBcIPKeTi16EK +QdamyrEqxioimHtLrSGY5wFXGD3SNXWOULKA7HIMwbvpD4w4ccuD2vv2uYmoMXeL +s1l8eOC2gPb5Tip10bn9ZuHK5xEVMAAzFjO5ycT8d2j6ZUAE7EgJXdjCv6l+59iU +texwnhXfX9Zla5K+wrMclWV4TIokjNfckPP4ZwBIrYsYG2SXtpszGWWBviZrb6Fz +1DI/aApGZzw0MiZoYQ0HuzIE39TCdH/nvjMtWK1LZy/fNxipBtfVHOIB9LxpNyYO +ijzcUbrY0bU2 +-----END ENCRYPTED PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt.pub b/files-jsch/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt.pub new file mode 100644 index 0000000..f5c9b1f --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ecdsa521_encrypted_scrypt.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACWNfRwfrfQb+K8d5+VutrDWme+Pk6UyiDvbuS48bCg3e9ptaWHQVtQ6xm1yLYUeF/ZVWn20+s2DntLGKX8dFCaSwEHVmX4oj6hx2V5zprevf3GShfyz+HXt4qzoPzVMyOPY/po4d5oxM1RM7oWaqNwvuVhU9Zk5MbJSzKq3atWJv/+AQ== test diff --git a/files-jsch/src/test/resources/pkcs8_ed25519 b/files-jsch/src/test/resources/pkcs8_ed25519 new file mode 100644 index 0000000..7114dd3 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ed25519 @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIBdzIGiV1WyXNDyPVUcXZrG3jBYaXHG7TBZK6rrRYRUX +-----END PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_ed25519.pub b/files-jsch/src/test/resources/pkcs8_ed25519.pub new file mode 100644 index 0000000..6ad5b04 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINKulAsCNhbGFNT4DlsMmQt57Y1/i40T2EWpeUWjiDL6 test diff --git a/files-jsch/src/test/resources/pkcs8_ed25519_encrypted_scrypt b/files-jsch/src/test/resources/pkcs8_ed25519_encrypted_scrypt new file mode 100644 index 0000000..ff0f5bb --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ed25519_encrypted_scrypt @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGTME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAhFrC34mnc2UgICQAAC +AQgCAQEwHQYJYIZIAWUDBAECBBC4Yl2xDb6OxiknqsqFrNJSBECqIsjJq1c947Wj +AVEwaAqDUkDPywSKR5R4b9lDAEclbUHfZAHDkDX9kk2+zwT1toPOuECGq3FN55BO +wOUhPR0o +-----END ENCRYPTED PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_ed25519_encrypted_scrypt.pub b/files-jsch/src/test/resources/pkcs8_ed25519_encrypted_scrypt.pub new file mode 100644 index 0000000..51f914e --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ed25519_encrypted_scrypt.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2oQIAotMhMO8LyWecYBIXFlxSFRoxm8A0Hpbn1mEOJ test diff --git a/files-jsch/src/test/resources/pkcs8_ed448 b/files-jsch/src/test/resources/pkcs8_ed448 new file mode 100644 index 0000000..0d44b40 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ed448 @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MEcCAQAwBQYDK2VxBDsEOei82miP3EX0EZ0HhIhYRIP5r4+MqIPN/x+Qz/9LbyPb +arD2I/6g3IU94gr6yne5fp3/coL8xBX+QA== +-----END PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_ed448.pub b/files-jsch/src/test/resources/pkcs8_ed448.pub new file mode 100644 index 0000000..b56426c --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ed448.pub @@ -0,0 +1 @@ +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADlrhnFj5z0wgxD0IY7ECoZE3/8v9cUWP7VJnyp0YPY0aLKBLnagj5/GMDV71bYW56Tl7AFx3EQvxYA= test diff --git a/files-jsch/src/test/resources/pkcs8_ed448_encrypted_scrypt b/files-jsch/src/test/resources/pkcs8_ed448_encrypted_scrypt new file mode 100644 index 0000000..0393dc0 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ed448_encrypted_scrypt @@ -0,0 +1,6 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIGjME8GCSqGSIb3DQEFDTBCMCEGCSsGAQQB2kcECzAUBAhr6TolSPij1wICQAAC +AQgCAQEwHQYJYIZIAWUDBAEqBBB73QBoaoKyPGPrWSkWZauEBFDiTfYOM29sHJFC +psVERa9DH3Ir1vvCqAL/IldUx+UcdzjQxQkYqZeFiNZrFuNlDPua8fLlvuH2JNCK +E7qNRbNDX/S/wk9ADpIrrkYFbmhTEg== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_ed448_encrypted_scrypt.pub b/files-jsch/src/test/resources/pkcs8_ed448_encrypted_scrypt.pub new file mode 100644 index 0000000..4bc65e8 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_ed448_encrypted_scrypt.pub @@ -0,0 +1 @@ +ssh-ed448 AAAACXNzaC1lZDQ0OAAAADn6JKamPkXumrMxQnui53u1e7U0WkROtrmraifd6LEDfbEW78xEmNPSVZARg0ydaeNYzc+iEGG/cYA= test diff --git a/files-jsch/src/test/resources/pkcs8_rsa b/files-jsch/src/test/resources/pkcs8_rsa new file mode 100644 index 0000000..903916e --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_rsa @@ -0,0 +1,40 @@ +-----BEGIN PRIVATE KEY----- +MIIG/AIBADANBgkqhkiG9w0BAQEFAASCBuYwggbiAgEAAoIBgQDCOSYk6+5VS4tJ +qK2Z095cPYHjlUT4Gqm2MM5xdzM2nK9bd4slUyz8fFYpWAIvu1G6Q5z9e/2Kfcme +86lCjN/lidSNHQBpz6xW2WQTuTPfSPW2lBblRovlezhypdHTdMt1NB7B499py1BF +1iLO5Y8URPCztTXawneMnUjvUp8g6dDKVDGNBssgwZ3TR8Fj/vrOkgafwtJAl/X4 +CJBXdt45dLre5eRXEoWDxTdW/GxUw/PtV/mW4Psheo3/RoIFequh2m7U8uSdXKvG +qm0ZlpSTSoen7uELSd+9iY3E4ULdm03Pom/O3WO+DOCmRb5YUTNFKZUVop3DKizj +Eggy9mbb5W2iJp+6K31YWGWHCR/HhFr/61R6yQomXzHhbqAEGH1FsBmpRDa6c7a/ +b8Xl4w4kbBlOOs21G+tvL9l3hYqBfWpPE8A72WGdkzaOltvt9TEDroNI2yopNWTV +XuUvJ2maFqiwf/4cYpGZqu3V4ccBpTJbhHQ3G3w5XVQ6Np42ez8CAwEAAQKCAYA0 +Ssi/VhpkMqO84EJlUUrkENdg2/amyh75Y6ihmhNa19LK0KPRF9Tb4eoc1Yo5Kbj2 +am/hO7nmyLk5J6dhuKYrmfF1UOKkmnpvI7azLMEPlAg78SE898KAta1cCNM2mJKS +6saBM4YaaNgjBWV3yQy9y1X3PInUbVlcrZhOfzNC3FEPuJ1it/qGxjplAUPLVYJX +ja9k4kJTi9Z4wm2Cbwmj9I6/pqvU2bLC6J1euTW9mMibFX2opshWmjWTDnvBV+ZX +jT0i+D2p0sRHO2oo0O5WKvII6UI2csEM5VVCR7wReNxvIjCFgQP6+P+msIl7YSvm +0Pz0RWbFVsbP+EqyAl8DDxLfq209Fq99V17T1lVu/4y9HEaI+4+hsO6JBCNNezK2 +nn9bq4GYlZ0xrxQuvEUG3BjaIJe0B7XIC2XUBA+530bImN8D70sO+3Q1gydB2+Sw +uf1sYXMSfJwyy6qOsw+mJUNxYowDcCo44MRcB23XFQDLEb2MAFC3/lIvNKlQSEkC +gcEA8P8UqV9J+ItSDjDtsA/un5T9DCy3zOJ8p9TMszJjuteCJw3huKFPG3oJIBiR +uex2kwLiS6bNnZiiyW6eFy8e+w/xOVed3XKjculIhoT1ZL0IiBCsaDa/+L9hBqG1 +257k9yfgTl5j1ZppRzy0JMEE1Y/hQ5CZxxZjw+Am3XgRFzqpLqhd5vi0oGXgL1PX +zHeiRp+oSnqr+JWw3DBhLbQ1SwbDXco0vyQj5nR1+EV7A/DeptPBeCn5Ox/p3g0R +PcxjAoHBAM5QnQHYVeNFIC4zK4WC42wnSLaqzQS0YCx20NSs79xmeqiOhkkqbnEk +2E/l4LKb0j2xtNEAIs389QfKoqwXlZB01Q8oOHMd6HKd7eelh+bkV7mig6rV1O64 +7TC1jTVlCKXL/jADVHd/DKCZ74C9AOubU8ONjSH0na6Stq/nYQrAj7aHKKJ2wBP2 +WRI3lj6Ma7Ebl3OtyXIay+gBegEMoAt/eJaJbcI4LirVl6vDTBXOGHtNSuJf9BEZ +plgsvCdGdQKBwADi3z+UMyBv2rhko6sfE+CQWrHdxDtDpfO0C6CpEcbRHhBos6jL +JxBRzZDJpleJsBHwU8a1cVIgCpE1D+3D5ZhEwb1VPiZfUoyMoRClkoVxUIO3k/q6 +INCYW5H7rECHgA4Mnn2LSVCyxapWZc8wyoTCh7CI6pfZ1DoXK+1qkJ2GQLMEOXws +8/UXCtEBvOxqkDU0RxknBFTgsArPPrw9SmjhOHyyzqjZSCyDxx9HmiE0lI0GgFKh +zOHTxzdi0upoQwKBwF0VAcFTWWywQ1SUwY36/6BMGQJRDS6SMtHcyVsqqw1FLxHd +O7jG7A0gEnf5vubWZoRvKKUi6pEFD4f0ZHBAM7p4+6da/hzx4W3U0wEwLaB+ZRXb +vIvW7brGtDzFrG/qpXwoDQacef9v0sKisOvkWgXyjlgsBQDM3Fdm2gWC3sV7G1u+ +nxatfPuEqasOaTrPH8dIK4yFW92fOV/zSpgumgaV/FHurxZxQmMQ+t0Dv9AWq/T1 +alO39uXALMybYkhWAQKBwCKNkCzsGc3yMzi32go5v9ysg1Pfj7qqIUzdxXWXFd71 +5e/1I4nfRi/6cZjmYQn6Br9oN/GkzMZXiPE3DUjwCvHPK3L9EqPPDGnxrFc05GnD +2JsTzYT1XqlYraCgKLljlULjAdQgAm3YaXEoBY3o46yvG7/7N19OBF1j5SkcRHyk +Pi7fBAKkIgBoPDCA9SO4Z/9aBmzImXmbkSQpawHOEYTk2xTebjYLl1oQiuy72b6s +F1BlgpW6GHpeDNVV+j8vTw== +-----END PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_rsa.pub b/files-jsch/src/test/resources/pkcs8_rsa.pub new file mode 100644 index 0000000..6e2a017 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDCOSYk6+5VS4tJqK2Z095cPYHjlUT4Gqm2MM5xdzM2nK9bd4slUyz8fFYpWAIvu1G6Q5z9e/2Kfcme86lCjN/lidSNHQBpz6xW2WQTuTPfSPW2lBblRovlezhypdHTdMt1NB7B499py1BF1iLO5Y8URPCztTXawneMnUjvUp8g6dDKVDGNBssgwZ3TR8Fj/vrOkgafwtJAl/X4CJBXdt45dLre5eRXEoWDxTdW/GxUw/PtV/mW4Psheo3/RoIFequh2m7U8uSdXKvGqm0ZlpSTSoen7uELSd+9iY3E4ULdm03Pom/O3WO+DOCmRb5YUTNFKZUVop3DKizjEggy9mbb5W2iJp+6K31YWGWHCR/HhFr/61R6yQomXzHhbqAEGH1FsBmpRDa6c7a/b8Xl4w4kbBlOOs21G+tvL9l3hYqBfWpPE8A72WGdkzaOltvt9TEDroNI2yopNWTVXuUvJ2maFqiwf/4cYpGZqu3V4ccBpTJbhHQ3G3w5XVQ6Np42ez8= test diff --git a/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha1 b/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha1 new file mode 100644 index 0000000..2bb176b --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha1 @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIj8U2DOUAM48CAggA +MB0GCWCGSAFlAwQBKgQQgkPCvl5Jsbu0bwVn+H4d1QSCBNCXNIf7CZi2cVPX3lbs +r2D0aka8fzyVyazOWK1BTTyrr0lA3EIUDHDwp1tXoGXmTfyD4cto+pCwqCGLJOWr +wwC4d0Av/Atdo+N4iePxrt2HBhDSWQsX7YyhntjBJD1Lk2LVt5qmlcxU+maizcLP +afTdCOHIhRIbxf2dhrxOD4Vk6ccq4cvzDLRZ2r97H/saB5xQDyZkQn29jRFBMWuU +nCbcVqgpfGV7shz1oecn8KyT6+B4Yxzv3SuxkUchZMX4FMj91Unr6B2XQ72X6ab8 +Ura6xaWuxIjkqTxKgwu4dVyujp3xTFA2DzIxdKogRurMLOvWBpAezOXr6whVrziT +5gF0JfFyDevdNWAB0YM0MGgoCncI4lsR8hwaxXOTqh2Z8o/L3+ec9u7vckNOQinT +fF0t7Gpup98Iu7uo3k2dqNtmUiHfX1PLhqtqdGhDjQTtjiUzHaOX82o3M3oGb5u4 +79c+R3snNtG8PlJDnR90YHns2YbRkJrFCqJ1qtxbN8qeWwUv5/Dc2Tmv2MwP9tUW +34/0gmz9ylZAXK53mRBCdqPVboDKJd6rWK512Z41Yqire0T37qO0GrauEJMtVuCV +dvO86jEc75sJcs/mGM0EGYWL9PlOphq/IHyFD1DHdA2WoieIyIg8o/DBsgw5xKW4 +wpu1Yaas3qG/X6XIMIGGlFl+H3g7gmSgmh1SjMsSnMFgVTMRmLgn1hnOaXNLrBDv +nZlhrfVMKk4dCE9JfTnlC2J2OdoGE9bmW3c4IKLtlCTyoyZn9hlzfW6zprjV/Cz+ +ZtnCv40nO4GozXBlixXd+OPPRF7cLLBwPYzMs19zbqZcnF1ZYVZuEoarSG+yKMOi +ZA9KPOwu8BVGoLW7WKG9XvTPpYeEUG1dNBnJyUGxg+m7CVM8f/2q4YBaJoZdmDBa +3AxCAFckhJIk1SUGt3+C5+QBwPU2g905umZIZ8cH2X3w6T4M+bjYdDYj2+CfYV9R +x1dSNwxMM4fVF4Q6/H1efyFjLeIpweSzqU9ZayxiDsVE+3LqPeJrcDOSDi4Qoaxz +Lub/4KJDkrFw9FtDDkM4VS9FVy0BzbHayla3gWqmSBnjc/7TEuWEfKl2oULAklME +aUAsKzw3pTXi63nFtS2nJyWDr/Te6peHmpXoTtXbUi5rIt2tcRqPEOowxjrSflnb +Xv+1CWbTqGkmf/gXpapbcNgqKQQiR5lsBboXApwOutGy8rw5lhUc/88rI4oS44el +vWhFdvBoQSuZLOXku8OoJvsv0HVLYa0apsDdCaI0bcO/0KZZKAy3VTaMZq5isgyT +oAs4DHU7LY2SVFCWFt9Abv10Zzsjl2Ai5br6wfhP04tV9/VdC29MFN8Mv8PfA0H0 +cMgm8UdWwRFdDSYyqh+/sdfMXRsoXUU4aDWcisn7CAIOqfFj3dkUkfYZZaFeTQEs +5k5pDn4xeyXX8wSyqjTiei7iK/rOmRPD4RyuOCG4KMmO8fJDNNuOQX2IhiKJSHGu +BeMrXUDZ4zGmjy1Rk8gxK96HYZRV7uEmaV5deVYh3w1CejpK3nwN+uVCM2sAdLV5 ++853Qp7/GzrCujzGbOUAp+h9nQy7YpdTRDeoffE2TKBc5jVXnvKRUS0JyVVnEtTG +H8gn/MacxmPzp72vKecGoelMMg== +-----END ENCRYPTED PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha1.pub b/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha1.pub new file mode 100644 index 0000000..76c1efd --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha1.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDDIUGLfSbG/8i18Y53PL59UJ71RN4XTKJEvVljZM9K0HvFkc0wOlzTOXoOAEJiTIq6JXVWjq31BJoC/x20ySmaj6hSOs6NGrnyxXzSbM4E+PzJo74avh+NXZ6ojD3CKg7XuShgX5+PUqEiAffK3TdzRX4NlkIfiIaHi1O+Q5drSGwnoB1C5sNeimLDhO9IPo699OvQu1Obh06gygWh5pnu0NBuiv7rWwBx9S80fJvqhAQATxv2RWFDqcvn/T0WApJs5FCpyLxKozTpOenB9OGaEEz3mpg6H4KPL6UwhyC7YLr3rRKyjff08uELvdQzsO8q6tO8uOaQngLFhLe5lrlV test diff --git a/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha256 b/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha256 new file mode 100644 index 0000000..6008d87 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha256 @@ -0,0 +1,42 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIHbTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQINQ6pBY4N8X4CAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBCj+GNhaK8p2R+feHgNRcWVBIIH +EE01wtlgC5EVkPa/OgdN7MA26FSIDHTNqj+0rDhzvpgrw8R3IFsb7WZf6klE5Qns +Ct/mZWt6ddMJwiI3xjPOQX6E4aJqgK5NanwftcuUjKICrOPseXEi8l+P0vwInn1t +zaDxFgIqo+N7L/0MXiFbubk2eoUfpj/xPaGz2DxQqilQabJfWGy6S3VSw1yvQqKP +q6IFJ9N+bRXbrrIRD3RgAkMfJr5MhFdwQPrWoUUr5U4s2JHY6r9hEaVn2UqCnopF +FUj3aX2JQtLuJaTsRw4MmnDcPysob83w+njx9+1dldXXNW3hfWZ3y0RhFH7CfbAl +h59yzS26rKweI0ir5Va+SVfdjHQWx8ICYr90rTt1tL79DHnmrwK48RJe0/mJ+ITc +eQdA9X8VcI0mRQRdACHt84N+fzNG0/w7iiCK9jpS5hePO6cUlgkpC9/WoNrTSV7R +NFTEmSp6LfTdMcWS0mt/8VdpwOcOlxe/UuXb8JSMk45ByutkYjrrvmDjx6hQLLhd +urPeSYiC28k3zBHQB+PVdbrX5waHIQQkEykYjwQZR+ICh0fwveDPn8Xhl/ZslkTm +xGmez+ZPssAtMLvlY+oDm43QjLaUBkwP8BH+vbhZ4Tmv2+Fv34+Dors4L4IvwXlf +nun4UilFHoMbnZKWDTAQ2DvPU8DPnEVVCuE65nhOLuHhxdjMBc+Bl3+RdMuNGKb4 +fvW965RrDUNMkqQK0mqHVMtvv4eJiZAR4FQa2SzQ+9bHBv14L2uwxI/d8nNUFbgh +imG4mnOJ7LacUDX3ewI/G2EgdJDVV7e1CJbTPu/EftX4uIkPite3JIcwF0ajr9+d +tm3yYAr8jLp1ZR7vh89eBFj7NPAjG8FmxjEVMU11lovND7ioL/f+FITl5blUqZok +4OCoOppa7BoPGULwB+fV5xiPa5V9KB4OqsJpIoCoMrxVMGsEkCgu358vdmbrMckg +B9yMPFfU8dF+SI6Galfz3/iR3x6FKW4TiqgqkrZbpb7zs1HhnzHPPdvniSatgOEa +QUSb2yZdO46++/2j+kfmhb7cx78ZaBQFMk1onv8sDBUY67LLD8ma3QkTJb3aKhpV +gwK6NIv9D+F3kNrpnscfp7OsvZBxIXmMBsmH86TI3k/x0/tjTD3Q1VYSL3OAetvc +uXz/WTide/T1EVSTdrv8H91QGypvxX9RIrf33KRZzCAdeGa7LABD2aNmhgif0gzi +0yzilnfBjNZIZ18Zb0cbJYPr4Dvx3A8iqhCjJczN5yxe5sjQB+sxE6O1bQWOIG1r +R1crXRjRTK3oauBiAfKTHmlmNUl805QBgjnMmeQLNGUOyVC0IdV3d/Q/dc4WEM9z +QoAN6OPWNhZJLVNCLYl7kDtj3GcjHde/QzGJjkrB/ycP989MXk8B646m3uEYgVFP +0HZseOF30gTvnLHNV99nDL16di6rr0St92hXtwOWR3NrGSWnzxSewP/d+adL2DEp +TIF+WAFmbXr/iaAMPuiI2TegGiMf1YuWuth9vNN3E57l3vKpPXizh0q0617nCS1l +yOkN/9ekjD4Cb8NjVdAxWlI9ibpO+ZNH7p+FzkxyiWbc5HcxGQnVcPpoZGiPpCMh +TEwlaj0YAuqb6wWksEpOeb7FAKslGkQR2Azw3JH5hhpRKsVdJcVXVEKlzizazC0N +wwe7Fm/CUdOiM5pW0/ZkvX13lRxxwWYRczpZ4hcu76DF8SMfO0Sks+BF1qNU87Ay +Ic23Nc4Dr9YiTlf2/MQmmQBBZ6l+9Ju/NbZYSaPC69Mfkt+ENn8EYcX108Plaioc +/rO1gkqE8g96ABvmddCRvqk0O9aV6odMCBBzeaV1X+teH2gkGTnhsE1QQhjScVxz +LXU3kgtfeTZG0hwHI0ZZm4KKPn7gb+vNQ8oOz1Dm1lGaatUuiwuOMu88M/hqWwh7 +V2l61LEe/ugCGZuCJFOyDHZpDeaTtX90pl7ijDuUfVZnTBfGkoyoIQScz8mJkKnr +yOV9oONevpF5Lf7uFihRpgWDBc1UsjV+lWeZo+IEra+W4hx5ADwqsThXKV9slhdq +/xJlbmD1hvFRHhdSBbiZ6UUCmfP8XxIKDi4Ghfgd0ysrrVWDOZPuws0lg1WsgUOn +kwHwfN0KXYfbdl+VQ3S51inRJUWAWDDQklAwBvWA+S5grhFNHRONk5OOBLvX8Atw +86nFW63a0PR3X0AFzLAJZrqF+EZbeloQgX2EX8gx8XFIPTy7XoS1T8/Ezq8AIfXk +JjF6aeBy8YAnRJn+4sPGAqX5QqktWcvIkD+kv2dZIADOhSRXxQq5ZA0WZchC6l18 +1lnAq+XXHq8NxupJ32rrZ1IRf1VIYCsJRMNguXKTWKZUHPAQxWhLuKW0ZBQJM3AE +t0Q+gYvgja/p8WiC3l6WnioqJdWD5/FEsRGLy2GnWsvI +-----END ENCRYPTED PRIVATE KEY----- diff --git a/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha256.pub b/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha256.pub new file mode 100644 index 0000000..8647cd1 --- /dev/null +++ b/files-jsch/src/test/resources/pkcs8_rsa_encrypted_hmacsha256.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCszdi6vvJvN331KkQI2SVEtrwBHQunxWGfMP3HnSn4aH656Mcx/9a33V/W5KwKoSgVeRsHVnbjby10yP1PaU898hILUDFBazlJUyfxvMybiP6TC6LMomCFqoncaNJ+hR2BsEQmrIfmD+cvIFADz8zPtjDeOSAPQumP42C5uXJKNKYM8nmpCUrHo8qSvbVLTX/UC8mZweenxY21IxnKSZkkuLkRtqp7+p+xc6bYNfXcdb6LyrR/jmD2GsnEXSmWFPxvYGAErUZBSAMF2xYGG34l+lCB7Pzvpfv/mB0MK46YL+pdKC/B3JjDu2izt7cEBTlhd/TwzNah5OfsXtVJBNlYi9FU/Ee7A6+ylb5WnNasU165ngIVBBL5UNg8Hg/W6XJJmAVZg1PsRbikNNszKeaRrTC3X35PEr6uab5hlcORcAWV2FYoYWN8iIv0rFJyaE7InpxSHp/G3lE6XHddcJrq6BdmURez3jShrCYg7DdnE9Nx7dBX1rbJ46lLxKY5lQs= test diff --git a/files-jsch/src/test/resources/ppkv2_dsa_unix.ppk b/files-jsch/src/test/resources/ppkv2_dsa_unix.ppk new file mode 100644 index 0000000..de591a3 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_dsa_unix.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-2: ssh-dss +Encryption: none +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAO8/3Ymo6sXoRb7gfTBqwAYPO+ck8aKsoBdQIn2oD9cX +2EIjXaoUXv/gK8tVN4EN0C8XADEFXpE3AQU5WhVZ11bv0AXOqGOreoP381pN72ap +hh9o989Ee2PET+wEXZnZ4PmftMcuhtHWC8Pq0coht7CO6Xt56ItQuvj3nxMwYc0h +vEFDOpHaFCyqiann9q3W5bLB9+ek2tJg5s1AVQKnlGCwn1fj+FXY1cRFDsp7AxGJ +ZPDSNjq4awEZwECG/QLQYiWT0FVILFv68SxQeGk6OlOGip4dKkdXPIhMqHy0GT0d +iBUm//UweV44S9IXPF9Xsiidq1Dn0vDe9e5OYNmV8kcAAAAVAKpXVF5GLMgMtlhS +vjICPOy/A+T7AAABAQDEFLE91TMvZkCGeAHHXHkjF02m3CafJ7qLej8RoTEeZxV8 +Op/XlRluKNn97XF5G3DHp/DWGqBgawbBwqEq7l3V97zRHESMlivPr0UuCvjSmFR/ +on5nFCbl3RtZJOHOUkamtv51zBfk124uw7+bpnm9aiDNUs+AHNC3SLSyvdBbz6+s +gt3kqD+FZyk+7TC/sOm+zuNmducFDDQIjldI7gx78b8D4BBx33+P5qW12PSlKGrQ +7E1Rnk+o9WMSJosq/D7lV56waSV6/AJE9lruQr3LGEwN28DrvbVWen8KtvivGlJU +wl1OK/hMBS5u+QZTIzN5M6iGB2hfJjBbF+LUONDNAAABAHBA/s/1ShLjRiIfenBf +nInfjl3a3DSF9M3/o1fuY6EFkNs8MR4P2aeOfk0zE4gmO3vlT7TFs960tdUdIHm4 +TFOo+PFp4pjvVSO3K93cAjzOTwMI4sPhB3rRJxrDmx1CWVdnBrWQKO78kLpLf63/ +QB71qEe7MirH4FXZRLFP39gRcWknBtHmez92CFlzKzqLQXAMfaSbZ95lPh/bwgQ8 +OCuYExuB3aw3QmoQQdzSJRfWfuCEsUMmJ3Aub/yUMkpfe+hmQwkG2v730MXp/uDX +fDXhZKkoh+v7Mhm04wzpxtB/C1dYpmfSP/i5ews4+9Lo9me6l54xy3+k05PPBBhn +Yzw= +Private-Lines: 1 +AAAAFBtGbtke8qQ6YY4xizrBFRlokJMi +Private-MAC: 10e180bb416d61cf0196c6b1c9b75382f092c20c diff --git a/files-jsch/src/test/resources/ppkv2_dsa_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_dsa_unix_encrypted.ppk new file mode 100644 index 0000000..59293ec --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_dsa_unix_encrypted.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-2: ssh-dss +Encryption: aes256-cbc +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAKU4cgWySBcfMzYy9aSOQwvH5JM1/M4UMNIB5BOZocC2 +FvJbEok0gTTbLCzGI9A0pe2bnr6EWx78v+aIH6p1GOmbhxZVY4+vkEUMH9Ee8v7X +MYqz+CCefWWiAH2bXNa1v7LC9mImiuKmcDdHiVLJwrj/TH3c2EUYlcyQSUGRyOmi +w5/auITPrAlmwhyB2ZXLC0X1qNKn+nJiwWxe+SsTXV6F38jbZo+fepbnVEqZ3wTU +VnW2gpk0z+LRiLM+1mhASykpmRCyggxW7H2NyNGW+9jVTXD8WCqB/z0o+7PVCPS8 +5qpyfNi6PhH+etXiYnyG8iGI19BWZxDmMCG8Sgr+AnEAAAAVALkJetYWk3MacexW +EA0Xa78/o9RfAAABAQCeNE28P6R13v2OUqUtBe6oYQPHOHzMFdEk+qJoXyzKmz7Q +uQiCGuJ90YUST3fhvsBCOY6lPc6zGfeaVb/r2loa6zm3/0DoOXc/dnU4sbB0auoh +PlwmjFXDwTQF5BRyxuRWtvPuNgKlgwFd/GcnIwlgW/jRIF3j5wopM3m12+yhZnW+ +gh575hjvf9edpra7Y1u1VR/jYEQRg+GuXFDvcGE3OTWpphAe0zfjwqIOMYYvx/JQ +4HMnB+FxJ4svs5aUOPwBXhMCT9YPWEWVr+1GBVf/VlKTrbapNODYEp0tuL2z8Fva +kgquJ2XGY74+6M1wbhrrDZ+LPd0Iuwcr7N0ghtVYAAABAEU47Jcr0BB3XEcmy/Ls +L+hSKudgmczV6QD+fxelS4mY3//8INn+wMRyQzgix4B5yGL6OzQ2RBGlqho2TdfL +o5UOjIoQzOpZsjZu2TLZezS5Lpx85ZVuLWwqezx1NZqGDXhtDz2tdUV8eNbePWb5 +788WXYXOW43ya6Z5RMoywkO7sZRziHH4I+WwlorUpDMgzzfGs2j91bpejsBYcSnq +cUDcHJedbMmcUQ53OWocK+/MwoYi8+F/UBuwAoZ8wYauasMZ/ph2k8ygzWHCv7NN +teyVAY3o5OvrML4sfu9kmgBIZKKfGZHeFptjQdQcOj7fTI3tuqocwiWVohYBF7la +KvI= +Private-Lines: 1 +WJAxySL5c2ikIb8JusI0EABRvX2f8O3tBbAuCx4o/i0= +Private-MAC: 3384ce7d2ed81d15d76e9f80c3196813800d241c diff --git a/files-jsch/src/test/resources/ppkv2_dsa_windows.ppk b/files-jsch/src/test/resources/ppkv2_dsa_windows.ppk new file mode 100644 index 0000000..023f2c6 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_dsa_windows.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-2: ssh-dss +Encryption: none +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAIRPWjGVTl5F+y9uOPu8PPtFyjFy+Qclpqww/vdKq7GV +AYPU5zdDHPvmEBZKfklfcROB5Ody3Bf7miR3RoLmmK9SZLDqEint+FdbvSWEooFY +exZmYukhS9FeGUwttMOz4K4JIAPMVBxbGMPOL2AzPhE45BT2PPUpXMNZTLft2YHY +hSIg30nsx+gc7tK7dkdY38pJ3Qtazc3jpyHnxT9o5qA1OdrA09nYHs9osneC4O29 +uru4Qz+foNP8XbGbkl1XxfooweQiElA9kvoOX+IgtJGgX2fuexAl0VcRmeb8lYxC +0KOo9QL/HRW84nC8ZvwhiOXwbvN6X+tmKqTpcYQXgOcAAAAVAKearN3tBfL6HPzk +CHzgGrUbSK1zAAABABW+7UNnhWAvSwiHShVSWm+eHjY56rss8OoYCvVRxDTLYhnZ +s8GvjlHJTTvuPbNMiWYe/vqwKhBDkNJe2cw2on58GsJwLLmdhsZzGRuCZ5twFLus +ZsjjRImjBTY5ZNYhdbhM4/527a7bSRLT+QPsedmaEvJHeSMHwDYwEb/WMsYnL7Xn +QTaPRjetz1a64zWIYQU6PTHW+HRrQDT2Dh/knymQvcw3QC5/ZwNf9k1HS8FdE5HC +9G9eQfPCGjiFGLfVfAKbq6HslLZO4Ea8WnFLm5CHz3+mFF+1fHXkahGxlOkPGMr/ +kSeIvcneiMAqUDINC7VcXShVAvATGyaVh324uwoAAAEBAIM8p/hq3+3N5V885fGy +BuO6H9RFmmFsuA5c70CXm3A7CHLNh1+ql72WzaRs3DyTICexOtEy/m+2jFd2suKa +Sa4F74OMQv4KaeeXrGuC8+EGdWFMN0Tf5UkvR/3igCFdxUbhfEa7qbMJBi5fOkkY +1/OCldoHHWwWmp7uJA9oY7AVaFcwVyzOOQtUxBL6rtAE8Bcff88H2TFT2vf0WX/f +KMKss69uzbphIIxnqnk152lA/gVWcT/mhCWa6NBIGJqKTNppKExTcLBcLlglqmLF +i616A7gm8Y1+gi3dnDVu/hWiyz8uOSE31LB4oJGX4fkhOcKgwMJbB00F2ZGPT7JM +4JU= +Private-Lines: 1 +AAAAFQCNWViCVJUIHWjoUKL46EXL0dwMeg== +Private-MAC: 93e11b58c7d05304857d602a2b0db8b8047414f3 diff --git a/files-jsch/src/test/resources/ppkv2_dsa_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_dsa_windows_encrypted.ppk new file mode 100644 index 0000000..8bd2afe --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_dsa_windows_encrypted.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-2: ssh-dss +Encryption: aes256-cbc +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAOex/Q9Z8ZKXJbCIQnLHKuJdhGNpdIIOsmFYpNGCClPz +4ZTZmk0Dbw7sn2KrshVAbi2RXAIz4gJn+MfHRF7okwgAgN94mMP6VG+D4B2Zpgyy +qWRYSSdV5AmbKc2VDX+DVK9qKmHlEX3Wjm4Yfw3s1kGN1/eWwI5MrHB7w5tA8Fw8 +iUfsRLnwTyGPBXbR1ATqT/obLAic6vogcpGojg1s61k8Rlz2bVMaYRu/RQsIdxvU +Vi27jn00ClTaMGWKH1ZMhnmjedvN7qI7GcAUgNXFjOGa786aPXzJDC0JdNcmDoei +9G/ynTJOpMMo8oy4bJLnVqL/kS8v5k+P4JeIhwvgp+MAAAAVALGpbWxdf1geBNR9 +INuHLoevUHjvAAABAQDijQTRDHymwb7TO5Lxy2P5MBvxlhTlRN4C+KIo5rnZMX+o +FeEX5/hG18grUn7FYdx5/4AHP5EhK4GIcoKYJqnEjXvfSuvBvD2OxES8y9QI5Y0D +ZSGyAsV0p0/9N6a/khkdJ5Zs9S8iFeeJkevIVsCskLRkz6rrBbsuiXB/N3ojtetE +QfFuznFVgEJyBNrk7QuVvu43nWnzYaF98ah2o4C4HdjAds57XzyhDks5kJsi1mry +l5jvbCSxcf4iZW9OsOHYcjifwfuM+hz2oYYf87obEIIMdOtsvHjMEfTmBqechUqq +sRP6wzv0u6g8rReMBldaoG2HFGpwjvMM+PGU/zdWAAABAAhLKqKGhmXzW3ZugZft +ycNwrxXFeVXFBywlC1qtDH8N/0aW4AvhWXBk2gw+rDXQudpNlVbtV9sT/+9JBM5i +Q9txRfDjVeEzdOuXR2ch51p9Ep7KTYoSaBrBqL5KCxsz12G1+KJazMmlbpxQbdWY +rAJrlRjTcHnYCDuhyUV/yCnrR+7VMAx5FyIduuRtYrCgo/+Z/Z9UklPU2596/pp7 +uozkCBR+RkOm7+9ldU+U9YiaUWymVmXhohFJvy9O6tqj3kZYQll2h7NnGpKVeIqp +8wNW5BoCyVwV+fIuyCVsWg5vOKTBu5GR2Kc3AMc18Uq+WEeC/jsXZS53ItONrdNs +QNk= +Private-Lines: 1 +zn+CjCYuUbnPlsKh8Ge/gwCgzNkTwlBn4oeqV6/G3oY= +Private-MAC: e0dc648a379bcbf2087192cb32fd1ab061ddba04 diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa256_unix.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa256_unix.ppk new file mode 100644 index 0000000..1dd1901 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa256_unix.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCo/5qxk4V6Z +ZGDFHDeVBr7ayBkc85+7SPuuY2OnBSrObqo06lw3nlH8dcloRzCt7qYvbmWTutlT +ujgSOJuvd3A= +Private-Lines: 1 +AAAAIEfIpXqum0vHuX3aJmumOiRTGEcPs8cB6B5cmKV7j8Pq +Private-MAC: c2c907c13abcd1e429a8a53c162fb27fa32bf88d diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa256_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa256_unix_encrypted.ppk new file mode 100644 index 0000000..cbd6d7d --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa256_unix_encrypted.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAJ3Mot6VLEk +kG5HscBLY3geI20FnwrgQi2wzAKiktPDhF39ZavVlOMzvkIGgQDxmo8WoQllY52e +d4e8+7KQ76M= +Private-Lines: 1 +Giq4fU1ffVMQzpRS7vPtl4eViHxNmagS8TmYqnvb4KsMip0XwtRkRd1tqV9UGS2F +Private-MAC: 9a55718d397e4bbbc0c00003bdcf5b8648da7f2c diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa256_windows.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa256_windows.ppk new file mode 100644 index 0000000..b571979 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa256_windows.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCDseBAbWBVv +O3A6gqqd2+Qm9uSTBPEFvvj/huI35w7iYhMmIjWAYwi9av867hF9GsRJaPGSkoWU +IcA6QBXarAM= +Private-Lines: 1 +AAAAIQCY9fEH+jqw1o/AtlCcSjksNFSuUILNENI6mcLywmdQLA== +Private-MAC: 9ed0ea63718ce130ee11733dec5f91abfc076d30 diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa256_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa256_windows_encrypted.ppk new file mode 100644 index 0000000..a57c147 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa256_windows_encrypted.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp256 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKIrHi0RIW1g +bd9+rJLTPJ5qAyXzNjEHidpxBzRnN22jDcnvBjqnAtfWPBjazs2KHSn/RP+Ktyej +Y1Q+mMikDDk= +Private-Lines: 1 +dYVv2mbM2lr/QhQ4Sro7ev9ztR0YAFD0ECI76rjyGRBTsbSH28KXEF4Vjq/eatWu +Private-MAC: 7034973c6f35c7da66e86c2bed7d8e8367bfd8ba diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa384_unix.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa384_unix.ppk new file mode 100644 index 0000000..7f705c8 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa384_unix.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBCTReLXxPo9 +3rpl1xlrrx7yYZgD0DzRieTvFO9C3a8Z8blHuZHNy2iljsIYeCPOtK9lcivx+faO +rp5FdE2VDroGPdDY2ZAuwF4/UxBiGNNEXWBhRFkupBcM6RbyjvCXaA== +Private-Lines: 2 +AAAAMH0T4NXp6wOqp8DWnEmXGlmAD+I5ytsSdlhznBdprZ2+wPZWcoa5XME85BUY +p3HhlA== +Private-MAC: df1b26fba219f717cf75002855565ce1b3c065b3 diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa384_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa384_unix_encrypted.ppk new file mode 100644 index 0000000..20df843 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa384_unix_encrypted.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBRylqfIuL/B +zq92j3g4UHd3TOlU2aG1U/2W76hrf1svwNPNe5d81xXA6j7Uh+AxOYfYrJRQr9Ko +tRvd8WcxbikMZs5O/dKG+xx8WmTcgvu+jVW4lCbZrZk4S7/hqIlVjw== +Private-Lines: 2 +mKo1enxDPaVSSEiOYuEL76/0NHTZLDCZuwy/KVXskXb2gwUu4cdbr7Vwj1IHnkBN +GIBLGOWIDva5Spqj0XdDLA== +Private-MAC: 4666bb8eeacc4a0b137edf9e7a32632298f83910 diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa384_windows.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa384_windows.ppk new file mode 100644 index 0000000..5c66ebf --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa384_windows.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPATBzcVXx8g ++unFg+8RAEvjiY/ZSb98vuIQ6MAtFCkNU2zgUHI9m/Jn9FitFd/F7KlIh4G5/wMB +2dGhks/FYSk+OxgAmhjif6lKLfD5veRCLhRnVbhTQm1fOmU0Sy+FSA== +Private-Lines: 2 +AAAAMQDHKqb0OEYb1K4KBaXYiXqkC+ybUGbleKdkx4V4VPfkDavMdGsOoFsyquuv +CRh3/b8= +Private-MAC: 8ef4699bc3c514b09b56ff672f30237da3d0956b diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa384_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa384_windows_encrypted.ppk new file mode 100644 index 0000000..3d26f17 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa384_windows_encrypted.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp384 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGxZq9s9JfIv +KluYcWj/f+OuiV0bJ9RLE8rT+8qF0syMvzZZVRqLmF+a9VxJ4uxxRVvmMQ1CXb5T +GtLVdBv1SrviKWMoaBw/sosSMr8v3eoQ04f4bs32fx8/dN4JQbCZEg== +Private-Lines: 2 +aTUgs7URruvHriTmaMe12Y4Ow5smlllupJ1BnErif5PwHd6MrXFCzYq0grADQKpS +0QaIGRrtXq4Hb7vvq+c6Mg== +Private-MAC: 79f4ff32f403112ce42a5206d0da4b9e53edf33a diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa521_unix.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa521_unix.ppk new file mode 100644 index 0000000..0f84e82 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa521_unix.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 +Encryption: none +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACuthNYwDJn +4I/b0fdhJdD3ykWe5gMBS91H9qWt0NHh4QTYxVyu3npbLveRVr9AWoF36UYIVF0Z +3WjFV4u4fOPapQFNlGDf1DNn8aipy/5iUKY02z+AxnWIIjToN0IPPizMw3LApwmy +0UaBSw7sZyfxNHD0Nu8wXsP8A6HG+Y01zSMAqg== +Private-Lines: 2 +AAAAQXaGiZwhRZG+dlWCB8h9+wc0Qer7IjpJ3kiRyYXLTku+FloU3F0MXJiIOnFV +31joL2SC2QEHHLEmvBXMT48R8AYy +Private-MAC: 187e49c327364d784c54b3221adf18ad544339c3 diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa521_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa521_unix_encrypted.ppk new file mode 100644 index 0000000..e9d350a --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa521_unix_encrypted.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 +Encryption: aes256-cbc +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAEUP4llO/qJ +cKrD5gZ6L+8+JzEU5RLICud1jgVh+QgtuuAZQUztdee3GEPmR+C9Mv5cAHg138OC +NcyRI1eukjFNDgAq3xeh0V6BwjZZur+v5zyTnXQASBwuI+iofZZqS0z8lsM48eIl +uTZuo3siTlv6uvFLs4ovN/cg67QyGIpsKusESQ== +Private-Lines: 2 +4OKuO5v+JHd96qUoo18Tb440u71nbpZNpXqSgBPr2VK5OAxVy+lqeccRtpAcUyXu +TU8TTrQGHTxGMNMMUxun9HDcV02oZ8It2MsywvtfTRM= +Private-MAC: e5aeb1290b219010b58cde9376db795b17dc1755 diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa521_windows.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa521_windows.ppk new file mode 100644 index 0000000..4527fdb --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa521_windows.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 +Encryption: none +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGIVlZVYKq7 +1fKe0Uxj73A5WPmvTuohTARpvnJ0VB5zIHFEtHRuI7Qkn6pRGv8rbKDRKygv1kZc +6rFZ2fgfhLxXcQHG1LwWM8x+XXoWMJM0C0kpCEErKd/tCZstQqg4v2JO0iG5+wmo +Z90Cru912t24fhFGfVeFIbLN/xqznIm0dZTN/Q== +Private-Lines: 2 +AAAAQQn76HAtrPDaRmvvO6XRFvuSRNt/onJPOFRF9pOFDZzBWbllnPf0vR5h7uOo +xn0UoPNYc1rQAd9s0a6LvEpMRUCJ +Private-MAC: d1deafe89f187d0f75d3d6eee2d292c5ee5f9dc1 diff --git a/files-jsch/src/test/resources/ppkv2_ecdsa521_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_ecdsa521_windows_encrypted.ppk new file mode 100644 index 0000000..676d587 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ecdsa521_windows_encrypted.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-2: ecdsa-sha2-nistp521 +Encryption: aes256-cbc +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACcIEokqqdX +r1cB4vJksHNPuIb4HhwaRqaCi22yZa1slGr5+KQhWc6TaRP2yEsXaskuwThVy5hN +jfONP/uB5IMj6QCxnR3bA/knuB2n7YRFlbIICLmCxr6QI5bz271MxCB+ccJO1L8s +SfEsS6IcxH7O3rYv7hDadwcY9t5VmsJ3t7A67w== +Private-Lines: 2 +plCduQfYJ2kW2Gbi2B6Rb0I0PXb+YgMTClhaOFJwpqV+z3GY9YK0VEwYj05cZya1 +HjGOtG69IWuPjrI523dcNYOeegI2BAYhFJLq05T4R/M= +Private-MAC: 3e73192522ebf55488bfefe8d5033294dc39a972 diff --git a/files-jsch/src/test/resources/ppkv2_ed25519_unix.ppk b/files-jsch/src/test/resources/ppkv2_ed25519_unix.ppk new file mode 100644 index 0000000..e3fa876 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ed25519_unix.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: none +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAINknjInRhIeQB0yCD/N+ilxpTNwD2OGA776pYHI8 +bOdZ +Private-Lines: 1 +AAAAIKg8xC9HSZbf6mY5WU6F7OWr9VGuDnJWLayXM3omacpf +Private-MAC: ce1310f32622d44cb5d90198d482772bdd6bab72 diff --git a/files-jsch/src/test/resources/ppkv2_ed25519_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_ed25519_unix_encrypted.ppk new file mode 100644 index 0000000..e511c69 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ed25519_unix_encrypted.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIFYLKsn61Pg/YYmumVe7oXik0o+Ca/P3vvN0Om3K +CHqo +Private-Lines: 1 +QGanXsZXxLK4SWcL9Y1JnGZxcbfBlZD4h2wi4YsyefXbhaTKCnX/+7SaRdXmFRB9 +Private-MAC: 30b7b90fe3c142891eebd05cbdb9c5e06c6ba448 diff --git a/files-jsch/src/test/resources/ppkv2_ed25519_windows.ppk b/files-jsch/src/test/resources/ppkv2_ed25519_windows.ppk new file mode 100644 index 0000000..72eeaf0 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ed25519_windows.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: none +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIHh90f1nUVIpoKQoYxtD66cTGgLZ33D3hCxVqS/N +5R3u +Private-Lines: 1 +AAAAIH3OoVCpAhXZns3AaYJE7WXYQVUsY8RB7lwdJ9FXFS1u +Private-MAC: 5942475d67b84bc1c89ad2fdb502dd8537835e7b diff --git a/files-jsch/src/test/resources/ppkv2_ed25519_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_ed25519_windows_encrypted.ppk new file mode 100644 index 0000000..579c149 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ed25519_windows_encrypted.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAILhbcrIlE43dp+IlosYszkS0c4zpwC60ec1MRwZu +xwvr +Private-Lines: 1 +ARj/EPcb2v9tt/miQAJGKAf3Iz8HkzMHl6Egc5M5VCu+mkRRnB1DIN6wNYtp7ncY +Private-MAC: 39813a46a634fd277b5a8bf379c804223df99027 diff --git a/files-jsch/src/test/resources/ppkv2_ed448_unix.ppk b/files-jsch/src/test/resources/ppkv2_ed448_unix.ppk new file mode 100644 index 0000000..cb99a2f --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ed448_unix.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ssh-ed448 +Encryption: none +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADnTDrr21+87QbcPF+2Ayh61ot/Jtf0c9yTy126ki8Um +/UUEW6uy87s/9gpS0OHyoH3GO3WgsUoe/AA= +Private-Lines: 2 +AAAAObILm/ALMj5npvYMwKLrb2gJu3ZK1pSO78zy6XPjSJ2SxwAFNcVi1ig9elS/ +T8ZoChnKOv3P0qarAA== +Private-MAC: 55827a80d4951757730ab061c617901d2c3dcb7b diff --git a/files-jsch/src/test/resources/ppkv2_ed448_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_ed448_unix_encrypted.ppk new file mode 100644 index 0000000..5313403 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ed448_unix_encrypted.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ssh-ed448 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADnwdqOa0pIeeorEzsEY7kskGeyZfhpkYZtbA/iR5Nyp +0cbPZnl9UFsfKQIYKJwPWwnim8BXTkEq6YA= +Private-Lines: 2 +/A3mDpDv6Qz0Ou4TR1tZEKsJDYbC5lIotFETja/Q0ZGpRTBjUWZ1xlVZAQwZhDqe +bIXTpngJcoMuU9ca+FlhAw== +Private-MAC: 8c73debd89d60515a535a88c0e05cbd828592cd4 diff --git a/files-jsch/src/test/resources/ppkv2_ed448_windows.ppk b/files-jsch/src/test/resources/ppkv2_ed448_windows.ppk new file mode 100644 index 0000000..dc32864 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ed448_windows.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ssh-ed448 +Encryption: none +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADk5uIOJgx7OfFkbkRV43yMP37dcnIVemLPjlaH/WRKj +LTwueFIgN5Mc4UYcIZtlbAaUhiiEvW7aYQA= +Private-Lines: 2 +AAAAOSPpYS2oZf4SDKYbuFSxf+CZzYA5LC7mVwEObSg/NcsadJM3l7chtEgC+dPI +OnzXuIwAaMzPSs+KAA== +Private-MAC: 14cf577bd1d0851fb5bd13c5a92695e0a04ccecb diff --git a/files-jsch/src/test/resources/ppkv2_ed448_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_ed448_windows_encrypted.ppk new file mode 100644 index 0000000..6b8ad50 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_ed448_windows_encrypted.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-2: ssh-ed448 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADkVNtCwBIBWO41k+QYmuMromfii6QbjVKRxjxbTYGPD +aw8u+gSCHAnqgaxi9qkc7HU0U01BJI5tJgA= +Private-Lines: 2 +eUusIDlLKMS0TWcSwgfEx+SSDzlYX7QNXkkCZeOv/oc2Tx6/Jtsv7CnwDrVybF3f +jbKnojgYqJ6OEV1uC0D8gg== +Private-MAC: 216027b1e8ec6ff47ee2ac9f69e6f743206f8076 diff --git a/files-jsch/src/test/resources/ppkv2_rsa_unix.ppk b/files-jsch/src/test/resources/ppkv2_rsa_unix.ppk new file mode 100644 index 0000000..3051221 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_rsa_unix.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-2: ssh-rsa +Encryption: none +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQDsUXXjApfYYCtRK2LrymKvcZpS4cf6agyO +j1/VW48g1GVtJM4g5kOjHWRqqsQZJCb8R9CvChCsJp4LPOS3tC36dSS52loeMoti +7Z3GtKORjPGKDO/byt7V68l02KZdaO6U91D73a7y9v0g+JsLtR4xkZMd1zXLK3Nn +T43rtLg116YBsLxAxbAC8eeC1xBF7MNzdLbmg8Gp9dA6iNnDlUhDsf2/FJ8a43Wb +U8inaNJh6eGd6NtL1fb9iDWho+JEhw7WbyoNrBAf4YlqO+3Y0oO1A4ezOspv3Vx8 +CkoZTrtyCzVCPEIV/3wSK40VQaMxt4ptbPwQK9mSUI2dG3kuzkxV +Private-Lines: 14 +AAABAQCKb84XhlVdtDir/Dae2pilHm/BTfLQh3+DpAoH+vMF0Gb6YS0/qKTG0Vka +A5+M+ti7wXZGlCbgMl8zRiDWXP7yUd2J4pgYSJPPCFP9K6UGhwKYVKvOnjGcL9x8 +Ts5muinJqngOk/cA7h+rSPfLC/b8IsOEH6artnCMfNYu1ldzchjmwt8u57xw0Yc1 +JqIqAnQD0ZFv50JsZzdHy719IgMFiqYnulAgK+mwGG6Fcbcz0Mi29dcqPkpDzFNz ++5Dm0oV+oipg4oSMHBpF4EaEBXPshSoPCMvid2IBYqZk/0P1VeHeTfD12Tz6J2ag +8bMxYh+BzBh9Lk23qLgbiy+zMOKhAAAAgQD3mVM5gnJvON4be5GoQHPsoZCLapFm +ykfGxbtexc/96T7k9iISHMzzFFss3+kf+wTiM+M/eWMCGMNfeDVqCLQKXvijhnGJ +zFsaEd2BAem7r0yBT35o0jcSNskHOFP9ZLmv1cx+QS3AMvW1d/Wgrdzg/n/N3oSy +6xG0lwYAP18CqwAAAIEA9FYmUBuUGP2fIev5INsIS6ffOiTiSGRjz2yRRc0SDVSL +PLknhdPrPDFJKvhOshkr+A3n2t/oqGI+ktbOuUxI7uhlPDL3D6Te/1ysObT0rXGR +9cV/SE8NKWe83J6wG21u0AZIgiOYKAgmUFSju93Pj22s8ECmmSTh0QwJV3If7P8A +AACBAPZa+3GZW7AwkHCPy2zjnCC49nnjVc/ZqodJiQ+95T4QQK8MzqlrQ+bTsoAS ++nN4WA9eOdvF6WQVrt5D/oLleA0I+c7iYd7JAOyiAVV75rHbBxpEUNgzP+VKymk4 +nR8N5IcbZJ/XXeUzQX7HaiRZansWPb+PvhbuYvKNsLD8pjr7 +Private-MAC: 456c364af11080a5032d3f01cf0adb5c425a13f6 diff --git a/files-jsch/src/test/resources/ppkv2_rsa_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_rsa_unix_encrypted.ppk new file mode 100644 index 0000000..319fdbe --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_rsa_unix_encrypted.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-2: ssh-rsa +Encryption: aes256-cbc +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCXX5GPy2zMByhViM5aW+lVH3oPVBVvsfXj +b7Afy2ldttHHkDMhoQKMNUScBTdyLwHrje6c0C99aYNkSl5TLXePWx4nIkNFrEyh +OfooY8CHPTuhCjfy+4A/psxeE5aoI5YGfffSr0uOVOUdhV3X3p1SmQsybzSiFz0T +HULrtirxoCbf5a1UGNafYoXPvtSS3/qGJnsnbxE/gL2mTEvOyLI7o3ffFIgbfOra +nbuow/HSboX/Uc9i2FsbJR4i6K5A6KB3NIL3gFJHxzjumJB4QXt7mUXzo0FWnOXL +ORFrzYd1Rf26AVcUc24wwStVAOtXO3EIr/AylwgsqWqoStOzoRFB +Private-Lines: 14 +8ObwsQzQGN/DN+sRAL8BwisB6emytEjK0EMer4XyScCaZWg0rKrGwM8HnLaenAVk +kyNcE6/Pe7/jPLJjqG2qpDo7eFSWML1JZdrByEbzsJL0lSKEIgJQEWw3ozl7eLD0 +Mmhm5WC3qivygw8OGXM6IZyT8WnjsUZGFUlqt7o2cZ7hPlrVFoIumn4mWlFA96EY +wjyWEsQiYPVtwuKIudwXrLWD/BcFOpKajNJ2xiZHDe15zPvJPTXCUiLP0/1OqEFC +5MhUWbQ26igKx2rMjtx0fN2I+U9eVBIFuNnqAh/nQ0gMet55H5fBkSwWUVSA8Wt1 +SQBTlbtWx828EAeycBdLnUxJzhNWmBMz4ljda8cBecKTKHzAhRkxA7KgBiml3omE +jUR4ZpxFoaL/y3tdMI4EyNnOOgpaHARamAqIrMB5JUEXNuYw+aOOkQlXmi+2JwuI +2nggRicIV1+Tlo2Zi9lb723J43xlvcdSpULo8acFw60ybn9/Z+lkQjHRN1W/AR0d +xzB/ytvIAFO9pHiRJrBkJ8K7GBGOR5XIrE1mn64zh+lgxhGdPyw73f6uBeeDKl+N +QtPI9xAtX+2ENsJCXzbdVF6GSYCNQ/PomZz04LiyzvGPLe2xyt5EgGw5eR7ii7cA +YdJsFgOSyqZm12NnuDFGlJdIHiB+nK5g0ZG3Q5KvI9xhJvYFaeJfGplP8ky3B9SI +BP1xRUAzbmdxs81LUJMQtNZGZFC491iId4LAGy2SPGkiJ9OsxZ7UM3mtX8LBd+Ju +R2OQZwMLJgGXkAd3bEphQwTFUnIAYHql6KTVbzxoZU+rjFrgFaphWCoD8HqOYx0W +ELK8ereV7vQVFngbQIiazZaD/RyYbYkLJdBO+PE9Sv9LMqr4H8tiTrRCDg2EwYk1 +Private-MAC: e00e7d3fc277d5fe759477456f348ae71c78fbfe diff --git a/files-jsch/src/test/resources/ppkv2_rsa_windows.ppk b/files-jsch/src/test/resources/ppkv2_rsa_windows.ppk new file mode 100644 index 0000000..1b534e7 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_rsa_windows.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-2: ssh-rsa +Encryption: none +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCdPnSRJDt/YuU5Oj1gq6JKYsJacWbgsdU+ +vpzAT7IiIVOoGB7oPi8EGcZSt1kIJBc9Lf70uRYCbCmq3v3EOLGpSdhV16oKQygS +gSZawq2YwYHzHnonp6akLfQQXwE/nnubX3S6iYAaJSrJ50APhKfQXYsz4CH2alrz +Yb9Y2TDl4hKfxYABgMza3O02BFooK2tbxy2+0fbOKB/qjamGVE/CoMcv4ChB9FDG +dUr0AVy7BYtfELAZ1dPrUh7lMCXa6JRRhFtdRWQGKmfM1nH/KpuzB+yZsl8kfHf4 +7qiX/mwTQM87JMxm7HuV8pHHr7DNFs9g30CreF+hbY1Cx+IKh+eR +Private-Lines: 14 +AAABAQCG7sc8nWjpAUZOe2mcAOx9BI5e6h0sB65D73G3nSvxGcQd5MTw6huSW0PS +Sz99OusuNsAn5IO8hHClDkGZFkVuTc30q+JgeAx1BJqTG6e4A6WtqKOOT9Ex5bUg +L0Z0/1x2kc0rHT7uMKKtK2HPbzhKF1uSomzCdbWiUGjQp1/Mg38Hvy1KETfjAb4l +4skIgqvCp/iIqd/2o+qonID3VYGRohHuf1BfGcFuwAXjwy1UQztPopHn6eclSLKd +J/YpZHkw5t4UNN6NZ/vpTlU0H4DJPmXFsW9lPT6qTRjrT91vumumwlV5vaEFOZdZ +a/b4KSh0QRs8Ae/X+g+nIRfT5t75AAAAgQDdovwknLlpl70yMktt2fvFZ1z/Tx22 +lc2Gs7wgrRlj76R7Y0RhrpRVCt/Y+Z6trocw6maHrtYzpF5IUFSteSoYvKqIT5kq +HDCsxnD6wlH9jmKoVovGRLpDNzSlNtLlUVdmkyTQmbJGX/YjWb4/B93e3kzkFe5e +PqGuI3YjR8Y0NwAAAIEAtZ+mZsJUuJOImvE37AbBljd6LlxRyVXqaTuXx5hetJ7U +VK6tbLmMUXSL5F0f2Ftg6+i093Qc2HsaE3+Dt+LkkmiPEM+o9FIK8xwgey/S8RQk +uGdaP84Q+cqIC0MMxOUuk3MWK1yKRDOZFYgjkfZPvhywY7d7scCPgzzZjoW6bncA +AACAVZHXu+Xc6VTaLjAwlLj4aV6vgcFBlJVP1GSBnMdnRqHrqYWMpmodsh6xjT+K +DkX0wkheNiqcxFs4FCIsGePGDdo4kofnYo6aH1a7jjAcZMQWgS9DxRgJQstYvGfr +qnaDGse0dmD/w6tyqEmeBP6AGZJBWx9SvzJ441NtAv3+r2c= +Private-MAC: 5c60a7b96b51ae767b879915d64328bfbe2867a6 diff --git a/files-jsch/src/test/resources/ppkv2_rsa_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv2_rsa_windows_encrypted.ppk new file mode 100644 index 0000000..7f86ebf --- /dev/null +++ b/files-jsch/src/test/resources/ppkv2_rsa_windows_encrypted.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-2: ssh-rsa +Encryption: aes256-cbc +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCPJDCMNOM7Huv+NNeP626Slq54aPceMLEa +oqlfvLJiQf11b8jXcKfFgLlp/f0Ltk10p+5hAGlzBw/nlYRU85i7Eq75kSb6LPcQ +3SNY1/NkrNcZcFf4p/AKeC0s1qgbypvjWy7Zk8AUooPFrhIo/kKaPMouq4hcyENz +2VKgpdJ21SzTxjDASj7zd9DZP8uc1Lsg3ftbr8Mi+MFFQRnFPLv/w2zIoLo6xwO0 +UplUIyAGAsRPDdRZQ50h9b5BFyFLnXUlwI/Jrpgs2J31Vg8J2j2Y3BFDgn/Mf/P9 +Rsvdo4fp/y+W195198VQ2gsH2+Az+3Id6ySsGhztBP5Pl2Z2K5Pt +Private-Lines: 14 +RU1YnbiIpjM2kSpm91PPq6ZEsvAJiM1ZBle98bdLIOI1KhrXzcP5GlDz0khoUHMF +8uFn3zS9pAV3t6UC38KYBH5/sRni1wDf6nfnJ12VrOxeSE4q+5lC8e5MipOyOx1b +RfwDb6o7tzUl8RHm+xmzihCHilUQB/05sDHy4JwMcN6QgA9hm23drSiCO8itd9tw +fhidYahW0JkGrQf5rNkgYgCssMfAqcecurng+RWTU/J8M0BMbp8AYEUVtPvOK95J +0kWz/lFG1d6oC1d5bcRL0LL4Oz6PWaVNf7+cjBRkJGfqJYtNi0zEN4R5MW8QTKye +C29Dqy2/+JLjaa2xVYbdFqwIgePENEJIu0/g+k6tAhMN0G8kLaApsclKEtYCdd83 +wlNdpFvPr31Xna1y5mfpXDgfEYYUf+t3vcD0+IluYMRzkDwv/BN6QGB1q00NmWZ7 +iWZv0NZzf2pVo7hFJC83pKXfnkhqaLlLywfyu8RYEmA3LwdFe45Dt0oS7xP6DnAq +bzgC3L+tMGoHrrKUhefqV8ma4m3rnBzobS8OCzwIRK8Jio1BgQuYX4EU4eu2DEmU +eVJENZDT03uNzvh6G5ews809qz3T04Gyj7gQLgh560BwZ+Q8CVm9LVyynht+DMgH +1CEGi7I8pW8aPZDcgY8EyS02NAf+kHUtHF9Y3nO2T0VyiBrHwyF13B6kuQATuWPu +SRJpQlcMytjdwAx/iPzQBg2Gdbc1hmPVzFi9N4LWpTLncSVdwMWBAEKF+qZBkXx+ +BjBal45UoAD8JiNa/jfRVU/BHclLEr0DU0gGeUcP+u1WGv1srHjrrDbpujj7qQfu +kIyq2p6IPkaCjz+6wHoZzlyY1fHGXS8c9qpMw7f/VohWDUkLP26hCErF+eiAtq1K +Private-MAC: f5e53c305287fa9b6b02422ee5de0dfee3ad1a5c diff --git a/files-jsch/src/test/resources/ppkv3_dsa_unix.ppk b/files-jsch/src/test/resources/ppkv3_dsa_unix.ppk new file mode 100644 index 0000000..9ac8834 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_dsa_unix.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-3: ssh-dss +Encryption: none +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAM6Xy9zencF2KDhq1r0gTXBdA4AL5Rtg5GYd4tx6qlSx +k3ZX634ude5bV8Io2UrsgvIDQHSfj+1J4sDpWnhMtT12KucddwAzLhb+/5gYEr1U +CZtPn1WlsvOXrJ9pmAt/MB1KppyG38UX3QK6Z04oWspqHWJBfO0oquUUGx5Ua9JS +neJCrzk++Sd1GkhjZElR51Hm7MKV9KZejn/tUZQK2FRjpfP50GsIlAs1v31OkY1g +lhgs90/6v7PyV1ljp27Nw1aNL+CxOjA/wYnFo+jE5+EsW81oT8C06n4yrcFySWFR +96wOfb2fAuCl4K9Ma4jN+7J0RLMY1++KzCsAtppi4KMAAAAVAMbFE/Q0VL6xGj97 +5l/7AqEVxWH7AAABAQCbU609B2d7d5EipWyjlETAs64vaKBXOAREcQr8HQfAkWmS +UBr//brBdNWeA4TVla4hyKL1CBiP1Hi6pDQ12lCB+J30ikRqdi45y2MvOR8OfEDy +3sn94sLwswARnMwdfHWrPepfyVdqOnIlhsYWzLpc5EhfOThvL8YZ78tPTCqyui8C +Oi3TznOFfW/do0ydEEmfQwJTlHi6dpFiokh+mpQVOlstehmgKzrolLEjccS/vnxS +qsdWDwKErbtg0bYhhV3rztiMcMozoBblgCSDiVmvGBAO8GnIoyX5/J+UfuwWYQKY +rtuZRfB4hRyRU4zKopPfQrZkc1Jummk959KgfeTIAAABAQCayyiO1dYHPHF6g21s +wekxrukaXix6sab4m+OLm0bNRa+7nI3A09wG+otKajznna/mdjcIaWuhdG5S4+l+ +aPypIUWTdM0krUu0A+9Pu9YpIgVgEFKVQX9PBcP5+WHanMaeYmLOCEP1WqFMJ5Hi +RCJmssOKwZJygH/KEMIGaDGhYnSmN6R+41j8sDAoY9/00WlSTKqpmVGTe7bJGWom +nhT08RsATIyvM2i/J13qrh5UfGprj6GsaE9q+Zjg47oMdZIRSgyp4jw9coQoR68T +emOmkODmXaoixfrGT1FC9LbqOmN/iDwv0QvLUen3RTbbB2lE3LwObBXJRpd44Rje +Mo7c +Private-Lines: 1 +AAAAFE1NvhJa+HevZ3y1nFhhtzEh3Yi3 +Private-MAC: de384c789bdafb1acfb4ad240ede6a24ba92b4c45d22906b0ae348f5caeb4302 diff --git a/files-jsch/src/test/resources/ppkv3_dsa_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_dsa_unix_encrypted.ppk new file mode 100644 index 0000000..0630252 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_dsa_unix_encrypted.ppk @@ -0,0 +1,30 @@ +PuTTY-User-Key-File-3: ssh-dss +Encryption: aes256-cbc +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBALvd0uyCDZFuKcpphdcnMEYVXnoy16rW5XIFhlxmvVSU +DDz3DAGyXISyhyQ43BhI8R0OeOr6VpivujFu0srXrcxcgsAy6yv/6uAvaeLfekc/ +JErS7OqVqACFmt5ISkEBcvWKnUseFRwoy2KCeiEo2GyKwyV00caLnYb1Th2CUNRF +ZcH5p5XLj3gwFbPf7EdkKV0PHWV4E5ptFVVT/aRijCJRQn//FlSNlG6iOfUF4IRU +pz05qivVGaHLWNOw1fSXK1Pc53qQqCAaEict3arhQCwSEKl1xktSNBDVmeapLPj7 +LNcZIGvAA52Tpg3qR1rp/8a+bdtteNkbRg4Gfx2uBuMAAAAVAKFdLforDVztYQzh +7pFjN8qAjogVAAABACTsvZOw0aPNwdQFiaQik1F08HQrjlp298vgbjIZFrgABE4x +euWMydEcxAKKEhy7Y9Go4l9shUddkHgk3Y+OLvAsifowqfc8Fpsqxh1WKLxnw9NO +5rjenO+kNeiwhX/oWa1VBMOa/O+iGTvCy1XcW3u0E1LqGVAx3DdkP6EHV91ma3d1 +LWih/RR8ClERNaovnETash8NQCHAmKejIObneLtNRqyaiNohYK0t5EAtP4JutVwr +snj3ZoSKrCZct2wVrMUkkqCsKcKygCsr29uk1xpVYBkv9zUn6V199ZB/uXanRLcL +/vq0UYWGV9nXU0shVanMwHNQaMk8oWnd6hTPB6EAAAEADwMN1uDvQqkyKel83xNG +4Er3LsSCx6iARNWpaV1ZLNrtf2h6MnYCKdSdyVgRjIL5coBQB5yZb/+CXm9QgysH ++K5FHXgnh4hjz8W8/Magj+qm3/SKDTmbLAsNvvvenQvayTWUGNQlfQET/tHvGRy7 +gz4r95phWGmxS8Zu1NkT0RHl6xxcSqWmvvjG/m2MuNo+5lfKFa2+j+Is59HwN3ZK +5umpPlJl4uM5SkyFD+iUbptOVCXdVV4xRhIzYy4wllADQQHkbNQtNGJT7JEHuot9 +2J3FWOCEQVw4F06j8NsdghKWIwGSlcHLHAbA8zZDq11R6vnhOFDwmfekxCwLXaBl +GQ== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: e10608eedddc7992eff0f91aeac654b4 +Private-Lines: 1 +0+i6uWp69WUFuFgBYYmfzrEw7wjMgKINMsfXSRh+JL8= +Private-MAC: f962cf010508ca8b62f2562881179e2d70afe18bbea3d48939d98b695710cb93 diff --git a/files-jsch/src/test/resources/ppkv3_dsa_windows.ppk b/files-jsch/src/test/resources/ppkv3_dsa_windows.ppk new file mode 100644 index 0000000..a2f9213 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_dsa_windows.ppk @@ -0,0 +1,25 @@ +PuTTY-User-Key-File-3: ssh-dss +Encryption: none +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAOGfXe0SXEr7JIMEZQo6rkTc6CfPIWsgqDYem72p84pn +6KykGD6RonyqDEfDoS0XpwIY2sCZLmwauEhTQtBZpMyix4sD49w+SjaHUP1aoAT7 +iPFvsXXGNw9/jgK5a67NzWxmcl5XMQG/VDjtVLWzNfl9OXHMSNmGUKQmlPZKXRrm +06SYVZMDsRbgSJgi6PNOatuVvfR+b2hK+ppMpQNKnHXe/vpgcblZ1ViPUTH4vG+0 +4PkidIbwHIIl5Yv69lUYOcycxR3yzd8ZC3a7qzVkxet+tGWCD4/PzfWEP+/G2TWX +bRuxV6YEaq9POmI0JuZa7CAZ8iA/OFtYua3TB4cUsSsAAAAVAJzJoUVy/fnPi6Uu +PLR8cCyhqB3HAAABAQDH61jRVXoAqKJRHgIWhBsrQMmAvOqVHyrALSlAchNT5cso +4e7Rd9ajJFg+hVZGKu5PoQdhp/8ERk8mnABKue6r9qO/6qT8NvCRuI0Wb8Sxgw5q +/4hzo4BVdx4UhLqTRl9rF7B5wLSjV75HIsqW61ZcbT9+VdYpRaNg8/Te1jfqwgrT +ndaD7jhkhU2P0Z7vHSRlzaeSksF6b+SRFu+bnQ6neBxZZTuMrSfKOSpp2ltLUvWr +Q48GJ0I/uE1HbQehz9Tn0Q3VA2BjpfKgj8bLE5YFokveuqjFULF7EiL4CmAt6qhi +QOl/f6zKR4gaLUjkVuvU/uyTr/da7/FfOJouETpFAAABAERldAO0JWHLr1nClDX+ +ZPN8XbbpjSaWO+fFLQO0A7QE/2gKJ8lt5qi39GOOhmUO10johHaOupqRqe4iCRnB +xMJeLxXmS2KbcwDke+IBRaid81ccocpcPOrpov582DjJuwZ0394Akyrc7uMonjBy ++JJaPwpXHQS4SXRe7NybuEE4S+DsV6DNx9WG5n5PFh4YELbzdP2oJZ3hyZ2MiZgo +02ShziEDPI1vQ4BBAsknp6Ubzt40Gt1yjyNMbGpv8IIRZzu+H8CIRnzbCYKT8mRK +e0HvTG48c6la0/wvvG62zfmbFYgkw0tQxgekpf3ojNpRJZ1vcQPgtvQ5IjkCYSF/ +ss4= +Private-Lines: 1 +AAAAFB8185dI+D57PqqRGfFcBGxjParZ +Private-MAC: bdd028dd46f482f8aad5a4ba4a40db927350d41f129f6fb80c3907cb51a22d20 diff --git a/files-jsch/src/test/resources/ppkv3_dsa_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_dsa_windows_encrypted.ppk new file mode 100644 index 0000000..088f99c --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_dsa_windows_encrypted.ppk @@ -0,0 +1,30 @@ +PuTTY-User-Key-File-3: ssh-dss +Encryption: aes256-cbc +Comment: test +Public-Lines: 18 +AAAAB3NzaC1kc3MAAAEBAIYJqKE0jtG+HpbHurIu+kVIuQLrMrgXhL7woINHOHGf +b/0O85u+3DsSy+s7f/M9bWUIqRtt2+JV2J/V28mDgF7IEeiP3OjSYspmoxnCy8d6 +g313NcYYoCqG9f/yWrzSXQtK3hD8+KNqm68GQx2L6lceyyHn3CgUFTwHw0MNntOf +Ak94PCoM5wOZCfqx9sZfTVk+uQb++aG8xcAqPoPQdTgaqrFa9lrp2Ul8RhM/eDpC +mByg8I5tIsbk17IVzS0gLp9slfInhwr2UjX2wIOlVOnNmS8UHLAwhDtdtHcWYdFb +EMpO9hFMJCz39JqnJ7g8qjct/bfMxAEqhwlGpgbYrz0AAAAVAL66GQDORVX2on1i +c6drzCN/9f4ZAAABAA8eU3kzUacUj50ggFzpTLuBBztILvjIpNGnXy7SUG5EWCZX +LvBPTjJf9idxL/0P42AlJqKpaZSsdDLeaPbyUsQ0B9R+eTVDKQd5po5kBmWcxqMJ +h0f1XU1CYFZgN2cvzmtyx9euekEka/j1mqGLuXHRpX0vak/wsy3T8mEPlqLr09c6 +hPUDG5U8NXEy3aWS5gRRj7wPDLAi1sFGo4ReUfjn6LJglbCi71tSirnV0dJJvVGp +XA17RzetDgZmIjdDur6XdGz+6B07puj/OUTTCFSFzJC0wNKGS/OjY9+59ziFj+M7 +LCA7HUAlYeL4n83EsZp4fHI09JyuLJppPo/4xxAAAAEAU2ThrVHZ3fT8JuXT4dgJ +VtgoBTQVf1vbzKA/o5yrOCKj+pPLFv+mXSJxLWvdSUeHkaRZF76hCwSpy6K+Wt9s +RSuL6qTPog8TT7Yhk0YgfMd/wXwYlhhoH6HqRIIQRHd1bIOn+n1DUWQHS2w6Ksd6 +jnVDd5cte+sBRNFeNjRJPUg2dzFqYQUgf5bfDu6/mhISduez7BctGb0cWtVPyjzQ +ivJRUVnLiUJJE3V0saEC2FYOalknyW2x99tolUn4YwOLjUuQm1oKZ+BEbXlgGe4p +Ke/JxB3nYHT78wP7ArnhImPGK8b0+/JD4nAA15GEkFLRV/u9qyZJe/iwbpqKz2vB +/Q== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: b2f8dee868310abc40ef9d96abcd67cf +Private-Lines: 1 +HUZSXpBSW1DZJtQ9dCmjGPKRfqwKXlOFleqL2zzeK7Q= +Private-MAC: a7217ec852a154487b30da65849d2f25a78d822881b739471164df5fa24c08ef diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa256_unix.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa256_unix.ppk new file mode 100644 index 0000000..406de6b --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa256_unix.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp256 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLoiF7se+l2K +wZh9oxhfTASh5diPDNvYbVgS98Q4yPE9uExiDDMeX2Nu9XLtnevuTWrmcAF6u6/o +zI3BIkeHiBQ= +Private-Lines: 1 +AAAAIE+1J+zx/1g2rtov3b16NoLXn/cW+jdeTQrXgllPzlC/ +Private-MAC: 9549460921fa6dc12d723f3985618f1c8fb719fca05f388a3405f4c3eee7f7f8 diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa256_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa256_unix_encrypted.ppk new file mode 100644 index 0000000..5cf8a89 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa256_unix_encrypted.ppk @@ -0,0 +1,15 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp256 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBGrSiZUMuXa3 +aTNDF/H+rTZb4wUBapMIf9QXzfoJI6hOYPcCtHGH8KChdeZ2U9W3ohzX/WZIX8IQ +9fpxcGyvpbc= +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 712b0ba1fdfd732b3dbdf061acadeb51 +Private-Lines: 1 ++RPUh/TDOJB+zgkGpLmCpmoaiQpD69WlNiES+pPbUQ1SF+fxZMhvme6k57zH6iem +Private-MAC: 58dd68fa08d7fdb217db1a69cc970320bd3fa45706b8a5b2a1feaa03efb25cb9 diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa256_windows.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa256_windows.ppk new file mode 100644 index 0000000..2c2b617 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa256_windows.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp256 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDMTBUib+twu +te16i5Rs2zKlVDMam5/RMWuFcXs2dvIs0kLP5N06diHYHBqvMPT9N336KMChz0/O +4S/dXR1pwHE= +Private-Lines: 1 +AAAAICNn2sFEDQ05pZvx8Wjcd+m/fHnlumgo7Rr7nE7XZd/0 +Private-MAC: a49e1637cc50016094abd341f817036d512f54f6a024947725e3ce9a2eacf67a diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa256_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa256_windows_encrypted.ppk new file mode 100644 index 0000000..3d059c6 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa256_windows_encrypted.ppk @@ -0,0 +1,15 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp256 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMQNLejYwJxw +VlAGIVi55Lb0zfkz18CEAwqDtrr6xs2sz+vqRU1xadOgEzHeWovBqCXdC9wVja20 +2PyupH5QPsg= +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 43ab4dfef809a010f8b46898613e7791 +Private-Lines: 1 +kChNCwDStowc8bfgLq8ZvT5PWqIe6tDfu4Cs6gwPAv5V9LOYJZhD0VzCLE4NsJg6 +Private-MAC: d1bca120202371a62af6c718c51d15cef782baa2d5bf87beb99585eef66569d3 diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa384_unix.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa384_unix.ppk new file mode 100644 index 0000000..976bfdb --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa384_unix.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp384 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBnR7wyGL+IO +T7A7vKr4Do1OGlsMhBFN28GuKUohpkd5rvvZfe/9UNbdobFa38KmLWpxzAlL9Go3 +AK6+7OpWz6HE8JHdrJGyRYoS87XVv4oIIzmp8B8GThUYiDt8Qh8FmA== +Private-Lines: 2 +AAAAMQDFnuMXVDcejvYI0FcivZkI2cZ750s1tLgc9U/zQUFIYyybb8oHM4UsPeko +qZKs9xM= +Private-MAC: 3c36b58ea113af96aac49762d118793c374fcb399f293a3a79d9241e37bb4ab8 diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa384_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa384_unix_encrypted.ppk new file mode 100644 index 0000000..0adb9e7 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa384_unix_encrypted.ppk @@ -0,0 +1,16 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp384 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBGdfKrIhLUFt +iMfBOFdF1LulPAF3OKNqlahGbA0bf/LXPf7tmbJUs+GvozsqYAbsMhed1vmwfBwp +aQdIG97JRSK4MK4yIrCjDjGwqmakX/hg/WUQZPi17463t9zSzFZ8xA== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 72bcd381a51296c255fb63a40cc2113e +Private-Lines: 2 +ROTdHYRD0zldAv4hvurg1k/BV7QnFgj7IdvsC5OA/kDSJmiXs0yJqyYKzdydS1xW +DrZV8KwWZO4GmvXUH0byDg== +Private-MAC: 4139eedb961ba84cb1375927930738cbe9ecf7feff471ae44a567b4264b5bf5c diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa384_windows.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa384_windows.ppk new file mode 100644 index 0000000..aaf79d0 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa384_windows.ppk @@ -0,0 +1,11 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp384 +Encryption: none +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNShRribjCP+ +pkCkAMwdj2LjxNrZjIKHtylcTdohEgfHJTXzub4tbq6odNS3izPwgMEfX6M6Vl5N +BIstk3c34+kDuJ/a+Cyn7lZ9HZPdDuaKE66PNp3zGK9linfzn3iFBA== +Private-Lines: 2 +AAAAMEWKgeXFZVyRT28hwsvRRB7H/ZhnII8d9uP0uhPPU4742NGWdsDIYtlEAW+N +54PC1A== +Private-MAC: cdca5f9ec10f19900830e393483c95a2f96056545ce560334f37e75c35169ccd diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa384_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa384_windows_encrypted.ppk new file mode 100644 index 0000000..b0a7c05 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa384_windows_encrypted.ppk @@ -0,0 +1,16 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp384 +Encryption: aes256-cbc +Comment: test +Public-Lines: 3 +AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBKGDqZyyEHaK +kg0ijDH9nRu86AiXmUNJcKWC05ksthEMheDRMyZtyqzIIwQI9Goum+Tktu3LeQS4 +fBh9aNHwMP2xN5uvj/uUYxN1dmQmLQ4NsQhpz+DY5TtxHJ/tUKvNpA== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 8de0354d6f5f58738c3756f7691e9b0d +Private-Lines: 2 +JuLWiPoGeL31caJQj5mk+sUE8BK4JsOqEZt24rIjNMcFU/zVrVD/GxnACvjwRAH7 +mlL0AuHbi/5L0pFOLaz4/w== +Private-MAC: 477fcc9f7ae260b3ba4715476a56c04871808e9e5353802dfa7b5caa3914c424 diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa521_unix.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa521_unix.ppk new file mode 100644 index 0000000..fb41827 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa521_unix.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp521 +Encryption: none +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBADwW+/zw0OO +BYVS2jEOym6OctYKqb1/uSTegImb7XOcklV6HZSnkGDrp89gzXL6AgoFWMQkySgR +93l1j22kCwxGWQFQBLalq5ekPGpqBiE31QNot0QKPat2f4O8bw0DO5sUNPmH7ImK +myxAjHGiCPhi5egoIgrgzHoGnx1ynMr32Pntkg== +Private-Lines: 2 +AAAAQgDp0Jv8HmHaLVbfLM9oSHMOjGKJ3/uZY9gC8IDuBovmnapY10H8f1Uo9GD9 +Fpo7ZBTXNqAaHAh+KWvFYmUq5TVLWg== +Private-MAC: de2f0fc2577ac8c553ab52b838f127f3426c8ca9de22cfcbd11cbf5dfe8ff6f6 diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa521_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa521_unix_encrypted.ppk new file mode 100644 index 0000000..0ff4466 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa521_unix_encrypted.ppk @@ -0,0 +1,17 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp521 +Encryption: aes256-cbc +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGCln1Y+fp/ +uQTIVhjoNHuuRGVSzHc8shdMTGUQz0q+TpDSEf+SViws/gS0+V73QggV5TcUqOIO +ra4uD8mGSieuUwHea4ieXw+cbcts9lpYdjWkFmgSGwYfIVyosFZ+aE07We7fNRqS +UpH5gyBpdSpFKAiBTwtv73FOIwz88I82E4HVQw== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 2eb44562d24a9caf29194f4b74ad0af9 +Private-Lines: 2 +yJ0r68xbDPwIcsHmNsDMTQaWCPbEPF043Sm1oJJ48fVS3/Mid6iBTMffqbM4QdHs +uVTyA5l4/Rh9GKgb1NZr+NMNthCmbDF34gEAbgc332k= +Private-MAC: 3234726c3bd4ced9ad412585c8fd287fdf011c655e0d51e441be33180e571bc3 diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa521_windows.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa521_windows.ppk new file mode 100644 index 0000000..e688ff3 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa521_windows.ppk @@ -0,0 +1,12 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp521 +Encryption: none +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAA37JVxrRSM +TbtG8SUUuPzzwjsrumeJD+100IbXoAcXK0mPbGei04TdyMTpfy1SDWL3YND5CSzc +uHMOjApNKrxJ6wB8A5LE178vFKb7kvYo5SJq+YAbv7utQqlMH4sdKdywGwvdSTSZ +XCu+mzx9ebdoyRsmPS7vBrJYKcQPHiBDa9iVMA== +Private-Lines: 2 +AAAAQgFaFuLM1IvKBh6hChBTqbN2px8shq+rd1YV59GcnV6zVlaEgbZ9Lpdz3SZc +nKVT9/BjJ8Q0IRoko/jl5+fGsVp5Eg== +Private-MAC: 517745eb8ed80dad2395e80999921c3abcb4690fb01b2bb73e4e232f177fdaed diff --git a/files-jsch/src/test/resources/ppkv3_ecdsa521_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_ecdsa521_windows_encrypted.ppk new file mode 100644 index 0000000..66bfc87 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ecdsa521_windows_encrypted.ppk @@ -0,0 +1,17 @@ +PuTTY-User-Key-File-3: ecdsa-sha2-nistp521 +Encryption: aes256-cbc +Comment: test +Public-Lines: 4 +AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAKniAtk8UO +BIVa/348YbbZAyiLgJKpbH+2rEB0tHG7YYf+fAAGAZJsE7K7bl68XzunBT1Hguyt +UpcjzNgOS2hx2AAVJT1ObdU0IK8+B0DTXrntG8BMKpDjpHZJr/RQLEDljHt6kc35 +5TLoKZygF7vUujNjceWFjSmBlFeCbr5Fl4KoXw== +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 9c4e53a5c080d688ac149f396ba7872e +Private-Lines: 2 +mpMJCAaqeTZ13LGQo68IepcXDAd0iq9nYG2pp9V+6dOVtXMHZVDVFsCYhD/vCeaj +lb247g8fSDyxQGvwk/CwVtq6uZXO/d+0W5K3ZgFOnVI= +Private-MAC: ec2a55039d54f981a8dc3126e820eac4c358800abdae76d00b91dd7226ec3374 diff --git a/files-jsch/src/test/resources/ppkv3_ed25519_unix.ppk b/files-jsch/src/test/resources/ppkv3_ed25519_unix.ppk new file mode 100644 index 0000000..8206d31 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ed25519_unix.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-3: ssh-ed25519 +Encryption: none +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIFrJ3kAPWlhD8uR/Mo1dqhqtJIYACuH6vIh7/oP+ +r8UY +Private-Lines: 1 +AAAAIMTyr4DIkFlwt9d07Iw2GX2KJ9F90y/GciEu20RwqF0o +Private-MAC: 37c84944792d7dc96a40c3d92e6a1d3d32a5a238568503d48580bc032a23a678 diff --git a/files-jsch/src/test/resources/ppkv3_ed25519_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_ed25519_unix_encrypted.ppk new file mode 100644 index 0000000..e72cd5d --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ed25519_unix_encrypted.ppk @@ -0,0 +1,14 @@ +PuTTY-User-Key-File-3: ssh-ed25519 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAINnWA1NU5+6rlguJ4vDwB7Ro3wTO+ntaSPfmWIXj +p384 +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: aa1e739cbaa9d86e771fdf499ad93a59 +Private-Lines: 1 +oFIxq7q4Xb3LfYANl6QhPbx/3poc5/tvVG5Fj0BHer9iHh5GrofY3D2P7JcS/j6P +Private-MAC: c819ba8f6fc792f60400ad71bf577cc9baba13ffa696c610dca162564a405f6b diff --git a/files-jsch/src/test/resources/ppkv3_ed25519_windows.ppk b/files-jsch/src/test/resources/ppkv3_ed25519_windows.ppk new file mode 100644 index 0000000..a61f1be --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ed25519_windows.ppk @@ -0,0 +1,9 @@ +PuTTY-User-Key-File-3: ssh-ed25519 +Encryption: none +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIGGj5a3dN4+iyqsAXenA/CzejFmBNyZinnYHhXXc +jbBV +Private-Lines: 1 +AAAAIDWvlS89N/MQ+7GMxk5N09vo7X6W8szaJ1mu6C4s9WsE +Private-MAC: 4eb0763c03faad6289f10d2dfdfeaed6b024b959cad06e1e042000b97e176745 diff --git a/files-jsch/src/test/resources/ppkv3_ed25519_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_ed25519_windows_encrypted.ppk new file mode 100644 index 0000000..99b825c --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ed25519_windows_encrypted.ppk @@ -0,0 +1,14 @@ +PuTTY-User-Key-File-3: ssh-ed25519 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIEy7J6rEkUafMJmtqE8SpWZg/uYx6EcME5FH3E3H +B3rR +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: b26d22df894298dac8ce32d3189e35d8 +Private-Lines: 1 +mDG3fado6OIW/+xkwWRYgi2/bU29GW1KQvM0otOqWKcJPpoySjtPuuja43FNyh1Z +Private-MAC: f61593c72cbad92ea1443a8c3b844b8fe52f4feb43f0c03e6f73b84401dbd2a6 diff --git a/files-jsch/src/test/resources/ppkv3_ed448_unix.ppk b/files-jsch/src/test/resources/ppkv3_ed448_unix.ppk new file mode 100644 index 0000000..4130052 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ed448_unix.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-3: ssh-ed448 +Encryption: none +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADkhFs87N5abAoXAcsLiYTDOCujVJngP6C9j5dSttY/E +BgCQBMK60a0vt1lF7b7Drby5kqg7+IvkGgA= +Private-Lines: 2 +AAAAOaA4VTeL/Vg12PIVqPhDcT9qfUT0dIZolnGVRBbU71tD9DyyLbykR5LADjMG +YVuHR3tKi1qkRyxoAA== +Private-MAC: c2623760d10dbe581b7dc8dad41d3a40f437fb9ac2787e4a877c654776dfbae5 diff --git a/files-jsch/src/test/resources/ppkv3_ed448_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_ed448_unix_encrypted.ppk new file mode 100644 index 0000000..f516024 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ed448_unix_encrypted.ppk @@ -0,0 +1,15 @@ +PuTTY-User-Key-File-3: ssh-ed448 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADldNmjRx57BM40rrc4DabuF0L+RhEkOcDC8/jAhtbdT +O0X+sqgITai5gSk1JGlIrCRIF83Kvs0TKIA= +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 42327c0d92de84745042deb048cb888b +Private-Lines: 2 +MD0ereDMhZNnTbZvgK5h0GlTlXZbdVNJfzaKrHth4q+ZMuZK3ONfTHbMgRKtsuFj +vRCgMJAJdq1qNCflJavE/w== +Private-MAC: 9bc62748d045aef6be0de10ecba4b626e41504636a7faf284f6ebac39a66d249 diff --git a/files-jsch/src/test/resources/ppkv3_ed448_windows.ppk b/files-jsch/src/test/resources/ppkv3_ed448_windows.ppk new file mode 100644 index 0000000..4ff9898 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ed448_windows.ppk @@ -0,0 +1,10 @@ +PuTTY-User-Key-File-3: ssh-ed448 +Encryption: none +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADkVoUhABT+RHfYEGAMKySSyUSoJFj6HHcz87U+io4Dp +e4ye88DGUU2Lnxfux/WQq61DPHWxQHo4GoA= +Private-Lines: 2 +AAAAObCkYvIfcF/UyhGmrFg1BIzY+m/1BjSOl/G7BXbjb/afcugNYfD8Mx6c3kLa +05UFTkTX858guX/lAA== +Private-MAC: 6f19bb57b0ef41d6abdd1c555a6247507db32e5d022032c9744f3f735bceefe5 diff --git a/files-jsch/src/test/resources/ppkv3_ed448_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_ed448_windows_encrypted.ppk new file mode 100644 index 0000000..1022884 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_ed448_windows_encrypted.ppk @@ -0,0 +1,15 @@ +PuTTY-User-Key-File-3: ssh-ed448 +Encryption: aes256-cbc +Comment: test +Public-Lines: 2 +AAAACXNzaC1lZDQ0OAAAADkdO1HUptQ2ksEmblqqTM8loY2Q/Ep003qRxW753XVa +z8eh9CejJvTO2FYtyHNib5syBD814By7C4A= +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: adfd1dcbca4e55bb379eee8123a41943 +Private-Lines: 2 +7v3Io0Ub3fTnNaE9qHuJ3Ua8KCPBQvDoznZ+ppbJdVV/gDrczoa2teEIQ6qonpYA +pIjHDQ/56tyVpt/yvkJGAg== +Private-MAC: 538f86533f231dce675843a305cf28bfe29f73434cb589bd08e6b9410f209a45 diff --git a/files-jsch/src/test/resources/ppkv3_rsa_unix.ppk b/files-jsch/src/test/resources/ppkv3_rsa_unix.ppk new file mode 100644 index 0000000..5297884 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_rsa_unix.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-3: ssh-rsa +Encryption: none +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCclEvT+gGh6exRwmgPhsN+PLMpcNltztLQ +1x8iKOsxCCbXWXRxfvvG/ye8lp4kEOWIZ1224UoG1401aNBVTGNUT7SVyAFSdnHG +7AGgFnLwJcxrsEHLX/f+gj/m4syNJfAg1PrS4O8IeaEnTdql9x4cYkzd+Jpa/OHZ +Lgltc8SHezhmxrc5Ylm1IMHu6gxJb0WwQ1mIZ44IAIaYvNflal7dP8v//bY+6kvS +5iARk6hDfiR/wH3926r7U0pB3siNDdz5yq7rjvTg6igtOhQDSnd9Drp0yHjBCyQo +iDqP1P2jhyfz1iDdf6u71CFhZK20rfbsUUHCjWilB00onvjjIDYJ +Private-Lines: 14 +AAABAHB5omK58xRA+e3dLW0BzEpRBg3dr4JOkGdOMGIUbKYDCguliZzBr9DJltzE +gQK9VHSAc/Qbr1Zs3lWgXg732V5GBx2U10ZKKP7Qp3Y8ygGx1T8CFLEn1ffvzkFn +Z7J4rx6WfzqeM3auEFIwfcC8W9fd0QOeQhrcDsw4YrNJ9sGuYIa/bo3VDNr4sbmc +odnbHSmxQFam9BV7bfxFcC3CPusvh4BdrT3vkQjlFR+RQOU6nDPpQ3Z3SXp12k3k +EoHClZuNKP1YCci8+uRWeobOo6rsJKJsmzFYnjNdPDH9ytQm2N7Pg93gB7Sqp27S +56XLoRSGzCeZqrW1Uc38wj/qX2UAAACBAO/PhzZFQOSOKydp+4d2U0ZM8YumRYYN +OBCd3XtypcThMjwj09smbZI2bMm7xGySwEW5VIRmry8swvy3bNFtybhlEV3/+SDl +spCCTKWUZykSCPJvWrt21LMe+DYvZRJhPyhjQvfStIjlDgyuJQVaPghx3KBaglvU +e6OGaBLbu9+nAAAAgQCnJldXgo0q9RXeSOJtiXKg8Y5ykLGALpkWudU6VdYGurbU +Q5Bu3gO3MyFeRV/GhHEZ3av0i63tfNBeOcWafm3OW1xaLaJDioHQ/KKlCfs5nXMC +Z6Qp+56mhk48unpdvOWSe3qorazrgo4XefKa/9nn8O6Im4DDOLPzAWxu8jJyzwAA +AIEAoX8jMlUD2McXRGBbRu3UoXRir0amRYONcYJN+kCeiptJl3WNETgni2Vt5Cgw +45mrIDQ3I+HkqhU+0cw6p8n9m4JkBQ7naWpyA32ofxdRPa9h0he5fZj+tDs3IQRY +OLh5cZMOxHsymrnHRCgLkddr+P976niOWEFc73MFvpxM3qE= +Private-MAC: 61abe803ffbf8f7ebd9c899ba3ef9279f08b433e9095eb248a28a38b45e6367e diff --git a/files-jsch/src/test/resources/ppkv3_rsa_unix_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_rsa_unix_encrypted.ppk new file mode 100644 index 0000000..833eef7 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_rsa_unix_encrypted.ppk @@ -0,0 +1,31 @@ +PuTTY-User-Key-File-3: ssh-rsa +Encryption: aes256-cbc +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCMTxeJiE+rerxDpf3UhJRVvaWulraY8VZr +yHhSPnHCODQw3MsQ0X6ECmTs8c3YeNRBRhiaB6zOQorUFqFbAwO2XIFQgDGk9qSJ +F1kwsVXKnReFqUQ4MQGjRt5odjHc4+00KnmfdLUubvLGJmzHYJ4Ia1+EEyzkUik4 +nVTxdreXrvFEIEej/QHJlhVSqsL2oKXKVMLAJ6OvGt9K1pFSJRCyedHVmR/z3tfs +iFqpw0R2P2WhhkK9jXG47tJcG9v5HPrGJqd3D+W982/YUFNFvLMVkq5qU4wEDFVh +1u7Z95dABtZm2TwzhrYfxdAIcYbZLpbFozWX+f2mEQRAbWws2YE7 +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 331886f5ea02ebe8d0aac746a408ede7 +Private-Lines: 14 +EpXqXpaS5gbssePDuppbh72EaQb4J17ReVx664sVo479mR07xjj1R24hP20H3FUr +zYif4C0NuWWqyUCZ4ULyjW5+08m3SgQ5WOva9bPLryWpI6CTDTZ8wVQqkmCgweKt +JtPVOtcrdCKze7udLa/Ah0PxNkFzsHNfu1aQfG6MnQ9p9orwzfy5A8pm36sxigwj +i6hHKMdcJY4+eA7Df5CkccVBiZlKr4nTChoSsSmBnLMKNSwR2EftW2H1uk9UzjxU +5VozBBeTkOqU9p6ph7pez5tDIZPN3krzQETIz1dXhTRrueard5l4slHV7N6W/k7t +JynUfkhiGfhwKEjD9GutdJ4oTTe1SNz/3ndVzRS/10uSCPEcn2SdL7QzxYG/lY3W +dAyS6hL8bR3V6NoxdTx/RWUqVrTfI0/dZpUN+h1iSsApB3M8Nth0nFhgsmveSPvT +K//weuOgfB7AtO0Jn+KG81sW0cBK4AlSr80BzfOhKpkgG5OciSw+P1VRE5eq3bzp +iRgbn8yKmvVXbY4NSX2Nued4C2z8MLMv1jHPyg7UOPhb52LyBGqWdT5iOURqWF/6 +Az37Tag7iDe8K/tFNBQUcazHNKFPnQpXPYJDo3ucRE2tV8txrhQws3WRvAh6n/Ag +Sb6Rz9OymLeSh/VAqcrWHb7PanGZGIo9XeWXRKKhU9SF0+vqqdPFCbVsleyVdoa4 +rIB7zOG15Rr81FaIVh2axTsKsMCIMiMNTOO6p55JT7ViVJr566LxgNA2FBZMEdhV +lXIfxz9JlLaP7a4/rTt4sp00PVorVjx/CeptKCRyptHYA2nM6In/tMHkkxd88l8d +6JESwNXAZz9JTWplWdMa0e0GjZ7hcazms0gWzd/5pMPAOjnixqqpEulGzmEX8EmK +Private-MAC: 1120af9034ed0384d24f636c7dcf926fdca1de80490322bc616b7f6bee225098 diff --git a/files-jsch/src/test/resources/ppkv3_rsa_windows.ppk b/files-jsch/src/test/resources/ppkv3_rsa_windows.ppk new file mode 100644 index 0000000..5b25a31 --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_rsa_windows.ppk @@ -0,0 +1,26 @@ +PuTTY-User-Key-File-3: ssh-rsa +Encryption: none +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCyu2eTD/3HRZnFhKMiy7YLGW7WUsT1uEgR +CUufHFbjwZqSdUMXplB7wD5g/iombD36oZtFGpZWjiReT8hh2k2dR5pzF7Hg9yTZ +osuBu8YRSTjwicc/NHTcDfl5kQFSGz40azFXWn1xJqPRQesDGZ2rQVloCxW771AV +S5hZvAybtM/qf6En7PxHJE4Eg7klvjH2bzhacDQch56u/ij+/ipwtqkWt40k6sMQ +7xtRIQH4AUNlqQ+3qpZxKfAl+NCbJ/V8Mj9BM3WuyeD6DYPwBT1mHXug9KWi7aii +2c/UvDv1xpF7u0mTRSW4Dkm7FTEbX8iPP1QF3ze87YEaF1qRkeMf +Private-Lines: 14 +AAABABoV6fb3xYU47kl6o0inzKjwDFIxgFeQjswVvDi7rR7Omd24SZOBDYwSMch8 +L/CtqZoTzhKW38xAMKSnJkrJzrwewDb+BVMdHv64mNUVb8IEGtBYe4EJCek4DOom +NLgkC78gyfHC3j4OcqhO45rvOSjOf9sEiyaDtf91qgWmwXxT9yfLeSXn+tbEBpez +1sUHw0Pk+Io2bG+jwrCEeA/sf0zBfoLAREAe9CcdZmhKpoXRadaN3falLpwH5fOk +wCnnjdxRCFgLpDZXsc3iA34hhM/mO7h2bnghe6h2KSrqgWJRdGL/+OpclJOx+l8s +L+n9n1/SMJtfLZ8mn6iZd74GAlEAAACBAPtVtmpNFNxw4S+arkhJGo5qplFQ6RNj +bbaCQWv7g7sYfZQuzRyq+ar4hn4Y71x8mLJKqlBNebtfctq0SzCSvFc/jOLqX+LH +/haOrM7WKh8QFVQ8UwtZZdzXImViEw4W1HGJvoLe/MksakTl6afYabLBsDUI04Fd +RekXhcy7sC5ZAAAAgQC2DLMio0Bgvz+hPotfHuSrBb49Gyqc/Nzt3jvFzvqFywU0 +lpXPjy/nCN7fEVuCusLaEM5KY0giQaLna/xDfcoFmpdfQR79E2NFu88nRf1nr4e/ +y/2etw0Pl1Eh/roWDjBi5bFpDBU3eDjF01ylcIbDTb6klJ4PON3gdHJx4WWeNwAA +AIBFMs9P/P9EIFebF91b4wvJTG1xS3b6aLE0jIHNv4inD/6u/4R8U3meGMlvzeZQ +xI56n6Hk8IaCEXehAj+VAfp9z2YHtPoAfUD8mbCKdBzgpW1P1AeOTIzvsi3+bHbm +ZM+e6cTsQGaZ9OJSRYCmGT7LiTRm6PQwyq+4FEP1t9VF3w== +Private-MAC: 20cf1acc614037e76bbc47942988e0aa3bc5582df1a173be2c468149139a406a diff --git a/files-jsch/src/test/resources/ppkv3_rsa_windows_encrypted.ppk b/files-jsch/src/test/resources/ppkv3_rsa_windows_encrypted.ppk new file mode 100644 index 0000000..a1190ad --- /dev/null +++ b/files-jsch/src/test/resources/ppkv3_rsa_windows_encrypted.ppk @@ -0,0 +1,31 @@ +PuTTY-User-Key-File-3: ssh-rsa +Encryption: aes256-cbc +Comment: test +Public-Lines: 6 +AAAAB3NzaC1yc2EAAAADAQABAAABAQCnTnSVyVw7nyqnxy/WWUUuFswXUsdwvNto +jMVuqltV3CTDHfX6+uMu+XBC6Gqr/AzHvwzOPvzR+/k+m5fV5zLvjfe3zTU1gs5E +mOtu3YbOnn6il/IvVvU9ILq/5Dg89aIbSYG9R28iu8KciZxHOOpjgFpmoCiMAg8x +4OWRHc6irKj5VKTXMJC+5pmb4QZlSKal7KPqKyAsKII6IxMjpPNJejZm/Omw3QZ1 +9YIN02ttbM8rwoLuPLi1DoZiAUvF7J3XTEUcDAn5y5upltk1Dx+bWhuth/sYZ26q +ksxPr+KmtHwJ5R8pWJ4CXtENlwxQiAWftVWW8MeQlyN17qPW+Z9x +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 21 +Argon2-Parallelism: 1 +Argon2-Salt: 8bfc7fec5e9a97b5c271a2ff59bbd42a +Private-Lines: 14 +BdVaIWyb2IRejqogeZj7d55V+oiRVB87REDIRT+2nsqmRpv8d9wkEZxa2hZD4JJI +yPJoLHyl6LwKrGWwwCDXf0DHTePNAAASAcp5G60XY/rAz/DMtYUfjFKYfOB3SjUr +G1hjswV+RHm1OT9g5lz7tkeeF5jd3JkZ+6vdqeTxKrosUdqkLkN9SDbVtyLnEnfK +up5RVoMN6jd8UZG90HIsuwR8/G4UwYPwKpj/5RwjKUx26yKPyOCl7tZcrZbA8+fw +UNsODwlFSNdU4roo1SrmVKaDQ5LtV+HXunIOTuxt0p/YfvFcLupgaWgMysXzmhja +sAQyZt5RB7rJOlWXKVOXdd0UWwYOhYTgTItY7tljns8SffOQMt0j+DoLZdMJq1Ej +qWIy53yqCUs1Nyjqv9J9ptUCUiDP0xM50Qeob2YpFK1gpOpPTBOD1GrhF9P33uYT +lhlsz8Wvh4U6txuO3DaPkfmNn4t/0lVdBh6x2Tv1x7OWsebmUQy1Pj5ffJJ6N4wt +UbGdAeiJ2IsGzHHRThmBrjCTxI14QTjIrNpl4Ntbj7x2vPXHVoDfgUJrkeFD8NxW +NaTn+WdYRtod3wLcdM6YAf+rBtGs4gGWK+eKPlepJhia9onRc7Esue/svHYTVgm4 +jdPhQyHPvunLfN+ryD1yZkCRfir0c4HP7sniolfhVYM2UBvzBj+HiauEEWXetFeM +96Cdian5ouUtwvKmoIR0K5KT7g7iDtH94WreGK0Le13EGV9gwEk33HEenbcpn7Db +0ScHiwhHp4sXsiXh75b1kEWAJZVe8B6xbwLegIZesXvDztCMYq1RToBJ7rrMdEoN +uRkinI6LFI5JzxhLDRpsmfSEJQM1SqxRmyoBVn6Jrd02giL1CxuadULsNc4LV4B9 +Private-MAC: 7531943a8f9d37c31a318dbacb9de07b14346541d5b100bd665b36a195daf60f diff --git a/gradle/test/junit5.gradle b/gradle/test/junit5.gradle index 383e798..1181a66 100644 --- a/gradle/test/junit5.gradle +++ b/gradle/test/junit5.gradle @@ -1,5 +1,6 @@ dependencies { testImplementation testLibs.junit.jupiter.api + testImplementation testLibs.junit.jupiter.params testImplementation testLibs.hamcrest testRuntimeOnly testLibs.junit.jupiter.engine testRuntimeOnly testLibs.junit.jupiter.platform.launcher diff --git a/settings.gradle b/settings.gradle index cd1d673..89ca8ac 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,6 +20,7 @@ dependencyResolutionManagement { } testLibs { version('junit', '5.10.2') + version('testcontainers', '1.20.0') library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit') library('junit-jupiter-params', 'org.junit.jupiter', 'junit-jupiter-params').versionRef('junit') library('junit-jupiter-engine', 'org.junit.jupiter', 'junit-jupiter-engine').versionRef('junit') @@ -29,6 +30,8 @@ dependencyResolutionManagement { library('junit4', 'junit', 'junit').version('4.13.2') library('mockito-core', 'org.mockito', 'mockito-core').version('5.11.0') library('mockito-junit-jupiter', 'org.mockito', 'mockito-junit-jupiter').version('5.11.0') + library('testcontainers', 'org.testcontainers', 'testcontainers').versionRef('testcontainers') + library('testcontainers-junit-jupiter', 'org.testcontainers', 'junit-jupiter').versionRef('testcontainers') } } } @@ -40,5 +43,6 @@ include 'files-ftp-mock' include 'files-sftp' include 'files-sftp-fs' include 'files-jadaptive-sftp' +include 'files-jsch' include 'files-webdav' include 'files-webdav-fs'