/*
* Copyright (C) 2005 Luca Veltri - University of Parma - Italy
*
* This file is part of MjSip (http://www.mjsip.org)
*
* MjSip 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.
*
* MjSip 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 MjSip; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Author(s):
* Luca Veltri (luca.veltri@unipr.it)
*/
package org.zoolu.tools;
import java.io.*;
import java.util.Random;
/** OTP (One Time Pad) encryption algorithm based on MD5 hash function.
* It uses a PRG (Pseudo-Random-Generator) function in OFB (Output Feadback)
* to genarate a byte-stream (the OTP) that is XORed with the plaintext.
*<br> The PRG is based on MD5.
*
* <p> The OTP is calculated starting from a key and an IV, as follows:
*<br> h_0=hash(skey|iv)
*<br> h_i=hash(skey|h_i-1)
*
* <p> where:
*<br> hash(.)==MD5(.)
*<br> skey==key
*
* <p> while the ciphertext is calculated as follows:
*<br> c_0=iv
*<br> c_i=m_i XOR h_i with i=1,2,..
*
* <p> Note that optionally it could modified initializing the skey as follows:
*<br> skey==hash(key|iv)
*<br> in order to not keep in memory the secret key for long time
*/
public class MD5OTP
{
/** Block size in bytes */
static int size;
/** the OTP stream key */
byte[] skey;
/** pseudorandom-stream (OTP) block */
byte[] h;
/** index within a single block */
int index;
/** Creates a new MD5OTP */
/*public MD5OTP(int bsize, byte[] key, byte[] iv)
{ init(bsize,key,iv);
}*/
/** Creates a new MD5OTP */
public MD5OTP(byte[] skey, byte[] iv)
{ init(16,skey,iv);
}
/** Creates a new MD5OTP with IV=0 */
public MD5OTP(byte[] skey)
{ init(16,skey,null);
}
/** Inits the MD5OTP algorithm */
private void init(int size, byte[] skey, byte[] iv)
{ this.size=size;
if (iv==null) { iv=new byte[size]; for (int i=0; i<size; i++) iv[i]=0; }
this.skey=skey;
//skey=hash(cat(key,iv));
h=clone(iv);
index=size-1;
}
/** Makes a clone of a byte array */
private static byte[] clone(byte[] bb)
{ byte[] cc=new byte[bb.length];
for (int i=0; i<bb.length; i++) cc[i]=bb[i];
return cc;
}
/** Concatenates two byte arrays */
private static byte[] cat(byte[] aa, byte[] bb)
{ byte[] cc=new byte[aa.length+bb.length];
for (int i=0; i<aa.length; i++) cc[i]=aa[i];
for (int i=0; i<bb.length; i++) cc[i+aa.length]=bb[i];
return cc;
}
/** Returns a sub array */
private static byte[] sub(byte[] bb, int begin, int end)
{ byte[] cc=new byte[end-begin];
for (int i=begin; i<end; i++) cc[i-begin]=bb[i];
return cc;
}
/** Return an hash of the byte array */
private static byte[] hash(byte[] bb)
{ return MD5.digest(bb);
}
/** Encrypts a block of bytes */
public byte[] update(byte[] m)
{ byte[] c=new byte[m.length];
for (int i=0; i<m.length; i++)
{ index++;
if (index==size)
{ // calculate new h_block
h=hash(cat(skey,h));
index=0;
}
c[i]=(byte)(m[i]^h[index]);
}
return c;
}
/** Encrypts a byte stream */
public void update(InputStream in, OutputStream out)
{ byte[] buff=new byte[2048];
int len;
try
{ while ((len=in.read(buff))>0)
out.write(update(sub(buff,0,len)));
}
catch (IOException e) { e.printStackTrace(); }
}
/** Encrypts an array of bytes. An IV is chosen and saved at top. */
public static byte[] encrypt(byte[] m, byte[] key)
{ // choose a random IV
byte[] iv=MD5.digest(Long.toString((new Random()).nextLong()));
// do encryption
byte[] c=(new MD5OTP(key,iv)).update(m);
return cat(iv,c);
}
/** Decrypts an array of bytes with the IV at top. */
public static byte[] decrypt(byte[] c, byte[] key)
{ // read the IV
byte[] iv=sub(c,0,16);
byte[] buf=sub(c,16,c.length);
return (new MD5OTP(key,iv)).update(buf);
}
/** Returns an hex representation of the byte array */
private static String asHex(byte[] bb)
{ StringBuffer buf=new StringBuffer(bb.length*2);
for (int i=0; i<bb.length; i++)
{ if (((int)bb[i] & 0xff) < 0x10) buf.append("0");
//buf.append(Long.toString((int)bb[i] & 0xff, 16));
buf.append(Integer.toHexString((int)bb[i] & 0xff));
}
return buf.toString();
}
public static void main(String[] args)
{
if (args.length<2)
{
System.exit(0);
}
byte[] msg=args[0].getBytes();
byte[] key=args[1].getBytes();
byte[] iv=null;
if (args.length>2) iv=args[2].getBytes();
byte[] cip=(new MD5OTP(key,iv)).update(msg);
cip=(new MD5OTP(key,iv)).update(cip);
}
}