Initial import.

master
Kenny Root 2007-11-17 05:58:42 +00:00
parent edfccaafe3
commit dfa41d0902
146 changed files with 20712 additions and 0 deletions

10
.classpath Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="lib" path="/Users/kenny/android/android.jar">
<attributes>
<attribute name="javadoc_location" value="file:/Users/kenny/android/docs/reference"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>

33
.project Normal file
View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>SSH</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.google.android.adt.ResourceManagerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.google.android.adt.PreCompilerBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>com.google.android.adt.ApkBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>com.google.android.adt.AndroidNature</nature>
</natures>
</projectDescription>

View File

@ -0,0 +1,5 @@
#Wed Nov 14 13:33:03 CST 2007
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.source=1.5

67
AndroidManifest.xml Normal file
View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.theb.ssh">
<application android:icon="@drawable/icon">
<provider class="HostDbProvider"
android:authorities="org.theb.provider.HostDb"/>
<activity class="HostsList" android:label="@string/title_hosts_list">
<intent-filter>
<action android:value="android.intent.action.MAIN" />
<category android:value="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:value="android.intent.action.PICK" />
<action android:value="android.intent.action.VIEW" />
<action android:value="android.intent.action.EDIT" />
<category android:value="android.intent.category.DEFAULT" />
<type android:value="vnd.android.cursor.dir/vnd.theb.host" />
</intent-filter>
</activity>
<activity class="HostEditor" android:label="@string/title_host"
android:theme="@android:style/Theme.Dialog">
<!-- This filter says that we can view or edit the data of
a single host -->
<intent-filter android:label="@string/resolve_edit">
<action android:value="org.theb.ssh.action.EDIT_HOST" />
<action android:value="org.theb.ssh.action.EDIT" />
<!-- DEFAULT: execute if being directly invoked. -->
<category android:value="android.intent.category.DEFAULT" />
<!-- ALTERNATIVE: show as an alternative action when the user is
working with this type of data. -->
<category android:value="android.intent.category.ALTERNATIVE" />
<!-- SELECTED_ALTERNATIVE: show as an alternative action the user
can perform when selecting this type of data. -->
<category android:value="android.intent.category.SELECTED_ALTERNATIVE" />
<type android:value="vnd.android.cursor.item/vnd.theb.host" />
</intent-filter>
<!-- This filter says that we can create a new note inside
of a directory of notes. -->
<intent-filter>
<action android:value="android.intent.action.INSERT" />
<category android:value="android.intent.category.DEFAULT" />
<type android:value="vnd.android.cursor.dir/vnd.theb.host" />
</intent-filter>
</activity>
<activity class="SecureShell" android:label="@string/title_shell">
<!-- This filter says that we can connect to a host -->
<intent-filter android:label="@string/resolve_edit">
<action android:value="org.theb.ssh.action.CONNECT_HOST" />
<action android:value="android.intent.action.PICK" />
<!-- DEFAULT: execute if being directly invoked. -->
<category android:value="android.intent.category.DEFAULT" />
<type android:value="vnd.android.cursor.item/vnd.theb.host" />
</intent-filter>
</activity>
<activity class="PasswordDialog" android:label="@string/title_password"
android:theme="@android:style/Theme.Dialog">
</activity>
</application>
</manifest>

BIN
bin/SSH.apk Normal file

Binary file not shown.

BIN
bin/classes.dex Normal file

Binary file not shown.

BIN
bin/resources.ap_ Normal file

Binary file not shown.

BIN
res/drawable/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView id="@+id/usernameLabel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Username:"/>
<EditText id="@+id/username"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/usernameLabel"/>
<TextView id="@+id/hostnameLabel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/username"
android:text="Hostname:"/>
<EditText id="@+id/hostname"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/hostnameLabel"/>
<TextView id="@+id/portLabel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/hostname"
android:text="Port:"/>
<EditText id="@+id/port"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="22"
android:layout_below="@id/portLabel"/>
<Button id="@+id/add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/port"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dip"
android:text="@string/button_add" />
<Button id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeft="@id/add"
android:layout_alignTop="@id/add"
android:text="@string/button_cancel" />
</RelativeLayout>

12
res/layout/main.xml Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView id="@+id/shell"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Hello World, SSH"
/>
</LinearLayout>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView id="@+id/message"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button id="@+id/dismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/message"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dip"
android:text="@string/button_ok" />
</RelativeLayout>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView id="@+id/passwordLabel"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Password:"/>
<EditText id="@+id/password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/passwordLabel"
android:maxLines="1"
android:password="true"/>
<Button id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/password"
android:layout_alignParentRight="true"
android:layout_marginLeft="10dip"
android:text="@string/button_ok" />
</RelativeLayout>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView id="@+id/output"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textSize="8sp"
android:typeface="monospace" />
</RelativeLayout>

6
res/values/colors.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<drawable name="blue">#770000ff</drawable>
<drawable name="even_stripe">#e5edfa</drawable>
<drawable name="odd_stripe">#ffffffff</drawable>
</resources>

20
res/values/strings.xml Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Secure Shell</string>
<string name="title_hosts_list">Hosts List</string>
<string name="title_host">Host</string>
<string name="title_shell">Secure Shell</string>
<string name="title_password">Input Password</string>
<string name="resolve_edit">Edit Host</string>
<string name="menu_insert">Add Host</string>
<string name="menu_delete">Delete Host</string>
<string name="button_ok">Ok</string>
<string name="button_add">Add</string>
<string name="button_cancel">Cancel</string>
<string name="button_change">Change</string>
</resources>

View File

@ -0,0 +1,61 @@
package com.trilead.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, plattner@trilead.com
* @version $Id: ChannelCondition.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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 <code>((cond & ChannelCondition.CLOSED) != 0)</code>.
*/
public static final int TIMEOUT = 1;
/**
* The underlying SSH-2 channel, however not necessarily the whole connection,
* has been closed. This implies <code>EOF</code>. Note that there may still
* be unread stdout or stderr data in the local window, i.e, <code>STDOUT_DATA</code>
* or/and <code>STDERR_DATA</code> 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, <code>STDOUT_DATA</code> or/and <code>STDERR_DATA</code>
* 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;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,53 @@
package com.trilead.ssh2;
/**
* In most cases you probably do not need the information contained in here.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: ConnectionInfo.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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;
}

View File

@ -0,0 +1,34 @@
package com.trilead.ssh2;
/**
* A <code>ConnectionMonitor</code> is used to get notified when the
* underlying socket of a connection is closed.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: ConnectionMonitor.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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.
* <p>
* This is an experimental feature.
* <p>
* You MUST NOT make any assumption about the thread that invokes this method.
* <p>
* <b>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.</b>
*
* @see Connection#addConnectionMonitor(ConnectionMonitor)
*
* @param reason Includes an indication why the socket was closed.
*/
public void connectionLost(Throwable reason);
}

View File

@ -0,0 +1,121 @@
package com.trilead.ssh2;
/**
* A <code>DHGexParameters</code> object can be used to specify parameters for
* the diffie-hellman group exchange.
* <p>
* Depending on which constructor is used, either the use of a
* <code>SSH_MSG_KEX_DH_GEX_REQUEST</code> or <code>SSH_MSG_KEX_DH_GEX_REQUEST_OLD</code>
* can be forced.
*
* @see Connection#setDHGexParameters(DHGexParameters)
* @author Christian Plattner, plattner@trilead.com
* @version $Id: DHGexParameters.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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
* <code>SSH_MSG_KEX_DH_GEX_REQUEST_OLD</code> request.
* Internally, the minimum and maximum group lengths will
* be set to zero.
*
* @param pref_group_len has to be &gt= 1024 and &lt;= 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
* <code>SSH_MSG_KEX_DH_GEX_REQUEST</code> request.
* <p>
* Note: older OpenSSH servers don't understand this request, in which
* case you should use the {@link #DHGexParameters(int)} constructor.
* <p>
* All values have to be &gt= 1024 and &lt;= 8192. Furthermore,
* min_group_len &lt;= pref_group_len &lt;= 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 <code>zero</code> 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 <code>zero</code> 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;
}
}

View File

@ -0,0 +1,83 @@
package com.trilead.ssh2;
/**
* A <code>HTTPProxyData</code> object is used to specify the needed connection data
* to connect through a HTTP proxy.
*
* @see Connection#setProxyData(ProxyData)
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: HTTPProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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, <code>null</code>, <code>null</code>)}
*
* @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, <code>null</code>, <code>null</code>, <code>null</code>)}
*
* @param proxyHost Proxy hostname.
* @param proxyPort Proxy port.
* @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
* @param proxyPass Password for basic authentication (<code>null</code> 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").
* <p>
* Please note: if you want to use basic authentication, then both <code>proxyUser</code>
* and <code>proxyPass</code> must be non-null.
* <p>
* Here is an example:
* <p>
* <code>
* new HTTPProxyData("192.168.1.1", "3128", "proxyuser", "secret", new String[] {"User-Agent: TrileadBasedClient/1.0", "X-My-Proxy-Option: something"});
* </code>
*
* @param proxyHost Proxy hostname.
* @param proxyPort Proxy port.
* @param proxyUser Username for basic authentication (<code>null</code> if no authentication is needed).
* @param proxyPass Password for basic authentication (<code>null</code> 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 <code>null</code>.
*/
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;
}
}

View File

@ -0,0 +1,29 @@
package com.trilead.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, plattner@trilead.com
* @version $Id: HTTPProxyException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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;
}
}

View File

@ -0,0 +1,55 @@
package com.trilead.ssh2;
/**
* An <code>InteractiveCallback</code> 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, plattner@trilead.com
* @version $Id: InteractiveCallback.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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).
* <p>
* 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.
* <p>
* Please refer to draft-ietf-secsh-auth-kbdinteract-XX.txt for the details.
* <p>
* 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 <code>numPrompts</code>) of Strings
* @param echo
* an array (length <code>numPrompts</code>) 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
* <code>numPrompts</code>.
*/
public String[] replyToChallenge(String name, String instruction, int numPrompts, String[] prompt, boolean[] echo)
throws Exception;
}

View File

@ -0,0 +1,835 @@
package com.trilead.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.SecureRandom;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Vector;
import com.trilead.ssh2.crypto.Base64;
import com.trilead.ssh2.crypto.digest.Digest;
import com.trilead.ssh2.crypto.digest.HMAC;
import com.trilead.ssh2.crypto.digest.MD5;
import com.trilead.ssh2.crypto.digest.SHA1;
import com.trilead.ssh2.signature.DSAPublicKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.RSAPublicKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
/**
* The <code>KnownHosts</code> class is a handy tool to verify received server hostkeys
* based on the information in <code>known_hosts</code> files (the ones used by OpenSSH).
* <p>
* It offers basically an in-memory database for known_hosts entries, as well as some
* helper functions. Entries from a <code>known_hosts</code> file can be loaded at construction time.
* It is also possible to add more keys later (e.g., one can parse different
* <code>known_hosts<code> files).
* <p>
* It is a thread safe implementation, therefore, you need only to instantiate one
* <code>KnownHosts</code> for your whole application.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: KnownHosts.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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 LinkedList publicKeys = new LinkedList();
public KnownHosts()
{
}
public KnownHosts(char[] knownHostsData) throws IOException
{
initialize(knownHostsData);
}
public KnownHosts(File knownHosts) throws IOException
{
initialize(knownHosts);
}
/**
* Adds a single public key entry to the database. Note: this will NOT add the public key
* to any physical file (e.g., "~/.ssh/known_hosts") - use <code>addHostkeyToFile()</code> 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("Unknwon host key type (" + 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 final String createHashedHostname(String hostname)
{
SHA1 sha1 = new SHA1();
byte[] salt = new byte[sha1.getDigestLength()];
new SecureRandom().nextBytes(salt);
byte[] hash = hmacSha1Hash(salt, hostname);
String base64_salt = new String(Base64.encode(salt));
String base64_hash = new String(Base64.encode(hash));
return new String("|1|" + base64_salt + "|" + base64_hash);
}
private static final byte[] hmacSha1Hash(byte[] salt, String hostname)
{
SHA1 sha1 = new SHA1();
if (salt.length != sha1.getDigestLength())
throw new IllegalArgumentException("Salt has wrong length (" + salt.length + ")");
HMAC hmac = new HMAC(sha1, salt, salt.length);
hmac.update(hostname.getBytes());
byte[] dig = new byte[hmac.getDigestLength()];
hmac.digest(dig);
return dig;
}
private final 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 = null;
byte[] hash = null;
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 = hmacSha1Hash(salt, hostname);
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)
{
Iterator i = publicKeys.iterator();
while (i.hasNext())
{
KnownHostsEntry ke = (KnownHostsEntry) i.next();
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 Vector getAllKeys(String hostname)
{
Vector keys = new Vector();
synchronized (publicKeys)
{
Iterator i = publicKeys.iterator();
while (i.hasNext())
{
KnownHostsEntry ke = (KnownHostsEntry) i.next();
if (hostnameMatches(ke.patterns, hostname) == false)
continue;
keys.addElement(ke.key);
}
}
return keys;
}
/**
* Try to find the preferred order of hostkey algorithms for the given hostname.
* Based on the type of hostkey that is present in the internal database
* (i.e., either <code>ssh-rsa</code> or <code>ssh-dss</code>)
* an ordered list of hostkey algorithms is returned which can be passed
* to <code>Connection.setServerHostKeyAlgorithms</code>.
*
* @param hostname
* @return <code>null</code> if no key for the given hostname is present or
* there are keys of multiple types present for the given hostname. Otherwise,
* an array with hostkey algorithms is returned (i.e., an array of length 2).
*/
public String[] getPreferredServerHostkeyAlgorithmOrder(String hostname)
{
String[] algos = recommendHostkeyAlgorithms(hostname);
if (algos != null)
return algos;
InetAddress[] ipAdresses = null;
try
{
ipAdresses = InetAddress.getAllByName(hostname);
}
catch (UnknownHostException e)
{
return null;
}
for (int i = 0; i < ipAdresses.length; i++)
{
algos = recommendHostkeyAlgorithms(ipAdresses[i].getHostAddress());
if (algos != null)
return algos;
}
return null;
}
private final boolean hostnameMatches(String[] hostpatterns, String hostname)
{
boolean isMatch = false;
boolean negate = false;
hostname = hostname.toLowerCase();
for (int k = 0; k < hostpatterns.length; k++)
{
if (hostpatterns[k] == null)
continue;
String pattern = null;
/* In contrast to OpenSSH we also allow negated hash entries (as well as hashed
* entries in lines with multiple entries).
*/
if ((hostpatterns[k].length() > 0) && (hostpatterns[k].charAt(0) == '!'))
{
pattern = hostpatterns[k].substring(1);
negate = true;
}
else
{
pattern = hostpatterns[k];
negate = false;
}
/* Optimize, no need to check this entry */
if ((isMatch) && (negate == false))
continue;
/* Now compare */
if (pattern.charAt(0) == '|')
{
if (checkHashed(pattern, hostname))
{
if (negate)
return false;
isMatch = true;
}
}
else
{
pattern = pattern.toLowerCase();
if ((pattern.indexOf('?') != -1) || (pattern.indexOf('*') != -1))
{
if (pseudoRegex(pattern.toCharArray(), 0, hostname.toCharArray(), 0))
{
if (negate)
return false;
isMatch = true;
}
}
else if (pattern.compareTo(hostname) == 0)
{
if (negate)
return false;
isMatch = true;
}
}
}
return isMatch;
}
private void initialize(char[] knownHostsData) throws IOException
{
BufferedReader br = new BufferedReader(new CharArrayReader(knownHostsData));
while (true)
{
String line = br.readLine();
if (line == null)
break;
line = line.trim();
if (line.startsWith("#"))
continue;
String[] arr = line.split(" ");
if (arr.length >= 3)
{
if ((arr[1].compareTo("ssh-rsa") == 0) || (arr[1].compareTo("ssh-dss") == 0))
{
String[] hostnames = arr[0].split(",");
byte[] msg = Base64.decode(arr[2].toCharArray());
addHostkey(hostnames, arr[1], msg);
}
}
}
}
private void initialize(File knownHosts) throws IOException
{
char[] buff = new char[512];
CharArrayWriter cw = new CharArrayWriter();
knownHosts.createNewFile();
FileReader fr = new FileReader(knownHosts);
while (true)
{
int len = fr.read(buff);
if (len < 0)
break;
cw.write(buff, 0, len);
}
fr.close();
initialize(cw.toCharArray());
}
private final boolean matchKeys(Object key1, Object key2)
{
if ((key1 instanceof RSAPublicKey) && (key2 instanceof RSAPublicKey))
{
RSAPublicKey savedRSAKey = (RSAPublicKey) key1;
RSAPublicKey remoteRSAKey = (RSAPublicKey) key2;
if (savedRSAKey.getE().equals(remoteRSAKey.getE()) == false)
return false;
if (savedRSAKey.getN().equals(remoteRSAKey.getN()) == false)
return false;
return true;
}
if ((key1 instanceof DSAPublicKey) && (key2 instanceof DSAPublicKey))
{
DSAPublicKey savedDSAKey = (DSAPublicKey) key1;
DSAPublicKey remoteDSAKey = (DSAPublicKey) key2;
if (savedDSAKey.getG().equals(remoteDSAKey.getG()) == false)
return false;
if (savedDSAKey.getP().equals(remoteDSAKey.getP()) == false)
return false;
if (savedDSAKey.getQ().equals(remoteDSAKey.getQ()) == false)
return false;
if (savedDSAKey.getY().equals(remoteDSAKey.getY()) == false)
return false;
return true;
}
return false;
}
private final boolean pseudoRegex(char[] pattern, int i, char[] match, int j)
{
/* This matching logic is equivalent to the one present in OpenSSH 4.1 */
while (true)
{
/* Are we at the end of the pattern? */
if (pattern.length == i)
return (match.length == j);
if (pattern[i] == '*')
{
i++;
if (pattern.length == i)
return true;
if ((pattern[i] != '*') && (pattern[i] != '?'))
{
while (true)
{
if ((pattern[i] == match[j]) && pseudoRegex(pattern, i + 1, match, j + 1))
return true;
j++;
if (match.length == j)
return false;
}
}
while (true)
{
if (pseudoRegex(pattern, i, match, j))
return true;
j++;
if (match.length == j)
return false;
}
}
if (match.length == j)
return false;
if ((pattern[i] != '?') && (pattern[i] != match[j]))
return false;
i++;
j++;
}
}
private String[] recommendHostkeyAlgorithms(String hostname)
{
String preferredAlgo = null;
Vector keys = getAllKeys(hostname);
for (int i = 0; i < keys.size(); i++)
{
String thisAlgo = null;
if (keys.elementAt(i) instanceof RSAPublicKey)
thisAlgo = "ssh-rsa";
else if (keys.elementAt(i) instanceof DSAPublicKey)
thisAlgo = "ssh-dss";
else
continue;
if (preferredAlgo != null)
{
/* If we find different key types, then return null */
if (preferredAlgo.compareTo(thisAlgo) != 0)
return null;
/* OK, we found the same algo again, optimize */
continue;
}
}
/* If we did not find anything that we know of, return null */
if (preferredAlgo == null)
return null;
/* Now put the preferred algo to the start of the array.
* You may ask yourself why we do it that way - basically, we could just
* return only the preferred algorithm: since we have a saved key of that
* type (sent earlier from the remote host), then that should work out.
* However, imagine that the server is (for whatever reasons) not offering
* that type of hostkey anymore (e.g., "ssh-rsa" was disabled and
* now "ssh-dss" is being used). If we then do not let the server send us
* a fresh key of the new type, then we shoot ourself into the foot:
* the connection cannot be established and hence the user cannot decide
* if he/she wants to accept the new key.
*/
if (preferredAlgo.equals("ssh-rsa"))
return new String[] { "ssh-rsa", "ssh-dss" };
return new String[] { "ssh-dss", "ssh-rsa" };
}
/**
* Checks the internal hostkey database for the given hostkey.
* If no matching key can be found, then the hostname is resolved to an IP address
* and the search is repeated using that IP address.
*
* @param hostname the server's hostname, will be matched with all hostname patterns
* @param serverHostKeyAlgorithm type of hostkey, either <code>ssh-rsa</code> or <code>ssh-dss</code>
* @param serverHostKey the key blob
* @return <ul>
* <li><code>HOSTKEY_IS_OK</code>: the given hostkey matches an entry for the given hostname</li>
* <li><code>HOSTKEY_IS_NEW</code>: no entries found for this hostname and this type of hostkey</li>
* <li><code>HOSTKEY_HAS_CHANGED</code>: hostname is known, but with another key of the same type
* (man-in-the-middle attack?)</li>
* </ul>
* @throws IOException if the supplied key blob cannot be parsed or does not match the given hostkey type.
*/
public int verifyHostkey(String hostname, String serverHostKeyAlgorithm, byte[] serverHostKey) throws IOException
{
Object remoteKey = null;
if ("ssh-rsa".equals(serverHostKeyAlgorithm))
{
remoteKey = RSASHA1Verify.decodeSSHRSAPublicKey(serverHostKey);
}
else if ("ssh-dss".equals(serverHostKeyAlgorithm))
{
remoteKey = DSASHA1Verify.decodeSSHDSAPublicKey(serverHostKey);
}
else
throw new IllegalArgumentException("Unknown hostkey type " + serverHostKeyAlgorithm);
int result = checkKey(hostname, remoteKey);
if (result == HOSTKEY_IS_OK)
return result;
InetAddress[] ipAdresses = null;
try
{
ipAdresses = InetAddress.getAllByName(hostname);
}
catch (UnknownHostException e)
{
return result;
}
for (int i = 0; i < ipAdresses.length; i++)
{
int newresult = checkKey(ipAdresses[i].getHostAddress(), remoteKey);
if (newresult == HOSTKEY_IS_OK)
return newresult;
if (newresult == HOSTKEY_HAS_CHANGED)
result = HOSTKEY_HAS_CHANGED;
}
return result;
}
/**
* Adds a single public key entry to the a known_hosts file.
* This method is designed to be used in a {@link ServerHostKeyVerifier}.
*
* @param knownHosts the file where the publickey entry will be appended.
* @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 final static void addHostkeyToFile(File knownHosts, String[] hostnames, String serverHostKeyAlgorithm,
byte[] serverHostKey) throws IOException
{
if ((hostnames == null) || (hostnames.length == 0))
throw new IllegalArgumentException("Need at least one hostname specification");
if ((serverHostKeyAlgorithm == null) || (serverHostKey == null))
throw new IllegalArgumentException();
CharArrayWriter writer = new CharArrayWriter();
for (int i = 0; i < hostnames.length; i++)
{
if (i != 0)
writer.write(',');
writer.write(hostnames[i]);
}
writer.write(' ');
writer.write(serverHostKeyAlgorithm);
writer.write(' ');
writer.write(Base64.encode(serverHostKey));
writer.write("\n");
char[] entry = writer.toCharArray();
RandomAccessFile raf = new RandomAccessFile(knownHosts, "rw");
long len = raf.length();
if (len > 0)
{
raf.seek(len - 1);
int last = raf.read();
if (last != '\n')
raf.write('\n');
}
raf.write(new String(entry).getBytes());
raf.close();
}
/**
* Generates a "raw" fingerprint of a hostkey.
*
* @param type either "md5" or "sha1"
* @param keyType either "ssh-rsa" or "ssh-dss"
* @param hostkey the hostkey
* @return the raw fingerprint
*/
static final private byte[] rawFingerPrint(String type, String keyType, byte[] hostkey)
{
Digest dig = null;
if ("md5".equals(type))
{
dig = new MD5();
}
else if ("sha1".equals(type))
{
dig = new SHA1();
}
else
throw new IllegalArgumentException("Unknown hash type " + type);
if ("ssh-rsa".equals(keyType))
{
}
else if ("ssh-dss".equals(keyType))
{
}
else
throw new IllegalArgumentException("Unknown key type " + keyType);
if (hostkey == null)
throw new IllegalArgumentException("hostkey is null");
dig.update(hostkey);
byte[] res = new byte[dig.getDigestLength()];
dig.digest(res);
return res;
}
/**
* Convert a raw fingerprint to hex representation (XX:YY:ZZ...).
* @param fingerprint raw fingerprint
* @return the hex representation
*/
static final private String rawToHexFingerprint(byte[] fingerprint)
{
final char[] alpha = "0123456789abcdef".toCharArray();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < fingerprint.length; i++)
{
if (i != 0)
sb.append(':');
int b = fingerprint[i] & 0xff;
sb.append(alpha[b >> 4]);
sb.append(alpha[b & 15]);
}
return sb.toString();
}
/**
* Convert a raw fingerprint to bubblebabble representation.
* @param raw raw fingerprint
* @return the bubblebabble representation
*/
static final private String rawToBubblebabbleFingerprint(byte[] raw)
{
final char[] v = "aeiouy".toCharArray();
final char[] c = "bcdfghklmnprstvzx".toCharArray();
StringBuffer sb = new StringBuffer();
int seed = 1;
int rounds = (raw.length / 2) + 1;
sb.append('x');
for (int i = 0; i < rounds; i++)
{
if (((i + 1) < rounds) || ((raw.length) % 2 != 0))
{
sb.append(v[(((raw[2 * i] >> 6) & 3) + seed) % 6]);
sb.append(c[(raw[2 * i] >> 2) & 15]);
sb.append(v[((raw[2 * i] & 3) + (seed / 6)) % 6]);
if ((i + 1) < rounds)
{
sb.append(c[(((raw[(2 * i) + 1])) >> 4) & 15]);
sb.append('-');
sb.append(c[(((raw[(2 * i) + 1]))) & 15]);
// As long as seed >= 0, seed will be >= 0 afterwards
seed = ((seed * 5) + (((raw[2 * i] & 0xff) * 7) + (raw[(2 * i) + 1] & 0xff))) % 36;
}
}
else
{
sb.append(v[seed % 6]); // seed >= 0, therefore index positive
sb.append('x');
sb.append(v[seed / 6]);
}
}
sb.append('x');
return sb.toString();
}
/**
* Convert a ssh2 key-blob into a human readable hex fingerprint.
* Generated fingerprints are identical to those generated by OpenSSH.
* <p>
* Example fingerprint: d0:cb:76:19:99:5a:03:fc:73:10:70:93:f2:44:63:47.
* @param keytype either "ssh-rsa" or "ssh-dss"
* @param publickey key blob
* @return Hex fingerprint
*/
public final static String createHexFingerprint(String keytype, byte[] publickey)
{
byte[] raw = rawFingerPrint("md5", keytype, publickey);
return rawToHexFingerprint(raw);
}
/**
* Convert a ssh2 key-blob into a human readable bubblebabble fingerprint.
* The used bubblebabble algorithm (taken from OpenSSH) generates fingerprints
* that are easier to remember for humans.
* <p>
* Example fingerprint: xofoc-bubuz-cazin-zufyl-pivuk-biduk-tacib-pybur-gonar-hotat-lyxux.
*
* @param keytype either "ssh-rsa" or "ssh-dss"
* @param publickey key data
* @return Bubblebabble fingerprint
*/
public final static String createBubblebabbleFingerprint(String keytype, byte[] publickey)
{
byte[] raw = rawFingerPrint("sha1", keytype, publickey);
return rawToBubblebabbleFingerprint(raw);
}
}

View File

@ -0,0 +1,63 @@
package com.trilead.ssh2;
import java.io.IOException;
import java.net.InetSocketAddress;
import com.trilead.ssh2.channel.ChannelManager;
import com.trilead.ssh2.channel.LocalAcceptThread;
/**
* A <code>LocalPortForwarder</code> forwards TCP/IP connections to a local
* port via the secure tunnel to another host (which may or may not be identical
* to the remote SSH-2 server). Checkout {@link Connection#createLocalPortForwarder(int, String, int)}
* on how to create one.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: LocalPortForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class LocalPortForwarder
{
ChannelManager cm;
String host_to_connect;
int port_to_connect;
LocalAcceptThread lat;
LocalPortForwarder(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;
lat = new LocalAcceptThread(cm, local_port, host_to_connect, port_to_connect);
lat.setDaemon(true);
lat.start();
}
LocalPortForwarder(ChannelManager cm, InetSocketAddress addr, 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;
lat = new LocalAcceptThread(cm, addr, host_to_connect, port_to_connect);
lat.setDaemon(true);
lat.start();
}
/**
* Stop TCP/IP forwarding of newly arriving connections.
*
* @throws IOException
*/
public void close() throws IOException
{
lat.stopWorking();
}
}

View File

@ -0,0 +1,78 @@
package com.trilead.ssh2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import com.trilead.ssh2.channel.Channel;
import com.trilead.ssh2.channel.ChannelManager;
import com.trilead.ssh2.channel.LocalAcceptThread;
/**
* A <code>LocalStreamForwarder</code> forwards an Input- and Outputstream
* pair via the secure tunnel to another host (which may or may not be identical
* to the remote SSH-2 server).
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: LocalStreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class LocalStreamForwarder
{
ChannelManager cm;
String host_to_connect;
int port_to_connect;
LocalAcceptThread lat;
Channel cn;
LocalStreamForwarder(ChannelManager cm, 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;
cn = cm.openDirectTCPIPChannel(host_to_connect, port_to_connect, "127.0.0.1", 0);
}
/**
* @return An <code>InputStream</code> object.
* @throws IOException
*/
public InputStream getInputStream() throws IOException
{
return cn.getStdoutStream();
}
/**
* Get the OutputStream. Please be aware that the implementation MAY use an
* internal buffer. To make sure that the buffered data is sent over the
* tunnel, you have to call the <code>flush</code> method of the
* <code>OutputStream</code>. To signal EOF, please use the
* <code>close</code> method of the <code>OutputStream</code>.
*
* @return An <code>OutputStream</code> object.
* @throws IOException
*/
public OutputStream getOutputStream() throws IOException
{
return cn.getStdinStream();
}
/**
* Close the underlying SSH forwarding channel and free up resources.
* You can also use this method to force the shutdown of the underlying
* forwarding channel. Pending output (OutputStream not flushed) will NOT
* be sent. Pending input (InputStream) can still be read. If the shutdown
* operation is already in progress (initiated from either side), then this
* call is a no-op.
*
* @throws IOException
*/
public void close() throws IOException
{
cm.closeChannel(cn, "Closed due to user request.", true);
}
}

View File

@ -0,0 +1,15 @@
package com.trilead.ssh2;
/**
* An abstract marker interface implemented by all proxy data implementations.
*
* @see HTTPProxyData
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: ProxyData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public abstract interface ProxyData
{
}

View File

@ -0,0 +1,711 @@
package com.trilead.ssh2;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* A very basic <code>SCPClient</code> that can be used to copy files from/to
* the SSH-2 server. On the server side, the "scp" program must be in the PATH.
* <p>
* This scp client is thread safe - you can download (and upload) different sets
* of files concurrently without any troubles. The <code>SCPClient</code> is
* actually mapping every request to a distinct {@link Session}.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: SCPClient.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class SCPClient
{
Connection conn;
class LenNamePair
{
long length;
String filename;
}
public SCPClient(Connection conn)
{
if (conn == null)
throw new IllegalArgumentException("Cannot accept null argument!");
this.conn = conn;
}
private void readResponse(InputStream is) throws IOException
{
int c = is.read();
if (c == 0)
return;
if (c == -1)
throw new IOException("Remote scp terminated unexpectedly.");
if ((c != 1) && (c != 2))
throw new IOException("Remote scp sent illegal error code.");
if (c == 2)
throw new IOException("Remote scp terminated with error.");
String err = receiveLine(is);
throw new IOException("Remote scp terminated with error (" + err + ").");
}
private String receiveLine(InputStream is) throws IOException
{
StringBuffer sb = new StringBuffer(30);
while (true)
{
/* This is a random limit - if your path names are longer, then adjust it */
if (sb.length() > 8192)
throw new IOException("Remote scp sent a too long line");
int c = is.read();
if (c < 0)
throw new IOException("Remote scp terminated unexpectedly.");
if (c == '\n')
break;
sb.append((char) c);
}
return sb.toString();
}
private LenNamePair parseCLine(String line) throws IOException
{
/* Minimum line: "xxxx y z" ---> 8 chars */
long len;
if (line.length() < 8)
throw new IOException("Malformed C line sent by remote SCP binary, line too short.");
if ((line.charAt(4) != ' ') || (line.charAt(5) == ' '))
throw new IOException("Malformed C line sent by remote SCP binary.");
int length_name_sep = line.indexOf(' ', 5);
if (length_name_sep == -1)
throw new IOException("Malformed C line sent by remote SCP binary.");
String length_substring = line.substring(5, length_name_sep);
String name_substring = line.substring(length_name_sep + 1);
if ((length_substring.length() <= 0) || (name_substring.length() <= 0))
throw new IOException("Malformed C line sent by remote SCP binary.");
if ((6 + length_substring.length() + name_substring.length()) != line.length())
throw new IOException("Malformed C line sent by remote SCP binary.");
try
{
len = Long.parseLong(length_substring);
}
catch (NumberFormatException e)
{
throw new IOException("Malformed C line sent by remote SCP binary, cannot parse file length.");
}
if (len < 0)
throw new IOException("Malformed C line sent by remote SCP binary, illegal file length.");
LenNamePair lnp = new LenNamePair();
lnp.length = len;
lnp.filename = name_substring;
return lnp;
}
private void sendBytes(Session sess, byte[] data, String fileName, String mode) throws IOException
{
OutputStream os = sess.getStdin();
InputStream is = new BufferedInputStream(sess.getStdout(), 512);
readResponse(is);
String cline = "C" + mode + " " + data.length + " " + fileName + "\n";
os.write(cline.getBytes());
os.flush();
readResponse(is);
os.write(data, 0, data.length);
os.write(0);
os.flush();
readResponse(is);
os.write("E\n".getBytes());
os.flush();
}
private void sendFiles(Session sess, String[] files, String[] remoteFiles, String mode) throws IOException
{
byte[] buffer = new byte[8192];
OutputStream os = new BufferedOutputStream(sess.getStdin(), 40000);
InputStream is = new BufferedInputStream(sess.getStdout(), 512);
readResponse(is);
for (int i = 0; i < files.length; i++)
{
File f = new File(files[i]);
long remain = f.length();
String remoteName;
if ((remoteFiles != null) && (remoteFiles.length > i) && (remoteFiles[i] != null))
remoteName = remoteFiles[i];
else
remoteName = f.getName();
String cline = "C" + mode + " " + remain + " " + remoteName + "\n";
os.write(cline.getBytes());
os.flush();
readResponse(is);
FileInputStream fis = null;
try
{
fis = new FileInputStream(f);
while (remain > 0)
{
int trans;
if (remain > buffer.length)
trans = buffer.length;
else
trans = (int) remain;
if (fis.read(buffer, 0, trans) != trans)
throw new IOException("Cannot read enough from local file " + files[i]);
os.write(buffer, 0, trans);
remain -= trans;
}
}
finally
{
if (fis != null)
fis.close();
}
os.write(0);
os.flush();
readResponse(is);
}
os.write("E\n".getBytes());
os.flush();
}
private void receiveFiles(Session sess, OutputStream[] targets) throws IOException
{
byte[] buffer = new byte[8192];
OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
InputStream is = new BufferedInputStream(sess.getStdout(), 40000);
os.write(0x0);
os.flush();
for (int i = 0; i < targets.length; i++)
{
LenNamePair lnp = null;
while (true)
{
int c = is.read();
if (c < 0)
throw new IOException("Remote scp terminated unexpectedly.");
String line = receiveLine(is);
if (c == 'T')
{
/* Ignore modification times */
continue;
}
if ((c == 1) || (c == 2))
throw new IOException("Remote SCP error: " + line);
if (c == 'C')
{
lnp = parseCLine(line);
break;
}
throw new IOException("Remote SCP error: " + ((char) c) + line);
}
os.write(0x0);
os.flush();
long remain = lnp.length;
while (remain > 0)
{
int trans;
if (remain > buffer.length)
trans = buffer.length;
else
trans = (int) remain;
int this_time_received = is.read(buffer, 0, trans);
if (this_time_received < 0)
{
throw new IOException("Remote scp terminated connection unexpectedly");
}
targets[i].write(buffer, 0, this_time_received);
remain -= this_time_received;
}
readResponse(is);
os.write(0x0);
os.flush();
}
}
private void receiveFiles(Session sess, String[] files, String target) throws IOException
{
byte[] buffer = new byte[8192];
OutputStream os = new BufferedOutputStream(sess.getStdin(), 512);
InputStream is = new BufferedInputStream(sess.getStdout(), 40000);
os.write(0x0);
os.flush();
for (int i = 0; i < files.length; i++)
{
LenNamePair lnp = null;
while (true)
{
int c = is.read();
if (c < 0)
throw new IOException("Remote scp terminated unexpectedly.");
String line = receiveLine(is);
if (c == 'T')
{
/* Ignore modification times */
continue;
}
if ((c == 1) || (c == 2))
throw new IOException("Remote SCP error: " + line);
if (c == 'C')
{
lnp = parseCLine(line);
break;
}
throw new IOException("Remote SCP error: " + ((char) c) + line);
}
os.write(0x0);
os.flush();
File f = new File(target + File.separatorChar + lnp.filename);
FileOutputStream fop = null;
try
{
fop = new FileOutputStream(f);
long remain = lnp.length;
while (remain > 0)
{
int trans;
if (remain > buffer.length)
trans = buffer.length;
else
trans = (int) remain;
int this_time_received = is.read(buffer, 0, trans);
if (this_time_received < 0)
{
throw new IOException("Remote scp terminated connection unexpectedly");
}
fop.write(buffer, 0, this_time_received);
remain -= this_time_received;
}
}
finally
{
if (fop != null)
fop.close();
}
readResponse(is);
os.write(0x0);
os.flush();
}
}
/**
* Copy a local file to a remote directory, uses mode 0600 when creating
* the file on the remote side.
*
* @param localFile
* Path and name of local file.
* @param remoteTargetDirectory
* Remote target directory. Use an empty string to specify the default directory.
*
* @throws IOException
*/
public void put(String localFile, String remoteTargetDirectory) throws IOException
{
put(new String[] { localFile }, remoteTargetDirectory, "0600");
}
/**
* Copy a set of local files to a remote directory, uses mode 0600 when
* creating files on the remote side.
*
* @param localFiles
* Paths and names of local file names.
* @param remoteTargetDirectory
* Remote target directory. Use an empty string to specify the default directory.
*
* @throws IOException
*/
public void put(String[] localFiles, String remoteTargetDirectory) throws IOException
{
put(localFiles, remoteTargetDirectory, "0600");
}
/**
* Copy a local file to a remote directory, uses the specified mode when
* creating the file on the remote side.
*
* @param localFile
* Path and name of local file.
* @param remoteTargetDirectory
* Remote target directory. Use an empty string to specify the default directory.
* @param mode
* a four digit string (e.g., 0644, see "man chmod", "man open")
* @throws IOException
*/
public void put(String localFile, String remoteTargetDirectory, String mode) throws IOException
{
put(new String[] { localFile }, remoteTargetDirectory, mode);
}
/**
* Copy a local file to a remote directory, uses the specified mode and remote filename
* when creating the file on the remote side.
*
* @param localFile
* Path and name of local file.
* @param remoteFileName
* The name of the file which will be created in the remote target directory.
* @param remoteTargetDirectory
* Remote target directory. Use an empty string to specify the default directory.
* @param mode
* a four digit string (e.g., 0644, see "man chmod", "man open")
* @throws IOException
*/
public void put(String localFile, String remoteFileName, String remoteTargetDirectory, String mode)
throws IOException
{
put(new String[] { localFile }, new String[] { remoteFileName }, remoteTargetDirectory, mode);
}
/**
* Create a remote file and copy the contents of the passed byte array into it.
* Uses mode 0600 for creating the remote file.
*
* @param data
* the data to be copied into the remote file.
* @param remoteFileName
* The name of the file which will be created in the remote target directory.
* @param remoteTargetDirectory
* Remote target directory. Use an empty string to specify the default directory.
* @throws IOException
*/
public void put(byte[] data, String remoteFileName, String remoteTargetDirectory) throws IOException
{
put(data, remoteFileName, remoteTargetDirectory, "0600");
}
/**
* Create a remote file and copy the contents of the passed byte array into it.
* The method use the specified mode when creating the file on the remote side.
*
* @param data
* the data to be copied into the remote file.
* @param remoteFileName
* The name of the file which will be created in the remote target directory.
* @param remoteTargetDirectory
* Remote target directory. Use an empty string to specify the default directory.
* @param mode
* a four digit string (e.g., 0644, see "man chmod", "man open")
* @throws IOException
*/
public void put(byte[] data, String remoteFileName, String remoteTargetDirectory, String mode) throws IOException
{
Session sess = null;
if ((remoteFileName == null) || (remoteTargetDirectory == null) || (mode == null))
throw new IllegalArgumentException("Null argument.");
if (mode.length() != 4)
throw new IllegalArgumentException("Invalid mode.");
for (int i = 0; i < mode.length(); i++)
if (Character.isDigit(mode.charAt(i)) == false)
throw new IllegalArgumentException("Invalid mode.");
remoteTargetDirectory = remoteTargetDirectory.trim();
remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
String cmd = "scp -t -d " + remoteTargetDirectory;
try
{
sess = conn.openSession();
sess.execCommand(cmd);
sendBytes(sess, data, remoteFileName, mode);
}
catch (IOException e)
{
throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
}
finally
{
if (sess != null)
sess.close();
}
}
/**
* Copy a set of local files to a remote directory, uses the specified mode
* when creating the files on the remote side.
*
* @param localFiles
* Paths and names of the local files.
* @param remoteTargetDirectory
* Remote target directory. Use an empty string to specify the default directory.
* @param mode
* a four digit string (e.g., 0644, see "man chmod", "man open")
* @throws IOException
*/
public void put(String[] localFiles, String remoteTargetDirectory, String mode) throws IOException
{
put(localFiles, null, remoteTargetDirectory, mode);
}
public void put(String[] localFiles, String[] remoteFiles, String remoteTargetDirectory, String mode)
throws IOException
{
Session sess = null;
/* remoteFiles may be null, indicating that the local filenames shall be used */
if ((localFiles == null) || (remoteTargetDirectory == null) || (mode == null))
throw new IllegalArgumentException("Null argument.");
if (mode.length() != 4)
throw new IllegalArgumentException("Invalid mode.");
for (int i = 0; i < mode.length(); i++)
if (Character.isDigit(mode.charAt(i)) == false)
throw new IllegalArgumentException("Invalid mode.");
if (localFiles.length == 0)
return;
remoteTargetDirectory = remoteTargetDirectory.trim();
remoteTargetDirectory = (remoteTargetDirectory.length() > 0) ? remoteTargetDirectory : ".";
String cmd = "scp -t -d " + remoteTargetDirectory;
for (int i = 0; i < localFiles.length; i++)
{
if (localFiles[i] == null)
throw new IllegalArgumentException("Cannot accept null filename.");
}
try
{
sess = conn.openSession();
sess.execCommand(cmd);
sendFiles(sess, localFiles, remoteFiles, mode);
}
catch (IOException e)
{
throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
}
finally
{
if (sess != null)
sess.close();
}
}
/**
* Download a file from the remote server to a local directory.
*
* @param remoteFile
* Path and name of the remote file.
* @param localTargetDirectory
* Local directory to put the downloaded file.
*
* @throws IOException
*/
public void get(String remoteFile, String localTargetDirectory) throws IOException
{
get(new String[] { remoteFile }, localTargetDirectory);
}
/**
* Download a file from the remote server and pipe its contents into an <code>OutputStream</code>.
* Please note that, to enable flexible usage of this method, the <code>OutputStream</code> will not
* be closed nor flushed.
*
* @param remoteFile
* Path and name of the remote file.
* @param target
* OutputStream where the contents of the file will be sent to.
* @throws IOException
*/
public void get(String remoteFile, OutputStream target) throws IOException
{
get(new String[] { remoteFile }, new OutputStream[] { target });
}
private void get(String remoteFiles[], OutputStream[] targets) throws IOException
{
Session sess = null;
if ((remoteFiles == null) || (targets == null))
throw new IllegalArgumentException("Null argument.");
if (remoteFiles.length != targets.length)
throw new IllegalArgumentException("Length of arguments does not match.");
if (remoteFiles.length == 0)
return;
String cmd = "scp -f";
for (int i = 0; i < remoteFiles.length; i++)
{
if (remoteFiles[i] == null)
throw new IllegalArgumentException("Cannot accept null filename.");
String tmp = remoteFiles[i].trim();
if (tmp.length() == 0)
throw new IllegalArgumentException("Cannot accept empty filename.");
cmd += (" " + tmp);
}
try
{
sess = conn.openSession();
sess.execCommand(cmd);
receiveFiles(sess, targets);
}
catch (IOException e)
{
throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
}
finally
{
if (sess != null)
sess.close();
}
}
/**
* Download a set of files from the remote server to a local directory.
*
* @param remoteFiles
* Paths and names of the remote files.
* @param localTargetDirectory
* Local directory to put the downloaded files.
*
* @throws IOException
*/
public void get(String remoteFiles[], String localTargetDirectory) throws IOException
{
Session sess = null;
if ((remoteFiles == null) || (localTargetDirectory == null))
throw new IllegalArgumentException("Null argument.");
if (remoteFiles.length == 0)
return;
String cmd = "scp -f";
for (int i = 0; i < remoteFiles.length; i++)
{
if (remoteFiles[i] == null)
throw new IllegalArgumentException("Cannot accept null filename.");
String tmp = remoteFiles[i].trim();
if (tmp.length() == 0)
throw new IllegalArgumentException("Cannot accept empty filename.");
cmd += (" " + tmp);
}
try
{
sess = conn.openSession();
sess.execCommand(cmd);
receiveFiles(sess, remoteFiles, localTargetDirectory);
}
catch (IOException e)
{
throw (IOException) new IOException("Error during SCP transfer.").initCause(e);
}
finally
{
if (sess != null)
sess.close();
}
}
}

View File

@ -0,0 +1,91 @@
package com.trilead.ssh2;
import java.io.IOException;
import com.trilead.ssh2.sftp.ErrorCodes;
/**
* Used in combination with the SFTPv3Client. This exception wraps
* error messages sent by the SFTP server.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: SFTPException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class SFTPException extends IOException
{
private static final long serialVersionUID = 578654644222421811L;
private final String sftpErrorMessage;
private final int sftpErrorCode;
private static String constructMessage(String s, int errorCode)
{
String[] detail = ErrorCodes.getDescription(errorCode);
if (detail == null)
return s + " (UNKNOW SFTP ERROR CODE)";
return s + " (" + detail[0] + ": " + detail[1] + ")";
}
SFTPException(String msg, int errorCode)
{
super(constructMessage(msg, errorCode));
sftpErrorMessage = msg;
sftpErrorCode = errorCode;
}
/**
* Get the error message sent by the server. Often, this
* message does not help a lot (e.g., "failure").
*
* @return the plain string as sent by the server.
*/
public String getServerErrorMessage()
{
return sftpErrorMessage;
}
/**
* Get the error code sent by the server.
*
* @return an error code as defined in the SFTP specs.
*/
public int getServerErrorCode()
{
return sftpErrorCode;
}
/**
* Get the symbolic name of the error code as given in the SFTP specs.
*
* @return e.g., "SSH_FX_INVALID_FILENAME".
*/
public String getServerErrorCodeSymbol()
{
String[] detail = ErrorCodes.getDescription(sftpErrorCode);
if (detail == null)
return "UNKNOW SFTP ERROR CODE " + sftpErrorCode;
return detail[0];
}
/**
* Get the description of the error code as given in the SFTP specs.
*
* @return e.g., "The filename is not valid."
*/
public String getServerErrorCodeVerbose()
{
String[] detail = ErrorCodes.getDescription(sftpErrorCode);
if (detail == null)
return "The error code " + sftpErrorCode + " is unknown.";
return detail[1];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
package com.trilead.ssh2;
/**
* A <code>SFTPv3DirectoryEntry</code> as returned by {@link SFTPv3Client#ls(String)}.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: SFTPv3DirectoryEntry.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class SFTPv3DirectoryEntry
{
/**
* A relative name within the directory, without any path components.
*/
public String filename;
/**
* An expanded format for the file name, similar to what is returned by
* "ls -l" on Un*x systems.
* <p>
* The format of this field is unspecified by the SFTP v3 protocol.
* It MUST be suitable for use in the output of a directory listing
* command (in fact, the recommended operation for a directory listing
* command is to simply display this data). However, clients SHOULD NOT
* attempt to parse the longname field for file attributes; they SHOULD
* use the attrs field instead.
* <p>
* The recommended format for the longname field is as follows:<br>
* <code>-rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer</code>
*/
public String longEntry;
/**
* The attributes of this entry.
*/
public SFTPv3FileAttributes attributes;
}

View File

@ -0,0 +1,145 @@
package com.trilead.ssh2;
/**
* A <code>SFTPv3FileAttributes</code> object represents detail information
* about a file on the server. Not all fields may/must be present.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: SFTPv3FileAttributes.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class SFTPv3FileAttributes
{
/**
* The SIZE attribute. <code>NULL</code> if not present.
*/
public Long size = null;
/**
* The UID attribute. <code>NULL</code> if not present.
*/
public Integer uid = null;
/**
* The GID attribute. <code>NULL</code> if not present.
*/
public Integer gid = null;
/**
* The POSIX permissions. <code>NULL</code> if not present.
* <p>
* Here is a list:
* <p>
* <pre>Note: these numbers are all OCTAL.
*
* S_IFMT 0170000 bitmask for the file type bitfields
* S_IFSOCK 0140000 socket
* S_IFLNK 0120000 symbolic link
* S_IFREG 0100000 regular file
* S_IFBLK 0060000 block device
* S_IFDIR 0040000 directory
* S_IFCHR 0020000 character device
* S_IFIFO 0010000 fifo
* S_ISUID 0004000 set UID bit
* S_ISGID 0002000 set GID bit
* S_ISVTX 0001000 sticky bit
*
* S_IRWXU 00700 mask for file owner permissions
* S_IRUSR 00400 owner has read permission
* S_IWUSR 00200 owner has write permission
* S_IXUSR 00100 owner has execute permission
* S_IRWXG 00070 mask for group permissions
* S_IRGRP 00040 group has read permission
* S_IWGRP 00020 group has write permission
* S_IXGRP 00010 group has execute permission
* S_IRWXO 00007 mask for permissions for others (not in group)
* S_IROTH 00004 others have read permission
* S_IWOTH 00002 others have write permisson
* S_IXOTH 00001 others have execute permission
* </pre>
*/
public Integer permissions = null;
/**
* The ATIME attribute. Represented as seconds from Jan 1, 1970 in UTC.
* <code>NULL</code> if not present.
*/
public Integer atime = null;
/**
* The MTIME attribute. Represented as seconds from Jan 1, 1970 in UTC.
* <code>NULL</code> if not present.
*/
public Integer mtime = null;
/**
* Checks if this entry is a directory.
*
* @return Returns true if permissions are available and they indicate
* that this entry represents a directory.
*/
public boolean isDirectory()
{
if (permissions == null)
return false;
return ((permissions.intValue() & 0040000) != 0);
}
/**
* Checks if this entry is a regular file.
*
* @return Returns true if permissions are available and they indicate
* that this entry represents a regular file.
*/
public boolean isRegularFile()
{
if (permissions == null)
return false;
return ((permissions.intValue() & 0100000) != 0);
}
/**
* Checks if this entry is a a symlink.
*
* @return Returns true if permissions are available and they indicate
* that this entry represents a symlink.
*/
public boolean isSymlink()
{
if (permissions == null)
return false;
return ((permissions.intValue() & 0120000) != 0);
}
/**
* Turn the POSIX permissions into a 7 digit octal representation.
* Note: the returned value is first masked with <code>0177777</code>.
*
* @return <code>NULL</code> if permissions are not available.
*/
public String getOctalPermissions()
{
if (permissions == null)
return null;
String res = Integer.toString(permissions.intValue() & 0177777, 8);
StringBuffer sb = new StringBuffer();
int leadingZeros = 7 - res.length();
while (leadingZeros > 0)
{
sb.append('0');
leadingZeros--;
}
sb.append(res);
return sb.toString();
}
}

View File

@ -0,0 +1,45 @@
package com.trilead.ssh2;
/**
* A <code>SFTPv3FileHandle</code>.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: SFTPv3FileHandle.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class SFTPv3FileHandle
{
final SFTPv3Client client;
final byte[] fileHandle;
boolean isClosed = false;
/* The constructor is NOT public */
SFTPv3FileHandle(SFTPv3Client client, byte[] h)
{
this.client = client;
this.fileHandle = h;
}
/**
* Get the SFTPv3Client instance which created this handle.
*
* @return A SFTPv3Client instance.
*/
public SFTPv3Client getClient()
{
return client;
}
/**
* Check if this handle was closed with the {@link SFTPv3Client#closeFile(SFTPv3FileHandle)} method
* of the <code>SFTPv3Client</code> instance which created the handle.
*
* @return if the handle is closed.
*/
public boolean isClosed()
{
return isClosed;
}
}

View File

@ -0,0 +1,31 @@
package com.trilead.ssh2;
/**
* A callback interface used to implement a client specific method of checking
* server host keys.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: ServerHostKeyVerifier.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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.
* <p>
* 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 (<code>ssh-rsa</code> or <code>ssh-dss</code>)
* @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;
}

View File

@ -0,0 +1,453 @@
package com.trilead.ssh2;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import com.trilead.ssh2.channel.Channel;
import com.trilead.ssh2.channel.ChannelManager;
import com.trilead.ssh2.channel.X11ServerData;
/**
* A <code>Session</code> 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, plattner@trilead.com
* @version $Id: Session.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class Session
{
ChannelManager cm;
Channel cn;
boolean flag_pty_requested = false;
boolean flag_x11_requested = false;
boolean flag_execution_started = false;
boolean flag_closed = false;
String x11FakeCookie = null;
final SecureRandom rnd;
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
* <code>requestPTY("dumb", 0, 0, 0, 0, null)</code>.
*
* @throws IOException
*/
public void requestDumbPTY() throws IOException
{
requestPTY("dumb", 0, 0, 0, 0, null);
}
/**
* Basically just another wrapper for lazy people - identical to calling
* <code>requestPTY(term, 0, 0, 0, 0, null)</code>.
*
* @throws IOException
*/
public void requestPTY(String term) throws IOException
{
requestPTY(term, 0, 0, 0, 0, null);
}
/**
* Allocate a pseudo-terminal for this session.
* <p>
* This method may only be called before a program or shell is started in
* this session.
* <p>
* Different aspects can be specified:
* <p>
* <ul>
* <li>The TERM environment variable value (e.g., vt100)</li>
* <li>The terminal's dimensions.</li>
* <li>The encoded terminal modes.</li>
* </ul>
* Zero dimension parameters are ignored. The character/row dimensions
* override the pixel dimensions (when nonzero). Pixel dimensions refer to
* the drawable area of the window. The dimension parameters are only
* informational. The encoding of terminal modes (parameter
* <code>terminal_modes</code>) 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 <code>null</code>)
* @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);
}
/**
* Request X11 forwarding for the current session.
* <p>
* You have to supply the name and port of your X-server.
* <p>
* 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 our 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 */
StringBuffer tmp = new StringBuffer(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
{
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);
}
/**
* 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 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 <code>Session</code>. 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 <code>Session</code> (otherwise this method may
* block, even though more data is available).
*
* @param timeout
* The (non-negative) timeout in <code>ms</code>. <code>0</code> means no
* timeout, the call may block forever.
* @return
* <ul>
* <li><code>0</code> if no more data will arrive.</li>
* <li><code>1</code> if more data is available.</li>
* <li><code>-1</code> if a timeout occurred.</li>
* </ul>
*
* @throws IOException
* @deprecated This method has been replaced with a much more powerful wait-for-condition
* interface and therefore acts only as a wrapper.
*
*/
public int waitUntilDataAvailable(long timeout) throws IOException
{
if (timeout < 0)
throw new IllegalArgumentException("timeout must not be negative!");
int conditions = cm.waitForCondition(cn, timeout, ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA
| ChannelCondition.EOF);
if ((conditions & ChannelCondition.TIMEOUT) != 0)
return -1;
if ((conditions & (ChannelCondition.STDOUT_DATA | ChannelCondition.STDERR_DATA)) != 0)
return 1;
/* Here we do not need to check separately for CLOSED, since CLOSED implies EOF */
if ((conditions & ChannelCondition.EOF) != 0)
return 0;
throw new IllegalStateException("Unexpected condition result (" + conditions + ")");
}
/**
* This method blocks until certain conditions hold true on the underlying SSH-2 channel.
* <p>
* This method returns as soon as one of the following happens:
* <ul>
* <li>at least of the specified conditions (see {@link ChannelCondition}) holds true</li>
* <li>timeout > 0 and a timeout occured (TIMEOUT will be set in result conditions)</a>
* <li>the underlying channel was closed (CLOSED will be set in result conditions)</a>
* </ul>
* <p>
* In any case, the result value contains ALL current conditions, which may be more
* than the specified condition set (i.e., never use the "==" operator to test for conditions
* in the bitmask, see also comments in {@link ChannelCondition}).
* <p>
* Note: do NOT call this method if you want to wait for STDOUT_DATA or STDERR_DATA and
* there are concurrent threads (e.g., StreamGobblers) that operate on either of the two
* InputStreams of this <code>Session</code> (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, <code>0</code> means no timeout
* @return all bitmask specifying all current conditions that are true
*/
public int waitForCondition(int condition_set, long timeout)
{
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 <code<WaitForCondition</code> method).
*
* @return An <code>Integer</code> holding the exit code, or
* <code>null</code> 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 <code>String</code> holding the name of the signal, or
* <code>null</code> 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 <code>close()</code>
* 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)
{
}
}
}
}

View File

@ -0,0 +1,229 @@
package com.trilead.ssh2;
import java.io.IOException;
import java.io.InputStream;
/**
* A <code>StreamGobbler</code> 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.
* <p>
* 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).
* <p>
* 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:
* <p>
* If you do not call the StreamGobbler's <code>read()</code> 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.
* <p>
* 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, plattner@trilead.com
* @version $Id: StreamGobbler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class StreamGobbler extends InputStream
{
class GobblerThread extends Thread
{
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 GobblerThread t;
private 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;
t = new GobblerThread();
t.setDaemon(true);
t.start();
}
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)
{
}
}
int b = buffer[read_pos++] & 0xff;
return b;
}
}
public int available() throws IOException
{
synchronized (synchronizer)
{
if (isClosed)
throw new IOException("This StreamGobbler is closed.");
return write_pos - read_pos;
}
}
public int read(byte[] b) throws IOException
{
return read(b, 0, b.length);
}
public void close() throws IOException
{
synchronized (synchronizer)
{
if (isClosed)
return;
isClosed = true;
isEOF = true;
synchronizer.notifyAll();
is.close();
}
}
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)
{
}
}
int avail = write_pos - read_pos;
avail = (avail > len) ? len : avail;
System.arraycopy(buffer, read_pos, b, off, avail);
read_pos += avail;
return avail;
}
}
}

View File

@ -0,0 +1,419 @@
package com.trilead.ssh2.auth;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Vector;
import com.trilead.ssh2.InteractiveCallback;
import com.trilead.ssh2.crypto.PEMDecoder;
import com.trilead.ssh2.packets.PacketServiceAccept;
import com.trilead.ssh2.packets.PacketServiceRequest;
import com.trilead.ssh2.packets.PacketUserauthBanner;
import com.trilead.ssh2.packets.PacketUserauthFailure;
import com.trilead.ssh2.packets.PacketUserauthInfoRequest;
import com.trilead.ssh2.packets.PacketUserauthInfoResponse;
import com.trilead.ssh2.packets.PacketUserauthRequestInteractive;
import com.trilead.ssh2.packets.PacketUserauthRequestNone;
import com.trilead.ssh2.packets.PacketUserauthRequestPassword;
import com.trilead.ssh2.packets.PacketUserauthRequestPublicKey;
import com.trilead.ssh2.packets.Packets;
import com.trilead.ssh2.packets.TypesWriter;
import com.trilead.ssh2.signature.DSAPrivateKey;
import com.trilead.ssh2.signature.DSASHA1Verify;
import com.trilead.ssh2.signature.DSASignature;
import com.trilead.ssh2.signature.RSAPrivateKey;
import com.trilead.ssh2.signature.RSASHA1Verify;
import com.trilead.ssh2.signature.RSASignature;
import com.trilead.ssh2.transport.MessageHandler;
import com.trilead.ssh2.transport.TransportManager;
/**
* AuthenticationManager.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: AuthenticationManager.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public class AuthenticationManager implements MessageHandler
{
TransportManager tm;
Vector packets = new Vector();
boolean connectionClosed = false;
String banner;
String[] remainingMethods = new String[0];
boolean isPartialSuccess = false;
boolean authenticated = false;
boolean initDone = false;
public AuthenticationManager(TransportManager tm)
{
this.tm = tm;
}
boolean methodPossible(String methName)
{
if (remainingMethods == null)
return false;
for (int i = 0; i < remainingMethods.length; i++)
{
if (remainingMethods[i].compareTo(methName) == 0)
return true;
}
return false;
}
byte[] deQueue() throws IOException
{
synchronized (packets)
{
while (packets.size() == 0)
{
if (connectionClosed)
throw (IOException) new IOException("The connection is closed.").initCause(tm
.getReasonClosedCause());
try
{
packets.wait();
}
catch (InterruptedException ign)
{
}
}
/* This sequence works with J2ME */
byte[] res = (byte[]) packets.firstElement();
packets.removeElementAt(0);
return res;
}
}
byte[] getNextMessage() throws IOException
{
while (true)
{
byte[] msg = deQueue();
if (msg[0] != Packets.SSH_MSG_USERAUTH_BANNER)
return msg;
PacketUserauthBanner sb = new PacketUserauthBanner(msg, 0, msg.length);
banner = sb.getBanner();
}
}
public String[] getRemainingMethods(String user) throws IOException
{
initialize(user);
return remainingMethods;
}
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());
PacketUserauthRequestNone urn = new PacketUserauthRequestNone("ssh-connection", user);
tm.sendMessage(urn.getPayload());
byte[] msg = getNextMessage();
new PacketServiceAccept(msg, 0, msg.length);
msg = getNextMessage();
initDone = true;
if (msg[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (msg[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(msg, 0, msg.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + msg[0] + ")");
}
return authenticated;
}
public boolean authenticatePublicKey(String user, char[] PEMPrivateKey, String password, SecureRandom rnd)
throws IOException
{
try
{
initialize(user);
if (methodPossible("publickey") == false)
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[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Publickey authentication failed.").initCause(e);
}
}
public boolean authenticateNone(String user) throws IOException
{
try
{
initialize(user);
return authenticated;
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("None authentication failed.").initCause(e);
}
}
public boolean authenticatePassword(String user, String pass) throws IOException
{
try
{
initialize(user);
if (methodPossible("password") == false)
throw new IOException("Authentication method password not supported by the server at this stage.");
PacketUserauthRequestPassword ua = new PacketUserauthRequestPassword("ssh-connection", user, pass);
tm.sendMessage(ua.getPayload());
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Password authentication failed.").initCause(e);
}
}
public boolean authenticateInteractive(String user, String[] submethods, InteractiveCallback cb) throws IOException
{
try
{
initialize(user);
if (methodPossible("keyboard-interactive") == false)
throw new IOException(
"Authentication method keyboard-interactive not supported by the server at this stage.");
if (submethods == null)
submethods = new String[0];
PacketUserauthRequestInteractive ua = new PacketUserauthRequestInteractive("ssh-connection", user,
submethods);
tm.sendMessage(ua.getPayload());
while (true)
{
byte[] ar = getNextMessage();
if (ar[0] == Packets.SSH_MSG_USERAUTH_SUCCESS)
{
authenticated = true;
tm.removeMessageHandler(this, 0, 255);
return true;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_FAILURE)
{
PacketUserauthFailure puf = new PacketUserauthFailure(ar, 0, ar.length);
remainingMethods = puf.getAuthThatCanContinue();
isPartialSuccess = puf.isPartialSuccess();
return false;
}
if (ar[0] == Packets.SSH_MSG_USERAUTH_INFO_REQUEST)
{
PacketUserauthInfoRequest pui = new PacketUserauthInfoRequest(ar, 0, ar.length);
String[] responses;
try
{
responses = cb.replyToChallenge(pui.getName(), pui.getInstruction(), pui.getNumPrompts(), pui
.getPrompt(), pui.getEcho());
}
catch (Exception e)
{
throw (IOException) new IOException("Exception in callback.").initCause(e);
}
if (responses == null)
throw new IOException("Your callback may not return NULL!");
PacketUserauthInfoResponse puir = new PacketUserauthInfoResponse(responses);
tm.sendMessage(puir.getPayload());
continue;
}
throw new IOException("Unexpected SSH message (type " + ar[0] + ")");
}
}
catch (IOException e)
{
tm.close(e, false);
throw (IOException) new IOException("Keyboard-interactive authentication failed.").initCause(e);
}
}
public void handleMessage(byte[] msg, int msglen) throws IOException
{
synchronized (packets)
{
if (msg == null)
{
connectionClosed = true;
}
else
{
byte[] tmp = new byte[msglen];
System.arraycopy(msg, 0, tmp, 0, msglen);
packets.addElement(tmp);
}
packets.notifyAll();
if (packets.size() > 5)
{
connectionClosed = true;
throw new IOException("Error, peer is flooding us with authentication packets.");
}
}
}
}

View File

@ -0,0 +1,207 @@
package com.trilead.ssh2.channel;
/**
* Channel.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: Channel.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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.
*
*/
static final int STATE_OPENING = 1;
static final int STATE_OPEN = 2;
static final int STATE_CLOSED = 4;
static final int CHANNEL_BUFFER_SIZE = 30000;
/*
* 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;
// 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 String reasonClosed = null;
public Channel(ChannelManager cm)
{
this.cm = cm;
this.localWindow = CHANNEL_BUFFER_SIZE;
this.localMaxPacketSize = 35000 - 1024; // leave enough slack
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 String getReasonClosed()
{
synchronized (reasonClosedLock)
{
return reasonClosed;
}
}
public void setReasonClosed(String reasonClosed)
{
synchronized (reasonClosedLock)
{
if (this.reasonClosed == null)
this.reasonClosed = reasonClosed;
}
}
}

View File

@ -0,0 +1,86 @@
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.io.InputStream;
/**
* ChannelInputStream.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: ChannelInputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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;
}
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;
}
public void close() throws IOException
{
isClosed = true;
}
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;
}
public int read(byte[] b) throws IOException
{
return read(b, 0, b.length);
}
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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.io.OutputStream;
/**
* ChannelOutputStream.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: ChannelOutputStream.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public final class ChannelOutputStream extends OutputStream
{
Channel c;
boolean isClosed = false;
ChannelOutputStream(Channel c)
{
this.c = c;
}
public void write(int b) throws IOException
{
byte[] buff = new byte[1];
buff[0] = (byte) b;
write(buff, 0, 1);
}
public void close() throws IOException
{
if (isClosed == false)
{
isClosed = true;
c.cm.sendEOF(c);
}
}
public void flush() throws IOException
{
if (isClosed)
throw new IOException("This OutputStream is closed.");
/* This is a no-op, since this stream is unbuffered */
}
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);
}
public void write(byte[] b) throws IOException
{
write(b, 0, b.length);
}
}

View File

@ -0,0 +1,13 @@
package com.trilead.ssh2.channel;
/**
* IChannelWorkerThread.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: IChannelWorkerThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
interface IChannelWorkerThread
{
public void stopWorking();
}

View File

@ -0,0 +1,135 @@
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
/**
* LocalAcceptThread.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: LocalAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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 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, "Weird error during creation of StreamForwarder (" + e.getMessage() + ")",
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 e)
{
}
}
}

View File

@ -0,0 +1,103 @@
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.net.Socket;
import com.trilead.ssh2.log.Logger;
/**
* RemoteAcceptThread.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: RemoteAcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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;
if (log.isEnabled())
log.log(20, "RemoteAcceptThread: " + remoteConnectedAddress + "/" + remoteConnectedPort + ", R: "
+ remoteOriginatorAddress + "/" + remoteOriginatorPort);
}
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 e)
{
}
}
/* 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.log(50, "IOException in proxy code: " + e.getMessage());
try
{
c.cm.closeChannel(c, "IOException in proxy code (" + e.getMessage() + ")", true);
}
catch (IOException e1)
{
}
try
{
if (s != null)
s.close();
}
catch (IOException e1)
{
}
}
}
}

View File

@ -0,0 +1,17 @@
package com.trilead.ssh2.channel;
/**
* RemoteForwardingData. Data about a requested remote forwarding.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: RemoteForwardingData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class RemoteForwardingData
{
public String bindAddress;
public int bindPort;
String targetAddress;
int targetPort;
}

View File

@ -0,0 +1,240 @@
package com.trilead.ssh2.channel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import com.trilead.ssh2.log.Logger;
/**
* RemoteX11AcceptThread.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: RemoteX11AcceptThread.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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;
}
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
*/
/*
* 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(new String(authProtocolName)) == false)
throw new IOException("Unknown X11 authorization protocol!");
if (authProtocolDataLength != 16)
throw new IOException("Wrong data length for X11 authorization data!");
StringBuffer tmp = new StringBuffer(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 e)
{
}
}
/* 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.log(50, "IOException in X11 proxy code: " + e.getMessage());
try
{
c.cm.closeChannel(c, "IOException in X11 proxy code (" + e.getMessage() + ")", true);
}
catch (IOException e1)
{
}
try
{
if (s != null)
s.close();
}
catch (IOException e1)
{
}
}
}
}

View File

@ -0,0 +1,112 @@
package com.trilead.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, plattner@trilead.com
* @version $Id: StreamForwarder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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;
}
public void run()
{
try
{
while (true)
{
int len = is.read(buffer);
if (len <= 0)
break;
os.write(buffer, 0, len);
os.flush();
}
}
catch (IOException ignore)
{
try
{
c.cm.closeChannel(c, "Closed due to exception in StreamForwarder (" + mode + "): "
+ ignore.getMessage(), true);
}
catch (IOException e)
{
}
}
finally
{
try
{
os.close();
}
catch (IOException e1)
{
}
try
{
is.close();
}
catch (IOException e2)
{
}
if (sibling != null)
{
while (sibling.isAlive())
{
try
{
sibling.join();
}
catch (InterruptedException e)
{
}
}
try
{
c.cm.closeChannel(c, "StreamForwarder (" + mode + ") is cleaning up the connection", true);
}
catch (IOException e3)
{
}
try
{
if (s != null)
s.close();
}
catch (IOException e1)
{
}
}
}
}
}

View File

@ -0,0 +1,16 @@
package com.trilead.ssh2.channel;
/**
* X11ServerData. Data regarding an x11 forwarding target.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: X11ServerData.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*
*/
public class X11ServerData
{
public String hostname;
public int port;
public byte[] x11_magic_cookie; /* not the remote (fake) one, the local (real) one */
}

View File

@ -0,0 +1,148 @@
package com.trilead.ssh2.crypto;
import java.io.CharArrayWriter;
import java.io.IOException;
/**
* Basic Base64 Support.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: Base64.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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;
}
}

View File

@ -0,0 +1,23 @@
package com.trilead.ssh2.crypto;
import com.trilead.ssh2.crypto.cipher.BlockCipherFactory;
import com.trilead.ssh2.crypto.digest.MAC;
import com.trilead.ssh2.transport.KexManager;
/**
* CryptoWishList.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: CryptoWishList.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class CryptoWishList
{
public String[] kexAlgorithms = KexManager.getDefaultKexAlgorithmList();
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();
}

View File

@ -0,0 +1,91 @@
package com.trilead.ssh2.crypto;
import java.math.BigInteger;
import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
/**
* Establishes key material for iv/key/mac (both directions).
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: KeyMaterial.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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)
{
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 IllegalArgumentException
{
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;
}
}

View File

@ -0,0 +1,377 @@
package com.trilead.ssh2.crypto;
import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.IOException;
import java.math.BigInteger;
import com.trilead.ssh2.crypto.cipher.AES;
import com.trilead.ssh2.crypto.cipher.BlockCipher;
import com.trilead.ssh2.crypto.cipher.CBCMode;
import com.trilead.ssh2.crypto.cipher.DES;
import com.trilead.ssh2.crypto.cipher.DESede;
import com.trilead.ssh2.crypto.digest.MD5;
import com.trilead.ssh2.signature.DSAPrivateKey;
import com.trilead.ssh2.signature.RSAPrivateKey;
/**
* PEM Support.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PEMDecoder.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class PEMDecoder
{
private static final int PEM_RSA_PRIVATE_KEY = 1;
private static final int PEM_DSA_PRIVATE_KEY = 2;
private static final 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;
md5.digest(tmp, 0);
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 IOException("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 IOException("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 final 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 */
}
StringBuffer keyData = new StringBuffer();
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 final 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 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, password.getBytes());
}
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");
}
}

View File

@ -0,0 +1,17 @@
package com.trilead.ssh2.crypto;
/**
* Parsed PEM structure.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PEMStructure.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class PEMStructure
{
int pemType;
String dekInfo[];
String procType[];
byte[] data;
}

View File

@ -0,0 +1,160 @@
package com.trilead.ssh2.crypto;
import java.io.IOException;
import java.math.BigInteger;
/**
* SimpleDERReader.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: SimpleDERReader.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
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);
BigInteger bi = new BigInteger(b);
return bi;
}
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 + ")");
byte[] b = readBytes(len);
return b;
}
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 + ")");
byte[] b = readBytes(len);
return b;
}
}

View File

@ -0,0 +1,698 @@
package com.trilead.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.
* <p>
* For further details see: <a
* href="http://csrc.nist.gov/encryption/aes/">http://csrc.nist.gov/encryption/aes/
* </a>.
*
* This implementation is based on optimizations from Dr. Brian Gladman's paper
* and C code at <a
* href="http://fp.gladman.plus.com/cryptography_technology/rijndael/">http://fp.gladman.plus.com/cryptography_technology/rijndael/
* </a>
*
* 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
* <p>
* This file contains the fast version with 8Kbytes of static tables for round
* precomputation
*
* @author See comments in the source file
* @version $Id: AES.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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);
}
}

View File

@ -0,0 +1,16 @@
package com.trilead.ssh2.crypto.cipher;
/**
* BlockCipher.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: BlockCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public interface BlockCipher
{
public void init(boolean forEncryption, byte[] key);
public int getBlockSize();
public void transformBlock(byte[] src, int srcoff, byte[] dst, int dstoff);
}

View File

@ -0,0 +1,115 @@
package com.trilead.ssh2.crypto.cipher;
import java.util.Vector;
/**
* BlockCipherFactory.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: BlockCipherFactory.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class BlockCipherFactory
{
static 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;
}
}
static Vector<CipherEntry> ciphers = new Vector<CipherEntry>();
static
{
/* Higher Priority First */
ciphers.addElement(new CipherEntry("aes256-ctr", 16, 32, "com.trilead.ssh2.crypto.cipher.AES"));
ciphers.addElement(new CipherEntry("aes192-ctr", 16, 24, "com.trilead.ssh2.crypto.cipher.AES"));
ciphers.addElement(new CipherEntry("aes128-ctr", 16, 16, "com.trilead.ssh2.crypto.cipher.AES"));
ciphers.addElement(new CipherEntry("blowfish-ctr", 8, 16, "com.trileadssh2.crypto.cipher.BlowFish"));
ciphers.addElement(new CipherEntry("aes256-cbc", 16, 32, "com.trilead.ssh2.crypto.cipher.AES"));
ciphers.addElement(new CipherEntry("aes192-cbc", 16, 24, "com.trilead.ssh2.crypto.cipher.AES"));
ciphers.addElement(new CipherEntry("aes128-cbc", 16, 16, "com.trilead.ssh2.crypto.cipher.AES"));
ciphers.addElement(new CipherEntry("blowfish-cbc", 8, 16, "com.trilead.ssh2.crypto.cipher.BlowFish"));
ciphers.addElement(new CipherEntry("3des-ctr", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede"));
ciphers.addElement(new CipherEntry("3des-cbc", 8, 24, "com.trilead.ssh2.crypto.cipher.DESede"));
}
public static String[] getDefaultCipherList()
{
String list[] = new String[ciphers.size()];
for (int i = 0; i < ciphers.size(); i++)
{
CipherEntry ce = ciphers.elementAt(i);
list[i] = new String(ce.type);
}
return list;
}
public static void checkCipherList(String[] cipherCandidates)
{
for (int i = 0; i < cipherCandidates.length; i++)
getEntry(cipherCandidates[i]);
}
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 (Exception e)
{
throw new IllegalArgumentException("Cannot instantiate " + type);
}
}
private static CipherEntry getEntry(String type)
{
for (int i = 0; i < ciphers.size(); i++)
{
CipherEntry ce = ciphers.elementAt(i);
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;
}
}

View File

@ -0,0 +1,403 @@
package com.trilead.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 $Id: BlowFish.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
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);
}
}

View File

@ -0,0 +1,78 @@
package com.trilead.ssh2.crypto.cipher;
/**
* CBCMode.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: CBCMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
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);
}
}

View File

@ -0,0 +1,62 @@
package com.trilead.ssh2.crypto.cipher;
/**
* This is CTR mode as described in draft-ietf-secsh-newmodes-XY.txt
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: CTRMode.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
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;
}
}
}

View File

@ -0,0 +1,144 @@
package com.trilead.ssh2.crypto.cipher;
import java.io.IOException;
import java.io.InputStream;
/**
* CipherInputStream.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: CipherInputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
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.
*/
final int BUFF_SIZE = 2048;
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 = 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;
}
}

View File

@ -0,0 +1,142 @@
package com.trilead.ssh2.crypto.cipher;
import java.io.IOException;
import java.io.OutputStream;
/**
* CipherOutputStream.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: CipherOutputStream.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
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.
*/
final int BUFF_SIZE = 2048;
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 (IOException) new IOException("Error while decrypting block.").initCause(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);
}
}

View File

@ -0,0 +1,373 @@
package com.trilead.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 $Id: DES.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*
*/
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);
}
}

View File

@ -0,0 +1,105 @@
package com.trilead.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 $Id: DESede.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*
*/
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.
*/
public void init(boolean encrypting, byte[] key)
{
key1 = generateWorkingKey(encrypting, key, 0);
key2 = generateWorkingKey(!encrypting, key, 8);
key3 = generateWorkingKey(encrypting, key, 16);
encrypt = encrypting;
}
public String getAlgorithmName()
{
return "DESede";
}
public int getBlockSize()
{
return 8;
}
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);
}
}
public void reset()
{
}
}

View File

@ -0,0 +1,35 @@
package com.trilead.ssh2.crypto.cipher;
/**
* NullCipher.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: NullCipher.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
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);
}
}

View File

@ -0,0 +1,146 @@
package com.trilead.ssh2.crypto.dh;
import java.math.BigInteger;
import java.security.SecureRandom;
import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
import com.trilead.ssh2.log.Logger;
/**
* DhExchange.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: DhExchange.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
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 */
BigInteger f;
/* 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 init(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);
x = new BigInteger(p.bitLength() - 1, rnd);
e = g.modPow(x, p);
}
/**
* @return Returns the e.
* @throws IllegalStateException
*/
public BigInteger getE()
{
if (e == null)
throw new IllegalStateException("DhDsaExchange not initialized!");
return e;
}
/**
* @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!");
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)
{
HashForSSH2Types hash = new HashForSSH2Types("SHA1");
if (log.isEnabled())
{
log.log(90, "Client: '" + new String(clientversion) + "'");
log.log(90, "Server: '" + new String(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();
}
}

View File

@ -0,0 +1,113 @@
package com.trilead.ssh2.crypto.dh;
import java.math.BigInteger;
import java.security.SecureRandom;
import com.trilead.ssh2.DHGexParameters;
import com.trilead.ssh2.crypto.digest.HashForSSH2Types;
/**
* DhGroupExchange.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: DhGroupExchange.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
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)
{
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();
}
}

View File

@ -0,0 +1,25 @@
package com.trilead.ssh2.crypto.digest;
/**
* Digest.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: Digest.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
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);
public void digest(byte[] out, int off);
}

View File

@ -0,0 +1,95 @@
package com.trilead.ssh2.crypto.digest;
/**
* HMAC.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: HMAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
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)
{
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)
{
digest(out, 0);
}
public final void digest(byte[] out, int off)
{
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);
}
}

View File

@ -0,0 +1,93 @@
package com.trilead.ssh2.crypto.digest;
import java.math.BigInteger;
/**
* HashForSSH2Types.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: HashForSSH2Types.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
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("MD5"))
{
md = new MD5();
}
else
throw new IllegalArgumentException("Unknown algorithm " + type);
}
public void updateByte(byte b)
{
/* HACK - to test it with J2ME */
byte[] tmp = new byte[1];
tmp[0] = b;
md.update(tmp);
}
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()
{
byte[] tmp = new byte[md.getDigestLength()];
getDigest(tmp);
return tmp;
}
public void getDigest(byte[] out)
{
getDigest(out, 0);
}
public void getDigest(byte[] out, int off)
{
md.digest(out, off);
}
}

View File

@ -0,0 +1,88 @@
package com.trilead.ssh2.crypto.digest;
/**
* MAC.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: MAC.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public final class MAC
{
Digest mac;
int size;
public final static String[] getMacList()
{
/* Higher Priority First */
return new String[] { "hmac-sha1-96", "hmac-sha1", "hmac-md5-96", "hmac-md5" };
}
public final static void checkMacList(String[] macs)
{
for (int i = 0; i < macs.length; i++)
getKeyLen(macs[i]);
}
public final static int getKeyLen(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;
throw new IllegalArgumentException("Unkown algorithm " + type);
}
public MAC(String type, byte[] key)
{
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
throw new IllegalArgumentException("Unkown algorithm " + type);
size = mac.getDigestLength();
}
public final void initMac(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)
{
mac.digest(out, off);
}
public final int size()
{
return size;
}
}

View File

@ -0,0 +1,268 @@
package com.trilead.ssh2.crypto.digest;
/**
* MD5. Based on the example code in RFC 1321. Optimized (...a little).
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: MD5.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
/*
* The following disclaimer has been copied from RFC 1321:
*
* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All rights
* reserved.
*
* License to copy and use this software is granted provided that it is
* identified as the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in
* all material mentioning or referencing this software or this function.
*
* License is also granted to make and use derivative works provided that such
* works are identified as "derived from the RSA Data Security, Inc. MD5
* Message-Digest Algorithm" in all material mentioning or referencing the
* derived work.
*
* RSA Data Security, Inc. makes no representations concerning either the
* merchantability of this software or the suitability of this software for any
* particular purpose. It is provided "as is" without express or implied
* warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* documentation and/or software.
*
*/
public final class MD5 implements Digest
{
private int state0, state1, state2, state3;
private long count;
private final byte[] block = new byte[64];
private final int x[] = new int[16];
private static final byte[] padding = new byte[] { (byte) 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
public MD5()
{
reset();
}
private static final int FF(int a, int b, int c, int d, int x, int s, int ac)
{
a += ((b & c) | ((~b) & d)) + x + ac;
return ((a << s) | (a >>> (32 - s))) + b;
}
private static final int GG(int a, int b, int c, int d, int x, int s, int ac)
{
a += ((b & d) | (c & (~d))) + x + ac;
return ((a << s) | (a >>> (32 - s))) + b;
}
private static final int HH(int a, int b, int c, int d, int x, int s, int ac)
{
a += (b ^ c ^ d) + x + ac;
return ((a << s) | (a >>> (32 - s))) + b;
}
private static final int II(int a, int b, int c, int d, int x, int s, int ac)
{
a += (c ^ (b | (~d))) + x + ac;
return ((a << s) | (a >>> (32 - s))) + b;
}
private static final void encode(byte[] dst, int dstoff, int word)
{
dst[dstoff] = (byte) (word);
dst[dstoff + 1] = (byte) (word >> 8);
dst[dstoff + 2] = (byte) (word >> 16);
dst[dstoff + 3] = (byte) (word >> 24);
}
private final void transform(byte[] src, int pos)
{
int a = state0;
int b = state1;
int c = state2;
int d = state3;
for (int i = 0; i < 16; i++, pos += 4)
{
x[i] = (src[pos] & 0xff) | ((src[pos + 1] & 0xff) << 8) | ((src[pos + 2] & 0xff) << 16)
| ((src[pos + 3] & 0xff) << 24);
}
/* Round 1 */
a = FF(a, b, c, d, x[0], 7, 0xd76aa478); /* 1 */
d = FF(d, a, b, c, x[1], 12, 0xe8c7b756); /* 2 */
c = FF(c, d, a, b, x[2], 17, 0x242070db); /* 3 */
b = FF(b, c, d, a, x[3], 22, 0xc1bdceee); /* 4 */
a = FF(a, b, c, d, x[4], 7, 0xf57c0faf); /* 5 */
d = FF(d, a, b, c, x[5], 12, 0x4787c62a); /* 6 */
c = FF(c, d, a, b, x[6], 17, 0xa8304613); /* 7 */
b = FF(b, c, d, a, x[7], 22, 0xfd469501); /* 8 */
a = FF(a, b, c, d, x[8], 7, 0x698098d8); /* 9 */
d = FF(d, a, b, c, x[9], 12, 0x8b44f7af); /* 10 */
c = FF(c, d, a, b, x[10], 17, 0xffff5bb1); /* 11 */
b = FF(b, c, d, a, x[11], 22, 0x895cd7be); /* 12 */
a = FF(a, b, c, d, x[12], 7, 0x6b901122); /* 13 */
d = FF(d, a, b, c, x[13], 12, 0xfd987193); /* 14 */
c = FF(c, d, a, b, x[14], 17, 0xa679438e); /* 15 */
b = FF(b, c, d, a, x[15], 22, 0x49b40821); /* 16 */
/* Round 2 */
a = GG(a, b, c, d, x[1], 5, 0xf61e2562); /* 17 */
d = GG(d, a, b, c, x[6], 9, 0xc040b340); /* 18 */
c = GG(c, d, a, b, x[11], 14, 0x265e5a51); /* 19 */
b = GG(b, c, d, a, x[0], 20, 0xe9b6c7aa); /* 20 */
a = GG(a, b, c, d, x[5], 5, 0xd62f105d); /* 21 */
d = GG(d, a, b, c, x[10], 9, 0x2441453); /* 22 */
c = GG(c, d, a, b, x[15], 14, 0xd8a1e681); /* 23 */
b = GG(b, c, d, a, x[4], 20, 0xe7d3fbc8); /* 24 */
a = GG(a, b, c, d, x[9], 5, 0x21e1cde6); /* 25 */
d = GG(d, a, b, c, x[14], 9, 0xc33707d6); /* 26 */
c = GG(c, d, a, b, x[3], 14, 0xf4d50d87); /* 27 */
b = GG(b, c, d, a, x[8], 20, 0x455a14ed); /* 28 */
a = GG(a, b, c, d, x[13], 5, 0xa9e3e905); /* 29 */
d = GG(d, a, b, c, x[2], 9, 0xfcefa3f8); /* 30 */
c = GG(c, d, a, b, x[7], 14, 0x676f02d9); /* 31 */
b = GG(b, c, d, a, x[12], 20, 0x8d2a4c8a); /* 32 */
/* Round 3 */
a = HH(a, b, c, d, x[5], 4, 0xfffa3942); /* 33 */
d = HH(d, a, b, c, x[8], 11, 0x8771f681); /* 34 */
c = HH(c, d, a, b, x[11], 16, 0x6d9d6122); /* 35 */
b = HH(b, c, d, a, x[14], 23, 0xfde5380c); /* 36 */
a = HH(a, b, c, d, x[1], 4, 0xa4beea44); /* 37 */
d = HH(d, a, b, c, x[4], 11, 0x4bdecfa9); /* 38 */
c = HH(c, d, a, b, x[7], 16, 0xf6bb4b60); /* 39 */
b = HH(b, c, d, a, x[10], 23, 0xbebfbc70); /* 40 */
a = HH(a, b, c, d, x[13], 4, 0x289b7ec6); /* 41 */
d = HH(d, a, b, c, x[0], 11, 0xeaa127fa); /* 42 */
c = HH(c, d, a, b, x[3], 16, 0xd4ef3085); /* 43 */
b = HH(b, c, d, a, x[6], 23, 0x4881d05); /* 44 */
a = HH(a, b, c, d, x[9], 4, 0xd9d4d039); /* 45 */
d = HH(d, a, b, c, x[12], 11, 0xe6db99e5); /* 46 */
c = HH(c, d, a, b, x[15], 16, 0x1fa27cf8); /* 47 */
b = HH(b, c, d, a, x[2], 23, 0xc4ac5665); /* 48 */
/* Round 4 */
a = II(a, b, c, d, x[0], 6, 0xf4292244); /* 49 */
d = II(d, a, b, c, x[7], 10, 0x432aff97); /* 50 */
c = II(c, d, a, b, x[14], 15, 0xab9423a7); /* 51 */
b = II(b, c, d, a, x[5], 21, 0xfc93a039); /* 52 */
a = II(a, b, c, d, x[12], 6, 0x655b59c3); /* 53 */
d = II(d, a, b, c, x[3], 10, 0x8f0ccc92); /* 54 */
c = II(c, d, a, b, x[10], 15, 0xffeff47d); /* 55 */
b = II(b, c, d, a, x[1], 21, 0x85845dd1); /* 56 */
a = II(a, b, c, d, x[8], 6, 0x6fa87e4f); /* 57 */
d = II(d, a, b, c, x[15], 10, 0xfe2ce6e0); /* 58 */
c = II(c, d, a, b, x[6], 15, 0xa3014314); /* 59 */
b = II(b, c, d, a, x[13], 21, 0x4e0811a1); /* 60 */
a = II(a, b, c, d, x[4], 6, 0xf7537e82); /* 61 */
d = II(d, a, b, c, x[11], 10, 0xbd3af235); /* 62 */
c = II(c, d, a, b, x[2], 15, 0x2ad7d2bb); /* 63 */
b = II(b, c, d, a, x[9], 21, 0xeb86d391); /* 64 */
state0 += a;
state1 += b;
state2 += c;
state3 += d;
}
public final void reset()
{
count = 0;
state0 = 0x67452301;
state1 = 0xefcdab89;
state2 = 0x98badcfe;
state3 = 0x10325476;
/* Clear traces in memory... */
for (int i = 0; i < 16; i++)
x[i] = 0;
}
public final void update(byte b)
{
final int space = 64 - ((int) (count & 0x3f));
count++;
block[64 - space] = b;
if (space == 1)
transform(block, 0);
}
public final void update(byte[] buff, int pos, int len)
{
int space = 64 - ((int) (count & 0x3f));
count += len;
while (len > 0)
{
if (len < space)
{
System.arraycopy(buff, pos, block, 64 - space, len);
break;
}
if (space == 64)
{
transform(buff, pos);
}
else
{
System.arraycopy(buff, pos, block, 64 - space, space);
transform(block, 0);
}
pos += space;
len -= space;
space = 64;
}
}
public final void update(byte[] b)
{
update(b, 0, b.length);
}
public final void digest(byte[] dst, int pos)
{
byte[] bits = new byte[8];
encode(bits, 0, (int) (count << 3));
encode(bits, 4, (int) (count >> 29));
int idx = (int) count & 0x3f;
int padLen = (idx < 56) ? (56 - idx) : (120 - idx);
update(padding, 0, padLen);
update(bits, 0, 8);
encode(dst, pos, state0);
encode(dst, pos + 4, state1);
encode(dst, pos + 8, state2);
encode(dst, pos + 12, state3);
reset();
}
public final void digest(byte[] dst)
{
digest(dst, 0);
}
public final int getDigestLength()
{
return 16;
}
}

View File

@ -0,0 +1,664 @@
package com.trilead.ssh2.crypto.digest;
/**
* SHA-1 implementation based on FIPS PUB 180-1.
* Highly optimized.
* <p>
* (http://www.itl.nist.gov/fipspubs/fip180-1.htm)
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: SHA1.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
*/
public final class SHA1 implements Digest
{
private int H0, H1, H2, H3, H4;
private final int[] w = new int[80];
private int currentPos;
private long currentLen;
public SHA1()
{
reset();
}
public final int getDigestLength()
{
return 20;
}
public final void reset()
{
H0 = 0x67452301;
H1 = 0xEFCDAB89;
H2 = 0x98BADCFE;
H3 = 0x10325476;
H4 = 0xC3D2E1F0;
currentPos = 0;
currentLen = 0;
/* In case of complete paranoia, we should also wipe out the
* information contained in the w[] array */
}
public final void update(byte b[])
{
update(b, 0, b.length);
}
public final void update(byte b[], int off, int len)
{
if (len >= 4)
{
int idx = currentPos >> 2;
switch (currentPos & 3)
{
case 0:
w[idx] = (((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
len -= 4;
currentPos += 4;
currentLen += 32;
if (currentPos == 64)
{
perform();
currentPos = 0;
}
break;
case 1:
w[idx] = (w[idx] << 24) | (((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
len -= 3;
currentPos += 3;
currentLen += 24;
if (currentPos == 64)
{
perform();
currentPos = 0;
}
break;
case 2:
w[idx] = (w[idx] << 16) | (((b[off++] & 0xff) << 8) | (b[off++] & 0xff));
len -= 2;
currentPos += 2;
currentLen += 16;
if (currentPos == 64)
{
perform();
currentPos = 0;
}
break;
case 3:
w[idx] = (w[idx] << 8) | (b[off++] & 0xff);
len--;
currentPos++;
currentLen += 8;
if (currentPos == 64)
{
perform();
currentPos = 0;
}
break;
}
/* Now currentPos is a multiple of 4 - this is the place to be...*/
while (len >= 8)
{
w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
| (b[off++] & 0xff);
currentPos += 4;
if (currentPos == 64)
{
perform();
currentPos = 0;
}
w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
| (b[off++] & 0xff);
currentPos += 4;
if (currentPos == 64)
{
perform();
currentPos = 0;
}
currentLen += 64;
len -= 8;
}
while (len < 0) //(len >= 4)
{
w[currentPos >> 2] = ((b[off++] & 0xff) << 24) | ((b[off++] & 0xff) << 16) | ((b[off++] & 0xff) << 8)
| (b[off++] & 0xff);
len -= 4;
currentPos += 4;
currentLen += 32;
if (currentPos == 64)
{
perform();
currentPos = 0;
}
}
}
/* Remaining bytes (1-3) */
while (len > 0)
{
/* Here is room for further improvements */
int idx = currentPos >> 2;
w[idx] = (w[idx] << 8) | (b[off++] & 0xff);
currentLen += 8;
currentPos++;
if (currentPos == 64)
{
perform();
currentPos = 0;
}
len--;
}
}
public final void update(byte b)
{
int idx = currentPos >> 2;
w[idx] = (w[idx] << 8) | (b & 0xff);
currentLen += 8;
currentPos++;
if (currentPos == 64)
{
perform();
currentPos = 0;
}
}
private final void putInt(byte[] b, int pos, int val)
{
b[pos] = (byte) (val >> 24);
b[pos + 1] = (byte) (val >> 16);
b[pos + 2] = (byte) (val >> 8);
b[pos + 3] = (byte) val;
}
public final void digest(byte[] out)
{
digest(out, 0);
}
public final void digest(byte[] out, int off)
{
/* Pad with a '1' and 7-31 zero bits... */
int idx = currentPos >> 2;
w[idx] = ((w[idx] << 8) | (0x80)) << ((3 - (currentPos & 3)) << 3);
currentPos = (currentPos & ~3) + 4;
if (currentPos == 64)
{
currentPos = 0;
perform();
}
else if (currentPos == 60)
{
currentPos = 0;
w[15] = 0;
perform();
}
/* Now currentPos is a multiple of 4 and we can do the remaining
* padding much more efficiently, furthermore we are sure
* that currentPos <= 56.
*/
for (int i = currentPos >> 2; i < 14; i++)
w[i] = 0;
w[14] = (int) (currentLen >> 32);
w[15] = (int) currentLen;
perform();
putInt(out, off, H0);
putInt(out, off + 4, H1);
putInt(out, off + 8, H2);
putInt(out, off + 12, H3);
putInt(out, off + 16, H4);
reset();
}
private final void perform()
{
for (int t = 16; t < 80; t++)
{
int x = w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16];
w[t] = ((x << 1) | (x >>> 31));
}
int A = H0;
int B = H1;
int C = H2;
int D = H3;
int E = H4;
/* Here we use variable substitution and loop unrolling
*
* === Original step:
*
* T = s5(A) + f(B,C,D) + E + w[0] + K;
* E = D; D = C; C = s30(B); B = A; A = T;
*
* === Rewritten step:
*
* T = s5(A + f(B,C,D) + E + w[0] + K;
* B = s30(B);
* E = D; D = C; C = B; B = A; A = T;
*
* === Let's rewrite things, introducing new variables:
*
* E0 = E; D0 = D; C0 = C; B0 = B; A0 = A;
*
* T = s5(A0) + f(B0,C0,D0) + E0 + w[0] + K;
* B0 = s30(B0);
* E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = T;
*
* T = s5(A1) + f(B1,C1,D1) + E1 + w[1] + K;
* B1 = s30(B1);
* E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = T;
*
* E = E2; D = E2; C = C2; B = B2; A = A2;
*
* === No need for 'T', we can write into 'Ex' instead since
* after the calculation of 'T' nobody is interested
* in 'Ex' anymore.
*
* E0 = E; D0 = D; C0 = C; B0 = B; A0 = A;
*
* E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K;
* B0 = s30(B0);
* E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0;
*
* E1 = E1 + s5(A1) + f(B1,C1,D1) + w[1] + K;
* B1 = s30(B1);
* E2 = D1; D2 = C1; C2 = B1; B2 = A1; A2 = E1;
*
* E = Ex; D = Ex; C = Cx; B = Bx; A = Ax;
*
* === Further optimization: get rid of the swap operations
* Idea: instead of swapping the variables, swap the names of
* the used variables in the next step:
*
* E0 = E; D0 = d; C0 = C; B0 = B; A0 = A;
*
* E0 = E0 + s5(A0) + f(B0,C0,D0) + w[0] + K;
* B0 = s30(B0);
* // E1 = D0; D1 = C0; C1 = B0; B1 = A0; A1 = E0;
*
* D0 = D0 + s5(E0) + f(A0,B0,C0) + w[1] + K;
* A0 = s30(A0);
* E2 = C0; D2 = B0; C2 = A0; B2 = E0; A2 = D0;
*
* E = E2; D = D2; C = C2; B = B2; A = A2;
*
* === OK, let's do this several times, also, directly
* use A (instead of A0) and B,C,D,E.
*
* E = E + s5(A) + f(B,C,D) + w[0] + K;
* B = s30(B);
* // E1 = D; D1 = C; C1 = B; B1 = A; A1 = E;
*
* D = D + s5(E) + f(A,B,C) + w[1] + K;
* A = s30(A);
* // E2 = C; D2 = B; C2 = A; B2 = E; A2 = D;
*
* C = C + s5(D) + f(E,A,B) + w[2] + K;
* E = s30(E);
* // E3 = B; D3 = A; C3 = E; B3 = D; A3 = C;
*
* B = B + s5(C) + f(D,E,A) + w[3] + K;
* D = s30(D);
* // E4 = A; D4 = E; C4 = D; B4 = C; A4 = B;
*
* A = A + s5(B) + f(C,D,E) + w[4] + K;
* C = s30(C);
* // E5 = E; D5 = D; C5 = C; B5 = B; A5 = A;
*
* //E = E5; D = D5; C = C5; B = B5; A = A5;
*
* === Very nice, after 5 steps each variable
* has the same contents as after 5 steps with
* the original algorithm!
*
* We therefore can easily unroll each interval,
* as the number of steps in each interval is a
* multiple of 5 (20 steps per interval).
*/
E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[0] + 0x5A827999;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[1] + 0x5A827999;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[2] + 0x5A827999;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[3] + 0x5A827999;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[4] + 0x5A827999;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[5] + 0x5A827999;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[6] + 0x5A827999;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[7] + 0x5A827999;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[8] + 0x5A827999;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[9] + 0x5A827999;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[10] + 0x5A827999;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[11] + 0x5A827999;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[12] + 0x5A827999;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[13] + 0x5A827999;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[14] + 0x5A827999;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + ((B & C) | ((~B) & D)) + w[15] + 0x5A827999;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + ((A & B) | ((~A) & C)) + w[16] + 0x5A827999;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + ((E & A) | ((~E) & B)) + w[17] + 0x5A827999;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + ((D & E) | ((~D) & A)) + w[18] + 0x5A827999;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + ((C & D) | ((~C) & E)) + w[19] + 0x5A827999;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[20] + 0x6ED9EBA1;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[21] + 0x6ED9EBA1;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[22] + 0x6ED9EBA1;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[23] + 0x6ED9EBA1;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[24] + 0x6ED9EBA1;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[25] + 0x6ED9EBA1;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[26] + 0x6ED9EBA1;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[27] + 0x6ED9EBA1;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[28] + 0x6ED9EBA1;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[29] + 0x6ED9EBA1;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[30] + 0x6ED9EBA1;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[31] + 0x6ED9EBA1;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[32] + 0x6ED9EBA1;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[33] + 0x6ED9EBA1;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[34] + 0x6ED9EBA1;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[35] + 0x6ED9EBA1;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[36] + 0x6ED9EBA1;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[37] + 0x6ED9EBA1;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[38] + 0x6ED9EBA1;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[39] + 0x6ED9EBA1;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[40] + 0x8F1BBCDC;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[41] + 0x8F1BBCDC;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[42] + 0x8F1BBCDC;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[43] + 0x8F1BBCDC;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[44] + 0x8F1BBCDC;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[45] + 0x8F1BBCDC;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[46] + 0x8F1BBCDC;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[47] + 0x8F1BBCDC;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[48] + 0x8F1BBCDC;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[49] + 0x8F1BBCDC;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[50] + 0x8F1BBCDC;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[51] + 0x8F1BBCDC;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[52] + 0x8F1BBCDC;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[53] + 0x8F1BBCDC;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[54] + 0x8F1BBCDC;
C = ((C << 30) | (C >>> 2));
E = E + ((A << 5) | (A >>> 27)) + ((B & C) | (B & D) | (C & D)) + w[55] + 0x8F1BBCDC;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + ((A & B) | (A & C) | (B & C)) + w[56] + 0x8F1BBCDC;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + ((E & A) | (E & B) | (A & B)) + w[57] + 0x8F1BBCDC;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + ((D & E) | (D & A) | (E & A)) + w[58] + 0x8F1BBCDC;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + ((C & D) | (C & E) | (D & E)) + w[59] + 0x8F1BBCDC;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[60] + 0xCA62C1D6;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[61] + 0xCA62C1D6;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[62] + 0xCA62C1D6;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[63] + 0xCA62C1D6;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[64] + 0xCA62C1D6;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[65] + 0xCA62C1D6;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[66] + 0xCA62C1D6;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[67] + 0xCA62C1D6;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[68] + 0xCA62C1D6;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[69] + 0xCA62C1D6;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[70] + 0xCA62C1D6;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[71] + 0xCA62C1D6;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[72] + 0xCA62C1D6;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[73] + 0xCA62C1D6;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[74] + 0xCA62C1D6;
C = ((C << 30) | (C >>> 2));
E += ((A << 5) | (A >>> 27)) + (B ^ C ^ D) + w[75] + 0xCA62C1D6;
B = ((B << 30) | (B >>> 2));
D += ((E << 5) | (E >>> 27)) + (A ^ B ^ C) + w[76] + 0xCA62C1D6;
A = ((A << 30) | (A >>> 2));
C += ((D << 5) | (D >>> 27)) + (E ^ A ^ B) + w[77] + 0xCA62C1D6;
E = ((E << 30) | (E >>> 2));
B += ((C << 5) | (C >>> 27)) + (D ^ E ^ A) + w[78] + 0xCA62C1D6;
D = ((D << 30) | (D >>> 2));
A += ((B << 5) | (B >>> 27)) + (C ^ D ^ E) + w[79] + 0xCA62C1D6;
C = ((C << 30) | (C >>> 2));
H0 += A;
H1 += B;
H2 += C;
H3 += D;
H4 += E;
// debug(80, H0, H1, H2, H3, H4);
}
private static final String toHexString(byte[] b)
{
final String hexChar = "0123456789ABCDEF";
StringBuffer sb = new StringBuffer();
for (int i = 0; i < b.length; i++)
{
sb.append(hexChar.charAt((b[i] >> 4) & 0x0f));
sb.append(hexChar.charAt(b[i] & 0x0f));
}
return sb.toString();
}
public static void main(String[] args)
{
SHA1 sha = new SHA1();
byte[] dig1 = new byte[20];
byte[] dig2 = new byte[20];
byte[] dig3 = new byte[20];
/*
* We do not specify a charset name for getBytes(), since we assume that
* the JVM's default encoder maps the _used_ ASCII characters exactly as
* getBytes("US-ASCII") would do. (Ah, yes, too lazy to catch the
* exception that can be thrown by getBytes("US-ASCII")). Note: This has
* no effect on the SHA-1 implementation, this is just for the following
* test code.
*/
sha.update("abc".getBytes());
sha.digest(dig1);
sha.update("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq".getBytes());
sha.digest(dig2);
for (int i = 0; i < 1000000; i++)
sha.update((byte) 'a');
sha.digest(dig3);
String dig1_res = toHexString(dig1);
String dig2_res = toHexString(dig2);
String dig3_res = toHexString(dig3);
String dig1_ref = "A9993E364706816ABA3E25717850C26C9CD0D89D";
String dig2_ref = "84983E441C3BD26EBAAE4AA1F95129E5E54670F1";
String dig3_ref = "34AA973CD4C4DAA4F61EEB2BDBAD27316534016F";
if (dig1_res.equals(dig1_ref))
System.out.println("SHA-1 Test 1 OK.");
else
System.out.println("SHA-1 Test 1 FAILED.");
if (dig2_res.equals(dig2_ref))
System.out.println("SHA-1 Test 2 OK.");
else
System.out.println("SHA-1 Test 2 FAILED.");
if (dig3_res.equals(dig3_ref))
System.out.println("SHA-1 Test 3 OK.");
else
System.out.println("SHA-1 Test 3 FAILED.");
if (dig3_res.equals(dig3_ref))
System.out.println("SHA-1 Test 3 OK.");
else
System.out.println("SHA-1 Test 3 FAILED.");
}
}

View File

@ -0,0 +1,49 @@
package com.trilead.ssh2.log;
/**
* Logger - a very simple logger, mainly used during development.
* Is not based on log4j (to reduce external dependencies).
* However, if needed, something like log4j could easily be
* hooked in.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: Logger.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
*/
public class Logger
{
private static final boolean enabled = false;
private static final int logLevel = 99;
private String className;
public final static Logger getLogger(Class x)
{
return new Logger(x);
}
public Logger(Class x)
{
this.className = x.getName();
}
public final boolean isEnabled()
{
return enabled;
}
public final void log(int level, String message)
{
if ((enabled) && (level <= logLevel))
{
long now = System.currentTimeMillis();
synchronized (this)
{
System.err.println(now + " : " + className + ": " + message);
// or send it to log4j or whatever...
}
}
}
}

View File

@ -0,0 +1,66 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
/**
* PacketChannelOpenConfirmation.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketChannelOpenConfirmation.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketChannelOpenConfirmation
{
byte[] payload;
public int recipientChannelID;
public int senderChannelID;
public int initialWindowSize;
public int maxPacketSize;
public PacketChannelOpenConfirmation(int recipientChannelID, int senderChannelID, int initialWindowSize,
int maxPacketSize)
{
this.recipientChannelID = recipientChannelID;
this.senderChannelID = senderChannelID;
this.initialWindowSize = initialWindowSize;
this.maxPacketSize = maxPacketSize;
}
public PacketChannelOpenConfirmation(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN_CONFIRMATION)
throw new IOException(
"This is not a SSH_MSG_CHANNEL_OPEN_CONFIRMATION! ("
+ packet_type + ")");
recipientChannelID = tr.readUINT32();
senderChannelID = tr.readUINT32();
initialWindowSize = tr.readUINT32();
maxPacketSize = tr.readUINT32();
if (tr.remain() != 0)
throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN_CONFIRMATION packet!");
}
public byte[] getPayload()
{
if (payload == null)
{
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();
}
return payload;
}
}

View File

@ -0,0 +1,66 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
/**
* PacketChannelOpenFailure.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketChannelOpenFailure.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketChannelOpenFailure
{
byte[] payload;
public int recipientChannelID;
public int reasonCode;
public String description;
public String languageTag;
public PacketChannelOpenFailure(int recipientChannelID, int reasonCode, String description,
String languageTag)
{
this.recipientChannelID = recipientChannelID;
this.reasonCode = reasonCode;
this.description = description;
this.languageTag = languageTag;
}
public PacketChannelOpenFailure(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
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 + ")");
recipientChannelID = tr.readUINT32();
reasonCode = tr.readUINT32();
description = tr.readString();
languageTag = tr.readString();
if (tr.remain() != 0)
throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN_FAILURE packet!");
}
public byte[] getPayload()
{
if (payload == null)
{
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();
}
return payload;
}
}

View File

@ -0,0 +1,57 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
/**
* PacketChannelWindowAdjust.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketChannelWindowAdjust.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketChannelWindowAdjust
{
byte[] payload;
public int recipientChannelID;
public int windowChange;
public PacketChannelWindowAdjust(int recipientChannelID, int windowChange)
{
this.recipientChannelID = recipientChannelID;
this.windowChange = windowChange;
}
public PacketChannelWindowAdjust(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
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 + ")");
recipientChannelID = tr.readUINT32();
windowChange = tr.readUINT32();
if (tr.remain() != 0)
throw new IOException("Padding in SSH_MSG_CHANNEL_WINDOW_ADJUST packet!");
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_CHANNEL_WINDOW_ADJUST);
tw.writeUINT32(recipientChannelID);
tw.writeUINT32(windowChange);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,56 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
/**
* PacketDisconnect.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketDisconnect.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketDisconnect
{
byte[] payload;
int reason;
String desc;
String lang;
public PacketDisconnect(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_DISCONNECT)
throw new IOException("This is not a Disconnect Packet! ("
+ packet_type + ")");
reason = tr.readUINT32();
desc = tr.readString();
lang = tr.readString();
}
public PacketDisconnect(int reason, String desc, String lang)
{
this.reason = reason;
this.desc = desc;
this.lang = lang;
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_DISCONNECT);
tw.writeUINT32(reason);
tw.writeString(desc);
tw.writeString(lang);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,41 @@
package com.trilead.ssh2.packets;
/**
* PacketGlobalCancelForwardRequest.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketGlobalCancelForwardRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketGlobalCancelForwardRequest
{
byte[] payload;
public boolean wantReply;
public String bindAddress;
public int bindPort;
public PacketGlobalCancelForwardRequest(boolean wantReply, String bindAddress, int bindPort)
{
this.wantReply = wantReply;
this.bindAddress = bindAddress;
this.bindPort = bindPort;
}
public byte[] getPayload()
{
if (payload == null)
{
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();
}
return payload;
}
}

View File

@ -0,0 +1,41 @@
package com.trilead.ssh2.packets;
/**
* PacketGlobalForwardRequest.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketGlobalForwardRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketGlobalForwardRequest
{
byte[] payload;
public boolean wantReply;
public String bindAddress;
public int bindPort;
public PacketGlobalForwardRequest(boolean wantReply, String bindAddress, int bindPort)
{
this.wantReply = wantReply;
this.bindAddress = bindAddress;
this.bindPort = bindPort;
}
public byte[] getPayload()
{
if (payload == null)
{
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();
}
return payload;
}
}

View File

@ -0,0 +1,59 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
/**
* PacketIgnore.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketIgnore.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketIgnore
{
byte[] payload;
byte[] data;
public void setData(byte[] data)
{
this.data = data;
payload = null;
}
public PacketIgnore()
{
}
public PacketIgnore(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_IGNORE)
throw new IOException("This is not a SSH_MSG_IGNORE packet! (" + packet_type + ")");
/* Could parse String body */
}
public byte[] getPayload()
{
if (payload == null)
{
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();
}
return payload;
}
}

View File

@ -0,0 +1,33 @@
package com.trilead.ssh2.packets;
import java.math.BigInteger;
/**
* PacketKexDHInit.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketKexDHInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketKexDHInit
{
byte[] payload;
BigInteger e;
public PacketKexDHInit(BigInteger e)
{
this.e = e;
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_KEXDH_INIT);
tw.writeMPInt(e);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,55 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
import java.math.BigInteger;
/**
* PacketKexDHReply.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketKexDHReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketKexDHReply
{
byte[] payload;
byte[] hostKey;
BigInteger f;
byte[] signature;
public PacketKexDHReply(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_KEXDH_REPLY)
throw new IOException("This is not a SSH_MSG_KEXDH_REPLY! ("
+ packet_type + ")");
hostKey = tr.readByteString();
f = tr.readMPINT();
signature = tr.readByteString();
if (tr.remain() != 0) throw new IOException("PADDING IN SSH_MSG_KEXDH_REPLY!");
}
public BigInteger getF()
{
return f;
}
public byte[] getHostKey()
{
return hostKey;
}
public byte[] getSignature()
{
return signature;
}
}

View File

@ -0,0 +1,50 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
import java.math.BigInteger;
/**
* PacketKexDhGexGroup.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketKexDhGexGroup.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketKexDhGexGroup
{
byte[] payload;
BigInteger p;
BigInteger g;
public PacketKexDhGexGroup(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_GROUP)
throw new IllegalArgumentException(
"This is not a SSH_MSG_KEX_DH_GEX_GROUP! (" + packet_type
+ ")");
p = tr.readMPINT();
g = tr.readMPINT();
if (tr.remain() != 0)
throw new IOException("PADDING IN SSH_MSG_KEX_DH_GEX_GROUP!");
}
public BigInteger getG()
{
return g;
}
public BigInteger getP()
{
return p;
}
}

View File

@ -0,0 +1,33 @@
package com.trilead.ssh2.packets;
import java.math.BigInteger;
/**
* PacketKexDhGexInit.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketKexDhGexInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketKexDhGexInit
{
byte[] payload;
BigInteger e;
public PacketKexDhGexInit(BigInteger e)
{
this.e = e;
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_INIT);
tw.writeMPInt(e);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,56 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
import java.math.BigInteger;
/**
* PacketKexDhGexReply.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketKexDhGexReply.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketKexDhGexReply
{
byte[] payload;
byte[] hostKey;
BigInteger f;
byte[] signature;
public PacketKexDhGexReply(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_KEX_DH_GEX_REPLY)
throw new IOException("This is not a SSH_MSG_KEX_DH_GEX_REPLY! (" + packet_type + ")");
hostKey = tr.readByteString();
f = tr.readMPINT();
signature = tr.readByteString();
if (tr.remain() != 0)
throw new IOException("PADDING IN SSH_MSG_KEX_DH_GEX_REPLY!");
}
public BigInteger getF()
{
return f;
}
public byte[] getHostKey()
{
return hostKey;
}
public byte[] getSignature()
{
return signature;
}
}

View File

@ -0,0 +1,39 @@
package com.trilead.ssh2.packets;
import com.trilead.ssh2.DHGexParameters;
/**
* PacketKexDhGexRequest.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketKexDhGexRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketKexDhGexRequest
{
byte[] payload;
int min;
int n;
int max;
public PacketKexDhGexRequest(DHGexParameters para)
{
this.min = para.getMin_group_len();
this.n = para.getPref_group_len();
this.max = para.getMax_group_len();
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST);
tw.writeUINT32(min);
tw.writeUINT32(n);
tw.writeUINT32(max);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,34 @@
package com.trilead.ssh2.packets;
import com.trilead.ssh2.DHGexParameters;
/**
* PacketKexDhGexRequestOld.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketKexDhGexRequestOld.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketKexDhGexRequestOld
{
byte[] payload;
int n;
public PacketKexDhGexRequestOld(DHGexParameters para)
{
this.n = para.getPref_group_len();
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_KEX_DH_GEX_REQUEST_OLD);
tw.writeUINT32(n);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,165 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
import java.security.SecureRandom;
import com.trilead.ssh2.crypto.CryptoWishList;
import com.trilead.ssh2.transport.KexParameters;
/**
* PacketKexInit.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketKexInit.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketKexInit
{
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 = new String[] { "none" };
kp.compression_algorithms_server_to_client = new String[] { "none" };
kp.languages_client_to_server = new String[] {};
kp.languages_server_to_client = new String[] {};
kp.first_kex_packet_follows = false;
kp.reserved_field1 = 0;
}
public PacketKexInit(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_KEXINIT)
throw new IOException("This is not a KexInitPacket! (" + 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 IOException("Padding in KexInitPacket!");
}
public byte[] getPayload()
{
if (payload == null)
{
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();
}
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;
}
}

View File

@ -0,0 +1,46 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
/**
* PacketNewKeys.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketNewKeys.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketNewKeys
{
byte[] payload;
public PacketNewKeys()
{
}
public PacketNewKeys(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_NEWKEYS)
throw new IOException("This is not a SSH_MSG_NEWKEYS! ("
+ packet_type + ")");
if (tr.remain() != 0)
throw new IOException("Padding in SSH_MSG_NEWKEYS packet!");
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_NEWKEYS);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,56 @@
package com.trilead.ssh2.packets;
/**
* PacketOpenDirectTCPIPChannel.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketOpenDirectTCPIPChannel.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketOpenDirectTCPIPChannel
{
byte[] payload;
int channelID;
int initialWindowSize;
int maxPacketSize;
String host_to_connect;
int port_to_connect;
String originator_IP_address;
int originator_port;
public PacketOpenDirectTCPIPChannel(int channelID, int initialWindowSize, int maxPacketSize,
String host_to_connect, int port_to_connect, String originator_IP_address,
int originator_port)
{
this.channelID = channelID;
this.initialWindowSize = initialWindowSize;
this.maxPacketSize = maxPacketSize;
this.host_to_connect = host_to_connect;
this.port_to_connect = port_to_connect;
this.originator_IP_address = originator_IP_address;
this.originator_port = originator_port;
}
public byte[] getPayload()
{
if (payload == null)
{
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();
}
return payload;
}
}

View File

@ -0,0 +1,62 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
/**
* PacketOpenSessionChannel.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketOpenSessionChannel.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketOpenSessionChannel
{
byte[] payload;
int channelID;
int initialWindowSize;
int maxPacketSize;
public PacketOpenSessionChannel(int channelID, int initialWindowSize,
int maxPacketSize)
{
this.channelID = channelID;
this.initialWindowSize = initialWindowSize;
this.maxPacketSize = maxPacketSize;
}
public PacketOpenSessionChannel(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_CHANNEL_OPEN)
throw new IOException("This is not a SSH_MSG_CHANNEL_OPEN! ("
+ packet_type + ")");
channelID = tr.readUINT32();
initialWindowSize = tr.readUINT32();
maxPacketSize = tr.readUINT32();
if (tr.remain() != 0)
throw new IOException("Padding in SSH_MSG_CHANNEL_OPEN packet!");
}
public byte[] getPayload()
{
if (payload == null)
{
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();
}
return payload;
}
}

View File

@ -0,0 +1,52 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
/**
* PacketServiceAccept.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketServiceAccept.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketServiceAccept
{
byte[] payload;
String serviceName;
public PacketServiceAccept(String serviceName)
{
this.serviceName = serviceName;
}
public PacketServiceAccept(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_SERVICE_ACCEPT)
throw new IOException("This is not a SSH_MSG_SERVICE_ACCEPT! ("
+ packet_type + ")");
serviceName = tr.readString();
if (tr.remain() != 0)
throw new IOException("Padding in SSH_MSG_SERVICE_ACCEPT packet!");
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_SERVICE_ACCEPT);
tw.writeString(serviceName);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,52 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
/**
* PacketServiceRequest.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketServiceRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketServiceRequest
{
byte[] payload;
String serviceName;
public PacketServiceRequest(String serviceName)
{
this.serviceName = serviceName;
}
public PacketServiceRequest(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_SERVICE_REQUEST)
throw new IOException("This is not a SSH_MSG_SERVICE_REQUEST! ("
+ packet_type + ")");
serviceName = tr.readString();
if (tr.remain() != 0)
throw new IOException("Padding in SSH_MSG_SERVICE_REQUEST packet!");
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_SERVICE_REQUEST);
tw.writeString(serviceName);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,39 @@
package com.trilead.ssh2.packets;
/**
* PacketSessionExecCommand.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketSessionExecCommand.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketSessionExecCommand
{
byte[] payload;
public int recipientChannelID;
public boolean wantReply;
public String command;
public PacketSessionExecCommand(int recipientChannelID, boolean wantReply, String command)
{
this.recipientChannelID = recipientChannelID;
this.wantReply = wantReply;
this.command = command;
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
tw.writeUINT32(recipientChannelID);
tw.writeString("exec");
tw.writeBoolean(wantReply);
tw.writeString(command);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,57 @@
package com.trilead.ssh2.packets;
/**
* PacketSessionPtyRequest.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketSessionPtyRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketSessionPtyRequest
{
byte[] payload;
public int recipientChannelID;
public boolean wantReply;
public String term;
public int character_width;
public int character_height;
public int pixel_width;
public int pixel_height;
public byte[] terminal_modes;
public PacketSessionPtyRequest(int recipientChannelID, boolean wantReply, String term,
int character_width, int character_height, int pixel_width, int pixel_height,
byte[] terminal_modes)
{
this.recipientChannelID = recipientChannelID;
this.wantReply = wantReply;
this.term = term;
this.character_width = character_width;
this.character_height = character_height;
this.pixel_width = pixel_width;
this.pixel_height = pixel_height;
this.terminal_modes = terminal_modes;
}
public byte[] getPayload()
{
if (payload == null)
{
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();
}
return payload;
}
}

View File

@ -0,0 +1,36 @@
package com.trilead.ssh2.packets;
/**
* PacketSessionStartShell.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketSessionStartShell.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketSessionStartShell
{
byte[] payload;
public int recipientChannelID;
public boolean wantReply;
public PacketSessionStartShell(int recipientChannelID, boolean wantReply)
{
this.recipientChannelID = recipientChannelID;
this.wantReply = wantReply;
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_CHANNEL_REQUEST);
tw.writeUINT32(recipientChannelID);
tw.writeString("shell");
tw.writeBoolean(wantReply);
payload = tw.getBytes();
}
return payload;
}
}

View File

@ -0,0 +1,40 @@
package com.trilead.ssh2.packets;
/**
* PacketSessionSubsystemRequest.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketSessionSubsystemRequest.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketSessionSubsystemRequest
{
byte[] payload;
public int recipientChannelID;
public boolean wantReply;
public String subsystem;
public PacketSessionSubsystemRequest(int recipientChannelID, boolean wantReply, String subsystem)
{
this.recipientChannelID = recipientChannelID;
this.wantReply = wantReply;
this.subsystem = subsystem;
}
public byte[] getPayload()
{
if (payload == null)
{
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();
tw.getBytes(payload);
}
return payload;
}
}

View File

@ -0,0 +1,53 @@
package com.trilead.ssh2.packets;
/**
* PacketSessionX11Request.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketSessionX11Request.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketSessionX11Request
{
byte[] payload;
public int recipientChannelID;
public boolean wantReply;
public boolean singleConnection;
String x11AuthenticationProtocol;
String x11AuthenticationCookie;
int x11ScreenNumber;
public PacketSessionX11Request(int recipientChannelID, boolean wantReply, boolean singleConnection,
String x11AuthenticationProtocol, String x11AuthenticationCookie, int x11ScreenNumber)
{
this.recipientChannelID = recipientChannelID;
this.wantReply = wantReply;
this.singleConnection = singleConnection;
this.x11AuthenticationProtocol = x11AuthenticationProtocol;
this.x11AuthenticationCookie = x11AuthenticationCookie;
this.x11ScreenNumber = x11ScreenNumber;
}
public byte[] getPayload()
{
if (payload == null)
{
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();
}
return payload;
}
}

View File

@ -0,0 +1,60 @@
package com.trilead.ssh2.packets;
import java.io.IOException;
/**
* PacketUserauthBanner.
*
* @author Christian Plattner, plattner@trilead.com
* @version $Id: PacketUserauthBanner.java,v 1.1 2007/10/15 12:49:55 cplattne Exp $
*/
public class PacketUserauthBanner
{
byte[] payload;
String message;
String language;
public PacketUserauthBanner(String message, String language)
{
this.message = message;
this.language = language;
}
public String getBanner()
{
return message;
}
public PacketUserauthBanner(byte payload[], int off, int len) throws IOException
{
this.payload = new byte[len];
System.arraycopy(payload, off, this.payload, 0, len);
TypesReader tr = new TypesReader(payload, off, len);
int packet_type = tr.readByte();
if (packet_type != Packets.SSH_MSG_USERAUTH_BANNER)
throw new IOException("This is not a SSH_MSG_USERAUTH_BANNER! (" + packet_type + ")");
message = tr.readString("UTF-8");
language = tr.readString();
if (tr.remain() != 0)
throw new IOException("Padding in SSH_MSG_USERAUTH_REQUEST packet!");
}
public byte[] getPayload()
{
if (payload == null)
{
TypesWriter tw = new TypesWriter();
tw.writeByte(Packets.SSH_MSG_USERAUTH_BANNER);
tw.writeString(message);
tw.writeString(language);
payload = tw.getBytes();
}
return payload;
}
}

Some files were not shown because too many files have changed in this diff Show More