Initial import.
parent
edfccaafe3
commit
dfa41d0902
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
|
@ -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>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 6.0 KiB |
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 >= 1024 and <= 8192
|
||||
*/
|
||||
public DHGexParameters(int pref_group_len)
|
||||
{
|
||||
if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
|
||||
throw new IllegalArgumentException("pref_group_len out of range!");
|
||||
|
||||
this.pref_group_len = pref_group_len;
|
||||
this.min_group_len = 0;
|
||||
this.max_group_len = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* This constructor can be used to force the sending of a
|
||||
* <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 >= 1024 and <= 8192. Furthermore,
|
||||
* min_group_len <= pref_group_len <= max_group_len.
|
||||
*
|
||||
* @param min_group_len
|
||||
* @param pref_group_len
|
||||
* @param max_group_len
|
||||
*/
|
||||
public DHGexParameters(int min_group_len, int pref_group_len, int max_group_len)
|
||||
{
|
||||
if ((min_group_len < MIN_ALLOWED) || (min_group_len > MAX_ALLOWED))
|
||||
throw new IllegalArgumentException("min_group_len out of range!");
|
||||
|
||||
if ((pref_group_len < MIN_ALLOWED) || (pref_group_len > MAX_ALLOWED))
|
||||
throw new IllegalArgumentException("pref_group_len out of range!");
|
||||
|
||||
if ((max_group_len < MIN_ALLOWED) || (max_group_len > MAX_ALLOWED))
|
||||
throw new IllegalArgumentException("max_group_len out of range!");
|
||||
|
||||
if ((pref_group_len < min_group_len) || (pref_group_len > max_group_len))
|
||||
throw new IllegalArgumentException("pref_group_len is incompatible with min and max!");
|
||||
|
||||
if (max_group_len < min_group_len)
|
||||
throw new IllegalArgumentException("max_group_len must not be smaller than min_group_len!");
|
||||
|
||||
this.min_group_len = min_group_len;
|
||||
this.pref_group_len = pref_group_len;
|
||||
this.max_group_len = max_group_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum group length.
|
||||
*
|
||||
* @return the maximum group length, may be <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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 */
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
|
@ -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...
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue