package gnu.crypto.cipher; // ---------------------------------------------------------------------------- // $Id: Square.java,v 1.10 2005/10/06 04:24:14 rsdio Exp $ // // Copyright (C) 2001, 2002, 2003, Free Software Foundation, Inc. // // This file is part of GNU Crypto. // // GNU Crypto 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, or (at your option) // any later version. // // GNU Crypto 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; see the file COPYING. If not, write to the // // Free Software Foundation Inc., // 51 Franklin Street, Fifth Floor, // Boston, MA 02110-1301 // USA // // Linking this library statically or dynamically with other modules is // making a combined work based on this library. Thus, the terms and // conditions of the GNU General Public License cover the whole // combination. // // As a special exception, the copyright holders of this library give // you permission to link this library with independent modules to // produce an executable, regardless of the license terms of these // independent modules, and to copy and distribute the resulting // executable under terms of your choice, provided that you also meet, // for each linked independent module, the terms and conditions of the // license of that module. An independent module is a module which is // not derived from or based on this library. If you modify this // library, you may extend this exception to your version of the // library, but you are not obligated to do so. If you do not wish to // do so, delete this exception statement from your version. // ---------------------------------------------------------------------------- import gnu.crypto.Registry; import gnu.crypto.util.Util; import java.security.InvalidKeyException; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; /** * <p>Square is a 128-bit key, 128-bit block cipher algorithm developed by Joan * Daemen, Lars Knudsen and Vincent Rijmen.</p> * * <p>References:</p> * * <ol> * <li><a href="http://www.esat.kuleuven.ac.be/~rijmen/square/">The block * cipher Square</a>.<br> * <a href="mailto:daemen.j@protonworld.com">Joan Daemen</a>, * <a href="mailto:lars.knudsen@esat.kuleuven.ac.be">Lars Knudsen</a> and * <a href="mailto:vincent.rijmen@esat.kuleuven.ac.be">Vincent Rijmen</a>.</li> * </ol> * * @version $Revision: 1.10 $ */ public final class Square extends BaseCipher { // Constants and variables // ------------------------------------------------------------------------- private static final int DEFAULT_BLOCK_SIZE = 16; // in bytes private static final int DEFAULT_KEY_SIZE = 16; // in bytes private static final int ROUNDS = 8; private static final int ROOT = 0x1F5; // for generating GF(2**8) private static final int[] OFFSET = new int[ROUNDS]; private static final String Sdata = "\uB1CE\uC395\u5AAD\uE702\u4D44\uFB91\u0C87\uA150"+ "\uCB67\u54DD\u468F\uE14E\uF0FD\uFCEB\uF9C4\u1A6E"+ "\u5EF5\uCC8D\u1C56\u43FE\u0761\uF875\u59FF\u0322"+ "\u8AD1\u13EE\u8800\u0E34\u1580\u94E3\uEDB5\u5323"+ "\u4B47\u17A7\u9035\uABD8\uB8DF\u4F57\u9A92\uDB1B"+ "\u3CC8\u9904\u8EE0\uD77D\u85BB\u402C\u3A45\uF142"+ "\u6520\u4118\u7225\u9370\u3605\uF20B\uA379\uEC08"+ "\u2731\u32B6\u7CB0\u0A73\u5B7B\uB781\uD20D\u6A26"+ "\u9E58\u9C83\u74B3\uAC30\u7A69\u770F\uAE21\uDED0"+ "\u2E97\u10A4\u98A8\uD468\u2D62\u296D\u1649\u76C7"+ "\uE8C1\u9637\uE5CA\uF4E9\u6312\uC2A6\u14BC\uD328"+ "\uAF2F\uE624\u52C6\uA009\uBD8C\uCF5D\u115F\u01C5"+ "\u9F3D\uA29B\uC93B\uBE51\u191F\u3F5C\uB2EF\u4ACD"+ "\uBFBA\u6F64\uD9F3\u3EB4\uAADC\uD506\uC07E\uF666"+ "\u6C84\u7138\uB91D\u7F9D\u488B\u2ADA\uA533\u8239"+ "\uD678\u86FA\uE42B\uA91E\u8960\u6BEA\u554C\uF7E2"; /** Substitution boxes for encryption and decryption. */ private static final byte[] Se = new byte[256]; private static final byte[] Sd = new byte[256]; /** Transposition boxes for encryption and decryption. */ private static final int[] Te = new int[256]; private static final int[] Td = new int[256]; /** * KAT vector (from ecb_vk): * I=87 * KEY=00000000000000000000020000000000 * CT=A9DF031B4E25E89F527EFFF89CB0BEBA */ private static final byte[] KAT_KEY = Util.toBytesFromString("00000000000000000000020000000000"); private static final byte[] KAT_CT = Util.toBytesFromString("A9DF031B4E25E89F527EFFF89CB0BEBA"); /** caches the result of the correctness test, once executed. */ private static Boolean valid; // Static code - to intialise lookup tables // ------------------------------------------------------------------------- static { int i, j; /* // Generate exp and log tables used in multiplication over GF(2 ** m) byte[] exp = new byte[256]; byte[] log = new byte[256]; exp[0] = 1; for (i = 1; i < 256; i++) { j = exp[i - 1] << 1; if ((j & 0x100) != 0) { j ^= ROOT; // reduce j (mod ROOT) } exp[i] = (byte) j; log[j & 0xFF] = (byte) i; } // Compute the substitution box Se[] and its inverse Sd[] based on // F(x) = x**{-1} plus affine transform of the output. Se[0] = 0; Se[1] = 1; for (i = 2; i < 256; i++) { Se[i] = exp[(255 - log[i]) & 0xFF]; } // Let Se[i] be represented as an 8-row vector V over GF(2); the affine // transformation is A * V + T, where the rows of the 8 x 8 matrix A are // contained in trans[0]...trans[7] and the 8-row vector T is contained // in 0xB1. int[] trans = new int[] {0x01, 0x03, 0x05, 0x0F, 0x1F, 0x3D, 0x7B, 0xD6}; int u, v; for (i = 0; i < 256; i++) { v = 0xB1; // affine part of the transform for (j = 0; j < 8; j++) { u = Se[i] & trans[j] & 0xFF; // column-wise mult. over GF(2) u ^= u >>> 4; // sum of all bits of u over GF(2) u ^= u >>> 2; u ^= u >>> 1; u &= 1; v ^= u << j; // row alignment of the result } Se[i] = (byte) v; Sd[v] = (byte) i; // inverse substitution box } System.out.println("Se="+Util.toUnicodeString(Se)); System.out.println("Sd="+Util.toUnicodeString(Sd)); */ /**/ // re-construct Se box values int limit = Sdata.length(); char c1; for (i = 0, j = 0; i < limit; i++) { c1 = Sdata.charAt(i); Se[j++] = (byte)(c1 >>> 8); Se[j++] = (byte) c1; } // compute Sd box values for (i = 0; i < 256; i++) { Sd[Se[i] & 0xFF] = (byte) i; } // generate OFFSET values OFFSET[0] = 1; for (i = 1; i < ROUNDS; i++) { OFFSET[i] = mul(OFFSET[i - 1], 2); OFFSET[i - 1] <<= 24; } OFFSET[ROUNDS - 1] <<= 24; // generate Te and Td boxes if we're not reading their values // Notes: // (1) The function mul() computes the product of two elements of GF(2**8) // with ROOT as reduction polynomial. // (2) the values used in computing the Te and Td are the GF(2**8) // coefficients of the diffusion polynomial c(x) and its inverse // (modulo x**4 + 1) d(x), defined in sections 2.1 and 4 of the Square // paper. for (i = 0; i < 256; i++) { j = Se[i] & 0xFF; Te[i] = (Se[i & 3] == 0) ? 0 : mul(j, 2) << 24 | j << 16 | j << 8 | mul(j, 3); j = Sd[i] & 0xFF; Td[i] = (Sd[i & 3] == 0) ? 0 : mul(j, 14) << 24 | mul(j, 9) << 16 | mul(j, 13) << 8 | mul(j, 11); } /**/ } // Constructor(s) // ------------------------------------------------------------------------- /** Trivial 0-arguments constructor. */ public Square() { super(Registry.SQUARE_CIPHER, DEFAULT_BLOCK_SIZE, DEFAULT_KEY_SIZE); } // Class methods // ------------------------------------------------------------------------- private static void square(byte[] in, int i, byte[] out, int j, int[][] K, int[] T, byte[] S) { int a = ((in[i++] ) << 24 | (in[i++] & 0xFF) << 16 | (in[i++] & 0xFF) << 8 | (in[i++] & 0xFF) ) ^ K[0][0]; int b = ((in[i++] ) << 24 | (in[i++] & 0xFF) << 16 | (in[i++] & 0xFF) << 8 | (in[i++] & 0xFF) ) ^ K[0][1]; int c = ((in[i++] ) << 24 | (in[i++] & 0xFF) << 16 | (in[i++] & 0xFF) << 8 | (in[i++] & 0xFF) ) ^ K[0][2]; int d = ((in[i++] ) << 24 | (in[i++] & 0xFF) << 16 | (in[i++] & 0xFF) << 8 | (in[i ] & 0xFF) ) ^ K[0][3]; int r, aa, bb, cc, dd; for (r = 1; r < ROUNDS; r++) { // R - 1 full rounds aa = T[(a >>> 24) ] ^ rot32R(T[(b >>> 24) ], 8) ^ rot32R(T[(c >>> 24) ], 16) ^ rot32R(T[(d >>> 24) ], 24) ^ K[r][0]; bb = T[(a >>> 16) & 0xFF] ^ rot32R(T[(b >>> 16) & 0xFF], 8) ^ rot32R(T[(c >>> 16) & 0xFF], 16) ^ rot32R(T[(d >>> 16) & 0xFF], 24) ^ K[r][1]; cc = T[(a >>> 8) & 0xFF] ^ rot32R(T[(b >>> 8) & 0xFF], 8) ^ rot32R(T[(c >>> 8) & 0xFF], 16) ^ rot32R(T[(d >>> 8) & 0xFF], 24) ^ K[r][2]; dd = T[ a & 0xFF] ^ rot32R(T[ b & 0xFF], 8) ^ rot32R(T[ c & 0xFF], 16) ^ rot32R(T[ d & 0xFF], 24) ^ K[r][3]; a = aa; b = bb; c = cc; d = dd; } // last round (diffusion becomes only transposition) aa = ((S[(a >>> 24) ] ) << 24 | (S[(b >>> 24) ] & 0xFF) << 16 | (S[(c >>> 24) ] & 0xFF) << 8 | (S[(d >>> 24) ] & 0xFF) ) ^ K[r][0]; bb = ((S[(a >>> 16) & 0xFF] ) << 24 | (S[(b >>> 16) & 0xFF] & 0xFF) << 16 | (S[(c >>> 16) & 0xFF] & 0xFF) << 8 | (S[(d >>> 16) & 0xFF] & 0xFF) ) ^ K[r][1]; cc = ((S[(a >>> 8) & 0xFF] ) << 24 | (S[(b >>> 8) & 0xFF] & 0xFF) << 16 | (S[(c >>> 8) & 0xFF] & 0xFF) << 8 | (S[(d >>> 8) & 0xFF] & 0xFF) ) ^ K[r][2]; dd = ((S[ a & 0xFF] ) << 24 | (S[ b & 0xFF] & 0xFF) << 16 | (S[ c & 0xFF] & 0xFF) << 8 | (S[ d & 0xFF] & 0xFF) ) ^ K[r][3]; out[j++] = (byte)(aa >>> 24); out[j++] = (byte)(aa >>> 16); out[j++] = (byte)(aa >>> 8); out[j++] = (byte) aa; out[j++] = (byte)(bb >>> 24); out[j++] = (byte)(bb >>> 16); out[j++] = (byte)(bb >>> 8); out[j++] = (byte) bb; out[j++] = (byte)(cc >>> 24); out[j++] = (byte)(cc >>> 16); out[j++] = (byte)(cc >>> 8); out[j++] = (byte) cc; out[j++] = (byte)(dd >>> 24); out[j++] = (byte)(dd >>> 16); out[j++] = (byte)(dd >>> 8); out[j ] = (byte) dd; } /** * <p>Applies the Theta function to an input <i>in</i> in order to produce in * <i>out</i> an internal session sub-key.</p> * * <p>Both <i>in</i> and <i>out</i> are arrays of four ints.</p> * * <p>Pseudo-code is:</p> * * <pre> * for (i = 0; i < 4; i++) { * out[i] = 0; * for (j = 0, n = 24; j < 4; j++, n -= 8) { * k = mul(in[i] >>> 24, G[0][j]) ^ * mul(in[i] >>> 16, G[1][j]) ^ * mul(in[i] >>> 8, G[2][j]) ^ * mul(in[i] , G[3][j]); * out[i] ^= k << n; * } * } * </pre> */ private static void transform(int[] in, int[] out) { int l3, l2, l1, l0, m; for (int i = 0; i < 4; i++) { l3 = in[i]; l2 = l3 >>> 8; l1 = l3 >>> 16; l0 = l3 >>> 24; m = ((mul(l0, 2) ^ mul(l1, 3) ^ l2 ^ l3 ) & 0xFF) << 24; m ^= ((l0 ^ mul(l1, 2) ^ mul(l2, 3) ^ l3 ) & 0xFF) << 16; m ^= ((l0 ^ l1 ^ mul(l2, 2) ^ mul(l3, 3)) & 0xFF) << 8; m ^= ((mul(l0, 3) ^ l1 ^ l2 ^ mul(l3, 2)) & 0xFF); out[i] = m; } } /** * <p>Left rotate a 32-bit chunk.</p> * * @param x the 32-bit data to rotate * @param s number of places to left-rotate by * @return the newly permutated value. */ private static int rot32L(int x, int s) { return x << s | x >>> (32 - s); } /** * <p>Right rotate a 32-bit chunk.</p> * * @param x the 32-bit data to rotate * @param s number of places to right-rotate by * @return the newly permutated value. */ private static int rot32R(int x, int s) { return x >>> s | x << (32 - s); } /** * <p>Returns the product of two binary numbers a and b, using the generator * ROOT as the modulus: p = (a * b) mod ROOT. ROOT Generates a suitable * Galois Field in GF(2**8).</p> * * <p>For best performance call it with abs(b) < abs(a).</p> * * @param a operand for multiply. * @param b operand for multiply. * @return the result of (a * b) % ROOT. */ private static final int mul(int a, int b) { if (a == 0) { return 0; } a &= 0xFF; b &= 0xFF; int result = 0; while (b != 0) { if ((b & 0x01) != 0) { result ^= a; } b >>>= 1; a <<= 1; if (a > 0xFF) { a ^= ROOT; } } return result & 0xFF; } // Instance methods // ------------------------------------------------------------------------- // java.lang.Cloneable interface implementation ---------------------------- public Object clone() { Square result = new Square(); result.currentBlockSize = this.currentBlockSize; return result; } // IBlockCipherSpi interface implementation -------------------------------- public Iterator blockSizes() { ArrayList al = new ArrayList(); al.add(new Integer(DEFAULT_BLOCK_SIZE)); return Collections.unmodifiableList(al).iterator(); } public Iterator keySizes() { ArrayList al = new ArrayList(); al.add(new Integer(DEFAULT_KEY_SIZE)); return Collections.unmodifiableList(al).iterator(); } public Object makeKey(byte[] uk, int bs) throws InvalidKeyException { if (bs != DEFAULT_BLOCK_SIZE) { throw new IllegalArgumentException(); } if (uk == null) { throw new InvalidKeyException("Empty key"); } if (uk.length != DEFAULT_KEY_SIZE) { throw new InvalidKeyException("Key is not 128-bit."); } int[][] Ke = new int[ROUNDS + 1][4]; int[][] Kd = new int[ROUNDS + 1][4]; int[][] tK = new int[ROUNDS + 1][4]; int i = 0; Ke[0][0] = (uk[i++] & 0xFF) << 24 | (uk[i++] & 0xFF) << 16 | (uk[i++] & 0xFF) << 8 | (uk[i++] & 0xFF); tK[0][0] = Ke[0][0]; Ke[0][1] = (uk[i++] & 0xFF) << 24 | (uk[i++] & 0xFF) << 16 | (uk[i++] & 0xFF) << 8 | (uk[i++] & 0xFF); tK[0][1] = Ke[0][1]; Ke[0][2] = (uk[i++] & 0xFF) << 24 | (uk[i++] & 0xFF) << 16 | (uk[i++] & 0xFF) << 8 | (uk[i++] & 0xFF); tK[0][2] = Ke[0][2]; Ke[0][3] = (uk[i++] & 0xFF) << 24 | (uk[i++] & 0xFF) << 16 | (uk[i++] & 0xFF) << 8 | (uk[i ] & 0xFF); tK[0][3] = Ke[0][3]; int j; for (i = 1, j = 0; i < ROUNDS + 1; i++, j++) { tK[i][0] = tK[j][0] ^ rot32L(tK[j][3], 8) ^ OFFSET[j]; tK[i][1] = tK[j][1] ^ tK[i][0]; tK[i][2] = tK[j][2] ^ tK[i][1]; tK[i][3] = tK[j][3] ^ tK[i][2]; System.arraycopy(tK[i], 0, Ke[i], 0, 4); transform(Ke[j], Ke[j]); } for (i = 0; i < ROUNDS; i++) { System.arraycopy(tK[ROUNDS - i], 0, Kd[i], 0, 4); } transform(tK[0], Kd[ROUNDS]); return new Object[] {Ke, Kd}; } public void encrypt(byte[] in, int i, byte[] out, int j, Object k, int bs) { if (bs != DEFAULT_BLOCK_SIZE) { throw new IllegalArgumentException(); } int[][] K = (int[][])((Object[]) k)[0]; square(in, i, out, j, K, Te, Se); } public void decrypt(byte[] in, int i, byte[] out, int j, Object k, int bs) { if (bs != DEFAULT_BLOCK_SIZE) { throw new IllegalArgumentException(); } int[][] K = (int[][])((Object[]) k)[1]; square(in, i, out, j, K, Td, Sd); } public boolean selfTest() { if (valid == null) { boolean result = super.selfTest(); // do symmetry tests if (result) { result = testKat(KAT_KEY, KAT_CT); } valid = new Boolean(result); } return valid.booleanValue(); } }