/*
* This file is part of "The Java Telnet Application".
*
* (c) Matthias L. Jugel, Marcus Mei�ner 1996-2002. All Rights Reserved.
* The file was changed by Radek Polak to work as midlet in MIDP 1.0
*
* Please visit http://javatelnet.org/ for updates and contact.
*
* --LICENSE NOTICE--
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* --LICENSE NOTICE--
*
* Information about the base of this code:
* Xuejia Lai: On the Design and Security of Block Ciphers, ETH
* Series in Information Processing, vol. 1, Hartung-Gorre
* Verlag, Konstanz, Switzerland, 1992. Another source was Bruce
* Schneier: Applied Cryptography, John Wiley & Sons, 1994.
*
* The IDEA mathematical formula may be covered by one or more of the
* following patents: PCT/CH91/00117, EP 0 482 154 B1, US Pat. 5,214,703.
*/
package ssh.v1;
/**
* @author Marcus Meissner
* @version $Id$
*/
public final class IDEA extends Cipher {
protected int[] key_schedule = new int[52];
protected int IV0 = 0;
protected int IV1 = 0;
public void encrypt( byte[] src, int srcOff, byte[] dest, int destOff, int len ) {
int[] out = new int[2];
int iv0 = IV0;
int iv1 = IV1;
int end = srcOff + len;
for ( int si = srcOff, di = destOff; si < end; si += 8, di += 8 ) {
encrypt( iv0, iv1, out );
iv0 = out[0];
iv1 = out[1];
iv0 ^= ( ( src[si + 3] & 0xff ) | ( ( src[si + 2] & 0xff ) << 8 ) | ( ( src[si + 1] & 0xff ) << 16 ) | ( ( src[si] & 0xff ) << 24 ) );
iv1 ^= ( ( src[si + 7] & 0xff ) | ( ( src[si + 6] & 0xff ) << 8 ) | ( ( src[si + 5] & 0xff ) << 16 ) | ( ( src[si + 4] & 0xff ) << 24 ) );
if ( di + 8 <= end ) {
dest[di + 3] = (byte) ( iv0 & 0xff );
dest[di + 2] = (byte) ( ( iv0 >>> 8 ) & 0xff );
dest[di + 1] = (byte) ( ( iv0 >>> 16 ) & 0xff );
dest[di] = (byte) ( ( iv0 >>> 24 ) & 0xff );
dest[di + 7] = (byte) ( iv1 & 0xff );
dest[di + 6] = (byte) ( ( iv1 >>> 8 ) & 0xff );
dest[di + 5] = (byte) ( ( iv1 >>> 16 ) & 0xff );
dest[di + 4] = (byte) ( ( iv1 >>> 24 ) & 0xff );
}
else {
switch ( end - di ) {
case 7:
dest[di + 6] = (byte) ( ( iv1 >>> 8 ) & 0xff );
case 6:
dest[di + 5] = (byte) ( ( iv1 >>> 16 ) & 0xff );
case 5:
dest[di + 4] = (byte) ( ( iv1 >>> 24 ) & 0xff );
case 4:
dest[di + 3] = (byte) ( iv0 & 0xff );
case 3:
dest[di + 2] = (byte) ( ( iv0 >>> 8 ) & 0xff );
case 2:
dest[di + 1] = (byte) ( ( iv0 >>> 16 ) & 0xff );
case 1:
dest[di] = (byte) ( ( iv0 >>> 24 ) & 0xff );
}
}
}
IV0 = iv0;
IV1 = iv1;
}
public void decrypt( byte[] src, int srcOff, byte[] dest, int destOff, int len ) {
int[] out = new int[2];
int iv0 = IV0;
int iv1 = IV1;
int plain0, plain1;
int end = srcOff + len;
for ( int si = srcOff, di = destOff; si < end; si += 8, di += 8 ) {
decrypt( iv0, iv1, out );
iv0 = ( ( src[si + 3] & 0xff ) | ( ( src[si + 2] & 0xff ) << 8 ) | ( ( src[si + 1] & 0xff ) << 16 ) | ( ( src[si] & 0xff ) << 24 ) );
iv1 = ( ( src[si + 7] & 0xff ) | ( ( src[si + 6] & 0xff ) << 8 ) | ( ( src[si + 5] & 0xff ) << 16 ) | ( ( src[si + 4] & 0xff ) << 24 ) );
plain0 = out[0] ^ iv0;
plain1 = out[1] ^ iv1;
if ( di + 8 <= end ) {
dest[di + 3] = (byte) ( plain0 & 0xff );
dest[di + 2] = (byte) ( ( plain0 >>> 8 ) & 0xff );
dest[di + 1] = (byte) ( ( plain0 >>> 16 ) & 0xff );
dest[di] = (byte) ( ( plain0 >>> 24 ) & 0xff );
dest[di + 7] = (byte) ( plain1 & 0xff );
dest[di + 6] = (byte) ( ( plain1 >>> 8 ) & 0xff );
dest[di + 5] = (byte) ( ( plain1 >>> 16 ) & 0xff );
dest[di + 4] = (byte) ( ( plain1 >>> 24 ) & 0xff );
}
else {
switch ( end - di ) {
case 7:
dest[di + 6] = (byte) ( ( plain1 >>> 8 ) & 0xff );
case 6:
dest[di + 5] = (byte) ( ( plain1 >>> 16 ) & 0xff );
case 5:
dest[di + 4] = (byte) ( ( plain1 >>> 24 ) & 0xff );
case 4:
dest[di + 3] = (byte) ( plain0 & 0xff );
case 3:
dest[di + 2] = (byte) ( ( plain0 >>> 8 ) & 0xff );
case 2:
dest[di + 1] = (byte) ( ( plain0 >>> 16 ) & 0xff );
case 1:
dest[di] = (byte) ( ( plain0 >>> 24 ) & 0xff );
}
}
}
IV0 = iv0;
IV1 = iv1;
}
public void setKey( byte[] key ) {
int i, ki = 0, j = 0;
for ( i = 0; i < 8; i++ )
key_schedule[i] = ( ( key[2 * i] & 0xff ) << 8 ) | ( key[( 2 * i ) + 1] & 0xff );
for ( i = 8, j = 0; i < 52; i++ ) {
j++;
key_schedule[ki + j + 7] = ( ( key_schedule[ki + ( j & 7 )] << 9 ) | ( key_schedule[ki + ( ( j + 1 ) & 7 )] >>> 7 ) ) & 0xffff;
ki += j & 8;
j &= 7;
}
}
public final void encrypt( int l, int r, int[] out ) {
int t1 = 0, t2 = 0, x1, x2, x3, x4, ki = 0;
x1 = ( l >>> 16 );
x2 = ( l & 0xffff );
x3 = ( r >>> 16 );
x4 = ( r & 0xffff );
for ( int round = 0; round < 8; round++ ) {
x1 = mulop( x1 & 0xffff, key_schedule[ki++] );
x2 = ( x2 + key_schedule[ki++] );
x3 = ( x3 + key_schedule[ki++] );
x4 = mulop( x4 & 0xffff, key_schedule[ki++] );
t1 = ( x1 ^ x3 );
t2 = ( x2 ^ x4 );
t1 = mulop( t1 & 0xffff, key_schedule[ki++] );
t2 = ( t1 + t2 );
t2 = mulop( t2 & 0xffff, key_schedule[ki++] );
t1 = ( t1 + t2 );
x1 = ( x1 ^ t2 );
x4 = ( x4 ^ t1 );
t1 = ( t1 ^ x2 );
x2 = ( t2 ^ x3 );
x3 = t1;
}
t2 = x2;
x1 = mulop( x1 & 0xffff, key_schedule[ki++] );
x2 = ( t1 + key_schedule[ki++] );
x3 = ( ( t2 + key_schedule[ki++] ) & 0xffff );
x4 = mulop( x4 & 0xffff, key_schedule[ki] );
out[0] = ( x1 << 16 ) | ( x2 & 0xffff );
out[1] = ( x3 << 16 ) | ( x4 & 0xffff );
}
public final void decrypt( int l, int r, int[] out ) {
encrypt( l, r, out );
}
public static final int mulop( int a, int b ) {
int ab = a * b;
if ( ab != 0 ) {
int lo = ab & 0xffff;
int hi = ( ab >>> 16 ) & 0xffff;
return ( ( lo - hi ) + ( ( lo < hi ) ? 1 : 0 ) );
}
if ( a == 0 )
return ( 1 - b );
return ( 1 - a );
}
/*
* !!! REMOVE DEBUG !!!
*
* public static void main(String[] argv) { byte[] key = { (byte) 0xd3,
* (byte) 0x96, (byte) 0xcf, (byte) 0x07, (byte) 0xfa, (byte) 0xa2, (byte)
* 0x64, (byte) 0xfe, (byte) 0xf3, (byte) 0xa2, (byte) 0x06, (byte) 0x07,
* (byte) 0x1a, (byte) 0xb6, (byte) 0x13, (byte) 0xf6 };
*
* byte[] txt = { (byte) 0x2e, (byte) 0xbe, (byte) 0xc5, (byte) 0xac, (byte)
* 0x02, (byte) 0xa1, (byte) 0xd5, (byte) 0x7f, (byte) 0x01, (byte) 0x00,
* (byte) 0x00, (byte) 0x00, (byte) 0x1f, (byte) 0x43, (byte) 0x6f, (byte)
* 0x72, (byte) 0x72, (byte) 0x75, (byte) 0x70, (byte) 0x74, (byte) 0x65,
* (byte) 0x64, (byte) 0x20, (byte) 0x63, (byte) 0x68, (byte) 0x65, (byte)
* 0x63, (byte) 0x6b, (byte) 0x20, (byte) 0x62, (byte) 0x79, (byte) 0x74,
* (byte) 0x65, (byte) 0x73, (byte) 0x20, (byte) 0x6f, (byte) 0x6e, (byte)
* 0x20, (byte) 0x69, (byte) 0x6e, (byte) 0x70, (byte) 0x75, (byte) 0x74,
* (byte) 0x2e, (byte) 0x91, (byte) 0x9a, (byte) 0x57, (byte) 0xdd };
*
* byte[] enc; byte[] dec;
*
* System.out.println("key: " + printHex(key)); System.out.println("txt: " +
* printHex(txt));
*
* IDEA cipher = new IDEA(); cipher.setKey(key);
*
* for (int i = 0; i < 52; i++) { if ((i & 0x7) == 0)
* System.out.println(""); System.out.print(" " + cipher.key_schedule[i]); }
*
* enc = cipher.encrypt(txt); System.out.println("enc: " + printHex(enc));
*
* cipher = new IDEA(); cipher.setKey(key); dec = cipher.decrypt(enc);
*
* System.out.println("dec: " + printHex(dec)); }
*
* static String printHex(byte[] buf) { byte[] out = new byte[buf.length +
* 1]; out[0] = 0; System.arraycopy(buf, 0, out, 1, buf.length); BigInteger
* big = new BigInteger(out); return big.toString(16); }
*/
}