From: peli0101 Date: Sat, 28 Mar 2009 23:03:22 +0000 (+0000) Subject: OI Safe: Include Trivium stream cipher from estreamJ. X-Git-Url: https://hydra-www.ietfng.org/gitweb/?a=commitdiff_plain;h=6f0bc0e9132847b2ed1fb5dbdcc890d6cc5b7757;p=android-vcpass-oisafe OI Safe: Include Trivium stream cipher from estreamJ. git-svn-id: http://openintents.googlecode.com/svn/trunk/Safe@2006 72b678ce-9140-0410-bee8-679b907dd61a --- diff --git a/src/estreamj/ciphers/trivium/Trivium.java b/src/estreamj/ciphers/trivium/Trivium.java new file mode 100644 index 0000000..9b7d1f1 --- /dev/null +++ b/src/estreamj/ciphers/trivium/Trivium.java @@ -0,0 +1,219 @@ + +package estreamj.ciphers.trivium; + +import estreamj.framework.*; + +public class Trivium implements ICipher +{ + final static int KEY_SIZE_BITS = 80; + final static int IV_SIZE_BITS = 80; + + /////////////////////////////////////////////////////////////////////////// + + byte[] key = new byte[10]; + int[] s = new int[10]; + + /////////////////////////////////////////////////////////////////////////// + + public int getKeySize() + { + return KEY_SIZE_BITS >> 3; + } + + public int getNonceSize() + { + return IV_SIZE_BITS >> 3; + } + + public int getWordSize() + { + return 4; + } + + public boolean isPatented() + { + return false; + } + + public void process( + byte[] inbuf, + int inOfs, + byte[] outbuf, + int outOfs, + int len) throws ESJException + { + int s11 = s[0]; + int s12 = s[1]; + int s13 = s[2]; + int s21 = s[3]; + int s22 = s[4]; + int s23 = s[5]; + int s31 = s[6]; + int s32 = s[7]; + int s33 = s[8]; + int s34 = s[9]; + + int outEnd = outOfs + (len & ~3); + + for (; outOfs < outEnd; outOfs+=4, inOfs+=4) + { + int t1, t2, t3, reg; + + t1 = ((s13 << 96-66) | (s12 >>> 66-64)) ^ ((s13 << 96-93 ) | (s12 >>> 93-64)); + t2 = ((s23 << 96-69) | (s22 >>> 69-64)) ^ ((s23 << 96-84 ) | (s22 >>> 84-64)); + t3 = ((s33 << 96-66) | (s32 >>> 66-64)) ^ ((s34 << 128-111) | (s33 >>> 111-96)); + + reg = t1 ^ t2 ^ t3; + outbuf[outOfs ] = (byte)(inbuf[inOfs ] ^ reg); + outbuf[outOfs + 1] = (byte)(inbuf[inOfs + 1] ^ reg >> 8); + outbuf[outOfs + 2] = (byte)(inbuf[inOfs + 2] ^ reg >> 16); + outbuf[outOfs + 3] = (byte)(inbuf[inOfs + 3] ^ reg >> 24); + + t1 ^= (((s13 << 96-91 ) | (s12 >>> 91-64)) & ((s13 << 96-92 ) | (s12 >>> 92-64))) ^ ((s23 << 96-78) | (s22 >>> 78-64)); + t2 ^= (((s23 << 96-82 ) | (s22 >>> 82-64)) & ((s23 << 96-83 ) | (s22 >>> 83-64))) ^ ((s33 << 96-87) | (s32 >>> 87-64)); + t3 ^= (((s34 << 128-109) | (s33 >>> 109-96)) & ((s34 << 128-110) | (s33 >>> 110-96))) ^ ((s13 << 96-69) | (s12 >>> 69-64)); + + s13 = s12; s12 = s11; s11 = t3; + s23 = s22; s22 = s21; s21 = t1; + s34 = s33; s33 = s32; s32 = s31; s31 = t2; + } + + // NOTE: could save some code memory by merging the two blocks, but that + // would decrease the speed because of additional conditional jumps... + outEnd = outOfs + (len & 3); + if (0 < outEnd) + { + int t1, t2, t3, reg; + + t1 = ((s13 << 96-66) | (s12 >>> 66-64)) ^ ((s13 << 96-93 ) | (s12 >>> 93-64)); + t2 = ((s23 << 96-69) | (s22 >>> 69-64)) ^ ((s23 << 96-84 ) | (s22 >>> 84-64)); + t3 = ((s33 << 96-66) | (s32 >>> 66-64)) ^ ((s34 << 128-111) | (s33 >>> 111-96)); + + reg = t1 ^ t2 ^ t3; + for (;outOfs < outEnd; outOfs++, inOfs++) + { + outbuf[outOfs] = (byte)(inbuf[inOfs] ^ reg); + reg >>= 8; + } + + t1 ^= (((s13 << 96-91 ) | (s12 >>> 91-64)) & ((s13 << 96-92 ) | (s12 >>> 92-64))) ^ ((s23 << 96-78) | (s22 >>> 78-64)); + t2 ^= (((s23 << 96-82 ) | (s22 >>> 82-64)) & ((s23 << 96-83 ) | (s22 >>> 83-64))) ^ ((s33 << 96-87) | (s32 >>> 87-64)); + t3 ^= (((s34 << 128-109) | (s33 >>> 109-96)) & ((s34 << 128-110) | (s33 >>> 110-96))) ^ ((s13 << 96-69) | (s12 >>> 69-64)); + + s13 = s12; s12 = s11; s11 = t3; + s23 = s22; s22 = s21; s21 = t1; + s34 = s33; s33 = s32; s32 = s31; s31 = t2; + } + + s[0] = s11; + s[1] = s12; + s[2] = s13; + s[3] = s21; + s[4] = s22; + s[5] = s23; + s[6] = s31; + s[7] = s32; + s[8] = s33; + s[9] = s34; + } + + public void reset() throws ESJException + { + // key is cached already, nothing to do here + } + + public void setupKey( + int mode, + byte[] key, + int ofs) throws ESJException + { + System.arraycopy(key, ofs, this.key, 0, this.key.length); + } + + public void setupNonce( + byte[] nonce, + int ofs) throws ESJException + { + byte[] key = this.key; + int[] s = this.s; + + int s11 = Utils.readInt32LE(key, 0); + int s12 = Utils.readInt32LE(key, 4); + int s13 = ( key[8] & 0x0ff) | + ((key[9] << 8) & 0x0ff00); + int s21 = Utils.readInt32LE(nonce, ofs); + int s22 = Utils.readInt32LE(nonce, ofs + 4); + int s23 = ( nonce[ofs + 8] & 0x0ff) | + ((nonce[ofs + 9] << 8) & 0x0ff00); + int s31 = 0; + int s32 = 0; + int s33 = 0; + int s34 = 0x07000; + + + ///////////////!!!!!!!!!!!!!!!!!!!!!!!! +// System.out.printf( +// "s11=%08x\n" + +// "s12=%08x\n" + +// "s13=%08x\n" + +// "s21=%08x\n" + +// "s22=%08x\n" + +// "s23=%08x\n" + +// "s31=%08x\n" + +// "s32=%08x\n" + +// "s33=%08x\n" + +// "s34=%08x\n", +// s11, s12, s13, +// s21, s22, s23, +// s31, s32, s33, s34); + + + for (int i = 0; i < 4*9; i++) + { + int t1, t2, t3; + + t1 = ((s13 << 96-66) | (s12 >>> 66-64)) ^ ((s13 << 96-93 ) | (s12 >>> 93-64)); + t2 = ((s23 << 96-69) | (s22 >>> 69-64)) ^ ((s23 << 96-84 ) | (s22 >>> 84-64)); + t3 = ((s33 << 96-66) | (s32 >>> 66-64)) ^ ((s34 << 128-111) | (s33 >>> 111-96)); + + t1 ^= (((s13 << 96-91 ) | (s12 >>> 91-64)) & ((s13 << 96-92 ) | (s12 >>> 92-64))) ^ ((s23 << 96-78) | (s22 >>> 78-64)); + t2 ^= (((s23 << 96-82 ) | (s22 >>> 82-64)) & ((s23 << 96-83 ) | (s22 >>> 83-64))) ^ ((s33 << 96-87) | (s32 >>> 87-64)); + t3 ^= (((s34 << 128-109) | (s33 >>> 109-96)) & ((s34 << 128-110) | (s33 >>> 110-96))) ^ ((s13 << 96-69) | (s12 >>> 69-64)); + + s13 = s12; s12 = s11; s11 = t3; + s23 = s22; s22 = s21; s21 = t1; + s34 = s33; s33 = s32; s32 = s31; s31 = t2; + } + + s[0] = s11; + s[1] = s12; + s[2] = s13; + s[3] = s21; + s[4] = s22; + s[5] = s23; + s[6] = s31; + s[7] = s32; + s[8] = s33; + s[9] = s34; + } + + /////////////////////////////////////////////////////////////////////////// + + static class Maker implements ICipherMaker + { + public ICipher create() throws ESJException + { + return new Trivium(); + } + + public String getName() + { + return "Trivium"; + } + } + + public static void register() + { + Engine.registerCipher(new Maker()); + } +} \ No newline at end of file diff --git a/src/estreamj/framework/ESJException.java b/src/estreamj/framework/ESJException.java new file mode 100644 index 0000000..45b3d7b --- /dev/null +++ b/src/estreamj/framework/ESJException.java @@ -0,0 +1,23 @@ + +package estreamj.framework; + +public class ESJException extends Exception +{ + private static final long serialVersionUID = 6375271013846829471L; + + public ESJException() { + super(); + } + + public ESJException(String message) { + super(message); + } + + public ESJException(Throwable cause) { + super(cause); + } + + public ESJException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/estreamj/framework/Engine.java b/src/estreamj/framework/Engine.java new file mode 100644 index 0000000..8d88859 --- /dev/null +++ b/src/estreamj/framework/Engine.java @@ -0,0 +1,131 @@ + +package estreamj.framework; + +import java.util.*; +import java.lang.reflect.*; + +/** + * The engine accumulates all the stream ciphers available. All implementations + * register with the engine by themselves. + */ +public class Engine +{ + /** + * @return the names of all ciphers registered; can be empty if no ciphers + * have been registered so far + */ + public static String[] getCipherNames() + { + String[] result; + + synchronized(_cphMks) + { + Vector lst = new Vector(); + lst.addAll(_cphMks.keySet()); + Collections.sort(lst); + result = lst.toArray(new String[lst.size()]); + } + + return result; + } + + /** + * Creates a new cipher instance. + * @param name name of the cipher to make + * @return new cipher instance + * @throws ESJException if any error occured + */ + public static ICipher createCipher(String name) throws ESJException + { + ICipherMaker maker; + + synchronized(_cphMks) + { + maker = _cphMks.get(name); + if (null == maker) + { + throw new ESJException("no maker registered for cipher \"" + + name + "\""); + } + } + return maker.create(); + } + + /////////////////////////////////////////////////////////////////////////// + + static HashMap _cphMks = + new HashMap(); + + /** + * Called by cipher implementations to register their factories, usually + * during startup time. + * @param cphMk the factory to register + */ + public static void registerCipher(ICipherMaker cphMk) + { + String name = cphMk.getName(); + + synchronized(_cphMks) + { + if (_cphMks.containsKey(name)) + { + System.err.println("cipher \"" + name + + "\" has been registered already"); + System.exit(1); + } + _cphMks.put(cphMk.getName(), cphMk); + } + } + + /////////////////////////////////////////////////////////////////////////// + + // all the cipher classes must be listed here, the rest of the registration + // is done via reflection; this is done by having every class implementing + // a 'public static void register()' method (see below) + static Class[] _cipherClasses = + { + //estreamj.ciphers.phelix.Phelix.class, + //estreamj.ciphers.hc256.HC256.class, + //estreamj.ciphers.salsa20.Salsa20.class, + //estreamj.ciphers.aes.AESCTR.class, + //estreamj.ciphers.mickey.MICKEY.class, + //estreamj.ciphers.mickey.MICKEY128.class, + //estreamj.ciphers.hermes8.Hermes8.class, + //estreamj.ciphers.rc4.RC4.class, + //estreamj.ciphers.dragon.Dragon.class, + //estreamj.ciphers.lex.LEX.class, + estreamj.ciphers.trivium.Trivium.class//, + //estreamj.ciphers.sosemanuk.Sosemanuk.class, + //estreamj.ciphers.grain.GrainP2.class, + //estreamj.ciphers.grain.Grain128.class, + //estreamj.ciphers.nil.Nil.class + }; + + /////////////////////////////////////////////////////////////////////////// + + static + { + try + { + for (int i = 0; i < _cipherClasses.length; i++) + { + // check if the register method is there and static + Class cls = _cipherClasses[i]; + Method mthd = cls.getMethod("register", (Class[])null); + if (Modifier.STATIC != (mthd.getModifiers() & Modifier.STATIC)) + { + throw new Exception( + "register() method is not static (" + cls + ")"); + } + // ready to register + mthd.invoke(null, (Object[])null); + } + } + catch (Exception e) + { + System.err.println( + "cipher registration error (" + e.getMessage() + ")"); + System.exit(1); + } + } +} diff --git a/src/estreamj/framework/ICipher.java b/src/estreamj/framework/ICipher.java new file mode 100644 index 0000000..10a6cfe --- /dev/null +++ b/src/estreamj/framework/ICipher.java @@ -0,0 +1,91 @@ + +package estreamj.framework; + +/** + * Generic interface every cipher needs to implement, so it can participate in + * the test framework. If a cipher comes in different flavors (variable key + * sizes, MAC computation or not, etc.) it should then implement a version for + * each of them - this keeps the actual testing logic simple. Simple is good. + */ +public interface ICipher +{ + /** + * mode: instance is used for encryption + */ + public final static int MODE_ENCRYPT = 0; + + /** + * mode: instance is used for decryption + */ + public final static int MODE_DECRYPT = 1; + + /////////////////////////////////////////////////////////////////////////// + + /** + * @return true: algorithm is patented, check with vendor for license + * details / false: free to use in private and commerical applications + */ + public boolean isPatented(); + + /** + * @return key size in bytes + */ + public int getKeySize(); + + /** + * @return nonce size in bytes. + */ + public int getNonceSize(); + + /** + * @return alignment of data needed during calls into process() + */ + public int getWordSize(); + + /////////////////////////////////////////////////////////////////////////// + + /** + * Resets the instance, so it can be reused. + * @exception ESJException if any error occurs + */ + public void reset() throws ESJException; + + /** + * Sets up a new key with the existing instance. + * @param mode see MODE_xxx + * @param key buffer with key material + * @param ofs where the key starts + * @exception ESJException if any error occurs + */ + public void setupKey(int mode, byte[] key, int ofs) + throws ESJException; + + /** + * Sets up a new nonce with the existing cipher instance. + * @param mode see MODE_xxx + * @param nonce buffer with nonce material + * @param ofs where the nonce starts + * @exception ESJException if any error occurs + */ + public void setupNonce(byte[] nonce, int ofs) + throws ESJException; + + /////////////////////////////////////////////////////////////////////////// + + /** + * Processes data. + * @param inbuf input buffer + * @param inOfs where to start reading from the input buffer + * @param outbuf output buffer + * @param outOfs where to start writing in the output buffer + * @param len number of bytes to process, must be aligned to the cipher's + * word size except on the last call where an arbitrary size can be used + * @throws ESJException in any error occured + */ + public void process( + byte[] inbuf, + int inOfs, + byte[] outbuf, + int outOfs, + int len) throws ESJException; +} diff --git a/src/estreamj/framework/ICipherMaker.java b/src/estreamj/framework/ICipherMaker.java new file mode 100644 index 0000000..ffaa867 --- /dev/null +++ b/src/estreamj/framework/ICipherMaker.java @@ -0,0 +1,22 @@ + +package estreamj.framework; + +/** + * Simple cipher factory. + */ +public interface ICipherMaker +{ + /** + * @return the name of cipher, which is used for queries - so it must be + * unique + */ + public String getName(); + + /** + * Create a new cipher instance. + * @return new instance, which can also be of the type ICipherMAC, use the + * "instanceof" keyword to find out what you are dealing with + * @throws ESJException if any error occured + */ + public ICipher create() throws ESJException; +} diff --git a/src/estreamj/framework/Utils.java b/src/estreamj/framework/Utils.java new file mode 100644 index 0000000..97f9336 --- /dev/null +++ b/src/estreamj/framework/Utils.java @@ -0,0 +1,230 @@ + +package estreamj.framework; + +import java.io.*; +import java.util.Arrays; + +public class Utils +{ + public static void fillPattern123(byte[] buf, int ofs, int len) + { + int counter, end; + + counter = 0; + end = ofs + len; + while (ofs < end) + { + buf[ofs++] = (byte)counter++; + } + } + + public static boolean checkPattern123(byte[] buf, int ofs, int len) + { + int counter, end; + + counter = 0; + end = ofs + len; + while (ofs < end) + { + if (buf[ofs] != (byte)counter) + { + return false; + } + counter++; + ofs++; + } + return true; + } + + public static byte[] makeOutputBuffer(int len, int extraLen) + { + byte[] result = new byte[len + extraLen]; + Arrays.fill(result, (byte)0xcc); + return result; + } + + public static boolean arraysEquals( + byte[] a, int ofsA, byte[] b, int ofsB, int len) + { + int end = ofsA + len; + while (ofsA < end) + { + if (b[ofsB++] != a[ofsA++]) + { + return false; + } + } + return true; + } + + public final static int readInt32LE(byte[] data, int ofs) + { + return ( data[ofs + 3] << 24) | + ((data[ofs + 2] & 0xff) << 16) | + ((data[ofs + 1] & 0xff) << 8) | + (data[ofs ] & 0xff); + } + + public final static void writeInt32LE(int value, byte[] data, int ofs) + { + data[ofs ] = (byte)(value ); + data[ofs + 1] = (byte)(value >>> 8); + data[ofs + 2] = (byte)(value >>> 16); + data[ofs + 3] = (byte)(value >>> 24); + } + + public final static int readInt32BE(byte[] data, int ofs) + { + return ( data[ofs ] << 24) | + ((data[ofs + 1] & 0xff) << 16) | + ((data[ofs + 2] & 0xff) << 8) | + (data[ofs + 3] & 0xff); + } + + public final static void writeInt32BE(int value, byte[] data, int ofs) + { + data[ofs + 3] = (byte)(value ); + data[ofs + 2] = (byte)(value >>> 8); + data[ofs + 1] = (byte)(value >>> 16); + data[ofs ] = (byte)(value >>> 24); + } + + public static byte[] hexStrToBytes(String hex) + { + int len = hex.length(); + if (1 == (len & 1)) + { + return null; + } + byte[] result = new byte[len >> 1]; + int r = 0; + int pos = 0; + while (pos < len) + { + int nReg = 0; + for (int nI = 0; nI < 2; nI++) + { + nReg <<= 4; + char c = Character.toLowerCase(hex.charAt(pos++)); + if ('0' <= c && '9' >= c) + { + nReg |= c - '0'; + } + else if ('a' <= c && 'f' >= c) + { + nReg |= (c - 'a') + 10; + } + else + { + return null; + } + } + result[r++] = (byte)nReg; + } + return result; + } + + final static char[] HEXTAB = + { + '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' + }; + + public static int hexDump( + InputStream is, + PrintStream ps, + int maxRead, + int bytesPerLine) + { + int read, chr, i, result; + char[] pad; + StringBuffer left, right; + + + if (1 > bytesPerLine) + { + bytesPerLine = 1; + } + + left = new StringBuffer(); + right = new StringBuffer(); + + result = 0; + + for (read = 0, i = 0;;) + { + if (-1 != maxRead) + { + if (maxRead <= read) + { + break; + } + } + + try + { + if (-1 == (chr = is.read())) + { + break; + } + } + catch (IOException ioe) + { + break; + } + + result++; + + if (0 < i++) + { + left.append(' '); + } + + left.append(HEXTAB[chr >>> 4]); + left.append(HEXTAB[chr & 0x0f]); + + right.append((chr < ' ') ? '.' : (char)chr); + + if (0 == (i % bytesPerLine)) + { + ps.print(left.toString()); + ps.print(" "); + ps.println(right.toString()); + + left.setLength(0); + right.setLength(0); + + i = 0; + } + } + + if (0 < i) + { + pad = new char[((bytesPerLine - i) * 3) + 4]; + Arrays.fill(pad, ' '); + + ps.print(left.toString()); + ps.print(pad); + ps.println(right.toString()); + } + + return result; + } + + public static byte[] swapByteOrder32(byte[] data, int ofs, int len) + { + int end = ofs + len; + byte tmp; + + for (; ofs < end; ofs += 4) + { + tmp = data[ofs]; + data[ofs] = data[ofs + 3]; + data[ofs + 3] = tmp; + + tmp = data[ofs + 1]; + data[ofs + 1] = data[ofs + 2]; + data[ofs + 2] = tmp; + } + return data; + } +}