# HG changeset patch
# User Carl Byington
-Based on the Trilead SSH2 client provided under a BSD-style -license. Copyright © 2007 Trilead -AG http://www.trilead.com +Based on the Ganymed SSH2 client provided under a BSD-style +license. Copyright © 2005 - 2006 Swiss Federal Institute of +Technology (ETH Zurich), Department of Computer +Science http://www.inf.ethz.ch, +Christian Plattner. Copyright © 2006 - 2013 Christian Plattner. +http://code.google.com/p/ganymed-ssh-2/ +The Java implementations of the AES, Blowfish and 3DES ciphers have been +taken (and slightly modified) from the cryptography package released by +"The Legion Of The Bouncy Castle". +Copyright © 2000 - 2004 The Legion Of The Bouncy Castle +http://www.bouncycastle.org +The following disclaimer applies: +
+ ++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
diff -r ce2f4e397703 -r 91a31873c42a ch.ethz.ssh2.LICENSE.txt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/ch.ethz.ssh2.LICENSE.txt Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,87 @@
+Copyright (c) 2006 - 2013 Christian Plattner. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+a.) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+b.) 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.
+c.) Neither the name of Christian Plattner nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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 software includes work that was released under the following license:
+
+Copyright (c) 2005 - 2006 Swiss Federal Institute of Technology (ETH Zurich),
+ Department of Computer Science (http://www.inf.ethz.ch),
+ Christian Plattner. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+a.) Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+b.) 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.
+c.) Neither the name of ETH Zurich nor the names of its contributors may
+ be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
+
+
+The Java implementations of the AES, Blowfish and 3DES ciphers have been
+taken (and slightly modified) from the cryptography package released by
+"The Legion Of The Bouncy Castle".
+
+Their license states the following:
+
+Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+(http://www.bouncycastle.org)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/AbstractSFTPClient.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/AbstractSFTPClient.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,744 @@
+package ch.ethz.ssh2;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketException;
+import java.nio.charset.Charset;
+import java.nio.charset.UnsupportedCharsetException;
+import java.util.HashMap;
+import java.util.Map;
+
+import ch.ethz.ssh2.channel.Channel;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+import ch.ethz.ssh2.sftp.AttribFlags;
+import ch.ethz.ssh2.sftp.ErrorCodes;
+import ch.ethz.ssh2.sftp.Packet;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @version $Id: AbstractSFTPClient.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public abstract class AbstractSFTPClient implements SFTPClient {
+
+ private static final Logger log = Logger.getLogger(SFTPv3Client.class);
+
+ private Session sess;
+
+ private InputStream is;
+ private OutputStream os;
+
+ private int next_request_id = 1000;
+
+ private String charset;
+
+ /**
+ * Parallel read requests maximum size.
+ */
+ private static final int DEFAULT_MAX_PARALLELISM = 64;
+
+ /**
+ * Parallel read requests.
+ */
+ private int parallelism = DEFAULT_MAX_PARALLELISM;
+
+ public void setRequestParallelism(int parallelism) {
+ this.parallelism = Math.min(parallelism, DEFAULT_MAX_PARALLELISM);
+ }
+
+ /**
+ * Mapping request ID to request.
+ */
+ private Map
+ * This is an experimental feature.
+ *
+ * You MUST NOT make any assumption about the thread that invokes this method.
+ *
+ * Please note: if the connection is not connected (e.g., there was no successful
+ * connect() call), then the invocation of {@link Connection#close()} will NOT trigger
+ * this method.
+ *
+ * @see Connection#addConnectionMonitor(ConnectionMonitor)
+ *
+ * @param reason Includes an indication why the socket was closed.
+ */
+ public void connectionLost(Throwable reason);
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/DHGexParameters.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/DHGexParameters.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * A
+ * Depending on which constructor is used, either the use of a
+ *
+ * Note: older OpenSSH servers don't understand this request, in which
+ * case you should use the {@link #DHGexParameters(int)} constructor.
+ *
+ * All values have to be >= 1024 and <= 8192. Furthermore,
+ * min_group_len <= pref_group_len <= max_group_len.
+ *
+ * @param min_group_len
+ * @param pref_group_len
+ * @param max_group_len
+ */
+ public DHGexParameters(int min_group_len, int pref_group_len, int max_group_len)
+ {
+ if ((min_group_len < MIN_ALLOWED) || (min_group_len > MAX_ALLOWED))
+ throw new IllegalArgumentException("min_group_len out of range!");
+
+ if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
+ throw new IllegalArgumentException("pref_group_len out of range!");
+
+ if ((max_group_len < MIN_ALLOWED) || (max_group_len > MAX_ALLOWED))
+ throw new IllegalArgumentException("max_group_len out of range!");
+
+ if ((pref_group_len < min_group_len) || (pref_group_len > max_group_len))
+ throw new IllegalArgumentException("pref_group_len is incompatible with min and max!");
+
+ if (max_group_len < min_group_len)
+ throw new IllegalArgumentException("max_group_len must not be smaller than min_group_len!");
+
+ this.min_group_len = min_group_len;
+ this.pref_group_len = pref_group_len;
+ this.max_group_len = max_group_len;
+ }
+
+ /**
+ * Get the maximum group length.
+ *
+ * @return the maximum group length, may be
+ * Please note: if you want to use basic authentication, then both
+ * Here is an example:
+ *
+ *
+ * Some copy-paste information from the standard: a command line interface
+ * (CLI) client SHOULD print the name and instruction (if non-empty), adding
+ * newlines. Then for each prompt in turn, the client SHOULD display the
+ * prompt and read the user input. The name and instruction fields MAY be
+ * empty strings, the client MUST be prepared to handle this correctly. The
+ * prompt field(s) MUST NOT be empty strings.
+ *
+ * Please refer to draft-ietf-secsh-auth-kbdinteract-XX.txt for the details.
+ *
+ * Note: clients SHOULD use control character filtering as discussed in
+ * RFC4251 to avoid attacks by including
+ * terminal control characters in the fields to be displayed.
+ *
+ * @param name
+ * the name String sent by the server.
+ * @param instruction
+ * the instruction String sent by the server.
+ * @param numPrompts
+ * number of prompts - may be zero (in this case, you should just
+ * return a String array of length zero).
+ * @param prompt
+ * an array (length close()
method, you are likely wasting resources.
+ */
+ @Override
+ public void close() {
+ sess.close();
+ }
+
+ /**
+ * Set the charset used to convert between Java Unicode Strings and byte encodings
+ * used by the server for paths and file names.
+ *
+ * @param charset the name of the charset to be used or null
to use UTF-8.
+ * @throws java.io.IOException
+ * @see #getCharset()
+ */
+ @Override
+ public void setCharset(String charset) throws IOException {
+ if(charset == null) {
+ this.charset = null;
+ return;
+ }
+ try {
+ Charset.forName(charset);
+ }
+ catch(UnsupportedCharsetException e) {
+ throw new IOException("This charset is not supported", e);
+ }
+ this.charset = charset;
+ }
+
+ /**
+ * The currently used charset for filename encoding/decoding.
+ *
+ * @return The name of the charset (null
if UTF-8 is used).
+ * @see #setCharset(String)
+ */
+ @Override
+ public String getCharset() {
+ return charset;
+ }
+
+ public abstract SFTPFileHandle openFile(String fileName, int flags, SFTPFileAttributes attr) throws IOException;
+
+ @Override
+ public void mkdir(String dirName, int posixPermissions) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(dirName, this.getCharset());
+ tw.writeUINT32(AttribFlags.SSH_FILEXFER_ATTR_PERMISSIONS);
+ tw.writeUINT32(posixPermissions);
+
+ sendMessage(Packet.SSH_FXP_MKDIR, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ @Override
+ public void rm(String fileName) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(fileName, this.getCharset());
+
+ sendMessage(Packet.SSH_FXP_REMOVE, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ @Override
+ public void rmdir(String dirName) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(dirName, this.getCharset());
+
+ sendMessage(Packet.SSH_FXP_RMDIR, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ @Override
+ public void mv(String oldPath, String newPath) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(oldPath, this.getCharset());
+ tw.writeString(newPath, this.getCharset());
+
+ sendMessage(Packet.SSH_FXP_RENAME, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ @Override
+ public String readLink(String path) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(path, charset);
+
+ sendMessage(Packet.SSH_FXP_READLINK, req_id, tw.getBytes());
+
+ byte[] resp = receiveMessage(34000);
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+ listener.read(Packet.forName(t));
+
+ int rep_id = tr.readUINT32();
+ if(rep_id != req_id) {
+ throw new RequestMismatchException();
+ }
+
+ if(t == Packet.SSH_FXP_NAME) {
+ int count = tr.readUINT32();
+
+ if(count != 1) {
+ throw new PacketTypeException(t);
+ }
+
+ return tr.readString(charset);
+ }
+
+ if(t != Packet.SSH_FXP_STATUS) {
+ throw new PacketTypeException(t);
+ }
+
+ int errorCode = tr.readUINT32();
+ String errorMessage = tr.readString();
+ listener.read(errorMessage);
+ throw new SFTPException(errorMessage, errorCode);
+ }
+
+ @Override
+ public void setstat(String path, SFTPFileAttributes attr) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(path, charset);
+ tw.writeBytes(attr.toBytes());
+
+ sendMessage(Packet.SSH_FXP_SETSTAT, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ @Override
+ public void fsetstat(SFTPFileHandle handle, SFTPFileAttributes attr) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(handle.getHandle(), 0, handle.getHandle().length);
+ tw.writeBytes(attr.toBytes());
+
+ sendMessage(Packet.SSH_FXP_FSETSTAT, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ @Override
+ public void createSymlink(String src, String target) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(src, charset);
+ tw.writeString(target, charset);
+
+ sendMessage(Packet.SSH_FXP_SYMLINK, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ @Override
+ public void createHardlink(String src, String target) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString("hardlink@openssh.com", charset);
+ tw.writeString(target, charset);
+ tw.writeString(src, charset);
+
+ sendMessage(Packet.SSH_FXP_EXTENDED, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ @Override
+ public String canonicalPath(String path) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(path, charset);
+
+ sendMessage(Packet.SSH_FXP_REALPATH, req_id, tw.getBytes());
+
+ byte[] resp = receiveMessage(34000);
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+ listener.read(Packet.forName(t));
+
+ int rep_id = tr.readUINT32();
+ if(rep_id != req_id) {
+ throw new RequestMismatchException();
+ }
+
+ if(t == Packet.SSH_FXP_NAME) {
+ int count = tr.readUINT32();
+
+ if(count != 1) {
+ throw new PacketFormatException("The server sent an invalid SSH_FXP_NAME packet.");
+ }
+ final String name = tr.readString(charset);
+ listener.read(name);
+ return name;
+ }
+
+ if(t != Packet.SSH_FXP_STATUS) {
+ throw new PacketTypeException(t);
+ }
+
+ int errorCode = tr.readUINT32();
+ String errorMessage = tr.readString();
+ listener.read(errorMessage);
+ throw new SFTPException(errorMessage, errorCode);
+ }
+
+ private void sendMessage(int type, int requestId, byte[] msg, int off, int len) throws IOException {
+ if(log.isDebugEnabled()) {
+ log.debug(String.format("Send message of type %d with request id %d", type, requestId));
+ }
+ listener.write(Packet.forName(type));
+
+ int msglen = len + 1;
+
+ if(type != Packet.SSH_FXP_INIT) {
+ msglen += 4;
+ }
+
+ os.write(msglen >> 24);
+ os.write(msglen >> 16);
+ os.write(msglen >> 8);
+ os.write(msglen);
+ os.write(type);
+
+ if(type != Packet.SSH_FXP_INIT) {
+ os.write(requestId >> 24);
+ os.write(requestId >> 16);
+ os.write(requestId >> 8);
+ os.write(requestId);
+ }
+
+ os.write(msg, off, len);
+ os.flush();
+ }
+
+ protected void sendMessage(int type, int requestId, byte[] msg) throws IOException {
+ sendMessage(type, requestId, msg, 0, msg.length);
+ }
+
+ private void readBytes(byte[] buff, int pos, int len) throws IOException {
+ while(len > 0) {
+ int count = is.read(buff, pos, len);
+ if(count < 0) {
+ throw new SocketException("Unexpected end of stream.");
+ }
+ len -= count;
+ pos += count;
+ }
+ }
+
+ /**
+ * Read a message and guarantee that the contents is not larger than
+ * maxlen
bytes.
+ *
+ * Note: receiveMessage(34000) actually means that the message may be up to 34004
+ * bytes (the length attribute preceding the contents is 4 bytes).
+ *
+ * @param maxlen
+ * @return the message contents
+ * @throws IOException
+ */
+ protected byte[] receiveMessage(int maxlen) throws IOException {
+ byte[] msglen = new byte[4];
+
+ readBytes(msglen, 0, 4);
+
+ int len = (((msglen[0] & 0xff) << 24) | ((msglen[1] & 0xff) << 16) | ((msglen[2] & 0xff) << 8) | (msglen[3] & 0xff));
+
+ if((len > maxlen) || (len <= 0)) {
+ throw new PacketFormatException(String.format("Illegal SFTP packet length %d", len));
+ }
+
+ byte[] msg = new byte[len];
+
+ readBytes(msg, 0, len);
+
+ return msg;
+ }
+
+ protected int generateNextRequestID() {
+ synchronized(this) {
+ return next_request_id++;
+ }
+ }
+
+ protected void closeHandle(byte[] handle) throws IOException {
+ int req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(handle, 0, handle.length);
+
+ sendMessage(Packet.SSH_FXP_CLOSE, req_id, tw.getBytes());
+
+ expectStatusOKMessage(req_id);
+ }
+
+ private void readStatus() throws IOException {
+ byte[] resp = receiveMessage(34000);
+
+ TypesReader tr = new TypesReader(resp);
+ int t = tr.readByte();
+ listener.read(Packet.forName(t));
+
+ // Search the pending queue
+ OutstandingStatusRequest status = pendingStatusQueue.remove(tr.readUINT32());
+ if(null == status) {
+ throw new RequestMismatchException();
+ }
+
+ // Evaluate the answer
+ if(t == Packet.SSH_FXP_STATUS) {
+ // In any case, stop sending more packets
+ int code = tr.readUINT32();
+ if(log.isDebugEnabled()) {
+ String[] desc = ErrorCodes.getDescription(code);
+ log.debug("Got SSH_FXP_STATUS (" + status.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")");
+ }
+ if(code == ErrorCodes.SSH_FX_OK) {
+ return;
+ }
+ String msg = tr.readString();
+ listener.read(msg);
+ throw new SFTPException(msg, code);
+ }
+ throw new PacketTypeException(t);
+ }
+
+ private void readPendingReadStatus() throws IOException {
+ byte[] resp = receiveMessage(34000);
+
+ TypesReader tr = new TypesReader(resp);
+ int t = tr.readByte();
+ listener.read(Packet.forName(t));
+
+ // Search the pending queue
+ OutstandingReadRequest status = pendingReadQueue.remove(tr.readUINT32());
+ if(null == status) {
+ throw new RequestMismatchException();
+ }
+
+ // Evaluate the answer
+ if(t == Packet.SSH_FXP_STATUS) {
+ // In any case, stop sending more packets
+ int code = tr.readUINT32();
+ if(log.isDebugEnabled()) {
+ String[] desc = ErrorCodes.getDescription(code);
+ log.debug("Got SSH_FXP_STATUS (" + status.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")");
+ }
+ if(code == ErrorCodes.SSH_FX_OK) {
+ return;
+ }
+ if(code == ErrorCodes.SSH_FX_EOF) {
+ return;
+ }
+ String msg = tr.readString();
+ listener.read(msg);
+ throw new SFTPException(msg, code);
+ }
+ throw new PacketTypeException(t);
+ }
+
+ protected void expectStatusOKMessage(int id) throws IOException {
+ byte[] resp = receiveMessage(34000);
+
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+ listener.read(Packet.forName(t));
+
+ int rep_id = tr.readUINT32();
+ if(rep_id != id) {
+ throw new RequestMismatchException();
+ }
+
+ if(t != Packet.SSH_FXP_STATUS) {
+ throw new PacketTypeException(t);
+ }
+
+ int errorCode = tr.readUINT32();
+
+ if(errorCode == ErrorCodes.SSH_FX_OK) {
+ return;
+ }
+ String errorMessage = tr.readString();
+ listener.read(errorMessage);
+ throw new SFTPException(errorMessage, errorCode);
+ }
+
+ @Override
+ public void closeFile(SFTPFileHandle handle) throws IOException {
+ while(!pendingReadQueue.isEmpty()) {
+ this.readPendingReadStatus();
+ }
+ while(!pendingStatusQueue.isEmpty()) {
+ this.readStatus();
+ }
+ closeHandle(handle.getHandle());
+ }
+
+ @Override
+ public int read(SFTPFileHandle handle, long fileOffset, byte[] dst, int dstoff, int len) throws IOException {
+ boolean errorOccured = false;
+ int remaining = len * parallelism;
+ //int clientOffset = dstoff;
+
+ long serverOffset = fileOffset;
+ for(OutstandingReadRequest r : pendingReadQueue.values()) {
+ // Server offset should take pending requests into account.
+ serverOffset += r.len;
+ }
+
+ while(true) {
+ // Stop if there was an error and no outstanding request
+ if((pendingReadQueue.size() == 0) && errorOccured) {
+ break;
+ }
+
+ // Send as many requests as we are allowed to
+ while(pendingReadQueue.size() < parallelism) {
+ if(errorOccured) {
+ break;
+ }
+ // Send the next read request
+ OutstandingReadRequest req = new OutstandingReadRequest();
+ req.req_id = generateNextRequestID();
+ req.serverOffset = serverOffset;
+ req.len = (remaining > len) ? len : remaining;
+ req.buffer = dst;
+ req.dstOffset = dstoff;
+
+ serverOffset += req.len;
+ //clientOffset += req.len;
+ remaining -= req.len;
+
+ sendReadRequest(req.req_id, handle, req.serverOffset, req.len);
+
+ pendingReadQueue.put(req.req_id, req);
+ }
+ if(pendingReadQueue.size() == 0) {
+ break;
+ }
+
+ // Receive a single answer
+ byte[] resp = receiveMessage(34000);
+ TypesReader tr = new TypesReader(resp);
+
+ int t = tr.readByte();
+ listener.read(Packet.forName(t));
+
+ // Search the pending queue
+ OutstandingReadRequest req = pendingReadQueue.remove(tr.readUINT32());
+ if(null == req) {
+ throw new RequestMismatchException();
+ }
+ // Evaluate the answer
+ if(t == Packet.SSH_FXP_STATUS) {
+ /* In any case, stop sending more packets */
+
+ int code = tr.readUINT32();
+ String msg = tr.readString();
+ listener.read(msg);
+
+ if(log.isDebugEnabled()) {
+ String[] desc = ErrorCodes.getDescription(code);
+ log.debug("Got SSH_FXP_STATUS (" + req.req_id + ") (" + ((desc != null) ? desc[0] : "UNKNOWN") + ")");
+ }
+ // Flag to read all pending requests but don't send any more.
+ errorOccured = true;
+ if(pendingReadQueue.isEmpty()) {
+ if(ErrorCodes.SSH_FX_EOF == code) {
+ return -1;
+ }
+ throw new SFTPException(msg, code);
+ }
+ }
+ else if(t == Packet.SSH_FXP_DATA) {
+ // OK, collect data
+ int readLen = tr.readUINT32();
+
+ if((readLen < 0) || (readLen > req.len)) {
+ throw new PacketFormatException("The server sent an invalid length field in a SSH_FXP_DATA packet.");
+ }
+
+ if(log.isDebugEnabled()) {
+ log.debug("Got SSH_FXP_DATA (" + req.req_id + ") " + req.serverOffset + "/" + readLen
+ + " (requested: " + req.len + ")");
+ }
+
+ // Read bytes into buffer
+ tr.readBytes(req.buffer, req.dstOffset, readLen);
+
+ if(readLen < req.len) {
+ /* Send this request packet again to request the remaing data in this slot. */
+ req.req_id = generateNextRequestID();
+ req.serverOffset += readLen;
+ req.len -= readLen;
+
+ log.debug("Requesting again: " + req.serverOffset + "/" + req.len);
+ sendReadRequest(req.req_id, handle, req.serverOffset, req.len);
+
+ pendingReadQueue.put(req.req_id, req);
+ }
+ return readLen;
+ }
+ else {
+ throw new PacketTypeException(t);
+ }
+ }
+ // Should never reach here.
+ throw new SFTPException("No EOF reached", -1);
+ }
+
+ private void sendReadRequest(int id, SFTPFileHandle handle, long offset, int len) throws IOException {
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(handle.getHandle(), 0, handle.getHandle().length);
+ tw.writeUINT64(offset);
+ tw.writeUINT32(len);
+
+ sendMessage(Packet.SSH_FXP_READ, id, tw.getBytes());
+ }
+
+ @Override
+ public void write(SFTPFileHandle handle, long fileOffset, byte[] src, int srcoff, int len) throws IOException {
+ while(len > 0) {
+ int writeRequestLen = len;
+
+ if(writeRequestLen > 32768) {
+ writeRequestLen = 32768;
+ }
+
+ // Send the next write request
+ OutstandingStatusRequest req = new OutstandingStatusRequest();
+ req.req_id = generateNextRequestID();
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeString(handle.getHandle(), 0, handle.getHandle().length);
+ tw.writeUINT64(fileOffset);
+ tw.writeString(src, srcoff, writeRequestLen);
+
+ sendMessage(Packet.SSH_FXP_WRITE, req.req_id, tw.getBytes());
+
+ pendingStatusQueue.put(req.req_id, req);
+
+ // Only read next status if parallelism reached
+ while(pendingStatusQueue.size() >= parallelism) {
+ this.readStatus();
+ }
+ fileOffset += writeRequestLen;
+ srcoff += writeRequestLen;
+ len -= writeRequestLen;
+ }
+ }
+
+
+ /**
+ * A read is divided into multiple requests sent sequentially before
+ * reading any status from the server
+ */
+ private static class OutstandingReadRequest {
+ int req_id;
+ /**
+ * Read offset to request on server starting at the file offset for the first request.
+ */
+ long serverOffset;
+ /**
+ * Length of requested data
+ */
+ int len;
+ /**
+ * Offset in destination buffer
+ */
+ int dstOffset;
+ /**
+ * Temporary buffer
+ */
+ byte[] buffer;
+ }
+
+ /**
+ * A read is divided into multiple requests sent sequentially before
+ * reading any status from the server
+ */
+ private static final class OutstandingStatusRequest {
+ int req_id;
+ }
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/AuthenticationResult.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/AuthenticationResult.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,21 @@
+
+package ch.ethz.ssh2;
+
+public enum AuthenticationResult {
+
+ /**
+ *
+ */
+ SUCCESS,
+ /**
+ * The authentication request to which this is a response was successful, however, more
+ * authentication requests are needed (multi-method authentication sequence).
+ *
+ * @see ServerAuthenticationCallback#getRemainingAuthMethods(ServerConnection)
+ */
+ PARTIAL_SUCCESS,
+ /**
+ * The server rejected the authentication request.
+ */
+ FAILURE
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/ChannelCondition.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ChannelCondition.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * Contains constants that can be used to specify what conditions to wait for on
+ * a SSH-2 channel (e.g., represented by a {@link Session}).
+ *
+ * @see Session#waitForCondition(int, long)
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public abstract interface ChannelCondition
+{
+ /**
+ * A timeout has occurred, none of your requested conditions is fulfilled.
+ * However, other conditions may be true - therefore, NEVER use the "=="
+ * operator to test for this (or any other) condition. Always use
+ * something like ((cond & ChannelCondition.CLOSED) != 0)
.
+ */
+ public static final int TIMEOUT = 1;
+
+ /**
+ * The underlying SSH-2 channel, however not necessarily the whole connection,
+ * has been closed. This implies EOF
. Note that there may still
+ * be unread stdout or stderr data in the local window, i.e, STDOUT_DATA
+ * or/and STDERR_DATA
may be set at the same time.
+ */
+ public static final int CLOSED = 2;
+
+ /**
+ * There is stdout data available that is ready to be consumed.
+ */
+ public static final int STDOUT_DATA = 4;
+
+ /**
+ * There is stderr data available that is ready to be consumed.
+ */
+ public static final int STDERR_DATA = 8;
+
+ /**
+ * EOF on has been reached, no more _new_ stdout or stderr data will arrive
+ * from the remote server. However, there may be unread stdout or stderr
+ * data, i.e, STDOUT_DATA
or/and STDERR_DATA
+ * may be set at the same time.
+ */
+ public static final int EOF = 16;
+
+ /**
+ * The exit status of the remote process is available.
+ * Some servers never send the exist status, or occasionally "forget" to do so.
+ */
+ public static final int EXIT_STATUS = 32;
+
+ /**
+ * The exit signal of the remote process is available.
+ */
+ public static final int EXIT_SIGNAL = 64;
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/Connection.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/Connection.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,1273 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import ch.ethz.ssh2.auth.AgentProxy;
+import ch.ethz.ssh2.auth.AuthenticationManager;
+import ch.ethz.ssh2.channel.ChannelManager;
+import ch.ethz.ssh2.compression.CompressionFactory;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.packets.PacketIgnore;
+import ch.ethz.ssh2.transport.ClientTransportManager;
+import ch.ethz.ssh2.transport.HTTPProxyClientTransportManager;
+import ch.ethz.ssh2.transport.KexManager;
+import ch.ethz.ssh2.util.TimeoutService;
+import ch.ethz.ssh2.util.TimeoutService.TimeoutToken;
+
+/**
+ * A Connection
is used to establish an encrypted TCP/IP
+ * connection to a SSH-2 server.
+ *
+ * Typically, one
+ *
+ *
+ *
+ * @author Christian Plattner
+ * @version $Id: Connection.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+
+public class Connection {
+ /**
+ * The identifier presented to the SSH-2 server. This is the same
+ * as the "softwareversion" defined in RFC 4253.
+ *
+ * NOTE: As per the RFC, the "softwareversion" string MUST consist of printable
+ * US-ASCII characters, with the exception of whitespace characters and the minus sign (-).
+ */
+ private String softwareversion
+ = String.format("Ganymed_%s", Version.getSpecification());
+
+ /* Will be used to generate all random data needed for the current connection.
+ * Note: SecureRandom.nextBytes() is thread safe.
+ */
+
+ private SecureRandom generator;
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @return The list of supported cipher algorithms by this implementation.
+ */
+ public static synchronized String[] getAvailableCiphers() {
+ return BlockCipherFactory.getDefaultCipherList();
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @return The list of supported MAC algorthims by this implementation.
+ */
+ public static synchronized String[] getAvailableMACs() {
+ return MAC.getMacList();
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @return The list of supported server host key algorthims by this implementation.
+ */
+ public static synchronized String[] getAvailableServerHostKeyAlgorithms() {
+ return KexManager.getDefaultServerHostkeyAlgorithmList();
+ }
+
+ private AuthenticationManager am;
+
+ private boolean authenticated;
+ private ChannelManager cm;
+
+ private CryptoWishList cryptoWishList
+ = new CryptoWishList();
+
+ private DHGexParameters dhgexpara
+ = new DHGexParameters();
+
+ private final String hostname;
+
+ private final int port;
+
+ private ClientTransportManager tm;
+
+ private boolean tcpNoDelay = false;
+
+ private HTTPProxyData proxy;
+
+ private ListConnection
object which can then be used
+ * to establish a connection to the specified SSH-2 server.
+ *
+ * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
+ *
+ * @param hostname the hostname of the SSH-2 server.
+ */
+ public Connection(String hostname) {
+ this(hostname, 22);
+ }
+
+ /**
+ * Prepares a fresh Connection
object which can then be used
+ * to establish a connection to the specified SSH-2 server.
+ *
+ * @param hostname the host where we later want to connect to.
+ * @param port port on the server, normally 22.
+ */
+ public Connection(String hostname, int port) {
+ this.hostname = hostname;
+ this.port = port;
+ }
+
+ /**
+ * Prepares a fresh Connection
object which can then be used
+ * to establish a connection to the specified SSH-2 server.
+ *
+ * @param hostname the host where we later want to connect to.
+ * @param port port on the server, normally 22.
+ * @param softwareversion Allows you to set a custom "softwareversion" string as defined in RFC 4253.
+ * NOTE: As per the RFC, the "softwareversion" string MUST consist of printable
+ * US-ASCII characters, with the exception of whitespace characters and the minus sign (-).
+ */
+ public Connection(String hostname, int port, String softwareversion) {
+ this.hostname = hostname;
+ this.port = port;
+ this.softwareversion = softwareversion;
+ }
+
+ public Connection(String hostname, int port, final HTTPProxyData proxy) {
+ this.hostname = hostname;
+ this.port = port;
+ this.proxy = proxy;
+ }
+
+ public Connection(String hostname, int port, String softwareversion, final HTTPProxyData proxy) {
+ this.hostname = hostname;
+ this.port = port;
+ this.softwareversion = softwareversion;
+ this.proxy = proxy;
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. This method
+ * is based on DSA (it uses DSA to sign a challenge sent by the server).
+ *
+ * If the authentication phase is complete, true
will be
+ * returned. If the server does not accept the request (or if further
+ * authentication steps are needed), false
is returned and
+ * one can retry either by using this or any other authentication method
+ * (use the getRemainingAuthMethods
method to get a list of
+ * the remaining possible methods).
+ *
+ * @param user A String
holding the username.
+ * @param pem A String
containing the DSA private key of the
+ * user in OpenSSH key format (PEM, you can't miss the
+ * "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
+ * linefeeds.
+ * @param password If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
+ * must specify the password. Otherwise, this argument will be
+ * ignored and can be set to null
.
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ * @deprecated You should use one of the {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
+ * methods, this method is just a wrapper for it and will
+ * disappear in future builds.
+ */
+ public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException {
+ if(tm == null) {
+ throw new IllegalStateException("Connection is not established!");
+ }
+
+ if(authenticated) {
+ throw new IllegalStateException("Connection is already authenticated!");
+ }
+
+ if(am == null) {
+ am = new AuthenticationManager(tm);
+ }
+
+ if(cm == null) {
+ cm = new ChannelManager(tm);
+ }
+
+ if(user == null) {
+ throw new IllegalArgumentException("user argument is null");
+ }
+
+ if(pem == null) {
+ throw new IllegalArgumentException("pem argument is null");
+ }
+
+ authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());
+
+ return authenticated;
+ }
+
+ /**
+ * A wrapper that calls {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
+ * authenticateWithKeyboardInteractivewith} a null
submethod list.
+ *
+ * @param user A String
holding the username.
+ * @param cb An InteractiveCallback
which will be used to
+ * determine the responses to the questions asked by the server.
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
+ throws IOException {
+ return authenticateWithKeyboardInteractive(user, null, cb);
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. This method
+ * is based on "keyboard-interactive", specified in
+ * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
+ * callback object which will be feeded with challenges generated by the
+ * server. Answers are then sent back to the server. It is possible that the
+ * callback will be called several times during the invocation of this
+ * method (e.g., if the server replies to the callback's answer(s) with
+ * another challenge...)
+ *
+ * If the authentication phase is complete, true
will be
+ * returned. If the server does not accept the request (or if further
+ * authentication steps are needed), false
is returned and
+ * one can retry either by using this or any other authentication method
+ * (use the getRemainingAuthMethods
method to get a list of
+ * the remaining possible methods).
+ *
+ * Note: some SSH servers advertise "keyboard-interactive", however, any
+ * interactive request will be denied (without having sent any challenge to
+ * the client).
+ *
+ * @param user A String
holding the username.
+ * @param submethods An array of submethod names, see
+ * draft-ietf-secsh-auth-kbdinteract-XX. May be null
+ * to indicate an empty list.
+ * @param cb An InteractiveCallback
which will be used to
+ * determine the responses to the questions asked by the server.
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
+ InteractiveCallback cb) throws IOException {
+ if(cb == null) {
+ throw new IllegalArgumentException("Callback may not ne NULL!");
+ }
+
+ if(tm == null) {
+ throw new IllegalStateException("Connection is not established!");
+ }
+
+ if(authenticated) {
+ throw new IllegalStateException("Connection is already authenticated!");
+ }
+
+ if(am == null) {
+ am = new AuthenticationManager(tm);
+ }
+
+ if(cm == null) {
+ cm = new ChannelManager(tm);
+ }
+
+ if(user == null) {
+ throw new IllegalArgumentException("user argument is null");
+ }
+
+ authenticated = am.authenticateInteractive(user, submethods, cb);
+
+ return authenticated;
+ }
+
+ public synchronized boolean authenticateWithAgent(String user, AgentProxy proxy) throws IOException {
+ if(tm == null) {
+ throw new IllegalStateException("Connection is not established!");
+ }
+
+ if(authenticated) {
+ throw new IllegalStateException("Connection is already authenticated!");
+ }
+
+ if(am == null) {
+ am = new AuthenticationManager(tm);
+ }
+
+ if(cm == null) {
+ cm = new ChannelManager(tm);
+ }
+
+ if(user == null) {
+ throw new IllegalArgumentException("user argument is null");
+ }
+
+ authenticated = am.authenticatePublicKey(user, proxy);
+
+ return authenticated;
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. This method
+ * sends username and password to the server.
+ *
+ * If the authentication phase is complete, true
will be
+ * returned. If the server does not accept the request (or if further
+ * authentication steps are needed), false
is returned and
+ * one can retry either by using this or any other authentication method
+ * (use the getRemainingAuthMethods
method to get a list of
+ * the remaining possible methods).
+ *
+ * Note: if this method fails, then please double-check that it is actually
+ * offered by the server (use {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
+ *
+ * Often, password authentication is disabled, but users are not aware of it.
+ * Many servers only offer "publickey" and "keyboard-interactive". However,
+ * even though "keyboard-interactive" *feels* like password authentication
+ * (e.g., when using the putty or openssh clients) it is *not* the same mechanism.
+ *
+ * @param user
+ * @param password
+ * @return if the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithPassword(String user, String password) throws IOException {
+ if(tm == null) {
+ throw new IllegalStateException("Connection is not established!");
+ }
+
+ if(authenticated) {
+ throw new IllegalStateException("Connection is already authenticated!");
+ }
+
+ if(am == null) {
+ am = new AuthenticationManager(tm);
+ }
+
+ if(cm == null) {
+ cm = new ChannelManager(tm);
+ }
+
+ if(user == null) {
+ throw new IllegalArgumentException("user argument is null");
+ }
+
+ if(password == null) {
+ throw new IllegalArgumentException("password argument is null");
+ }
+
+ authenticated = am.authenticatePassword(user, password);
+
+ return authenticated;
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself.
+ * This method can be used to explicitly use the special "none"
+ * authentication method (where only a username has to be specified).
+ *
+ * Note 1: The "none" method may always be tried by clients, however as by
+ * the specs, the server will not explicitly announce it. In other words,
+ * the "none" token will never show up in the list returned by
+ * {@link #getRemainingAuthMethods(String)}.
+ *
+ * Note 2: no matter which one of the authenticateWithXXX() methods
+ * you call, the library will always issue exactly one initial "none"
+ * authentication request to retrieve the initially allowed list of
+ * authentication methods by the server. Please read RFC 4252 for the
+ * details.
+ *
+ * If the authentication phase is complete, true
will be
+ * returned. If further authentication steps are needed, false
+ * is returned and one can retry by any other authentication method
+ * (use the getRemainingAuthMethods
method to get a list of
+ * the remaining possible methods).
+ *
+ * @param user
+ * @return if the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithNone(String user) throws IOException {
+ if(tm == null) {
+ throw new IllegalStateException("Connection is not established!");
+ }
+
+ if(authenticated) {
+ throw new IllegalStateException("Connection is already authenticated!");
+ }
+
+ if(am == null) {
+ am = new AuthenticationManager(tm);
+ }
+
+ if(cm == null) {
+ cm = new ChannelManager(tm);
+ }
+
+ if(user == null) {
+ throw new IllegalArgumentException("user argument is null");
+ }
+
+ /* Trigger the sending of the PacketUserauthRequestNone packet */
+ /* (if not already done) */
+
+ authenticated = am.authenticateNone(user);
+
+ return authenticated;
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself.
+ * The authentication method "publickey" works by signing a challenge
+ * sent by the server. The signature is either DSA or RSA based - it
+ * just depends on the type of private key you specify, either a DSA
+ * or RSA private key in PEM format. And yes, this is may seem to be a
+ * little confusing, the method is called "publickey" in the SSH-2 protocol
+ * specification, however since we need to generate a signature, you
+ * actually have to supply a private key =).
+ *
+ * The private key contained in the PEM file may also be encrypted ("Proc-Type: 4,ENCRYPTED").
+ * The library supports DES-CBC and DES-EDE3-CBC encryption, as well
+ * as the more exotic PEM encrpytions AES-128-CBC, AES-192-CBC and AES-256-CBC.
+ *
+ * If the authentication phase is complete, true
will be
+ * returned. If the server does not accept the request (or if further
+ * authentication steps are needed), false
is returned and
+ * one can retry either by using this or any other authentication method
+ * (use the getRemainingAuthMethods
method to get a list of
+ * the remaining possible methods).
+ *
+ * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
+ * it is not in the expected format. You have to convert it to the OpenSSH
+ * key format by using the "puttygen" tool (can be downloaded from the Putty
+ * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
+ * functionality to get a proper PEM file.
+ *
+ * @param user A String
holding the username.
+ * @param pemPrivateKey A char[]
containing a DSA or RSA private key of the
+ * user in OpenSSH key format (PEM, you can't miss the
+ * "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
+ * tag). The char array may contain linebreaks/linefeeds.
+ * @param password If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED") then
+ * you must specify a password. Otherwise, this argument will be ignored
+ * and can be set to null
.
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
+ throws IOException {
+ if(tm == null) {
+ throw new IllegalStateException("Connection is not established!");
+ }
+
+ if(authenticated) {
+ throw new IllegalStateException("Connection is already authenticated!");
+ }
+
+ if(am == null) {
+ am = new AuthenticationManager(tm);
+ }
+
+ if(cm == null) {
+ cm = new ChannelManager(tm);
+ }
+
+ if(user == null) {
+ throw new IllegalArgumentException("user argument is null");
+ }
+
+ if(pemPrivateKey == null) {
+ throw new IllegalArgumentException("pemPrivateKey argument is null");
+ }
+
+ authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());
+
+ return authenticated;
+ }
+
+ /**
+ * A convenience wrapper function which reads in a private key (PEM format, either DSA or RSA)
+ * and then calls authenticateWithPublicKey(String, char[], String)
.
+ *
+ * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
+ * it is not in the expected format. You have to convert it to the OpenSSH
+ * key format by using the "puttygen" tool (can be downloaded from the Putty
+ * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
+ * functionality to get a proper PEM file.
+ *
+ * @param user A String
holding the username.
+ * @param pemFile A File
object pointing to a file containing a DSA or RSA
+ * private key of the user in OpenSSH key format (PEM, you can't miss the
+ * "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
+ * tag).
+ * @param password If the PEM file is encrypted then you must specify the password.
+ * Otherwise, this argument will be ignored and can be set to null
.
+ * @return whether the connection is now authenticated.
+ * @throws IOException
+ */
+ public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
+ throws IOException {
+ if(pemFile == null) {
+ throw new IllegalArgumentException("pemFile argument is null");
+ }
+
+ char[] buff = new char[256];
+
+ CharArrayWriter cw = new CharArrayWriter();
+
+ FileReader fr = new FileReader(pemFile);
+
+ while(true) {
+ int len = fr.read(buff);
+ if(len < 0) {
+ break;
+ }
+ cw.write(buff, 0, len);
+ }
+
+ fr.close();
+
+ return authenticateWithPublicKey(user, cw.toCharArray(), password);
+ }
+
+ /**
+ * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any time,
+ * but it is best to add connection monitors before invoking
+ * connect()
to avoid glitches (e.g., you add a connection monitor after
+ * a successful connect(), but the connection has died in the mean time. Then,
+ * your connection monitor won't be notified.)
+ *
+ * You can add as many monitors as you like. If a monitor has already been added, then
+ * this method does nothing.
+ *
+ * @param cmon An object implementing the {@link ConnectionMonitor} interface.
+ * @see ConnectionMonitor
+ */
+ public synchronized void addConnectionMonitor(ConnectionMonitor cmon) {
+ if(!connectionMonitors.contains(cmon)) {
+ connectionMonitors.add(cmon);
+ if(tm != null) {
+ tm.setConnectionMonitors(connectionMonitors);
+ }
+ }
+ }
+
+ /**
+ * Remove a {@link ConnectionMonitor} from this connection.
+ *
+ * @param cmon
+ * @return whether the monitor could be removed
+ */
+ public synchronized boolean removeConnectionMonitor(ConnectionMonitor cmon) {
+ boolean existed = connectionMonitors.remove(cmon);
+ if(tm != null) {
+ tm.setConnectionMonitors(connectionMonitors);
+ }
+ return existed;
+ }
+
+ /**
+ * Close the connection to the SSH-2 server. All assigned sessions will be
+ * closed, too. Can be called at any time. Don't forget to call this once
+ * you don't need a connection anymore - otherwise the receiver thread may
+ * run forever.
+ */
+ public synchronized void close() {
+ if(cm != null) {
+ cm.closeAllChannels();
+ }
+ if(tm != null) {
+ tm.close();
+ tm = null;
+ }
+ am = null;
+ cm = null;
+ authenticated = false;
+ }
+
+ public synchronized void close(IOException t) {
+ if(cm != null) {
+ cm.closeAllChannels();
+ }
+ if(tm != null) {
+ tm.close(t);
+ tm = null;
+ }
+ am = null;
+ cm = null;
+ authenticated = false;
+ }
+
+ /**
+ * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
+ *
+ * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
+ * @throws IOException
+ */
+ public synchronized ConnectionInfo connect() throws IOException {
+ return connect(null, 0, 0);
+ }
+
+ /**
+ * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
+ *
+ * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
+ * @throws IOException
+ */
+ public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException {
+ return connect(verifier, 0, 0);
+ }
+
+ /**
+ * Connect to the SSH-2 server and, as soon as the server has presented its
+ * host key, use the {@link ServerHostKeyVerifier#verifyServerHostKey(String,
+ * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()}
+ * method of the verifier
to ask for permission to proceed.
+ * If verifier
is null
, then any host key will be
+ * accepted - this is NOT recommended, since it makes man-in-the-middle attackes
+ * VERY easy (somebody could put a proxy SSH server between you and the real server).
+ *
+ * Note: The verifier will be called before doing any crypto calculations
+ * (i.e., diffie-hellman). Therefore, if you don't like the presented host key then
+ * no CPU cycles are wasted (and the evil server has less information about us).
+ *
+ * However, it is still possible that the server presented a fake host key: the server
+ * cheated (typically a sign for a man-in-the-middle attack) and is not able to generate
+ * a signature that matches its host key. Don't worry, the library will detect such
+ * a scenario later when checking the signature (the signature cannot be checked before
+ * having completed the diffie-hellman exchange).
+ *
+ * Note 2: The {@link ServerHostKeyVerifier#verifyServerHostKey(String,
+ * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method
+ * will *NOT* be called from the current thread, the call is being made from a
+ * background thread (there is a background dispatcher thread for every
+ * established connection).
+ *
+ * Note 3: This method will block as long as the key exchange of the underlying connection
+ * has not been completed (and you have not specified any timeouts).
+ *
+ * Note 4: If you want to re-use a connection object that was successfully connected,
+ * then you must call the {@link #close()} method before invoking connect()
again.
+ *
+ * @param verifier An object that implements the
+ * {@link ServerHostKeyVerifier} interface. Pass null
+ * to accept any server host key - NOT recommended.
+ * @param connectTimeout Connect the underlying TCP socket to the server with the given timeout
+ * value (non-negative, in milliseconds). Zero means no timeout.
+ * @param kexTimeout Timeout for complete connection establishment (non-negative,
+ * in milliseconds). Zero means no timeout. The timeout counts from the
+ * moment you invoke the connect() method and is cancelled as soon as the
+ * first key-exchange round has finished. It is possible that
+ * the timeout event will be fired during the invocation of the
+ * verifier
callback, but it will only have an effect after
+ * the verifier
returns.
+ * @return A {@link ConnectionInfo} object containing the details of
+ * the established connection.
+ * @throws IOException If any problem occurs, e.g., the server's host key is not
+ * accepted by the verifier
or there is problem during
+ * the initial crypto setup (e.g., the signature sent by the server is wrong).
+ *
+ * In case of a timeout (either connectTimeout or kexTimeout)
+ * a SocketTimeoutException is thrown.
+ *
+ * An exception may also be thrown if the connection was already successfully
+ * connected (no matter if the connection broke in the mean time) and you invoke
+ * connect()
again without having called {@link #close()} first.
+ *
+ * If a HTTP proxy is being used and the proxy refuses the connection,
+ * then a {@link HTTPProxyException} may be thrown, which
+ * contains the details returned by the proxy. If the proxy is buggy and does
+ * not return a proper HTTP response, then a normal IOException is thrown instead.
+ */
+ public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
+ throws IOException {
+ final class TimeoutState {
+ boolean isCancelled = false;
+ boolean timeoutSocketClosed = false;
+ }
+
+ if(tm != null) {
+ throw new IllegalStateException(String.format("Connection to %s is already in connected state", hostname));
+ }
+
+ if(connectTimeout < 0) {
+ throw new IllegalArgumentException("connectTimeout must be non-negative!");
+ }
+
+ if(kexTimeout < 0) {
+ throw new IllegalArgumentException("kexTimeout must be non-negative!");
+ }
+
+ final TimeoutState state = new TimeoutState();
+
+ if(null == proxy) {
+ tm = new ClientTransportManager(new Socket());
+ }
+ else {
+ tm = new HTTPProxyClientTransportManager(new Socket(), proxy);
+ }
+ tm.setSoTimeout(connectTimeout);
+ tm.setTcpNoDelay(tcpNoDelay);
+ tm.setConnectionMonitors(connectionMonitors);
+
+ try {
+ TimeoutToken token = null;
+
+ if(kexTimeout > 0) {
+ final Runnable timeoutHandler = new Runnable() {
+ @Override
+ public void run() {
+ synchronized(state) {
+ if(state.isCancelled) {
+ return;
+ }
+ state.timeoutSocketClosed = true;
+ tm.close(new SocketTimeoutException("The connect timeout expired"));
+ }
+ }
+ };
+
+ long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
+
+ token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
+ }
+
+ tm.connect(hostname, port, softwareversion, cryptoWishList, verifier, dhgexpara, connectTimeout,
+ getOrCreateSecureRND());
+
+ /* Wait until first KEX has finished */
+
+ ConnectionInfo ci = tm.getConnectionInfo(1);
+
+ /* Now try to cancel the timeout, if needed */
+
+ if(token != null) {
+ TimeoutService.cancelTimeoutHandler(token);
+
+ /* Were we too late? */
+
+ synchronized(state) {
+ if(state.timeoutSocketClosed) {
+ throw new IOException("This exception will be replaced by the one below =)");
+ }
+ /* Just in case the "cancelTimeoutHandler" invocation came just a little bit
+ * too late but the handler did not enter the semaphore yet - we can
+ * still stop it.
+ */
+ state.isCancelled = true;
+ }
+ }
+
+ return ci;
+ }
+ catch(SocketTimeoutException e) {
+ throw e;
+ }
+ catch(HTTPProxyException e) {
+ throw e;
+ }
+ catch(IOException e) {
+ // This will also invoke any registered connection monitors
+ close(e);
+
+ synchronized(state) {
+ /* Show a clean exception, not something like "the socket is closed!?!" */
+ if(state.timeoutSocketClosed) {
+ throw new SocketTimeoutException(String.format("The kexTimeout (%d ms) expired.", kexTimeout));
+ }
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Creates a new {@link LocalPortForwarder}.
+ * A LocalPortForwarder
forwards TCP/IP connections that arrive at a local
+ * port via the secure tunnel to another host (which may or may not be
+ * identical to the remote SSH-2 server).
+ *
+ * This method must only be called after one has passed successfully the authentication step.
+ * There is no limit on the number of concurrent forwardings.
+ *
+ * @param local_port the local port the LocalPortForwarder shall bind to.
+ * @param host_to_connect target address (IP or hostname)
+ * @param port_to_connect target port
+ * @return A {@link LocalPortForwarder} object.
+ * @throws IOException
+ */
+ public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
+ int port_to_connect) throws IOException {
+ this.checkConnection();
+
+ return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
+ }
+
+ /**
+ * Creates a new {@link LocalPortForwarder}.
+ * A LocalPortForwarder
forwards TCP/IP connections that arrive at a local
+ * port via the secure tunnel to another host (which may or may not be
+ * identical to the remote SSH-2 server).
+ *
+ * This method must only be called after one has passed successfully the authentication step.
+ * There is no limit on the number of concurrent forwardings.
+ *
+ * @param addr specifies the InetSocketAddress where the local socket shall be bound to.
+ * @param host_to_connect target address (IP or hostname)
+ * @param port_to_connect target port
+ * @return A {@link LocalPortForwarder} object.
+ * @throws IOException
+ */
+ public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect,
+ int port_to_connect) throws IOException {
+ this.checkConnection();
+
+ return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect);
+ }
+
+ /**
+ * Creates a new {@link LocalStreamForwarder}.
+ * A LocalStreamForwarder
manages an Input/Outputstream pair
+ * that is being forwarded via the secure tunnel into a TCP/IP connection to another host
+ * (which may or may not be identical to the remote SSH-2 server).
+ *
+ * @param host_to_connect
+ * @param port_to_connect
+ * @return A {@link LocalStreamForwarder} object.
+ * @throws IOException
+ */
+ public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
+ throws IOException {
+ this.checkConnection();
+
+ return new LocalStreamForwarder(cm, host_to_connect, port_to_connect);
+ }
+
+ /**
+ * Create a very basic {@link SCPClient} that can be used to copy
+ * files from/to the SSH-2 server.
+ *
+ * Works only after one has passed successfully the authentication step.
+ * There is no limit on the number of concurrent SCP clients.
+ *
+ * Note: This factory method will probably disappear in the future.
+ *
+ * @return A {@link SCPClient} object.
+ * @throws IOException
+ */
+ public synchronized SCPClient createSCPClient() throws IOException {
+ this.checkConnection();
+
+ return new SCPClient(this);
+ }
+
+ /**
+ * Force an asynchronous key re-exchange (the call does not block). The
+ * latest values set for MAC, Cipher and DH group exchange parameters will
+ * be used. If a key exchange is currently in progress, then this method has
+ * the only effect that the so far specified parameters will be used for the
+ * next (server driven) key exchange.
+ *
+ * Note: This implementation will never start a key exchange (other than the initial one)
+ * unless you or the SSH-2 server ask for it.
+ *
+ * @throws IOException In case of any failure behind the scenes.
+ */
+ public synchronized void forceKeyExchange() throws IOException {
+ this.checkConnection();
+
+ tm.forceKeyExchange(cryptoWishList, dhgexpara, null, null);
+ }
+
+ /**
+ * Returns the hostname that was passed to the constructor.
+ *
+ * @return the hostname
+ */
+ public synchronized String getHostname() {
+ return hostname;
+ }
+
+ /**
+ * Returns the port that was passed to the constructor.
+ *
+ * @return the TCP port
+ */
+ public synchronized int getPort() {
+ return port;
+ }
+
+ /**
+ * Returns a {@link ConnectionInfo} object containing the details of
+ * the connection. Can be called as soon as the connection has been
+ * established (successfully connected).
+ *
+ * @return A {@link ConnectionInfo} object.
+ * @throws IOException In case of any failure behind the scenes.
+ */
+ public synchronized ConnectionInfo getConnectionInfo() throws IOException {
+ this.checkConnection();
+
+ return tm.getConnectionInfo(1);
+ }
+
+ /**
+ * After a successful connect, one has to authenticate oneself. This method
+ * can be used to tell which authentication methods are supported by the
+ * server at a certain stage of the authentication process (for the given
+ * username).
+ *
+ * Note 1: the username will only be used if no authentication step was done
+ * so far (it will be used to ask the server for a list of possible
+ * authentication methods by sending the initial "none" request). Otherwise,
+ * this method ignores the user name and returns a cached method list
+ * (which is based on the information contained in the last negative server response).
+ *
+ * Note 2: the server may return method names that are not supported by this
+ * implementation.
+ *
+ * After a successful authentication, this method must not be called
+ * anymore.
+ *
+ * @param user A String
holding the username.
+ * @return a (possibly emtpy) array holding authentication method names.
+ * @throws IOException
+ */
+ public synchronized String[] getRemainingAuthMethods(String user) throws IOException {
+ if(user == null) {
+ throw new IllegalArgumentException("user argument may not be NULL!");
+ }
+
+ if(tm == null) {
+ throw new IllegalStateException("Connection is not established!");
+ }
+
+ if(authenticated) {
+ throw new IllegalStateException("Connection is already authenticated!");
+ }
+
+ if(am == null) {
+ am = new AuthenticationManager(tm);
+ }
+
+ if(cm == null) {
+ cm = new ChannelManager(tm);
+ }
+
+ final Settrue
if no further authentication steps are
+ * needed.
+ */
+ public synchronized boolean isAuthenticationComplete() {
+ return authenticated;
+ }
+
+ /**
+ * Returns true if there was at least one failed authentication request and
+ * the last failed authentication request was marked with "partial success"
+ * by the server. This is only needed in the rare case of SSH-2 server setups
+ * that cannot be satisfied with a single successful authentication request
+ * (i.e., multiple authentication steps are needed.)
+ *
+ * If you are interested in the details, then have a look at RFC4252.
+ *
+ * @return if the there was a failed authentication step and the last one
+ * was marked as a "partial success".
+ */
+ public synchronized boolean isAuthenticationPartialSuccess() {
+ if(am == null) {
+ return false;
+ }
+ return am.getPartialSuccess();
+ }
+
+ /**
+ * Checks if a specified authentication method is available. This method is
+ * actually just a wrapper for {@link #getRemainingAuthMethods(String)
+ * getRemainingAuthMethods()}.
+ *
+ * @param user A String
holding the username.
+ * @param method An authentication method name (e.g., "publickey", "password",
+ * "keyboard-interactive") as specified by the SSH-2 standard.
+ * @return if the specified authentication method is currently available.
+ * @throws IOException
+ */
+ public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException {
+ String methods[] = getRemainingAuthMethods(user);
+ for(final String m : methods) {
+ if(m.compareTo(method) == 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private SecureRandom getOrCreateSecureRND() {
+ if(generator == null) {
+ generator = new SecureRandom();
+ }
+ return generator;
+ }
+
+ /**
+ * Open a new {@link Session} on this connection. Works only after one has passed
+ * successfully the authentication step. There is no limit on the number of
+ * concurrent sessions.
+ *
+ * @return A {@link Session} object.
+ * @throws IOException
+ */
+ public synchronized Session openSession() throws IOException {
+ this.checkConnection();
+
+ return new Session(cm, getOrCreateSecureRND());
+ }
+
+ /**
+ * Send an SSH_MSG_IGNORE packet. This method will generate a random data attribute
+ * (length between 0 (invlusive) and 16 (exclusive) bytes, contents are random bytes).
+ *
+ * This method must only be called once the connection is established.
+ *
+ * @throws IOException
+ */
+ public synchronized void sendIgnorePacket() throws IOException {
+ SecureRandom rnd = getOrCreateSecureRND();
+
+ byte[] data = new byte[rnd.nextInt(16)];
+ rnd.nextBytes(data);
+
+ sendIgnorePacket(data);
+ }
+
+ /**
+ * Send an SSH_MSG_IGNORE packet with the given data attribute.
+ *
+ * This method must only be called once the connection is established.
+ *
+ * @throws IOException
+ */
+ public synchronized void sendIgnorePacket(byte[] data) throws IOException {
+ this.checkConnection();
+
+ PacketIgnore pi = new PacketIgnore(data);
+
+ tm.sendMessage(pi.getPayload());
+ }
+
+ /**
+ * Controls whether compression is used on the link or not.
+ */
+ public synchronized void setCompression(String[] algorithms) {
+ CompressionFactory.checkCompressorList(algorithms);
+ cryptoWishList.c2s_comp_algos = algorithms;
+ }
+
+ public synchronized void enableCompression() {
+ cryptoWishList.c2s_comp_algos = CompressionFactory.getDefaultCompressorList();
+ cryptoWishList.s2c_comp_algos = CompressionFactory.getDefaultCompressorList();
+ }
+
+ public synchronized void disableCompression() {
+ cryptoWishList.c2s_comp_algos = new String[]{"none"};
+ cryptoWishList.s2c_comp_algos = new String[]{"none"};
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ */
+ public synchronized void setClient2ServerCiphers(final String[] ciphers) {
+ if((ciphers == null) || (ciphers.length == 0)) {
+ throw new IllegalArgumentException();
+ }
+ BlockCipherFactory.checkCipherList(ciphers);
+ cryptoWishList.c2s_enc_algos = ciphers;
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ */
+ public synchronized void setClient2ServerMACs(final String[] macs) {
+ MAC.checkMacList(macs);
+ cryptoWishList.c2s_mac_algos = macs;
+ }
+
+ /**
+ * Sets the parameters for the diffie-hellman group exchange. Unless you
+ * know what you are doing, you will never need this. Default values are
+ * defined in the {@link DHGexParameters} class.
+ *
+ * @param dgp {@link DHGexParameters}, non null.
+ */
+ public synchronized void setDHGexParameters(DHGexParameters dgp) {
+ if(dgp == null) {
+ throw new IllegalArgumentException();
+ }
+
+ dhgexpara = dgp;
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ */
+ public synchronized void setServer2ClientCiphers(final String[] ciphers) {
+ BlockCipherFactory.checkCipherList(ciphers);
+ cryptoWishList.s2c_enc_algos = ciphers;
+ }
+
+ /**
+ * Unless you know what you are doing, you will never need this.
+ */
+ public synchronized void setServer2ClientMACs(final String[] macs) {
+ MAC.checkMacList(macs);
+ cryptoWishList.s2c_mac_algos = macs;
+ }
+
+ /**
+ * Define the set of allowed server host key algorithms to be used for
+ * the following key exchange operations.
+ *
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @param algos An array of allowed server host key algorithms.
+ * SSH-2 defines ssh-dss
and ssh-rsa
.
+ * The entries of the array must be ordered after preference, i.e.,
+ * the entry at index 0 is the most preferred one. You must specify
+ * at least one entry.
+ */
+ public synchronized void setServerHostKeyAlgorithms(final String[] algos) {
+ KexManager.checkServerHostkeyAlgorithmsList(algos);
+ cryptoWishList.serverHostKeyAlgorithms = algos;
+ }
+
+ /**
+ * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the underlying socket.
+ *
+ * Can be called at any time. If the connection has not yet been established
+ * then the passed value will be stored and set after the socket has been set up.
+ * The default value that will be used is false
.
+ *
+ * @param enable the argument passed to the Socket.setTCPNoDelay()
method.
+ * @throws IOException
+ */
+ public synchronized void setTCPNoDelay(boolean enable) throws IOException {
+ tcpNoDelay = enable;
+ if(tm != null) {
+ tm.setTcpNoDelay(enable);
+ }
+ }
+
+ /**
+ * Request a remote port forwarding.
+ * If successful, then forwarded connections will be redirected to the given target address.
+ * You can cancle a requested remote port forwarding by calling
+ * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
+ *
+ * A call of this method will block until the peer either agreed or disagreed to your request-
+ *
+ * Note 1: this method typically fails if you
+ *
+ *
+ *
+ * Note 2: (from the openssh man page): By default, the listening socket on the server will be
+ * bound to the loopback interface only. This may be overriden by specifying a bind address.
+ * Specifying a remote bind address will only succeed if the server's GatewayPorts option
+ * is enabled (see sshd_config(5)).
+ *
+ * @param bindAddress address to bind to on the server:
+ *
+ *
+ * @param bindPort port number to bind on the server (must be > 0)
+ * @param targetAddress the target address (IP or hostname)
+ * @param targetPort the target port
+ * @throws IOException
+ */
+ public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
+ int targetPort) throws IOException {
+ this.checkConnection();
+
+ if((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0)) {
+ throw new IllegalArgumentException();
+ }
+
+ cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
+ }
+
+ /**
+ * Cancel an earlier requested remote port forwarding.
+ * Currently active forwardings will not be affected (e.g., disrupted).
+ * Note that further connection forwarding requests may be received until
+ * this method has returned.
+ *
+ * @param bindPort the allocated port number on the server
+ * @throws IOException if the remote side refuses the cancel request or another low
+ * level error occurs (e.g., the underlying connection is closed)
+ */
+ public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException {
+ this.checkConnection();
+
+ cm.requestCancelGlobalForward(bindPort);
+ }
+
+ /**
+ * Provide your own instance of SecureRandom. Can be used, e.g., if you
+ * want to seed the used SecureRandom generator manually.
+ *
+ * The SecureRandom instance is used during key exchanges, public key authentication,
+ * x11 cookie generation and the like.
+ *
+ * @param rnd a SecureRandom instance
+ */
+ public synchronized void setSecureRandom(SecureRandom rnd) {
+ if(rnd == null) {
+ throw new IllegalArgumentException();
+ }
+ this.generator = rnd;
+ }
+
+ private void checkConnection() throws IllegalStateException {
+ if(tm == null) {
+ throw new IllegalStateException("You need to establish a connection first.");
+ }
+ if(!authenticated) {
+ throw new IllegalStateException("The connection is not authenticated.");
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/ConnectionInfo.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ConnectionInfo.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * In most cases you probably do not need the information contained in here.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class ConnectionInfo
+{
+ /**
+ * The used key exchange (KEX) algorithm in the latest key exchange.
+ */
+ public String keyExchangeAlgorithm;
+
+ /**
+ * The currently used crypto algorithm for packets from to the client to the
+ * server.
+ */
+ public String clientToServerCryptoAlgorithm;
+ /**
+ * The currently used crypto algorithm for packets from to the server to the
+ * client.
+ */
+ public String serverToClientCryptoAlgorithm;
+
+ /**
+ * The currently used MAC algorithm for packets from to the client to the
+ * server.
+ */
+ public String clientToServerMACAlgorithm;
+ /**
+ * The currently used MAC algorithm for packets from to the server to the
+ * client.
+ */
+ public String serverToClientMACAlgorithm;
+
+ /**
+ * The type of the server host key (currently either "ssh-dss" or
+ * "ssh-rsa").
+ */
+ public String serverHostKeyAlgorithm;
+
+ /**
+ * The server host key that was sent during the latest key exchange.
+ */
+ public byte[] serverHostKey;
+
+ /**
+ * Number of kex exchanges performed on this connection so far.
+ */
+ public int keyExchangeCounter = 0;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/ConnectionMonitor.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ConnectionMonitor.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * A ConnectionMonitor
is used to get notified when the
+ * underlying socket of a connection is closed.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public interface ConnectionMonitor
+{
+ /**
+ * This method is called after the connection's underlying
+ * socket has been closed. E.g., due to the {@link Connection#close()} request of the
+ * user, if the peer closed the connection, due to a fatal error during connect()
+ * (also if the socket cannot be established) or if a fatal error occured on
+ * an established connection.
+ * DHGexParameters
object can be used to specify parameters for
+ * the diffie-hellman group exchange.
+ * SSH_MSG_KEX_DH_GEX_REQUEST
or SSH_MSG_KEX_DH_GEX_REQUEST_OLD
+ * can be forced.
+ *
+ * @see Connection#setDHGexParameters(DHGexParameters)
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class DHGexParameters
+{
+ private final int min_group_len;
+ private final int pref_group_len;
+ private final int max_group_len;
+
+ private static final int MIN_ALLOWED = 1024;
+ private static final int MAX_ALLOWED = 8192;
+
+ /**
+ * Same as calling {@link #DHGexParameters(int, int, int) DHGexParameters(1024, 1024, 4096)}.
+ * This is also the default used by the Connection class.
+ *
+ */
+ public DHGexParameters()
+ {
+ this(1024, 1024, 4096);
+ }
+
+ /**
+ * This constructor can be used to force the sending of a
+ * SSH_MSG_KEX_DH_GEX_REQUEST_OLD
request.
+ * Internally, the minimum and maximum group lengths will
+ * be set to zero.
+ *
+ * @param pref_group_len has to be >= 1024 and <= 8192
+ */
+ public DHGexParameters(int pref_group_len)
+ {
+ if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
+ throw new IllegalArgumentException("pref_group_len out of range!");
+
+ this.pref_group_len = pref_group_len;
+ this.min_group_len = 0;
+ this.max_group_len = 0;
+ }
+
+ /**
+ * This constructor can be used to force the sending of a
+ * SSH_MSG_KEX_DH_GEX_REQUEST
request.
+ * zero
if
+ * SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested
+ */
+ public int getMax_group_len()
+ {
+ return max_group_len;
+ }
+
+ /**
+ * Get the minimum group length.
+ *
+ * @return minimum group length, may be zero
if
+ * SSH_MSG_KEX_DH_GEX_REQUEST_OLD should be requested
+ */
+ public int getMin_group_len()
+ {
+ return min_group_len;
+ }
+
+ /**
+ * Get the preferred group length.
+ *
+ * @return the preferred group length
+ */
+ public int getPref_group_len()
+ {
+ return pref_group_len;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/HTTPProxyData.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/HTTPProxyData.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * A HTTPProxyData
object is used to specify the needed connection data
+ * to connect through a HTTP proxy.
+ *
+ * @see Connection#setProxyData(ProxyData)
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class HTTPProxyData implements ProxyData
+{
+ public final String proxyHost;
+ public final int proxyPort;
+ public final String proxyUser;
+ public final String proxyPass;
+ public final String[] requestHeaderLines;
+
+ /**
+ * Same as calling {@link #HTTPProxyData(String, int, String, String) HTTPProxyData(proxyHost, proxyPort, null
, null
)}
+ *
+ * @param proxyHost Proxy hostname.
+ * @param proxyPort Proxy port.
+ */
+ public HTTPProxyData(String proxyHost, int proxyPort)
+ {
+ this(proxyHost, proxyPort, null, null);
+ }
+
+ /**
+ * Same as calling {@link #HTTPProxyData(String, int, String, String, String[]) HTTPProxyData(proxyHost, proxyPort, null
, null
, null
)}
+ *
+ * @param proxyHost Proxy hostname.
+ * @param proxyPort Proxy port.
+ * @param proxyUser Username for basic authentication (null
if no authentication is needed).
+ * @param proxyPass Password for basic authentication (null
if no authentication is needed).
+ */
+ public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass)
+ {
+ this(proxyHost, proxyPort, proxyUser, proxyPass, null);
+ }
+
+ /**
+ * Connection data for a HTTP proxy. It is possible to specify a username and password
+ * if the proxy requires basic authentication. Also, additional request header lines can
+ * be specified (e.g., "User-Agent: CERN-LineMode/2.15 libwww/2.17b3").
+ * proxyUser
+ * and proxyPass
must be non-null.
+ *
+ * new HTTPProxyData("192.168.1.1", "3128", "proxyuser", "secret", new String[] {"User-Agent: GanymedBasedClient/1.0", "X-My-Proxy-Option: something"});
+ *
+ *
+ * @param proxyHost Proxy hostname.
+ * @param proxyPort Proxy port.
+ * @param proxyUser Username for basic authentication (null
if no authentication is needed).
+ * @param proxyPass Password for basic authentication (null
if no authentication is needed).
+ * @param requestHeaderLines An array with additional request header lines (without end-of-line markers)
+ * that have to be sent to the server. May be null
.
+ */
+
+ public HTTPProxyData(String proxyHost, int proxyPort, String proxyUser, String proxyPass,
+ String[] requestHeaderLines)
+ {
+ if (proxyHost == null)
+ throw new IllegalArgumentException("proxyHost must be non-null");
+
+ if (proxyPort < 0)
+ throw new IllegalArgumentException("proxyPort must be non-negative");
+
+ this.proxyHost = proxyHost;
+ this.proxyPort = proxyPort;
+ this.proxyUser = proxyUser;
+ this.proxyPass = proxyPass;
+ this.requestHeaderLines = requestHeaderLines;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/HTTPProxyException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/HTTPProxyException.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+/**
+ * May be thrown upon connect() if a HTTP proxy is being used.
+ *
+ * @see Connection#connect()
+ * @see Connection#setProxyData(ProxyData)
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class HTTPProxyException extends IOException
+{
+ private static final long serialVersionUID = 2241537397104426186L;
+
+ public final String httpResponse;
+ public final int httpErrorCode;
+
+ public HTTPProxyException(String httpResponse, int httpErrorCode)
+ {
+ super("HTTP Proxy Error (" + httpErrorCode + " " + httpResponse + ")");
+ this.httpResponse = httpResponse;
+ this.httpErrorCode = httpErrorCode;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/InteractiveCallback.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/InteractiveCallback.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+/**
+ * An InteractiveCallback
is used to respond to challenges sent
+ * by the server if authentication mode "keyboard-interactive" is selected.
+ *
+ * @see Connection#authenticateWithKeyboardInteractive(String,
+ * String[], InteractiveCallback)
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public interface InteractiveCallback
+{
+ /**
+ * This callback interface is used during a "keyboard-interactive"
+ * authentication. Every time the server sends a set of challenges (however,
+ * most often just one challenge at a time), this callback function will be
+ * called to give your application a chance to talk to the user and to
+ * determine the response(s).
+ * numPrompts
) of Strings
+ * @param echo
+ * an array (length numPrompts
) of booleans. For
+ * each prompt, the corresponding echo field indicates whether or
+ * not the user input should be echoed as characters are typed.
+ * @return an array of reponses - the array size must match the parameter
+ * numPrompts
.
+ */
+ public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo)
+ throws Exception;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/KnownHosts.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/KnownHosts.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,837 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+import java.io.BufferedReader;
+import java.io.CharArrayReader;
+import java.io.CharArrayWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.DigestException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import ch.ethz.ssh2.crypto.Base64;
+import ch.ethz.ssh2.crypto.digest.Digest;
+import ch.ethz.ssh2.crypto.digest.HMAC;
+import ch.ethz.ssh2.crypto.digest.MD5;
+import ch.ethz.ssh2.crypto.digest.SHA1;
+import ch.ethz.ssh2.signature.DSAPublicKey;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.RSAPublicKey;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * The KnownHosts
class is a handy tool to verify received server hostkeys
+ * based on the information in known_hosts
files (the ones used by OpenSSH).
+ * known_hosts
file can be loaded at construction time.
+ * It is also possible to add more keys later (e.g., one can parse different
+ * known_hosts
files).
+ *
+ * It is a thread safe implementation, therefore, you need only to instantiate one
+ *
KnownHosts
for your whole application.
+ *
+ * @author Christian Plattner
+ * @version $Id: KnownHosts.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $
+ */
+
+public class KnownHosts {
+ public static final int HOSTKEY_IS_OK = 0;
+ public static final int HOSTKEY_IS_NEW = 1;
+ public static final int HOSTKEY_HAS_CHANGED = 2;
+
+ private class KnownHostsEntry {
+ String[] patterns;
+ Object key;
+
+ KnownHostsEntry(String[] patterns, Object key) {
+ this.patterns = patterns;
+ this.key = key;
+ }
+ }
+
+ private final LinkedListaddHostkeyToFile()
for that purpose.
+ * This method is designed to be used in a {@link ServerHostKeyVerifier}.
+ *
+ * @param hostnames a list of hostname patterns - at least one most be specified. Check out the
+ * OpenSSH sshd man page for a description of the pattern matching algorithm.
+ * @param serverHostKeyAlgorithm as passed to the {@link ServerHostKeyVerifier}.
+ * @param serverHostKey as passed to the {@link ServerHostKeyVerifier}.
+ * @throws IOException
+ */
+ public void addHostkey(String hostnames[], String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException {
+ if(hostnames == null) {
+ throw new IllegalArgumentException("hostnames may not be null");
+ }
+
+ if("ssh-rsa".equals(serverHostKeyAlgorithm)) {
+ RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
+
+ synchronized(publicKeys) {
+ publicKeys.add(new KnownHostsEntry(hostnames, rpk));
+ }
+ }
+ else if("ssh-dss".equals(serverHostKeyAlgorithm)) {
+ DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
+
+ synchronized(publicKeys) {
+ publicKeys.add(new KnownHostsEntry(hostnames, dpk));
+ }
+ }
+ else {
+ throw new IOException(String.format("Unknown host key type %s", serverHostKeyAlgorithm));
+ }
+ }
+
+ /**
+ * Parses the given known_hosts data and adds entries to the database.
+ *
+ * @param knownHostsData
+ * @throws IOException
+ */
+ public void addHostkeys(char[] knownHostsData) throws IOException {
+ initialize(knownHostsData);
+ }
+
+ /**
+ * Parses the given known_hosts file and adds entries to the database.
+ *
+ * @param knownHosts
+ * @throws IOException
+ */
+ public void addHostkeys(File knownHosts) throws IOException {
+ initialize(knownHosts);
+ }
+
+ /**
+ * Generate the hashed representation of the given hostname. Useful for adding entries
+ * with hashed hostnames to a known_hosts file. (see -H option of OpenSSH key-gen).
+ *
+ * @param hostname
+ * @return the hashed representation, e.g., "|1|cDhrv7zwEUV3k71CEPHnhHZezhA=|Xo+2y6rUXo2OIWRAYhBOIijbJMA="
+ */
+ public static String createHashedHostname(String hostname) throws IOException {
+ SHA1 sha1 = new SHA1();
+
+ byte[] salt = new byte[sha1.getDigestLength()];
+
+ new SecureRandom().nextBytes(salt);
+
+ byte[] hash;
+ try {
+ hash = hmacSha1Hash(salt, hostname);
+ }
+ catch(IOException e) {
+ throw new IOException(e);
+ }
+
+ String base64_salt = new String(Base64.encode(salt));
+ String base64_hash = new String(Base64.encode(hash));
+
+ return String.format("|1|%s|%s", base64_salt, base64_hash);
+ }
+
+ private static byte[] hmacSha1Hash(byte[] salt, String hostname) throws IOException {
+ SHA1 sha1 = new SHA1();
+
+ if(salt.length != sha1.getDigestLength()) {
+ throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")");
+ }
+ try {
+ HMAC hmac = new HMAC(sha1, salt, salt.length);
+
+ hmac.update(StringEncoder.GetBytes(hostname));
+
+ byte[] dig = new byte[hmac.getDigestLength()];
+
+ hmac.digest(dig);
+
+ return dig;
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private boolean checkHashed(String entry, String hostname) {
+ if(entry.startsWith("|1|") == false) {
+ return false;
+ }
+
+ int delim_idx = entry.indexOf('|', 3);
+
+ if(delim_idx == -1) {
+ return false;
+ }
+
+ String salt_base64 = entry.substring(3, delim_idx);
+ String hash_base64 = entry.substring(delim_idx + 1);
+
+ byte[] salt;
+ byte[] hash;
+
+ try {
+ salt = Base64.decode(salt_base64.toCharArray());
+ hash = Base64.decode(hash_base64.toCharArray());
+ }
+ catch(IOException e) {
+ return false;
+ }
+
+ SHA1 sha1 = new SHA1();
+
+ if(salt.length != sha1.getDigestLength()) {
+ return false;
+ }
+
+ byte[] dig = new byte[0];
+ try {
+ dig = hmacSha1Hash(salt, hostname);
+ }
+ catch(IOException e) {
+ return false;
+ }
+
+ for(int i = 0; i < dig.length; i++) {
+ if(dig[i] != hash[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private int checkKey(String remoteHostname, Object remoteKey) {
+ int result = HOSTKEY_IS_NEW;
+
+ synchronized(publicKeys) {
+ for(KnownHostsEntry ke : publicKeys) {
+ if(hostnameMatches(ke.patterns, remoteHostname) == false) {
+ continue;
+ }
+
+ boolean res = matchKeys(ke.key, remoteKey);
+
+ if(res == true) {
+ return HOSTKEY_IS_OK;
+ }
+
+ result = HOSTKEY_HAS_CHANGED;
+ }
+ }
+ return result;
+ }
+
+ private List
ServerConnection
that will communicate
+ * with the client over the given Socket
.
+ *
+ * Note: you need to call {@link #connect()} or {@link #connect(int)} to
+ * perform the initial handshake and establish the encrypted communication.
+ *
+ * @see #connect(int)
+ *
+ * @param s The socket
+ */
+ public ServerConnection(Socket s)
+ {
+ this(s, null, null);
+ }
+
+ public ServerConnection(Socket s, String softwareversion) {
+ this(s, null, null);
+ this.softwareversion = softwareversion;
+ }
+
+ /**
+ * Creates a new ServerConnection
that will communicate
+ * with the client over the given Socket
.
+ *
+ * Note: you need to call {@link #connect()} or {@link #connect(int)} to + * perform the initial handshake and establish the encrypted communication. + *
+ * Please read the javadoc for the {@link #connect(int)} method.
+ *
+ * @see #connect(int)
+ *
+ * @param s The socket
+ * @param dsa_key The DSA hostkey, may be NULL
+ * @param rsa_key The RSA hostkey, may be NULL
+ */
+ public ServerConnection(Socket s, DSAPrivateKey dsa_key, RSAPrivateKey rsa_key)
+ {
+ state.s = s;
+ state.softwareversion = softwareversion;
+ state.next_dsa_key = dsa_key;
+ state.next_rsa_key = rsa_key;
+ fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key);
+ }
+
+ /**
+ * Establish the connection and block until the first handshake has completed.
+ *
+ * Note: this is a wrapper that calls connect(0)
(i.e., connect with no timeout).
+ *
+ * Please read the javadoc for the {@link #connect(int)} method. + * + * @see #connect(int) + * + * @throws IOException + */ + public synchronized void connect() throws IOException + { + connect(0); + } + + /** + * Establish the connection and block until the first handshake has completed. + *
+ * Note 1: either a DSA or a RSA (or both) hostkey must be set before calling this method. + *
+ * Note 2: You must set the callbacks for authentication ({@link #setAuthenticationCallback(ServerAuthenticationCallback)})
+ * and connection events ({@link #setServerConnectionCallback(ServerConnectionCallback)}).
+ *
+ * @see #setPEMHostKey(char[], String)
+ * @see #setPEMHostKey(File, String)
+ * @see #setRsaHostKey(RSAPrivateKey)
+ * @see #setDsaHostKey(DSAPrivateKey)
+ *
+ * @param timeout_milliseconds Timeout in milliseconds, 0
means no timeout.
+ * @throws IOException
+ */
+ public synchronized void connect(int timeout_milliseconds) throws IOException
+ {
+ synchronized (state)
+ {
+ if (state.cb_conn == null)
+ throw new IllegalStateException("The callback for connection events has not been set.");
+
+ if (state.cb_auth == null)
+ throw new IllegalStateException("The callback for authentication events has not been set.");
+
+ if (state.tm != null)
+ throw new IllegalStateException("The initial handshake has already been started.");
+
+ if ((state.next_dsa_key == null) && (state.next_rsa_key == null))
+ throw new IllegalStateException("Neither a RSA nor a DSA host key has been specified!");
+
+ state.tm = new ServerTransportManager(state.s);
+ }
+
+ state.tm.connect(state);
+
+ /* Wait until first KEX has finished */
+
+ state.tm.getConnectionInfo(1);
+ }
+
+ /**
+ * Retrieve the underlying socket.
+ *
+ * @return the socket that has been passed to the constructor.
+ */
+ public Socket getSocket()
+ {
+ return state.s;
+ }
+
+ /**
+ * Force an asynchronous key re-exchange (the call does not block). The
+ * latest values set for MAC, Cipher and DH group exchange parameters will
+ * be used. If a key exchange is currently in progress, then this method has
+ * the only effect that the so far specified parameters will be used for the
+ * next (client driven) key exchange. You may call this method only after
+ * the initial key exchange has been established.
+ *
+ * Note: This implementation will never start automatically a key exchange (other than the initial one) + * unless you or the connected SSH-2 client ask for it. + * + * @throws IOException + * In case of any failure behind the scenes. + */ + public synchronized void forceKeyExchange() throws IOException + { + synchronized (state) + { + if (state.tm == null) + throw new IllegalStateException( + "Cannot force another key exchange, you need to start the key exchange first."); + + state.tm.forceKeyExchange(state.next_cryptoWishList, null, state.next_dsa_key, state.next_rsa_key); + } + } + + /** + * Returns a {@link ConnectionInfo} object containing the details of + * the connection. May be called as soon as the first key exchange has been + * started. The method blocks in case the first key exchange has not been completed. + *
+ * Note: upon return of this method, authentication may still be pending. + * + * @return A {@link ConnectionInfo} object. + * @throws IOException + * In case of any failure behind the scenes; e.g., first key exchange was aborted. + */ + public synchronized ConnectionInfo getConnectionInfo() throws IOException + { + synchronized (state) + { + if (state.tm == null) + throw new IllegalStateException( + "Cannot get details of connection, you need to start the key exchange first."); + } + + return state.tm.getConnectionInfo(1); + } + + /** + * Change the current DSA hostkey. Either a DSA or RSA private key must be set for a successful handshake with + * the client. + *
+ * Note: You can change an existing DSA hostkey after the initial kex exchange (the new value will
+ * be used during the next server initiated key exchange), but you cannot remove (i.e., set to null
) the
+ * current DSA key, otherwise the next key exchange may fail in case the client supports only DSA hostkeys.
+ *
+ * @param dsa_hostkey
+ */
+ public synchronized void setDsaHostKey(DSAPrivateKey dsa_hostkey)
+ {
+ synchronized (state)
+ {
+ if ((dsa_hostkey == null) && (state.next_dsa_key != null) && (state.tm != null))
+ throw new IllegalStateException("Cannot remove DSA hostkey after first key exchange.");
+
+ state.next_dsa_key = dsa_hostkey;
+ fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key);
+ }
+ }
+
+ /**
+ * Change the current RSA hostkey. Either a DSA or RSA private key must be set for a successful handshake with
+ * the client.
+ *
+ * Note: You can change an existing RSA hostkey after the initial kex exchange (the new value will
+ * be used during the next server initiated key exchange), but you cannot remove (i.e., set to null
) the
+ * current RSA key, otherwise the next key exchange may fail in case the client supports only RSA hostkeys.
+ *
+ * @param rsa_hostkey
+ */
+ public synchronized void setRsaHostKey(RSAPrivateKey rsa_hostkey)
+ {
+ synchronized (state)
+ {
+ if ((rsa_hostkey == null) && (state.next_rsa_key != null) && (state.tm != null))
+ throw new IllegalStateException("Cannot remove RSA hostkey after first key exchange.");
+
+ state.next_rsa_key = rsa_hostkey;
+ fixCryptoWishList(state.next_cryptoWishList, state.next_dsa_key, state.next_rsa_key);
+ }
+ }
+
+ /**
+ * Utility method that loads a PEM based hostkey (either RSA or DSA based) and
+ * calls either setRsaHostKey()
or setDsaHostKey()
.
+ *
+ * @param pemdata The PEM data
+ * @param password Password, may be null in case the PEM data is not password protected
+ * @throws IOException In case of any error.
+ */
+ public void setPEMHostKey(char[] pemdata, String password) throws IOException
+ {
+ Object key = PEMDecoder.decode(pemdata, password);
+
+ if (key instanceof DSAPrivateKey)
+ setDsaHostKey((DSAPrivateKey) key);
+
+ if (key instanceof RSAPrivateKey)
+ setRsaHostKey((RSAPrivateKey) key);
+ }
+
+ /**
+ * Utility method that loads a hostkey from a PEM file (either RSA or DSA based) and
+ * calls either setRsaHostKey()
or setDsaHostKey()
.
+ *
+ * @param pemFile The PEM file
+ * @param password Password, may be null in case the PEM file is not password protected
+ * @throws IOException
+ */
+ public void setPEMHostKey(File pemFile, String password) throws IOException
+ {
+ if (pemFile == null)
+ throw new IllegalArgumentException("pemfile argument is null");
+
+ char[] buff = new char[256];
+
+ CharArrayWriter cw = new CharArrayWriter();
+
+ FileReader fr = new FileReader(pemFile);
+
+ while (true)
+ {
+ int len = fr.read(buff);
+ if (len < 0)
+ break;
+ cw.write(buff, 0, len);
+ }
+
+ fr.close();
+
+ setPEMHostKey(cw.toCharArray(), password);
+ }
+
+ private void fixCryptoWishList(CryptoWishList next_cryptoWishList, DSAPrivateKey next_dsa_key,
+ RSAPrivateKey next_rsa_key)
+ {
+ if ((next_dsa_key != null) && (next_rsa_key != null))
+ next_cryptoWishList.serverHostKeyAlgorithms = new String[] { "ssh-rsa", "ssh-dss" };
+ else if (next_dsa_key != null)
+ next_cryptoWishList.serverHostKeyAlgorithms = new String[] { "ssh-dss" };
+ else if (next_rsa_key != null)
+ next_cryptoWishList.serverHostKeyAlgorithms = new String[] { "ssh-rsa" };
+ else
+ next_cryptoWishList.serverHostKeyAlgorithms = new String[0];
+ }
+
+ /**
+ * Callback interface with methods that will be called upon events
+ * generated by the client (e.g., client opens a new Session which results in a ServerSession
).
+ *
+ * Note: This must be set before the first handshake. + * + * @param cb The callback implementation + */ + public synchronized void setServerConnectionCallback(ServerConnectionCallback cb) + { + synchronized (state) + { + state.cb_conn = cb; + } + } + + /** + * Callback interface with methods that will be called upon authentication events. + *
+ * Note: This must be set before the first handshake. + * + * @param cb The callback implementation + */ + public synchronized void setAuthenticationCallback(ServerAuthenticationCallback cb) + { + synchronized (state) + { + state.cb_auth = cb; + } + } + + /** + * Close the connection to the SSH-2 server. All assigned sessions will be + * closed, too. Can be called at any time. Don't forget to call this once + * you don't need a connection anymore - otherwise the receiver thread may + * run forever. + */ + public void close() + { + synchronized (state) + { + if (state.cm != null) + state.cm.closeAllChannels(); + + if (state.tm != null) + { + state.tm.close(); + } + } + } + + public void close(IOException t) + { + synchronized (state) + { + if (state.cm != null) + state.cm.closeAllChannels(); + + if (state.tm != null) + { + state.tm.close(t); + } + } + } +} diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/ServerConnectionCallback.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ch/ethz/ssh2/ServerConnectionCallback.java Fri Jul 18 11:21:46 2014 -0700 @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2012-2013 Christian Plattner. All rights reserved. + * Please refer to the LICENSE.txt for licensing details. + */ +package ch.ethz.ssh2; + +public interface ServerConnectionCallback +{ + public ServerSessionCallback acceptSession(ServerSession session); +} diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/ServerHostKeyVerifier.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ch/ethz/ssh2/ServerHostKeyVerifier.java Fri Jul 18 11:21:46 2014 -0700 @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. + * Please refer to the LICENSE.txt for licensing details. + */ +package ch.ethz.ssh2; + +/** + * A callback interface used to implement a client specific method of checking + * server host keys. + * + * @author Christian Plattner + * @version 2.50, 03/15/10 + */ + +public interface ServerHostKeyVerifier +{ + /** + * The actual verifier method, it will be called by the key exchange code + * on EVERY key exchange - this can happen several times during the lifetime + * of a connection. + *
+ * Note: SSH-2 servers are allowed to change their hostkey at ANY time.
+ *
+ * @param hostname the hostname used to create the {@link Connection} object
+ * @param port the remote TCP port
+ * @param serverHostKeyAlgorithm the public key algorithm (ssh-rsa
or ssh-dss
)
+ * @param serverHostKey the server's public key blob
+ * @return if the client wants to accept the server's host key - if not, the
+ * connection will be closed.
+ * @throws Exception Will be wrapped with an IOException, extended version of returning false =)
+ */
+ public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, byte[] serverHostKey)
+ throws Exception;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/ServerSession.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ServerSession.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2012-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A ServerSession represents the server-side of a session channel.
+ *
+ * @see Session
+ *
+ * @author Christian
+ *
+ */
+public interface ServerSession
+{
+ public InputStream getStdout();
+
+ public InputStream getStderr();
+
+ public OutputStream getStdin();
+
+ public void close();
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/ServerSessionCallback.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/ServerSessionCallback.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+/**
+ * The interface for an object that receives events based on requests that
+ * a client sends on a session channel. Once the session channel has been set up,
+ * a program is started at the server end. The program can be a shell, an application
+ * program, or a subsystem with a host-independent name. Only one of these requests
+ * can succeed per channel.
+ *
IOException
. Currently, this will
+ * tear down the whole SSH connection.
+ *
+ * @see ServerSession
+ *
+ * @author Christian
+ *
+ */
+public interface ServerSessionCallback
+{
+ public Runnable requestPtyReq(ServerSession ss, PtySettings pty) throws IOException;
+
+ public Runnable requestEnv(ServerSession ss, String name, String value) throws IOException;
+
+ public Runnable requestShell(ServerSession ss) throws IOException;
+
+ public Runnable requestExec(ServerSession ss, String command) throws IOException;
+
+ public Runnable requestSubsystem(ServerSession ss, String subsystem) throws IOException;
+
+ /**
+ * When the window (terminal) size changes on the client side, it MAY send a message to the other side to inform it of the new dimensions.
+ *
+ * @param ss the corresponding session
+ * @param term_width_columns
+ * @param term_height_rows
+ * @param term_width_pixels
+ */
+ public void requestWindowChange(ServerSession ss, int term_width_columns, int term_height_rows,
+ int term_width_pixels, int term_height_pixels) throws IOException;
+
+ /**
+ * A signal can be delivered to the remote process/service. Some systems may not implement signals, in which case they SHOULD ignore this message.
+ *
+ * @param ss the corresponding session
+ * @param signal (a string without the "SIG" prefix)
+ * @return
+ * @throws IOException
+ */
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/Session.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/Session.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,490 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.channel.Channel;
+import ch.ethz.ssh2.channel.ChannelManager;
+import ch.ethz.ssh2.channel.X11ServerData;
+
+/**
+ * A Session
is a remote execution of a program. "Program" means
+ * in this context either a shell, an application or a system command. The
+ * program may or may not have a tty. Only one single program can be started on
+ * a session. However, multiple sessions can be active simultaneously.
+ *
+ * @author Christian Plattner
+ * @version $Id: Session.java 96 2014-04-08 15:14:37Z dkocher@sudo.ch $
+ */
+public class Session
+{
+ private ChannelManager cm;
+ private Channel cn;
+
+ private boolean flag_pty_requested = false;
+ private boolean flag_x11_requested = false;
+ private boolean flag_execution_started = false;
+ private boolean flag_closed = false;
+
+ private String x11FakeCookie = null;
+
+ private final SecureRandom rnd;
+
+ protected Session(ChannelManager cm, SecureRandom rnd) throws IOException
+ {
+ this.cm = cm;
+ this.cn = cm.openSessionChannel();
+ this.rnd = rnd;
+ }
+
+ /**
+ * Basically just a wrapper for lazy people - identical to calling
+ * requestPTY("dumb", 0, 0, 0, 0, null)
.
+ *
+ * @throws IOException
+ */
+ public void requestDumbPTY() throws IOException
+ {
+ requestPTY("dumb", 0, 0, 0, 0, null);
+ }
+
+ /**
+ * Basically just another wrapper for lazy people - identical to calling
+ * requestPTY(term, 0, 0, 0, 0, null)
.
+ *
+ * @throws IOException
+ */
+ public void requestPTY(String term) throws IOException
+ {
+ requestPTY(term, 0, 0, 0, 0, null);
+ }
+
+ /**
+ * Allocate a pseudo-terminal for this session.
+ *
+ * This method may only be called before a program or shell is started in
+ * this session.
+ *
+ * Different aspects can be specified:
+ *
+ * terminal_modes
) is described in RFC4254.
+ *
+ * @param term The TERM environment variable value (e.g., vt100)
+ * @param term_width_characters terminal width, characters (e.g., 80)
+ * @param term_height_characters terminal height, rows (e.g., 24)
+ * @param term_width_pixels terminal width, pixels (e.g., 640)
+ * @param term_height_pixels terminal height, pixels (e.g., 480)
+ * @param terminal_modes encoded terminal modes (may be null
)
+ * @throws IOException
+ */
+ public void requestPTY(String term, int term_width_characters, int term_height_characters, int term_width_pixels,
+ int term_height_pixels, byte[] terminal_modes) throws IOException
+ {
+ if (term == null)
+ throw new IllegalArgumentException("TERM cannot be null.");
+
+ if ((terminal_modes != null) && (terminal_modes.length > 0))
+ {
+ if (terminal_modes[terminal_modes.length - 1] != 0)
+ throw new IOException("Illegal terminal modes description, does not end in zero byte");
+ }
+ else
+ terminal_modes = new byte[]{0};
+
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (flag_pty_requested)
+ throw new IOException("A PTY was already requested.");
+
+ if (flag_execution_started)
+ throw new IOException(
+ "Cannot request PTY at this stage anymore, a remote execution has already started.");
+
+ flag_pty_requested = true;
+ }
+
+ cm.requestPTY(cn, term, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels,
+ terminal_modes);
+ }
+
+ /**
+ * Tells the server that the size of the terminal has changed.
+ *
+ * See {@link #requestPTY(String, int, int, int, int, byte[])} for more details about how parameters are interpreted.
+ *
+ * @param term_width_characters
+ * terminal width, characters (e.g., 80)
+ * @param term_height_characters
+ * terminal height, rows (e.g., 24)
+ * @param term_width_pixels
+ * terminal width, pixels (e.g., 640)
+ * @param term_height_pixels
+ * terminal height, pixels (e.g., 480)
+ * @throws IOException
+ */
+ public void requestWindowChange(int term_width_characters, int term_height_characters, int term_width_pixels,
+ int term_height_pixels) throws IOException
+ {
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (!flag_pty_requested)
+ throw new IOException("A PTY was not requested.");
+ }
+
+ cm.requestWindowChange(cn, term_width_characters, term_height_characters, term_width_pixels, term_height_pixels);
+ }
+
+ /**
+ * Request X11 forwarding for the current session.
+ *
+ * You have to supply the name and port of your X-server.
+ *
+ * This method may only be called before a program or shell is started in
+ * this session.
+ *
+ * @param hostname the hostname of the real (target) X11 server (e.g., 127.0.0.1)
+ * @param port the port of the real (target) X11 server (e.g., 6010)
+ * @param cookie if non-null, then present this cookie to the real X11 server
+ * @param singleConnection if true, then the server is instructed to only forward one single
+ * connection, no more connections shall be forwarded after first, or after the session
+ * channel has been closed
+ * @throws IOException
+ */
+ public void requestX11Forwarding(String hostname, int port, byte[] cookie, boolean singleConnection)
+ throws IOException
+ {
+ if (hostname == null)
+ throw new IllegalArgumentException("hostname argument may not be null");
+
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (flag_x11_requested)
+ throw new IOException("X11 forwarding was already requested.");
+
+ if (flag_execution_started)
+ throw new IOException(
+ "Cannot request X11 forwarding at this stage anymore, a remote execution has already started.");
+
+ flag_x11_requested = true;
+ }
+
+ /* X11ServerData - used to store data about the target X11 server */
+
+ X11ServerData x11data = new X11ServerData();
+
+ x11data.hostname = hostname;
+ x11data.port = port;
+ x11data.x11_magic_cookie = cookie; /* if non-null, then present this cookie to the real X11 server */
+
+ /* Generate fake cookie - this one is used between remote clients and the ganymed proxy */
+
+ byte[] fakeCookie = new byte[16];
+ String hexEncodedFakeCookie;
+
+ /* Make sure that this fake cookie is unique for this connection */
+
+ while (true)
+ {
+ rnd.nextBytes(fakeCookie);
+
+ /* Generate also hex representation of fake cookie */
+
+ StringBuilder tmp = new StringBuilder(32);
+ for (int i = 0; i < fakeCookie.length; i++)
+ {
+ String digit2 = Integer.toHexString(fakeCookie[i] & 0xff);
+ tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
+ }
+ hexEncodedFakeCookie = tmp.toString();
+
+ /* Well, yes, chances are low, but we want to be on the safe side */
+
+ if (cm.checkX11Cookie(hexEncodedFakeCookie) == null)
+ break;
+ }
+
+ /* Ask for X11 forwarding */
+
+ cm.requestX11(cn, singleConnection, "MIT-MAGIC-COOKIE-1", hexEncodedFakeCookie, 0);
+
+ /* OK, that went fine, get ready to accept X11 connections... */
+ /* ... but only if the user has not called close() in the meantime =) */
+
+ synchronized (this)
+ {
+ if (flag_closed == false)
+ {
+ this.x11FakeCookie = hexEncodedFakeCookie;
+ cm.registerX11Cookie(hexEncodedFakeCookie, x11data);
+ }
+ }
+
+ /* Now it is safe to start remote X11 programs */
+ }
+
+ /**
+ * Execute a command on the remote machine.
+ *
+ * @param cmd The command to execute on the remote host.
+ * @throws IOException
+ */
+ public void execCommand(String cmd) throws IOException
+ {
+ this.execCommand(cmd, null);
+ }
+
+ /**
+ * Execute a command on the remote machine.
+ *
+ * @param cmd The command to execute on the remote host.
+ * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
+ * @throws IOException
+ */
+ public void execCommand(String cmd, String charsetName) throws IOException
+ {
+ if (cmd == null)
+ throw new IllegalArgumentException("cmd argument may not be null");
+
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (flag_execution_started)
+ throw new IOException("A remote execution has already started.");
+
+ flag_execution_started = true;
+ }
+
+ cm.requestExecCommand(cn, cmd, charsetName);
+ }
+
+ /**
+ * Start a shell on the remote machine.
+ *
+ * @throws IOException
+ */
+ public void startShell() throws IOException
+ {
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (flag_execution_started)
+ throw new IOException("A remote execution has already started.");
+
+ flag_execution_started = true;
+ }
+
+ cm.requestShell(cn);
+ }
+
+ /**
+ * Start a subsystem on the remote machine.
+ * Unless you know what you are doing, you will never need this.
+ *
+ * @param name the name of the subsystem.
+ * @throws IOException
+ */
+ public void startSubSystem(String name) throws IOException
+ {
+ if (name == null)
+ throw new IllegalArgumentException("name argument may not be null");
+
+ synchronized (this)
+ {
+ /* The following is just a nicer error, we would catch it anyway later in the channel code */
+ if (flag_closed)
+ throw new IOException("This session is closed.");
+
+ if (flag_execution_started)
+ throw new IOException("A remote execution has already started.");
+
+ flag_execution_started = true;
+ }
+
+ cm.requestSubSystem(cn, name);
+ }
+
+ public int getState()
+ {
+ return cn.getState();
+ }
+
+ public InputStream getStdout()
+ {
+ return cn.getStdoutStream();
+ }
+
+ public InputStream getStderr()
+ {
+ return cn.getStderrStream();
+ }
+
+ public OutputStream getStdin()
+ {
+ return cn.getStdinStream();
+ }
+
+ /**
+ * This method blocks until there is more data available on either the
+ * stdout or stderr InputStream of this Session
. Very useful
+ * if you do not want to use two parallel threads for reading from the two
+ * InputStreams. One can also specify a timeout. NOTE: do NOT call this
+ * method if you use concurrent threads that operate on either of the two
+ * InputStreams of this Session
(otherwise this method may
+ * block, even though more data is available).
+ *
+ * @param timeout The (non-negative) timeout in ms
. 0
means no
+ * timeout, the call may block forever.
+ * @return 0
if no more data will arrive.1
if more data is available.-1
if a timeout occurred.Session
(otherwise this method may
+ * block, even though more data is available in the StreamGobblers).
+ *
+ * @param condition_set a bitmask based on {@link ChannelCondition} values
+ * @param timeout non-negative timeout in ms, 0
means no timeout
+ * @return all bitmask specifying all current conditions that are true
+ */
+
+ public int waitForCondition(int condition_set, long timeout) throws IOException {
+ if (timeout < 0)
+ throw new IllegalArgumentException("timeout must be non-negative!");
+
+ return cm.waitForCondition(cn, timeout, condition_set);
+ }
+
+ /**
+ * Get the exit code/status from the remote command - if available. Be
+ * careful - not all server implementations return this value. It is
+ * generally a good idea to call this method only when all data from the
+ * remote side has been consumed (see also the method).
+ *
+ * @return An Integer
holding the exit code, or
+ * null
if no exit code is (yet) available.
+ */
+ public Integer getExitStatus()
+ {
+ return cn.getExitStatus();
+ }
+
+ /**
+ * Get the name of the signal by which the process on the remote side was
+ * stopped - if available and applicable. Be careful - not all server
+ * implementations return this value.
+ *
+ * @return An String
holding the name of the signal, or
+ * null
if the process exited normally or is still
+ * running (or if the server forgot to send this information).
+ */
+ public String getExitSignal()
+ {
+ return cn.getExitSignal();
+ }
+
+ /**
+ * Close this session. NEVER forget to call this method to free up resources -
+ * even if you got an exception from one of the other methods (or when
+ * getting an Exception on the Input- or OutputStreams). Sometimes these other
+ * methods may throw an exception, saying that the underlying channel is
+ * closed (this can happen, e.g., if the other server sent a close message.)
+ * However, as long as you have not called the close()
+ * method, you may be wasting (local) resources.
+ */
+ public void close()
+ {
+ synchronized (this)
+ {
+ if (flag_closed)
+ return;
+
+ flag_closed = true;
+
+ if (x11FakeCookie != null)
+ cm.unRegisterX11Cookie(x11FakeCookie, true);
+
+ try
+ {
+ cm.closeChannel(cn, "Closed due to user request", true);
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/SimpleServerSessionCallback.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/SimpleServerSessionCallback.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+
+/**
+ * A basic ServerSessionCallback implementation.
+ *
+ * Note: you should derive from this class instead of implementing
+ * the {@link ServerSessionCallback} interface directly. This way
+ * your code works also in case the interface gets extended in future
+ * versions.
+ *
+ * @author Christian
+ *
+ */
+public class SimpleServerSessionCallback implements ServerSessionCallback
+{
+ public Runnable requestShell(ServerSession ss) throws IOException
+ {
+ return null;
+ }
+
+ public Runnable requestExec(ServerSession ss, String command) throws IOException
+ {
+ return null;
+ }
+
+ public Runnable requestSubsystem(ServerSession ss, String subsystem) throws IOException
+ {
+ return null;
+ }
+
+ public Runnable requestPtyReq(ServerSession ss, PtySettings pty) throws IOException
+ {
+ return null;
+ }
+
+ /**
+ * By default, silently ignore passwd environment variables.
+ */
+ public Runnable requestEnv(ServerSession ss, String name, String value) throws IOException
+ {
+ return new Runnable()
+ {
+ public void run()
+ {
+ /* Do nothing */
+ }
+ };
+ }
+
+ public void requestWindowChange(ServerSession ss, int term_width_columns, int term_height_rows,
+ int term_width_pixels, int term_height_pixels) throws IOException
+ {
+ }
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/StreamGobbler.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/StreamGobbler.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+
+/**
+ * A StreamGobbler
is an InputStream that uses an internal worker
+ * thread to constantly consume input from another InputStream. It uses a buffer
+ * to store the consumed data. The buffer size is automatically adjusted, if needed.
+ *
+ * This class is sometimes very convenient - if you wrap a session's STDOUT and STDERR
+ * InputStreams with instances of this class, then you don't have to bother about
+ * the shared window of STDOUT and STDERR in the low level SSH-2 protocol,
+ * since all arriving data will be immediatelly consumed by the worker threads.
+ * Also, as a side effect, the streams will be buffered (e.g., single byte
+ * read() operations are faster).
+ *
+ * Other SSH for Java libraries include this functionality by default in
+ * their STDOUT and STDERR InputStream implementations, however, please be aware
+ * that this approach has also a downside:
+ *
+ * If you do not call the StreamGobbler's read()
method often enough
+ * and the peer is constantly sending huge amounts of data, then you will sooner or later
+ * encounter a low memory situation due to the aggregated data (well, it also depends on the Java heap size).
+ * Joe Average will like this class anyway - a paranoid programmer would never use such an approach.
+ *
+ * The term "StreamGobbler" was taken from an article called "When Runtime.exec() won't",
+ * see http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class StreamGobbler extends InputStream
+{
+ class GobblerThread extends Thread
+ {
+ @Override
+ public void run()
+ {
+ byte[] buff = new byte[8192];
+
+ while (true)
+ {
+ try
+ {
+ int avail = is.read(buff);
+
+ synchronized (synchronizer)
+ {
+ if (avail <= 0)
+ {
+ isEOF = true;
+ synchronizer.notifyAll();
+ break;
+ }
+
+ int space_available = buffer.length - write_pos;
+
+ if (space_available < avail)
+ {
+ /* compact/resize buffer */
+
+ int unread_size = write_pos - read_pos;
+ int need_space = unread_size + avail;
+
+ byte[] new_buffer = buffer;
+
+ if (need_space > buffer.length)
+ {
+ int inc = need_space / 3;
+ inc = (inc < 256) ? 256 : inc;
+ inc = (inc > 8192) ? 8192 : inc;
+ new_buffer = new byte[need_space + inc];
+ }
+
+ if (unread_size > 0)
+ System.arraycopy(buffer, read_pos, new_buffer, 0, unread_size);
+
+ buffer = new_buffer;
+
+ read_pos = 0;
+ write_pos = unread_size;
+ }
+
+ System.arraycopy(buff, 0, buffer, write_pos, avail);
+ write_pos += avail;
+
+ synchronizer.notifyAll();
+ }
+ }
+ catch (IOException e)
+ {
+ synchronized (synchronizer)
+ {
+ exception = e;
+ synchronizer.notifyAll();
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private InputStream is;
+
+ private final Object synchronizer = new Object();
+
+ private boolean isEOF = false;
+ private boolean isClosed = false;
+ private IOException exception = null;
+
+ private byte[] buffer = new byte[2048];
+ private int read_pos = 0;
+ private int write_pos = 0;
+
+ public StreamGobbler(InputStream is)
+ {
+ this.is = is;
+ GobblerThread t = new GobblerThread();
+ t.setDaemon(true);
+ t.start();
+ }
+
+ @Override
+ public int read() throws IOException
+ {
+ synchronized (synchronizer)
+ {
+ if (isClosed)
+ throw new IOException("This StreamGobbler is closed.");
+
+ while (read_pos == write_pos)
+ {
+ if (exception != null)
+ throw exception;
+
+ if (isEOF)
+ return -1;
+
+ try
+ {
+ synchronizer.wait();
+ }
+ catch (InterruptedException e)
+ {
+ throw new InterruptedIOException();
+ }
+ }
+ return buffer[read_pos++] & 0xff;
+ }
+ }
+
+ @Override
+ public int available() throws IOException
+ {
+ synchronized (synchronizer)
+ {
+ if (isClosed)
+ throw new IOException("This StreamGobbler is closed.");
+
+ return write_pos - read_pos;
+ }
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException
+ {
+ return read(b, 0, b.length);
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ synchronized (synchronizer)
+ {
+ if (isClosed)
+ return;
+ isClosed = true;
+ isEOF = true;
+ synchronizer.notifyAll();
+ is.close();
+ }
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (b == null)
+ throw new NullPointerException();
+
+ if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
+ throw new IndexOutOfBoundsException();
+
+ if (len == 0)
+ return 0;
+ synchronized (synchronizer)
+ {
+ if (isClosed)
+ throw new IOException("This StreamGobbler is closed.");
+
+ while (read_pos == write_pos)
+ {
+ if (exception != null)
+ throw exception;
+
+ if (isEOF)
+ return -1;
+
+ try
+ {
+ synchronizer.wait();
+ }
+ catch (InterruptedException e)
+ {
+ throw new InterruptedIOException();
+ }
+ }
+
+ int avail = write_pos - read_pos;
+
+ avail = (avail > len) ? len : avail;
+
+ System.arraycopy(buffer, read_pos, b, off, avail);
+
+ read_pos += avail;
+
+ return avail;
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/Version.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/Version.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,30 @@
+package ch.ethz.ssh2;
+
+/**
+ * Provides version information from the manifest.
+ *
+ * @version $Id: Version.java 140 2014-04-15 13:16:25Z dkocher@sudo.ch $
+ */
+public class Version
+{
+ public static String getSpecification()
+ {
+ Package pkg = Version.class.getPackage();
+ return (pkg == null) ? "SNAPSHOT" : pkg.getSpecificationVersion() == null ? "SNAPSHOT" : pkg.getSpecificationVersion();
+ }
+
+ public static String getImplementation()
+ {
+ Package pkg = Version.class.getPackage();
+ return (pkg == null) ? "SNAPSHOT" : pkg.getImplementationVersion() == null ? "SNAPSHOT" : pkg.getImplementationVersion();
+ }
+
+ /**
+ * A simple main method that prints the version and exits
+ */
+ public static void main(String[] args)
+ {
+ System.out.println("Version: " + getSpecification());
+ System.out.println("Implementation: " + getImplementation());
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/auth/AgentIdentity.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/auth/AgentIdentity.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.auth;
+
+public interface AgentIdentity {
+ String getAlgName();
+ byte[] getPublicKeyBlob();
+ byte[] sign(byte[] data);
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/auth/AgentProxy.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/auth/AgentProxy.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.auth;
+
+import java.util.Collection;
+
+public interface AgentProxy {
+ public Collection getIdentities();
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/auth/AuthenticationManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/auth/AuthenticationManager.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,396 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.auth;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.security.SecureRandom;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+
+import ch.ethz.ssh2.InteractiveCallback;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.crypto.PEMDecoder;
+import ch.ethz.ssh2.packets.PacketServiceAccept;
+import ch.ethz.ssh2.packets.PacketServiceRequest;
+import ch.ethz.ssh2.packets.PacketUserauthBanner;
+import ch.ethz.ssh2.packets.PacketUserauthFailure;
+import ch.ethz.ssh2.packets.PacketUserauthInfoRequest;
+import ch.ethz.ssh2.packets.PacketUserauthInfoResponse;
+import ch.ethz.ssh2.packets.PacketUserauthRequestInteractive;
+import ch.ethz.ssh2.packets.PacketUserauthRequestNone;
+import ch.ethz.ssh2.packets.PacketUserauthRequestPassword;
+import ch.ethz.ssh2.packets.PacketUserauthRequestPublicKey;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.packets.TypesWriter;
+import ch.ethz.ssh2.signature.DSAPrivateKey;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.DSASignature;
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.signature.RSASignature;
+import ch.ethz.ssh2.transport.ClientTransportManager;
+import ch.ethz.ssh2.transport.MessageHandler;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: AuthenticationManager.java 161 2014-05-01 18:01:55Z dkocher@sudo.ch $
+ */
+public class AuthenticationManager implements MessageHandler {
+ private ClientTransportManager tm;
+
+ private final BlockingQueue packets
+ = new ArrayBlockingQueue(5);
+
+ private boolean connectionClosed = false;
+
+ private String banner;
+
+ private Set remainingMethods
+ = new HashSet();
+
+ private boolean isPartialSuccess = false;
+
+ private boolean authenticated = false;
+ private boolean initDone = false;
+
+ public AuthenticationManager(ClientTransportManager tm) {
+ this.tm = tm;
+ }
+
+ private byte[] deQueue() throws IOException {
+ if(connectionClosed) {
+ throw tm.getReasonClosedCause();
+ }
+ // Wait for packet
+ try {
+ return packets.take();
+ }
+ catch(InterruptedException e) {
+ throw new InterruptedIOException(e.getMessage());
+ }
+ }
+
+ byte[] getNextMessage() throws IOException {
+ while(true) {
+ byte[] message = deQueue();
+ switch(message[0]) {
+ case Packets.SSH_MSG_USERAUTH_BANNER:
+ // The server may send an SSH_MSG_USERAUTH_BANNER message at any
+ // time after this authentication protocol starts and before
+ // authentication is successful.
+ PacketUserauthBanner sb = new PacketUserauthBanner(message);
+ banner = sb.getBanner();
+ break;
+ default:
+ return message;
+ }
+ }
+ }
+
+ public Set getRemainingMethods(String user) throws IOException {
+ initialize(user);
+ return remainingMethods;
+ }
+
+ public String getBanner() {
+ return banner;
+ }
+
+ public boolean getPartialSuccess() {
+ return isPartialSuccess;
+ }
+
+ private boolean initialize(String user) throws IOException {
+ if(initDone == false) {
+ tm.registerMessageHandler(this, 0, 255);
+
+ PacketServiceRequest sr = new PacketServiceRequest("ssh-userauth");
+ tm.sendMessage(sr.getPayload());
+
+ final PacketServiceAccept accept = new PacketServiceAccept(this.getNextMessage());
+
+ PacketUserauthRequestNone auth = new PacketUserauthRequestNone("ssh-connection", user);
+ tm.sendMessage(auth.getPayload());
+
+ byte[] message = this.getNextMessage();
+ initDone = true;
+ switch(message[0]) {
+ case Packets.SSH_MSG_USERAUTH_SUCCESS:
+ authenticated = true;
+ tm.removeMessageHandler(this);
+ return true;
+ case Packets.SSH_MSG_USERAUTH_FAILURE:
+ PacketUserauthFailure puf = new PacketUserauthFailure(message);
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+ return false;
+ }
+ throw new PacketTypeException(message[0]);
+ }
+ return authenticated;
+ }
+
+ public boolean authenticatePublicKey(String user, AgentProxy proxy) throws IOException {
+ initialize(user);
+
+ boolean success;
+ for(AgentIdentity identity : proxy.getIdentities()) {
+ success = authenticatePublicKey(user, identity);
+ if(success) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean authenticatePublicKey(String user, AgentIdentity identity) throws IOException {
+ if(!remainingMethods.contains("publickey")) {
+ throw new IOException("Authentication method not supported");
+ }
+
+ byte[] pubKeyBlob = identity.getPublicKeyBlob();
+ if(pubKeyBlob == null) {
+ return false;
+ }
+
+ TypesWriter tw = new TypesWriter();
+ byte[] H = tm.getSessionIdentifier();
+
+ tw.writeString(H, 0, H.length);
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString("ssh-connection");
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString(identity.getAlgName());
+ tw.writeString(pubKeyBlob, 0, pubKeyBlob.length);
+
+ byte[] msg = tw.getBytes();
+ byte[] response = identity.sign(msg);
+
+ PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey(
+ "ssh-connection", user, identity.getAlgName(), pubKeyBlob, response);
+ tm.sendMessage(ua.getPayload());
+
+ byte[] message = getNextMessage();
+ final int type = message[0];
+ switch(type) {
+ case Packets.SSH_MSG_USERAUTH_SUCCESS:
+ authenticated = true;
+ tm.removeMessageHandler(this);
+ return true;
+ case Packets.SSH_MSG_USERAUTH_FAILURE:
+ PacketUserauthFailure puf = new PacketUserauthFailure(message);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+
+ return false;
+ }
+ throw new PacketTypeException(type);
+ }
+
+ public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
+ throws IOException {
+ try {
+ initialize(user);
+
+ if(!remainingMethods.contains("publickey")) {
+ throw new IOException("Authentication method publickey not supported by the server at this stage.");
+ }
+
+ Object key = PEMDecoder.decode(PEMPrivateKey, password);
+
+ if(key instanceof DSAPrivateKey) {
+ DSAPrivateKey pk = (DSAPrivateKey) key;
+
+ byte[] pk_enc = DSASHA1Verify.encodeSSHDSAPublicKey(pk.getPublicKey());
+
+ TypesWriter tw = new TypesWriter();
+
+ byte[] H = tm.getSessionIdentifier();
+
+ tw.writeString(H, 0, H.length);
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString("ssh-connection");
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString("ssh-dss");
+ tw.writeString(pk_enc, 0, pk_enc.length);
+
+ byte[] msg = tw.getBytes();
+
+ DSASignature ds = DSASHA1Verify.generateSignature(msg, pk, rnd);
+
+ byte[] ds_enc = DSASHA1Verify.encodeSSHDSASignature(ds);
+
+ PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+ "ssh-dss", pk_enc, ds_enc);
+ tm.sendMessage(ua.getPayload());
+ }
+ else if(key instanceof RSAPrivateKey) {
+ RSAPrivateKey pk = (RSAPrivateKey) key;
+
+ byte[] pk_enc = RSASHA1Verify.encodeSSHRSAPublicKey(pk.getPublicKey());
+
+ TypesWriter tw = new TypesWriter();
+ {
+ byte[] H = tm.getSessionIdentifier();
+
+ tw.writeString(H, 0, H.length);
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString("ssh-connection");
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString("ssh-rsa");
+ tw.writeString(pk_enc, 0, pk_enc.length);
+ }
+
+ byte[] msg = tw.getBytes();
+
+ RSASignature ds = RSASHA1Verify.generateSignature(msg, pk);
+
+ byte[] rsa_sig_enc = RSASHA1Verify.encodeSSHRSASignature(ds);
+
+ PacketUserauthRequestPublicKey ua = new PacketUserauthRequestPublicKey("ssh-connection", user,
+ "ssh-rsa", pk_enc, rsa_sig_enc);
+ tm.sendMessage(ua.getPayload());
+ }
+ else {
+ throw new IOException("Unknown private key type returned by the PEM decoder.");
+ }
+ byte[] message = getNextMessage();
+ final int type = message[0];
+ switch(type) {
+ case Packets.SSH_MSG_USERAUTH_SUCCESS:
+ authenticated = true;
+ tm.removeMessageHandler(this);
+ return true;
+ case Packets.SSH_MSG_USERAUTH_FAILURE:
+ PacketUserauthFailure puf = new PacketUserauthFailure(message);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+
+ return false;
+ }
+ throw new PacketTypeException(type);
+ }
+ catch(IOException e) {
+ tm.close(e);
+ throw e;
+ }
+ }
+
+ public boolean authenticateNone(String user) throws IOException {
+ try {
+ initialize(user);
+ return authenticated;
+ }
+ catch(IOException e) {
+ tm.close(e);
+ throw e;
+ }
+ }
+
+ public boolean authenticatePassword(String user, String pass) throws IOException {
+ try {
+ initialize(user);
+
+ if(!remainingMethods.contains("password")) {
+ throw new IOException("Authentication method not supported");
+ }
+
+ PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
+ tm.sendMessage(ua.getPayload());
+
+ byte[] message = getNextMessage();
+ final int type = message[0];
+ switch(type) {
+ case Packets.SSH_MSG_USERAUTH_SUCCESS:
+ authenticated = true;
+ tm.removeMessageHandler(this);
+ return true;
+ case Packets.SSH_MSG_USERAUTH_FAILURE:
+ PacketUserauthFailure puf = new PacketUserauthFailure(message);
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+ return false;
+ }
+ throw new PacketTypeException(type);
+ }
+ catch(IOException e) {
+ tm.close(e);
+ throw e;
+ }
+ }
+
+ public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException {
+ try {
+ initialize(user);
+
+ if(!remainingMethods.contains("keyboard-interactive")) {
+ throw new IOException(
+ "Authentication method keyboard-interactive not supported by the server at this stage.");
+ }
+
+ PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
+ submethods);
+
+ tm.sendMessage(ua.getPayload());
+
+ while(true) {
+ byte[] message = getNextMessage();
+ final int type = message[0];
+ switch(type) {
+ case Packets.SSH_MSG_USERAUTH_SUCCESS:
+ authenticated = true;
+ tm.removeMessageHandler(this);
+ return true;
+ case Packets.SSH_MSG_USERAUTH_FAILURE:
+ PacketUserauthFailure puf = new PacketUserauthFailure(message);
+
+ remainingMethods = puf.getAuthThatCanContinue();
+ isPartialSuccess = puf.isPartialSuccess();
+
+ return false;
+ case Packets.SSH_MSG_USERAUTH_INFO_REQUEST:
+ PacketUserauthInfoRequest info = new PacketUserauthInfoRequest(message);
+ String[] responses;
+ try {
+ responses = cb.replyToChallenge(info.getName(), info.getInstruction(), info.getNumPrompts(),
+ info.getPrompt(), info.getEcho());
+ }
+ catch(Exception e) {
+ throw new IOException("Exception in callback.", e);
+ }
+ PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
+ tm.sendMessage(puir.getPayload());
+ continue;
+ }
+ throw new PacketTypeException(type);
+ }
+ }
+ catch(IOException e) {
+ tm.close(e);
+ throw e;
+ }
+ }
+
+ @Override
+ public void handleFailure(final IOException failure) {
+ connectionClosed = true;
+ }
+
+ @Override
+ public void handleMessage(byte[] message) throws IOException {
+ packets.add(message);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/auth/ServerAuthenticationManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/auth/ServerAuthenticationManager.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,137 @@
+
+package ch.ethz.ssh2.auth;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import ch.ethz.ssh2.AuthenticationResult;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.ServerAuthenticationCallback;
+import ch.ethz.ssh2.channel.ChannelManager;
+import ch.ethz.ssh2.packets.PacketServiceAccept;
+import ch.ethz.ssh2.packets.PacketServiceRequest;
+import ch.ethz.ssh2.packets.PacketUserauthBanner;
+import ch.ethz.ssh2.packets.PacketUserauthFailure;
+import ch.ethz.ssh2.packets.PacketUserauthSuccess;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.server.ServerConnectionState;
+import ch.ethz.ssh2.transport.MessageHandler;
+
+public class ServerAuthenticationManager implements MessageHandler {
+ private final ServerConnectionState state;
+
+ public ServerAuthenticationManager(ServerConnectionState state) {
+ this.state = state;
+ state.tm.registerMessageHandler(this, 0, 255);
+ }
+
+ private void sendresult(AuthenticationResult result) throws IOException {
+ if(AuthenticationResult.SUCCESS == result) {
+ PacketUserauthSuccess pus = new PacketUserauthSuccess();
+ state.tm.sendAsynchronousMessage(pus.getPayload());
+
+ state.tm.removeMessageHandler(this);
+ state.tm.registerMessageHandler(this, 50, 79);
+
+ state.cm = new ChannelManager(state);
+
+ state.flag_auth_completed = true;
+
+ }
+ else {
+ Set remaining_methods = new HashSet();
+
+ if(state.cb_auth != null) {
+ remaining_methods.addAll(Arrays.asList(
+ state.cb_auth.getRemainingAuthMethods(state.conn)));
+ }
+ PacketUserauthFailure puf = new PacketUserauthFailure(remaining_methods,
+ AuthenticationResult.PARTIAL_SUCCESS == result);
+ state.tm.sendAsynchronousMessage(puf.getPayload());
+ }
+ }
+
+ @Override
+ public void handleFailure(final IOException failure) {
+ //
+ }
+
+ @Override
+ public void handleMessage(byte[] msg) throws IOException {
+ /* Ignore all authentication messages after successful auth */
+
+ if(state.flag_auth_completed) {
+ return;
+ }
+
+ if(!state.flag_auth_serviceRequested) {
+ /* Must be PacketServiceRequest */
+
+ PacketServiceRequest psr = new PacketServiceRequest(msg);
+
+ if(!"ssh-userauth".equals(psr.getServiceName())) {
+ throw new IOException("SSH protocol error, expected ssh-userauth service request");
+ }
+
+ PacketServiceAccept psa = new PacketServiceAccept("ssh-userauth");
+ state.tm.sendAsynchronousMessage(psa.getPayload());
+
+ String banner = state.cb_auth.initAuthentication(state.conn);
+
+ if(banner != null) {
+ PacketUserauthBanner pub = new PacketUserauthBanner(banner);
+ state.tm.sendAsynchronousMessage(pub.getPayload());
+ }
+
+ state.flag_auth_serviceRequested = true;
+
+ return;
+ }
+
+ ServerAuthenticationCallback cb = state.cb_auth;
+
+ TypesReader tr = new TypesReader(msg);
+ int packet_type = tr.readByte();
+
+ if(packet_type == Packets.SSH_MSG_USERAUTH_REQUEST) {
+ String username = tr.readString("UTF-8");
+ String service = tr.readString();
+ String method = tr.readString();
+
+ if(!"ssh-connection".equals(service)) {
+ sendresult(AuthenticationResult.FAILURE);
+ return;
+ }
+
+ if("none".equals(method)) {
+ if(cb != null) {
+ sendresult(cb.authenticateWithNone(state.conn, username));
+ return;
+ }
+ }
+
+ if("password".equals(method)) {
+ boolean flag_change_pass = tr.readBoolean();
+
+ if(flag_change_pass) {
+ sendresult(AuthenticationResult.FAILURE);
+ return;
+ }
+
+ String password = tr.readString("UTF-8");
+
+ if(cb != null) {
+ sendresult(cb.authenticateWithPassword(state.conn, username, password));
+ return;
+ }
+ }
+
+ sendresult(AuthenticationResult.FAILURE);
+ return;
+ }
+ throw new PacketTypeException(packet_type);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/Channel.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/Channel.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.transport.TransportManager;
+
+/**
+ * Channel.
+ *
+ * @author Christian Plattner
+ * @version $Id: Channel.java 123 2014-04-12 21:11:47Z dkocher@sudo.ch $
+ */
+public class Channel
+{
+ /*
+ * OK. Here is an important part of the JVM Specification:
+ * (http://java.sun.com/docs/books/vmspec/2nd-edition/html/Threads.doc.html#22214)
+ *
+ * Any association between locks and variables is purely conventional.
+ * Locking any lock conceptually flushes all variables from a thread's
+ * working memory, and unlocking any lock forces the writing out to main
+ * memory of all variables that the thread has assigned. That a lock may be
+ * associated with a particular object or a class is purely a convention.
+ * (...)
+ *
+ * If a thread uses a particular shared variable only after locking a
+ * particular lock and before the corresponding unlocking of that same lock,
+ * then the thread will read the shared value of that variable from main
+ * memory after the lock operation, if necessary, and will copy back to main
+ * memory the value most recently assigned to that variable before the
+ * unlock operation.
+ *
+ * This, in conjunction with the mutual exclusion rules for locks, suffices
+ * to guarantee that values are correctly transmitted from one thread to
+ * another through shared variables.
+ *
+ * ====> Always keep that in mind when modifying the Channel/ChannelManger
+ * code.
+ *
+ */
+
+ public static final int STATE_OPENING = 1;
+ public static final int STATE_OPEN = 2;
+ public static final int STATE_CLOSED = 4;
+
+ static final int CHANNEL_BUFFER_SIZE = 32 * 1024 * 3 * 2;
+
+ /*
+ * To achieve correctness, the following rules have to be respected when
+ * accessing this object:
+ */
+
+ // These fields can always be read
+ final ChannelManager cm;
+ final ChannelOutputStream stdinStream;
+ final ChannelInputStream stdoutStream;
+ final ChannelInputStream stderrStream;
+
+ // In case this channel belongs to a server-side session.
+ ServerSessionImpl ss;
+
+ // These two fields will only be written while the Channel is in state
+ // STATE_OPENING.
+ // The code makes sure that the two fields are written out when the state is
+ // changing to STATE_OPEN.
+ // Therefore, if you know that the Channel is in state STATE_OPEN, then you
+ // can read these two fields without synchronizing on the Channel. However, make
+ // sure that you get the latest values (e.g., flush caches by synchronizing on any
+ // object). However, to be on the safe side, you can lock the channel.
+
+ int localID = -1;
+ int remoteID = -1;
+
+ /*
+ * Make sure that we never send a data/EOF/WindowChange msg after a CLOSE
+ * msg.
+ *
+ * This is a little bit complicated, but we have to do it in that way, since
+ * we cannot keep a lock on the Channel during the send operation (this
+ * would block sometimes the receiver thread, and, in extreme cases, can
+ * lead to a deadlock on both sides of the connection (senders are blocked
+ * since the receive buffers on the other side are full, and receiver
+ * threads wait for the senders to finish). It all depends on the
+ * implementation on the other side. But we cannot make any assumptions, we
+ * have to assume the worst case. Confused? Just believe me.
+ */
+
+ /*
+ * If you send a message on a channel, then you have to aquire the
+ * "channelSendLock" and check the "closeMessageSent" flag (this variable
+ * may only be accessed while holding the "channelSendLock" !!!
+ *
+ * BTW: NEVER EVER SEND MESSAGES FROM THE RECEIVE THREAD - see explanation
+ * above.
+ */
+
+ final Object channelSendLock = new Object();
+ boolean closeMessageSent = false;
+
+ /*
+ * Stop memory fragmentation by allocating this often used buffer.
+ * May only be used while holding the channelSendLock
+ */
+
+ final byte[] msgWindowAdjust = new byte[9];
+
+ // If you access (read or write) any of the following fields, then you have
+ // to synchronize on the channel.
+
+ int state = STATE_OPENING;
+
+ boolean closeMessageRecv = false;
+
+ /* This is a stupid implementation. At the moment we can only wait
+ * for one pending request per channel.
+ */
+ int successCounter = 0;
+ int failedCounter = 0;
+
+ int localWindow = 0; /* locally, we use a small window, < 2^31 */
+ long remoteWindow = 0; /* long for readable 2^32 - 1 window support */
+
+ int localMaxPacketSize = -1;
+ int remoteMaxPacketSize = -1;
+
+ final byte[] stdoutBuffer = new byte[CHANNEL_BUFFER_SIZE];
+ final byte[] stderrBuffer = new byte[CHANNEL_BUFFER_SIZE];
+
+ int stdoutReadpos = 0;
+ int stdoutWritepos = 0;
+ int stderrReadpos = 0;
+ int stderrWritepos = 0;
+
+ boolean EOF = false;
+
+ Integer exit_status;
+
+ String exit_signal;
+
+ // we keep the x11 cookie so that this channel can be closed when this
+ // specific x11 forwarding gets stopped
+
+ String hexX11FakeCookie;
+
+ // reasonClosed is special, since we sometimes need to access it
+ // while holding the channelSendLock.
+ // We protect it with a private short term lock.
+
+ private final Object reasonClosedLock = new Object();
+ private IOException reasonClosed = null;
+
+ public Channel(ChannelManager cm)
+ {
+ this.cm = cm;
+
+ this.localWindow = CHANNEL_BUFFER_SIZE;
+ this.localMaxPacketSize = TransportManager.MAX_PACKET_SIZE;
+
+ this.stdinStream = new ChannelOutputStream(this);
+ this.stdoutStream = new ChannelInputStream(this, false);
+ this.stderrStream = new ChannelInputStream(this, true);
+ }
+
+ /* Methods to allow access from classes outside of this package */
+
+ public ChannelInputStream getStderrStream()
+ {
+ return stderrStream;
+ }
+
+ public ChannelOutputStream getStdinStream()
+ {
+ return stdinStream;
+ }
+
+ public ChannelInputStream getStdoutStream()
+ {
+ return stdoutStream;
+ }
+
+ public String getExitSignal()
+ {
+ synchronized (this)
+ {
+ return exit_signal;
+ }
+ }
+
+ public Integer getExitStatus()
+ {
+ synchronized (this)
+ {
+ return exit_status;
+ }
+ }
+
+ public IOException getReasonClosed()
+ {
+ synchronized (reasonClosedLock)
+ {
+ return reasonClosed;
+ }
+ }
+
+ public void setReasonClosed(IOException e)
+ {
+ synchronized (reasonClosedLock)
+ {
+ this.reasonClosed = e;
+ }
+ }
+
+ public int getState()
+ {
+ return this.state;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/ChannelClosedException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/ChannelClosedException.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2011 David Kocher. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+
+/**
+ * @version $Id: ChannelClosedException.java 3183 2007-07-30 19:22:34Z dkocher $
+ */
+public class ChannelClosedException extends IOException
+{
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public ChannelClosedException(String s)
+ {
+ super(s);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/ChannelInputStream.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/ChannelInputStream.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * ChannelInputStream.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class ChannelInputStream extends InputStream
+{
+ Channel c;
+
+ boolean isClosed = false;
+ boolean isEOF = false;
+ boolean extendedFlag = false;
+
+ ChannelInputStream(Channel c, boolean isExtended)
+ {
+ this.c = c;
+ this.extendedFlag = isExtended;
+ }
+
+ @Override
+ public int available() throws IOException
+ {
+ if (isEOF)
+ return 0;
+
+ int avail = c.cm.getAvailable(c, extendedFlag);
+
+ /* We must not return -1 on EOF */
+
+ return (avail > 0) ? avail : 0;
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ isClosed = true;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (b == null)
+ throw new NullPointerException();
+
+ if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
+ throw new IndexOutOfBoundsException();
+
+ if (len == 0)
+ return 0;
+
+ if (isEOF)
+ return -1;
+
+ int ret = c.cm.getChannelData(c, extendedFlag, b, off, len);
+
+ if (ret == -1)
+ {
+ isEOF = true;
+ }
+
+ return ret;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException
+ {
+ return read(b, 0, b.length);
+ }
+
+ @Override
+ public int read() throws IOException
+ {
+ /* Yes, this stream is pure and unbuffered, a single byte read() is slow */
+
+ final byte b[] = new byte[1];
+
+ int ret = read(b, 0, 1);
+
+ if (ret != 1)
+ return -1;
+
+ return b[0] & 0xff;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/ChannelManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/ChannelManager.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,1644 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import ch.ethz.ssh2.ChannelCondition;
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.PtySettings;
+import ch.ethz.ssh2.ServerConnectionCallback;
+import ch.ethz.ssh2.ServerSessionCallback;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.*;
+import ch.ethz.ssh2.server.ServerConnectionState;
+import ch.ethz.ssh2.transport.MessageHandler;
+import ch.ethz.ssh2.transport.TransportManager;
+
+/**
+ * ChannelManager. Please read the comments in Channel.java.
+ *
+ * Besides the crypto part, this is the core of the library.
+ *
+ * @author Christian Plattner
+ * @version $Id: ChannelManager.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public class ChannelManager implements MessageHandler {
+ private static final Logger log = Logger.getLogger(ChannelManager.class);
+
+ private final ServerConnectionState server_state;
+ private final TransportManager tm;
+
+ private final Map x11_magic_cookies = new HashMap();
+
+ private final List channels = new ArrayList();
+ private int nextLocalChannel = 100;
+ private boolean shutdown = false;
+ private int globalSuccessCounter = 0;
+ private int globalFailedCounter = 0;
+
+ private final Map remoteForwardings = new HashMap();
+
+ private final List listenerThreads = new ArrayList();
+
+ private boolean listenerThreadsAllowed = true;
+
+ /**
+ * Constructor for client-mode.
+ *
+ * @param tm
+ */
+ public ChannelManager(TransportManager tm) {
+ this.server_state = null;
+ this.tm = tm;
+ tm.registerMessageHandler(this, 80, 100);
+ }
+
+ /**
+ * Constructor for server-mode.
+ *
+ * @param state
+ */
+ public ChannelManager(ServerConnectionState state) {
+ this.server_state = state;
+ this.tm = state.tm;
+ tm.registerMessageHandler(this, 80, 100);
+ }
+
+ private Channel getChannel(int id) {
+ synchronized(channels) {
+ for(Channel c : channels) {
+ if(c.localID == id) {
+ return c;
+ }
+ }
+ }
+ return null;
+ }
+
+ private void removeChannel(int id) {
+ synchronized(channels) {
+ for(Channel c : channels) {
+ if(c.localID == id) {
+ channels.remove(c);
+ break;
+ }
+ }
+ }
+ }
+
+ private int addChannel(Channel c) {
+ synchronized(channels) {
+ channels.add(c);
+ return nextLocalChannel++;
+ }
+ }
+
+ private void waitUntilChannelOpen(Channel c) throws IOException {
+ synchronized(c) {
+ while(c.state == Channel.STATE_OPENING) {
+ try {
+ c.wait();
+ }
+ catch(InterruptedException e) {
+ throw new InterruptedIOException(e.getMessage());
+ }
+ }
+
+ if(c.state != Channel.STATE_OPEN) {
+ removeChannel(c.localID);
+ throw c.getReasonClosed();
+ }
+ }
+ }
+
+ private void waitForGlobalSuccessOrFailure() throws IOException {
+ synchronized(channels) {
+ while((globalSuccessCounter == 0) && (globalFailedCounter == 0)) {
+ if(shutdown) {
+ throw new IOException("The connection is being shutdown");
+ }
+
+ try {
+ channels.wait();
+ }
+ catch(InterruptedException e) {
+ throw new InterruptedIOException(e.getMessage());
+ }
+ }
+ if((globalFailedCounter == 0) && (globalSuccessCounter == 1)) {
+ return;
+ }
+ if((globalFailedCounter == 1) && (globalSuccessCounter == 0)) {
+ throw new IOException("The server denied the request (did you enable port forwarding?)");
+ }
+ throw new IOException("Illegal state. The server sent " + globalSuccessCounter
+ + " SSH_MSG_REQUEST_SUCCESS and " + globalFailedCounter + " SSH_MSG_REQUEST_FAILURE messages.");
+ }
+ }
+
+ private void waitForChannelSuccessOrFailure(Channel c) throws IOException {
+ synchronized(c) {
+ while((c.successCounter == 0) && (c.failedCounter == 0)) {
+ if(c.state != Channel.STATE_OPEN) {
+ throw c.getReasonClosed();
+ }
+ try {
+ c.wait();
+ }
+ catch(InterruptedException ignore) {
+ throw new InterruptedIOException();
+ }
+ }
+ if((c.failedCounter == 0) && (c.successCounter == 1)) {
+ return;
+ }
+ if((c.failedCounter == 1) && (c.successCounter == 0)) {
+ throw new IOException("The server denied the request.");
+ }
+ throw new IOException("Illegal state. The server sent " + c.successCounter
+ + " SSH_MSG_CHANNEL_SUCCESS and " + c.failedCounter + " SSH_MSG_CHANNEL_FAILURE messages.");
+ }
+ }
+
+ public void registerX11Cookie(String hexFakeCookie, X11ServerData data) {
+ synchronized(x11_magic_cookies) {
+ x11_magic_cookies.put(hexFakeCookie, data);
+ }
+ }
+
+ public void unRegisterX11Cookie(String hexFakeCookie, boolean killChannels) {
+ if(hexFakeCookie == null) {
+ throw new IllegalStateException("hexFakeCookie may not be null");
+ }
+
+ synchronized(x11_magic_cookies) {
+ x11_magic_cookies.remove(hexFakeCookie);
+ }
+
+ if(killChannels == false) {
+ return;
+ }
+
+ log.debug("Closing all X11 channels for the given fake cookie");
+
+ List channel_copy = new ArrayList();
+
+ synchronized(channels) {
+ channel_copy.addAll(channels);
+ }
+
+ for(Channel c : channel_copy) {
+ synchronized(c) {
+ if(hexFakeCookie.equals(c.hexX11FakeCookie) == false) {
+ continue;
+ }
+ }
+
+ try {
+ closeChannel(c, "Closing X11 channel since the corresponding session is closing", true);
+ }
+ catch(IOException ignored) {
+ }
+ }
+ }
+
+ public X11ServerData checkX11Cookie(String hexFakeCookie) {
+ synchronized(x11_magic_cookies) {
+ if(hexFakeCookie != null) {
+ return x11_magic_cookies.get(hexFakeCookie);
+ }
+ }
+ return null;
+ }
+
+ public void closeAllChannels() {
+ log.debug("Closing all channels");
+
+ List channel_copy = new ArrayList();
+
+ synchronized(channels) {
+ channel_copy.addAll(channels);
+ }
+
+ for(Channel c : channel_copy) {
+ try {
+ closeChannel(c, "Closing all channels", true);
+ }
+ catch(IOException ignored) {
+ }
+ }
+ }
+
+ public void closeChannel(Channel c, String reason, boolean force) throws IOException {
+ this.closeChannel(c, new ChannelClosedException(reason), force);
+ }
+
+ public void closeChannel(Channel c, IOException reason, boolean force) throws IOException {
+ byte msg[] = new byte[5];
+
+ synchronized(c) {
+ if(force) {
+ c.state = Channel.STATE_CLOSED;
+ c.EOF = true;
+ }
+
+ c.setReasonClosed(reason);
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_CLOSE;
+ msg[1] = (byte) (c.remoteID >> 24);
+ msg[2] = (byte) (c.remoteID >> 16);
+ msg[3] = (byte) (c.remoteID >> 8);
+ msg[4] = (byte) (c.remoteID);
+
+ c.notifyAll();
+ }
+
+ synchronized(c.channelSendLock) {
+ if(c.closeMessageSent) {
+ return;
+ }
+ tm.sendMessage(msg);
+ c.closeMessageSent = true;
+ }
+
+ log.debug("Sent SSH_MSG_CHANNEL_CLOSE (channel " + c.localID + ")");
+ }
+
+ public void sendEOF(Channel c) throws IOException {
+ byte[] msg = new byte[5];
+
+ synchronized(c) {
+ if(c.state != Channel.STATE_OPEN) {
+ return;
+ }
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_EOF;
+ msg[1] = (byte) (c.remoteID >> 24);
+ msg[2] = (byte) (c.remoteID >> 16);
+ msg[3] = (byte) (c.remoteID >> 8);
+ msg[4] = (byte) (c.remoteID);
+ }
+
+ synchronized(c.channelSendLock) {
+ if(c.closeMessageSent == true) {
+ return;
+ }
+ tm.sendMessage(msg);
+ }
+
+
+ log.debug("Sent EOF (Channel " + c.localID + "/" + c.remoteID + ")");
+ }
+
+ public void sendOpenConfirmation(Channel c) throws IOException {
+ PacketChannelOpenConfirmation pcoc = null;
+
+ synchronized(c) {
+ if(c.state != Channel.STATE_OPENING) {
+ return;
+ }
+
+ c.state = Channel.STATE_OPEN;
+
+ pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID, c.localWindow, c.localMaxPacketSize);
+ }
+
+ synchronized(c.channelSendLock) {
+ if(c.closeMessageSent == true) {
+ return;
+ }
+ tm.sendMessage(pcoc.getPayload());
+ }
+ }
+
+ public void sendData(Channel c, byte[] buffer, int pos, int len) throws IOException {
+ while(len > 0) {
+ int thislen = 0;
+ byte[] msg;
+
+ synchronized(c) {
+ while(true) {
+ if(c.state == Channel.STATE_CLOSED) {
+ throw c.getReasonClosed();
+ }
+ if(c.state != Channel.STATE_OPEN) {
+ throw new ChannelClosedException("SSH channel in strange state. (" + c.state + ")");
+ }
+
+ if(c.remoteWindow != 0) {
+ break;
+ }
+
+ try {
+ c.wait();
+ }
+ catch(InterruptedException e) {
+ throw new InterruptedIOException(e.getMessage());
+ }
+ }
+
+ /* len > 0, no sign extension can happen when comparing */
+
+ thislen = (c.remoteWindow >= len) ? len : (int) c.remoteWindow;
+
+ int estimatedMaxDataLen = c.remoteMaxPacketSize - (tm.getPacketOverheadEstimate() + 9);
+
+ /* The worst case scenario =) a true bottleneck */
+
+ if(estimatedMaxDataLen <= 0) {
+ estimatedMaxDataLen = 1;
+ }
+
+ if(thislen > estimatedMaxDataLen) {
+ thislen = estimatedMaxDataLen;
+ }
+
+ c.remoteWindow -= thislen;
+
+ msg = new byte[1 + 8 + thislen];
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_DATA;
+ msg[1] = (byte) (c.remoteID >> 24);
+ msg[2] = (byte) (c.remoteID >> 16);
+ msg[3] = (byte) (c.remoteID >> 8);
+ msg[4] = (byte) (c.remoteID);
+ msg[5] = (byte) (thislen >> 24);
+ msg[6] = (byte) (thislen >> 16);
+ msg[7] = (byte) (thislen >> 8);
+ msg[8] = (byte) (thislen);
+
+ System.arraycopy(buffer, pos, msg, 9, thislen);
+ }
+
+ synchronized(c.channelSendLock) {
+ if(c.closeMessageSent) {
+ throw c.getReasonClosed();
+ }
+ tm.sendMessage(msg);
+ }
+
+ pos += thislen;
+ len -= thislen;
+ }
+ }
+
+ public int requestGlobalForward(String bindAddress, int bindPort, String targetAddress, int targetPort)
+ throws IOException {
+ RemoteForwardingData rfd = new RemoteForwardingData();
+
+ rfd.bindAddress = bindAddress;
+ rfd.bindPort = bindPort;
+ rfd.targetAddress = targetAddress;
+ rfd.targetPort = targetPort;
+
+ synchronized(remoteForwardings) {
+ if(remoteForwardings.get(bindPort) != null) {
+ throw new IOException("There is already a forwarding for remote port " + bindPort);
+ }
+ remoteForwardings.put(bindPort, rfd);
+ }
+
+ synchronized(channels) {
+ globalSuccessCounter = globalFailedCounter = 0;
+ }
+
+ PacketGlobalForwardRequest pgf = new PacketGlobalForwardRequest(true, bindAddress, bindPort);
+ tm.sendMessage(pgf.getPayload());
+
+ log.debug("Requesting a remote forwarding ('" + bindAddress + "', " + bindPort + ")");
+
+ try {
+ waitForGlobalSuccessOrFailure();
+ }
+ catch(IOException e) {
+ synchronized(remoteForwardings) {
+ remoteForwardings.remove(bindPort);
+ }
+ throw e;
+ }
+
+ return bindPort;
+ }
+
+ public void requestCancelGlobalForward(int bindPort) throws IOException {
+ RemoteForwardingData rfd;
+
+ synchronized(remoteForwardings) {
+ rfd = remoteForwardings.get(bindPort);
+
+ if(rfd == null) {
+ throw new IOException("Sorry, there is no known remote forwarding for remote port " + bindPort);
+ }
+ }
+
+ synchronized(channels) {
+ globalSuccessCounter = globalFailedCounter = 0;
+ }
+
+ PacketGlobalCancelForwardRequest pgcf = new PacketGlobalCancelForwardRequest(true, rfd.bindAddress,
+ rfd.bindPort);
+ tm.sendMessage(pgcf.getPayload());
+
+ log.debug("Requesting cancelation of remote forward ('" + rfd.bindAddress + "', " + rfd.bindPort + ")");
+
+ waitForGlobalSuccessOrFailure();
+
+ /* Only now we are sure that no more forwarded connections will arrive */
+
+ synchronized(remoteForwardings) {
+ remoteForwardings.remove(bindPort);
+ }
+ }
+
+ public void registerThread(IChannelWorkerThread thr) throws IOException {
+ synchronized(listenerThreads) {
+ if(listenerThreadsAllowed == false) {
+ throw new IOException("Too late, this connection is closed.");
+ }
+ listenerThreads.add(thr);
+ }
+ }
+
+ public Channel openDirectTCPIPChannel(String host_to_connect, int port_to_connect, String originator_IP_address,
+ int originator_port) throws IOException {
+ Channel c = new Channel(this);
+
+ synchronized(c) {
+ c.localID = addChannel(c);
+ // end of synchronized block forces writing out to main memory
+ }
+
+ PacketOpenDirectTCPIPChannel dtc = new PacketOpenDirectTCPIPChannel(c.localID, c.localWindow,
+ c.localMaxPacketSize, host_to_connect, port_to_connect, originator_IP_address, originator_port);
+
+ tm.sendMessage(dtc.getPayload());
+
+ waitUntilChannelOpen(c);
+
+ return c;
+ }
+
+ public Channel openSessionChannel() throws IOException {
+ Channel c = new Channel(this);
+
+ synchronized(c) {
+ c.localID = addChannel(c);
+ // end of synchronized block forces the writing out to main memory
+ }
+
+ log.debug("Sending SSH_MSG_CHANNEL_OPEN (Channel " + c.localID + ")");
+
+ PacketOpenSessionChannel smo = new PacketOpenSessionChannel(c.localID, c.localWindow, c.localMaxPacketSize);
+ tm.sendMessage(smo.getPayload());
+
+ waitUntilChannelOpen(c);
+
+ return c;
+ }
+
+ public void requestPTY(Channel c, String term, int term_width_characters, int term_height_characters,
+ int term_width_pixels, int term_height_pixels, byte[] terminal_modes) throws IOException {
+ PacketSessionPtyRequest spr;
+
+ synchronized(c) {
+ if(c.state != Channel.STATE_OPEN) {
+ throw c.getReasonClosed();
+ }
+ spr = new PacketSessionPtyRequest(c.remoteID, true, term, term_width_characters, term_height_characters,
+ term_width_pixels, term_height_pixels, terminal_modes);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized(c.channelSendLock) {
+ if(c.closeMessageSent) {
+ throw c.getReasonClosed();
+ }
+ tm.sendMessage(spr.getPayload());
+ }
+
+ try {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch(IOException e) {
+ throw new IOException("PTY request failed", e);
+ }
+ }
+
+ public void requestWindowChange(Channel c, int term_width_characters, int term_height_characters,
+ int term_width_pixels, int term_height_pixels) throws IOException {
+ PacketWindowChange pwc;
+
+ synchronized(c) {
+ if(c.state != Channel.STATE_OPEN) {
+ throw c.getReasonClosed();
+ }
+ pwc = new PacketWindowChange(c.remoteID, term_width_characters, term_height_characters,
+ term_width_pixels, term_height_pixels);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized(c.channelSendLock) {
+ if(c.closeMessageSent) {
+ throw c.getReasonClosed();
+ }
+ tm.sendMessage(pwc.getPayload());
+ }
+
+ try {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch(IOException e) {
+ throw new IOException("The window-change request failed.", e);
+ }
+ }
+
+ public void requestX11(Channel c, boolean singleConnection, String x11AuthenticationProtocol,
+ String x11AuthenticationCookie, int x11ScreenNumber) throws IOException {
+ PacketSessionX11Request psr;
+
+ synchronized(c) {
+ if(c.state != Channel.STATE_OPEN) {
+ throw c.getReasonClosed();
+ }
+ psr = new PacketSessionX11Request(c.remoteID, true, singleConnection, x11AuthenticationProtocol,
+ x11AuthenticationCookie, x11ScreenNumber);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized(c.channelSendLock) {
+ if(c.closeMessageSent) {
+ throw c.getReasonClosed();
+ }
+ tm.sendMessage(psr.getPayload());
+ }
+
+ log.debug("Requesting X11 forwarding (Channel " + c.localID + "/" + c.remoteID + ")");
+
+ try {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch(IOException e) {
+ throw new IOException("The X11 request failed.", e);
+ }
+ }
+
+ public void requestSubSystem(Channel c, String subSystemName) throws IOException {
+ PacketSessionSubsystemRequest ssr;
+
+ synchronized(c) {
+ if(c.state != Channel.STATE_OPEN) {
+ throw c.getReasonClosed();
+ }
+ ssr = new PacketSessionSubsystemRequest(c.remoteID, true, subSystemName);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized(c.channelSendLock) {
+ if(c.closeMessageSent) {
+ throw c.getReasonClosed();
+ }
+ tm.sendMessage(ssr.getPayload());
+ }
+
+ try {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch(IOException e) {
+ throw new IOException("The subsystem request failed.", e);
+ }
+ }
+
+ public void requestExecCommand(Channel c, String cmd) throws IOException {
+ this.requestExecCommand(c, cmd, null);
+ }
+
+ /**
+ * @param charsetName The charset used to convert between Java Unicode Strings and byte encodings
+ */
+ public void requestExecCommand(Channel c, String cmd, String charsetName) throws IOException {
+ PacketSessionExecCommand sm;
+
+ synchronized(c) {
+ if(c.state != Channel.STATE_OPEN) {
+ throw c.getReasonClosed();
+ }
+ sm = new PacketSessionExecCommand(c.remoteID, true, cmd, charsetName);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized(c.channelSendLock) {
+ if(c.closeMessageSent) {
+ throw c.getReasonClosed();
+ }
+ tm.sendMessage(sm.getPayload());
+ }
+
+ log.debug("Executing command (channel " + c.localID + ", '" + cmd + "')");
+
+ try {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch(IOException e) {
+ throw new IOException("The execute request failed.", e);
+ }
+ }
+
+ public void requestShell(Channel c) throws IOException {
+ PacketSessionStartShell sm;
+
+ synchronized(c) {
+ if(c.state != Channel.STATE_OPEN) {
+ throw c.getReasonClosed();
+ }
+ sm = new PacketSessionStartShell(c.remoteID, true);
+
+ c.successCounter = c.failedCounter = 0;
+ }
+
+ synchronized(c.channelSendLock) {
+ if(c.closeMessageSent) {
+ throw c.getReasonClosed();
+ }
+ tm.sendMessage(sm.getPayload());
+ }
+
+ try {
+ waitForChannelSuccessOrFailure(c);
+ }
+ catch(IOException e) {
+ throw new IOException("The shell request failed.", e);
+ }
+ }
+
+ public void msgChannelExtendedData(byte[] msg) throws IOException {
+ if(msg.length <= 13) {
+ throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong size (%d)", msg.length));
+ }
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+ int dataType = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+ int len = ((msg[9] & 0xff) << 24) | ((msg[10] & 0xff) << 16) | ((msg[11] & 0xff) << 8) | (msg[12] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if(c == null) {
+ throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_EXTENDED_DATA message for non-existent channel " + id);
+ }
+
+ if(dataType != Packets.SSH_EXTENDED_DATA_STDERR) {
+ throw new PacketFormatException("SSH_MSG_CHANNEL_EXTENDED_DATA message has unknown type (" + dataType + ")");
+ }
+
+ if(len != (msg.length - 13)) {
+ throw new PacketFormatException("SSH_MSG_CHANNEL_EXTENDED_DATA message has wrong len (calculated " + (msg.length - 13)
+ + ", got " + len + ")");
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_EXTENDED_DATA (channel " + id + ", " + len + ")");
+
+ synchronized(c) {
+ if(c.state == Channel.STATE_CLOSED) {
+ return; // ignore
+ }
+
+ if(c.state != Channel.STATE_OPEN) {
+ throw new PacketTypeException("Got SSH_MSG_CHANNEL_EXTENDED_DATA, but channel is not in correct state ("
+ + c.state + ")");
+ }
+
+ if(c.localWindow < len) {
+ throw new PacketFormatException("Remote sent too much data, does not fit into window.");
+ }
+
+ c.localWindow -= len;
+
+ System.arraycopy(msg, 13, c.stderrBuffer, c.stderrWritepos, len);
+ c.stderrWritepos += len;
+
+ c.notifyAll();
+ }
+ }
+
+ /**
+ * Wait until for a condition.
+ *
+ * @param c Channel
+ * @param timeout in ms, 0 means no timeout.
+ * @param condition_mask minimum event mask (at least one of the conditions must be fulfilled)
+ * @return all current events
+ */
+ public int waitForCondition(Channel c, long timeout, int condition_mask) throws IOException {
+ long end_time = 0;
+ boolean end_time_set = false;
+
+ synchronized(c) {
+ while(true) {
+ int current_cond = 0;
+
+ int stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
+ int stderrAvail = c.stderrWritepos - c.stderrReadpos;
+
+ if(stdoutAvail > 0) {
+ current_cond = current_cond | ChannelCondition.STDOUT_DATA;
+ }
+
+ if(stderrAvail > 0) {
+ current_cond = current_cond | ChannelCondition.STDERR_DATA;
+ }
+
+ if(c.EOF) {
+ current_cond = current_cond | ChannelCondition.EOF;
+ }
+
+ if(c.getExitStatus() != null) {
+ current_cond = current_cond | ChannelCondition.EXIT_STATUS;
+ }
+
+ if(c.getExitSignal() != null) {
+ current_cond = current_cond | ChannelCondition.EXIT_SIGNAL;
+ }
+
+ if(c.state == Channel.STATE_CLOSED) {
+ return current_cond | ChannelCondition.CLOSED | ChannelCondition.EOF;
+ }
+
+ if((current_cond & condition_mask) != 0) {
+ return current_cond;
+ }
+
+ if(timeout > 0) {
+ if(!end_time_set) {
+ end_time = System.currentTimeMillis() + timeout;
+ end_time_set = true;
+ }
+ else {
+ timeout = end_time - System.currentTimeMillis();
+
+ if(timeout <= 0) {
+ return current_cond | ChannelCondition.TIMEOUT;
+ }
+ }
+ }
+
+ try {
+ if(timeout > 0) {
+ c.wait(timeout);
+ }
+ else {
+ c.wait();
+ }
+ }
+ catch(InterruptedException e) {
+ throw new InterruptedIOException(e.getMessage());
+ }
+ }
+ }
+ }
+
+ public int getAvailable(Channel c, boolean extended) throws IOException {
+ synchronized(c) {
+ int avail;
+
+ if(extended) {
+ avail = c.stderrWritepos - c.stderrReadpos;
+ }
+ else {
+ avail = c.stdoutWritepos - c.stdoutReadpos;
+ }
+
+ return ((avail > 0) ? avail : (c.EOF ? -1 : 0));
+ }
+ }
+
+ public int getChannelData(Channel c, boolean extended, byte[] target, int off, int len) throws IOException {
+ int copylen = 0;
+ int increment = 0;
+ int remoteID = 0;
+ int localID = 0;
+
+ synchronized(c) {
+ int stdoutAvail = 0;
+ int stderrAvail = 0;
+
+ while(true) {
+ /*
+ * Data available? We have to return remaining data even if the
+ * channel is already closed.
+ */
+
+ stdoutAvail = c.stdoutWritepos - c.stdoutReadpos;
+ stderrAvail = c.stderrWritepos - c.stderrReadpos;
+
+ if((!extended) && (stdoutAvail != 0)) {
+ break;
+ }
+
+ if((extended) && (stderrAvail != 0)) {
+ break;
+ }
+
+ /* Do not wait if more data will never arrive (EOF or CLOSED) */
+
+ if((c.EOF) || (c.state != Channel.STATE_OPEN)) {
+ return -1;
+ }
+
+ try {
+ c.wait();
+ }
+ catch(InterruptedException e) {
+ throw new InterruptedIOException(e.getMessage());
+ }
+ }
+
+ /* OK, there is some data. Return it. */
+
+ if(!extended) {
+ copylen = (stdoutAvail > len) ? len : stdoutAvail;
+ System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, target, off, copylen);
+ c.stdoutReadpos += copylen;
+
+ if(c.stdoutReadpos != c.stdoutWritepos)
+
+ {
+ System.arraycopy(c.stdoutBuffer, c.stdoutReadpos, c.stdoutBuffer, 0, c.stdoutWritepos
+ - c.stdoutReadpos);
+ }
+
+ c.stdoutWritepos -= c.stdoutReadpos;
+ c.stdoutReadpos = 0;
+ }
+ else {
+ copylen = (stderrAvail > len) ? len : stderrAvail;
+ System.arraycopy(c.stderrBuffer, c.stderrReadpos, target, off, copylen);
+ c.stderrReadpos += copylen;
+
+ if(c.stderrReadpos != c.stderrWritepos)
+
+ {
+ System.arraycopy(c.stderrBuffer, c.stderrReadpos, c.stderrBuffer, 0, c.stderrWritepos
+ - c.stderrReadpos);
+ }
+
+ c.stderrWritepos -= c.stderrReadpos;
+ c.stderrReadpos = 0;
+ }
+
+ if(c.state != Channel.STATE_OPEN) {
+ return copylen;
+ }
+
+ if(c.localWindow < ((Channel.CHANNEL_BUFFER_SIZE + 1) / 2)) {
+ int minFreeSpace = Math.min(Channel.CHANNEL_BUFFER_SIZE - c.stdoutWritepos,
+ Channel.CHANNEL_BUFFER_SIZE - c.stderrWritepos);
+
+ increment = minFreeSpace - c.localWindow;
+ c.localWindow = minFreeSpace;
+ }
+
+ remoteID = c.remoteID; /* read while holding the lock */
+ localID = c.localID; /* read while holding the lock */
+ }
+
+ /*
+ * If a consumer reads stdout and stdin in parallel, we may end up with
+ * sending two msgWindowAdjust messages. Luckily, it
+ * does not matter in which order they arrive at the server.
+ */
+
+ if(increment > 0) {
+ log.debug("Sending SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + localID + ", " + increment + ")");
+
+ synchronized(c.channelSendLock) {
+ byte[] msg = c.msgWindowAdjust;
+
+ msg[0] = Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST;
+ msg[1] = (byte) (remoteID >> 24);
+ msg[2] = (byte) (remoteID >> 16);
+ msg[3] = (byte) (remoteID >> 8);
+ msg[4] = (byte) (remoteID);
+ msg[5] = (byte) (increment >> 24);
+ msg[6] = (byte) (increment >> 16);
+ msg[7] = (byte) (increment >> 8);
+ msg[8] = (byte) (increment);
+
+ if(!c.closeMessageSent) {
+ tm.sendMessage(msg);
+ }
+ }
+ }
+
+ return copylen;
+ }
+
+ public void msgChannelData(byte[] msg) throws IOException {
+ if(msg.length <= 9) {
+ throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_DATA message has wrong size (%d)", msg.length));
+ }
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+ int len = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if(c == null) {
+ throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_DATA message for non-existent channel " + id);
+ }
+
+ if(len != (msg.length - 9)) {
+ throw new PacketFormatException("SSH_MSG_CHANNEL_DATA message has wrong len (calculated " + (msg.length - 9) + ", got "
+ + len + ")");
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_DATA (channel " + id + ", " + len + ")");
+
+ synchronized(c) {
+ if(c.state == Channel.STATE_CLOSED) {
+ return; // ignore
+ }
+
+ if(c.state != Channel.STATE_OPEN) {
+ throw new PacketTypeException("Got SSH_MSG_CHANNEL_DATA, but channel is not in correct state (" + c.state + ")");
+ }
+
+ if(c.localWindow < len) {
+ throw new IOException("Remote sent too much data, does not fit into window.");
+ }
+
+ c.localWindow -= len;
+
+ System.arraycopy(msg, 9, c.stdoutBuffer, c.stdoutWritepos, len);
+ c.stdoutWritepos += len;
+
+ c.notifyAll();
+ }
+ }
+
+ public void msgChannelWindowAdjust(byte[] msg) throws IOException {
+ if(msg.length != 9) {
+ throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_WINDOW_ADJUST message has wrong size (%d)", msg.length));
+ }
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+ int windowChange = ((msg[5] & 0xff) << 24) | ((msg[6] & 0xff) << 16) | ((msg[7] & 0xff) << 8) | (msg[8] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if(c == null) {
+ throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_WINDOW_ADJUST message for non-existent channel " + id);
+ }
+
+ synchronized(c) {
+ final long huge = 0xFFFFffffL; /* 2^32 - 1 */
+
+ c.remoteWindow += (windowChange & huge); /* avoid sign extension */
+
+ /* TODO - is this a good heuristic? */
+
+ if((c.remoteWindow > huge)) {
+ c.remoteWindow = huge;
+ }
+
+ c.notifyAll();
+ }
+
+
+ log.debug("Got SSH_MSG_CHANNEL_WINDOW_ADJUST (channel " + id + ", " + windowChange + ")");
+ }
+
+ public void msgChannelOpen(byte[] msg) throws IOException {
+ TypesReader tr = new TypesReader(msg);
+
+ tr.readByte(); // skip packet type
+ String channelType = tr.readString();
+ int remoteID = tr.readUINT32(); /* sender channel */
+ int remoteWindow = tr.readUINT32(); /* initial window size */
+ int remoteMaxPacketSize = tr.readUINT32(); /* maximum packet size */
+
+ if("x11".equals(channelType)) {
+ synchronized(x11_magic_cookies) {
+ /* If we did not request X11 forwarding, then simply ignore this bogus request. */
+
+ if(x11_magic_cookies.size() == 0) {
+ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
+ Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, "X11 forwarding not activated", "");
+
+ tm.sendAsynchronousMessage(pcof.getPayload());
+
+ log.warning("Unexpected X11 request, denying it!");
+
+ return;
+ }
+ }
+
+ String remoteOriginatorAddress = tr.readString();
+ int remoteOriginatorPort = tr.readUINT32();
+
+ Channel c = new Channel(this);
+
+ synchronized(c) {
+ c.remoteID = remoteID;
+ c.remoteWindow = remoteWindow & 0xFFFFffffL; /* properly convert UINT32 to long */
+ c.remoteMaxPacketSize = remoteMaxPacketSize;
+ c.localID = addChannel(c);
+ }
+
+ /*
+ * The open confirmation message will be sent from another thread
+ */
+
+ RemoteX11AcceptThread rxat = new RemoteX11AcceptThread(c, remoteOriginatorAddress, remoteOriginatorPort);
+ rxat.setDaemon(true);
+ rxat.start();
+
+ return;
+ }
+
+ if("forwarded-tcpip".equals(channelType)) {
+ String remoteConnectedAddress = tr.readString(); /* address that was connected */
+ int remoteConnectedPort = tr.readUINT32(); /* port that was connected */
+ String remoteOriginatorAddress = tr.readString(); /* originator IP address */
+ int remoteOriginatorPort = tr.readUINT32(); /* originator port */
+
+ RemoteForwardingData rfd;
+
+ synchronized(remoteForwardings) {
+ rfd = remoteForwardings.get(remoteConnectedPort);
+ }
+
+ if(rfd == null) {
+ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID,
+ Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
+ "No thanks, unknown port in forwarded-tcpip request", "");
+
+ /* Always try to be polite. */
+
+ tm.sendAsynchronousMessage(pcof.getPayload());
+
+ log.debug("Unexpected forwarded-tcpip request, denying it!");
+
+ return;
+ }
+
+ Channel c = new Channel(this);
+
+ synchronized(c) {
+ c.remoteID = remoteID;
+ c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
+ c.remoteMaxPacketSize = remoteMaxPacketSize;
+ c.localID = addChannel(c);
+ }
+
+ /*
+ * The open confirmation message will be sent from another thread.
+ */
+
+ RemoteAcceptThread rat = new RemoteAcceptThread(c, remoteConnectedAddress, remoteConnectedPort,
+ remoteOriginatorAddress, remoteOriginatorPort, rfd.targetAddress, rfd.targetPort);
+
+ rat.setDaemon(true);
+ rat.start();
+
+ return;
+ }
+
+ if((server_state != null) && ("session".equals(channelType))) {
+ ServerConnectionCallback cb;
+
+ synchronized(server_state) {
+ cb = server_state.cb_conn;
+ }
+
+ if(cb == null) {
+ tm.sendAsynchronousMessage(new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,
+ "Sessions are currently not enabled", "en").getPayload());
+
+ return;
+ }
+
+ final Channel c = new Channel(this);
+
+ synchronized(c) {
+ c.remoteID = remoteID;
+ c.remoteWindow = remoteWindow & 0xFFFFffffL; /* convert UINT32 to long */
+ c.remoteMaxPacketSize = remoteMaxPacketSize;
+ c.localID = addChannel(c);
+ c.state = Channel.STATE_OPEN;
+ c.ss = new ServerSessionImpl(c);
+ }
+
+ PacketChannelOpenConfirmation pcoc = new PacketChannelOpenConfirmation(c.remoteID, c.localID,
+ c.localWindow, c.localMaxPacketSize);
+
+ tm.sendAsynchronousMessage(pcoc.getPayload());
+
+ c.ss.sscb = cb.acceptSession(c.ss);
+
+ return;
+ }
+
+ /* Tell the server that we have no idea what it is talking about */
+
+ PacketChannelOpenFailure pcof = new PacketChannelOpenFailure(remoteID, Packets.SSH_OPEN_UNKNOWN_CHANNEL_TYPE,
+ "Unknown channel type", "");
+
+ tm.sendAsynchronousMessage(pcof.getPayload());
+
+
+ log.warning("The peer tried to open an unsupported channel type (" + channelType + ")");
+ }
+
+ /* Starts the given runnable in a foreground (non-daemon) thread */
+ private void runAsync(Runnable r) {
+ Thread t = new Thread(r);
+ t.start();
+ }
+
+ public void msgChannelRequest(byte[] msg) throws IOException {
+ TypesReader tr = new TypesReader(msg);
+
+ tr.readByte(); // skip packet type
+ int id = tr.readUINT32();
+
+ Channel c = getChannel(id);
+
+ if(c == null) {
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_REQUEST message for non-existent channel " + id);
+ }
+
+ ServerSessionImpl server_session = null;
+
+ if(server_state != null) {
+ synchronized(c) {
+ server_session = c.ss;
+ }
+ }
+
+ String type = tr.readString("US-ASCII");
+ boolean wantReply = tr.readBoolean();
+
+ log.debug("Got SSH_MSG_CHANNEL_REQUEST (channel " + id + ", '" + type + "')");
+
+ if(type.equals("exit-status")) {
+ if(wantReply) {
+ throw new IOException(
+ "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-status message, 'want reply' is true");
+ }
+
+ int exit_status = tr.readUINT32();
+
+ if(tr.remain() != 0) {
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+ }
+
+ synchronized(c) {
+ c.exit_status = exit_status;
+ c.notifyAll();
+ }
+
+ log.debug("Got EXIT STATUS (channel " + id + ", status " + exit_status + ")");
+
+ return;
+ }
+
+ if((server_state == null) && (type.equals("exit-signal"))) {
+ if(wantReply) {
+ throw new IOException(
+ "Badly formatted SSH_MSG_CHANNEL_REQUEST exit-signal message, 'want reply' is true");
+ }
+
+ String signame = tr.readString("US-ASCII");
+ tr.readBoolean();
+ tr.readString();
+ tr.readString();
+
+ if(tr.remain() != 0) {
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+ }
+
+ synchronized(c) {
+ c.exit_signal = signame;
+ c.notifyAll();
+ }
+
+ log.debug("Got EXIT SIGNAL (channel " + id + ", signal " + signame + ")");
+
+ return;
+ }
+
+ if((server_session != null) && (type.equals("pty-req"))) {
+ PtySettings pty = new PtySettings();
+
+ pty.term = tr.readString();
+ pty.term_width_characters = tr.readUINT32();
+ pty.term_height_characters = tr.readUINT32();
+ pty.term_width_pixels = tr.readUINT32();
+ pty.term_height_pixels = tr.readUINT32();
+ pty.terminal_modes = tr.readByteString();
+
+ if(tr.remain() != 0) {
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+ }
+
+ Runnable run_after_sending_success = null;
+
+ ServerSessionCallback sscb = server_session.getServerSessionCallback();
+
+ if(sscb != null) {
+ run_after_sending_success = sscb.requestPtyReq(server_session, pty);
+ }
+
+ if(wantReply) {
+ if(run_after_sending_success != null) {
+ tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
+ }
+ else {
+ tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+ }
+ }
+
+ if(run_after_sending_success != null) {
+ runAsync(run_after_sending_success);
+ }
+
+ return;
+ }
+
+ if((server_session != null) && (type.equals("shell"))) {
+ if(tr.remain() != 0) {
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+ }
+
+ Runnable run_after_sending_success = null;
+ ServerSessionCallback sscb = server_session.getServerSessionCallback();
+
+ if(sscb != null) {
+ run_after_sending_success = sscb.requestShell(server_session);
+ }
+
+ if(wantReply) {
+ if(run_after_sending_success != null) {
+ tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
+ }
+ else {
+ tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+ }
+ }
+
+ if(run_after_sending_success != null) {
+ runAsync(run_after_sending_success);
+ }
+
+ return;
+ }
+
+ if((server_session != null) && (type.equals("exec"))) {
+ String command = tr.readString();
+
+ if(tr.remain() != 0) {
+ throw new IOException("Badly formatted SSH_MSG_CHANNEL_REQUEST message");
+ }
+
+ Runnable run_after_sending_success = null;
+ ServerSessionCallback sscb = server_session.getServerSessionCallback();
+
+ if(sscb != null) {
+ run_after_sending_success = sscb.requestExec(server_session, command);
+ }
+
+ if(wantReply) {
+ if(run_after_sending_success != null) {
+ tm.sendAsynchronousMessage(new PacketChannelSuccess(c.remoteID).getPayload());
+ }
+ else {
+ tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+ }
+ }
+
+ if(run_after_sending_success != null) {
+ runAsync(run_after_sending_success);
+ }
+
+ return;
+ }
+
+ /* We simply ignore unknown channel requests, however, if the server wants a reply,
+ * then we signal that we have no idea what it is about.
+ */
+
+ if(wantReply) {
+ tm.sendAsynchronousMessage(new PacketChannelFailure(c.remoteID).getPayload());
+ }
+
+ log.debug("Channel request '" + type + "' is not known, ignoring it");
+ }
+
+ public void msgChannelEOF(byte[] msg) throws IOException {
+ if(msg.length != 5) {
+ throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_EOF message has wrong size (%d)", msg.length));
+ }
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if(c == null) {
+ throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_EOF message for non-existent channel " + id);
+ }
+
+ synchronized(c) {
+ c.EOF = true;
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_EOF (channel " + id + ")");
+ }
+
+ public void msgChannelClose(byte[] msg) throws IOException {
+ if(msg.length != 5) {
+ throw new PacketFormatException("SSH_MSG_CHANNEL_CLOSE message has wrong size (" + msg.length + ")");
+ }
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if(c == null) {
+ throw new IOException("Unexpected SSH_MSG_CHANNEL_CLOSE message for non-existent channel " + id);
+ }
+
+ synchronized(c) {
+ c.EOF = true;
+ c.state = Channel.STATE_CLOSED;
+ c.setReasonClosed(new ChannelClosedException("Close requested by remote"));
+ c.closeMessageRecv = true;
+
+ removeChannel(c.localID);
+
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_CLOSE (channel " + id + ")");
+ }
+
+ public void msgChannelSuccess(byte[] msg) throws IOException {
+ if(msg.length != 5) {
+ throw new PacketFormatException("SSH_MSG_CHANNEL_SUCCESS message has wrong size (" + msg.length + ")");
+ }
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if(c == null) {
+ throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_SUCCESS message for non-existent channel " + id);
+ }
+
+ synchronized(c) {
+ c.successCounter++;
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_SUCCESS (channel " + id + ")");
+ }
+
+ public void msgChannelFailure(byte[] msg) throws IOException {
+ if(msg.length != 5) {
+ throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_FAILURE message has wrong size (%d)", msg.length));
+ }
+
+ int id = ((msg[1] & 0xff) << 24) | ((msg[2] & 0xff) << 16) | ((msg[3] & 0xff) << 8) | (msg[4] & 0xff);
+
+ Channel c = getChannel(id);
+
+ if(c == null) {
+ throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_FAILURE message for non-existent channel " + id);
+ }
+
+ synchronized(c) {
+ c.failedCounter++;
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_FAILURE (channel " + id + ")");
+ }
+
+ public void msgChannelOpenConfirmation(byte[] msg) throws IOException {
+ PacketChannelOpenConfirmation sm = new PacketChannelOpenConfirmation(msg);
+
+ Channel c = getChannel(sm.getRecipientChannelID());
+
+ if(c == null) {
+ throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for non-existent channel "
+ + sm.getRecipientChannelID());
+ }
+
+ synchronized(c) {
+ if(c.state != Channel.STATE_OPENING) {
+ throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_OPEN_CONFIRMATION message for channel "
+ + sm.getRecipientChannelID());
+ }
+
+ c.remoteID = sm.getSenderChannelID();
+ c.remoteWindow = sm.getInitialWindowSize() & 0xFFFFffffL; /* convert UINT32 to long */
+ c.remoteMaxPacketSize = sm.getMaxPacketSize();
+ c.state = Channel.STATE_OPEN;
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_OPEN_CONFIRMATION (channel " + sm.getRecipientChannelID() + " / remote: "
+ + sm.getSenderChannelID() + ")");
+ }
+
+ public void msgChannelOpenFailure(byte[] msg) throws IOException {
+ if(msg.length < 5) {
+ throw new PacketFormatException(String.format("SSH_MSG_CHANNEL_OPEN_FAILURE message has wrong size (%d)", msg.length));
+ }
+
+ TypesReader tr = new TypesReader(msg);
+
+ tr.readByte(); // skip packet type
+ int id = tr.readUINT32(); /* sender channel */
+
+ Channel c = getChannel(id);
+
+ if(c == null) {
+ throw new PacketTypeException("Unexpected SSH_MSG_CHANNEL_OPEN_FAILURE message for non-existent channel " + id);
+ }
+
+ int reasonCode = tr.readUINT32();
+ String description = tr.readString("UTF-8");
+
+ String reasonCodeSymbolicName;
+
+ switch(reasonCode) {
+ case 1:
+ reasonCodeSymbolicName = "SSH_OPEN_ADMINISTRATIVELY_PROHIBITED";
+ break;
+ case 2:
+ reasonCodeSymbolicName = "SSH_OPEN_CONNECT_FAILED";
+ break;
+ case 3:
+ reasonCodeSymbolicName = "SSH_OPEN_UNKNOWN_CHANNEL_TYPE";
+ break;
+ case 4:
+ reasonCodeSymbolicName = "SSH_OPEN_RESOURCE_SHORTAGE";
+ break;
+ default:
+ reasonCodeSymbolicName = "UNKNOWN REASON CODE (" + reasonCode + ")";
+ }
+
+ StringBuilder descriptionBuffer = new StringBuilder();
+ descriptionBuffer.append(description);
+
+ for(int i = 0; i < descriptionBuffer.length(); i++) {
+ char cc = descriptionBuffer.charAt(i);
+
+ if((cc >= 32) && (cc <= 126)) {
+ continue;
+ }
+ descriptionBuffer.setCharAt(i, '\uFFFD');
+ }
+
+ synchronized(c) {
+ c.EOF = true;
+ c.state = Channel.STATE_CLOSED;
+ c.setReasonClosed(new ChannelClosedException(String.format("The server refused to open the channel (%s, '%s')",
+ reasonCodeSymbolicName, descriptionBuffer.toString())));
+ c.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_CHANNEL_OPEN_FAILURE (channel " + id + ")");
+ }
+
+ public void msgGlobalRequest(byte[] msg) throws IOException {
+ /* Currently we do not support any kind of global request */
+
+ TypesReader tr = new TypesReader(msg);
+
+ tr.readByte(); // skip packet type
+ String requestName = tr.readString();
+ boolean wantReply = tr.readBoolean();
+
+ if(wantReply) {
+ byte[] reply_failure = new byte[1];
+ reply_failure[0] = Packets.SSH_MSG_REQUEST_FAILURE;
+
+ tm.sendAsynchronousMessage(reply_failure);
+ }
+
+ /* We do not clean up the requestName String - that is OK for debug */
+
+ log.debug("Got SSH_MSG_GLOBAL_REQUEST (" + requestName + ")");
+ }
+
+ public void msgGlobalSuccess() throws IOException {
+ synchronized(channels) {
+ globalSuccessCounter++;
+ channels.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_REQUEST_SUCCESS");
+ }
+
+ public void msgGlobalFailure() throws IOException {
+ synchronized(channels) {
+ globalFailedCounter++;
+ channels.notifyAll();
+ }
+
+ log.debug("Got SSH_MSG_REQUEST_FAILURE");
+ }
+
+ @Override
+ public void handleFailure(final IOException failure) {
+ log.debug("HandleMessage: got shutdown");
+
+ synchronized(listenerThreads) {
+ for(IChannelWorkerThread lat : listenerThreads) {
+ lat.stopWorking();
+ }
+ listenerThreadsAllowed = false;
+ }
+
+ synchronized(channels) {
+ shutdown = true;
+
+ for(Channel c : channels) {
+ synchronized(c) {
+ c.EOF = true;
+ c.state = Channel.STATE_CLOSED;
+ c.setReasonClosed(failure);
+ c.closeMessageRecv = true;
+ c.notifyAll();
+ }
+ }
+ channels.clear();
+ channels.notifyAll(); /* Notify global response waiters */
+ }
+ }
+
+ public void handleMessage(byte[] msg) throws IOException {
+ switch(msg[0]) {
+ case Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
+ msgChannelOpenConfirmation(msg);
+ break;
+ case Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST:
+ msgChannelWindowAdjust(msg);
+ break;
+ case Packets.SSH_MSG_CHANNEL_DATA:
+ msgChannelData(msg);
+ break;
+ case Packets.SSH_MSG_CHANNEL_EXTENDED_DATA:
+ msgChannelExtendedData(msg);
+ break;
+ case Packets.SSH_MSG_CHANNEL_REQUEST:
+ msgChannelRequest(msg);
+ break;
+ case Packets.SSH_MSG_CHANNEL_EOF:
+ msgChannelEOF(msg);
+ break;
+ case Packets.SSH_MSG_CHANNEL_OPEN:
+ msgChannelOpen(msg);
+ break;
+ case Packets.SSH_MSG_CHANNEL_CLOSE:
+ msgChannelClose(msg);
+ break;
+ case Packets.SSH_MSG_CHANNEL_SUCCESS:
+ msgChannelSuccess(msg);
+ break;
+ case Packets.SSH_MSG_CHANNEL_FAILURE:
+ msgChannelFailure(msg);
+ break;
+ case Packets.SSH_MSG_CHANNEL_OPEN_FAILURE:
+ msgChannelOpenFailure(msg);
+ break;
+ case Packets.SSH_MSG_GLOBAL_REQUEST:
+ msgGlobalRequest(msg);
+ break;
+ case Packets.SSH_MSG_REQUEST_SUCCESS:
+ msgGlobalSuccess();
+ break;
+ case Packets.SSH_MSG_REQUEST_FAILURE:
+ msgGlobalFailure();
+ break;
+ default:
+ throw new PacketTypeException(msg[0]);
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/ChannelOutputStream.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/ChannelOutputStream.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * ChannelOutputStream.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class ChannelOutputStream extends OutputStream
+{
+ Channel c;
+
+ boolean isClosed = false;
+
+ ChannelOutputStream(Channel c)
+ {
+ this.c = c;
+ }
+
+ @Override
+ public void write(int b) throws IOException
+ {
+ byte[] buff = new byte[1];
+
+ buff[0] = (byte) b;
+
+ write(buff, 0, 1);
+ }
+
+ @Override
+ public void close() throws IOException
+ {
+ if (isClosed == false)
+ {
+ isClosed = true;
+ c.cm.sendEOF(c);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException
+ {
+ if (isClosed)
+ throw new IOException("This OutputStream is closed.");
+
+ /* This is a no-op, since this stream is unbuffered */
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ if (isClosed)
+ throw new IOException("This OutputStream is closed.");
+
+ if (b == null)
+ throw new NullPointerException();
+
+ if ((off < 0) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0) || (off > b.length))
+ throw new IndexOutOfBoundsException();
+
+ if (len == 0)
+ return;
+
+ c.cm.sendData(c, b, off, len);
+ }
+
+ @Override
+ public void write(byte[] b) throws IOException
+ {
+ write(b, 0, b.length);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/IChannelWorkerThread.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/IChannelWorkerThread.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+/**
+ * IChannelWorkerThread.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+interface IChannelWorkerThread
+{
+ public void stopWorking();
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/LocalAcceptThread.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/LocalAcceptThread.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * LocalAcceptThread.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class LocalAcceptThread extends Thread implements IChannelWorkerThread
+{
+ ChannelManager cm;
+ String host_to_connect;
+ int port_to_connect;
+
+ final ServerSocket ss;
+
+ public LocalAcceptThread(ChannelManager cm, int local_port, String host_to_connect, int port_to_connect)
+ throws IOException
+ {
+ this.cm = cm;
+ this.host_to_connect = host_to_connect;
+ this.port_to_connect = port_to_connect;
+
+ ss = new ServerSocket(local_port);
+ }
+
+ public LocalAcceptThread(ChannelManager cm, InetSocketAddress localAddress, String host_to_connect,
+ int port_to_connect) throws IOException
+ {
+ this.cm = cm;
+ this.host_to_connect = host_to_connect;
+ this.port_to_connect = port_to_connect;
+
+ ss = new ServerSocket();
+ ss.bind(localAddress);
+ }
+
+ public ServerSocket getServerSocket()
+ {
+ return ss;
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ cm.registerThread(this);
+ }
+ catch (IOException e)
+ {
+ stopWorking();
+ return;
+ }
+
+ while (true)
+ {
+ Socket s = null;
+
+ try
+ {
+ s = ss.accept();
+ }
+ catch (IOException e)
+ {
+ stopWorking();
+ return;
+ }
+
+ Channel cn = null;
+ StreamForwarder r2l = null;
+ StreamForwarder l2r = null;
+
+ try
+ {
+ /* This may fail, e.g., if the remote port is closed (in optimistic terms: not open yet) */
+
+ cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, s.getInetAddress().getHostAddress(), s
+ .getPort());
+
+ }
+ catch (IOException e)
+ {
+ /* Simply close the local socket and wait for the next incoming connection */
+
+ try
+ {
+ s.close();
+ }
+ catch (IOException ignore)
+ {
+ }
+
+ continue;
+ }
+
+ try
+ {
+ r2l = new StreamForwarder(cn, null, null, cn.stdoutStream, s.getOutputStream(), "RemoteToLocal");
+ l2r = new StreamForwarder(cn, r2l, s, s.getInputStream(), cn.stdinStream, "LocalToRemote");
+ }
+ catch (IOException e)
+ {
+ try
+ {
+ /* This message is only visible during debugging, since we discard the channel immediatelly */
+ cn.cm.closeChannel(cn, e, true);
+ }
+ catch (IOException ignore)
+ {
+ }
+
+ continue;
+ }
+
+ r2l.setDaemon(true);
+ l2r.setDaemon(true);
+ r2l.start();
+ l2r.start();
+ }
+ }
+
+ public void stopWorking()
+ {
+ try
+ {
+ /* This will lead to an IOException in the ss.accept() call */
+ ss.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/RemoteAcceptThread.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/RemoteAcceptThread.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import ch.ethz.ssh2.log.Logger;
+
+/**
+ * RemoteAcceptThread.
+ *
+ * @author Christian Plattner
+ * @version $Id: RemoteAcceptThread.java 119 2014-04-12 20:30:58Z dkocher@sudo.ch $
+ */
+public class RemoteAcceptThread extends Thread
+{
+ private static final Logger log = Logger.getLogger(RemoteAcceptThread.class);
+
+ Channel c;
+
+ String remoteConnectedAddress;
+ int remoteConnectedPort;
+ String remoteOriginatorAddress;
+ int remoteOriginatorPort;
+ String targetAddress;
+ int targetPort;
+
+ Socket s;
+
+ public RemoteAcceptThread(Channel c, String remoteConnectedAddress, int remoteConnectedPort,
+ String remoteOriginatorAddress, int remoteOriginatorPort, String targetAddress, int targetPort)
+ {
+ this.c = c;
+ this.remoteConnectedAddress = remoteConnectedAddress;
+ this.remoteConnectedPort = remoteConnectedPort;
+ this.remoteOriginatorAddress = remoteOriginatorAddress;
+ this.remoteOriginatorPort = remoteOriginatorPort;
+ this.targetAddress = targetAddress;
+ this.targetPort = targetPort;
+
+ log.debug("RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
+ + remoteOriginatorAddress + "/" + remoteOriginatorPort);
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ c.cm.sendOpenConfirmation(c);
+
+ s = new Socket(targetAddress, targetPort);
+
+ StreamForwarder r2l = new StreamForwarder(c, null, null, c.getStdoutStream(), s.getOutputStream(),
+ "RemoteToLocal");
+ StreamForwarder l2r = new StreamForwarder(c, null, null, s.getInputStream(), c.getStdinStream(),
+ "LocalToRemote");
+
+ /* No need to start two threads, one can be executed in the current thread */
+
+ r2l.setDaemon(true);
+ r2l.start();
+ l2r.run();
+
+ while (r2l.isAlive())
+ {
+ try
+ {
+ r2l.join();
+ }
+ catch (InterruptedException ignored)
+ {
+ }
+ }
+
+ /* If the channel is already closed, then this is a no-op */
+
+ c.cm.closeChannel(c, "EOF on both streams reached.", true);
+ s.close();
+ }
+ catch (IOException e)
+ {
+ log.warning("IOException in proxy code: " + e.getMessage());
+
+ try
+ {
+ c.cm.closeChannel(c, e, true);
+ }
+ catch (IOException ignored)
+ {
+ }
+ try
+ {
+ if (s != null)
+ s.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/RemoteForwardingData.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/RemoteForwardingData.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+/**
+ * RemoteForwardingData. Data about a requested remote forwarding.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class RemoteForwardingData
+{
+ public String bindAddress;
+ public int bindPort;
+
+ String targetAddress;
+ int targetPort;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/RemoteX11AcceptThread.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/RemoteX11AcceptThread.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * RemoteX11AcceptThread.
+ *
+ * @author Christian Plattner
+ * @version $Id: RemoteX11AcceptThread.java 119 2014-04-12 20:30:58Z dkocher@sudo.ch $
+ */
+public class RemoteX11AcceptThread extends Thread
+{
+ private static final Logger log = Logger.getLogger(RemoteX11AcceptThread.class);
+
+ Channel c;
+
+ String remoteOriginatorAddress;
+ int remoteOriginatorPort;
+
+ Socket s;
+
+ public RemoteX11AcceptThread(Channel c, String remoteOriginatorAddress, int remoteOriginatorPort)
+ {
+ this.c = c;
+ this.remoteOriginatorAddress = remoteOriginatorAddress;
+ this.remoteOriginatorPort = remoteOriginatorPort;
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ /* Send Open Confirmation */
+
+ c.cm.sendOpenConfirmation(c);
+
+ /* Read startup packet from client */
+
+ OutputStream remote_os = c.getStdinStream();
+ InputStream remote_is = c.getStdoutStream();
+
+ /* The following code is based on the protocol description given in:
+ * Scheifler/Gettys,
+ * X Windows System: Core and Extension Protocols:
+ * X Version 11, Releases 6 and 6.1 ISBN 1-55558-148-X
+ * (from the ETH library - after being here for almost ten
+ * years one of the few books I borrowed... sad but true =)
+ */
+
+ /*
+ * Client startup:
+ *
+ * 1 0X42 MSB first/0x6c lSB first - byteorder
+ * 1 - unused
+ * 2 card16 - protocol-major-version
+ * 2 card16 - protocol-minor-version
+ * 2 n - lenght of authorization-protocol-name
+ * 2 d - lenght of authorization-protocol-data
+ * 2 - unused
+ * string8 - authorization-protocol-name
+ * p - unused, p=pad(n)
+ * string8 - authorization-protocol-data
+ * q - unused, q=pad(d)
+ *
+ * pad(X) = (4 - (X mod 4)) mod 4
+ *
+ * Server response:
+ *
+ * 1 (0 failed, 2 authenticate, 1 success)
+ * ...
+ *
+ */
+
+ /* Later on we will simply forward the first 6 header bytes to the "real" X11 server */
+
+ byte[] header = new byte[6];
+
+ if (remote_is.read(header) != 6)
+ throw new IOException("Unexpected EOF on X11 startup!");
+
+ if ((header[0] != 0x42) && (header[0] != 0x6c)) // 0x42 MSB first, 0x6C LSB first
+ throw new IOException("Unknown endian format in X11 message!");
+
+ /* Yes, I came up with this myself - shall I file an application for a patent? =) */
+
+ int idxMSB = (header[0] == 0x42) ? 0 : 1;
+
+ /* Read authorization data header */
+
+ byte[] auth_buff = new byte[6];
+
+ if (remote_is.read(auth_buff) != 6)
+ throw new IOException("Unexpected EOF on X11 startup!");
+
+ int authProtocolNameLength = ((auth_buff[idxMSB] & 0xff) << 8) | (auth_buff[1 - idxMSB] & 0xff);
+ int authProtocolDataLength = ((auth_buff[2 + idxMSB] & 0xff) << 8) | (auth_buff[3 - idxMSB] & 0xff);
+
+ if ((authProtocolNameLength > 256) || (authProtocolDataLength > 256))
+ throw new IOException("Buggy X11 authorization data");
+
+ int authProtocolNamePadding = ((4 - (authProtocolNameLength % 4)) % 4);
+ int authProtocolDataPadding = ((4 - (authProtocolDataLength % 4)) % 4);
+
+ byte[] authProtocolName = new byte[authProtocolNameLength];
+ byte[] authProtocolData = new byte[authProtocolDataLength];
+
+ byte[] paddingBuffer = new byte[4];
+
+ if (remote_is.read(authProtocolName) != authProtocolNameLength)
+ throw new IOException("Unexpected EOF on X11 startup! (authProtocolName)");
+
+ if (remote_is.read(paddingBuffer, 0, authProtocolNamePadding) != authProtocolNamePadding)
+ throw new IOException("Unexpected EOF on X11 startup! (authProtocolNamePadding)");
+
+ if (remote_is.read(authProtocolData) != authProtocolDataLength)
+ throw new IOException("Unexpected EOF on X11 startup! (authProtocolData)");
+
+ if (remote_is.read(paddingBuffer, 0, authProtocolDataPadding) != authProtocolDataPadding)
+ throw new IOException("Unexpected EOF on X11 startup! (authProtocolDataPadding)");
+
+ if ("MIT-MAGIC-COOKIE-1".equals(StringEncoder.GetString(authProtocolName)) == false)
+ throw new IOException("Unknown X11 authorization protocol!");
+
+ if (authProtocolDataLength != 16)
+ throw new IOException("Wrong data length for X11 authorization data!");
+
+ StringBuilder tmp = new StringBuilder(32);
+ for (int i = 0; i < authProtocolData.length; i++)
+ {
+ String digit2 = Integer.toHexString(authProtocolData[i] & 0xff);
+ tmp.append((digit2.length() == 2) ? digit2 : "0" + digit2);
+ }
+ String hexEncodedFakeCookie = tmp.toString();
+
+ /* Order is very important here - it may be that a certain x11 forwarding
+ * gets disabled right in the moment when we check and register our connection
+ * */
+
+ synchronized (c)
+ {
+ /* Please read the comment in Channel.java */
+ c.hexX11FakeCookie = hexEncodedFakeCookie;
+ }
+
+ /* Now check our fake cookie directory to see if we produced this cookie */
+
+ X11ServerData sd = c.cm.checkX11Cookie(hexEncodedFakeCookie);
+
+ if (sd == null)
+ throw new IOException("Invalid X11 cookie received.");
+
+ /* If the session which corresponds to this cookie is closed then we will
+ * detect this: the session's close code will close all channels
+ * with the session's assigned x11 fake cookie.
+ */
+
+ s = new Socket(sd.hostname, sd.port);
+
+ OutputStream x11_os = s.getOutputStream();
+ InputStream x11_is = s.getInputStream();
+
+ /* Now we are sending the startup packet to the real X11 server */
+
+ x11_os.write(header);
+
+ if (sd.x11_magic_cookie == null)
+ {
+ byte[] emptyAuthData = new byte[6];
+ /* empty auth data, hopefully you are connecting to localhost =) */
+ x11_os.write(emptyAuthData);
+ }
+ else
+ {
+ if (sd.x11_magic_cookie.length != 16)
+ throw new IOException("The real X11 cookie has an invalid length!");
+
+ /* send X11 cookie specified by client */
+ x11_os.write(auth_buff);
+ x11_os.write(authProtocolName); /* re-use */
+ x11_os.write(paddingBuffer, 0, authProtocolNamePadding);
+ x11_os.write(sd.x11_magic_cookie);
+ x11_os.write(paddingBuffer, 0, authProtocolDataPadding);
+ }
+
+ x11_os.flush();
+
+ /* Start forwarding traffic */
+
+ StreamForwarder r2l = new StreamForwarder(c, null, null, remote_is, x11_os, "RemoteToX11");
+ StreamForwarder l2r = new StreamForwarder(c, null, null, x11_is, remote_os, "X11ToRemote");
+
+ /* No need to start two threads, one can be executed in the current thread */
+
+ r2l.setDaemon(true);
+ r2l.start();
+ l2r.run();
+
+ while (r2l.isAlive())
+ {
+ try
+ {
+ r2l.join();
+ }
+ catch (InterruptedException ignored)
+ {
+ }
+ }
+
+ /* If the channel is already closed, then this is a no-op */
+
+ c.cm.closeChannel(c, "EOF on both X11 streams reached.", true);
+ s.close();
+ }
+ catch (IOException e)
+ {
+ log.warning("IOException in X11 proxy code: " + e.getMessage());
+
+ try
+ {
+ c.cm.closeChannel(c, e, true);
+ }
+ catch (IOException ignored)
+ {
+ }
+ try
+ {
+ if (s != null)
+ s.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/ServerSessionImpl.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/ServerSessionImpl.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,61 @@
+
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import ch.ethz.ssh2.ServerSession;
+import ch.ethz.ssh2.ServerSessionCallback;
+
+public class ServerSessionImpl implements ServerSession
+{
+ Channel c;
+ public ServerSessionCallback sscb;
+
+ public ServerSessionImpl(Channel c)
+ {
+ this.c = c;
+ }
+
+ public int getState()
+ {
+ return c.getState();
+ }
+
+ public InputStream getStdout()
+ {
+ return c.getStdoutStream();
+ }
+
+ public InputStream getStderr()
+ {
+ return c.getStderrStream();
+ }
+
+ public OutputStream getStdin()
+ {
+ return c.getStdinStream();
+ }
+
+ public void close()
+ {
+ try
+ {
+ c.cm.closeChannel(c, "Closed due to server request", true);
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+
+ public synchronized ServerSessionCallback getServerSessionCallback()
+ {
+ return sscb;
+ }
+
+ public synchronized void setServerSessionCallback(ServerSessionCallback sscb)
+ {
+ this.sscb = sscb;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/StreamForwarder.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/StreamForwarder.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ * A StreamForwarder forwards data between two given streams.
+ * If two StreamForwarder threads are used (one for each direction)
+ * then one can be configured to shutdown the underlying channel/socket
+ * if both threads have finished forwarding (EOF).
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class StreamForwarder extends Thread
+{
+ OutputStream os;
+ InputStream is;
+ byte[] buffer = new byte[Channel.CHANNEL_BUFFER_SIZE];
+ Channel c;
+ StreamForwarder sibling;
+ Socket s;
+ String mode;
+
+ StreamForwarder(Channel c, StreamForwarder sibling, Socket s, InputStream is, OutputStream os, String mode)
+ throws IOException
+ {
+ this.is = is;
+ this.os = os;
+ this.mode = mode;
+ this.c = c;
+ this.sibling = sibling;
+ this.s = s;
+ }
+
+ @Override
+ public void run()
+ {
+ try
+ {
+ while (true)
+ {
+ int len = is.read(buffer);
+ if (len <= 0)
+ break;
+ os.write(buffer, 0, len);
+ os.flush();
+ }
+ }
+ catch (IOException e)
+ {
+ try
+ {
+ c.cm.closeChannel(c, e, true);
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ finally
+ {
+ try
+ {
+ os.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ try
+ {
+ is.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+
+ if (sibling != null)
+ {
+ while (sibling.isAlive())
+ {
+ try
+ {
+ sibling.join();
+ }
+ catch (InterruptedException ignored)
+ {
+ }
+ }
+
+ try
+ {
+ c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
+ }
+ catch (IOException ignored)
+ {
+ }
+
+ try
+ {
+ if (s != null)
+ s.close();
+ }
+ catch (IOException ignored)
+ {
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/channel/X11ServerData.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/channel/X11ServerData.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.channel;
+
+/**
+ * X11ServerData. Data regarding an x11 forwarding target.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ *
+ */
+public class X11ServerData
+{
+ public String hostname;
+ public int port;
+ public byte[] x11_magic_cookie; /* not the remote (fake) one, the local (real) one */
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/compression/CompressionFactory.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/compression/CompressionFactory.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,66 @@
+package ch.ethz.ssh2.compression;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Kenny Root
+ * @version $Id: CompressionFactory.java 156 2014-04-29 12:02:35Z dkocher@sudo.ch $
+ */
+public class CompressionFactory {
+ static class CompressorEntry {
+ String type;
+ String compressorClass;
+
+ public CompressorEntry(String type, String compressorClass) {
+ this.type = type;
+ this.compressorClass = compressorClass;
+ }
+ }
+
+ private static final List compressors
+ = new ArrayList();
+
+ static {
+ // Higher priority first
+ compressors.add(new CompressorEntry("none", null));
+ }
+
+ public static String[] getDefaultCompressorList() {
+ String list[] = new String[compressors.size()];
+ for(int i = 0; i < compressors.size(); i++) {
+ CompressorEntry ce = compressors.get(i);
+ list[i] = ce.type;
+ }
+ return list;
+ }
+
+ public static void checkCompressorList(String[] list) {
+ for(final String candidate : list) {
+ getEntry(candidate);
+ }
+ }
+
+ public static Compressor createCompressor(String type) {
+ try {
+ CompressorEntry ce = getEntry(type);
+ if(null == ce.compressorClass) {
+ return null;
+ }
+ Class> cc = Class.forName(ce.compressorClass);
+ return (Compressor) cc.newInstance();
+ }
+ catch(Exception e) {
+ throw new IllegalArgumentException("Cannot instantiate " + type);
+ }
+ }
+
+ private static CompressorEntry getEntry(String type) {
+ for(CompressorEntry ce : compressors) {
+ if(ce.type.equals(type)) {
+ return ce;
+ }
+ }
+ throw new IllegalArgumentException("Unknown algorithm " + type);
+ }
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/compression/Compressor.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/compression/Compressor.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,15 @@
+package ch.ethz.ssh2.compression;
+
+import java.io.IOException;
+
+/**
+ * @author Kenny Root
+ * @version $Id: Compressor.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public interface Compressor {
+ int getBufferSize();
+
+ int compress(byte[] buf, int start, int len, byte[] output) throws IOException;
+
+ byte[] uncompress(byte[] buf, int start, int[] len) throws IOException;
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/Base64.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/Base64.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+
+/**
+ * Basic Base64 Support.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class Base64
+{
+ static final char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
+
+ public static char[] encode(byte[] content)
+ {
+ CharArrayWriter cw = new CharArrayWriter((4 * content.length) / 3);
+
+ int idx = 0;
+
+ int x = 0;
+
+ for (int i = 0; i < content.length; i++)
+ {
+ if (idx == 0)
+ x = (content[i] & 0xff) << 16;
+ else if (idx == 1)
+ x = x | ((content[i] & 0xff) << 8);
+ else
+ x = x | (content[i] & 0xff);
+
+ idx++;
+
+ if (idx == 3)
+ {
+ cw.write(alphabet[x >> 18]);
+ cw.write(alphabet[(x >> 12) & 0x3f]);
+ cw.write(alphabet[(x >> 6) & 0x3f]);
+ cw.write(alphabet[x & 0x3f]);
+
+ idx = 0;
+ }
+ }
+
+ if (idx == 1)
+ {
+ cw.write(alphabet[x >> 18]);
+ cw.write(alphabet[(x >> 12) & 0x3f]);
+ cw.write('=');
+ cw.write('=');
+ }
+
+ if (idx == 2)
+ {
+ cw.write(alphabet[x >> 18]);
+ cw.write(alphabet[(x >> 12) & 0x3f]);
+ cw.write(alphabet[(x >> 6) & 0x3f]);
+ cw.write('=');
+ }
+
+ return cw.toCharArray();
+ }
+
+ public static byte[] decode(char[] message) throws IOException
+ {
+ byte buff[] = new byte[4];
+ byte dest[] = new byte[message.length];
+
+ int bpos = 0;
+ int destpos = 0;
+
+ for (int i = 0; i < message.length; i++)
+ {
+ int c = message[i];
+
+ if ((c == '\n') || (c == '\r') || (c == ' ') || (c == '\t'))
+ continue;
+
+ if ((c >= 'A') && (c <= 'Z'))
+ {
+ buff[bpos++] = (byte) (c - 'A');
+ }
+ else if ((c >= 'a') && (c <= 'z'))
+ {
+ buff[bpos++] = (byte) ((c - 'a') + 26);
+ }
+ else if ((c >= '0') && (c <= '9'))
+ {
+ buff[bpos++] = (byte) ((c - '0') + 52);
+ }
+ else if (c == '+')
+ {
+ buff[bpos++] = 62;
+ }
+ else if (c == '/')
+ {
+ buff[bpos++] = 63;
+ }
+ else if (c == '=')
+ {
+ buff[bpos++] = 64;
+ }
+ else
+ {
+ throw new IOException("Illegal char in base64 code.");
+ }
+
+ if (bpos == 4)
+ {
+ bpos = 0;
+
+ if (buff[0] == 64)
+ break;
+
+ if (buff[1] == 64)
+ throw new IOException("Unexpected '=' in base64 code.");
+
+ if (buff[2] == 64)
+ {
+ int v = (((buff[0] & 0x3f) << 6) | ((buff[1] & 0x3f)));
+ dest[destpos++] = (byte) (v >> 4);
+ break;
+ }
+ else if (buff[3] == 64)
+ {
+ int v = (((buff[0] & 0x3f) << 12) | ((buff[1] & 0x3f) << 6) | ((buff[2] & 0x3f)));
+ dest[destpos++] = (byte) (v >> 10);
+ dest[destpos++] = (byte) (v >> 2);
+ break;
+ }
+ else
+ {
+ int v = (((buff[0] & 0x3f) << 18) | ((buff[1] & 0x3f) << 12) | ((buff[2] & 0x3f) << 6) | ((buff[3] & 0x3f)));
+ dest[destpos++] = (byte) (v >> 16);
+ dest[destpos++] = (byte) (v >> 8);
+ dest[destpos++] = (byte) (v);
+ }
+ }
+ }
+
+ byte[] res = new byte[destpos];
+ System.arraycopy(dest, 0, res, 0, destpos);
+
+ return res;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/CryptoWishList.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/CryptoWishList.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+import ch.ethz.ssh2.compression.CompressionFactory;
+import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.transport.KexManager;
+
+/**
+ * CryptoWishList.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class CryptoWishList
+{
+ public String[] kexAlgorithms = KexManager.getDefaultClientKexAlgorithmList();
+ public String[] serverHostKeyAlgorithms = KexManager.getDefaultServerHostkeyAlgorithmList();
+ public String[] c2s_enc_algos = BlockCipherFactory.getDefaultCipherList();
+ public String[] s2c_enc_algos = BlockCipherFactory.getDefaultCipherList();
+ public String[] c2s_mac_algos = MAC.getMacList();
+ public String[] s2c_mac_algos = MAC.getMacList();
+ public String[] c2s_comp_algos = CompressionFactory.getDefaultCompressorList();
+ public String[] s2c_comp_algos = CompressionFactory.getDefaultCompressorList();
+
+ public static CryptoWishList forServer()
+ {
+ CryptoWishList cwl = new CryptoWishList();
+ cwl.kexAlgorithms = KexManager.getDefaultServerKexAlgorithmList();
+ return cwl;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/KeyMaterial.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/KeyMaterial.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.crypto.digest.HashForSSH2Types;
+
+/**
+ * Establishes key material for iv/key/mac (both directions).
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class KeyMaterial {
+ public byte[] initial_iv_client_to_server;
+ public byte[] initial_iv_server_to_client;
+ public byte[] enc_key_client_to_server;
+ public byte[] enc_key_server_to_client;
+ public byte[] integrity_key_client_to_server;
+ public byte[] integrity_key_server_to_client;
+
+ private static byte[] calculateKey(HashForSSH2Types sh, BigInteger K, byte[] H, byte type, byte[] SessionID,
+ int keyLength) throws IOException {
+ byte[] res = new byte[keyLength];
+
+ int dglen = sh.getDigestLength();
+ int numRounds = (keyLength + dglen - 1) / dglen;
+
+ byte[][] tmp = new byte[numRounds][];
+
+ sh.reset();
+ sh.updateBigInt(K);
+ sh.updateBytes(H);
+ sh.updateByte(type);
+ sh.updateBytes(SessionID);
+
+ tmp[0] = sh.getDigest();
+
+ int off = 0;
+ int produced = Math.min(dglen, keyLength);
+
+ System.arraycopy(tmp[0], 0, res, off, produced);
+
+ keyLength -= produced;
+ off += produced;
+
+ for(int i = 1; i < numRounds; i++) {
+ sh.updateBigInt(K);
+ sh.updateBytes(H);
+
+ for(int j = 0; j < i; j++) {
+ sh.updateBytes(tmp[j]);
+ }
+
+ tmp[i] = sh.getDigest();
+
+ produced = Math.min(dglen, keyLength);
+ System.arraycopy(tmp[i], 0, res, off, produced);
+ keyLength -= produced;
+ off += produced;
+ }
+
+ return res;
+ }
+
+ public static KeyMaterial create(String hashType, byte[] H, BigInteger K, byte[] SessionID, int keyLengthCS,
+ int blockSizeCS, int macLengthCS, int keyLengthSC, int blockSizeSC, int macLengthSC)
+ throws IOException {
+ KeyMaterial km = new KeyMaterial();
+
+ HashForSSH2Types sh = new HashForSSH2Types(hashType);
+
+ km.initial_iv_client_to_server = calculateKey(sh, K, H, (byte) 'A', SessionID, blockSizeCS);
+
+ km.initial_iv_server_to_client = calculateKey(sh, K, H, (byte) 'B', SessionID, blockSizeSC);
+
+ km.enc_key_client_to_server = calculateKey(sh, K, H, (byte) 'C', SessionID, keyLengthCS);
+
+ km.enc_key_server_to_client = calculateKey(sh, K, H, (byte) 'D', SessionID, keyLengthSC);
+
+ km.integrity_key_client_to_server = calculateKey(sh, K, H, (byte) 'E', SessionID, macLengthCS);
+
+ km.integrity_key_server_to_client = calculateKey(sh, K, H, (byte) 'F', SessionID, macLengthSC);
+
+ return km;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/PEMDecoder.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/PEMDecoder.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+import java.io.BufferedReader;
+import java.io.CharArrayReader;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.DigestException;
+
+import ch.ethz.ssh2.crypto.cipher.AES;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.cipher.CBCMode;
+import ch.ethz.ssh2.crypto.cipher.DES;
+import ch.ethz.ssh2.crypto.cipher.DESede;
+import ch.ethz.ssh2.crypto.digest.MD5;
+import ch.ethz.ssh2.signature.DSAPrivateKey;
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * PEM Support.
+ *
+ * @author Christian Plattner
+ * @version $Id: PEMDecoder.java 154 2014-04-28 11:45:02Z dkocher@sudo.ch $
+ */
+public class PEMDecoder
+{
+ private static final int PEM_RSA_PRIVATE_KEY = 1;
+ private static final int PEM_DSA_PRIVATE_KEY = 2;
+
+ private static int hexToInt(char c)
+ {
+ if ((c >= 'a') && (c <= 'f'))
+ {
+ return (c - 'a') + 10;
+ }
+
+ if ((c >= 'A') && (c <= 'F'))
+ {
+ return (c - 'A') + 10;
+ }
+
+ if ((c >= '0') && (c <= '9'))
+ {
+ return (c - '0');
+ }
+
+ throw new IllegalArgumentException("Need hex char");
+ }
+
+ private static byte[] hexToByteArray(String hex)
+ {
+ if (hex == null)
+ throw new IllegalArgumentException("null argument");
+
+ if ((hex.length() % 2) != 0)
+ throw new IllegalArgumentException("Uneven string length in hex encoding.");
+
+ byte decoded[] = new byte[hex.length() / 2];
+
+ for (int i = 0; i < decoded.length; i++)
+ {
+ int hi = hexToInt(hex.charAt(i * 2));
+ int lo = hexToInt(hex.charAt((i * 2) + 1));
+
+ decoded[i] = (byte) (hi * 16 + lo);
+ }
+
+ return decoded;
+ }
+
+ private static byte[] generateKeyFromPasswordSaltWithMD5(byte[] password, byte[] salt, int keyLen)
+ throws IOException
+ {
+ if (salt.length < 8)
+ throw new IllegalArgumentException("Salt needs to be at least 8 bytes for key generation.");
+
+ MD5 md5 = new MD5();
+
+ byte[] key = new byte[keyLen];
+ byte[] tmp = new byte[md5.getDigestLength()];
+
+ while (true)
+ {
+ md5.update(password, 0, password.length);
+ md5.update(salt, 0, 8); // ARGH we only use the first 8 bytes of the salt in this step.
+ // This took me two hours until I got AES-xxx running.
+
+ int copy = (keyLen < tmp.length) ? keyLen : tmp.length;
+
+ try {
+ md5.digest(tmp, 0);
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+
+ System.arraycopy(tmp, 0, key, key.length - keyLen, copy);
+
+ keyLen -= copy;
+
+ if (keyLen == 0)
+ return key;
+
+ md5.update(tmp, 0, tmp.length);
+ }
+ }
+
+ private static byte[] removePadding(byte[] buff, int blockSize) throws IOException
+ {
+ /* Removes RFC 1423/PKCS #7 padding */
+
+ int rfc_1423_padding = buff[buff.length - 1] & 0xff;
+
+ if ((rfc_1423_padding < 1) || (rfc_1423_padding > blockSize))
+ throw new PEMDecryptException("Decrypted PEM has wrong padding, did you specify the correct password?");
+
+ for (int i = 2; i <= rfc_1423_padding; i++)
+ {
+ if (buff[buff.length - i] != rfc_1423_padding)
+ throw new PEMDecryptException("Decrypted PEM has wrong padding, did you specify the correct password?");
+ }
+
+ byte[] tmp = new byte[buff.length - rfc_1423_padding];
+ System.arraycopy(buff, 0, tmp, 0, buff.length - rfc_1423_padding);
+ return tmp;
+ }
+
+ private static PEMStructure parsePEM(char[] pem) throws IOException
+ {
+ PEMStructure ps = new PEMStructure();
+
+ String line = null;
+
+ BufferedReader br = new BufferedReader(new CharArrayReader(pem));
+
+ String endLine = null;
+
+ while (true)
+ {
+ line = br.readLine();
+
+ if (line == null)
+ throw new IOException("Invalid PEM structure, '-----BEGIN...' missing");
+
+ line = line.trim();
+
+ if (line.startsWith("-----BEGIN DSA PRIVATE KEY-----"))
+ {
+ endLine = "-----END DSA PRIVATE KEY-----";
+ ps.pemType = PEM_DSA_PRIVATE_KEY;
+ break;
+ }
+
+ if (line.startsWith("-----BEGIN RSA PRIVATE KEY-----"))
+ {
+ endLine = "-----END RSA PRIVATE KEY-----";
+ ps.pemType = PEM_RSA_PRIVATE_KEY;
+ break;
+ }
+ }
+
+ while (true)
+ {
+ line = br.readLine();
+
+ if (line == null)
+ throw new IOException("Invalid PEM structure, " + endLine + " missing");
+
+ line = line.trim();
+
+ int sem_idx = line.indexOf(':');
+
+ if (sem_idx == -1)
+ break;
+
+ String name = line.substring(0, sem_idx + 1);
+ String value = line.substring(sem_idx + 1);
+
+ String values[] = value.split(",");
+
+ for (int i = 0; i < values.length; i++)
+ values[i] = values[i].trim();
+
+ // Proc-Type: 4,ENCRYPTED
+ // DEK-Info: DES-EDE3-CBC,579B6BE3E5C60483
+
+ if ("Proc-Type:".equals(name))
+ {
+ ps.procType = values;
+ continue;
+ }
+
+ if ("DEK-Info:".equals(name))
+ {
+ ps.dekInfo = values;
+ continue;
+ }
+ /* Ignore line */
+ }
+
+ StringBuilder keyData = new StringBuilder();
+
+ while (true)
+ {
+ if (line == null)
+ throw new IOException("Invalid PEM structure, " + endLine + " missing");
+
+ line = line.trim();
+
+ if (line.startsWith(endLine))
+ break;
+
+ keyData.append(line);
+
+ line = br.readLine();
+ }
+
+ char[] pem_chars = new char[keyData.length()];
+ keyData.getChars(0, pem_chars.length, pem_chars, 0);
+
+ ps.data = Base64.decode(pem_chars);
+
+ if (ps.data.length == 0)
+ throw new IOException("Invalid PEM structure, no data available");
+
+ return ps;
+ }
+
+ private static void decryptPEM(PEMStructure ps, byte[] pw) throws IOException
+ {
+ if (ps.dekInfo == null)
+ throw new IOException("Broken PEM, no mode and salt given, but encryption enabled");
+
+ if (ps.dekInfo.length != 2)
+ throw new IOException("Broken PEM, DEK-Info is incomplete!");
+
+ String algo = ps.dekInfo[0];
+ byte[] salt = hexToByteArray(ps.dekInfo[1]);
+
+ BlockCipher bc = null;
+
+ if (algo.equals("DES-EDE3-CBC"))
+ {
+ DESede des3 = new DESede();
+ des3.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
+ bc = new CBCMode(des3, salt, false);
+ }
+ else if (algo.equals("DES-CBC"))
+ {
+ DES des = new DES();
+ des.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 8));
+ bc = new CBCMode(des, salt, false);
+ }
+ else if (algo.equals("AES-128-CBC"))
+ {
+ AES aes = new AES();
+ aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 16));
+ bc = new CBCMode(aes, salt, false);
+ }
+ else if (algo.equals("AES-192-CBC"))
+ {
+ AES aes = new AES();
+ aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 24));
+ bc = new CBCMode(aes, salt, false);
+ }
+ else if (algo.equals("AES-256-CBC"))
+ {
+ AES aes = new AES();
+ aes.init(false, generateKeyFromPasswordSaltWithMD5(pw, salt, 32));
+ bc = new CBCMode(aes, salt, false);
+ }
+ else
+ {
+ throw new IOException("Cannot decrypt PEM structure, unknown cipher " + algo);
+ }
+
+ if ((ps.data.length % bc.getBlockSize()) != 0)
+ throw new IOException("Invalid PEM structure, size of encrypted block is not a multiple of "
+ + bc.getBlockSize());
+
+ /* Now decrypt the content */
+
+ byte[] dz = new byte[ps.data.length];
+
+ for (int i = 0; i < ps.data.length / bc.getBlockSize(); i++)
+ {
+ bc.transformBlock(ps.data, i * bc.getBlockSize(), dz, i * bc.getBlockSize());
+ }
+
+ /* Now check and remove RFC 1423/PKCS #7 padding */
+
+ dz = removePadding(dz, bc.getBlockSize());
+
+ ps.data = dz;
+ ps.dekInfo = null;
+ ps.procType = null;
+ }
+
+ public static final boolean isPEMEncrypted(PEMStructure ps) throws IOException
+ {
+ if (ps.procType == null)
+ return false;
+
+ if (ps.procType.length != 2)
+ throw new IOException("Unknown Proc-Type field.");
+
+ if ("4".equals(ps.procType[0]) == false)
+ throw new IOException("Unknown Proc-Type field (" + ps.procType[0] + ")");
+
+ if ("ENCRYPTED".equals(ps.procType[1]))
+ return true;
+
+ return false;
+ }
+
+ public static final boolean isPEMEncrypted(char[] pem) throws IOException
+ {
+ return isPEMEncrypted(parsePEM(pem));
+ }
+
+ public static Object decode(char[] pem, String password) throws IOException
+ {
+ PEMStructure ps = parsePEM(pem);
+
+ if (isPEMEncrypted(ps))
+ {
+ if (password == null)
+ throw new IOException("PEM is encrypted, but no password was specified");
+
+ decryptPEM(ps, StringEncoder.GetBytes(password));
+ }
+
+ if (ps.pemType == PEM_DSA_PRIVATE_KEY)
+ {
+ SimpleDERReader dr = new SimpleDERReader(ps.data);
+
+ byte[] seq = dr.readSequenceAsByteArray();
+
+ if (dr.available() != 0)
+ throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
+
+ dr.resetInput(seq);
+
+ BigInteger version = dr.readInt();
+
+ if (version.compareTo(BigInteger.ZERO) != 0)
+ throw new IOException("Wrong version (" + version + ") in DSA PRIVATE KEY DER stream.");
+
+ BigInteger p = dr.readInt();
+ BigInteger q = dr.readInt();
+ BigInteger g = dr.readInt();
+ BigInteger y = dr.readInt();
+ BigInteger x = dr.readInt();
+
+ if (dr.available() != 0)
+ throw new IOException("Padding in DSA PRIVATE KEY DER stream.");
+
+ return new DSAPrivateKey(p, q, g, y, x);
+ }
+
+ if (ps.pemType == PEM_RSA_PRIVATE_KEY)
+ {
+ SimpleDERReader dr = new SimpleDERReader(ps.data);
+
+ byte[] seq = dr.readSequenceAsByteArray();
+
+ if (dr.available() != 0)
+ throw new IOException("Padding in RSA PRIVATE KEY DER stream.");
+
+ dr.resetInput(seq);
+
+ BigInteger version = dr.readInt();
+
+ if ((version.compareTo(BigInteger.ZERO) != 0) && (version.compareTo(BigInteger.ONE) != 0))
+ throw new IOException("Wrong version (" + version + ") in RSA PRIVATE KEY DER stream.");
+
+ BigInteger n = dr.readInt();
+ BigInteger e = dr.readInt();
+ BigInteger d = dr.readInt();
+
+ return new RSAPrivateKey(d, e, n);
+ }
+
+ throw new IOException("PEM problem: it is of unknown type");
+ }
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/PEMDecryptException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/PEMDecryptException.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2006-2014 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+import java.io.IOException;
+
+/**
+ * @version $Id: PEMDecryptException.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class PEMDecryptException extends IOException
+{
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public PEMDecryptException(String message)
+ {
+ super(message);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/PEMStructure.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/PEMStructure.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+/**
+ * Parsed PEM structure.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class PEMStructure
+{
+ int pemType;
+ String dekInfo[];
+ String procType[];
+ byte[] data;
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/SimpleDERReader.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/SimpleDERReader.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto;
+
+import java.io.IOException;
+
+import java.math.BigInteger;
+
+/**
+ * SimpleDERReader.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class SimpleDERReader
+{
+ byte[] buffer;
+ int pos;
+ int count;
+
+ public SimpleDERReader(byte[] b)
+ {
+ resetInput(b);
+ }
+
+ public SimpleDERReader(byte[] b, int off, int len)
+ {
+ resetInput(b, off, len);
+ }
+
+ public void resetInput(byte[] b)
+ {
+ resetInput(b, 0, b.length);
+ }
+
+ public void resetInput(byte[] b, int off, int len)
+ {
+ buffer = b;
+ pos = off;
+ count = len;
+ }
+
+ private byte readByte() throws IOException
+ {
+ if (count <= 0)
+ throw new IOException("DER byte array: out of data");
+ count--;
+ return buffer[pos++];
+ }
+
+ private byte[] readBytes(int len) throws IOException
+ {
+ if (len > count)
+ throw new IOException("DER byte array: out of data");
+
+ byte[] b = new byte[len];
+
+ System.arraycopy(buffer, pos, b, 0, len);
+
+ pos += len;
+ count -= len;
+
+ return b;
+ }
+
+ public int available()
+ {
+ return count;
+ }
+
+ private int readLength() throws IOException
+ {
+ int len = readByte() & 0xff;
+
+ if ((len & 0x80) == 0)
+ return len;
+
+ int remain = len & 0x7F;
+
+ if (remain == 0)
+ return -1;
+
+ len = 0;
+
+ while (remain > 0)
+ {
+ len = len << 8;
+ len = len | (readByte() & 0xff);
+ remain--;
+ }
+
+ return len;
+ }
+
+ public int ignoreNextObject() throws IOException
+ {
+ int type = readByte() & 0xff;
+
+ int len = readLength();
+
+ if ((len < 0) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ readBytes(len);
+
+ return type;
+ }
+
+ public BigInteger readInt() throws IOException
+ {
+ int type = readByte() & 0xff;
+
+ if (type != 0x02)
+ throw new IOException("Expected DER Integer, but found type " + type);
+
+ int len = readLength();
+
+ if ((len < 0) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ byte[] b = readBytes(len);
+
+ return new BigInteger(b);
+ }
+
+ public byte[] readSequenceAsByteArray() throws IOException
+ {
+ int type = readByte() & 0xff;
+
+ if (type != 0x30)
+ throw new IOException("Expected DER Sequence, but found type " + type);
+
+ int len = readLength();
+
+ if ((len < 0) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ return readBytes(len);
+ }
+
+ public byte[] readOctetString() throws IOException
+ {
+ int type = readByte() & 0xff;
+
+ if (type != 0x04)
+ throw new IOException("Expected DER Octetstring, but found type " + type);
+
+ int len = readLength();
+
+ if ((len < 0) || len > available())
+ throw new IOException("Illegal len in DER object (" + len + ")");
+
+ return readBytes(len);
+ }
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/AES.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/AES.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,697 @@
+package ch.ethz.ssh2.crypto.cipher;
+
+/*
+ This file was shamelessly taken from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * An implementation of the AES (Rijndael), from FIPS-197.
+ *
+ * For further details see: http://csrc.nist.gov/encryption/aes/
+ * .
+ *
+ * This implementation is based on optimizations from Dr. Brian Gladman's paper
+ * and C code at http://fp.gladman.plus.com/cryptography_technology/rijndael/
+ *
+ *
+ * There are three levels of tradeoff of speed vs memory Because java has no
+ * preprocessor, they are written as three separate classes from which to choose
+ *
+ * The fastest uses 8Kbytes of static tables to precompute round calculations, 4
+ * 256 word tables for encryption and 4 for decryption.
+ *
+ * The middle performance version uses only one 256 word table for each, for a
+ * total of 2Kbytes, adding 12 rotate operations per round to compute the values
+ * contained in the other tables from the contents of the first
+ *
+ * The slowest version uses no static tables at all and computes the values in
+ * each round
+ *
+ * This file contains the fast version with 8Kbytes of static tables for round
+ * precomputation
+ *
+ * @author See comments in the source file
+ * @version 2.50, 03/15/10
+ */
+public class AES implements BlockCipher
+{
+ // The S box
+ private static final byte[] S = { (byte) 99, (byte) 124, (byte) 119, (byte) 123, (byte) 242, (byte) 107,
+ (byte) 111, (byte) 197, (byte) 48, (byte) 1, (byte) 103, (byte) 43, (byte) 254, (byte) 215, (byte) 171,
+ (byte) 118, (byte) 202, (byte) 130, (byte) 201, (byte) 125, (byte) 250, (byte) 89, (byte) 71, (byte) 240,
+ (byte) 173, (byte) 212, (byte) 162, (byte) 175, (byte) 156, (byte) 164, (byte) 114, (byte) 192, (byte) 183,
+ (byte) 253, (byte) 147, (byte) 38, (byte) 54, (byte) 63, (byte) 247, (byte) 204, (byte) 52, (byte) 165,
+ (byte) 229, (byte) 241, (byte) 113, (byte) 216, (byte) 49, (byte) 21, (byte) 4, (byte) 199, (byte) 35,
+ (byte) 195, (byte) 24, (byte) 150, (byte) 5, (byte) 154, (byte) 7, (byte) 18, (byte) 128, (byte) 226,
+ (byte) 235, (byte) 39, (byte) 178, (byte) 117, (byte) 9, (byte) 131, (byte) 44, (byte) 26, (byte) 27,
+ (byte) 110, (byte) 90, (byte) 160, (byte) 82, (byte) 59, (byte) 214, (byte) 179, (byte) 41, (byte) 227,
+ (byte) 47, (byte) 132, (byte) 83, (byte) 209, (byte) 0, (byte) 237, (byte) 32, (byte) 252, (byte) 177,
+ (byte) 91, (byte) 106, (byte) 203, (byte) 190, (byte) 57, (byte) 74, (byte) 76, (byte) 88, (byte) 207,
+ (byte) 208, (byte) 239, (byte) 170, (byte) 251, (byte) 67, (byte) 77, (byte) 51, (byte) 133, (byte) 69,
+ (byte) 249, (byte) 2, (byte) 127, (byte) 80, (byte) 60, (byte) 159, (byte) 168, (byte) 81, (byte) 163,
+ (byte) 64, (byte) 143, (byte) 146, (byte) 157, (byte) 56, (byte) 245, (byte) 188, (byte) 182, (byte) 218,
+ (byte) 33, (byte) 16, (byte) 255, (byte) 243, (byte) 210, (byte) 205, (byte) 12, (byte) 19, (byte) 236,
+ (byte) 95, (byte) 151, (byte) 68, (byte) 23, (byte) 196, (byte) 167, (byte) 126, (byte) 61, (byte) 100,
+ (byte) 93, (byte) 25, (byte) 115, (byte) 96, (byte) 129, (byte) 79, (byte) 220, (byte) 34, (byte) 42,
+ (byte) 144, (byte) 136, (byte) 70, (byte) 238, (byte) 184, (byte) 20, (byte) 222, (byte) 94, (byte) 11,
+ (byte) 219, (byte) 224, (byte) 50, (byte) 58, (byte) 10, (byte) 73, (byte) 6, (byte) 36, (byte) 92,
+ (byte) 194, (byte) 211, (byte) 172, (byte) 98, (byte) 145, (byte) 149, (byte) 228, (byte) 121, (byte) 231,
+ (byte) 200, (byte) 55, (byte) 109, (byte) 141, (byte) 213, (byte) 78, (byte) 169, (byte) 108, (byte) 86,
+ (byte) 244, (byte) 234, (byte) 101, (byte) 122, (byte) 174, (byte) 8, (byte) 186, (byte) 120, (byte) 37,
+ (byte) 46, (byte) 28, (byte) 166, (byte) 180, (byte) 198, (byte) 232, (byte) 221, (byte) 116, (byte) 31,
+ (byte) 75, (byte) 189, (byte) 139, (byte) 138, (byte) 112, (byte) 62, (byte) 181, (byte) 102, (byte) 72,
+ (byte) 3, (byte) 246, (byte) 14, (byte) 97, (byte) 53, (byte) 87, (byte) 185, (byte) 134, (byte) 193,
+ (byte) 29, (byte) 158, (byte) 225, (byte) 248, (byte) 152, (byte) 17, (byte) 105, (byte) 217, (byte) 142,
+ (byte) 148, (byte) 155, (byte) 30, (byte) 135, (byte) 233, (byte) 206, (byte) 85, (byte) 40, (byte) 223,
+ (byte) 140, (byte) 161, (byte) 137, (byte) 13, (byte) 191, (byte) 230, (byte) 66, (byte) 104, (byte) 65,
+ (byte) 153, (byte) 45, (byte) 15, (byte) 176, (byte) 84, (byte) 187, (byte) 22, };
+
+ // The inverse S-box
+ private static final byte[] Si = { (byte) 82, (byte) 9, (byte) 106, (byte) 213, (byte) 48, (byte) 54, (byte) 165,
+ (byte) 56, (byte) 191, (byte) 64, (byte) 163, (byte) 158, (byte) 129, (byte) 243, (byte) 215, (byte) 251,
+ (byte) 124, (byte) 227, (byte) 57, (byte) 130, (byte) 155, (byte) 47, (byte) 255, (byte) 135, (byte) 52,
+ (byte) 142, (byte) 67, (byte) 68, (byte) 196, (byte) 222, (byte) 233, (byte) 203, (byte) 84, (byte) 123,
+ (byte) 148, (byte) 50, (byte) 166, (byte) 194, (byte) 35, (byte) 61, (byte) 238, (byte) 76, (byte) 149,
+ (byte) 11, (byte) 66, (byte) 250, (byte) 195, (byte) 78, (byte) 8, (byte) 46, (byte) 161, (byte) 102,
+ (byte) 40, (byte) 217, (byte) 36, (byte) 178, (byte) 118, (byte) 91, (byte) 162, (byte) 73, (byte) 109,
+ (byte) 139, (byte) 209, (byte) 37, (byte) 114, (byte) 248, (byte) 246, (byte) 100, (byte) 134, (byte) 104,
+ (byte) 152, (byte) 22, (byte) 212, (byte) 164, (byte) 92, (byte) 204, (byte) 93, (byte) 101, (byte) 182,
+ (byte) 146, (byte) 108, (byte) 112, (byte) 72, (byte) 80, (byte) 253, (byte) 237, (byte) 185, (byte) 218,
+ (byte) 94, (byte) 21, (byte) 70, (byte) 87, (byte) 167, (byte) 141, (byte) 157, (byte) 132, (byte) 144,
+ (byte) 216, (byte) 171, (byte) 0, (byte) 140, (byte) 188, (byte) 211, (byte) 10, (byte) 247, (byte) 228,
+ (byte) 88, (byte) 5, (byte) 184, (byte) 179, (byte) 69, (byte) 6, (byte) 208, (byte) 44, (byte) 30,
+ (byte) 143, (byte) 202, (byte) 63, (byte) 15, (byte) 2, (byte) 193, (byte) 175, (byte) 189, (byte) 3,
+ (byte) 1, (byte) 19, (byte) 138, (byte) 107, (byte) 58, (byte) 145, (byte) 17, (byte) 65, (byte) 79,
+ (byte) 103, (byte) 220, (byte) 234, (byte) 151, (byte) 242, (byte) 207, (byte) 206, (byte) 240, (byte) 180,
+ (byte) 230, (byte) 115, (byte) 150, (byte) 172, (byte) 116, (byte) 34, (byte) 231, (byte) 173, (byte) 53,
+ (byte) 133, (byte) 226, (byte) 249, (byte) 55, (byte) 232, (byte) 28, (byte) 117, (byte) 223, (byte) 110,
+ (byte) 71, (byte) 241, (byte) 26, (byte) 113, (byte) 29, (byte) 41, (byte) 197, (byte) 137, (byte) 111,
+ (byte) 183, (byte) 98, (byte) 14, (byte) 170, (byte) 24, (byte) 190, (byte) 27, (byte) 252, (byte) 86,
+ (byte) 62, (byte) 75, (byte) 198, (byte) 210, (byte) 121, (byte) 32, (byte) 154, (byte) 219, (byte) 192,
+ (byte) 254, (byte) 120, (byte) 205, (byte) 90, (byte) 244, (byte) 31, (byte) 221, (byte) 168, (byte) 51,
+ (byte) 136, (byte) 7, (byte) 199, (byte) 49, (byte) 177, (byte) 18, (byte) 16, (byte) 89, (byte) 39,
+ (byte) 128, (byte) 236, (byte) 95, (byte) 96, (byte) 81, (byte) 127, (byte) 169, (byte) 25, (byte) 181,
+ (byte) 74, (byte) 13, (byte) 45, (byte) 229, (byte) 122, (byte) 159, (byte) 147, (byte) 201, (byte) 156,
+ (byte) 239, (byte) 160, (byte) 224, (byte) 59, (byte) 77, (byte) 174, (byte) 42, (byte) 245, (byte) 176,
+ (byte) 200, (byte) 235, (byte) 187, (byte) 60, (byte) 131, (byte) 83, (byte) 153, (byte) 97, (byte) 23,
+ (byte) 43, (byte) 4, (byte) 126, (byte) 186, (byte) 119, (byte) 214, (byte) 38, (byte) 225, (byte) 105,
+ (byte) 20, (byte) 99, (byte) 85, (byte) 33, (byte) 12, (byte) 125, };
+
+ // vector used in calculating key schedule (powers of x in GF(256))
+ private static final int[] rcon = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab,
+ 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 };
+
+ // precomputation tables of calculations for rounds
+ private static final int[] T0 = { 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6,
+ 0xb16f6fde, 0x54c5c591, 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d,
+ 0x9a7676ec, 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb,
+ 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, 0xc2b7b775,
+ 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, 0x5c343468, 0xf4a5a551,
+ 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, 0x0c040408, 0x52c7c795, 0x65232346,
+ 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df,
+ 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36,
+ 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd,
+ 0x712f2f5e, 0x97848413, 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179,
+ 0xed5b5bb6, 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85,
+ 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, 0xcf45458a,
+ 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, 0xf35151a2, 0xfea3a35d,
+ 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf,
+ 0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3,
+ 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8,
+ 0xe75d5dba, 0x2b191932, 0x957373e6, 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54,
+ 0xab90903b, 0x8388880b, 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16,
+ 0x76dbdbad, 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8,
+ 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, 0x32e7e7d5,
+ 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, 0xb46c6cd8, 0xfa5656ac,
+ 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, 0xd5baba6f, 0x887878f0, 0x6f25254a,
+ 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e,
+ 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890,
+ 0x05030306, 0x01f6f6f7, 0x120e0e1c, 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199,
+ 0x271d1d3a, 0xb99e9e27, 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07,
+ 0xa7949433, 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5,
+ 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, 0xc3414182,
+ 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c };
+
+ private static final int[] T1 = { 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd,
+ 0x6f6fdeb1, 0xc5c59154, 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6,
+ 0x7676ec9a, 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b,
+ 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, 0xb7b775c2,
+ 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, 0x3434685c, 0xa5a551f4,
+ 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, 0x0404080c, 0xc7c79552, 0x23234665,
+ 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d,
+ 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d,
+ 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e,
+ 0x2f2f5e71, 0x84841397, 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8,
+ 0x5b5bb6ed, 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a,
+ 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, 0x45458acf,
+ 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, 0x5151a2f3, 0xa3a35dfe,
+ 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, 0xbcbc63df, 0xb6b677c1, 0xdadaaf75,
+ 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f,
+ 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac,
+ 0x5d5dbae7, 0x1919322b, 0x7373e695, 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e,
+ 0x90903bab, 0x88880b83, 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d,
+ 0xdbdbad76, 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4,
+ 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, 0xe7e7d532,
+ 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, 0x6c6cd8b4, 0x5656acfa,
+ 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, 0xbaba6fd5, 0x7878f088, 0x25254a6f,
+ 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21,
+ 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8,
+ 0x03030605, 0xf6f6f701, 0x0e0e1c12, 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958,
+ 0x1d1d3a27, 0x9e9e27b9, 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789,
+ 0x949433a7, 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a,
+ 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, 0x414182c3,
+ 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a };
+
+ private static final int[] T2 = { 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b,
+ 0x6fdeb16f, 0xc59154c5, 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab,
+ 0x76ec9a76, 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0,
+ 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, 0xb775c2b7,
+ 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, 0x34685c34, 0xa551f4a5,
+ 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, 0x04080c04, 0xc79552c7, 0x23466523,
+ 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2,
+ 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b,
+ 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3,
+ 0x2f5e712f, 0x84139784, 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1,
+ 0x5bb6ed5b, 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf,
+ 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, 0x458acf45,
+ 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, 0x51a2f351, 0xa35dfea3,
+ 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da,
+ 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec,
+ 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64,
+ 0x5dbae75d, 0x19322b19, 0x73e69573, 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a,
+ 0x903bab90, 0x880b8388, 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b,
+ 0xdbad76db, 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c,
+ 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, 0xe7d532e7,
+ 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, 0x6cd8b46c, 0x56acfa56,
+ 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, 0xba6fd5ba, 0x78f08878, 0x254a6f25,
+ 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f,
+ 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848,
+ 0x03060503, 0xf6f701f6, 0x0e1c120e, 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1,
+ 0x1d3a271d, 0x9e27b99e, 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e,
+ 0x9433a794, 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf,
+ 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, 0x4182c341,
+ 0x9929b099, 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16 };
+
+ private static final int[] T3 = { 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b,
+ 0xdeb16f6f, 0x9154c5c5, 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab,
+ 0xec9a7676, 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0,
+ 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, 0x75c2b7b7,
+ 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, 0x685c3434, 0x51f4a5a5,
+ 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, 0x080c0404, 0x9552c7c7, 0x46652323,
+ 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2,
+ 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b,
+ 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3,
+ 0x5e712f2f, 0x13978484, 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1,
+ 0xb6ed5b5b, 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf,
+ 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, 0x8acf4545,
+ 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, 0xa2f35151, 0x5dfea3a3,
+ 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada,
+ 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec,
+ 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464,
+ 0xbae75d5d, 0x322b1919, 0xe6957373, 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a,
+ 0x3bab9090, 0x0b838888, 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b,
+ 0xad76dbdb, 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c,
+ 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, 0xd532e7e7,
+ 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, 0xd8b46c6c, 0xacfa5656,
+ 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, 0x6fd5baba, 0xf0887878, 0x4a6f2525,
+ 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f,
+ 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848,
+ 0x06050303, 0xf701f6f6, 0x1c120e0e, 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1,
+ 0x3a271d1d, 0x27b99e9e, 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x07898e8e,
+ 0x33a79494, 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf,
+ 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, 0x82c34141,
+ 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616 };
+
+ private static final int[] Tinv0 = { 0x50a7f451, 0x5365417e, 0xc3a4171a, 0x965e273a, 0xcb6bab3b, 0xf1459d1f,
+ 0xab58faac, 0x9303e34b, 0x55fa3020, 0xf66d76ad, 0x9176cc88, 0x254c02f5, 0xfcd7e54f, 0xd7cb2ac5, 0x80443526,
+ 0x8fa362b5, 0x495ab1de, 0x671bba25, 0x980eea45, 0xe1c0fe5d, 0x02752fc3, 0x12f04c81, 0xa397468d, 0xc6f9d36b,
+ 0xe75f8f03, 0x959c9215, 0xeb7a6dbf, 0xda595295, 0x2d83bed4, 0xd3217458, 0x2969e049, 0x44c8c98e, 0x6a89c275,
+ 0x78798ef4, 0x6b3e5899, 0xdd71b927, 0xb64fe1be, 0x17ad88f0, 0x66ac20c9, 0xb43ace7d, 0x184adf63, 0x82311ae5,
+ 0x60335197, 0x457f5362, 0xe07764b1, 0x84ae6bbb, 0x1ca081fe, 0x942b08f9, 0x58684870, 0x19fd458f, 0x876cde94,
+ 0xb7f87b52, 0x23d373ab, 0xe2024b72, 0x578f1fe3, 0x2aab5566, 0x0728ebb2, 0x03c2b52f, 0x9a7bc586, 0xa50837d3,
+ 0xf2872830, 0xb2a5bf23, 0xba6a0302, 0x5c8216ed, 0x2b1ccf8a, 0x92b479a7, 0xf0f207f3, 0xa1e2694e, 0xcdf4da65,
+ 0xd5be0506, 0x1f6234d1, 0x8afea6c4, 0x9d532e34, 0xa055f3a2, 0x32e18a05, 0x75ebf6a4, 0x39ec830b, 0xaaef6040,
+ 0x069f715e, 0x51106ebd, 0xf98a213e, 0x3d06dd96, 0xae053edd, 0x46bde64d, 0xb58d5491, 0x055dc471, 0x6fd40604,
+ 0xff155060, 0x24fb9819, 0x97e9bdd6, 0xcc434089, 0x779ed967, 0xbd42e8b0, 0x888b8907, 0x385b19e7, 0xdbeec879,
+ 0x470a7ca1, 0xe90f427c, 0xc91e84f8, 0x00000000, 0x83868009, 0x48ed2b32, 0xac70111e, 0x4e725a6c, 0xfbff0efd,
+ 0x5638850f, 0x1ed5ae3d, 0x27392d36, 0x64d90f0a, 0x21a65c68, 0xd1545b9b, 0x3a2e3624, 0xb1670a0c, 0x0fe75793,
+ 0xd296eeb4, 0x9e919b1b, 0x4fc5c080, 0xa220dc61, 0x694b775a, 0x161a121c, 0x0aba93e2, 0xe52aa0c0, 0x43e0223c,
+ 0x1d171b12, 0x0b0d090e, 0xadc78bf2, 0xb9a8b62d, 0xc8a91e14, 0x8519f157, 0x4c0775af, 0xbbdd99ee, 0xfd607fa3,
+ 0x9f2601f7, 0xbcf5725c, 0xc53b6644, 0x347efb5b, 0x7629438b, 0xdcc623cb, 0x68fcedb6, 0x63f1e4b8, 0xcadc31d7,
+ 0x10856342, 0x40229713, 0x2011c684, 0x7d244a85, 0xf83dbbd2, 0x1132f9ae, 0x6da129c7, 0x4b2f9e1d, 0xf330b2dc,
+ 0xec52860d, 0xd0e3c177, 0x6c16b32b, 0x99b970a9, 0xfa489411, 0x2264e947, 0xc48cfca8, 0x1a3ff0a0, 0xd82c7d56,
+ 0xef903322, 0xc74e4987, 0xc1d138d9, 0xfea2ca8c, 0x360bd498, 0xcf81f5a6, 0x28de7aa5, 0x268eb7da, 0xa4bfad3f,
+ 0xe49d3a2c, 0x0d927850, 0x9bcc5f6a, 0x62467e54, 0xc2138df6, 0xe8b8d890, 0x5ef7392e, 0xf5afc382, 0xbe805d9f,
+ 0x7c93d069, 0xa92dd56f, 0xb31225cf, 0x3b99acc8, 0xa77d1810, 0x6e639ce8, 0x7bbb3bdb, 0x097826cd, 0xf418596e,
+ 0x01b79aec, 0xa89a4f83, 0x656e95e6, 0x7ee6ffaa, 0x08cfbc21, 0xe6e815ef, 0xd99be7ba, 0xce366f4a, 0xd4099fea,
+ 0xd67cb029, 0xafb2a431, 0x31233f2a, 0x3094a5c6, 0xc066a235, 0x37bc4e74, 0xa6ca82fc, 0xb0d090e0, 0x15d8a733,
+ 0x4a9804f1, 0xf7daec41, 0x0e50cd7f, 0x2ff69117, 0x8dd64d76, 0x4db0ef43, 0x544daacc, 0xdf0496e4, 0xe3b5d19e,
+ 0x1b886a4c, 0xb81f2cc1, 0x7f516546, 0x04ea5e9d, 0x5d358c01, 0x737487fa, 0x2e410bfb, 0x5a1d67b3, 0x52d2db92,
+ 0x335610e9, 0x1347d66d, 0x8c61d79a, 0x7a0ca137, 0x8e14f859, 0x893c13eb, 0xee27a9ce, 0x35c961b7, 0xede51ce1,
+ 0x3cb1477a, 0x59dfd29c, 0x3f73f255, 0x79ce1418, 0xbf37c773, 0xeacdf753, 0x5baafd5f, 0x146f3ddf, 0x86db4478,
+ 0x81f3afca, 0x3ec468b9, 0x2c342438, 0x5f40a3c2, 0x72c31d16, 0x0c25e2bc, 0x8b493c28, 0x41950dff, 0x7101a839,
+ 0xdeb30c08, 0x9ce4b4d8, 0x90c15664, 0x6184cb7b, 0x70b632d5, 0x745c6c48, 0x4257b8d0 };
+
+ private static final int[] Tinv1 = { 0xa7f45150, 0x65417e53, 0xa4171ac3, 0x5e273a96, 0x6bab3bcb, 0x459d1ff1,
+ 0x58faacab, 0x03e34b93, 0xfa302055, 0x6d76adf6, 0x76cc8891, 0x4c02f525, 0xd7e54ffc, 0xcb2ac5d7, 0x44352680,
+ 0xa362b58f, 0x5ab1de49, 0x1bba2567, 0x0eea4598, 0xc0fe5de1, 0x752fc302, 0xf04c8112, 0x97468da3, 0xf9d36bc6,
+ 0x5f8f03e7, 0x9c921595, 0x7a6dbfeb, 0x595295da, 0x83bed42d, 0x217458d3, 0x69e04929, 0xc8c98e44, 0x89c2756a,
+ 0x798ef478, 0x3e58996b, 0x71b927dd, 0x4fe1beb6, 0xad88f017, 0xac20c966, 0x3ace7db4, 0x4adf6318, 0x311ae582,
+ 0x33519760, 0x7f536245, 0x7764b1e0, 0xae6bbb84, 0xa081fe1c, 0x2b08f994, 0x68487058, 0xfd458f19, 0x6cde9487,
+ 0xf87b52b7, 0xd373ab23, 0x024b72e2, 0x8f1fe357, 0xab55662a, 0x28ebb207, 0xc2b52f03, 0x7bc5869a, 0x0837d3a5,
+ 0x872830f2, 0xa5bf23b2, 0x6a0302ba, 0x8216ed5c, 0x1ccf8a2b, 0xb479a792, 0xf207f3f0, 0xe2694ea1, 0xf4da65cd,
+ 0xbe0506d5, 0x6234d11f, 0xfea6c48a, 0x532e349d, 0x55f3a2a0, 0xe18a0532, 0xebf6a475, 0xec830b39, 0xef6040aa,
+ 0x9f715e06, 0x106ebd51, 0x8a213ef9, 0x06dd963d, 0x053eddae, 0xbde64d46, 0x8d5491b5, 0x5dc47105, 0xd406046f,
+ 0x155060ff, 0xfb981924, 0xe9bdd697, 0x434089cc, 0x9ed96777, 0x42e8b0bd, 0x8b890788, 0x5b19e738, 0xeec879db,
+ 0x0a7ca147, 0x0f427ce9, 0x1e84f8c9, 0x00000000, 0x86800983, 0xed2b3248, 0x70111eac, 0x725a6c4e, 0xff0efdfb,
+ 0x38850f56, 0xd5ae3d1e, 0x392d3627, 0xd90f0a64, 0xa65c6821, 0x545b9bd1, 0x2e36243a, 0x670a0cb1, 0xe757930f,
+ 0x96eeb4d2, 0x919b1b9e, 0xc5c0804f, 0x20dc61a2, 0x4b775a69, 0x1a121c16, 0xba93e20a, 0x2aa0c0e5, 0xe0223c43,
+ 0x171b121d, 0x0d090e0b, 0xc78bf2ad, 0xa8b62db9, 0xa91e14c8, 0x19f15785, 0x0775af4c, 0xdd99eebb, 0x607fa3fd,
+ 0x2601f79f, 0xf5725cbc, 0x3b6644c5, 0x7efb5b34, 0x29438b76, 0xc623cbdc, 0xfcedb668, 0xf1e4b863, 0xdc31d7ca,
+ 0x85634210, 0x22971340, 0x11c68420, 0x244a857d, 0x3dbbd2f8, 0x32f9ae11, 0xa129c76d, 0x2f9e1d4b, 0x30b2dcf3,
+ 0x52860dec, 0xe3c177d0, 0x16b32b6c, 0xb970a999, 0x489411fa, 0x64e94722, 0x8cfca8c4, 0x3ff0a01a, 0x2c7d56d8,
+ 0x903322ef, 0x4e4987c7, 0xd138d9c1, 0xa2ca8cfe, 0x0bd49836, 0x81f5a6cf, 0xde7aa528, 0x8eb7da26, 0xbfad3fa4,
+ 0x9d3a2ce4, 0x9278500d, 0xcc5f6a9b, 0x467e5462, 0x138df6c2, 0xb8d890e8, 0xf7392e5e, 0xafc382f5, 0x805d9fbe,
+ 0x93d0697c, 0x2dd56fa9, 0x1225cfb3, 0x99acc83b, 0x7d1810a7, 0x639ce86e, 0xbb3bdb7b, 0x7826cd09, 0x18596ef4,
+ 0xb79aec01, 0x9a4f83a8, 0x6e95e665, 0xe6ffaa7e, 0xcfbc2108, 0xe815efe6, 0x9be7bad9, 0x366f4ace, 0x099fead4,
+ 0x7cb029d6, 0xb2a431af, 0x233f2a31, 0x94a5c630, 0x66a235c0, 0xbc4e7437, 0xca82fca6, 0xd090e0b0, 0xd8a73315,
+ 0x9804f14a, 0xdaec41f7, 0x50cd7f0e, 0xf691172f, 0xd64d768d, 0xb0ef434d, 0x4daacc54, 0x0496e4df, 0xb5d19ee3,
+ 0x886a4c1b, 0x1f2cc1b8, 0x5165467f, 0xea5e9d04, 0x358c015d, 0x7487fa73, 0x410bfb2e, 0x1d67b35a, 0xd2db9252,
+ 0x5610e933, 0x47d66d13, 0x61d79a8c, 0x0ca1377a, 0x14f8598e, 0x3c13eb89, 0x27a9ceee, 0xc961b735, 0xe51ce1ed,
+ 0xb1477a3c, 0xdfd29c59, 0x73f2553f, 0xce141879, 0x37c773bf, 0xcdf753ea, 0xaafd5f5b, 0x6f3ddf14, 0xdb447886,
+ 0xf3afca81, 0xc468b93e, 0x3424382c, 0x40a3c25f, 0xc31d1672, 0x25e2bc0c, 0x493c288b, 0x950dff41, 0x01a83971,
+ 0xb30c08de, 0xe4b4d89c, 0xc1566490, 0x84cb7b61, 0xb632d570, 0x5c6c4874, 0x57b8d042 };
+
+ private static final int[] Tinv2 = { 0xf45150a7, 0x417e5365, 0x171ac3a4, 0x273a965e, 0xab3bcb6b, 0x9d1ff145,
+ 0xfaacab58, 0xe34b9303, 0x302055fa, 0x76adf66d, 0xcc889176, 0x02f5254c, 0xe54ffcd7, 0x2ac5d7cb, 0x35268044,
+ 0x62b58fa3, 0xb1de495a, 0xba25671b, 0xea45980e, 0xfe5de1c0, 0x2fc30275, 0x4c8112f0, 0x468da397, 0xd36bc6f9,
+ 0x8f03e75f, 0x9215959c, 0x6dbfeb7a, 0x5295da59, 0xbed42d83, 0x7458d321, 0xe0492969, 0xc98e44c8, 0xc2756a89,
+ 0x8ef47879, 0x58996b3e, 0xb927dd71, 0xe1beb64f, 0x88f017ad, 0x20c966ac, 0xce7db43a, 0xdf63184a, 0x1ae58231,
+ 0x51976033, 0x5362457f, 0x64b1e077, 0x6bbb84ae, 0x81fe1ca0, 0x08f9942b, 0x48705868, 0x458f19fd, 0xde94876c,
+ 0x7b52b7f8, 0x73ab23d3, 0x4b72e202, 0x1fe3578f, 0x55662aab, 0xebb20728, 0xb52f03c2, 0xc5869a7b, 0x37d3a508,
+ 0x2830f287, 0xbf23b2a5, 0x0302ba6a, 0x16ed5c82, 0xcf8a2b1c, 0x79a792b4, 0x07f3f0f2, 0x694ea1e2, 0xda65cdf4,
+ 0x0506d5be, 0x34d11f62, 0xa6c48afe, 0x2e349d53, 0xf3a2a055, 0x8a0532e1, 0xf6a475eb, 0x830b39ec, 0x6040aaef,
+ 0x715e069f, 0x6ebd5110, 0x213ef98a, 0xdd963d06, 0x3eddae05, 0xe64d46bd, 0x5491b58d, 0xc471055d, 0x06046fd4,
+ 0x5060ff15, 0x981924fb, 0xbdd697e9, 0x4089cc43, 0xd967779e, 0xe8b0bd42, 0x8907888b, 0x19e7385b, 0xc879dbee,
+ 0x7ca1470a, 0x427ce90f, 0x84f8c91e, 0x00000000, 0x80098386, 0x2b3248ed, 0x111eac70, 0x5a6c4e72, 0x0efdfbff,
+ 0x850f5638, 0xae3d1ed5, 0x2d362739, 0x0f0a64d9, 0x5c6821a6, 0x5b9bd154, 0x36243a2e, 0x0a0cb167, 0x57930fe7,
+ 0xeeb4d296, 0x9b1b9e91, 0xc0804fc5, 0xdc61a220, 0x775a694b, 0x121c161a, 0x93e20aba, 0xa0c0e52a, 0x223c43e0,
+ 0x1b121d17, 0x090e0b0d, 0x8bf2adc7, 0xb62db9a8, 0x1e14c8a9, 0xf1578519, 0x75af4c07, 0x99eebbdd, 0x7fa3fd60,
+ 0x01f79f26, 0x725cbcf5, 0x6644c53b, 0xfb5b347e, 0x438b7629, 0x23cbdcc6, 0xedb668fc, 0xe4b863f1, 0x31d7cadc,
+ 0x63421085, 0x97134022, 0xc6842011, 0x4a857d24, 0xbbd2f83d, 0xf9ae1132, 0x29c76da1, 0x9e1d4b2f, 0xb2dcf330,
+ 0x860dec52, 0xc177d0e3, 0xb32b6c16, 0x70a999b9, 0x9411fa48, 0xe9472264, 0xfca8c48c, 0xf0a01a3f, 0x7d56d82c,
+ 0x3322ef90, 0x4987c74e, 0x38d9c1d1, 0xca8cfea2, 0xd498360b, 0xf5a6cf81, 0x7aa528de, 0xb7da268e, 0xad3fa4bf,
+ 0x3a2ce49d, 0x78500d92, 0x5f6a9bcc, 0x7e546246, 0x8df6c213, 0xd890e8b8, 0x392e5ef7, 0xc382f5af, 0x5d9fbe80,
+ 0xd0697c93, 0xd56fa92d, 0x25cfb312, 0xacc83b99, 0x1810a77d, 0x9ce86e63, 0x3bdb7bbb, 0x26cd0978, 0x596ef418,
+ 0x9aec01b7, 0x4f83a89a, 0x95e6656e, 0xffaa7ee6, 0xbc2108cf, 0x15efe6e8, 0xe7bad99b, 0x6f4ace36, 0x9fead409,
+ 0xb029d67c, 0xa431afb2, 0x3f2a3123, 0xa5c63094, 0xa235c066, 0x4e7437bc, 0x82fca6ca, 0x90e0b0d0, 0xa73315d8,
+ 0x04f14a98, 0xec41f7da, 0xcd7f0e50, 0x91172ff6, 0x4d768dd6, 0xef434db0, 0xaacc544d, 0x96e4df04, 0xd19ee3b5,
+ 0x6a4c1b88, 0x2cc1b81f, 0x65467f51, 0x5e9d04ea, 0x8c015d35, 0x87fa7374, 0x0bfb2e41, 0x67b35a1d, 0xdb9252d2,
+ 0x10e93356, 0xd66d1347, 0xd79a8c61, 0xa1377a0c, 0xf8598e14, 0x13eb893c, 0xa9ceee27, 0x61b735c9, 0x1ce1ede5,
+ 0x477a3cb1, 0xd29c59df, 0xf2553f73, 0x141879ce, 0xc773bf37, 0xf753eacd, 0xfd5f5baa, 0x3ddf146f, 0x447886db,
+ 0xafca81f3, 0x68b93ec4, 0x24382c34, 0xa3c25f40, 0x1d1672c3, 0xe2bc0c25, 0x3c288b49, 0x0dff4195, 0xa8397101,
+ 0x0c08deb3, 0xb4d89ce4, 0x566490c1, 0xcb7b6184, 0x32d570b6, 0x6c48745c, 0xb8d04257 };
+
+ private static final int[] Tinv3 = { 0x5150a7f4, 0x7e536541, 0x1ac3a417, 0x3a965e27, 0x3bcb6bab, 0x1ff1459d,
+ 0xacab58fa, 0x4b9303e3, 0x2055fa30, 0xadf66d76, 0x889176cc, 0xf5254c02, 0x4ffcd7e5, 0xc5d7cb2a, 0x26804435,
+ 0xb58fa362, 0xde495ab1, 0x25671bba, 0x45980eea, 0x5de1c0fe, 0xc302752f, 0x8112f04c, 0x8da39746, 0x6bc6f9d3,
+ 0x03e75f8f, 0x15959c92, 0xbfeb7a6d, 0x95da5952, 0xd42d83be, 0x58d32174, 0x492969e0, 0x8e44c8c9, 0x756a89c2,
+ 0xf478798e, 0x996b3e58, 0x27dd71b9, 0xbeb64fe1, 0xf017ad88, 0xc966ac20, 0x7db43ace, 0x63184adf, 0xe582311a,
+ 0x97603351, 0x62457f53, 0xb1e07764, 0xbb84ae6b, 0xfe1ca081, 0xf9942b08, 0x70586848, 0x8f19fd45, 0x94876cde,
+ 0x52b7f87b, 0xab23d373, 0x72e2024b, 0xe3578f1f, 0x662aab55, 0xb20728eb, 0x2f03c2b5, 0x869a7bc5, 0xd3a50837,
+ 0x30f28728, 0x23b2a5bf, 0x02ba6a03, 0xed5c8216, 0x8a2b1ccf, 0xa792b479, 0xf3f0f207, 0x4ea1e269, 0x65cdf4da,
+ 0x06d5be05, 0xd11f6234, 0xc48afea6, 0x349d532e, 0xa2a055f3, 0x0532e18a, 0xa475ebf6, 0x0b39ec83, 0x40aaef60,
+ 0x5e069f71, 0xbd51106e, 0x3ef98a21, 0x963d06dd, 0xddae053e, 0x4d46bde6, 0x91b58d54, 0x71055dc4, 0x046fd406,
+ 0x60ff1550, 0x1924fb98, 0xd697e9bd, 0x89cc4340, 0x67779ed9, 0xb0bd42e8, 0x07888b89, 0xe7385b19, 0x79dbeec8,
+ 0xa1470a7c, 0x7ce90f42, 0xf8c91e84, 0x00000000, 0x09838680, 0x3248ed2b, 0x1eac7011, 0x6c4e725a, 0xfdfbff0e,
+ 0x0f563885, 0x3d1ed5ae, 0x3627392d, 0x0a64d90f, 0x6821a65c, 0x9bd1545b, 0x243a2e36, 0x0cb1670a, 0x930fe757,
+ 0xb4d296ee, 0x1b9e919b, 0x804fc5c0, 0x61a220dc, 0x5a694b77, 0x1c161a12, 0xe20aba93, 0xc0e52aa0, 0x3c43e022,
+ 0x121d171b, 0x0e0b0d09, 0xf2adc78b, 0x2db9a8b6, 0x14c8a91e, 0x578519f1, 0xaf4c0775, 0xeebbdd99, 0xa3fd607f,
+ 0xf79f2601, 0x5cbcf572, 0x44c53b66, 0x5b347efb, 0x8b762943, 0xcbdcc623, 0xb668fced, 0xb863f1e4, 0xd7cadc31,
+ 0x42108563, 0x13402297, 0x842011c6, 0x857d244a, 0xd2f83dbb, 0xae1132f9, 0xc76da129, 0x1d4b2f9e, 0xdcf330b2,
+ 0x0dec5286, 0x77d0e3c1, 0x2b6c16b3, 0xa999b970, 0x11fa4894, 0x472264e9, 0xa8c48cfc, 0xa01a3ff0, 0x56d82c7d,
+ 0x22ef9033, 0x87c74e49, 0xd9c1d138, 0x8cfea2ca, 0x98360bd4, 0xa6cf81f5, 0xa528de7a, 0xda268eb7, 0x3fa4bfad,
+ 0x2ce49d3a, 0x500d9278, 0x6a9bcc5f, 0x5462467e, 0xf6c2138d, 0x90e8b8d8, 0x2e5ef739, 0x82f5afc3, 0x9fbe805d,
+ 0x697c93d0, 0x6fa92dd5, 0xcfb31225, 0xc83b99ac, 0x10a77d18, 0xe86e639c, 0xdb7bbb3b, 0xcd097826, 0x6ef41859,
+ 0xec01b79a, 0x83a89a4f, 0xe6656e95, 0xaa7ee6ff, 0x2108cfbc, 0xefe6e815, 0xbad99be7, 0x4ace366f, 0xead4099f,
+ 0x29d67cb0, 0x31afb2a4, 0x2a31233f, 0xc63094a5, 0x35c066a2, 0x7437bc4e, 0xfca6ca82, 0xe0b0d090, 0x3315d8a7,
+ 0xf14a9804, 0x41f7daec, 0x7f0e50cd, 0x172ff691, 0x768dd64d, 0x434db0ef, 0xcc544daa, 0xe4df0496, 0x9ee3b5d1,
+ 0x4c1b886a, 0xc1b81f2c, 0x467f5165, 0x9d04ea5e, 0x015d358c, 0xfa737487, 0xfb2e410b, 0xb35a1d67, 0x9252d2db,
+ 0xe9335610, 0x6d1347d6, 0x9a8c61d7, 0x377a0ca1, 0x598e14f8, 0xeb893c13, 0xceee27a9, 0xb735c961, 0xe1ede51c,
+ 0x7a3cb147, 0x9c59dfd2, 0x553f73f2, 0x1879ce14, 0x73bf37c7, 0x53eacdf7, 0x5f5baafd, 0xdf146f3d, 0x7886db44,
+ 0xca81f3af, 0xb93ec468, 0x382c3424, 0xc25f40a3, 0x1672c31d, 0xbc0c25e2, 0x288b493c, 0xff41950d, 0x397101a8,
+ 0x08deb30c, 0xd89ce4b4, 0x6490c156, 0x7b6184cb, 0xd570b632, 0x48745c6c, 0xd04257b8 };
+
+ private int shift(int r, int shift)
+ {
+ return (((r >>> shift) | (r << (32 - shift))));
+ }
+
+ /* multiply four bytes in GF(2^8) by 'x' {02} in parallel */
+
+ private static final int m1 = 0x80808080;
+ private static final int m2 = 0x7f7f7f7f;
+ private static final int m3 = 0x0000001b;
+
+ private int FFmulX(int x)
+ {
+ return (((x & m2) << 1) ^ (((x & m1) >>> 7) * m3));
+ }
+
+ /*
+ * The following defines provide alternative definitions of FFmulX that
+ * might give improved performance if a fast 32-bit multiply is not
+ * available.
+ *
+ * private int FFmulX(int x) { int u = x & m1; u |= (u >> 1); return ((x &
+ * m2) < < 1) ^ ((u >>> 3) | (u >>> 6)); } private static final int m4 =
+ * 0x1b1b1b1b; private int FFmulX(int x) { int u = x & m1; return ((x & m2) < <
+ * 1) ^ ((u - (u >>> 7)) & m4); }
+ *
+ */
+
+ private int inv_mcol(int x)
+ {
+ int f2 = FFmulX(x);
+ int f4 = FFmulX(f2);
+ int f8 = FFmulX(f4);
+ int f9 = x ^ f8;
+
+ return f2 ^ f4 ^ f8 ^ shift(f2 ^ f9, 8) ^ shift(f4 ^ f9, 16) ^ shift(f9, 24);
+ }
+
+ private int subWord(int x)
+ {
+ return (S[x & 255] & 255 | ((S[(x >> 8) & 255] & 255) << 8) | ((S[(x >> 16) & 255] & 255) << 16) | S[(x >> 24) & 255] << 24);
+ }
+
+ /**
+ * Calculate the necessary round keys The number of calculations depends on
+ * key size and block size AES specified a fixed block size of 128 bits and
+ * key sizes 128/192/256 bits This code is written assuming those are the
+ * only possible values
+ */
+ private int[][] generateWorkingKey(byte[] key, boolean forEncryption)
+ {
+ int KC = key.length / 4; // key length in words
+ int t;
+
+ if (((KC != 4) && (KC != 6) && (KC != 8)) || ((KC * 4) != key.length))
+ {
+ throw new IllegalArgumentException("Key length not 128/192/256 bits.");
+ }
+
+ ROUNDS = KC + 6; // This is not always true for the generalized
+ // Rijndael that allows larger block sizes
+ int[][] W = new int[ROUNDS + 1][4]; // 4 words in a block
+
+ //
+ // copy the key into the round key array
+ //
+
+ t = 0;
+ for (int i = 0; i < key.length; t++)
+ {
+ W[t >> 2][t & 3] = (key[i] & 0xff) | ((key[i + 1] & 0xff) << 8) | ((key[i + 2] & 0xff) << 16)
+ | (key[i + 3] << 24);
+ i += 4;
+ }
+
+ //
+ // while not enough round key material calculated
+ // calculate new values
+ //
+ int k = (ROUNDS + 1) << 2;
+ for (int i = KC; (i < k); i++)
+ {
+ int temp = W[(i - 1) >> 2][(i - 1) & 3];
+ if ((i % KC) == 0)
+ {
+ temp = subWord(shift(temp, 8)) ^ rcon[(i / KC) - 1];
+ }
+ else if ((KC > 6) && ((i % KC) == 4))
+ {
+ temp = subWord(temp);
+ }
+
+ W[i >> 2][i & 3] = W[(i - KC) >> 2][(i - KC) & 3] ^ temp;
+ }
+
+ if (!forEncryption)
+ {
+ for (int j = 1; j < ROUNDS; j++)
+ {
+ for (int i = 0; i < 4; i++)
+ {
+ W[j][i] = inv_mcol(W[j][i]);
+ }
+ }
+ }
+
+ return W;
+ }
+
+ private int ROUNDS;
+ private int[][] WorkingKey = null;
+ private int C0, C1, C2, C3;
+ private boolean doEncrypt;
+
+ private static final int BLOCK_SIZE = 16;
+
+ /**
+ * default constructor - 128 bit block size.
+ */
+ public AES()
+ {
+ }
+
+ /**
+ * initialise an AES cipher.
+ *
+ * @param forEncryption
+ * whether or not we are for encryption.
+ * @param key
+ * the key required to set up the cipher.
+ * @exception IllegalArgumentException
+ * if the params argument is inappropriate.
+ */
+
+ public final void init(boolean forEncryption, byte[] key)
+ {
+ WorkingKey = generateWorkingKey(key, forEncryption);
+ this.doEncrypt = forEncryption;
+ }
+
+ public final String getAlgorithmName()
+ {
+ return "AES";
+ }
+
+ public final int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ public final int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+ {
+ if (WorkingKey == null)
+ {
+ throw new IllegalStateException("AES engine not initialised");
+ }
+
+ if ((inOff + (32 / 2)) > in.length)
+ {
+ throw new IllegalArgumentException("input buffer too short");
+ }
+
+ if ((outOff + (32 / 2)) > out.length)
+ {
+ throw new IllegalArgumentException("output buffer too short");
+ }
+
+ if (doEncrypt)
+ {
+ unpackBlock(in, inOff);
+ encryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+ else
+ {
+ unpackBlock(in, inOff);
+ decryptBlock(WorkingKey);
+ packBlock(out, outOff);
+ }
+
+ return BLOCK_SIZE;
+ }
+
+ public final void reset()
+ {
+ }
+
+ private void unpackBlock(byte[] bytes, int off)
+ {
+ int index = off;
+
+ C0 = (bytes[index++] & 0xff);
+ C0 |= (bytes[index++] & 0xff) << 8;
+ C0 |= (bytes[index++] & 0xff) << 16;
+ C0 |= bytes[index++] << 24;
+
+ C1 = (bytes[index++] & 0xff);
+ C1 |= (bytes[index++] & 0xff) << 8;
+ C1 |= (bytes[index++] & 0xff) << 16;
+ C1 |= bytes[index++] << 24;
+
+ C2 = (bytes[index++] & 0xff);
+ C2 |= (bytes[index++] & 0xff) << 8;
+ C2 |= (bytes[index++] & 0xff) << 16;
+ C2 |= bytes[index++] << 24;
+
+ C3 = (bytes[index++] & 0xff);
+ C3 |= (bytes[index++] & 0xff) << 8;
+ C3 |= (bytes[index++] & 0xff) << 16;
+ C3 |= bytes[index++] << 24;
+ }
+
+ private void packBlock(byte[] bytes, int off)
+ {
+ int index = off;
+
+ bytes[index++] = (byte) C0;
+ bytes[index++] = (byte) (C0 >> 8);
+ bytes[index++] = (byte) (C0 >> 16);
+ bytes[index++] = (byte) (C0 >> 24);
+
+ bytes[index++] = (byte) C1;
+ bytes[index++] = (byte) (C1 >> 8);
+ bytes[index++] = (byte) (C1 >> 16);
+ bytes[index++] = (byte) (C1 >> 24);
+
+ bytes[index++] = (byte) C2;
+ bytes[index++] = (byte) (C2 >> 8);
+ bytes[index++] = (byte) (C2 >> 16);
+ bytes[index++] = (byte) (C2 >> 24);
+
+ bytes[index++] = (byte) C3;
+ bytes[index++] = (byte) (C3 >> 8);
+ bytes[index++] = (byte) (C3 >> 16);
+ bytes[index++] = (byte) (C3 >> 24);
+ }
+
+ private void encryptBlock(int[][] KW)
+ {
+ int r, r0, r1, r2, r3;
+
+ C0 ^= KW[0][0];
+ C1 ^= KW[0][1];
+ C2 ^= KW[0][2];
+ C3 ^= KW[0][3];
+
+ for (r = 1; r < ROUNDS - 1;)
+ {
+ r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
+ r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
+ r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
+ r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
+ C0 = T0[r0 & 255] ^ T1[(r1 >> 8) & 255] ^ T2[(r2 >> 16) & 255] ^ T3[(r3 >> 24) & 255] ^ KW[r][0];
+ C1 = T0[r1 & 255] ^ T1[(r2 >> 8) & 255] ^ T2[(r3 >> 16) & 255] ^ T3[(r0 >> 24) & 255] ^ KW[r][1];
+ C2 = T0[r2 & 255] ^ T1[(r3 >> 8) & 255] ^ T2[(r0 >> 16) & 255] ^ T3[(r1 >> 24) & 255] ^ KW[r][2];
+ C3 = T0[r3 & 255] ^ T1[(r0 >> 8) & 255] ^ T2[(r1 >> 16) & 255] ^ T3[(r2 >> 24) & 255] ^ KW[r++][3];
+ }
+
+ r0 = T0[C0 & 255] ^ T1[(C1 >> 8) & 255] ^ T2[(C2 >> 16) & 255] ^ T3[(C3 >> 24) & 255] ^ KW[r][0];
+ r1 = T0[C1 & 255] ^ T1[(C2 >> 8) & 255] ^ T2[(C3 >> 16) & 255] ^ T3[(C0 >> 24) & 255] ^ KW[r][1];
+ r2 = T0[C2 & 255] ^ T1[(C3 >> 8) & 255] ^ T2[(C0 >> 16) & 255] ^ T3[(C1 >> 24) & 255] ^ KW[r][2];
+ r3 = T0[C3 & 255] ^ T1[(C0 >> 8) & 255] ^ T2[(C1 >> 16) & 255] ^ T3[(C2 >> 24) & 255] ^ KW[r++][3];
+
+ // the final round's table is a simple function of S so we don't use a
+ // whole other four tables for it
+
+ C0 = (S[r0 & 255] & 255) ^ ((S[(r1 >> 8) & 255] & 255) << 8) ^ ((S[(r2 >> 16) & 255] & 255) << 16)
+ ^ (S[(r3 >> 24) & 255] << 24) ^ KW[r][0];
+ C1 = (S[r1 & 255] & 255) ^ ((S[(r2 >> 8) & 255] & 255) << 8) ^ ((S[(r3 >> 16) & 255] & 255) << 16)
+ ^ (S[(r0 >> 24) & 255] << 24) ^ KW[r][1];
+ C2 = (S[r2 & 255] & 255) ^ ((S[(r3 >> 8) & 255] & 255) << 8) ^ ((S[(r0 >> 16) & 255] & 255) << 16)
+ ^ (S[(r1 >> 24) & 255] << 24) ^ KW[r][2];
+ C3 = (S[r3 & 255] & 255) ^ ((S[(r0 >> 8) & 255] & 255) << 8) ^ ((S[(r1 >> 16) & 255] & 255) << 16)
+ ^ (S[(r2 >> 24) & 255] << 24) ^ KW[r][3];
+
+ }
+
+ private void decryptBlock(int[][] KW)
+ {
+ int r, r0, r1, r2, r3;
+
+ C0 ^= KW[ROUNDS][0];
+ C1 ^= KW[ROUNDS][1];
+ C2 ^= KW[ROUNDS][2];
+ C3 ^= KW[ROUNDS][3];
+
+ for (r = ROUNDS - 1; r > 1;)
+ {
+ r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255]
+ ^ KW[r][0];
+ r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255]
+ ^ KW[r][1];
+ r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255]
+ ^ KW[r][2];
+ r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255]
+ ^ KW[r--][3];
+ C0 = Tinv0[r0 & 255] ^ Tinv1[(r3 >> 8) & 255] ^ Tinv2[(r2 >> 16) & 255] ^ Tinv3[(r1 >> 24) & 255]
+ ^ KW[r][0];
+ C1 = Tinv0[r1 & 255] ^ Tinv1[(r0 >> 8) & 255] ^ Tinv2[(r3 >> 16) & 255] ^ Tinv3[(r2 >> 24) & 255]
+ ^ KW[r][1];
+ C2 = Tinv0[r2 & 255] ^ Tinv1[(r1 >> 8) & 255] ^ Tinv2[(r0 >> 16) & 255] ^ Tinv3[(r3 >> 24) & 255]
+ ^ KW[r][2];
+ C3 = Tinv0[r3 & 255] ^ Tinv1[(r2 >> 8) & 255] ^ Tinv2[(r1 >> 16) & 255] ^ Tinv3[(r0 >> 24) & 255]
+ ^ KW[r--][3];
+ }
+
+ r0 = Tinv0[C0 & 255] ^ Tinv1[(C3 >> 8) & 255] ^ Tinv2[(C2 >> 16) & 255] ^ Tinv3[(C1 >> 24) & 255] ^ KW[r][0];
+ r1 = Tinv0[C1 & 255] ^ Tinv1[(C0 >> 8) & 255] ^ Tinv2[(C3 >> 16) & 255] ^ Tinv3[(C2 >> 24) & 255] ^ KW[r][1];
+ r2 = Tinv0[C2 & 255] ^ Tinv1[(C1 >> 8) & 255] ^ Tinv2[(C0 >> 16) & 255] ^ Tinv3[(C3 >> 24) & 255] ^ KW[r][2];
+ r3 = Tinv0[C3 & 255] ^ Tinv1[(C2 >> 8) & 255] ^ Tinv2[(C1 >> 16) & 255] ^ Tinv3[(C0 >> 24) & 255] ^ KW[r--][3];
+
+ // the final round's table is a simple function of Si so we don't use a
+ // whole other four tables for it
+
+ C0 = (Si[r0 & 255] & 255) ^ ((Si[(r3 >> 8) & 255] & 255) << 8) ^ ((Si[(r2 >> 16) & 255] & 255) << 16)
+ ^ (Si[(r1 >> 24) & 255] << 24) ^ KW[0][0];
+ C1 = (Si[r1 & 255] & 255) ^ ((Si[(r0 >> 8) & 255] & 255) << 8) ^ ((Si[(r3 >> 16) & 255] & 255) << 16)
+ ^ (Si[(r2 >> 24) & 255] << 24) ^ KW[0][1];
+ C2 = (Si[r2 & 255] & 255) ^ ((Si[(r1 >> 8) & 255] & 255) << 8) ^ ((Si[(r0 >> 16) & 255] & 255) << 16)
+ ^ (Si[(r3 >> 24) & 255] << 24) ^ KW[0][2];
+ C3 = (Si[r3 & 255] & 255) ^ ((Si[(r2 >> 8) & 255] & 255) << 8) ^ ((Si[(r1 >> 16) & 255] & 255) << 16)
+ ^ (Si[(r0 >> 24) & 255] << 24) ^ KW[0][3];
+ }
+
+ public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ processBlock(src, srcoff, dst, dstoff);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/BlockCipher.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/BlockCipher.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+/**
+ * BlockCipher.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public interface BlockCipher
+{
+ public void init(boolean forEncryption, byte[] key);
+
+ public int getBlockSize();
+
+ public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff);
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/BlockCipherFactory.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/BlockCipherFactory.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * BlockCipherFactory.
+ *
+ * @author Christian Plattner
+ * @version $Id: BlockCipherFactory.java 86 2014-04-07 14:15:18Z dkocher@sudo.ch $
+ */
+public class BlockCipherFactory
+{
+ private static final class CipherEntry
+ {
+ String type;
+ int blocksize;
+ int keysize;
+ String cipherClass;
+
+ public CipherEntry(String type, int blockSize, int keySize, String cipherClass)
+ {
+ this.type = type;
+ this.blocksize = blockSize;
+ this.keysize = keySize;
+ this.cipherClass = cipherClass;
+ }
+ }
+
+ private static final List ciphers = new ArrayList();
+
+ static
+ {
+ /* Higher Priority First */
+ ciphers.add(new CipherEntry("aes128-ctr", 16, 16, "ch.ethz.ssh2.crypto.cipher.AES"));
+ ciphers.add(new CipherEntry("aes192-ctr", 16, 24, "ch.ethz.ssh2.crypto.cipher.AES"));
+ ciphers.add(new CipherEntry("aes256-ctr", 16, 32, "ch.ethz.ssh2.crypto.cipher.AES"));
+ ciphers.add(new CipherEntry("blowfish-ctr", 8, 16, "ch.ethz.ssh2.crypto.cipher.BlowFish"));
+
+ ciphers.add(new CipherEntry("aes128-cbc", 16, 16, "ch.ethz.ssh2.crypto.cipher.AES"));
+ ciphers.add(new CipherEntry("aes192-cbc", 16, 24, "ch.ethz.ssh2.crypto.cipher.AES"));
+ ciphers.add(new CipherEntry("aes256-cbc", 16, 32, "ch.ethz.ssh2.crypto.cipher.AES"));
+ ciphers.add(new CipherEntry("blowfish-cbc", 8, 16, "ch.ethz.ssh2.crypto.cipher.BlowFish"));
+
+ ciphers.add(new CipherEntry("3des-ctr", 8, 24, "ch.ethz.ssh2.crypto.cipher.DESede"));
+ ciphers.add(new CipherEntry("3des-cbc", 8, 24, "ch.ethz.ssh2.crypto.cipher.DESede"));
+ }
+
+ public static String[] getDefaultCipherList()
+ {
+ List list = new ArrayList(ciphers.size());
+ for (CipherEntry ce : ciphers)
+ {
+ list.add(ce.type);
+ }
+ return list.toArray(new String[ciphers.size()]);
+ }
+
+ public static void checkCipherList(String[] cipherCandidates)
+ {
+ for (String cipherCandidate : cipherCandidates)
+ {
+ getEntry(cipherCandidate);
+ }
+ }
+
+ // @SuppressWarnings("rawtypes")
+ public static BlockCipher createCipher(String type, boolean encrypt, byte[] key, byte[] iv)
+ {
+ try
+ {
+ CipherEntry ce = getEntry(type);
+ Class> cc = Class.forName(ce.cipherClass);
+ BlockCipher bc = (BlockCipher) cc.newInstance();
+
+ if (type.endsWith("-cbc"))
+ {
+ bc.init(encrypt, key);
+ return new CBCMode(bc, iv, encrypt);
+ }
+ else if (type.endsWith("-ctr"))
+ {
+ bc.init(true, key);
+ return new CTRMode(bc, iv, encrypt);
+ }
+ throw new IllegalArgumentException("Cannot instantiate " + type);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate " + type, e);
+ }
+ catch (InstantiationException e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate " + type, e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new IllegalArgumentException("Cannot instantiate " + type, e);
+ }
+ }
+
+ private static CipherEntry getEntry(String type)
+ {
+ for (CipherEntry ce : ciphers)
+ {
+ if (ce.type.equals(type))
+ {
+ return ce;
+ }
+ }
+ throw new IllegalArgumentException("Unkown algorithm " + type);
+ }
+
+ public static int getBlockSize(String type)
+ {
+ CipherEntry ce = getEntry(type);
+ return ce.blocksize;
+ }
+
+ public static int getKeySize(String type)
+ {
+ CipherEntry ce = getEntry(type);
+ return ce.keysize;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/BlowFish.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/BlowFish.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,402 @@
+package ch.ethz.ssh2.crypto.cipher;
+
+/*
+ This file was shamelessly taken from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * A class that provides Blowfish key encryption operations, such as encoding
+ * data and generating keys. All the algorithms herein are from Applied
+ * Cryptography and implement a simplified cryptography interface.
+ *
+ * @author See comments in the source file
+ * @version 2.50, 03/15/10
+ */
+public class BlowFish implements BlockCipher
+{
+
+ private final static int[] KP = { 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0,
+ 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5,
+ 0xB5470917, 0x9216D5D9, 0x8979FB1B },
+
+ KS0 = { 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 },
+
+ KS1 = { 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 },
+
+ KS2 = { 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 },
+
+ KS3 = { 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 };
+
+ // ====================================
+ // Useful constants
+ // ====================================
+
+ private static final int ROUNDS = 16;
+ private static final int BLOCK_SIZE = 8; // bytes = 64 bits
+ private static final int SBOX_SK = 256;
+ private static final int P_SZ = ROUNDS + 2;
+
+ private final int[] S0, S1, S2, S3; // the s-boxes
+ private final int[] P; // the p-array
+
+ private boolean doEncrypt = false;
+
+ private byte[] workingKey = null;
+
+ public BlowFish()
+ {
+ S0 = new int[SBOX_SK];
+ S1 = new int[SBOX_SK];
+ S2 = new int[SBOX_SK];
+ S3 = new int[SBOX_SK];
+ P = new int[P_SZ];
+ }
+
+ /**
+ * initialise a Blowfish cipher.
+ *
+ * @param encrypting
+ * whether or not we are for encryption.
+ * @param key
+ * the key required to set up the cipher.
+ * @exception IllegalArgumentException
+ * if the params argument is inappropriate.
+ */
+ public void init(boolean encrypting, byte[] key)
+ {
+ this.doEncrypt = encrypting;
+ this.workingKey = key;
+ setKey(this.workingKey);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "Blowfish";
+ }
+
+ public final void transformBlock(byte[] in, int inOff, byte[] out, int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("Blowfish not initialised");
+ }
+
+ if (doEncrypt)
+ {
+ encryptBlock(in, inOff, out, outOff);
+ }
+ else
+ {
+ decryptBlock(in, inOff, out, outOff);
+ }
+ }
+
+ public void reset()
+ {
+ }
+
+ public int getBlockSize()
+ {
+ return BLOCK_SIZE;
+ }
+
+ // ==================================
+ // Private Implementation
+ // ==================================
+
+ private int F(int x)
+ {
+ return (((S0[(x >>> 24)] + S1[(x >>> 16) & 0xff]) ^ S2[(x >>> 8) & 0xff]) + S3[x & 0xff]);
+ }
+
+ /**
+ * apply the encryption cycle to each value pair in the table.
+ */
+ private void processTable(int xl, int xr, int[] table)
+ {
+ int size = table.length;
+
+ for (int s = 0; s < size; s += 2)
+ {
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ table[s] = xr;
+ table[s + 1] = xl;
+
+ xr = xl; // end of cycle swap
+ xl = table[s];
+ }
+ }
+
+ private void setKey(byte[] key)
+ {
+ /*
+ * - comments are from _Applied Crypto_, Schneier, p338 please be
+ * careful comparing the two, AC numbers the arrays from 1, the enclosed
+ * code from 0.
+ *
+ * (1) Initialise the S-boxes and the P-array, with a fixed string This
+ * string contains the hexadecimal digits of pi (3.141...)
+ */
+ System.arraycopy(KS0, 0, S0, 0, SBOX_SK);
+ System.arraycopy(KS1, 0, S1, 0, SBOX_SK);
+ System.arraycopy(KS2, 0, S2, 0, SBOX_SK);
+ System.arraycopy(KS3, 0, S3, 0, SBOX_SK);
+
+ System.arraycopy(KP, 0, P, 0, P_SZ);
+
+ /*
+ * (2) Now, XOR P[0] with the first 32 bits of the key, XOR P[1] with
+ * the second 32-bits of the key, and so on for all bits of the key (up
+ * to P[17]). Repeatedly cycle through the key bits until the entire
+ * P-array has been XOR-ed with the key bits
+ */
+ int keyLength = key.length;
+ int keyIndex = 0;
+
+ for (int i = 0; i < P_SZ; i++)
+ {
+ // get the 32 bits of the key, in 4 * 8 bit chunks
+ int data = 0x0000000;
+ for (int j = 0; j < 4; j++)
+ {
+ // create a 32 bit block
+ data = (data << 8) | (key[keyIndex++] & 0xff);
+
+ // wrap when we get to the end of the key
+ if (keyIndex >= keyLength)
+ {
+ keyIndex = 0;
+ }
+ }
+ // XOR the newly created 32 bit chunk onto the P-array
+ P[i] ^= data;
+ }
+
+ /*
+ * (3) Encrypt the all-zero string with the Blowfish algorithm, using
+ * the subkeys described in (1) and (2)
+ *
+ * (4) Replace P1 and P2 with the output of step (3)
+ *
+ * (5) Encrypt the output of step(3) using the Blowfish algorithm, with
+ * the modified subkeys.
+ *
+ * (6) Replace P3 and P4 with the output of step (5)
+ *
+ * (7) Continue the process, replacing all elements of the P-array and
+ * then all four S-boxes in order, with the output of the continuously
+ * changing Blowfish algorithm
+ */
+
+ processTable(0, 0, P);
+ processTable(P[P_SZ - 2], P[P_SZ - 1], S0);
+ processTable(S0[SBOX_SK - 2], S0[SBOX_SK - 1], S1);
+ processTable(S1[SBOX_SK - 2], S1[SBOX_SK - 1], S2);
+ processTable(S2[SBOX_SK - 2], S2[SBOX_SK - 1], S3);
+ }
+
+ /**
+ * Encrypt the given input starting at the given offset and place the result
+ * in the provided buffer starting at the given offset. The input will be an
+ * exact multiple of our blocksize.
+ */
+ private void encryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
+ {
+ int xl = BytesTo32bits(src, srcIndex);
+ int xr = BytesTo32bits(src, srcIndex + 4);
+
+ xl ^= P[0];
+
+ for (int i = 1; i < ROUNDS; i += 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i + 1];
+ }
+
+ xr ^= P[ROUNDS + 1];
+
+ Bits32ToBytes(xr, dst, dstIndex);
+ Bits32ToBytes(xl, dst, dstIndex + 4);
+ }
+
+ /**
+ * Decrypt the given input starting at the given offset and place the result
+ * in the provided buffer starting at the given offset. The input will be an
+ * exact multiple of our blocksize.
+ */
+ private void decryptBlock(byte[] src, int srcIndex, byte[] dst, int dstIndex)
+ {
+ int xl = BytesTo32bits(src, srcIndex);
+ int xr = BytesTo32bits(src, srcIndex + 4);
+
+ xl ^= P[ROUNDS + 1];
+
+ for (int i = ROUNDS; i > 0; i -= 2)
+ {
+ xr ^= F(xl) ^ P[i];
+ xl ^= F(xr) ^ P[i - 1];
+ }
+
+ xr ^= P[0];
+
+ Bits32ToBytes(xr, dst, dstIndex);
+ Bits32ToBytes(xl, dst, dstIndex + 4);
+ }
+
+ private int BytesTo32bits(byte[] b, int i)
+ {
+ return ((b[i] & 0xff) << 24) | ((b[i + 1] & 0xff) << 16) | ((b[i + 2] & 0xff) << 8) | ((b[i + 3] & 0xff));
+ }
+
+ private void Bits32ToBytes(int in, byte[] b, int offset)
+ {
+ b[offset + 3] = (byte) in;
+ b[offset + 2] = (byte) (in >> 8);
+ b[offset + 1] = (byte) (in >> 16);
+ b[offset] = (byte) (in >> 24);
+ }
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/CBCMode.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/CBCMode.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+/**
+ * CBCMode.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class CBCMode implements BlockCipher
+{
+ BlockCipher tc;
+ int blockSize;
+ boolean doEncrypt;
+
+ byte[] cbc_vector;
+ byte[] tmp_vector;
+
+ public void init(boolean forEncryption, byte[] key)
+ {
+ }
+
+ public CBCMode(BlockCipher tc, byte[] iv, boolean doEncrypt)
+ throws IllegalArgumentException
+ {
+ this.tc = tc;
+ this.blockSize = tc.getBlockSize();
+ this.doEncrypt = doEncrypt;
+
+ if (this.blockSize != iv.length)
+ throw new IllegalArgumentException("IV must be " + blockSize
+ + " bytes long! (currently " + iv.length + ")");
+
+ this.cbc_vector = new byte[blockSize];
+ this.tmp_vector = new byte[blockSize];
+ System.arraycopy(iv, 0, cbc_vector, 0, blockSize);
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ private void encryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ for (int i = 0; i < blockSize; i++)
+ cbc_vector[i] ^= src[srcoff + i];
+
+ tc.transformBlock(cbc_vector, 0, dst, dstoff);
+
+ System.arraycopy(dst, dstoff, cbc_vector, 0, blockSize);
+ }
+
+ private void decryptBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ /* Assume the worst, src and dst are overlapping... */
+
+ System.arraycopy(src, srcoff, tmp_vector, 0, blockSize);
+
+ tc.transformBlock(src, srcoff, dst, dstoff);
+
+ for (int i = 0; i < blockSize; i++)
+ dst[dstoff + i] ^= cbc_vector[i];
+
+ /* ...that is why we need a tmp buffer. */
+
+ byte[] swap = cbc_vector;
+ cbc_vector = tmp_vector;
+ tmp_vector = swap;
+ }
+
+ public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ if (doEncrypt)
+ encryptBlock(src, srcoff, dst, dstoff);
+ else
+ decryptBlock(src, srcoff, dst, dstoff);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/CTRMode.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/CTRMode.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+/**
+ * This is CTR mode as described in draft-ietf-secsh-newmodes-XY.txt
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class CTRMode implements BlockCipher
+{
+ byte[] X;
+ byte[] Xenc;
+
+ BlockCipher bc;
+ int blockSize;
+ boolean doEncrypt;
+
+ int count = 0;
+
+ public void init(boolean forEncryption, byte[] key)
+ {
+ }
+
+ public CTRMode(BlockCipher tc, byte[] iv, boolean doEnc) throws IllegalArgumentException
+ {
+ bc = tc;
+ blockSize = bc.getBlockSize();
+ doEncrypt = doEnc;
+
+ if (blockSize != iv.length)
+ throw new IllegalArgumentException("IV must be " + blockSize + " bytes long! (currently " + iv.length + ")");
+
+ X = new byte[blockSize];
+ Xenc = new byte[blockSize];
+
+ System.arraycopy(iv, 0, X, 0, blockSize);
+ }
+
+ public final int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public final void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ bc.transformBlock(X, 0, Xenc, 0);
+
+ for (int i = 0; i < blockSize; i++)
+ {
+ dst[dstoff + i] = (byte) (src[srcoff + i] ^ Xenc[i]);
+ }
+
+ for (int i = (blockSize - 1); i >= 0; i--)
+ {
+ X[i]++;
+ if (X[i] != 0)
+ break;
+
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/CipherInputStream.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/CipherInputStream.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * CipherInputStream.
+ *
+ * @author Christian Plattner
+ * @version $Id: CipherInputStream.java 11 2011-05-27 14:14:06Z dkocher@sudo.ch $
+ */
+public class CipherInputStream
+{
+ BlockCipher currentCipher;
+ InputStream bi;
+ byte[] buffer;
+ byte[] enc;
+ int blockSize;
+ int pos;
+
+ /*
+ * We cannot use java.io.BufferedInputStream, since that is not available in
+ * J2ME. Everything could be improved alot here.
+ */
+
+ private static final int BUFF_SIZE = 8192;
+ byte[] input_buffer = new byte[BUFF_SIZE];
+ int input_buffer_pos = 0;
+ int input_buffer_size = 0;
+
+ public CipherInputStream(BlockCipher tc, InputStream bi)
+ {
+ this.bi = bi;
+ changeCipher(tc);
+ }
+
+ private int fill_buffer() throws IOException
+ {
+ input_buffer_pos = 0;
+ input_buffer_size = 0;
+ input_buffer_size = bi.read(input_buffer, 0, BUFF_SIZE);
+ return input_buffer_size;
+ }
+
+ private int internal_read(byte[] b, int off, int len) throws IOException
+ {
+ if (input_buffer_size < 0)
+ {
+ return -1;
+ }
+
+ if (input_buffer_pos >= input_buffer_size)
+ {
+ if (fill_buffer() <= 0)
+ {
+ return -1;
+ }
+ }
+
+ int avail = input_buffer_size - input_buffer_pos;
+ int thiscopy = (len > avail) ? avail : len;
+
+ System.arraycopy(input_buffer, input_buffer_pos, b, off, thiscopy);
+ input_buffer_pos += thiscopy;
+
+ return thiscopy;
+ }
+
+ public void changeCipher(BlockCipher bc)
+ {
+ this.currentCipher = bc;
+ blockSize = bc.getBlockSize();
+ buffer = new byte[blockSize];
+ enc = new byte[blockSize];
+ pos = blockSize;
+ }
+
+ private void getBlock() throws IOException
+ {
+ int n = 0;
+ while (n < blockSize)
+ {
+ int len = internal_read(enc, n, blockSize - n);
+ if (len < 0)
+ {
+ throw new IOException("Cannot read full block, EOF reached.");
+ }
+ n += len;
+ }
+
+ try
+ {
+ currentCipher.transformBlock(enc, 0, buffer, 0);
+ }
+ catch (Exception e)
+ {
+ throw new IOException("Error while decrypting block.");
+ }
+ pos = 0;
+ }
+
+ public int read(byte[] dst) throws IOException
+ {
+ return read(dst, 0, dst.length);
+ }
+
+ public int read(byte[] dst, int off, int len) throws IOException
+ {
+ int count = 0;
+
+ while (len > 0)
+ {
+ if (pos >= blockSize)
+ {
+ getBlock();
+ }
+
+ int avail = blockSize - pos;
+ int copy = Math.min(avail, len);
+ System.arraycopy(buffer, pos, dst, off, copy);
+ pos += copy;
+ off += copy;
+ len -= copy;
+ count += copy;
+ }
+ return count;
+ }
+
+ public int read() throws IOException
+ {
+ if (pos >= blockSize)
+ {
+ getBlock();
+ }
+ return buffer[pos++] & 0xff;
+ }
+
+ public int readPlain(byte[] b, int off, int len) throws IOException
+ {
+ if (pos != blockSize)
+ {
+ throw new IOException("Cannot read plain since crypto buffer is not aligned.");
+ }
+ int n = 0;
+ while (n < len)
+ {
+ int cnt = internal_read(b, off + n, len - n);
+ if (cnt < 0)
+ {
+ throw new IOException("Cannot fill buffer, EOF reached.");
+ }
+ n += cnt;
+ }
+ return n;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/CipherOutputStream.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/CipherOutputStream.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * CipherOutputStream.
+ *
+ * @author Christian Plattner
+ * @version $Id: CipherOutputStream.java 85 2014-04-07 14:05:09Z dkocher@sudo.ch $
+ */
+public class CipherOutputStream
+{
+ BlockCipher currentCipher;
+ OutputStream bo;
+ byte[] buffer;
+ byte[] enc;
+ int blockSize;
+ int pos;
+
+ /*
+ * We cannot use java.io.BufferedOutputStream, since that is not available
+ * in J2ME. Everything could be improved here alot.
+ */
+
+ private static final int BUFF_SIZE = 8192;
+ byte[] out_buffer = new byte[BUFF_SIZE];
+ int out_buffer_pos = 0;
+
+ public CipherOutputStream(BlockCipher tc, OutputStream bo)
+ {
+ this.bo = bo;
+ changeCipher(tc);
+ }
+
+ private void internal_write(byte[] src, int off, int len) throws IOException
+ {
+ while (len > 0)
+ {
+ int space = BUFF_SIZE - out_buffer_pos;
+ int copy = (len > space) ? space : len;
+
+ System.arraycopy(src, off, out_buffer, out_buffer_pos, copy);
+
+ off += copy;
+ out_buffer_pos += copy;
+ len -= copy;
+
+ if (out_buffer_pos >= BUFF_SIZE)
+ {
+ bo.write(out_buffer, 0, BUFF_SIZE);
+ out_buffer_pos = 0;
+ }
+ }
+ }
+
+ private void internal_write(int b) throws IOException
+ {
+ out_buffer[out_buffer_pos++] = (byte) b;
+ if (out_buffer_pos >= BUFF_SIZE)
+ {
+ bo.write(out_buffer, 0, BUFF_SIZE);
+ out_buffer_pos = 0;
+ }
+ }
+
+ public void flush() throws IOException
+ {
+ if (pos != 0)
+ {
+ throw new IOException("FATAL: cannot flush since crypto buffer is not aligned.");
+ }
+
+ if (out_buffer_pos > 0)
+ {
+ bo.write(out_buffer, 0, out_buffer_pos);
+ out_buffer_pos = 0;
+ }
+ bo.flush();
+ }
+
+ public void changeCipher(BlockCipher bc)
+ {
+ this.currentCipher = bc;
+ blockSize = bc.getBlockSize();
+ buffer = new byte[blockSize];
+ enc = new byte[blockSize];
+ pos = 0;
+ }
+
+ private void writeBlock() throws IOException
+ {
+ try
+ {
+ currentCipher.transformBlock(buffer, 0, enc, 0);
+ }
+ catch (Exception e)
+ {
+ throw new IOException("Error while decrypting block.", e);
+ }
+
+ internal_write(enc, 0, blockSize);
+ pos = 0;
+ }
+
+ public void write(byte[] src, int off, int len) throws IOException
+ {
+ while (len > 0)
+ {
+ int avail = blockSize - pos;
+ int copy = Math.min(avail, len);
+
+ System.arraycopy(src, off, buffer, pos, copy);
+ pos += copy;
+ off += copy;
+ len -= copy;
+
+ if (pos >= blockSize)
+ {
+ writeBlock();
+ }
+ }
+ }
+
+ public void write(int b) throws IOException
+ {
+ buffer[pos++] = (byte) b;
+ if (pos >= blockSize)
+ {
+ writeBlock();
+ }
+ }
+
+ public void writePlain(int b) throws IOException
+ {
+ if (pos != 0)
+ {
+ throw new IOException("Cannot write plain since crypto buffer is not aligned.");
+ }
+ internal_write(b);
+ }
+
+ public void writePlain(byte[] b, int off, int len) throws IOException
+ {
+ if (pos != 0)
+ {
+ throw new IOException("Cannot write plain since crypto buffer is not aligned.");
+ }
+ internal_write(b, off, len);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/DES.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/DES.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,372 @@
+package ch.ethz.ssh2.crypto.cipher;
+
+/*
+ This file is based on the 3DES implementation from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * DES.
+ *
+ * @author See comments in the source file
+ * @version 2.50, 03/15/10
+ *
+ */
+public class DES implements BlockCipher
+{
+ private int[] workingKey = null;
+
+ /**
+ * standard constructor.
+ */
+ public DES()
+ {
+ }
+
+ /**
+ * initialise a DES cipher.
+ *
+ * @param encrypting
+ * whether or not we are for encryption.
+ * @param key
+ * the parameters required to set up the cipher.
+ * @exception IllegalArgumentException
+ * if the params argument is inappropriate.
+ */
+ public void init(boolean encrypting, byte[] key)
+ {
+ this.workingKey = generateWorkingKey(encrypting, key, 0);
+ }
+
+ public String getAlgorithmName()
+ {
+ return "DES";
+ }
+
+ public int getBlockSize()
+ {
+ return 8;
+ }
+
+ public void transformBlock(byte[] in, int inOff, byte[] out, int outOff)
+ {
+ if (workingKey == null)
+ {
+ throw new IllegalStateException("DES engine not initialised!");
+ }
+
+ desFunc(workingKey, in, inOff, out, outOff);
+ }
+
+ public void reset()
+ {
+ }
+
+ /**
+ * what follows is mainly taken from "Applied Cryptography", by Bruce
+ * Schneier, however it also bears great resemblance to Richard
+ * Outerbridge's D3DES...
+ */
+
+ static short[] Df_Key = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32,
+ 0x10, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67 };
+
+ static short[] bytebit = { 0200, 0100, 040, 020, 010, 04, 02, 01 };
+
+ static int[] bigbyte = { 0x800000, 0x400000, 0x200000, 0x100000, 0x80000, 0x40000, 0x20000, 0x10000, 0x8000,
+ 0x4000, 0x2000, 0x1000, 0x800, 0x400, 0x200, 0x100, 0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1 };
+
+ /*
+ * Use the key schedule specified in the Standard (ANSI X3.92-1981).
+ */
+
+ static byte[] pc1 = { 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2,
+ 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12,
+ 4, 27, 19, 11, 3 };
+
+ static byte[] totrot = { 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 };
+
+ static byte[] pc2 = { 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, 40,
+ 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
+
+ static int[] SP1 = { 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004,
+ 0x00010000, 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+ 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, 0x00010004,
+ 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, 0x00010000, 0x01010404,
+ 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, 0x01010004, 0x00010000, 0x00010400,
+ 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+ 0x01000004, 0x00000404, 0x00010404, 0x01010400, 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004,
+ 0x00010400, 0x00000000, 0x01010004 };
+
+ static int[] SP2 = { 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020,
+ 0x80008020, 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+ 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, 0x00100020,
+ 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, 0x00000000, 0x00108020,
+ 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, 0x80100000, 0x80008000, 0x00000020,
+ 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+ 0x00100020, 0x80008020, 0x80000020, 0x00100020, 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000,
+ 0x80100020, 0x80108020, 0x00108000 };
+
+ static int[] SP3 = { 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208,
+ 0x08000200, 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+ 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, 0x08000208,
+ 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, 0x08020200, 0x08000000,
+ 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, 0x00000200, 0x00020008, 0x08020208,
+ 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+ 0x00000008, 0x00020208, 0x00020200, 0x08000008, 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208,
+ 0x00000008, 0x08020008, 0x00020200 };
+
+ static int[] SP4 = { 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001,
+ 0x00002001, 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+ 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, 0x00800081,
+ 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, 0x00800080, 0x00800001,
+ 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, 0x00002080, 0x00800080, 0x00800081,
+ 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+ 0x00800001, 0x00002001, 0x00802080, 0x00800081, 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080,
+ 0x00800000, 0x00002000, 0x00802080 };
+
+ static int[] SP5 = { 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000,
+ 0x02080000, 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+ 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, 0x42080000,
+ 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, 0x00080000, 0x42000100,
+ 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, 0x02000100, 0x40000000, 0x42080000,
+ 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+ 0x02080000, 0x00000000, 0x40080000, 0x42000000, 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000,
+ 0x40080000, 0x02080100, 0x40000100 };
+
+ static int[] SP6 = { 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010,
+ 0x00400000, 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+ 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, 0x20400010,
+ 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, 0x20004000, 0x00000010,
+ 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, 0x00400000, 0x20004000, 0x20000000,
+ 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+ 0x00000010, 0x00004000, 0x20400000, 0x00404010, 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000,
+ 0x20000000, 0x00400010, 0x20004010 };
+
+ static int[] SP7 = { 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802,
+ 0x04200800, 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+ 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, 0x04200000,
+ 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, 0x04000000, 0x00200800,
+ 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, 0x00200002, 0x04000000, 0x04000800,
+ 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+ 0x00200800, 0x00000000, 0x00000002, 0x04200802, 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002,
+ 0x04000800, 0x00000800, 0x00200002 };
+
+ static int[] SP8 = { 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040,
+ 0x10000000, 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+ 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, 0x00001040,
+ 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, 0x00041040, 0x00040000,
+ 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, 0x10001000, 0x00000040, 0x10000040,
+ 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+ 0x10040000, 0x10001000, 0x10001040, 0x00000000, 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040,
+ 0x00040040, 0x10000000, 0x10041000 };
+
+ /**
+ * generate an integer based working key based on our secret key and what we
+ * processing we are planning to do.
+ *
+ * Acknowledgements for this routine go to James Gillogly & Phil Karn.
+ * (whoever, and wherever they are!).
+ */
+ protected int[] generateWorkingKey(boolean encrypting, byte[] key, int off)
+ {
+ int[] newKey = new int[32];
+ boolean[] pc1m = new boolean[56], pcr = new boolean[56];
+
+ for (int j = 0; j < 56; j++)
+ {
+ int l = pc1[j];
+
+ pc1m[j] = ((key[off + (l >>> 3)] & bytebit[l & 07]) != 0);
+ }
+
+ for (int i = 0; i < 16; i++)
+ {
+ int l, m, n;
+
+ if (encrypting)
+ {
+ m = i << 1;
+ }
+ else
+ {
+ m = (15 - i) << 1;
+ }
+
+ n = m + 1;
+ newKey[m] = newKey[n] = 0;
+
+ for (int j = 0; j < 28; j++)
+ {
+ l = j + totrot[i];
+ if (l < 28)
+ {
+ pcr[j] = pc1m[l];
+ }
+ else
+ {
+ pcr[j] = pc1m[l - 28];
+ }
+ }
+
+ for (int j = 28; j < 56; j++)
+ {
+ l = j + totrot[i];
+ if (l < 56)
+ {
+ pcr[j] = pc1m[l];
+ }
+ else
+ {
+ pcr[j] = pc1m[l - 28];
+ }
+ }
+
+ for (int j = 0; j < 24; j++)
+ {
+ if (pcr[pc2[j]])
+ {
+ newKey[m] |= bigbyte[j];
+ }
+
+ if (pcr[pc2[j + 24]])
+ {
+ newKey[n] |= bigbyte[j];
+ }
+ }
+ }
+
+ //
+ // store the processed key
+ //
+ for (int i = 0; i != 32; i += 2)
+ {
+ int i1, i2;
+
+ i1 = newKey[i];
+ i2 = newKey[i + 1];
+
+ newKey[i] = ((i1 & 0x00fc0000) << 6) | ((i1 & 0x00000fc0) << 10) | ((i2 & 0x00fc0000) >>> 10)
+ | ((i2 & 0x00000fc0) >>> 6);
+
+ newKey[i + 1] = ((i1 & 0x0003f000) << 12) | ((i1 & 0x0000003f) << 16) | ((i2 & 0x0003f000) >>> 4)
+ | (i2 & 0x0000003f);
+ }
+
+ return newKey;
+ }
+
+ /**
+ * the DES engine.
+ */
+ protected void desFunc(int[] wKey, byte[] in, int inOff, byte[] out, int outOff)
+ {
+ int work, right, left;
+
+ left = (in[inOff + 0] & 0xff) << 24;
+ left |= (in[inOff + 1] & 0xff) << 16;
+ left |= (in[inOff + 2] & 0xff) << 8;
+ left |= (in[inOff + 3] & 0xff);
+
+ right = (in[inOff + 4] & 0xff) << 24;
+ right |= (in[inOff + 5] & 0xff) << 16;
+ right |= (in[inOff + 6] & 0xff) << 8;
+ right |= (in[inOff + 7] & 0xff);
+
+ work = ((left >>> 4) ^ right) & 0x0f0f0f0f;
+ right ^= work;
+ left ^= (work << 4);
+ work = ((left >>> 16) ^ right) & 0x0000ffff;
+ right ^= work;
+ left ^= (work << 16);
+ work = ((right >>> 2) ^ left) & 0x33333333;
+ left ^= work;
+ right ^= (work << 2);
+ work = ((right >>> 8) ^ left) & 0x00ff00ff;
+ left ^= work;
+ right ^= (work << 8);
+ right = ((right << 1) | ((right >>> 31) & 1)) & 0xffffffff;
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = ((left << 1) | ((left >>> 31) & 1)) & 0xffffffff;
+
+ for (int round = 0; round < 8; round++)
+ {
+ int fval;
+
+ work = (right << 28) | (right >>> 4);
+ work ^= wKey[round * 4 + 0];
+ fval = SP7[work & 0x3f];
+ fval |= SP5[(work >>> 8) & 0x3f];
+ fval |= SP3[(work >>> 16) & 0x3f];
+ fval |= SP1[(work >>> 24) & 0x3f];
+ work = right ^ wKey[round * 4 + 1];
+ fval |= SP8[work & 0x3f];
+ fval |= SP6[(work >>> 8) & 0x3f];
+ fval |= SP4[(work >>> 16) & 0x3f];
+ fval |= SP2[(work >>> 24) & 0x3f];
+ left ^= fval;
+ work = (left << 28) | (left >>> 4);
+ work ^= wKey[round * 4 + 2];
+ fval = SP7[work & 0x3f];
+ fval |= SP5[(work >>> 8) & 0x3f];
+ fval |= SP3[(work >>> 16) & 0x3f];
+ fval |= SP1[(work >>> 24) & 0x3f];
+ work = left ^ wKey[round * 4 + 3];
+ fval |= SP8[work & 0x3f];
+ fval |= SP6[(work >>> 8) & 0x3f];
+ fval |= SP4[(work >>> 16) & 0x3f];
+ fval |= SP2[(work >>> 24) & 0x3f];
+ right ^= fval;
+ }
+
+ right = (right << 31) | (right >>> 1);
+ work = (left ^ right) & 0xaaaaaaaa;
+ left ^= work;
+ right ^= work;
+ left = (left << 31) | (left >>> 1);
+ work = ((left >>> 8) ^ right) & 0x00ff00ff;
+ right ^= work;
+ left ^= (work << 8);
+ work = ((left >>> 2) ^ right) & 0x33333333;
+ right ^= work;
+ left ^= (work << 2);
+ work = ((right >>> 16) ^ left) & 0x0000ffff;
+ left ^= work;
+ right ^= (work << 16);
+ work = ((right >>> 4) ^ left) & 0x0f0f0f0f;
+ left ^= work;
+ right ^= (work << 4);
+
+ out[outOff + 0] = (byte) ((right >>> 24) & 0xff);
+ out[outOff + 1] = (byte) ((right >>> 16) & 0xff);
+ out[outOff + 2] = (byte) ((right >>> 8) & 0xff);
+ out[outOff + 3] = (byte) (right & 0xff);
+ out[outOff + 4] = (byte) ((left >>> 24) & 0xff);
+ out[outOff + 5] = (byte) ((left >>> 16) & 0xff);
+ out[outOff + 6] = (byte) ((left >>> 8) & 0xff);
+ out[outOff + 7] = (byte) (left & 0xff);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/DESede.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/DESede.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,109 @@
+package ch.ethz.ssh2.crypto.cipher;
+
+/*
+ This file was shamelessly taken (and modified) from the Bouncy Castle Crypto package.
+ Their licence file states the following:
+
+ Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle
+ (http://www.bouncycastle.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/**
+ * DESede.
+ *
+ * @author See comments in the source file
+ * @version 2.50, 03/15/10
+ *
+ */
+public class DESede extends DES
+{
+ private int[] key1 = null;
+ private int[] key2 = null;
+ private int[] key3 = null;
+
+ private boolean encrypt;
+
+ /**
+ * standard constructor.
+ */
+ public DESede()
+ {
+ }
+
+ /**
+ * initialise a DES cipher.
+ *
+ * @param encrypting
+ * whether or not we are for encryption.
+ * @param key
+ * the parameters required to set up the cipher.
+ * @exception IllegalArgumentException
+ * if the params argument is inappropriate.
+ */
+ @Override
+ public void init(boolean encrypting, byte[] key)
+ {
+ key1 = generateWorkingKey(encrypting, key, 0);
+ key2 = generateWorkingKey(!encrypting, key, 8);
+ key3 = generateWorkingKey(encrypting, key, 16);
+
+ encrypt = encrypting;
+ }
+
+ @Override
+ public String getAlgorithmName()
+ {
+ return "DESede";
+ }
+
+ @Override
+ public int getBlockSize()
+ {
+ return 8;
+ }
+
+ @Override
+ public void transformBlock(byte[] in, int inOff, byte[] out, int outOff)
+ {
+ if (key1 == null)
+ {
+ throw new IllegalStateException("DESede engine not initialised!");
+ }
+
+ if (encrypt)
+ {
+ desFunc(key1, in, inOff, out, outOff);
+ desFunc(key2, out, outOff, out, outOff);
+ desFunc(key3, out, outOff, out, outOff);
+ }
+ else
+ {
+ desFunc(key3, in, inOff, out, outOff);
+ desFunc(key2, out, outOff, out, outOff);
+ desFunc(key1, out, outOff, out, outOff);
+ }
+ }
+
+ @Override
+ public void reset()
+ {
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/cipher/NullCipher.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/cipher/NullCipher.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.cipher;
+
+/**
+ * NullCipher.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class NullCipher implements BlockCipher
+{
+ private int blockSize = 8;
+
+ public NullCipher()
+ {
+ }
+
+ public NullCipher(int blockSize)
+ {
+ this.blockSize = blockSize;
+ }
+
+ public void init(boolean forEncryption, byte[] key)
+ {
+ }
+
+ public int getBlockSize()
+ {
+ return blockSize;
+ }
+
+ public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff)
+ {
+ System.arraycopy(src, srcoff, dst, dstoff, blockSize);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/dh/DhExchange.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/dh/DhExchange.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.crypto.digest.HashForSSH2Types;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: DhExchange.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $
+ */
+public class DhExchange {
+ private static final Logger log = Logger.getLogger(DhExchange.class);
+
+ /* Given by the standard */
+
+ static final BigInteger p1, p14;
+ static final BigInteger g;
+
+ BigInteger p;
+
+ /* Client public and private */
+
+ BigInteger e;
+ BigInteger x;
+
+ /* Server public and private */
+
+ BigInteger f;
+ BigInteger y;
+
+ /* Shared secret */
+
+ BigInteger k;
+
+ static {
+ final String p1_string = "17976931348623159077083915679378745319786029604875"
+ + "60117064444236841971802161585193689478337958649255415021805654859805036464"
+ + "40548199239100050792877003355816639229553136239076508735759914822574862575"
+ + "00742530207744771258955095793777842444242661733472762929938766870920560605"
+ + "0270810842907692932019128194467627007";
+
+ final String p14_string = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129"
+ + "024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0"
+ + "A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB"
+ + "6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A"
+ + "163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208"
+ + "552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36C"
+ + "E3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF69558171"
+ + "83995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF";
+
+ p1 = new BigInteger(p1_string);
+ p14 = new BigInteger(p14_string, 16);
+ g = new BigInteger("2");
+ }
+
+ public DhExchange() {
+ }
+
+ public void clientInit(int group, SecureRandom rnd) {
+ k = null;
+
+ if(group == 1) {
+ p = p1;
+ }
+ else if(group == 14) {
+ p = p14;
+ }
+ else {
+ throw new IllegalArgumentException("Unknown DH group " + group);
+ }
+
+ while(true) {
+ x = new BigInteger(p.bitLength() - 1, rnd);
+ if(x.compareTo(BigInteger.ONE) > 0) {
+ break;
+ }
+ }
+
+ e = g.modPow(x, p);
+ }
+
+ public void serverInit(int group, SecureRandom rnd) {
+ k = null;
+
+ if(group == 1) {
+ p = p1;
+ }
+ else if(group == 14) {
+ p = p14;
+ }
+ else {
+ throw new IllegalArgumentException("Unknown DH group " + group);
+ }
+
+ y = new BigInteger(p.bitLength() - 1, rnd);
+
+ f = g.modPow(y, p);
+ }
+
+ /**
+ * @return Returns the e.
+ * @throws IllegalStateException
+ */
+ public BigInteger getE() {
+ if(e == null) {
+ throw new IllegalStateException("DhDsaExchange not initialized!");
+ }
+
+ return e;
+ }
+
+ /**
+ * @return Returns the f.
+ * @throws IllegalStateException
+ */
+ public BigInteger getF() {
+ if(f == null) {
+ throw new IllegalStateException("DhDsaExchange not initialized!");
+ }
+
+ return f;
+ }
+
+ /**
+ * @return Returns the shared secret k.
+ * @throws IllegalStateException
+ */
+ public BigInteger getK() {
+ if(k == null) {
+ throw new IllegalStateException("Shared secret not yet known, need f first!");
+ }
+
+ return k;
+ }
+
+ /**
+ * @param f
+ */
+ public void setF(BigInteger f) {
+ if(e == null) {
+ throw new IllegalStateException("DhDsaExchange not initialized!");
+ }
+
+ if(BigInteger.ZERO.compareTo(f) >= 0 || p.compareTo(f) <= 0) {
+ throw new IllegalArgumentException("Invalid f specified!");
+ }
+
+ this.f = f;
+ this.k = f.modPow(x, p);
+ }
+
+ /**
+ * @param e
+ */
+ public void setE(BigInteger e) {
+ if(f == null) {
+ throw new IllegalStateException("DhDsaExchange not initialized!");
+ }
+
+ if(BigInteger.ZERO.compareTo(e) >= 0 || p.compareTo(e) <= 0) {
+ throw new IllegalArgumentException("Invalid e specified!");
+ }
+
+ this.e = e;
+ this.k = e.modPow(y, p);
+ }
+
+ public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
+ byte[] serverKexPayload, byte[] hostKey) throws IOException {
+ HashForSSH2Types hash = new HashForSSH2Types("SHA1");
+
+ if(log.isInfoEnabled()) {
+ log.info("Client: '" + StringEncoder.GetString(clientversion) + "'");
+ log.info("Server: '" + StringEncoder.GetString(serverversion) + "'");
+ }
+
+ hash.updateByteString(clientversion);
+ hash.updateByteString(serverversion);
+ hash.updateByteString(clientKexPayload);
+ hash.updateByteString(serverKexPayload);
+ hash.updateByteString(hostKey);
+ hash.updateBigInt(e);
+ hash.updateBigInt(f);
+ hash.updateBigInt(k);
+
+ return hash.getDigest();
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/dh/DhGroupExchange.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/dh/DhGroupExchange.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.dh;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.DHGexParameters;
+import ch.ethz.ssh2.crypto.digest.HashForSSH2Types;
+
+/**
+ * DhGroupExchange.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class DhGroupExchange {
+ /* Given by the standard */
+
+ private BigInteger p;
+ private BigInteger g;
+
+ /* Client public and private */
+
+ private BigInteger e;
+ private BigInteger x;
+
+ /* Server public */
+
+ private BigInteger f;
+
+ /* Shared secret */
+
+ private BigInteger k;
+
+ public DhGroupExchange(BigInteger p, BigInteger g) {
+ this.p = p;
+ this.g = g;
+ }
+
+ public void init(SecureRandom rnd) {
+ k = null;
+
+ x = new BigInteger(p.bitLength() - 1, rnd);
+ e = g.modPow(x, p);
+ }
+
+ /**
+ * @return Returns the e.
+ */
+ public BigInteger getE() {
+ if(e == null) {
+ throw new IllegalStateException("Not initialized!");
+ }
+
+ return e;
+ }
+
+ /**
+ * @return Returns the shared secret k.
+ */
+ public BigInteger getK() {
+ if(k == null) {
+ throw new IllegalStateException("Shared secret not yet known, need f first!");
+ }
+
+ return k;
+ }
+
+ /**
+ * Sets f and calculates the shared secret.
+ */
+ public void setF(BigInteger f) {
+ if(e == null) {
+ throw new IllegalStateException("Not initialized!");
+ }
+
+ BigInteger zero = BigInteger.valueOf(0);
+
+ if(zero.compareTo(f) >= 0 || p.compareTo(f) <= 0) {
+ throw new IllegalArgumentException("Invalid f specified!");
+ }
+
+ this.f = f;
+ this.k = f.modPow(x, p);
+ }
+
+ public byte[] calculateH(byte[] clientversion, byte[] serverversion, byte[] clientKexPayload,
+ byte[] serverKexPayload, byte[] hostKey, DHGexParameters para) throws IOException {
+ HashForSSH2Types hash = new HashForSSH2Types("SHA1");
+
+ hash.updateByteString(clientversion);
+ hash.updateByteString(serverversion);
+ hash.updateByteString(clientKexPayload);
+ hash.updateByteString(serverKexPayload);
+ hash.updateByteString(hostKey);
+ if(para.getMin_group_len() > 0) {
+ hash.updateUINT32(para.getMin_group_len());
+ }
+ hash.updateUINT32(para.getPref_group_len());
+ if(para.getMax_group_len() > 0) {
+ hash.updateUINT32(para.getMax_group_len());
+ }
+ hash.updateBigInt(p);
+ hash.updateBigInt(g);
+ hash.updateBigInt(e);
+ hash.updateBigInt(f);
+ hash.updateBigInt(k);
+
+ return hash.getDigest();
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/digest/Digest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/Digest.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+
+/**
+ * Digest.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public interface Digest {
+ public int getDigestLength();
+
+ public void update(byte b);
+
+ public void update(byte[] b);
+
+ public void update(byte b[], int off, int len);
+
+ public void reset();
+
+ public void digest(byte[] out) throws DigestException;
+
+ public void digest(byte[] out, int off) throws DigestException;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/digest/HMAC.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/HMAC.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+
+/**
+ * HMAC.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class HMAC implements Digest
+{
+ Digest md;
+ byte[] k_xor_ipad;
+ byte[] k_xor_opad;
+
+ byte[] tmp;
+
+ int size;
+
+ public HMAC(Digest md, byte[] key, int size) throws DigestException {
+ this.md = md;
+ this.size = size;
+
+ tmp = new byte[md.getDigestLength()];
+
+ final int BLOCKSIZE = 64;
+
+ k_xor_ipad = new byte[BLOCKSIZE];
+ k_xor_opad = new byte[BLOCKSIZE];
+
+ if (key.length > BLOCKSIZE)
+ {
+ md.reset();
+ md.update(key);
+ md.digest(tmp);
+ key = tmp;
+ }
+
+ System.arraycopy(key, 0, k_xor_ipad, 0, key.length);
+ System.arraycopy(key, 0, k_xor_opad, 0, key.length);
+
+ for (int i = 0; i < BLOCKSIZE; i++)
+ {
+ k_xor_ipad[i] ^= 0x36;
+ k_xor_opad[i] ^= 0x5C;
+ }
+ md.update(k_xor_ipad);
+ }
+
+ public final int getDigestLength()
+ {
+ return size;
+ }
+
+ public final void update(byte b)
+ {
+ md.update(b);
+ }
+
+ public final void update(byte[] b)
+ {
+ md.update(b);
+ }
+
+ public final void update(byte[] b, int off, int len)
+ {
+ md.update(b, off, len);
+ }
+
+ public final void reset()
+ {
+ md.reset();
+ md.update(k_xor_ipad);
+ }
+
+ public final void digest(byte[] out) throws DigestException {
+ digest(out, 0);
+ }
+
+ public final void digest(byte[] out, int off) throws DigestException {
+ md.digest(tmp);
+
+ md.update(k_xor_opad);
+ md.update(tmp);
+
+ md.digest(tmp);
+
+ System.arraycopy(tmp, 0, out, off, size);
+
+ md.update(k_xor_ipad);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/digest/HashForSSH2Types.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/HashForSSH2Types.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.DigestException;
+
+/**
+ * HashForSSH2Types.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class HashForSSH2Types {
+ Digest md;
+
+ public HashForSSH2Types(Digest md) {
+ this.md = md;
+ }
+
+ public HashForSSH2Types(String type) {
+ if(type.equals("SHA1")) {
+ md = new SHA1();
+ }
+ else if(type.equals("SHA2")) {
+ md = new SHA256();
+ }
+ else if(type.equals("MD5")) {
+ md = new MD5();
+ }
+ else {
+ throw new IllegalArgumentException(String.format("Unknown algorithm %s", type));
+ }
+ }
+
+ public void updateByte(byte b) {
+ md.update(b);
+ }
+
+ public void updateBytes(byte[] b) {
+ md.update(b);
+ }
+
+ public void updateUINT32(int v) {
+ md.update((byte) (v >> 24));
+ md.update((byte) (v >> 16));
+ md.update((byte) (v >> 8));
+ md.update((byte) (v));
+ }
+
+ public void updateByteString(byte[] b) {
+ updateUINT32(b.length);
+ updateBytes(b);
+ }
+
+ public void updateBigInt(BigInteger b) {
+ updateByteString(b.toByteArray());
+ }
+
+ public void reset() {
+ md.reset();
+ }
+
+ public int getDigestLength() {
+ return md.getDigestLength();
+ }
+
+ public byte[] getDigest() throws IOException {
+ byte[] tmp = new byte[md.getDigestLength()];
+ getDigest(tmp);
+ return tmp;
+ }
+
+ public void getDigest(byte[] out) throws IOException {
+ try {
+ getDigest(out, 0);
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+ }
+
+ public void getDigest(byte[] out, int off) throws DigestException {
+ md.digest(out, off);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/digest/MAC.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/MAC.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.io.IOException;
+import java.security.DigestException;
+
+/**
+ * MAC.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class MAC {
+ private Digest mac;
+ private int size;
+
+ public static String[] getMacList() {
+ // Higher priority first. Added SHA-2 algorithms as in RFC 6668
+ return new String[]{"hmac-sha1-96", "hmac-sha1", "hmac-md5-96", "hmac-md5", "hmac-sha2-256", "hmac-sha2-512"};
+ }
+
+ public static void checkMacList(final String[] macs) {
+ for(String m : macs) {
+ getKeyLen(m);
+ }
+ }
+
+ public static int getKeyLen(final String type) {
+ if(type.equals("hmac-sha1")) {
+ return 20;
+ }
+ if(type.equals("hmac-sha1-96")) {
+ return 20;
+ }
+ if(type.equals("hmac-md5")) {
+ return 16;
+ }
+ if(type.equals("hmac-md5-96")) {
+ return 16;
+ }
+ if(type.equals("hmac-sha2-256")) {
+ return 32;
+ }
+ if(type.equals("hmac-sha2-512")) {
+ return 64;
+ }
+ throw new IllegalArgumentException(String.format("Unknown algorithm %s", type));
+ }
+
+ public MAC(final String type, final byte[] key) throws DigestException {
+ if(type.equals("hmac-sha1")) {
+ mac = new HMAC(new SHA1(), key, 20);
+ }
+ else if(type.equals("hmac-sha1-96")) {
+ mac = new HMAC(new SHA1(), key, 12);
+ }
+ else if(type.equals("hmac-md5")) {
+ mac = new HMAC(new MD5(), key, 16);
+ }
+ else if(type.equals("hmac-md5-96")) {
+ mac = new HMAC(new MD5(), key, 12);
+ }
+ else if(type.equals("hmac-sha2-256")) {
+ mac = new HMAC(new SHA256(), key, 32);
+ }
+ else if(type.equals("hmac-sha2-512")) {
+ mac = new HMAC(new SHA512(), key, 64);
+ }
+ else {
+ throw new IllegalArgumentException(String.format("Unknown algorithm %s", type));
+ }
+ size = mac.getDigestLength();
+ }
+
+ public final void initMac(final int seq) {
+ mac.reset();
+ mac.update((byte) (seq >> 24));
+ mac.update((byte) (seq >> 16));
+ mac.update((byte) (seq >> 8));
+ mac.update((byte) (seq));
+ }
+
+ public final void update(byte[] packetdata, int off, int len) {
+ mac.update(packetdata, off, len);
+ }
+
+ public final void getMac(byte[] out, int off) throws IOException {
+ try {
+ mac.digest(out, off);
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+ }
+
+ public final int size() {
+ return size;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/digest/MD5.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/MD5.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @version $Id: MD5.java 154 2014-04-28 11:45:02Z dkocher@sudo.ch $
+ */
+public final class MD5 implements Digest {
+
+ private MessageDigest md;
+
+ public MD5() {
+ try {
+ md = MessageDigest.getInstance("MD5");
+ }
+ catch(NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public final int getDigestLength() {
+ return md.getDigestLength();
+ }
+
+ public final void reset() {
+ md.reset();
+ }
+
+ public final void update(byte b[]) {
+ this.update(b, 0, b.length);
+ }
+
+ public final void update(byte b[], int off, int len) {
+ md.update(b, off, len);
+ }
+
+ public final void update(byte b) {
+ md.update(b);
+ }
+
+ public final void digest(byte[] out) throws DigestException {
+ this.digest(out, 0);
+ }
+
+ public final void digest(byte[] out, int off) throws DigestException {
+ md.digest(out, off, out.length);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/digest/SHA1.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/SHA1.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @version $Id: SHA1.java 154 2014-04-28 11:45:02Z dkocher@sudo.ch $
+ */
+public final class SHA1 implements Digest {
+
+ private MessageDigest md;
+
+ public SHA1() {
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ }
+ catch(NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public final int getDigestLength() {
+ return md.getDigestLength();
+ }
+
+ public final void reset() {
+ md.reset();
+ }
+
+ public final void update(byte b[]) {
+ this.update(b, 0, b.length);
+ }
+
+ public final void update(byte b[], int off, int len) {
+ md.update(b, off, len);
+ }
+
+ public final void update(byte b) {
+ md.update(b);
+ }
+
+ public final void digest(byte[] out) throws DigestException {
+ this.digest(out, 0);
+ }
+
+ public final void digest(byte[] out, int off) throws DigestException {
+ md.digest(out, off, out.length);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/digest/SHA256.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/SHA256.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @version $Id: SHA256.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $
+ */
+public final class SHA256 implements Digest {
+
+ private MessageDigest md;
+
+ public SHA256() {
+ try {
+ md = MessageDigest.getInstance("SHA-256");
+ }
+ catch(NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public final int getDigestLength() {
+ return md.getDigestLength();
+ }
+
+ public final void reset() {
+ md.reset();
+ }
+
+ public final void update(byte b[]) {
+ this.update(b, 0, b.length);
+ }
+
+ public final void update(byte b[], int off, int len) {
+ md.update(b, off, len);
+ }
+
+ public final void update(byte b) {
+ md.update(b);
+ }
+
+ public final void digest(byte[] out) throws DigestException {
+ this.digest(out, 0);
+ }
+
+ public final void digest(byte[] out, int off) throws DigestException {
+ md.digest(out, off, out.length);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/crypto/digest/SHA512.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/crypto/digest/SHA512.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.crypto.digest;
+
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @version $Id: SHA512.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $
+ */
+public final class SHA512 implements Digest {
+
+ private MessageDigest md;
+
+ public SHA512() {
+ try {
+ md = MessageDigest.getInstance("SHA-512");
+ }
+ catch(NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public final int getDigestLength() {
+ return md.getDigestLength();
+ }
+
+ public final void reset() {
+ md.reset();
+ }
+
+ public final void update(byte b[]) {
+ md.update(b);
+ }
+
+ public final void update(byte b[], int off, int len) {
+ md.update(b, off, len);
+ }
+
+ public final void update(byte b) {
+ md.update(b);
+ }
+
+ public final void digest(byte[] out) {
+ md.digest(out);
+ }
+
+ public final void digest(byte[] out, int off) throws DigestException {
+ md.digest(out, off, out.length);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/log/Logger.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/log/Logger.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.log;
+
+import java.util.logging.Level;
+
+/**
+ * Logger delegating to JRE logging. By default, this is disabled and only
+ * used during development.
+ *
+ * @author Christian Plattner
+ * @version $Id: Logger.java 49 2013-08-01 12:28:42Z cleondris@gmail.com $
+ */
+public class Logger
+{
+ private java.util.logging.Logger delegate;
+
+ public static volatile boolean enabled = false;
+
+ public static Logger getLogger(Class> x)
+ {
+ return new Logger(x);
+ }
+
+ public Logger(Class> x)
+ {
+ this.delegate = java.util.logging.Logger.getLogger(x.getName());
+ }
+
+ public boolean isDebugEnabled()
+ {
+ return enabled && delegate.isLoggable(Level.FINER);
+ }
+
+ public void debug(String message)
+ {
+ if (enabled)
+ delegate.fine(message);
+ }
+
+ public boolean isInfoEnabled()
+ {
+ return enabled && delegate.isLoggable(Level.FINE);
+ }
+
+ public void info(String message)
+ {
+ if (enabled)
+ delegate.info(message);
+ }
+
+ public boolean isWarningEnabled()
+ {
+ return enabled && delegate.isLoggable(Level.WARNING);
+ }
+
+ public void warning(String message)
+ {
+ if (enabled)
+ delegate.warning(message);
+ }
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketChannelFailure.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelFailure.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketChannelFailure.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketChannelFailure {
+ private final byte[] payload;
+
+ public PacketChannelFailure(int recipientChannelID) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_FAILURE);
+ tw.writeUINT32(recipientChannelID);
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketChannelOpenConfirmation.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelOpenConfirmation.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketChannelOpenConfirmation.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketChannelOpenConfirmation {
+
+ private final byte[] payload;
+
+ private final int recipientChannelID;
+ private final int senderChannelID;
+ private final int initialWindowSize;
+ private final int maxPacketSize;
+
+ public PacketChannelOpenConfirmation(int recipientChannelID, int senderChannelID, int initialWindowSize,
+ int maxPacketSize) {
+ this.recipientChannelID = recipientChannelID;
+ this.senderChannelID = senderChannelID;
+ this.initialWindowSize = initialWindowSize;
+ this.maxPacketSize = maxPacketSize;
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeUINT32(senderChannelID);
+ tw.writeUINT32(initialWindowSize);
+ tw.writeUINT32(maxPacketSize);
+ payload = tw.getBytes();
+ }
+
+ public PacketChannelOpenConfirmation(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION) {
+ throw new PacketTypeException(packet_type);
+ }
+
+ recipientChannelID = tr.readUINT32();
+ senderChannelID = tr.readUINT32();
+ initialWindowSize = tr.readUINT32();
+ maxPacketSize = tr.readUINT32();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public int getRecipientChannelID() {
+ return recipientChannelID;
+ }
+
+ public int getSenderChannelID() {
+ return senderChannelID;
+ }
+
+ public int getInitialWindowSize() {
+ return initialWindowSize;
+ }
+
+ public int getMaxPacketSize() {
+ return maxPacketSize;
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketChannelOpenFailure.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelOpenFailure.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketChannelOpenFailure.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketChannelOpenFailure {
+
+ private final byte[] payload;
+
+ public PacketChannelOpenFailure(int recipientChannelID, int reasonCode, String description,
+ String languageTag) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN_FAILURE);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeUINT32(reasonCode);
+ tw.writeString(description);
+ tw.writeString(languageTag);
+ payload = tw.getBytes();
+ }
+
+ public PacketChannelOpenFailure(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_CHANNEL_OPEN_FAILURE) {
+ throw new IOException(
+ "This is not a SSH_MSG_CHANNEL_OPEN_FAILURE! ("
+ + packet_type + ")"
+ );
+ }
+
+ int recipientChannelID = tr.readUINT32();
+ int reasonCode = tr.readUINT32();
+ String description = tr.readString();
+ String languageTag = tr.readString();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketChannelSuccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelSuccess.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketChannelSuccess.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketChannelSuccess {
+
+ private final byte[] payload;
+
+ public PacketChannelSuccess(int recipientChannelID) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_SUCCESS);
+ tw.writeUINT32(recipientChannelID);
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketChannelWindowAdjust.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketChannelWindowAdjust.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketChannelWindowAdjust.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketChannelWindowAdjust {
+
+ private final byte[] payload;
+
+ public PacketChannelWindowAdjust(int recipientChannelID, int windowChange) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeUINT32(windowChange);
+ payload = tw.getBytes();
+ }
+
+ public PacketChannelWindowAdjust(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST) {
+ throw new IOException(
+ "This is not a SSH_MSG_CHANNEL_WINDOW_ADJUST! ("
+ + packet_type + ")"
+ );
+ }
+
+ int recipientChannelID = tr.readUINT32();
+ int windowChange = tr.readUINT32();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketDisconnect.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketDisconnect.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketDisconnect.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketDisconnect {
+
+ public enum Reason {
+ SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT,
+ SSH_DISCONNECT_PROTOCOL_ERROR,
+ SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ SSH_DISCONNECT_RESERVED,
+ SSH_DISCONNECT_MAC_ERROR,
+ SSH_DISCONNECT_COMPRESSION_ERROR,
+ SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
+ SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE,
+ SSH_DISCONNECT_CONNECTION_LOST,
+ SSH_DISCONNECT_BY_APPLICATION,
+ SSH_DISCONNECT_TOO_MANY_CONNECTIONS,
+ SSH_DISCONNECT_AUTH_CANCELLED_BY_USER,
+ SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
+ SSH_DISCONNECT_ILLEGAL_USER_NAME
+ }
+
+ private final byte[] payload;
+
+ private final Reason reason;
+
+ private final String message;
+
+ public PacketDisconnect(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_DISCONNECT) {
+ throw new PacketTypeException(packet_type);
+ }
+ reason = PacketDisconnect.Reason.values()[tr.readUINT32()];
+ message = tr.readString();
+ String lang = tr.readString();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public PacketDisconnect(Reason reason, String desc) {
+ this.reason = reason;
+ this.message = desc;
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_DISCONNECT);
+ tw.writeUINT32(reason.ordinal());
+ tw.writeString(desc);
+ tw.writeString("");
+ payload = tw.getBytes();
+ }
+
+ public Reason getReason() {
+ return reason;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketGlobalCancelForwardRequest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketGlobalCancelForwardRequest.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketGlobalCancelForwardRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketGlobalCancelForwardRequest {
+
+ private final byte[] payload;
+
+ public PacketGlobalCancelForwardRequest(boolean wantReply, String bindAddress, int bindPort) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
+
+ tw.writeString("cancel-tcpip-forward");
+ tw.writeBoolean(wantReply);
+ tw.writeString(bindAddress);
+ tw.writeUINT32(bindPort);
+
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketGlobalForwardRequest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketGlobalForwardRequest.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketGlobalForwardRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketGlobalForwardRequest {
+
+ private final byte[] payload;
+
+ public PacketGlobalForwardRequest(boolean wantReply, String bindAddress, int bindPort) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_GLOBAL_REQUEST);
+
+ tw.writeString("tcpip-forward");
+ tw.writeBoolean(wantReply);
+ tw.writeString(bindAddress);
+ tw.writeUINT32(bindPort);
+
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketIgnore.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketIgnore.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketIgnore.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketIgnore {
+ private final byte[] payload;
+
+ public PacketIgnore(byte[] data) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_IGNORE);
+
+ if(data != null) {
+ tw.writeString(data, 0, data.length);
+ }
+ else {
+ tw.writeString("");
+ }
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketKexDHInit.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDHInit.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDHInit.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDHInit {
+
+ private final byte[] payload;
+
+ private final BigInteger e;
+
+ public PacketKexDHInit(BigInteger e) {
+ this.e = e;
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEXDH_INIT);
+ tw.writeMPInt(e);
+ payload = tw.getBytes();
+ }
+
+ public PacketKexDHInit(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_KEXDH_INIT) {
+ throw new PacketTypeException(packet_type);
+ }
+
+ e = tr.readMPINT();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public BigInteger getE() {
+ return e;
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketKexDHReply.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDHReply.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDHReply.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDHReply {
+
+ private final byte[] payload;
+
+ private final byte[] hostKey;
+ private final BigInteger f;
+ private final byte[] signature;
+
+ public PacketKexDHReply(byte[] hostKey, BigInteger f, byte[] signature) {
+ this.hostKey = hostKey;
+ this.f = f;
+ this.signature = signature;
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEXDH_REPLY);
+ tw.writeString(hostKey, 0, hostKey.length);
+ tw.writeMPInt(f);
+ tw.writeString(signature, 0, signature.length);
+ payload = tw.getBytes();
+ }
+
+ public PacketKexDHReply(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_KEXDH_REPLY) {
+ throw new PacketTypeException(packet_type);
+ }
+ hostKey = tr.readByteString();
+ f = tr.readMPINT();
+ signature = tr.readByteString();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+
+ public BigInteger getF() {
+ return f;
+ }
+
+ public byte[] getHostKey() {
+ return hostKey;
+ }
+
+ public byte[] getSignature() {
+ return signature;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketKexDhGexGroup.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDhGexGroup.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDhGexGroup.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDhGexGroup {
+
+ private final BigInteger p;
+ private final BigInteger g;
+
+ public PacketKexDhGexGroup(byte payload[]) throws IOException {
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_KEX_DH_GEX_GROUP) {
+ throw new PacketTypeException(packet_type);
+ }
+
+ p = tr.readMPINT();
+ g = tr.readMPINT();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public BigInteger getG() {
+ return g;
+ }
+
+ public BigInteger getP() {
+ return p;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketKexDhGexInit.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDhGexInit.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.math.BigInteger;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDhGexInit.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDhGexInit {
+
+ private final byte[] payload;
+
+ public PacketKexDhGexInit(BigInteger e) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_INIT);
+ tw.writeMPInt(e);
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketKexDhGexReply.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDhGexReply.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDhGexReply.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDhGexReply {
+
+ private final byte[] hostKey;
+ private final BigInteger f;
+ private final byte[] signature;
+
+ public PacketKexDhGexReply(byte payload[]) throws IOException {
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_KEX_DH_GEX_REPLY) {
+ throw new PacketTypeException(packet_type);
+ }
+
+ hostKey = tr.readByteString();
+ f = tr.readMPINT();
+ signature = tr.readByteString();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public BigInteger getF() {
+ return f;
+ }
+
+ public byte[] getHostKey() {
+ return hostKey;
+ }
+
+ public byte[] getSignature() {
+ return signature;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketKexDhGexRequest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDhGexRequest.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import ch.ethz.ssh2.DHGexParameters;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDhGexRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDhGexRequest {
+ private final byte[] payload;
+
+ public PacketKexDhGexRequest(DHGexParameters para) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST);
+ tw.writeUINT32(para.getMin_group_len());
+ tw.writeUINT32(para.getPref_group_len());
+ tw.writeUINT32(para.getMax_group_len());
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketKexDhGexRequestOld.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexDhGexRequestOld.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import ch.ethz.ssh2.DHGexParameters;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexDhGexRequestOld.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexDhGexRequestOld {
+
+ private final byte[] payload;
+
+ public PacketKexDhGexRequestOld(DHGexParameters para) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST_OLD);
+ tw.writeUINT32(para.getPref_group_len());
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketKexInit.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketKexInit.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.transport.KexParameters;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketKexInit.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketKexInit {
+ private final byte[] payload;
+
+ KexParameters kp = new KexParameters();
+
+ public PacketKexInit(CryptoWishList cwl, SecureRandom rnd) {
+ kp.cookie = new byte[16];
+ rnd.nextBytes(kp.cookie);
+
+ kp.kex_algorithms = cwl.kexAlgorithms;
+ kp.server_host_key_algorithms = cwl.serverHostKeyAlgorithms;
+ kp.encryption_algorithms_client_to_server = cwl.c2s_enc_algos;
+ kp.encryption_algorithms_server_to_client = cwl.s2c_enc_algos;
+ kp.mac_algorithms_client_to_server = cwl.c2s_mac_algos;
+ kp.mac_algorithms_server_to_client = cwl.s2c_mac_algos;
+ kp.compression_algorithms_client_to_server = cwl.c2s_comp_algos;
+ kp.compression_algorithms_server_to_client = cwl.s2c_comp_algos;
+ kp.languages_client_to_server = new String[]{""};
+ kp.languages_server_to_client = new String[]{""};
+ kp.first_kex_packet_follows = false;
+ kp.reserved_field1 = 0;
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_KEXINIT);
+ tw.writeBytes(kp.cookie, 0, 16);
+ tw.writeNameList(kp.kex_algorithms);
+ tw.writeNameList(kp.server_host_key_algorithms);
+ tw.writeNameList(kp.encryption_algorithms_client_to_server);
+ tw.writeNameList(kp.encryption_algorithms_server_to_client);
+ tw.writeNameList(kp.mac_algorithms_client_to_server);
+ tw.writeNameList(kp.mac_algorithms_server_to_client);
+ tw.writeNameList(kp.compression_algorithms_client_to_server);
+ tw.writeNameList(kp.compression_algorithms_server_to_client);
+ tw.writeNameList(kp.languages_client_to_server);
+ tw.writeNameList(kp.languages_server_to_client);
+ tw.writeBoolean(kp.first_kex_packet_follows);
+ tw.writeUINT32(kp.reserved_field1);
+ payload = tw.getBytes();
+ }
+
+ public PacketKexInit(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_KEXINIT) {
+ throw new PacketTypeException(packet_type);
+ }
+ kp.cookie = tr.readBytes(16);
+ kp.kex_algorithms = tr.readNameList();
+ kp.server_host_key_algorithms = tr.readNameList();
+ kp.encryption_algorithms_client_to_server = tr.readNameList();
+ kp.encryption_algorithms_server_to_client = tr.readNameList();
+ kp.mac_algorithms_client_to_server = tr.readNameList();
+ kp.mac_algorithms_server_to_client = tr.readNameList();
+ kp.compression_algorithms_client_to_server = tr.readNameList();
+ kp.compression_algorithms_server_to_client = tr.readNameList();
+ kp.languages_client_to_server = tr.readNameList();
+ kp.languages_server_to_client = tr.readNameList();
+ kp.first_kex_packet_follows = tr.readBoolean();
+ kp.reserved_field1 = tr.readUINT32();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+
+ public KexParameters getKexParameters() {
+ return kp;
+ }
+
+ public String[] getCompression_algorithms_client_to_server() {
+ return kp.compression_algorithms_client_to_server;
+ }
+
+ public String[] getCompression_algorithms_server_to_client() {
+ return kp.compression_algorithms_server_to_client;
+ }
+
+ public byte[] getCookie() {
+ return kp.cookie;
+ }
+
+ public String[] getEncryption_algorithms_client_to_server() {
+ return kp.encryption_algorithms_client_to_server;
+ }
+
+ public String[] getEncryption_algorithms_server_to_client() {
+ return kp.encryption_algorithms_server_to_client;
+ }
+
+ public boolean isFirst_kex_packet_follows() {
+ return kp.first_kex_packet_follows;
+ }
+
+ public String[] getKex_algorithms() {
+ return kp.kex_algorithms;
+ }
+
+ public String[] getLanguages_client_to_server() {
+ return kp.languages_client_to_server;
+ }
+
+ public String[] getLanguages_server_to_client() {
+ return kp.languages_server_to_client;
+ }
+
+ public String[] getMac_algorithms_client_to_server() {
+ return kp.mac_algorithms_client_to_server;
+ }
+
+ public String[] getMac_algorithms_server_to_client() {
+ return kp.mac_algorithms_server_to_client;
+ }
+
+ public int getReserved_field1() {
+ return kp.reserved_field1;
+ }
+
+ public String[] getServer_host_key_algorithms() {
+ return kp.server_host_key_algorithms;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketNewKeys.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketNewKeys.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketNewKeys.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketNewKeys {
+ private final byte[] payload;
+
+ public PacketNewKeys() {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_NEWKEYS);
+ payload = tw.getBytes();
+ }
+
+ public PacketNewKeys(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_NEWKEYS) {
+ throw new PacketTypeException(packet_type);
+ }
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketOpenDirectTCPIPChannel.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketOpenDirectTCPIPChannel.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketOpenDirectTCPIPChannel.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketOpenDirectTCPIPChannel {
+ private final byte[] payload;
+
+ public PacketOpenDirectTCPIPChannel(int channelID, int initialWindowSize, int maxPacketSize,
+ String host_to_connect, int port_to_connect, String originator_IP_address,
+ int originator_port) {
+ TypesWriter tw = new TypesWriter();
+
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN);
+ tw.writeString("direct-tcpip");
+ tw.writeUINT32(channelID);
+ tw.writeUINT32(initialWindowSize);
+ tw.writeUINT32(maxPacketSize);
+ tw.writeString(host_to_connect);
+ tw.writeUINT32(port_to_connect);
+ tw.writeString(originator_IP_address);
+ tw.writeUINT32(originator_port);
+
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketOpenSessionChannel.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketOpenSessionChannel.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketOpenSessionChannel.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketOpenSessionChannel {
+ private final byte[] payload;
+
+ public PacketOpenSessionChannel(int channelID, int initialWindowSize, int maxPacketSize) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_OPEN);
+ tw.writeString("session");
+ tw.writeUINT32(channelID);
+ tw.writeUINT32(initialWindowSize);
+ tw.writeUINT32(maxPacketSize);
+ payload = tw.getBytes();
+ }
+
+ public PacketOpenSessionChannel(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_CHANNEL_OPEN) {
+ throw new PacketTypeException(packet_type);
+ }
+
+ int channelID = tr.readUINT32();
+ int initialWindowSize = tr.readUINT32();
+ int maxPacketSize = tr.readUINT32();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketServiceAccept.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketServiceAccept.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketServiceAccept.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketServiceAccept {
+ private final byte[] payload;
+
+ public PacketServiceAccept(String serviceName) {
+
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_SERVICE_ACCEPT);
+ tw.writeString(serviceName);
+ payload = tw.getBytes();
+ }
+
+ public PacketServiceAccept(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_SERVICE_ACCEPT) {
+ throw new PacketTypeException(packet_type);
+ }
+ if(tr.remain() != 0) {
+ String serviceName = tr.readString();
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketServiceRequest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketServiceRequest.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketServiceRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketServiceRequest {
+ private final byte[] payload;
+
+ private final String serviceName;
+
+ public PacketServiceRequest(String serviceName) {
+ this.serviceName = serviceName;
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_SERVICE_REQUEST);
+ tw.writeString(serviceName);
+ payload = tw.getBytes();
+ }
+
+ public String getServiceName() {
+ return serviceName;
+ }
+
+ public PacketServiceRequest(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_SERVICE_REQUEST) {
+ throw new PacketTypeException(packet_type);
+ }
+
+ serviceName = tr.readString();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketSessionExecCommand.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketSessionExecCommand.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketSessionExecCommand.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketSessionExecCommand {
+ private final byte[] payload;
+
+ public PacketSessionExecCommand(int recipientChannelID, boolean wantReply, String command, String charsetName) throws UnsupportedEncodingException {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("exec");
+ tw.writeBoolean(wantReply);
+ tw.writeString(command, charsetName);
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() throws IOException {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketSessionPtyRequest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketSessionPtyRequest.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketSessionPtyRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketSessionPtyRequest {
+ private final byte[] payload;
+
+ public PacketSessionPtyRequest(int recipientChannelID, boolean wantReply, String term,
+ int character_width, int character_height, int pixel_width, int pixel_height,
+ byte[] terminal_modes) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("pty-req");
+ tw.writeBoolean(wantReply);
+ tw.writeString(term);
+ tw.writeUINT32(character_width);
+ tw.writeUINT32(character_height);
+ tw.writeUINT32(pixel_width);
+ tw.writeUINT32(pixel_height);
+ tw.writeString(terminal_modes, 0, terminal_modes.length);
+
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketSessionStartShell.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketSessionStartShell.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketSessionStartShell.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketSessionStartShell {
+ private final byte[] payload;
+
+ public PacketSessionStartShell(int recipientChannelID, boolean wantReply) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("shell");
+ tw.writeBoolean(wantReply);
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketSessionSubsystemRequest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketSessionSubsystemRequest.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketSessionSubsystemRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketSessionSubsystemRequest {
+ private final byte[] payload;
+
+ public PacketSessionSubsystemRequest(int recipientChannelID, boolean wantReply, String subsystem) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("subsystem");
+ tw.writeBoolean(wantReply);
+ tw.writeString(subsystem);
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketSessionX11Request.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketSessionX11Request.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketSessionX11Request.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketSessionX11Request {
+ private final byte[] payload;
+
+ public PacketSessionX11Request(int recipientChannelID, boolean wantReply, boolean singleConnection,
+ String x11AuthenticationProtocol, String x11AuthenticationCookie, int x11ScreenNumber) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("x11-req");
+ tw.writeBoolean(wantReply);
+
+ tw.writeBoolean(singleConnection);
+ tw.writeString(x11AuthenticationProtocol);
+ tw.writeString(x11AuthenticationCookie);
+ tw.writeUINT32(x11ScreenNumber);
+
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketUserauthBanner.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthBanner.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthBanner.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthBanner {
+ private final byte[] payload;
+
+ private final String message;
+
+ public PacketUserauthBanner(String message) {
+ this.message = message;
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_BANNER);
+ tw.writeString(message);
+ tw.writeString("");
+ payload = tw.getBytes();
+ }
+
+ public String getBanner() {
+ return message;
+ }
+
+ public PacketUserauthBanner(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_USERAUTH_BANNER) {
+ throw new PacketTypeException(packet_type);
+ }
+ message = tr.readString("UTF-8");
+ String language = tr.readString();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketUserauthFailure.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthFailure.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthFailure.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthFailure {
+
+ private final byte[] payload;
+
+ private Set authThatCanContinue;
+
+ private boolean partialSuccess;
+
+ public PacketUserauthFailure(Set authThatCanContinue, boolean partialSuccess) {
+ this.authThatCanContinue = authThatCanContinue;
+ this.partialSuccess = partialSuccess;
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_FAILURE);
+ tw.writeNameList(authThatCanContinue.toArray(new String[authThatCanContinue.size()]));
+ tw.writeBoolean(partialSuccess);
+ payload = tw.getBytes();
+ }
+
+ public PacketUserauthFailure(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_USERAUTH_FAILURE) {
+ throw new PacketTypeException(packet_type);
+ }
+ authThatCanContinue = new HashSet(Arrays.asList(tr.readNameList()));
+ partialSuccess = tr.readBoolean();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+
+ public Set getAuthThatCanContinue() {
+ return authThatCanContinue;
+ }
+
+ public boolean isPartialSuccess() {
+ return partialSuccess;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketUserauthInfoRequest.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthInfoRequest.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthInfoRequest.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthInfoRequest {
+
+ private final String name;
+ private final String instruction;
+ private final String languageTag;
+ private final int numPrompts;
+
+ private final String prompt[];
+ private final boolean echo[];
+
+ public PacketUserauthInfoRequest(byte payload[]) throws IOException {
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_USERAUTH_INFO_REQUEST) {
+ throw new PacketTypeException(packet_type);
+ }
+ name = tr.readString();
+ instruction = tr.readString();
+ languageTag = tr.readString();
+
+ numPrompts = tr.readUINT32();
+
+ prompt = new String[numPrompts];
+ echo = new boolean[numPrompts];
+
+ for(int i = 0; i < numPrompts; i++) {
+ prompt[i] = tr.readString();
+ echo[i] = tr.readBoolean();
+ }
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public boolean[] getEcho() {
+ return echo;
+ }
+
+ public String getInstruction() {
+ return instruction;
+ }
+
+ public String getLanguageTag() {
+ return languageTag;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getNumPrompts() {
+ return numPrompts;
+ }
+
+ public String[] getPrompt() {
+ return prompt;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketUserauthInfoResponse.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthInfoResponse.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthInfoResponse.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthInfoResponse {
+ private final byte[] payload;
+
+ public PacketUserauthInfoResponse(String[] responses) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_INFO_RESPONSE);
+ tw.writeUINT32(responses.length);
+ for(String response : responses) {
+ tw.writeString(response);
+ }
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketUserauthRequestInteractive.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthRequestInteractive.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthRequestInteractive.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthRequestInteractive {
+
+ private final byte[] payload;
+
+ public PacketUserauthRequestInteractive(String serviceName, String user, String[] submethods) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString(serviceName);
+ tw.writeString("keyboard-interactive");
+ tw.writeString(""); // draft-ietf-secsh-newmodes-04.txt says that
+ // the language tag should be empty.
+ tw.writeNameList(null == submethods ? new String[]{} : submethods);
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketUserauthRequestNone.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthRequestNone.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthRequestNone.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthRequestNone {
+
+ private final byte[] payload;
+
+ public PacketUserauthRequestNone(String serviceName, String user) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString(serviceName);
+ tw.writeString("none");
+ payload = tw.getBytes();
+ }
+
+ public PacketUserauthRequestNone(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_USERAUTH_REQUEST) {
+ throw new PacketTypeException(packet_type);
+ }
+ String userName = tr.readString();
+ String serviceName = tr.readString();
+
+ String method = tr.readString();
+
+ if(!method.equals("none")) {
+ throw new IOException(String.format("Unexpected method %s", method));
+ }
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketUserauthRequestPassword.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthRequestPassword.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthRequestPassword.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthRequestPassword {
+
+ private final byte[] payload;
+
+ public PacketUserauthRequestPassword(String serviceName, String user, String pass) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString(serviceName);
+ tw.writeString("password");
+ tw.writeBoolean(false);
+ tw.writeString(pass);
+ payload = tw.getBytes();
+ }
+
+ public PacketUserauthRequestPassword(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_USERAUTH_REQUEST) {
+ throw new PacketTypeException(packet_type);
+ }
+ String userName = tr.readString();
+ String serviceName = tr.readString();
+
+ String method = tr.readString();
+
+ if(!method.equals("password")) {
+ throw new IOException(String.format("Unexpected method %s", method));
+ }
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketUserauthRequestPublicKey.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthRequestPublicKey.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthRequestPublicKey.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthRequestPublicKey {
+
+ private final byte[] payload;
+
+ public PacketUserauthRequestPublicKey(String serviceName, String user,
+ String pkAlgorithmName, byte[] pk, byte[] sig) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_REQUEST);
+ tw.writeString(user);
+ tw.writeString(serviceName);
+ tw.writeString("publickey");
+ tw.writeBoolean(true);
+ tw.writeString(pkAlgorithmName);
+ tw.writeString(pk, 0, pk.length);
+ tw.writeString(sig, 0, sig.length);
+ payload = tw.getBytes();
+ }
+
+ public PacketUserauthRequestPublicKey(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_USERAUTH_REQUEST) {
+ throw new PacketTypeException(packet_type);
+ }
+ String userName = tr.readString();
+ String serviceName = tr.readString();
+
+ String method = tr.readString();
+
+ if(!method.equals("publickey")) {
+ throw new IOException(String.format("Unexpected method %s", method));
+ }
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketUserauthSuccess.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketUserauthSuccess.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.PacketTypeException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: PacketUserauthSuccess.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class PacketUserauthSuccess {
+
+ private final byte[] payload;
+
+ public PacketUserauthSuccess() {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_USERAUTH_SUCCESS);
+ payload = tw.getBytes();
+ }
+
+ public PacketUserauthSuccess(byte payload[]) throws IOException {
+ this.payload = payload;
+
+ TypesReader tr = new TypesReader(payload);
+
+ int packet_type = tr.readByte();
+
+ if(packet_type != Packets.SSH_MSG_USERAUTH_SUCCESS) {
+ throw new PacketTypeException(packet_type);
+ }
+ if(tr.remain() != 0) {
+ throw new PacketFormatException(String.format("Padding in %s", Packets.getMessageName(packet_type)));
+ }
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/PacketWindowChange.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/PacketWindowChange.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,31 @@
+package ch.ethz.ssh2.packets;
+
+/**
+ * Indicates that that size of the terminal (window) size has changed on the client side.
+ *
+ * See section 6.7 of RFC 4254.
+ *
+ * @author Kohsuke Kawaguchi
+ */
+public final class PacketWindowChange {
+ private final byte[] payload;
+
+ public PacketWindowChange(int recipientChannelID,
+ int character_width, int character_height, int pixel_width, int pixel_height) {
+ TypesWriter tw = new TypesWriter();
+ tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
+ tw.writeUINT32(recipientChannelID);
+ tw.writeString("window-change");
+ tw.writeBoolean(false);
+ tw.writeUINT32(character_width);
+ tw.writeUINT32(character_height);
+ tw.writeUINT32(pixel_width);
+ tw.writeUINT32(pixel_height);
+
+ payload = tw.getBytes();
+ }
+
+ public byte[] getPayload() {
+ return payload;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/Packets.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/Packets.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+/**
+ * Packets.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class Packets
+{
+ public static final int SSH_MSG_DISCONNECT = 1;
+ public static final int SSH_MSG_IGNORE = 2;
+ public static final int SSH_MSG_UNIMPLEMENTED = 3;
+ public static final int SSH_MSG_DEBUG = 4;
+ public static final int SSH_MSG_SERVICE_REQUEST = 5;
+ public static final int SSH_MSG_SERVICE_ACCEPT = 6;
+
+ public static final int SSH_MSG_KEXINIT = 20;
+ public static final int SSH_MSG_NEWKEYS = 21;
+
+ public static final int SSH_MSG_KEXDH_INIT = 30;
+ public static final int SSH_MSG_KEXDH_REPLY = 31;
+
+ public static final int SSH_MSG_KEX_DH_GEX_REQUEST_OLD = 30;
+ public static final int SSH_MSG_KEX_DH_GEX_REQUEST = 34;
+ public static final int SSH_MSG_KEX_DH_GEX_GROUP = 31;
+ public static final int SSH_MSG_KEX_DH_GEX_INIT = 32;
+ public static final int SSH_MSG_KEX_DH_GEX_REPLY = 33;
+
+ public static final int SSH_MSG_USERAUTH_REQUEST = 50;
+ public static final int SSH_MSG_USERAUTH_FAILURE = 51;
+ public static final int SSH_MSG_USERAUTH_SUCCESS = 52;
+ public static final int SSH_MSG_USERAUTH_BANNER = 53;
+ public static final int SSH_MSG_USERAUTH_INFO_REQUEST = 60;
+ public static final int SSH_MSG_USERAUTH_INFO_RESPONSE = 61;
+
+ public static final int SSH_MSG_GLOBAL_REQUEST = 80;
+ public static final int SSH_MSG_REQUEST_SUCCESS = 81;
+ public static final int SSH_MSG_REQUEST_FAILURE = 82;
+
+ public static final int SSH_MSG_CHANNEL_OPEN = 90;
+ public static final int SSH_MSG_CHANNEL_OPEN_CONFIRMATION = 91;
+ public static final int SSH_MSG_CHANNEL_OPEN_FAILURE = 92;
+ public static final int SSH_MSG_CHANNEL_WINDOW_ADJUST = 93;
+ public static final int SSH_MSG_CHANNEL_DATA = 94;
+ public static final int SSH_MSG_CHANNEL_EXTENDED_DATA = 95;
+ public static final int SSH_MSG_CHANNEL_EOF = 96;
+ public static final int SSH_MSG_CHANNEL_CLOSE = 97;
+ public static final int SSH_MSG_CHANNEL_REQUEST = 98;
+ public static final int SSH_MSG_CHANNEL_SUCCESS = 99;
+ public static final int SSH_MSG_CHANNEL_FAILURE = 100;
+
+ public static final int SSH_EXTENDED_DATA_STDERR = 1;
+
+ public static final int SSH_OPEN_ADMINISTRATIVELY_PROHIBITED = 1;
+ public static final int SSH_OPEN_CONNECT_FAILED = 2;
+ public static final int SSH_OPEN_UNKNOWN_CHANNEL_TYPE = 3;
+ public static final int SSH_OPEN_RESOURCE_SHORTAGE = 4;
+
+ private static final String[] reverseNames = new String[101];
+
+ static
+ {
+ reverseNames[1] = "SSH_MSG_DISCONNECT";
+ reverseNames[2] = "SSH_MSG_IGNORE";
+ reverseNames[3] = "SSH_MSG_UNIMPLEMENTED";
+ reverseNames[4] = "SSH_MSG_DEBUG";
+ reverseNames[5] = "SSH_MSG_SERVICE_REQUEST";
+ reverseNames[6] = "SSH_MSG_SERVICE_ACCEPT";
+
+ reverseNames[20] = "SSH_MSG_KEXINIT";
+ reverseNames[21] = "SSH_MSG_NEWKEYS";
+
+ reverseNames[30] = "SSH_MSG_KEXDH_INIT";
+ reverseNames[31] = "SSH_MSG_KEXDH_REPLY/SSH_MSG_KEX_DH_GEX_GROUP";
+ reverseNames[32] = "SSH_MSG_KEX_DH_GEX_INIT";
+ reverseNames[33] = "SSH_MSG_KEX_DH_GEX_REPLY";
+ reverseNames[34] = "SSH_MSG_KEX_DH_GEX_REQUEST";
+
+ reverseNames[50] = "SSH_MSG_USERAUTH_REQUEST";
+ reverseNames[51] = "SSH_MSG_USERAUTH_FAILURE";
+ reverseNames[52] = "SSH_MSG_USERAUTH_SUCCESS";
+ reverseNames[53] = "SSH_MSG_USERAUTH_BANNER";
+
+ reverseNames[60] = "SSH_MSG_USERAUTH_INFO_REQUEST";
+ reverseNames[61] = "SSH_MSG_USERAUTH_INFO_RESPONSE";
+
+ reverseNames[80] = "SSH_MSG_GLOBAL_REQUEST";
+ reverseNames[81] = "SSH_MSG_REQUEST_SUCCESS";
+ reverseNames[82] = "SSH_MSG_REQUEST_FAILURE";
+
+ reverseNames[90] = "SSH_MSG_CHANNEL_OPEN";
+ reverseNames[91] = "SSH_MSG_CHANNEL_OPEN_CONFIRMATION";
+ reverseNames[92] = "SSH_MSG_CHANNEL_OPEN_FAILURE";
+ reverseNames[93] = "SSH_MSG_CHANNEL_WINDOW_ADJUST";
+ reverseNames[94] = "SSH_MSG_CHANNEL_DATA";
+ reverseNames[95] = "SSH_MSG_CHANNEL_EXTENDED_DATA";
+ reverseNames[96] = "SSH_MSG_CHANNEL_EOF";
+ reverseNames[97] = "SSH_MSG_CHANNEL_CLOSE";
+ reverseNames[98] = "SSH_MSG_CHANNEL_REQUEST";
+ reverseNames[99] = "SSH_MSG_CHANNEL_SUCCESS";
+ reverseNames[100] = "SSH_MSG_CHANNEL_FAILURE";
+ }
+
+ public static final String getMessageName(int type)
+ {
+ String res = null;
+
+ if ((type >= 0) && (type < reverseNames.length))
+ {
+ res = reverseNames[type];
+ }
+
+ return (res == null) ? ("UNKNOWN MSG " + type) : res;
+ }
+
+ // public static final void debug(String tag, byte[] msg)
+ // {
+ // System.err.println(tag + " Type: " + msg[0] + ", LEN: " + msg.length);
+ //
+ // for (int i = 0; i < msg.length; i++)
+ // {
+ // if (((msg[i] >= 'a') && (msg[i] <= 'z')) || ((msg[i] >= 'A') && (msg[i] <= 'Z'))
+ // || ((msg[i] >= '0') && (msg[i] <= '9')) || (msg[i] == ' '))
+ // System.err.print((char) msg[i]);
+ // else
+ // System.err.print(".");
+ // }
+ // System.err.println();
+ // System.err.flush();
+ // }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/TypesReader.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/TypesReader.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.IOException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: TypesReader.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class TypesReader {
+ byte[] arr;
+ int pos = 0;
+ int max = 0;
+
+ public TypesReader(byte[] arr) {
+ this.arr = arr;
+ pos = 0;
+ max = arr.length;
+ }
+
+ public TypesReader(byte[] arr, int off) {
+ this.arr = arr;
+ this.pos = off;
+ this.max = arr.length;
+
+ if((pos < 0) || (pos > arr.length)) {
+ throw new IllegalArgumentException("Illegal offset.");
+ }
+ }
+
+ public TypesReader(byte[] arr, int off, int len) {
+ this.arr = arr;
+ this.pos = off;
+ this.max = off + len;
+
+ if((pos < 0) || (pos > arr.length)) {
+ throw new IllegalArgumentException("Illegal offset.");
+ }
+
+ if((max < 0) || (max > arr.length)) {
+ throw new IllegalArgumentException("Illegal length.");
+ }
+ }
+
+ public int readByte() throws IOException {
+ if(pos >= max) {
+ throw new PacketFormatException("Packet too short.");
+ }
+
+ return (arr[pos++] & 0xff);
+ }
+
+ public byte[] readBytes(int len) throws IOException {
+ if((pos + len) > max) {
+ throw new PacketFormatException("Packet too short.");
+ }
+
+ byte[] res = new byte[len];
+
+ System.arraycopy(arr, pos, res, 0, len);
+ pos += len;
+
+ return res;
+ }
+
+ public void readBytes(byte[] dst, int off, int len) throws IOException {
+ if((pos + len) > max) {
+ throw new PacketFormatException("Packet too short.");
+ }
+
+ System.arraycopy(arr, pos, dst, off, len);
+ pos += len;
+ }
+
+ public boolean readBoolean() throws IOException {
+ if(pos >= max) {
+ throw new PacketFormatException("Packet too short.");
+ }
+
+ return (arr[pos++] != 0);
+ }
+
+ public int readUINT32() throws IOException {
+ if((pos + 4) > max) {
+ throw new PacketFormatException("Packet too short.");
+ }
+
+ return ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
+ | (arr[pos++] & 0xff);
+ }
+
+ public long readUINT64() throws IOException {
+ if((pos + 8) > max) {
+ throw new PacketFormatException("Packet too short.");
+ }
+
+ long high = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
+ | (arr[pos++] & 0xff); /* sign extension may take place - will be shifted away =) */
+
+ long low = ((arr[pos++] & 0xff) << 24) | ((arr[pos++] & 0xff) << 16) | ((arr[pos++] & 0xff) << 8)
+ | (arr[pos++] & 0xff); /* sign extension may take place - handle below */
+
+ return (high << 32) | (low & 0xffffffffl); /* see Java language spec (15.22.1, 5.6.2) */
+ }
+
+ public BigInteger readMPINT() throws IOException {
+ BigInteger b;
+
+ byte raw[] = readByteString();
+
+ if(raw.length == 0) {
+ b = BigInteger.ZERO;
+ }
+ else {
+ b = new BigInteger(raw);
+ }
+
+ return b;
+ }
+
+ public byte[] readByteString() throws IOException {
+ int len = readUINT32();
+
+ if((len + pos) > max) {
+ throw new PacketFormatException("Malformed SSH byte string.");
+ }
+
+ byte[] res = new byte[len];
+ System.arraycopy(arr, pos, res, 0, len);
+ pos += len;
+ return res;
+ }
+
+ public String readString(String charsetName) throws IOException {
+ int len = readUINT32();
+
+ if((len + pos) > max) {
+ throw new PacketFormatException("Malformed SSH string.");
+ }
+
+ String res = (charsetName == null) ? new String(arr, pos, len) : new String(arr, pos, len, charsetName);
+ pos += len;
+
+ return res;
+ }
+
+ public String readString() throws IOException {
+ int len = readUINT32();
+
+ if((len + pos) > max) {
+ throw new PacketFormatException("Malformed SSH string.");
+ }
+
+ String res = StringEncoder.GetString(arr, pos, len);
+ pos += len;
+
+ return res;
+ }
+
+ public String[] readNameList() throws IOException {
+ return readString().split(",");
+ }
+
+ public int remain() {
+ return max - pos;
+ }
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/packets/TypesWriter.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/packets/TypesWriter.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.packets;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigInteger;
+
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: TypesWriter.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public final class TypesWriter {
+ byte arr[];
+ int pos;
+
+ public TypesWriter() {
+ arr = new byte[256];
+ pos = 0;
+ }
+
+ private void resize(int len) {
+ byte new_arr[] = new byte[len];
+ System.arraycopy(arr, 0, new_arr, 0, arr.length);
+ arr = new_arr;
+ }
+
+ public int length() {
+ return pos;
+ }
+
+ public byte[] getBytes() {
+ byte[] dst = new byte[pos];
+ System.arraycopy(arr, 0, dst, 0, pos);
+ return dst;
+ }
+
+ public void getBytes(byte dst[]) {
+ System.arraycopy(arr, 0, dst, 0, pos);
+ }
+
+ public void writeUINT32(int val, int off) {
+ if((off + 4) > arr.length) {
+ resize(off + 32);
+ }
+
+ arr[off++] = (byte) (val >> 24);
+ arr[off++] = (byte) (val >> 16);
+ arr[off++] = (byte) (val >> 8);
+ arr[off++] = (byte) val;
+ }
+
+ public void writeUINT32(int val) {
+ writeUINT32(val, pos);
+ pos += 4;
+ }
+
+ public void writeUINT64(long val) {
+ if((pos + 8) > arr.length) {
+ resize(arr.length + 32);
+ }
+
+ arr[pos++] = (byte) (val >> 56);
+ arr[pos++] = (byte) (val >> 48);
+ arr[pos++] = (byte) (val >> 40);
+ arr[pos++] = (byte) (val >> 32);
+ arr[pos++] = (byte) (val >> 24);
+ arr[pos++] = (byte) (val >> 16);
+ arr[pos++] = (byte) (val >> 8);
+ arr[pos++] = (byte) val;
+ }
+
+ public void writeBoolean(boolean v) {
+ if((pos + 1) > arr.length) {
+ resize(arr.length + 32);
+ }
+
+ arr[pos++] = v ? (byte) 1 : (byte) 0;
+ }
+
+ public void writeByte(int v, int off) {
+ if((off + 1) > arr.length) {
+ resize(off + 32);
+ }
+
+ arr[off] = (byte) v;
+ }
+
+ public void writeByte(int v) {
+ writeByte(v, pos);
+ pos++;
+ }
+
+ public void writeMPInt(BigInteger b) {
+ byte raw[] = b.toByteArray();
+
+ if((raw.length == 1) && (raw[0] == 0)) {
+ writeUINT32(0); /* String with zero bytes of data */
+ }
+ else {
+ writeString(raw, 0, raw.length);
+ }
+ }
+
+ public void writeBytes(byte[] buff) {
+ writeBytes(buff, 0, buff.length);
+ }
+
+ public void writeBytes(byte[] buff, int off, int len) {
+ if((pos + len) > arr.length) {
+ resize(arr.length + len + 32);
+ }
+
+ System.arraycopy(buff, off, arr, pos, len);
+ pos += len;
+ }
+
+ public void writeString(byte[] buff, int off, int len) {
+ writeUINT32(len);
+ writeBytes(buff, off, len);
+ }
+
+ public void writeString(String v) {
+ byte[] b = StringEncoder.GetBytes(v);
+
+ writeUINT32(b.length);
+ writeBytes(b, 0, b.length);
+ }
+
+ public void writeString(String v, String charsetName) throws UnsupportedEncodingException {
+ byte[] b = (charsetName == null) ? StringEncoder.GetBytes(v) : v.getBytes(charsetName);
+
+ writeUINT32(b.length);
+ writeBytes(b, 0, b.length);
+ }
+
+ public void writeNameList(String v[]) {
+ StringBuilder sb = new StringBuilder();
+ for(int i = 0; i < v.length; i++) {
+ if(i > 0) {
+ sb.append(',');
+ }
+ sb.append(v[i]);
+ }
+ writeString(sb.toString());
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/server/ServerConnectionState.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/server/ServerConnectionState.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.server;
+
+import java.net.Socket;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.ServerAuthenticationCallback;
+import ch.ethz.ssh2.ServerConnection;
+import ch.ethz.ssh2.ServerConnectionCallback;
+import ch.ethz.ssh2.auth.ServerAuthenticationManager;
+import ch.ethz.ssh2.channel.ChannelManager;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.signature.DSAPrivateKey;
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+import ch.ethz.ssh2.transport.ClientServerHello;
+import ch.ethz.ssh2.transport.ServerTransportManager;
+
+public class ServerConnectionState
+{
+ public ServerConnection conn;
+
+ public SecureRandom generator = new SecureRandom();
+
+ public String softwareversion;
+
+ //public String auth_banner = null;
+ public ServerConnectionCallback cb_conn;
+ public ServerAuthenticationCallback cb_auth;
+
+ /* Settings for the next key exchange */
+ public CryptoWishList next_cryptoWishList = CryptoWishList.forServer();
+ public DSAPrivateKey next_dsa_key;
+ public RSAPrivateKey next_rsa_key;
+
+ public Socket s;
+
+ public ClientServerHello csh;
+ public ServerTransportManager tm;
+ public ServerAuthenticationManager am;
+ public ChannelManager cm;
+
+ public boolean flag_auth_serviceRequested = false;
+ public boolean flag_auth_completed = false;
+
+ public ServerConnectionState(ServerConnection conn)
+ {
+ this.conn = conn;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/AceFlags.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AceFlags.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,17 @@
+package ch.ethz.ssh2.sftp;
+
+/**
+ * @version $Id: AceFlags.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public final class AceFlags {
+ private AceFlags() {
+ }
+
+ public static final int ACE4_FILE_INHERIT_ACE = 0x00000001;
+ public static final int ACE4_DIRECTORY_INHERIT_ACE = 0x00000002;
+ public static final int ACE4_NO_PROPAGATE_INHERIT_ACE = 0x00000004;
+ public static final int ACE4_INHERIT_ONLY_ACE = 0x00000008;
+ public static final int ACE4_SUCCESSFUL_ACCESS_ACE_FLAG = 0x00000010;
+ public static final int ACE4_FAILED_ACCESS_ACE_FLAG = 0x00000020;
+ public static final int ACE4_IDENTIFIER_GROUP = 0x00000040;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/AceMask.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AceMask.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,28 @@
+package ch.ethz.ssh2.sftp;
+
+/**
+ * @version $Id: AceMask.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public final class AceMask {
+ private AceMask() {
+ }
+
+ public static final int ACE4_READ_DATA = 0x00000001;
+ public static final int ACE4_LIST_DIRECTORY = 0x00000001;
+ public static final int ACE4_WRITE_DATA = 0x00000002;
+ public static final int ACE4_ADD_FILE = 0x00000002;
+ public static final int ACE4_APPEND_DATA = 0x00000004;
+ public static final int ACE4_ADD_SUBDIRECTORY = 0x00000004;
+ public static final int ACE4_READ_NAMED_ATTRS = 0x00000008;
+ public static final int ACE4_WRITE_NAMED_ATTRS = 0x00000010;
+ public static final int ACE4_EXECUTE = 0x00000020;
+ public static final int ACE4_DELETE_CHILD = 0x00000040;
+ public static final int ACE4_READ_ATTRIBUTES = 0x00000080;
+ public static final int ACE4_WRITE_ATTRIBUTES = 0x00000100;
+ public static final int ACE4_DELETE = 0x00010000;
+ public static final int ACE4_READ_ACL = 0x00020000;
+ public static final int ACE4_WRITE_ACL = 0x00040000;
+ public static final int ACE4_WRITE_OWNER = 0x00080000;
+ public static final int ACE4_SYNCHRONIZE = 0x00100000;
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/AceType.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AceType.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,16 @@
+package ch.ethz.ssh2.sftp;
+
+/**
+ * @version $Id: AceType.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public final class AceType {
+
+ private AceType() {
+ }
+
+ private static final int ACE4_ACCESS_ALLOWED_ACE_TYPE = 0x00000000;
+ private static final int ACE4_ACCESS_DENIED_ACE_TYPE = 0x00000001;
+ private static final int ACE4_SYSTEM_AUDIT_ACE_TYPE = 0x00000002;
+ private static final int ACE4_SYSTEM_ALARM_ACE_TYPE = 0x00000003;
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/AclFlags.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AclFlags.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,45 @@
+package ch.ethz.ssh2.sftp;
+
+/**
+ * @version $Id: AclFlags.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public final class AclFlags {
+
+ /**
+ * If INCLUDED is set during a setstat operation, then the client
+ * intends to modify the ALLOWED/DENIED entries of the ACL.
+ * Otherwise, the client intends for these entries to be
+ * preserved.
+ */
+ public static final int SFX_ACL_CONTROL_INCLUDED = 0x00000001;
+ /**
+ * If the PRESENT bit is not set, then the client wishes to remove
+ * control entries. If the server doesn't support separate
+ * control and audit information, the client MUST not clear this
+ * bit without also clearing the AUDIT_ALARM_PRESENT bit.
+ */
+ public static final int SFX_ACL_CONTROL_PRESENT = 0x00000002;
+ /**
+ * If INHERITED is set, then ALLOW/DENY ACEs MAY be inherited from
+ * the parent directory. If it is off, then they MUST not be
+ * INHERITED. If the server does not support controlling
+ * inheritance, then the client MUST clear this bit; in this case
+ * the inheritance properties of the server are undefined.
+ */
+ public static final int SFX_ACL_CONTROL_INHERITED = 0x00000004;
+ /**
+ * If INCLUDE is set during a setstat operation, then the client
+ * intends to modify the AUDIT/ALARM entries of the ACL.
+ * Otherwise, the client intends for these entries to be
+ * preserved.
+ */
+ public static final int SFX_ACL_AUDIT_ALARM_INCLUDED = 0x00000010;
+ /**
+ * If INHERITED is set, then AUDIT/ALARM ACEs MAY be inherited
+ * from the parent directory. If it is off, then they MUST not be
+ * INHERITED. If the server does not support controlling
+ * inheritance, then the client MUST clear this bit; in this case
+ * the inheritance properties of the server are undefined.
+ */
+ public static final int SFX_ACL_AUDIT_ALARM_INHERITED = 0x00000020;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/AttrTextHints.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AttrTextHints.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ *
+ * Values for the 'text-hint' field in the SFTP ATTRS data type.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ *
+ */
+public class AttrTextHints
+{
+ /**
+ * The server knows the file is a text file, and should be opened
+ * using the SSH_FXF_ACCESS_TEXT_MODE flag.
+ */
+ public static final int SSH_FILEXFER_ATTR_KNOWN_TEXT = 0x00;
+
+ /**
+ * The server has applied a heuristic or other mechanism and
+ * believes that the file should be opened with the
+ * SSH_FXF_ACCESS_TEXT_MODE flag.
+ */
+ public static final int SSH_FILEXFER_ATTR_GUESSED_TEXT = 0x01;
+
+ /**
+ * The server knows the file has binary content.
+ */
+ public static final int SSH_FILEXFER_ATTR_KNOWN_BINARY = 0x02;
+
+ /**
+ * The server has applied a heuristic or other mechanism and
+ * believes has binary content, and should not be opened with the
+ * SSH_FXF_ACCESS_TEXT_MODE flag.
+ */
+ public static final int SSH_FILEXFER_ATTR_GUESSED_BINARY = 0x03;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/AttribBits.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AttribBits.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * SFTP Attribute Bits for the "attrib-bits" and "attrib-bits-valid" fields
+ * of the SFTP ATTR data type.
+ *
+ * Yes, these are the "attrib-bits", even though they have "_FLAGS_" in
+ * their name. Don't ask - I did not invent it.
+ *
+ * "These fields, taken together, reflect various attributes of the file
+ * or directory, on the server. Bits not set in 'attrib-bits-valid' MUST be
+ * ignored in the 'attrib-bits' field. This allows both the server and the
+ * client to communicate only the bits it knows about without inadvertently
+ * twiddling bits they don't understand."
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class AttribBits {
+
+ private AttribBits() {
+ }
+
+ /**
+ * Advisory, read-only bit. This bit is not part of the access
+ * control information on the file, but is rather an advisory field
+ * indicating that the file should not be written.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_READONLY = 0x00000001;
+
+ /**
+ * The file is part of the operating system.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_SYSTEM = 0x00000002;
+
+ /**
+ * File SHOULD NOT be shown to user unless specifically requested.
+ * For example, most UNIX systems SHOULD set this bit if the filename
+ * begins with a 'period'. This bit may be read-only (see section 5.4 of
+ * the SFTP standard draft). Most UNIX systems will not allow this to be
+ * changed.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_HIDDEN = 0x00000004;
+
+ /**
+ * This attribute applies only to directories. This attribute is
+ * always read-only, and cannot be modified. This attribute means
+ * that files and directory names in this directory should be compared
+ * without regard to case.
+ *
+ * It is recommended that where possible, the server's filesystem be
+ * allowed to do comparisons. For example, if a client wished to prompt
+ * a user before overwriting a file, it should not compare the new name
+ * with the previously retrieved list of names in the directory. Rather,
+ * it should first try to create the new file by specifying
+ * SSH_FXF_CREATE_NEW flag. Then, if this fails and returns
+ * SSH_FX_FILE_ALREADY_EXISTS, it should prompt the user and then retry
+ * the create specifying SSH_FXF_CREATE_TRUNCATE.
+ *
+ * Unless otherwise specified, filenames are assumed to be case sensitive.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE = 0x00000008;
+
+ /**
+ * The file should be included in backup / archive operations.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_ARCHIVE = 0x00000010;
+
+ /**
+ * The file is stored on disk using file-system level transparent
+ * encryption. This flag does not affect the file data on the wire
+ * (for either READ or WRITE requests.)
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED = 0x00000020;
+
+ /**
+ * The file is stored on disk using file-system level transparent
+ * compression. This flag does not affect the file data on the wire.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_COMPRESSED = 0x00000040;
+
+ /**
+ * The file is a sparse file; this means that file blocks that have
+ * not been explicitly written are not stored on disk. For example, if
+ * a client writes a buffer at 10 M from the beginning of the file,
+ * the blocks between the previous EOF marker and the 10 M offset would
+ * not consume physical disk space.
+ *
+ * Some servers may store all files as sparse files, in which case
+ * this bit will be unconditionally set. Other servers may not have
+ * a mechanism for determining if the file is sparse, and so the file
+ * MAY be stored sparse even if this flag is not set.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_SPARSE = 0x00000080;
+
+ /**
+ * Opening the file without either the SSH_FXF_ACCESS_APPEND_DATA or
+ * the SSH_FXF_ACCESS_APPEND_DATA_ATOMIC flag (see section 8.1.1.3
+ * of the SFTP standard draft) MUST result in an
+ * SSH_FX_INVALID_PARAMETER error.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_APPEND_ONLY = 0x00000100;
+
+ /**
+ * The file cannot be deleted or renamed, no hard link can be created
+ * to this file, and no data can be written to the file.
+ *
+ * This bit implies a stronger level of protection than
+ * SSH_FILEXFER_ATTR_FLAGS_READONLY, the file permission mask or ACLs.
+ * Typically even the superuser cannot write to immutable files, and
+ * only the superuser can set or remove the bit.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_IMMUTABLE = 0x00000200;
+
+ /**
+ * When the file is modified, the changes are written synchronously
+ * to the disk.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_SYNC = 0x00000400;
+
+ /**
+ * The server MAY include this bit in a directory listing or realpath
+ * response. It indicates there was a failure in the translation to UTF-8.
+ * If this flag is included, the server SHOULD also include the
+ * UNTRANSLATED_NAME attribute.
+ */
+ public static final int SSH_FILEXFER_ATTR_FLAGS_TRANSLATION_ERR = 0x00000800;
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/AttribFlags.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AttribFlags.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * Attribute Flags. The 'valid-attribute-flags' field in
+ * the SFTP ATTRS data type specifies which of the fields are actually present.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class AttribFlags {
+
+ private AttribFlags() {
+ }
+
+ /**
+ * Indicates that the 'allocation-size' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_SIZE = 0x00000001;
+
+ /**
+ * Protocol version 6:
+ * 0x00000002 was used in a previous version of this protocol.
+ * It is now a reserved value and MUST NOT appear in the mask.
+ * Some future version of this protocol may reuse this value.
+ */
+ public static final int SSH_FILEXFER_ATTR_V3_UIDGID = 0x00000002;
+
+ /**
+ * Indicates that the 'permissions' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_PERMISSIONS = 0x00000004;
+
+ /**
+ * Indicates that the 'atime' and 'mtime' field are present
+ * (protocol v3).
+ */
+ public static final int SSH_FILEXFER_ATTR_V3_ACMODTIME = 0x00000008;
+
+ /**
+ * Indicates that the 'atime' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_ACCESSTIME = 0x00000008;
+
+ /**
+ * Indicates that the 'createtime' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_CREATETIME = 0x00000010;
+
+ /**
+ * Indicates that the 'mtime' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_MODIFYTIME = 0x00000020;
+
+ /**
+ * Indicates that the 'acl' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_ACL = 0x00000040;
+
+ /**
+ * Indicates that the 'owner' and 'group' fields are present.
+ */
+ public static final int SSH_FILEXFER_ATTR_OWNERGROUP = 0x00000080;
+
+ /**
+ * Indicates that additionally to the 'atime', 'createtime',
+ * 'mtime' and 'ctime' fields (if present), there is also
+ * 'atime-nseconds', 'createtime-nseconds', 'mtime-nseconds'
+ * and 'ctime-nseconds'.
+ */
+ public static final int SSH_FILEXFER_ATTR_SUBSECOND_TIMES = 0x00000100;
+
+ /**
+ * Indicates that the 'attrib-bits' and 'attrib-bits-valid'
+ * fields are present.
+ */
+ public static final int SSH_FILEXFER_ATTR_BITS = 0x00000200;
+
+ /**
+ * Indicates that the 'allocation-size' field is present. Field specifies the number of bytes that the
+ * file consumes on disk.
+ */
+ public static final int SSH_FILEXFER_ATTR_ALLOCATION_SIZE = 0x00000400;
+
+ /**
+ * Indicates that the 'text-hint' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_TEXT_HINT = 0x00000800;
+
+ /**
+ * Indicates that the 'mime-type' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_MIME_TYPE = 0x00001000;
+
+ /**
+ * Indicates that the 'link-count' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_LINK_COUNT = 0x00002000;
+
+ /**
+ * Indicates that the 'untranslated-name' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_UNTRANSLATED_NAME = 0x00004000;
+
+ /**
+ * Indicates that the 'ctime' field is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_CTIME = 0x00008000;
+
+ /**
+ * Indicates that the 'extended-count' field (and probablby some
+ * 'extensions') is present.
+ */
+ public static final int SSH_FILEXFER_ATTR_EXTENDED = 0x80000000;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/AttribPermissions.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AttribPermissions.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * Permissions for the 'permissions' field in the SFTP ATTRS data type.
+ *
+ * "The 'permissions' field contains a bit mask specifying file permissions.
+ * These permissions correspond to the st_mode field of the stat structure
+ * defined by POSIX [IEEE.1003-1.1996]."
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class AttribPermissions {
+ private AttribPermissions() {
+ }
+
+ /* Octal values! */
+
+ public static final int S_IRUSR = 0400;
+ public static final int S_IWUSR = 0200;
+ public static final int S_IXUSR = 0100;
+ public static final int S_IRGRP = 0040;
+ public static final int S_IWGRP = 0020;
+ public static final int S_IXGRP = 0010;
+ public static final int S_IROTH = 0004;
+ public static final int S_IWOTH = 0002;
+ public static final int S_IXOTH = 0001;
+ public static final int S_ISUID = 04000;
+ public static final int S_ISGID = 02000;
+ public static final int S_ISVTX = 01000;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/AttribTypes.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/AttribTypes.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * Types for the 'type' field in the SFTP ATTRS data type.
+ *
+ * "On a POSIX system, these values would be derived from the mode field
+ * of the stat structure. SPECIAL should be used for files that are of
+ * a known type which cannot be expressed in the protocol. UNKNOWN
+ * should be used if the type is not known."
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class AttribTypes {
+ private AttribTypes() {
+ }
+
+ public static final int SSH_FILEXFER_TYPE_REGULAR = 1;
+ public static final int SSH_FILEXFER_TYPE_DIRECTORY = 2;
+ public static final int SSH_FILEXFER_TYPE_SYMLINK = 3;
+ public static final int SSH_FILEXFER_TYPE_SPECIAL = 4;
+ /**
+ * Should be used if the type is not known.
+ */
+ public static final int SSH_FILEXFER_TYPE_UNKNOWN = 5;
+ public static final int SSH_FILEXFER_TYPE_SOCKET = 6;
+ public static final int SSH_FILEXFER_TYPE_CHAR_DEVICE = 7;
+ public static final int SSH_FILEXFER_TYPE_BLOCK_DEVICE = 8;
+ public static final int SSH_FILEXFER_TYPE_FIFO = 9;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/ErrorCodes.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/ErrorCodes.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * SFTP Error Codes
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class ErrorCodes {
+ private ErrorCodes() {
+ }
+
+ 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;
+ /**
+ * The server MUST respond with SSH_FXP_STATUS(SSH_FX_OP_UNSUPPORTED) if
+ * it receives a packet it does not recognize.
+ */
+ public static final int SSH_FX_OP_UNSUPPORTED = 8;
+ public static final int SSH_FX_INVALID_HANDLE = 9;
+ public static final int SSH_FX_NO_SUCH_PATH = 10;
+ public static final int SSH_FX_FILE_ALREADY_EXISTS = 11;
+ public static final int SSH_FX_WRITE_PROTECT = 12;
+ public static final int SSH_FX_NO_MEDIA = 13;
+ public static final int SSH_FX_NO_SPACE_ON_FILESYSTEM = 14;
+ public static final int SSH_FX_QUOTA_EXCEEDED = 15;
+ public static final int SSH_FX_UNKNOWN_PRINCIPAL = 16;
+ public static final int SSH_FX_LOCK_CONFLICT = 17;
+ public static final int SSH_FX_DIR_NOT_EMPTY = 18;
+ public static final int SSH_FX_NOT_A_DIRECTORY = 19;
+ public static final int SSH_FX_INVALID_FILENAME = 20;
+ public static final int SSH_FX_LINK_LOOP = 21;
+ public static final int SSH_FX_CANNOT_DELETE = 22;
+ public static final int SSH_FX_INVALID_PARAMETER = 23;
+ public static final int SSH_FX_FILE_IS_A_DIRECTORY = 24;
+ public static final int SSH_FX_BYTE_RANGE_LOCK_CONFLICT = 25;
+ public static final int SSH_FX_BYTE_RANGE_LOCK_REFUSED = 26;
+ public static final int SSH_FX_DELETE_PENDING = 27;
+ public static final int SSH_FX_FILE_CORRUPT = 28;
+ public static final int SSH_FX_OWNER_INVALID = 29;
+ public static final int SSH_FX_GROUP_INVALID = 30;
+ public static final int SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK = 31;
+
+ private static final String[][] messages = {
+
+ {"SSH_FX_OK", "Indicates successful completion of the operation."},
+ {"SSH_FX_EOF",
+ "An attempt to read past the end-of-file was made; or, there are no more directory entries to return."},
+ {"SSH_FX_NO_SUCH_FILE", "A reference was made to a file which does not exist."},
+ {"SSH_FX_PERMISSION_DENIED", "The user does not have sufficient permissions to perform the operation."},
+ {"SSH_FX_FAILURE", "An error occurred, but no specific error code exists to describe the failure."},
+ {"SSH_FX_BAD_MESSAGE", "A badly formatted packet or other SFTP protocol incompatibility was detected."},
+ {"SSH_FX_NO_CONNECTION", "There is no connection to the server."},
+ {"SSH_FX_CONNECTION_LOST", "The connection to the server was lost."},
+ {"SSH_FX_OP_UNSUPPORTED",
+ "An attempted operation could not be completed by the server because the server does not support the operation."},
+ {"SSH_FX_INVALID_HANDLE", "The handle value was invalid."},
+ {"SSH_FX_NO_SUCH_PATH", "The file path does not exist or is invalid."},
+ {"SSH_FX_FILE_ALREADY_EXISTS", "The file already exists."},
+ {"SSH_FX_WRITE_PROTECT", "The file is on read-only media, or the media is write protected."},
+ {"SSH_FX_NO_MEDIA",
+ "The requested operation cannot be completed because there is no media available in the drive."},
+ {"SSH_FX_NO_SPACE_ON_FILESYSTEM",
+ "The requested operation cannot be completed because there is insufficient free space on the filesystem."},
+ {"SSH_FX_QUOTA_EXCEEDED",
+ "The operation cannot be completed because it would exceed the user's storage quota."},
+ {
+ "SSH_FX_UNKNOWN_PRINCIPAL",
+ "A principal referenced by the request (either the 'owner', 'group', or 'who' field of an ACL), was unknown. The error specific data contains the problematic names."},
+ {"SSH_FX_LOCK_CONFLICT", "The file could not be opened because it is locked by another process."},
+ {"SSH_FX_DIR_NOT_EMPTY", "The directory is not empty."},
+ {"SSH_FX_NOT_A_DIRECTORY", "The specified file is not a directory."},
+ {"SSH_FX_INVALID_FILENAME", "The filename is not valid."},
+ {"SSH_FX_LINK_LOOP",
+ "Too many symbolic links encountered or, an SSH_FXF_NOFOLLOW open encountered a symbolic link as the final component."},
+ {"SSH_FX_CANNOT_DELETE",
+ "The file cannot be deleted. One possible reason is that the advisory READONLY attribute-bit is set."},
+ {"SSH_FX_INVALID_PARAMETER",
+ "One of the parameters was out of range, or the parameters specified cannot be used together."},
+ {"SSH_FX_FILE_IS_A_DIRECTORY",
+ "The specified file was a directory in a context where a directory cannot be used."},
+ {"SSH_FX_BYTE_RANGE_LOCK_CONFLICT",
+ " A read or write operation failed because another process's mandatory byte-range lock overlaps with the request."},
+ {"SSH_FX_BYTE_RANGE_LOCK_REFUSED", "A request for a byte range lock was refused."},
+ {"SSH_FX_DELETE_PENDING", "An operation was attempted on a file for which a delete operation is pending."},
+ {"SSH_FX_FILE_CORRUPT", "The file is corrupt; an filesystem integrity check should be run."},
+ {"SSH_FX_OWNER_INVALID", "The principal specified can not be assigned as an owner of a file."},
+ {"SSH_FX_GROUP_INVALID", "The principal specified can not be assigned as the primary group of a file."},
+ {"SSH_FX_NO_MATCHING_BYTE_RANGE_LOCK",
+ "The requested operation could not be completed because the specifed byte range lock has not been granted."},
+
+ };
+
+ public static final String[] getDescription(int errorCode) {
+ if((errorCode < 0) || (errorCode >= messages.length)) {
+ return null;
+ }
+
+ return messages[errorCode];
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/OpenFlags.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/OpenFlags.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * SFTP Open Flags.
+ *
+ * The following table is provided to assist in mapping POSIX semantics
+ * to equivalent SFTP file open parameters:
+ *
+ *
+ * - O_RDONLY
+ *
- desired-access = READ_DATA | READ_ATTRIBUTES
+ *
+ *
+ *
+ * - O_WRONLY
+ *
- desired-access = WRITE_DATA | WRITE_ATTRIBUTES
+ *
+ *
+ *
+ * - O_RDWR
+ *
- desired-access = READ_DATA | READ_ATTRIBUTES | WRITE_DATA | WRITE_ATTRIBUTES
+ *
+ *
+ *
+ * - O_APPEND
+ *
+ * - desired-access = WRITE_DATA | WRITE_ATTRIBUTES | APPEND_DATA
+ * - flags = SSH_FXF_ACCESS_APPEND_DATA and or SSH_FXF_ACCESS_APPEND_DATA_ATOMIC
+ *
+ *
+ *
+ *
+ * - O_CREAT
+ *
+ * - flags = SSH_FXF_OPEN_OR_CREATE
+ *
+ *
+ *
+ *
+ * - O_TRUNC
+ *
+ * - flags = SSH_FXF_TRUNCATE_EXISTING
+ *
+ *
+ *
+ *
+ * - O_TRUNC|O_CREATE
+ *
+ * - flags = SSH_FXF_CREATE_TRUNCATE
+ *
+ *
+ *
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public final class OpenFlags {
+ private OpenFlags() {
+ }
+
+ /**
+ * Disposition is a 3 bit field that controls how the file is opened.
+ * The server MUST support these bits (possible enumaration values:
+ * SSH_FXF_CREATE_NEW, SSH_FXF_CREATE_TRUNCATE, SSH_FXF_OPEN_EXISTING,
+ * SSH_FXF_OPEN_OR_CREATE or SSH_FXF_TRUNCATE_EXISTING).
+ */
+ public static final int SSH_FXF_ACCESS_DISPOSITION = 0x00000007;
+
+ /**
+ * A new file is created; if the file already exists, the server
+ * MUST return status SSH_FX_FILE_ALREADY_EXISTS.
+ */
+ public static final int SSH_FXF_CREATE_NEW = 0x00000000;
+
+ /**
+ * A new file is created; if the file already exists, it is opened
+ * and truncated.
+ */
+ public static final int SSH_FXF_CREATE_TRUNCATE = 0x00000001;
+
+ /**
+ * An existing file is opened. If the file does not exist, the
+ * server MUST return SSH_FX_NO_SUCH_FILE. If a directory in the
+ * path does not exist, the server SHOULD return
+ * SSH_FX_NO_SUCH_PATH. It is also acceptable if the server
+ * returns SSH_FX_NO_SUCH_FILE in this case.
+ */
+ public static final int SSH_FXF_OPEN_EXISTING = 0x00000002;
+
+ /**
+ * If the file exists, it is opened. If the file does not exist,
+ * it is created.
+ */
+ public static final int SSH_FXF_OPEN_OR_CREATE = 0x00000003;
+
+ /**
+ * An existing file is opened and truncated. If the file does not
+ * exist, the server MUST return the same error codes as defined
+ * for SSH_FXF_OPEN_EXISTING.
+ */
+ public static final int SSH_FXF_TRUNCATE_EXISTING = 0x00000004;
+
+ /**
+ * Data is always written at the end of the file. The offset field
+ * of the SSH_FXP_WRITE requests are ignored.
+ *
+ * Data is not required to be appended atomically. This means that
+ * if multiple writers attempt to append data simultaneously, data
+ * from the first may be lost. However, data MAY be appended
+ * atomically.
+ */
+ public static final int SSH_FXF_ACCESS_APPEND_DATA = 0x00000008;
+
+ /**
+ * Data is always written at the end of the file. The offset field
+ * of the SSH_FXP_WRITE requests are ignored.
+ *
+ * Data MUST be written atomically so that there is no chance that
+ * multiple appenders can collide and result in data being lost.
+ *
+ * If both append flags are specified, the server SHOULD use atomic
+ * append if it is available, but SHOULD use non-atomic appends
+ * otherwise. The server SHOULD NOT fail the request in this case.
+ */
+ public static final int SSH_FXF_ACCESS_APPEND_DATA_ATOMIC = 0x00000010;
+
+ /**
+ * Indicates that the server should treat the file as text and
+ * convert it to the canonical newline convention in use.
+ * (See Determining Server Newline Convention in section 5.3 in the
+ * SFTP standard draft).
+ *
+ * When a file is opened with this flag, the offset field in the read
+ * and write functions is ignored.
+ *
+ * Servers MUST process multiple, parallel reads and writes correctly
+ * in this mode. Naturally, it is permissible for them to do this by
+ * serializing the requests.
+ *
+ * Clients SHOULD use the SSH_FXF_ACCESS_APPEND_DATA flag to append
+ * data to a text file rather then using write with a calculated offset.
+ */
+ public static final int SSH_FXF_ACCESS_TEXT_MODE = 0x00000020;
+
+ /**
+ * The server MUST guarantee that no other handle has been opened
+ * with ACE4_READ_DATA access, and that no other handle will be
+ * opened with ACE4_READ_DATA access until the client closes the
+ * handle. (This MUST apply both to other clients and to other
+ * processes on the server.)
+ *
+ * If there is a conflicting lock the server MUST return
+ * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
+ * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
+ *
+ * Other handles MAY be opened for ACE4_WRITE_DATA or any other
+ * combination of accesses, as long as ACE4_READ_DATA is not included
+ * in the mask.
+ */
+ public static final int SSH_FXF_ACCESS_BLOCK_READ = 0x00000040;
+
+ /**
+ * The server MUST guarantee that no other handle has been opened
+ * with ACE4_WRITE_DATA or ACE4_APPEND_DATA access, and that no other
+ * handle will be opened with ACE4_WRITE_DATA or ACE4_APPEND_DATA
+ * access until the client closes the handle. (This MUST apply both
+ * to other clients and to other processes on the server.)
+ *
+ * If there is a conflicting lock the server MUST return
+ * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
+ * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
+ *
+ * Other handles MAY be opened for ACE4_READ_DATA or any other
+ * combination of accesses, as long as neither ACE4_WRITE_DATA nor
+ * ACE4_APPEND_DATA are included in the mask.
+ */
+ public static final int SSH_FXF_ACCESS_BLOCK_WRITE = 0x00000080;
+
+ /**
+ * The server MUST guarantee that no other handle has been opened
+ * with ACE4_DELETE access, opened with the
+ * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that no other handle
+ * will be opened with ACE4_DELETE access or with the
+ * SSH_FXF_ACCESS_DELETE_ON_CLOSE flag set, and that the file itself
+ * is not deleted in any other way until the client closes the handle.
+ *
+ * If there is a conflicting lock the server MUST return
+ * SSH_FX_LOCK_CONFLICT. If the server cannot make the locking
+ * guarantee, it MUST return SSH_FX_OP_UNSUPPORTED.
+ */
+ public static final int SSH_FXF_ACCESS_BLOCK_DELETE = 0x00000100;
+
+ /**
+ * If this bit is set, the above BLOCK modes are advisory. In advisory
+ * mode, only other accesses that specify a BLOCK mode need be
+ * considered when determining whether the BLOCK can be granted,
+ * and the server need not prevent I/O operations that violate the
+ * block mode.
+ *
+ * The server MAY perform mandatory locking even if the BLOCK_ADVISORY
+ * bit is set.
+ */
+ public static final int SSH_FXF_ACCESS_BLOCK_ADVISORY = 0x00000200;
+
+ /**
+ * If the final component of the path is a symlink, then the open
+ * MUST fail, and the error SSH_FX_LINK_LOOP MUST be returned.
+ */
+ public static final int SSH_FXF_ACCESS_NOFOLLOW = 0x00000400;
+
+ /**
+ * The file should be deleted when the last handle to it is closed.
+ * (The last handle may not be an sftp-handle.) This MAY be emulated
+ * by a server if the OS doesn't support it by deleting the file when
+ * this handle is closed.
+ *
+ * It is implementation specific whether the directory entry is
+ * removed immediately or when the handle is closed.
+ */
+ public static final int SSH_FXF_ACCESS_DELETE_ON_CLOSE = 0x00000800;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/sftp/Packet.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/sftp/Packet.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.sftp;
+
+/**
+ * SFTP Paket Types
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class Packet {
+ public static final int SSH_FXP_INIT = 1;
+ public static final int SSH_FXP_VERSION = 2;
+ public static final int SSH_FXP_OPEN = 3;
+ public static final int SSH_FXP_CLOSE = 4;
+ public static final int SSH_FXP_READ = 5;
+ public static final int SSH_FXP_WRITE = 6;
+ public static final int SSH_FXP_LSTAT = 7;
+ public static final int SSH_FXP_FSTAT = 8;
+ public static final int SSH_FXP_SETSTAT = 9;
+ public static final int SSH_FXP_FSETSTAT = 10;
+ public static final int SSH_FXP_OPENDIR = 11;
+ public static final int SSH_FXP_READDIR = 12;
+ public static final int SSH_FXP_REMOVE = 13;
+ public static final int SSH_FXP_MKDIR = 14;
+ public static final int SSH_FXP_RMDIR = 15;
+ public static final int SSH_FXP_REALPATH = 16;
+ public static final int SSH_FXP_STAT = 17;
+ public static final int SSH_FXP_RENAME = 18;
+ public static final int SSH_FXP_READLINK = 19;
+ public static final int SSH_FXP_SYMLINK = 20;
+ public static final int SSH_FXP_LINK = 21;
+
+ public static final int SSH_FXP_STATUS = 101;
+ public static final int SSH_FXP_HANDLE = 102;
+ public static final int SSH_FXP_DATA = 103;
+ public static final int SSH_FXP_NAME = 104;
+ public static final int SSH_FXP_ATTRS = 105;
+
+ /**
+ * SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY packets can be used to
+ * implement extensions, which can be vendor specific.
+ */
+ public static final int SSH_FXP_EXTENDED = 200;
+ /**
+ * SSH_FXP_EXTENDED and SSH_FXP_EXTENDED_REPLY packets can be used to
+ * implement extensions, which can be vendor specific.
+ */
+ public static final int SSH_FXP_EXTENDED_REPLY = 201;
+
+ public static String forName(int type) {
+ switch(type) {
+ case SSH_FXP_INIT:
+ return "SSH_FXP_INIT";
+ case SSH_FXP_VERSION:
+ return "SSH_FXP_VERSION";
+ case SSH_FXP_OPEN:
+ return "SSH_FXP_OPEN";
+ case SSH_FXP_CLOSE:
+ return "SSH_FXP_CLOSE";
+ case SSH_FXP_READ:
+ return "SSH_FXP_READ";
+ case SSH_FXP_WRITE:
+ return "SSH_FXP_WRITE";
+ case SSH_FXP_LSTAT:
+ return "SSH_FXP_LSTAT";
+ case SSH_FXP_FSTAT:
+ return "SSH_FXP_FSTAT";
+ case SSH_FXP_SETSTAT:
+ return "SSH_FXP_SETSTAT";
+ case SSH_FXP_FSETSTAT:
+ return "SSH_FXP_FSETSTAT";
+ case SSH_FXP_OPENDIR:
+ return "SSH_FXP_OPENDIR";
+ case SSH_FXP_READDIR:
+ return "SSH_FXP_READDIR";
+ case SSH_FXP_REMOVE:
+ return "SSH_FXP_REMOVE";
+ case SSH_FXP_MKDIR:
+ return "SSH_FXP_MKDIR";
+ case SSH_FXP_RMDIR:
+ return "SSH_FXP_RMDIR";
+ case SSH_FXP_REALPATH:
+ return "SSH_FXP_REALPATH";
+ case SSH_FXP_STAT:
+ return "SSH_FXP_STAT";
+ case SSH_FXP_RENAME:
+ return "SSH_FXP_RENAME";
+ case SSH_FXP_READLINK:
+ return "SSH_FXP_READLINK";
+ case SSH_FXP_SYMLINK:
+ return "SSH_FXP_SYMLINK";
+ case SSH_FXP_STATUS:
+ return "SSH_FXP_STATUS";
+ case SSH_FXP_HANDLE:
+ return "SSH_FXP_HANDLE";
+ case SSH_FXP_DATA:
+ return "SSH_FXP_DATA";
+ case SSH_FXP_NAME:
+ return "SSH_FXP_NAME";
+ case SSH_FXP_ATTRS:
+ return "SSH_FXP_ATTRS";
+ case SSH_FXP_EXTENDED:
+ return "SSH_FXP_EXTENDED";
+ case SSH_FXP_EXTENDED_REPLY:
+ return "SSH_FXP_EXTENDED_REPLY";
+ }
+ return null;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/signature/DSAPrivateKey.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/DSAPrivateKey.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.signature;
+
+import java.math.BigInteger;
+
+/**
+ * DSAPrivateKey.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class DSAPrivateKey
+{
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger g;
+ private BigInteger x;
+ private BigInteger y;
+
+ public DSAPrivateKey(BigInteger p, BigInteger q, BigInteger g,
+ BigInteger y, BigInteger x)
+ {
+ this.p = p;
+ this.q = q;
+ this.g = g;
+ this.y = y;
+ this.x = x;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+
+ public BigInteger getX()
+ {
+ return x;
+ }
+
+ public DSAPublicKey getPublicKey()
+ {
+ return new DSAPublicKey(p, q, g, y);
+ }
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/signature/DSAPublicKey.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/DSAPublicKey.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.signature;
+
+import java.math.BigInteger;
+
+/**
+ * DSAPublicKey.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class DSAPublicKey
+{
+ private BigInteger p;
+ private BigInteger q;
+ private BigInteger g;
+ private BigInteger y;
+
+ public DSAPublicKey(BigInteger p, BigInteger q, BigInteger g, BigInteger y)
+ {
+ this.p = p;
+ this.q = q;
+ this.g = g;
+ this.y = y;
+ }
+
+ public BigInteger getP()
+ {
+ return p;
+ }
+
+ public BigInteger getQ()
+ {
+ return q;
+ }
+
+ public BigInteger getG()
+ {
+ return g;
+ }
+
+ public BigInteger getY()
+ {
+ return y;
+ }
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/signature/DSASHA1Verify.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/DSASHA1Verify.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.signature;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.DigestException;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.crypto.digest.SHA1;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+
+/**
+ * DSASHA1Verify.
+ *
+ * @author Christian Plattner
+ * @version $Id: DSASHA1Verify.java 154 2014-04-28 11:45:02Z dkocher@sudo.ch $
+ */
+public class DSASHA1Verify {
+ private static final Logger log = Logger.getLogger(DSASHA1Verify.class);
+
+ public static DSAPublicKey decodeSSHDSAPublicKey(byte[] key) throws IOException {
+ TypesReader tr = new TypesReader(key);
+
+ String key_format = tr.readString();
+
+ if(!key_format.equals("ssh-dss")) {
+ throw new IllegalArgumentException("Not a ssh-dss public key");
+ }
+
+ BigInteger p = tr.readMPINT();
+ BigInteger q = tr.readMPINT();
+ BigInteger g = tr.readMPINT();
+ BigInteger y = tr.readMPINT();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException("Padding in DSA public key");
+ }
+
+ return new DSAPublicKey(p, q, g, y);
+ }
+
+ public static byte[] encodeSSHDSAPublicKey(DSAPublicKey pk) throws IOException {
+ TypesWriter tw = new TypesWriter();
+
+ tw.writeString("ssh-dss");
+ tw.writeMPInt(pk.getP());
+ tw.writeMPInt(pk.getQ());
+ tw.writeMPInt(pk.getG());
+ tw.writeMPInt(pk.getY());
+
+ return tw.getBytes();
+ }
+
+ public static byte[] encodeSSHDSASignature(DSASignature ds) {
+ TypesWriter tw = new TypesWriter();
+
+ tw.writeString("ssh-dss");
+
+ byte[] r = ds.getR().toByteArray();
+ byte[] s = ds.getS().toByteArray();
+
+ byte[] a40 = new byte[40];
+
+ /* Patch (unsigned) r and s into the target array. */
+
+ int r_copylen = (r.length < 20) ? r.length : 20;
+ int s_copylen = (s.length < 20) ? s.length : 20;
+
+ System.arraycopy(r, r.length - r_copylen, a40, 20 - r_copylen, r_copylen);
+ System.arraycopy(s, s.length - s_copylen, a40, 40 - s_copylen, s_copylen);
+
+ tw.writeString(a40, 0, 40);
+
+ return tw.getBytes();
+ }
+
+ public static DSASignature decodeSSHDSASignature(byte[] sig) throws IOException {
+ byte[] rsArray;
+
+ if(sig.length == 40) {
+ rsArray = sig;
+ }
+ else {
+ TypesReader tr = new TypesReader(sig);
+
+ String sig_format = tr.readString();
+
+ if(sig_format.equals("ssh-dss") == false) {
+ throw new PacketFormatException("Peer sent wrong signature format");
+ }
+
+ rsArray = tr.readByteString();
+
+ if(rsArray.length != 40) {
+ throw new PacketFormatException("Peer sent corrupt signature");
+ }
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException("Padding in DSA signature!");
+ }
+ }
+
+ /* Remember, s and r are unsigned ints. */
+
+ byte[] tmp = new byte[20];
+
+ System.arraycopy(rsArray, 0, tmp, 0, 20);
+ BigInteger r = new BigInteger(1, tmp);
+
+ System.arraycopy(rsArray, 20, tmp, 0, 20);
+ BigInteger s = new BigInteger(1, tmp);
+
+ if(log.isDebugEnabled()) {
+ log.debug("decoded ssh-dss signature: first bytes r(" + ((rsArray[0]) & 0xff) + "), s("
+ + ((rsArray[20]) & 0xff) + ")");
+ }
+
+ return new DSASignature(r, s);
+ }
+
+ public static boolean verifySignature(byte[] message, DSASignature ds, DSAPublicKey dpk) throws IOException {
+ /* Inspired by Bouncycastle's DSASigner class */
+
+ SHA1 md = new SHA1();
+ md.update(message);
+ byte[] sha_message = new byte[md.getDigestLength()];
+ try {
+ md.digest(sha_message);
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+
+ BigInteger m = new BigInteger(1, sha_message);
+
+ BigInteger r = ds.getR();
+ BigInteger s = ds.getS();
+
+ BigInteger g = dpk.getG();
+ BigInteger p = dpk.getP();
+ BigInteger q = dpk.getQ();
+ BigInteger y = dpk.getY();
+
+ BigInteger zero = BigInteger.ZERO;
+
+ if(log.isDebugEnabled()) {
+ log.debug("ssh-dss signature: m: " + m.toString(16));
+ log.debug("ssh-dss signature: r: " + r.toString(16));
+ log.debug("ssh-dss signature: s: " + s.toString(16));
+ log.debug("ssh-dss signature: g: " + g.toString(16));
+ log.debug("ssh-dss signature: p: " + p.toString(16));
+ log.debug("ssh-dss signature: q: " + q.toString(16));
+ log.debug("ssh-dss signature: y: " + y.toString(16));
+ }
+
+ if(zero.compareTo(r) >= 0 || q.compareTo(r) <= 0) {
+ log.warning("ssh-dss signature: zero.compareTo(r) >= 0 || q.compareTo(r) <= 0");
+ return false;
+ }
+
+ if(zero.compareTo(s) >= 0 || q.compareTo(s) <= 0) {
+ log.warning("ssh-dss signature: zero.compareTo(s) >= 0 || q.compareTo(s) <= 0");
+ return false;
+ }
+
+ BigInteger w = s.modInverse(q);
+
+ BigInteger u1 = m.multiply(w).mod(q);
+ BigInteger u2 = r.multiply(w).mod(q);
+
+ u1 = g.modPow(u1, p);
+ u2 = y.modPow(u2, p);
+
+ BigInteger v = u1.multiply(u2).mod(p).mod(q);
+
+ return v.equals(r);
+ }
+
+ public static DSASignature generateSignature(byte[] message, DSAPrivateKey pk, SecureRandom rnd) throws IOException {
+ SHA1 md = new SHA1();
+ md.update(message);
+ byte[] sha_message = new byte[md.getDigestLength()];
+ try {
+ md.digest(sha_message);
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+
+ BigInteger m = new BigInteger(1, sha_message);
+ BigInteger k;
+ int qBitLength = pk.getQ().bitLength();
+
+ do {
+ k = new BigInteger(qBitLength, rnd);
+ }
+ while(k.compareTo(pk.getQ()) >= 0);
+
+ BigInteger r = pk.getG().modPow(k, pk.getP()).mod(pk.getQ());
+
+ k = k.modInverse(pk.getQ()).multiply(m.add((pk).getX().multiply(r)));
+
+ BigInteger s = k.mod(pk.getQ());
+
+ return new DSASignature(r, s);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/signature/DSASignature.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/DSASignature.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.signature;
+
+import java.math.BigInteger;
+
+/**
+ * DSASignature.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class DSASignature
+{
+ private BigInteger r;
+ private BigInteger s;
+
+ public DSASignature(BigInteger r, BigInteger s)
+ {
+ this.r = r;
+ this.s = s;
+ }
+
+ public BigInteger getR()
+ {
+ return r;
+ }
+
+ public BigInteger getS()
+ {
+ return s;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/signature/RSAPrivateKey.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/RSAPrivateKey.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.signature;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * RSAPrivateKey.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class RSAPrivateKey
+{
+ private BigInteger d;
+ private BigInteger e;
+ private BigInteger n;
+
+ public RSAPrivateKey(BigInteger d, BigInteger e, BigInteger n)
+ {
+ this.d = d;
+ this.e = e;
+ this.n = n;
+ }
+
+ public BigInteger getD()
+ {
+ return d;
+ }
+
+ public BigInteger getE()
+ {
+ return e;
+ }
+
+ public BigInteger getN()
+ {
+ return n;
+ }
+
+ public RSAPublicKey getPublicKey()
+ {
+ return new RSAPublicKey(e, n);
+ }
+
+ /**
+ * Generate an RSA hostkey for testing purposes only.
+ *
+ * @param numbits Key length in bits
+ * @return
+ */
+ public static RSAPrivateKey generateKey(int numbits)
+ {
+ return generateKey(new SecureRandom(), numbits);
+ }
+
+ /**
+ * Generate an RSA hostkey for testing purposes only.
+ *
+ * @param rnd Source for random bits
+ * @param numbits Key length in bits
+ * @return
+ */
+ public static RSAPrivateKey generateKey(SecureRandom rnd, int numbits)
+ {
+ BigInteger p = BigInteger.probablePrime(numbits / 2, rnd);
+ BigInteger q = BigInteger.probablePrime(numbits / 2, rnd);
+ BigInteger phi = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE));
+
+ BigInteger n = p.multiply(q);
+ BigInteger e = new BigInteger("65537");
+ BigInteger d = e.modInverse(phi);
+
+ return new RSAPrivateKey(d, e, n);
+ }
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/signature/RSAPublicKey.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/RSAPublicKey.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.signature;
+
+import java.math.BigInteger;
+
+/**
+ * RSAPublicKey.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class RSAPublicKey
+{
+ BigInteger e;
+ BigInteger n;
+
+ public RSAPublicKey(BigInteger e, BigInteger n)
+ {
+ this.e = e;
+ this.n = n;
+ }
+
+ public BigInteger getE()
+ {
+ return e;
+ }
+
+ public BigInteger getN()
+ {
+ return n;
+ }
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/signature/RSASHA1Verify.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/RSASHA1Verify.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.signature;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.DigestException;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.crypto.SimpleDERReader;
+import ch.ethz.ssh2.crypto.digest.SHA1;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.packets.TypesWriter;
+
+/**
+ * RSASHA1Verify.
+ *
+ * @author Christian Plattner
+ * @version $Id: RSASHA1Verify.java 154 2014-04-28 11:45:02Z dkocher@sudo.ch $
+ */
+public class RSASHA1Verify {
+ private static final Logger log = Logger.getLogger(RSASHA1Verify.class);
+
+ public static RSAPublicKey decodeSSHRSAPublicKey(byte[] key) throws IOException {
+ TypesReader tr = new TypesReader(key);
+
+ String key_format = tr.readString();
+
+ if(!key_format.equals("ssh-rsa")) {
+ throw new IllegalArgumentException("Not a ssh-rsa public key");
+ }
+
+ BigInteger e = tr.readMPINT();
+ BigInteger n = tr.readMPINT();
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException("Padding in RSA public key");
+ }
+
+ return new RSAPublicKey(e, n);
+ }
+
+ public static byte[] encodeSSHRSAPublicKey(RSAPublicKey pk) throws IOException {
+ TypesWriter tw = new TypesWriter();
+
+ tw.writeString("ssh-rsa");
+ tw.writeMPInt(pk.getE());
+ tw.writeMPInt(pk.getN());
+
+ return tw.getBytes();
+ }
+
+ public static RSASignature decodeSSHRSASignature(byte[] sig) throws IOException {
+ TypesReader tr = new TypesReader(sig);
+
+ String sig_format = tr.readString();
+
+ if(!sig_format.equals("ssh-rsa")) {
+ throw new PacketFormatException("Peer sent wrong signature format");
+ }
+
+ /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
+ * containing s (which is an integer, without lengths or padding, unsigned and in
+ * network byte order)." See also below.
+ */
+
+ byte[] s = tr.readByteString();
+
+ if(s.length == 0) {
+ throw new PacketFormatException("Error in RSA signature, S is empty.");
+ }
+
+ if(log.isDebugEnabled()) {
+ log.debug("Decoding ssh-rsa signature string (length: " + s.length + ")");
+ }
+
+ if(tr.remain() != 0) {
+ throw new PacketFormatException("Padding in RSA signature!");
+ }
+
+ return new RSASignature(new BigInteger(1, s));
+ }
+
+ public static byte[] encodeSSHRSASignature(RSASignature sig) throws IOException {
+ TypesWriter tw = new TypesWriter();
+
+ tw.writeString("ssh-rsa");
+
+ /* S is NOT an MPINT. "The value for 'rsa_signature_blob' is encoded as a string
+ * containing s (which is an integer, without lengths or padding, unsigned and in
+ * network byte order)."
+ */
+
+ byte[] s = sig.getS().toByteArray();
+
+ /* Remove first zero sign byte, if present */
+
+ if((s.length > 1) && (s[0] == 0x00)) {
+ tw.writeString(s, 1, s.length - 1);
+ }
+ else {
+ tw.writeString(s, 0, s.length);
+ }
+
+ return tw.getBytes();
+ }
+
+ public static RSASignature generateSignature(byte[] message, RSAPrivateKey pk) throws IOException {
+ SHA1 md = new SHA1();
+ md.update(message);
+ byte[] sha_message = new byte[md.getDigestLength()];
+ try {
+ md.digest(sha_message);
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+
+ byte[] der_header = new byte[]{0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00,
+ 0x04, 0x14};
+
+ int rsa_block_len = (pk.getN().bitLength() + 7) / 8;
+
+ int num_pad = rsa_block_len - (2 + der_header.length + sha_message.length) - 1;
+
+ if(num_pad < 8) {
+ throw new PacketFormatException("Cannot sign with RSA, message too long");
+ }
+
+ byte[] sig = new byte[der_header.length + sha_message.length + 2 + num_pad];
+
+ sig[0] = 0x01;
+
+ for(int i = 0; i < num_pad; i++) {
+ sig[i + 1] = (byte) 0xff;
+ }
+
+ sig[num_pad + 1] = 0x00;
+
+ System.arraycopy(der_header, 0, sig, 2 + num_pad, der_header.length);
+ System.arraycopy(sha_message, 0, sig, 2 + num_pad + der_header.length, sha_message.length);
+
+ BigInteger m = new BigInteger(1, sig);
+
+ BigInteger s = m.modPow(pk.getD(), pk.getN());
+
+ return new RSASignature(s);
+ }
+
+ public static boolean verifySignature(byte[] message, RSASignature ds, RSAPublicKey dpk) throws IOException {
+ SHA1 md = new SHA1();
+ md.update(message);
+ byte[] sha_message = new byte[md.getDigestLength()];
+ try {
+ md.digest(sha_message);
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+
+ BigInteger n = dpk.getN();
+ BigInteger e = dpk.getE();
+ BigInteger s = ds.getS();
+
+ if(n.compareTo(s) <= 0) {
+ log.warning("ssh-rsa signature: n.compareTo(s) <= 0");
+ return false;
+ }
+
+ int rsa_block_len = (n.bitLength() + 7) / 8;
+
+ /* And now the show begins */
+
+ if(rsa_block_len < 1) {
+ log.warning("ssh-rsa signature: rsa_block_len < 1");
+ return false;
+ }
+
+ byte[] v = s.modPow(e, n).toByteArray();
+
+ int startpos = 0;
+
+ if((v.length > 0) && (v[0] == 0x00)) {
+ startpos++;
+ }
+
+ if((v.length - startpos) != (rsa_block_len - 1)) {
+ log.warning("ssh-rsa signature: (v.length - startpos) != (rsa_block_len - 1)");
+ return false;
+ }
+
+ if(v[startpos] != 0x01) {
+ log.warning("ssh-rsa signature: v[startpos] != 0x01");
+ return false;
+ }
+
+ int pos = startpos + 1;
+
+ while(true) {
+ if(pos >= v.length) {
+ log.warning("ssh-rsa signature: pos >= v.length");
+ return false;
+ }
+ if(v[pos] == 0x00) {
+ break;
+ }
+ if(v[pos] != (byte) 0xff) {
+ log.warning("ssh-rsa signature: v[pos] != (byte) 0xff");
+ return false;
+ }
+ pos++;
+ }
+
+ int num_pad = pos - (startpos + 1);
+
+ if(num_pad < 8) {
+ log.warning("ssh-rsa signature: num_pad < 8");
+ return false;
+ }
+
+ pos++;
+
+ if(pos >= v.length) {
+ log.warning("ssh-rsa signature: pos >= v.length");
+ return false;
+ }
+
+ SimpleDERReader dr = new SimpleDERReader(v, pos, v.length - pos);
+
+ byte[] seq = dr.readSequenceAsByteArray();
+
+ if(dr.available() != 0) {
+ log.warning("ssh-rsa signature: dr.available() != 0");
+ return false;
+ }
+
+ dr.resetInput(seq);
+
+ /* Read digestAlgorithm */
+
+ byte digestAlgorithm[] = dr.readSequenceAsByteArray();
+
+ /* Inspired by RFC 3347, however, ignoring the comment regarding old BER based implementations */
+
+ if((digestAlgorithm.length < 8) || (digestAlgorithm.length > 9)) {
+ log.warning("ssh-rsa signature: (digestAlgorithm.length < 8) || (digestAlgorithm.length > 9)");
+ return false;
+ }
+
+ byte[] digestAlgorithm_sha1 = new byte[]{0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00};
+
+ for(int i = 0; i < digestAlgorithm.length; i++) {
+ if(digestAlgorithm[i] != digestAlgorithm_sha1[i]) {
+ log.warning("ssh-rsa signature: digestAlgorithm[i] != digestAlgorithm_sha1[i]");
+ return false;
+ }
+ }
+
+ byte[] digest = dr.readOctetString();
+
+ if(dr.available() != 0) {
+ log.warning("ssh-rsa signature: dr.available() != 0 (II)");
+ return false;
+ }
+
+ if(digest.length != sha_message.length) {
+ log.warning("ssh-rsa signature: digest.length != sha_message.length");
+ return false;
+ }
+
+ for(int i = 0; i < sha_message.length; i++) {
+ if(sha_message[i] != digest[i]) {
+ log.warning("ssh-rsa signature: sha_message[i] != digest[i]");
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/signature/RSASignature.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/signature/RSASignature.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.signature;
+
+import java.math.BigInteger;
+
+
+/**
+ * RSASignature.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+
+public class RSASignature
+{
+ BigInteger s;
+
+ public BigInteger getS()
+ {
+ return s;
+ }
+
+ public RSASignature(BigInteger s)
+ {
+ this.s = s;
+ }
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/ClientKexManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/ClientKexManager.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.security.DigestException;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.ConnectionInfo;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.ServerHostKeyVerifier;
+import ch.ethz.ssh2.compression.CompressionFactory;
+import ch.ethz.ssh2.compression.Compressor;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
+import ch.ethz.ssh2.crypto.dh.DhExchange;
+import ch.ethz.ssh2.crypto.dh.DhGroupExchange;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.packets.PacketKexDHInit;
+import ch.ethz.ssh2.packets.PacketKexDHReply;
+import ch.ethz.ssh2.packets.PacketKexDhGexGroup;
+import ch.ethz.ssh2.packets.PacketKexDhGexInit;
+import ch.ethz.ssh2.packets.PacketKexDhGexReply;
+import ch.ethz.ssh2.packets.PacketKexDhGexRequest;
+import ch.ethz.ssh2.packets.PacketKexDhGexRequestOld;
+import ch.ethz.ssh2.packets.PacketKexInit;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.signature.DSAPublicKey;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.DSASignature;
+import ch.ethz.ssh2.signature.RSAPublicKey;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.signature.RSASignature;
+
+/**
+ * @version $Id: ClientKexManager.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public class ClientKexManager extends KexManager {
+
+ private final ServerHostKeyVerifier verifier;
+
+ private final String hostname;
+
+ private final int port;
+
+ public ClientKexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port,
+ ServerHostKeyVerifier keyVerifier, SecureRandom rnd) {
+ super(tm, csh, initialCwl, rnd);
+ this.hostname = hostname;
+ this.port = port;
+ this.verifier = keyVerifier;
+ }
+
+ protected boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException {
+ if(kxs.np.server_host_key_algo.equals("ssh-rsa")) {
+ RSASignature rs = RSASHA1Verify.decodeSSHRSASignature(sig);
+ RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey);
+
+ log.debug("Verifying ssh-rsa signature");
+
+ return RSASHA1Verify.verifySignature(kxs.H, rs, rpk);
+ }
+
+ if(kxs.np.server_host_key_algo.equals("ssh-dss")) {
+ DSASignature ds = DSASHA1Verify.decodeSSHDSASignature(sig);
+ DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey);
+
+ log.debug("Verifying ssh-dss signature");
+
+ return DSASHA1Verify.verifySignature(kxs.H, ds, dpk);
+ }
+
+ throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'");
+ }
+
+ @Override
+ public void handleFailure(final IOException failure) {
+ synchronized(accessLock) {
+ connectionClosed = true;
+ accessLock.notifyAll();
+ }
+ }
+
+ @Override
+ public synchronized void handleMessage(byte[] msg) throws IOException {
+ PacketKexInit kip;
+
+ if((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT)) {
+ throw new PacketTypeException(msg[0]);
+ }
+
+ if(ignore_next_kex_packet) {
+ ignore_next_kex_packet = false;
+ return;
+ }
+
+ if(msg[0] == Packets.SSH_MSG_KEXINIT) {
+ if((kxs != null) && (kxs.state != 0)) {
+ throw new PacketTypeException(msg[0]);
+ }
+
+ if(kxs == null) {
+ /*
+ * Ah, OK, peer wants to do KEX. Let's be nice and play
+ * together.
+ */
+ kxs = new KexState();
+ kxs.dhgexParameters = nextKEXdhgexParameters;
+ kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
+ kxs.localKEX = kip;
+ tm.sendKexMessage(kip.getPayload());
+ }
+
+ kip = new PacketKexInit(msg);
+ kxs.remoteKEX = kip;
+
+ kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters());
+
+ if(kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false)) {
+ // Guess was wrong, we need to ignore the next kex packet.
+ ignore_next_kex_packet = true;
+ }
+
+ if(kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1")) {
+ if(kxs.dhgexParameters.getMin_group_len() == 0) {
+ PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters);
+ tm.sendKexMessage(dhgexreq.getPayload());
+
+ }
+ else {
+ PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters);
+ tm.sendKexMessage(dhgexreq.getPayload());
+ }
+ kxs.state = 1;
+ return;
+ }
+
+ if(kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
+ || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")) {
+ kxs.dhx = new DhExchange();
+
+ if(kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")) {
+ kxs.dhx.clientInit(1, rnd);
+ }
+ else {
+ kxs.dhx.clientInit(14, rnd);
+ }
+
+ PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE());
+ tm.sendKexMessage(kp.getPayload());
+ kxs.state = 1;
+ return;
+ }
+
+ throw new IllegalStateException("Unkown KEX method!");
+ }
+
+ if(msg[0] == Packets.SSH_MSG_NEWKEYS) {
+ if(km == null) {
+ throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
+ }
+
+ BlockCipher cbc;
+ MAC mac;
+ Compressor comp;
+
+ try {
+ cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false,
+ km.enc_key_server_to_client, km.initial_iv_server_to_client);
+
+ try {
+ mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client);
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+
+ comp = CompressionFactory.createCompressor(kxs.np.comp_algo_server_to_client);
+ }
+ catch(IllegalArgumentException e) {
+ throw new IOException(e.getMessage());
+ }
+
+ tm.changeRecvCipher(cbc, mac);
+ tm.changeRecvCompression(comp);
+
+ ConnectionInfo sci = new ConnectionInfo();
+
+ kexCount++;
+
+ sci.keyExchangeAlgorithm = kxs.np.kex_algo;
+ sci.keyExchangeCounter = kexCount;
+ sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
+ sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
+ sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
+ sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
+ sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
+ sci.serverHostKey = kxs.remote_hostkey;
+
+ synchronized(accessLock) {
+ lastConnInfo = sci;
+ accessLock.notifyAll();
+ }
+
+ kxs = null;
+ return;
+ }
+
+ if((kxs == null) || (kxs.state == 0)) {
+ throw new IOException("Unexpected Kex submessage!");
+ }
+
+ if(kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1")) {
+ if(kxs.state == 1) {
+ PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg);
+ kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG());
+ kxs.dhgx.init(rnd);
+ PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE());
+ tm.sendKexMessage(dhgexinit.getPayload());
+ kxs.state = 2;
+ return;
+ }
+
+ if(kxs.state == 2) {
+ PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg);
+
+ kxs.remote_hostkey = dhgexrpl.getHostKey();
+
+ if(verifier != null) {
+ try {
+ if(!verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.remote_hostkey)) {
+ throw new IOException("The server host key was not accepted by the verifier callback");
+ }
+ }
+ catch(Exception e) {
+ throw new IOException(
+ "The server host key was not accepted by the verifier callback.", e);
+ }
+ }
+
+ kxs.dhgx.setF(dhgexrpl.getF());
+
+ try {
+ kxs.H = kxs.dhgx.calculateH(csh.getClientString(), csh.getServerString(),
+ kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), dhgexrpl.getHostKey(),
+ kxs.dhgexParameters);
+ }
+ catch(IllegalArgumentException e) {
+ throw new IOException("KEX error.", e);
+ }
+ if(!verifySignature(dhgexrpl.getSignature(), kxs.remote_hostkey)) {
+ throw new IOException("Invalid remote host key signature");
+ }
+ kxs.K = kxs.dhgx.getK();
+ finishKex(true);
+ kxs.state = -1;
+ return;
+ }
+
+ throw new IllegalStateException("Illegal State in KEX Exchange!");
+ }
+
+ if(kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
+ || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")) {
+ if(kxs.state == 1) {
+
+ PacketKexDHReply dhr = new PacketKexDHReply(msg);
+
+ kxs.remote_hostkey = dhr.getHostKey();
+
+ if(verifier != null) {
+ try {
+ if(!verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.remote_hostkey)) {
+ throw new IOException("The server host key was not accepted by the verifier callback");
+ }
+ }
+ catch(Exception e) {
+ throw new IOException("The server host key was not accepted by the verifier callback", e);
+ }
+ }
+ kxs.dhx.setF(dhr.getF());
+ try {
+ kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(),
+ kxs.remoteKEX.getPayload(), dhr.getHostKey());
+ }
+ catch(IllegalArgumentException e) {
+ throw new IOException("KEX error.", e);
+ }
+ if(!verifySignature(dhr.getSignature(), kxs.remote_hostkey)) {
+ throw new IOException("Invalid remote host key signature");
+ }
+ kxs.K = kxs.dhx.getK();
+ finishKex(true);
+ kxs.state = -1;
+ return;
+ }
+ }
+ throw new IllegalStateException(String.format("Unknown KEX method %s", kxs.np.kex_algo));
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/ClientServerHello.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/ClientServerHello.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStream;
+
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: ClientServerHello.java 155 2014-04-28 12:01:19Z dkocher@sudo.ch $
+ */
+public class ClientServerHello {
+ private final String client_line;
+ private final String server_line;
+
+ private ClientServerHello(String client_line, String server_line) {
+ this.client_line = client_line;
+ this.server_line = server_line;
+ }
+
+ public static ClientServerHello clientHello(String softwareversion, InputStream bi, OutputStream bo)
+ throws IOException {
+ return exchange(softwareversion, bi, bo, true);
+ }
+
+ public static ClientServerHello serverHello(String softwareversion, InputStream bi, OutputStream bo)
+ throws IOException {
+ return exchange(softwareversion, bi, bo, false);
+ }
+
+ private static ClientServerHello exchange(String softwareversion, InputStream bi, OutputStream bo, boolean clientMode)
+ throws IOException {
+ String localIdentifier = String.format("SSH-2.0-%s", softwareversion);
+
+ bo.write(StringEncoder.GetBytes(String.format("%s\r\n", localIdentifier)));
+ bo.flush();
+
+ // Expect SSH-protoversion-softwareversion SP comments CR LF
+ String remoteIdentifier = new LineNumberReader(new InputStreamReader(bi)).readLine();
+ if(null == remoteIdentifier) {
+ throw new IOException("Premature connection close");
+ }
+ if(!remoteIdentifier.startsWith("SSH-")) {
+ throw new IOException(String.format("Malformed SSH identification %s", remoteIdentifier));
+ }
+ if(!remoteIdentifier.startsWith("SSH-1.99-")
+ && !remoteIdentifier.startsWith("SSH-2.0-")) {
+ throw new IOException(String.format("Incompatible remote protocol version %s", remoteIdentifier));
+ }
+ if(clientMode) {
+ return new ClientServerHello(localIdentifier, remoteIdentifier);
+ }
+ else {
+ return new ClientServerHello(remoteIdentifier, localIdentifier);
+ }
+ }
+
+ /**
+ * @return Returns the client_versioncomment.
+ */
+ public byte[] getClientString() {
+ return StringEncoder.GetBytes(client_line);
+ }
+
+ /**
+ * @return Returns the server_versioncomment.
+ */
+ public byte[] getServerString() {
+ return StringEncoder.GetBytes(server_line);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/ClientTransportManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/ClientTransportManager.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,59 @@
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.DHGexParameters;
+import ch.ethz.ssh2.ServerHostKeyVerifier;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+
+/**
+ * @version $Id: ClientTransportManager.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class ClientTransportManager extends TransportManager {
+
+ private final Socket sock;
+
+ public ClientTransportManager(final Socket socket) {
+ super(socket);
+ this.sock = socket;
+ }
+
+ public void setTcpNoDelay(boolean state) throws IOException {
+ sock.setTcpNoDelay(state);
+ }
+
+ public void setSoTimeout(int timeout) throws IOException {
+ sock.setSoTimeout(timeout);
+ }
+
+ public void connect(String hostname, int port, String softwareversion, CryptoWishList cwl,
+ ServerHostKeyVerifier verifier, DHGexParameters dhgex, int connectTimeout, SecureRandom rnd)
+ throws IOException {
+ // Establish the TCP connection to the SSH-2 server
+ this.connect(hostname, port, connectTimeout);
+
+ // Parse the server line and say hello - important: this information is later needed for the
+ // key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
+ // for later use.
+
+ ClientServerHello csh = ClientServerHello.clientHello(softwareversion, sock.getInputStream(),
+ sock.getOutputStream());
+
+ TransportConnection tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd);
+
+ KexManager km = new ClientKexManager(this, csh, cwl, hostname, port, verifier, rnd);
+ super.init(tc, km);
+
+ km.initiateKEX(cwl, dhgex, null, null);
+
+ this.startReceiver();
+ }
+
+ protected void connect(String hostname, int port, int connectTimeout)
+ throws IOException {
+ sock.connect(new InetSocketAddress(hostname, port), connectTimeout);
+ }
+}
\ No newline at end of file
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/DisconnectException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/DisconnectException.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,22 @@
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+
+import ch.ethz.ssh2.packets.PacketDisconnect;
+
+/**
+ * @version $Id: DisconnectException.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public class DisconnectException extends IOException {
+
+ private PacketDisconnect.Reason reason;
+
+ public DisconnectException(final PacketDisconnect.Reason reason, final String message) {
+ super(message);
+ this.reason = reason;
+ }
+
+ public PacketDisconnect.Reason getReason() {
+ return reason;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/HTTPProxyClientTransportManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/HTTPProxyClientTransportManager.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,111 @@
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import ch.ethz.ssh2.HTTPProxyData;
+import ch.ethz.ssh2.HTTPProxyException;
+import ch.ethz.ssh2.crypto.Base64;
+import ch.ethz.ssh2.util.StringEncoder;
+
+/**
+ * @version $Id: HTTPProxyClientTransportManager.java 155 2014-04-28 12:01:19Z dkocher@sudo.ch $
+ */
+public class HTTPProxyClientTransportManager extends ClientTransportManager {
+
+ /**
+ * Used to tell the library that the connection shall be established through a proxy server.
+ */
+
+ private HTTPProxyData pd;
+
+ private final Socket sock;
+
+ public HTTPProxyClientTransportManager(final Socket socket, final HTTPProxyData pd) {
+ super(socket);
+ this.sock = socket;
+ this.pd = pd;
+ }
+
+ @Override
+ protected void connect(final String hostname, final int port, final int connectTimeout) throws IOException {
+
+ sock.connect(new InetSocketAddress(pd.proxyHost, pd.proxyPort), connectTimeout);
+
+ // Tell the proxy where we actually want to connect to
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("CONNECT ");
+ sb.append(hostname);
+ sb.append(':');
+ sb.append(port);
+ sb.append(" HTTP/1.0\r\n");
+
+ if((pd.proxyUser != null) && (pd.proxyPass != null)) {
+ String credentials = pd.proxyUser + ":" + pd.proxyPass;
+ char[] encoded = Base64.encode(StringEncoder.GetBytes(credentials));
+ sb.append("Proxy-Authorization: Basic ");
+ sb.append(encoded);
+ sb.append("\r\n");
+ }
+
+ if(pd.requestHeaderLines != null) {
+ for(int i = 0; i < pd.requestHeaderLines.length; i++) {
+ if(pd.requestHeaderLines[i] != null) {
+ sb.append(pd.requestHeaderLines[i]);
+ sb.append("\r\n");
+ }
+ }
+ }
+
+ sb.append("\r\n");
+
+ OutputStream out = sock.getOutputStream();
+
+ out.write(StringEncoder.GetBytes(sb.toString()));
+ out.flush();
+
+ // Parse the HTTP response
+
+ InputStream in = sock.getInputStream();
+
+ final LineNumberReader reader = new LineNumberReader(new InputStreamReader(in));
+ String httpReponse = reader.readLine();
+
+ if(!httpReponse.startsWith("HTTP/")) {
+ throw new IOException("The proxy did not send back a valid HTTP response.");
+ }
+
+ // "HTTP/1.X XYZ X" => 14 characters minimum
+
+ if((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' ')) {
+ throw new IOException("The proxy did not send back a valid HTTP response.");
+ }
+
+ int errorCode;
+
+ try {
+ errorCode = Integer.parseInt(httpReponse.substring(9, 12));
+ }
+ catch(NumberFormatException ignore) {
+ throw new IOException("The proxy did not send back a valid HTTP response.");
+ }
+
+ if((errorCode < 0) || (errorCode > 999)) {
+ throw new IOException("The proxy did not send back a valid HTTP response.");
+ }
+
+ if(errorCode != 200) {
+ throw new HTTPProxyException(httpReponse.substring(13), errorCode);
+ }
+
+ while(reader.readLine() != null) {
+ // Read until empty line
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/KexManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/KexManager.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.security.DigestException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import ch.ethz.ssh2.ConnectionInfo;
+import ch.ethz.ssh2.DHGexParameters;
+import ch.ethz.ssh2.compression.CompressionFactory;
+import ch.ethz.ssh2.compression.Compressor;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.crypto.KeyMaterial;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.PacketKexInit;
+import ch.ethz.ssh2.packets.PacketNewKeys;
+import ch.ethz.ssh2.signature.DSAPrivateKey;
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+
+/**
+ * @version $Id: KexManager.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $
+ */
+public abstract class KexManager implements MessageHandler {
+ protected static final Logger log = Logger.getLogger(KexManager.class);
+
+ KexState kxs;
+ int kexCount = 0;
+ KeyMaterial km;
+ byte[] sessionId;
+ ClientServerHello csh;
+
+ final Object accessLock = new Object();
+ ConnectionInfo lastConnInfo = null;
+
+ boolean connectionClosed = false;
+
+ boolean ignore_next_kex_packet = false;
+
+ final TransportManager tm;
+
+ CryptoWishList nextKEXcryptoWishList;
+ DHGexParameters nextKEXdhgexParameters;
+ DSAPrivateKey nextKEXdsakey;
+ RSAPrivateKey nextKEXrsakey;
+
+ final SecureRandom rnd;
+
+ public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, SecureRandom rnd) {
+ this.tm = tm;
+ this.csh = csh;
+ this.nextKEXcryptoWishList = initialCwl;
+ this.nextKEXdhgexParameters = new DHGexParameters();
+ this.rnd = rnd;
+ }
+
+ public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException {
+ synchronized(accessLock) {
+ while(true) {
+ if((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount)) {
+ return lastConnInfo;
+ }
+ if(connectionClosed) {
+ throw tm.getReasonClosedCause();
+ }
+ try {
+ accessLock.wait();
+ }
+ catch(InterruptedException e) {
+ throw new InterruptedIOException(e.getMessage());
+ }
+ }
+ }
+ }
+
+ private String getFirstMatch(String[] client, String[] server) throws NegotiateException {
+ if(client == null || server == null) {
+ throw new IllegalArgumentException();
+ }
+ for(String c : client) {
+ for(String s : server) {
+ if(c.equals(s)) {
+ return c;
+ }
+ }
+ }
+ throw new NegotiateException(String.format("Negotiation failed for %s", Arrays.toString(server)));
+ }
+
+ private boolean compareFirstOfNameList(String[] a, String[] b) {
+ if(a == null || b == null) {
+ throw new IllegalArgumentException();
+ }
+ if((a.length == 0) && (b.length == 0)) {
+ return true;
+ }
+ if((a.length == 0) || (b.length == 0)) {
+ return false;
+ }
+ return (a[0].equals(b[0]));
+ }
+
+ private boolean isGuessOK(KexParameters cpar, KexParameters spar) {
+ if(cpar == null || spar == null) {
+ throw new IllegalArgumentException();
+ }
+ if(!compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms)) {
+ return false;
+ }
+ if(!compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms)) {
+ return false;
+ }
+
+ /*
+ * We do NOT check here if the other algorithms can be agreed on, this
+ * is just a check if kex_algorithms and server_host_key_algorithms were
+ * guessed right!
+ */
+
+ return true;
+ }
+
+ protected NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server)
+ throws NegotiateException {
+ NegotiatedParameters np = new NegotiatedParameters();
+
+ np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms);
+
+ log.info("kex_algo=" + np.kex_algo);
+
+ np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms,
+ server.server_host_key_algorithms);
+
+ log.info("server_host_key_algo=" + np.server_host_key_algo);
+
+ np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server,
+ server.encryption_algorithms_client_to_server);
+ np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client,
+ server.encryption_algorithms_server_to_client);
+
+ log.info("enc_algo_client_to_server=" + np.enc_algo_client_to_server);
+ log.info("enc_algo_server_to_client=" + np.enc_algo_server_to_client);
+
+ np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server,
+ server.mac_algorithms_client_to_server);
+ np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client,
+ server.mac_algorithms_server_to_client);
+
+ log.info("mac_algo_client_to_server=" + np.mac_algo_client_to_server);
+ log.info("mac_algo_server_to_client=" + np.mac_algo_server_to_client);
+
+ np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server,
+ server.compression_algorithms_client_to_server);
+ np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client,
+ server.compression_algorithms_server_to_client);
+
+ log.info("comp_algo_client_to_server=" + np.comp_algo_client_to_server);
+ log.info("comp_algo_server_to_client=" + np.comp_algo_server_to_client);
+
+ np.lang_client_to_server = getFirstMatch(client.languages_client_to_server,
+ server.languages_client_to_server);
+
+ np.lang_server_to_client = getFirstMatch(client.languages_server_to_client,
+ server.languages_server_to_client);
+
+ if(isGuessOK(client, server)) {
+ np.guessOK = true;
+ }
+ return np;
+ }
+
+ public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex, DSAPrivateKey dsa, RSAPrivateKey rsa)
+ throws IOException {
+ nextKEXcryptoWishList = cwl;
+ nextKEXdhgexParameters = dhgex;
+ nextKEXdsakey = dsa;
+ nextKEXrsakey = rsa;
+
+ if(kxs == null) {
+ kxs = new KexState();
+ kxs.local_dsa_key = dsa;
+ kxs.local_rsa_key = rsa;
+ kxs.dhgexParameters = nextKEXdhgexParameters;
+ kxs.localKEX = new PacketKexInit(nextKEXcryptoWishList, rnd);
+ tm.sendKexMessage(kxs.localKEX.getPayload());
+ }
+ }
+
+ private boolean establishKeyMaterial() throws IOException {
+ try {
+ int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server);
+ int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server);
+ int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server);
+
+ int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client);
+ int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client);
+ int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client);
+
+ km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len,
+ enc_sc_key_len, enc_sc_block_len, mac_sc_key_len);
+ }
+ catch(IllegalArgumentException e) {
+ return false;
+ }
+ return true;
+ }
+
+ protected void finishKex(boolean clientMode) throws IOException {
+ if(sessionId == null) {
+ sessionId = kxs.H;
+ }
+
+ establishKeyMaterial();
+
+ /* Tell the other side that we start using the new material */
+
+ PacketNewKeys ign = new PacketNewKeys();
+ tm.sendKexMessage(ign.getPayload());
+
+ BlockCipher cbc;
+ MAC mac;
+ Compressor comp;
+
+ try {
+ cbc = BlockCipherFactory.createCipher(clientMode ? kxs.np.enc_algo_client_to_server
+ : kxs.np.enc_algo_server_to_client, true, clientMode ? km.enc_key_client_to_server
+ : km.enc_key_server_to_client, clientMode ? km.initial_iv_client_to_server
+ : km.initial_iv_server_to_client);
+
+ try {
+ mac = new MAC(clientMode ? kxs.np.mac_algo_client_to_server : kxs.np.mac_algo_server_to_client, clientMode
+ ? km.integrity_key_client_to_server : km.integrity_key_server_to_client);
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+
+ comp = CompressionFactory.createCompressor(kxs.np.comp_algo_client_to_server);
+ }
+ catch(IllegalArgumentException f) {
+ throw new IOException(String.format("Fatal error initializing ciphers. %s", f.getMessage()));
+ }
+
+ tm.changeSendCipher(cbc, mac);
+ tm.changeSendCompression(comp);
+ tm.kexFinished();
+ }
+
+ public static String[] getDefaultServerHostkeyAlgorithmList() {
+ return new String[]{"ssh-rsa", "ssh-dss"};
+ }
+
+ public static void checkServerHostkeyAlgorithmsList(String[] algos) {
+ for(final String algo : algos) {
+ if("ssh-rsa".equals(algo)) {
+ continue;
+ }
+ if("ssh-dss".equals(algo)) {
+ continue;
+ }
+ throw new IllegalArgumentException(String.format("Unknown server host key algorithm %s", algo));
+ }
+ }
+
+ public static String[] getDefaultClientKexAlgorithmList() {
+ return new String[]{"diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1",
+ "diffie-hellman-group1-sha1"};
+ }
+
+ public static String[] getDefaultServerKexAlgorithmList() {
+ return new String[]{"diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1"};
+ }
+
+ public static void checkKexAlgorithmList(String[] algos) {
+ for(final String algo : algos) {
+ if("diffie-hellman-group-exchange-sha1".equals(algo)) {
+ continue;
+ }
+ if("diffie-hellman-group14-sha1".equals(algo)) {
+ continue;
+ }
+ if("diffie-hellman-group1-sha1".equals(algo)) {
+ continue;
+ }
+ throw new IllegalArgumentException(String.format("Unknown kex algorithm %s", algo));
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/KexParameters.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/KexParameters.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.util.Arrays;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: KexParameters.java 159 2014-05-01 14:12:34Z dkocher@sudo.ch $
+ */
+public class KexParameters
+{
+ public byte[] cookie;
+ public String[] kex_algorithms;
+ public String[] server_host_key_algorithms;
+ public String[] encryption_algorithms_client_to_server;
+ public String[] encryption_algorithms_server_to_client;
+ public String[] mac_algorithms_client_to_server;
+ public String[] mac_algorithms_server_to_client;
+ public String[] compression_algorithms_client_to_server;
+ public String[] compression_algorithms_server_to_client;
+ public String[] languages_client_to_server;
+ public String[] languages_server_to_client;
+ public boolean first_kex_packet_follows;
+ public int reserved_field1;
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("KexParameters{");
+ sb.append("cookie=").append(Arrays.toString(cookie));
+ sb.append(", kex_algorithms=").append(Arrays.toString(kex_algorithms));
+ sb.append(", server_host_key_algorithms=").append(Arrays.toString(server_host_key_algorithms));
+ sb.append(", encryption_algorithms_client_to_server=").append(Arrays.toString(encryption_algorithms_client_to_server));
+ sb.append(", encryption_algorithms_server_to_client=").append(Arrays.toString(encryption_algorithms_server_to_client));
+ sb.append(", mac_algorithms_client_to_server=").append(Arrays.toString(mac_algorithms_client_to_server));
+ sb.append(", mac_algorithms_server_to_client=").append(Arrays.toString(mac_algorithms_server_to_client));
+ sb.append(", compression_algorithms_client_to_server=").append(Arrays.toString(compression_algorithms_client_to_server));
+ sb.append(", compression_algorithms_server_to_client=").append(Arrays.toString(compression_algorithms_server_to_client));
+ sb.append(", languages_client_to_server=").append(Arrays.toString(languages_client_to_server));
+ sb.append(", languages_server_to_client=").append(Arrays.toString(languages_server_to_client));
+ sb.append(", first_kex_packet_follows=").append(first_kex_packet_follows);
+ sb.append(", reserved_field1=").append(reserved_field1);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/KexState.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/KexState.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import ch.ethz.ssh2.DHGexParameters;
+import ch.ethz.ssh2.crypto.dh.DhExchange;
+import ch.ethz.ssh2.crypto.dh.DhGroupExchange;
+import java.math.BigInteger;
+import ch.ethz.ssh2.packets.PacketKexInit;
+import ch.ethz.ssh2.signature.DSAPrivateKey;
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+
+/**
+ * KexState.
+ *
+ * @author Christian Plattner
+ * @version 2.50, 03/15/10
+ */
+public class KexState
+{
+ public PacketKexInit localKEX;
+ public PacketKexInit remoteKEX;
+ public NegotiatedParameters np;
+ public int state = 0;
+
+ public BigInteger K;
+ public byte[] H;
+
+ public byte[] remote_hostkey;
+
+ public DhExchange dhx;
+ public DhGroupExchange dhgx;
+ public DHGexParameters dhgexParameters;
+
+ public DSAPrivateKey local_dsa_key;
+ public RSAPrivateKey local_rsa_key;
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/MessageHandler.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/MessageHandler.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: MessageHandler.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public interface MessageHandler {
+ public void handleMessage(byte[] msg) throws IOException;
+
+ public void handleFailure(IOException failure);
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/NegotiateException.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/NegotiateException.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+
+/**
+ * @version $Id: NegotiateException.java 149 2014-04-28 09:18:35Z dkocher@sudo.ch $
+ */
+public class NegotiateException extends IOException {
+ private static final long serialVersionUID = 3689910669428143157L;
+
+ public NegotiateException() {
+ //
+ }
+
+ public NegotiateException(String message) {
+ super(message);
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/NegotiatedParameters.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/NegotiatedParameters.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: NegotiatedParameters.java 159 2014-05-01 14:12:34Z dkocher@sudo.ch $
+ */
+public class NegotiatedParameters
+{
+ public boolean guessOK;
+ public String kex_algo;
+ public String server_host_key_algo;
+ public String enc_algo_client_to_server;
+ public String enc_algo_server_to_client;
+ public String mac_algo_client_to_server;
+ public String mac_algo_server_to_client;
+ public String comp_algo_client_to_server;
+ public String comp_algo_server_to_client;
+ public String lang_client_to_server;
+ public String lang_server_to_client;
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("NegotiatedParameters{");
+ sb.append("guessOK=").append(guessOK);
+ sb.append(", kex_algo='").append(kex_algo).append('\'');
+ sb.append(", server_host_key_algo='").append(server_host_key_algo).append('\'');
+ sb.append(", enc_algo_client_to_server='").append(enc_algo_client_to_server).append('\'');
+ sb.append(", enc_algo_server_to_client='").append(enc_algo_server_to_client).append('\'');
+ sb.append(", mac_algo_client_to_server='").append(mac_algo_client_to_server).append('\'');
+ sb.append(", mac_algo_server_to_client='").append(mac_algo_server_to_client).append('\'');
+ sb.append(", comp_algo_client_to_server='").append(comp_algo_client_to_server).append('\'');
+ sb.append(", comp_algo_server_to_client='").append(comp_algo_server_to_client).append('\'');
+ sb.append(", lang_client_to_server='").append(lang_client_to_server).append('\'');
+ sb.append(", lang_server_to_client='").append(lang_server_to_client).append('\'');
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/ServerKexManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/ServerKexManager.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.security.DigestException;
+
+import ch.ethz.ssh2.ConnectionInfo;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.auth.ServerAuthenticationManager;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
+import ch.ethz.ssh2.crypto.dh.DhExchange;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.packets.PacketKexDHInit;
+import ch.ethz.ssh2.packets.PacketKexDHReply;
+import ch.ethz.ssh2.packets.PacketKexInit;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.server.ServerConnectionState;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.DSASignature;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.signature.RSASignature;
+
+/**
+ * @version $Id: ServerKexManager.java 160 2014-05-01 14:30:26Z dkocher@sudo.ch $
+ */
+public class ServerKexManager extends KexManager {
+
+ private final ServerConnectionState state;
+
+ private boolean authenticationStarted = false;
+
+ public ServerKexManager(ServerConnectionState state) {
+ super(state.tm, state.csh, state.next_cryptoWishList, state.generator);
+ this.state = state;
+ }
+
+ @Override
+ public void handleFailure(final IOException failure) {
+ synchronized(accessLock) {
+ connectionClosed = true;
+ accessLock.notifyAll();
+ }
+ }
+
+ @Override
+ public void handleMessage(byte[] msg) throws IOException {
+ PacketKexInit kip;
+
+ if((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT)) {
+ throw new PacketTypeException(msg[0]);
+ }
+
+ if(ignore_next_kex_packet) {
+ ignore_next_kex_packet = false;
+ return;
+ }
+
+ if(msg[0] == Packets.SSH_MSG_KEXINIT) {
+ if((kxs != null) && (kxs.state != 0)) {
+ throw new PacketTypeException(msg[0]);
+ }
+
+ if(kxs == null) {
+ /*
+ * Ah, OK, peer wants to do KEX. Let's be nice and play
+ * together.
+ */
+ kxs = new KexState();
+ kxs.local_dsa_key = nextKEXdsakey;
+ kxs.local_rsa_key = nextKEXrsakey;
+ kxs.dhgexParameters = nextKEXdhgexParameters;
+ kip = new PacketKexInit(nextKEXcryptoWishList, rnd);
+ kxs.localKEX = kip;
+ tm.sendKexMessage(kip.getPayload());
+ }
+
+ kip = new PacketKexInit(msg);
+ kxs.remoteKEX = kip;
+
+ kxs.np = mergeKexParameters(kxs.remoteKEX.getKexParameters(), kxs.localKEX.getKexParameters());
+
+ if(kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false)) {
+ // Guess was wrong, we need to ignore the next kex packet.
+ ignore_next_kex_packet = true;
+ }
+
+ if(kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
+ || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")) {
+ kxs.dhx = new DhExchange();
+
+ if(kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")) {
+ kxs.dhx.serverInit(1, rnd);
+ }
+ else {
+ kxs.dhx.serverInit(14, rnd);
+ }
+
+ kxs.state = 1;
+ return;
+ }
+
+ throw new IllegalStateException("Unkown KEX method!");
+ }
+
+ if(msg[0] == Packets.SSH_MSG_NEWKEYS) {
+ if(km == null) {
+ throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!");
+ }
+
+ BlockCipher cbc;
+ MAC mac;
+
+ try {
+ cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, false,
+ km.enc_key_client_to_server, km.initial_iv_client_to_server);
+
+ try {
+ mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server);
+ }
+ catch(DigestException e) {
+ throw new IOException(e);
+ }
+
+ }
+ catch(IllegalArgumentException e) {
+ throw new IOException(e);
+ }
+
+ tm.changeRecvCipher(cbc, mac);
+
+ ConnectionInfo sci = new ConnectionInfo();
+
+ kexCount++;
+
+ sci.keyExchangeAlgorithm = kxs.np.kex_algo;
+ sci.keyExchangeCounter = kexCount;
+ sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server;
+ sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client;
+ sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server;
+ sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client;
+ sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo;
+ sci.serverHostKey = kxs.remote_hostkey;
+
+ synchronized(accessLock) {
+ lastConnInfo = sci;
+ accessLock.notifyAll();
+ }
+
+ kxs = null;
+ return;
+ }
+
+ if((kxs == null) || (kxs.state == 0)) {
+ throw new IOException("Unexpected Kex submessage!");
+ }
+
+ if(kxs.np.kex_algo.equals("diffie-hellman-group1-sha1")
+ || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1")) {
+ if(kxs.state == 1) {
+ PacketKexDHInit dhi = new PacketKexDHInit(msg);
+
+ kxs.dhx.setE(dhi.getE());
+
+ byte[] hostKey = null;
+
+ if(kxs.np.server_host_key_algo.equals("ssh-rsa")) {
+ hostKey = RSASHA1Verify.encodeSSHRSAPublicKey(kxs.local_rsa_key.getPublicKey());
+ }
+
+ if(kxs.np.server_host_key_algo.equals("ssh-dss")) {
+ hostKey = DSASHA1Verify.encodeSSHDSAPublicKey(kxs.local_dsa_key.getPublicKey());
+ }
+
+ try {
+ kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(),
+ kxs.remoteKEX.getPayload(), kxs.localKEX.getPayload(), hostKey);
+ }
+ catch(IllegalArgumentException e) {
+ throw new IOException("KEX error.", e);
+ }
+
+ kxs.K = kxs.dhx.getK();
+
+ byte[] signature = null;
+
+ if(kxs.np.server_host_key_algo.equals("ssh-rsa")) {
+ RSASignature rs = RSASHA1Verify.generateSignature(kxs.H, kxs.local_rsa_key);
+ signature = RSASHA1Verify.encodeSSHRSASignature(rs);
+ }
+
+ if(kxs.np.server_host_key_algo.equals("ssh-dss")) {
+ DSASignature ds = DSASHA1Verify.generateSignature(kxs.H, kxs.local_dsa_key, rnd);
+ signature = DSASHA1Verify.encodeSSHDSASignature(ds);
+ }
+
+ PacketKexDHReply dhr = new PacketKexDHReply(hostKey, kxs.dhx.getF(), signature);
+ tm.sendKexMessage(dhr.getPayload());
+
+ finishKex(false);
+ kxs.state = -1;
+
+ if(authenticationStarted == false) {
+ authenticationStarted = true;
+ state.am = new ServerAuthenticationManager(state);
+ }
+
+ return;
+ }
+ }
+ throw new IllegalStateException(String.format("Unknown KEX method %s", kxs.np.kex_algo));
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/ServerTransportManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/ServerTransportManager.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,42 @@
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.net.Socket;
+
+import ch.ethz.ssh2.server.ServerConnectionState;
+
+/**
+ * @version $Id: ServerTransportManager.java 151 2014-04-28 10:03:39Z dkocher@sudo.ch $
+ */
+public class ServerTransportManager extends TransportManager
+{
+
+ private final Socket sock;
+
+ public ServerTransportManager(final Socket socket)
+ {
+ super(socket);
+ // TCP connection is already established
+ this.sock = socket;
+ }
+
+ public void connect(ServerConnectionState state) throws IOException
+ {
+ /* Parse the client lin
+ e and say hello - important: this information is later needed for the
+ * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object
+ * for later use.
+ */
+
+ state.csh = ClientServerHello.serverHello(state.softwareversion, sock.getInputStream(), sock.getOutputStream());
+
+ TransportConnection tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), state.generator);
+ KexManager km = new ServerKexManager(state);
+
+ super.init(tc, km);
+
+ km.initiateKEX(state.next_cryptoWishList, null, state.next_dsa_key, state.next_rsa_key);
+
+ this.startReceiver();
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/TransportConnection.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/TransportConnection.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import ch.ethz.ssh2.PacketFormatException;
+import ch.ethz.ssh2.compression.Compressor;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.cipher.CipherInputStream;
+import ch.ethz.ssh2.crypto.cipher.CipherOutputStream;
+import ch.ethz.ssh2.crypto.cipher.NullCipher;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.Packets;
+
+/**
+ * TransportConnection.
+ *
+ * @author Christian Plattner
+ * @version $Id: TransportConnection.java 144 2014-04-25 12:48:25Z dkocher@sudo.ch $
+ */
+public class TransportConnection {
+ private static final Logger log = Logger.getLogger(TransportConnection.class);
+
+ int send_seq_number = 0;
+
+ int recv_seq_number = 0;
+
+ CipherInputStream cis;
+
+ CipherOutputStream cos;
+
+ boolean useRandomPadding;
+
+ /* Depends on current MAC and CIPHER */
+
+ MAC send_mac;
+
+ byte[] send_mac_buffer;
+
+ int send_padd_blocksize = 8;
+
+ MAC recv_mac;
+
+ byte[] recv_mac_buffer;
+
+ byte[] recv_mac_buffer_cmp;
+
+ int recv_padd_blocksize = 8;
+
+ Compressor recv_comp;
+
+ Compressor send_comp;
+
+ boolean can_compress;
+
+ byte[] recv_comp_buffer;
+
+ byte[] send_comp_buffer;
+
+ /* won't change */
+
+ final byte[] send_padding_buffer = new byte[256];
+
+ final byte[] send_packet_header_buffer = new byte[5];
+
+ final byte[] recv_padding_buffer = new byte[256];
+
+ final byte[] recv_packet_header_buffer = new byte[5];
+
+ boolean recv_packet_header_present = false;
+
+ ClientServerHello csh;
+
+ final SecureRandom rnd;
+
+ public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd) {
+ this.cis = new CipherInputStream(new NullCipher(), is);
+ this.cos = new CipherOutputStream(new NullCipher(), os);
+ this.rnd = rnd;
+ }
+
+ public void changeRecvCipher(BlockCipher bc, MAC mac) {
+ cis.changeCipher(bc);
+ recv_mac = mac;
+ recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
+ recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
+ recv_padd_blocksize = bc.getBlockSize();
+ if(recv_padd_blocksize < 8) {
+ recv_padd_blocksize = 8;
+ }
+ }
+
+ public void changeSendCipher(BlockCipher bc, MAC mac) {
+ if((bc instanceof NullCipher) == false) {
+ /* Only use zero byte padding for the first few packets */
+ useRandomPadding = true;
+ /* Once we start encrypting, there is no way back */
+ }
+
+ cos.changeCipher(bc);
+ send_mac = mac;
+ send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
+ send_padd_blocksize = bc.getBlockSize();
+ if(send_padd_blocksize < 8) {
+ send_padd_blocksize = 8;
+ }
+ }
+
+ public void changeRecvCompression(Compressor comp) {
+ recv_comp = comp;
+
+ if(comp != null) {
+ recv_comp_buffer = new byte[comp.getBufferSize()];
+ }
+ }
+
+ public void changeSendCompression(Compressor comp) {
+ send_comp = comp;
+
+ if(comp != null) {
+ send_comp_buffer = new byte[comp.getBufferSize()];
+ }
+ }
+
+ public void sendMessage(byte[] message) throws IOException {
+ sendMessage(message, 0, message.length, 0);
+ }
+
+ public void sendMessage(byte[] message, int off, int len) throws IOException {
+ sendMessage(message, off, len, 0);
+ }
+
+ public int getPacketOverheadEstimate() {
+ // return an estimate for the paket overhead (for send operations)
+ return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
+ }
+
+ public void sendMessage(byte[] message, int off, int len, int padd) throws IOException {
+ if(padd < 4) {
+ padd = 4;
+ }
+ else if(padd > 64) {
+ padd = 64;
+ }
+
+ if(send_comp != null && can_compress) {
+ len = send_comp.compress(message, off, len, send_comp_buffer);
+ message = send_comp_buffer;
+ }
+
+ int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
+
+ int slack = packet_len % send_padd_blocksize;
+
+ if(slack != 0) {
+ packet_len += (send_padd_blocksize - slack);
+ }
+
+ if(packet_len < 16) {
+ packet_len = 16;
+ }
+
+ int padd_len = packet_len - (5 + len);
+
+ if(useRandomPadding) {
+ for(int i = 0; i < padd_len; i = i + 4) {
+ /*
+ * don't waste calls to rnd.nextInt() (by using only 8bit of the
+ * output). just believe me: even though we may write here up to 3
+ * bytes which won't be used, there is no "buffer overflow" (i.e.,
+ * arrayindexoutofbounds). the padding buffer is big enough =) (256
+ * bytes, and that is bigger than any current cipher block size + 64).
+ */
+
+ int r = rnd.nextInt();
+ send_padding_buffer[i] = (byte) r;
+ send_padding_buffer[i + 1] = (byte) (r >> 8);
+ send_padding_buffer[i + 2] = (byte) (r >> 16);
+ send_padding_buffer[i + 3] = (byte) (r >> 24);
+ }
+ }
+ else {
+ /* use zero padding for unencrypted traffic */
+ for(int i = 0; i < padd_len; i++) {
+ send_padding_buffer[i] = 0;
+ }
+ /* Actually this code is paranoid: we never filled any
+ * bytes into the padding buffer so far, therefore it should
+ * consist of zeros only.
+ */
+ }
+
+ send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24);
+ send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16);
+ send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8);
+ send_packet_header_buffer[3] = (byte) ((packet_len - 4));
+ send_packet_header_buffer[4] = (byte) padd_len;
+
+ cos.write(send_packet_header_buffer, 0, 5);
+ cos.write(message, off, len);
+ cos.write(send_padding_buffer, 0, padd_len);
+
+ if(send_mac != null) {
+ send_mac.initMac(send_seq_number);
+ send_mac.update(send_packet_header_buffer, 0, 5);
+ send_mac.update(message, off, len);
+ send_mac.update(send_padding_buffer, 0, padd_len);
+
+ send_mac.getMac(send_mac_buffer, 0);
+ cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
+ }
+
+ cos.flush();
+
+ if(log.isDebugEnabled()) {
+ log.debug("Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
+ }
+
+ send_seq_number++;
+ }
+
+ public int peekNextMessageLength() throws IOException {
+ if(recv_packet_header_present == false) {
+ cis.read(recv_packet_header_buffer, 0, 5);
+ recv_packet_header_present = true;
+ }
+
+ int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
+ | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
+ | ((recv_packet_header_buffer[3] & 0xff));
+
+ int padding_length = recv_packet_header_buffer[4] & 0xff;
+
+ if(packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12) {
+ throw new PacketFormatException(String.format("Illegal packet size (%d)", packet_length));
+ }
+
+ int payload_length = packet_length - padding_length - 1;
+
+ if(payload_length < 0) {
+ throw new PacketFormatException(String.format("Illegal padding_length in packet from remote (%d)", padding_length));
+ }
+
+ return payload_length;
+ }
+
+ public int receiveMessage(byte buffer[], int off, int len) throws IOException {
+ if(recv_packet_header_present == false) {
+ cis.read(recv_packet_header_buffer, 0, 5);
+ }
+ else {
+ recv_packet_header_present = false;
+ }
+
+ int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
+ | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
+ | ((recv_packet_header_buffer[3] & 0xff));
+
+ int padding_length = recv_packet_header_buffer[4] & 0xff;
+
+ if(packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12) {
+ throw new PacketFormatException(String.format("Illegal packet size (%d)", packet_length));
+ }
+
+ int payload_length = packet_length - padding_length - 1;
+
+ if(payload_length < 0) {
+ throw new PacketFormatException(String.format("Illegal padding_length in packet from remote (%d)", padding_length));
+ }
+
+ if(payload_length >= len) {
+ throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");
+ }
+
+ cis.read(buffer, off, payload_length);
+ cis.read(recv_padding_buffer, 0, padding_length);
+
+ if(recv_mac != null) {
+ cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
+
+ recv_mac.initMac(recv_seq_number);
+ recv_mac.update(recv_packet_header_buffer, 0, 5);
+ recv_mac.update(buffer, off, payload_length);
+ recv_mac.update(recv_padding_buffer, 0, padding_length);
+ recv_mac.getMac(recv_mac_buffer_cmp, 0);
+
+ for(int i = 0; i < recv_mac_buffer.length; i++) {
+ if(recv_mac_buffer[i] != recv_mac_buffer_cmp[i]) {
+ throw new IOException("Remote sent corrupt MAC.");
+ }
+ }
+ }
+
+ recv_seq_number++;
+
+ if(log.isDebugEnabled()) {
+ log.debug("Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
+ + " bytes payload");
+ }
+
+ if(recv_comp != null && can_compress) {
+ int[] uncomp_len = new int[]{payload_length};
+ buffer = recv_comp.uncompress(buffer, off, uncomp_len);
+ return uncomp_len[0];
+ }
+ else {
+ return payload_length;
+ }
+ }
+
+ public void startCompression() {
+ can_compress = true;
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/transport/TransportManager.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/transport/TransportManager.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2006-2013 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.transport;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+
+import ch.ethz.ssh2.ConnectionInfo;
+import ch.ethz.ssh2.ConnectionMonitor;
+import ch.ethz.ssh2.DHGexParameters;
+import ch.ethz.ssh2.PacketTypeException;
+import ch.ethz.ssh2.compression.Compressor;
+import ch.ethz.ssh2.crypto.CryptoWishList;
+import ch.ethz.ssh2.crypto.cipher.BlockCipher;
+import ch.ethz.ssh2.crypto.digest.MAC;
+import ch.ethz.ssh2.log.Logger;
+import ch.ethz.ssh2.packets.PacketDisconnect;
+import ch.ethz.ssh2.packets.Packets;
+import ch.ethz.ssh2.packets.TypesReader;
+import ch.ethz.ssh2.signature.DSAPrivateKey;
+import ch.ethz.ssh2.signature.RSAPrivateKey;
+
+/**
+ * Yes, the "standard" is a big mess. On one side, the say that arbitrary channel
+ * packets are allowed during kex exchange, on the other side we need to blindly
+ * ignore the next _packet_ if the KEX guess was wrong. Where do we know from that
+ * the next packet is not a channel data packet? Yes, we could check if it is in
+ * the KEX range. But the standard says nothing about this. The OpenSSH guys
+ * block local "normal" traffic during KEX. That's fine - however, they assume
+ * that the other side is doing the same. During re-key, if they receive traffic
+ * other than KEX, they become horribly irritated and kill the connection. Since
+ * we are very likely going to communicate with OpenSSH servers, we have to play
+ * the same game - even though we could do better.
+ *
+ * @author Christian Plattner
+ * @version $Id: TransportManager.java 161 2014-05-01 18:01:55Z dkocher@sudo.ch $
+ */
+public abstract class TransportManager {
+ private static final Logger log = Logger.getLogger(TransportManager.class);
+
+ private static final class HandlerEntry {
+ MessageHandler mh;
+ int low;
+ int high;
+ }
+
+ /**
+ * Advertised maximum SSH packet size that the other side can send to us.
+ */
+ public static final int MAX_PACKET_SIZE = 64 * 1024;
+
+ private final List asynchronousQueue
+ = new ArrayList();
+
+ private Thread asynchronousThread = null;
+ private boolean asynchronousPending = false;
+
+ private Socket socket;
+
+ protected TransportManager(final Socket socket) {
+ this.socket = socket;
+ }
+
+ private static final class AsynchronousEntry {
+ public byte[] message;
+
+ public AsynchronousEntry(byte[] message) {
+ this.message = message;
+ }
+ }
+
+ private final class AsynchronousWorker implements Runnable {
+ @Override
+ public void run() {
+ while(true) {
+ final AsynchronousEntry item;
+ synchronized(asynchronousQueue) {
+ if(asynchronousQueue.size() == 0) {
+ // Only now we may reset the flag, since we are sure that all queued items
+ // have been sent (there is a slight delay between de-queuing and sending,
+ // this is why we need this flag! See code below. Sending takes place outside
+ // of this lock, this is why a test for size()==0 (from another thread) does not ensure
+ // that all messages have been sent.
+
+ asynchronousPending = false;
+
+ // Notify any senders that they can proceed, all async messages have been delivered
+
+ asynchronousQueue.notifyAll();
+
+ // After the queue is empty for about 2 seconds, stop this thread
+ try {
+ asynchronousQueue.wait(2000);
+ }
+ catch(InterruptedException ignore) {
+ //
+ }
+ if(asynchronousQueue.size() == 0) {
+ asynchronousThread = null;
+ return;
+ }
+ }
+ item = asynchronousQueue.remove(0);
+ }
+ try {
+ sendMessageImmediate(item.message);
+ }
+ catch(IOException e) {
+ // There is no point in handling it - it simply means that the connection has a problem and we should stop
+ // sending asynchronously messages. We do not need to signal that we have exited (asynchronousThread = null):
+ // further messages in the queue cannot be sent by this or any other thread.
+ // Other threads will sooner or later (when receiving or sending the next message) get the
+ // same IOException and get to the same conclusion.
+ log.warning(e.getMessage());
+ return;
+ }
+ }
+ }
+ }
+
+ private final Object connectionSemaphore = new Object();
+
+ private boolean flagKexOngoing;
+
+ private boolean connectionClosed;
+ private IOException reasonClosedCause;
+
+ private TransportConnection tc;
+ private KexManager km;
+
+ private final List messageHandlers
+ = new ArrayList();
+
+ private List connectionMonitors
+ = new ArrayList();
+
+ protected void init(TransportConnection tc, KexManager km) {
+ this.tc = tc;
+ this.km = km;
+ }
+
+ public int getPacketOverheadEstimate() {
+ return tc.getPacketOverheadEstimate();
+ }
+
+ public ConnectionInfo getConnectionInfo(int kexNumber) throws IOException {
+ return km.getOrWaitForConnectionInfo(kexNumber);
+ }
+
+ public IOException getReasonClosedCause() {
+ synchronized(connectionSemaphore) {
+ return reasonClosedCause;
+ }
+ }
+
+ public byte[] getSessionIdentifier() {
+ return km.sessionId;
+ }
+
+ public void close() {
+ // It is safe now to acquire the semaphore.
+ synchronized(connectionSemaphore) {
+ if(!connectionClosed) {
+ try {
+ tc.sendMessage(new PacketDisconnect(
+ PacketDisconnect.Reason.SSH_DISCONNECT_BY_APPLICATION, "").getPayload());
+ }
+ catch(IOException ignore) {
+ //
+ }
+ try {
+ socket.close();
+ }
+ catch(IOException ignore) {
+ //
+ }
+ connectionClosed = true;
+ synchronized(this) {
+ for(ConnectionMonitor cmon : connectionMonitors) {
+ cmon.connectionLost(reasonClosedCause);
+ }
+ }
+ }
+ connectionSemaphore.notifyAll();
+ }
+ }
+
+ public void close(IOException cause) {
+ // Do not acquire the semaphore, perhaps somebody is inside (and waits until
+ // the remote side is ready to accept new data
+ try {
+ socket.close();
+ }
+ catch(IOException ignore) {
+ }
+ // It is safe now to acquire the semaphore.
+ synchronized(connectionSemaphore) {
+ connectionClosed = true;
+ reasonClosedCause = cause;
+ connectionSemaphore.notifyAll();
+ }
+ synchronized(this) {
+ for(ConnectionMonitor cmon : connectionMonitors) {
+ cmon.connectionLost(reasonClosedCause);
+ }
+ }
+ }
+
+ protected void startReceiver() throws IOException {
+ final Thread receiveThread = new Thread(new Runnable() {
+ public void run() {
+ try {
+ receiveLoop();
+ // Can only exit with exception
+ }
+ catch(IOException e) {
+ close(e);
+ log.warning(e.getMessage());
+ // Tell all handlers that it is time to say goodbye
+ if(km != null) {
+ km.handleFailure(e);
+ }
+ for(HandlerEntry he : messageHandlers) {
+ he.mh.handleFailure(e);
+ }
+ }
+ if(log.isDebugEnabled()) {
+ log.debug("Receive thread: back from receiveLoop");
+ }
+ }
+ });
+ receiveThread.setName("Transport Manager");
+ receiveThread.setDaemon(true);
+ receiveThread.start();
+ }
+
+ public void registerMessageHandler(MessageHandler mh, int low, int high) {
+ HandlerEntry he = new HandlerEntry();
+ he.mh = mh;
+ he.low = low;
+ he.high = high;
+
+ synchronized(messageHandlers) {
+ messageHandlers.add(he);
+ }
+ }
+
+ public void removeMessageHandler(MessageHandler handler) {
+ synchronized(messageHandlers) {
+ for(int i = 0; i < messageHandlers.size(); i++) {
+ HandlerEntry he = messageHandlers.get(i);
+ if(he.mh == handler) {
+ messageHandlers.remove(i);
+ break;
+ }
+ }
+ }
+ }
+
+ public void sendKexMessage(byte[] msg) throws IOException {
+ synchronized(connectionSemaphore) {
+ if(connectionClosed) {
+ throw reasonClosedCause;
+ }
+ flagKexOngoing = true;
+ try {
+ tc.sendMessage(msg);
+ }
+ catch(IOException e) {
+ close(e);
+ throw e;
+ }
+ }
+ }
+
+ public void kexFinished() throws IOException {
+ synchronized(connectionSemaphore) {
+ flagKexOngoing = false;
+ connectionSemaphore.notifyAll();
+ }
+ }
+
+ /**
+ * @param cwl Crypto wishlist
+ * @param dhgex Diffie-hellman group exchange
+ * @param dsa may be null if this is a client connection
+ * @param rsa may be null if this is a client connection
+ * @throws IOException
+ */
+ public void forceKeyExchange(CryptoWishList cwl, DHGexParameters dhgex, DSAPrivateKey dsa, RSAPrivateKey rsa)
+ throws IOException {
+ synchronized(connectionSemaphore) {
+ if(connectionClosed) {
+ // Inform the caller that there is no point in triggering a new kex
+ throw reasonClosedCause;
+ }
+ }
+ km.initiateKEX(cwl, dhgex, dsa, rsa);
+ }
+
+ public void changeRecvCipher(BlockCipher bc, MAC mac) {
+ tc.changeRecvCipher(bc, mac);
+ }
+
+ public void changeSendCipher(BlockCipher bc, MAC mac) {
+ tc.changeSendCipher(bc, mac);
+ }
+
+ public void changeRecvCompression(Compressor comp) {
+ tc.changeRecvCompression(comp);
+ }
+
+ public void changeSendCompression(Compressor comp) {
+ tc.changeSendCompression(comp);
+ }
+
+ public void sendAsynchronousMessage(byte[] msg) throws IOException {
+ synchronized(asynchronousQueue) {
+ asynchronousQueue.add(new AsynchronousEntry(msg));
+ asynchronousPending = true;
+
+ /* This limit should be flexible enough. We need this, otherwise the peer
+ * can flood us with global requests (and other stuff where we have to reply
+ * with an asynchronous message) and (if the server just sends data and does not
+ * read what we send) this will probably put us in a low memory situation
+ * (our send queue would grow and grow and...) */
+
+ if(asynchronousQueue.size() > 100) {
+ throw new IOException("The peer is not consuming our asynchronous replies.");
+ }
+
+ // Check if we have an asynchronous sending thread
+ if(asynchronousThread == null) {
+ asynchronousThread = new Thread(new AsynchronousWorker());
+ asynchronousThread.setDaemon(true);
+ asynchronousThread.start();
+ // The thread will stop after 2 seconds of inactivity (i.e., empty queue)
+ }
+ asynchronousQueue.notifyAll();
+ }
+ }
+
+ public void setConnectionMonitors(List monitors) {
+ synchronized(this) {
+ connectionMonitors = new ArrayList();
+ connectionMonitors.addAll(monitors);
+ }
+ }
+
+ /**
+ * Send a message but ensure that all queued messages are being sent first.
+ *
+ * @param msg Message
+ * @throws IOException
+ */
+ public void sendMessage(byte[] msg) throws IOException {
+ synchronized(asynchronousQueue) {
+ while(asynchronousPending) {
+ try {
+ asynchronousQueue.wait();
+ }
+ catch(InterruptedException e) {
+ throw new InterruptedIOException(e.getMessage());
+ }
+ }
+ }
+ sendMessageImmediate(msg);
+ }
+
+ /**
+ * Send message, ignore queued async messages that have not been delivered yet.
+ * Will be called directly from the asynchronousThread thread.
+ *
+ * @param msg Message
+ * @throws IOException
+ */
+ public void sendMessageImmediate(byte[] msg) throws IOException {
+ synchronized(connectionSemaphore) {
+ while(true) {
+ if(connectionClosed) {
+ throw reasonClosedCause;
+ }
+ if(!flagKexOngoing) {
+ break;
+ }
+ try {
+ connectionSemaphore.wait();
+ }
+ catch(InterruptedException e) {
+ throw new InterruptedIOException(e.getMessage());
+ }
+ }
+
+ try {
+ tc.sendMessage(msg);
+ }
+ catch(IOException e) {
+ close(e);
+ throw e;
+ }
+ }
+ }
+
+ private void receiveLoop() throws IOException {
+ while(true) {
+ final byte[] buffer = new byte[MAX_PACKET_SIZE];
+ final int length = tc.receiveMessage(buffer, 0, buffer.length);
+ final byte[] packet = new byte[length];
+ System.arraycopy(buffer, 0, packet, 0, length);
+ final int type = packet[0] & 0xff;
+ switch(type) {
+ case Packets.SSH_MSG_IGNORE:
+ break;
+ case Packets.SSH_MSG_DEBUG: {
+ TypesReader tr = new TypesReader(packet);
+ tr.readByte();
+ // always_display
+ tr.readBoolean();
+ String message = tr.readString();
+ if(log.isDebugEnabled()) {
+ log.debug(String.format("Debug message from remote: '%s'", message));
+ }
+ break;
+ }
+ case Packets.SSH_MSG_UNIMPLEMENTED:
+ throw new PacketTypeException(type);
+ case Packets.SSH_MSG_DISCONNECT: {
+ final PacketDisconnect disconnect = new PacketDisconnect(packet);
+ throw new DisconnectException(disconnect.getReason(), disconnect.getMessage());
+ }
+ case Packets.SSH_MSG_KEXINIT:
+ case Packets.SSH_MSG_NEWKEYS:
+ case Packets.SSH_MSG_KEXDH_INIT:
+ case Packets.SSH_MSG_KEXDH_REPLY:
+ case Packets.SSH_MSG_KEX_DH_GEX_REQUEST:
+ case Packets.SSH_MSG_KEX_DH_GEX_INIT:
+ case Packets.SSH_MSG_KEX_DH_GEX_REPLY:
+ // Is it a KEX Packet
+ km.handleMessage(packet);
+ break;
+ case Packets.SSH_MSG_USERAUTH_SUCCESS:
+ tc.startCompression();
+ // Continue with message handlers
+ default:
+ boolean handled = false;
+ for(HandlerEntry handler : messageHandlers) {
+ if((handler.low <= type) && (type <= handler.high)) {
+ handler.mh.handleMessage(packet);
+ handled = true;
+ break;
+ }
+ }
+ if(!handled) {
+ throw new PacketTypeException(type);
+ }
+ break;
+ }
+ if(log.isDebugEnabled()) {
+ log.debug(String.format("Handled packet %d", type));
+ }
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/util/StringEncoder.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/util/StringEncoder.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner.
+ * All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+package ch.ethz.ssh2.util;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * @author Christian Plattner
+ * @version $Id: StringEncoder.java 43 2011-06-21 18:34:06Z dkocher@sudo.ch $
+ */
+public class StringEncoder
+{
+ public static byte[] GetBytes(String data)
+ {
+ try {
+ return data.getBytes("UTF-8");
+ }
+ catch(UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static String GetString(byte[] data)
+ {
+ return GetString(data, 0, data.length);
+ }
+
+ public static String GetString(byte[] data, int off, int len)
+ {
+ try {
+ return new String(data, off, len, "UTF-8");
+ }
+ catch(UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff -r ce2f4e397703 -r 91a31873c42a src/ch/ethz/ssh2/util/TimeoutService.java
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ch/ethz/ssh2/util/TimeoutService.java Fri Jul 18 11:21:46 2014 -0700
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
+ * Please refer to the LICENSE.txt for licensing details.
+ */
+
+package ch.ethz.ssh2.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+
+import ch.ethz.ssh2.log.Logger;
+
+/**
+ * TimeoutService (beta). Here you can register a timeout.
+ *
+ * Implemented having large scale programs in mind: if you open many concurrent SSH connections
+ * that rely on timeouts, then there will be only one timeout thread. Once all timeouts
+ * have expired/are cancelled, the thread will (sooner or later) exit.
+ * Only after new timeouts arrive a new thread (singleton) will be instantiated.
+ *
+ * @author Christian Plattner
+ * @version $Id: TimeoutService.java 89 2014-04-07 14:36:24Z dkocher@sudo.ch $
+ */
+public class TimeoutService
+{
+ private static final Logger log = Logger.getLogger(TimeoutService.class);
+
+ public static class TimeoutToken
+ {
+ private long runTime;
+ private Runnable handler;
+
+ private TimeoutToken(long runTime, Runnable handler)
+ {
+ this.runTime = runTime;
+ this.handler = handler;
+ }
+ }
+
+ private static class TimeoutThread extends Thread
+ {
+ @Override
+ public void run()
+ {
+ synchronized (todolist)
+ {
+ while (true)
+ {
+ if (todolist.size() == 0)
+ {
+ timeoutThread = null;
+ return;
+ }
+
+ long now = System.currentTimeMillis();
+
+ TimeoutToken tt = todolist.getFirst();
+
+ if (tt.runTime > now)
+ {
+ /* Not ready yet, sleep a little bit */
+
+ try
+ {
+ todolist.wait(tt.runTime - now);
+ }
+ catch (InterruptedException ignored)
+ {
+ }
+
+ /* We cannot simply go on, since it could be that the token
+ * was removed (cancelled) or another one has been inserted in
+ * the meantime.
+ */
+
+ continue;
+ }
+
+ todolist.removeFirst();
+
+ try
+ {
+ tt.handler.run();
+ }
+ catch (Exception e)
+ {
+ StringWriter sw = new StringWriter();
+ e.printStackTrace(new PrintWriter(sw));
+ log.warning("Exeception in Timeout handler:" + e.getMessage() + "(" + sw.toString() + ")");
+ }
+ }
+ }
+ }
+ }
+
+ /* The list object is also used for locking purposes */
+ private static final LinkedList todolist = new LinkedList();
+
+ private static Thread timeoutThread = null;
+
+ /**
+ * It is assumed that the passed handler will not execute for a long time.
+ *
+ * @param runTime
+ * @param handler
+ * @return a TimeoutToken that can be used to cancel the timeout.
+ */
+ public static TimeoutToken addTimeoutHandler(long runTime, Runnable handler)
+ {
+ TimeoutToken token = new TimeoutToken(runTime, handler);
+
+ synchronized (todolist)
+ {
+ todolist.add(token);
+
+ Collections.sort(todolist, new Comparator()
+ {
+ public int compare(TimeoutToken o1, TimeoutToken o2)
+ {
+ if (o1.runTime > o2.runTime)
+ return 1;
+ if (o1.runTime == o2.runTime)
+ return 0;
+ return -1;
+ }
+ });
+
+ if (timeoutThread != null)
+ timeoutThread.interrupt();
+ else
+ {
+ timeoutThread = new TimeoutThread();
+ timeoutThread.setDaemon(true);
+ timeoutThread.start();
+ }
+ }
+
+ return token;
+ }
+
+ public static void cancelTimeoutHandler(TimeoutToken token)
+ {
+ synchronized (todolist)
+ {
+ todolist.remove(token);
+
+ if (timeoutThread != null)
+ timeoutThread.interrupt();
+ }
+ }
+
+}
diff -r ce2f4e397703 -r 91a31873c42a src/com/five_ten_sg/connectbot/GeneratePubkeyActivity.java
--- a/src/com/five_ten_sg/connectbot/GeneratePubkeyActivity.java Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/GeneratePubkeyActivity.java Fri Jul 18 11:21:46 2014 -0700
@@ -48,7 +48,7 @@
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
public class GeneratePubkeyActivity extends Activity implements OnEntropyGatheredListener {
public final static String TAG = "ConnectBot.GeneratePubkeyActivity";
diff -r ce2f4e397703 -r 91a31873c42a src/com/five_ten_sg/connectbot/PubkeyListActivity.java
--- a/src/com/five_ten_sg/connectbot/PubkeyListActivity.java Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/PubkeyListActivity.java Fri Jul 18 11:21:46 2014 -0700
@@ -63,9 +63,9 @@
import android.widget.TextView;
import android.widget.Toast;
-import com.trilead.ssh2.crypto.Base64;
-import com.trilead.ssh2.crypto.PEMDecoder;
-import com.trilead.ssh2.crypto.PEMStructure;
+import ch.ethz.ssh2.crypto.Base64;
+import ch.ethz.ssh2.crypto.PEMDecoder;
+import ch.ethz.ssh2.crypto.PEMStructure;
/**
* List public keys in database by nickname and describe their properties. Allow users to import,
diff -r ce2f4e397703 -r 91a31873c42a src/com/five_ten_sg/connectbot/service/AuthAgentService.java
--- a/src/com/five_ten_sg/connectbot/service/AuthAgentService.java Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/service/AuthAgentService.java Fri Jul 18 11:21:46 2014 -0700
@@ -29,9 +29,9 @@
import android.util.Log;
import com.madgag.ssh.android.authagent.AndroidAuthAgent;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
-import com.trilead.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
public class AuthAgentService extends Service {
private static final String TAG = "ConnectBot.AuthAgentService";
diff -r ce2f4e397703 -r 91a31873c42a src/com/five_ten_sg/connectbot/transport/SSH.java
--- a/src/com/five_ten_sg/connectbot/transport/SSH.java Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/transport/SSH.java Fri Jul 18 11:21:46 2014 -0700
@@ -59,23 +59,23 @@
import android.os.Environment;
import android.util.Log;
-import com.trilead.ssh2.AuthAgentCallback;
-import com.trilead.ssh2.ChannelCondition;
-import com.trilead.ssh2.Connection;
-import com.trilead.ssh2.ConnectionInfo;
-import com.trilead.ssh2.ConnectionMonitor;
-import com.trilead.ssh2.DynamicPortForwarder;
-import com.trilead.ssh2.InteractiveCallback;
-import com.trilead.ssh2.KnownHosts;
-import com.trilead.ssh2.LocalPortForwarder;
-import com.trilead.ssh2.SCPClient;
-import com.trilead.ssh2.ServerHostKeyVerifier;
-import com.trilead.ssh2.Session;
-import com.trilead.ssh2.HTTPProxyData;
-import com.trilead.ssh2.HTTPProxyException;
-import com.trilead.ssh2.crypto.PEMDecoder;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.AuthAgentCallback;
+import ch.ethz.ssh2.ChannelCondition;
+import ch.ethz.ssh2.Connection;
+import ch.ethz.ssh2.ConnectionInfo;
+import ch.ethz.ssh2.ConnectionMonitor;
+import ch.ethz.ssh2.DynamicPortForwarder;
+import ch.ethz.ssh2.InteractiveCallback;
+import ch.ethz.ssh2.KnownHosts;
+import ch.ethz.ssh2.LocalPortForwarder;
+import ch.ethz.ssh2.SCPClient;
+import ch.ethz.ssh2.ServerHostKeyVerifier;
+import ch.ethz.ssh2.Session;
+import ch.ethz.ssh2.HTTPProxyData;
+import ch.ethz.ssh2.HTTPProxyException;
+import ch.ethz.ssh2.crypto.PEMDecoder;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
/**
* @author Kenny Root
diff -r ce2f4e397703 -r 91a31873c42a src/com/five_ten_sg/connectbot/util/HostDatabase.java
--- a/src/com/five_ten_sg/connectbot/util/HostDatabase.java Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/util/HostDatabase.java Fri Jul 18 11:21:46 2014 -0700
@@ -33,7 +33,7 @@
import android.database.sqlite.SQLiteException;
import android.util.Log;
-import com.trilead.ssh2.KnownHosts;
+import ch.ethz.ssh2.KnownHosts;
/**
* Contains information about various SSH hosts, include public hostkey if known
diff -r ce2f4e397703 -r 91a31873c42a src/com/five_ten_sg/connectbot/util/PubkeyUtils.java
--- a/src/com/five_ten_sg/connectbot/util/PubkeyUtils.java Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/util/PubkeyUtils.java Fri Jul 18 11:21:46 2014 -0700
@@ -64,11 +64,11 @@
import com.five_ten_sg.connectbot.bean.PubkeyBean;
import android.util.Log;
-import com.trilead.ssh2.crypto.Base64;
-import com.trilead.ssh2.crypto.SimpleDERReader;
-import com.trilead.ssh2.signature.DSASHA1Verify;
-import com.trilead.ssh2.signature.ECDSASHA2Verify;
-import com.trilead.ssh2.signature.RSASHA1Verify;
+import ch.ethz.ssh2.crypto.Base64;
+import ch.ethz.ssh2.crypto.SimpleDERReader;
+import ch.ethz.ssh2.signature.DSASHA1Verify;
+import ch.ethz.ssh2.signature.ECDSASHA2Verify;
+import ch.ethz.ssh2.signature.RSASHA1Verify;
public class PubkeyUtils {
private static final String TAG = "PubkeyUtils";
diff -r ce2f4e397703 -r 91a31873c42a src/com/five_ten_sg/connectbot/util/XmlBuilder.java
--- a/src/com/five_ten_sg/connectbot/util/XmlBuilder.java Thu Jul 17 22:09:05 2014 -0700
+++ b/src/com/five_ten_sg/connectbot/util/XmlBuilder.java Fri Jul 18 11:21:46 2014 -0700
@@ -17,7 +17,7 @@
package com.five_ten_sg.connectbot.util;
-import com.trilead.ssh2.crypto.Base64;
+import ch.ethz.ssh2.crypto.Base64;
/**
* @author Kenny Root
diff -r ce2f4e397703 -r 91a31873c42a xml/510connectbot.in
--- a/xml/510connectbot.in Thu Jul 17 22:09:05 2014 -0700
+++ b/xml/510connectbot.in Fri Jul 18 11:21:46 2014 -0700
@@ -251,9 +251,6 @@
The tn5250 ssl/tls key storage should use the same storage mechanism
as the base ssh key storage.
-
- com.trilead.ssh2 should be updated to the ganymed fork at ch.ethz.ssh2.
-
@@ -293,9 +290,29 @@
http://the-b.org and Jeffrey Sharkey http://jsharkey.org
- Based on the Trilead SSH2 client provided
- under a BSD-style license. Copyright (C) 2007 Trilead
- AG. http://www.trilead.com
+ Based on the Ganymed SSH2 client provided under a BSD-style
+ license. Copyright (C) 2005 - 2006 Swiss Federal Institute of
+ Technology (ETH Zurich), Department of Computer Science
+ http://www.inf.ethz.ch, Christian Plattner. Copyright (C) 2006 - 2013
+ Christian Plattner. http://code.google.com/p/ganymed-ssh-2/ The Java
+ implementations of the AES, Blowfish and 3DES ciphers have been taken
+ (and slightly modified) from the cryptography package released by "The
+ Legion Of The Bouncy Castle". Copyright (C) 2000 - 2004 The Legion Of
+ The Bouncy Castle http://www.bouncycastle.org The following disclaimer
+ applies:
+
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS 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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
Based on JTA Telnet/SSH client provided under the