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();
}
}