/*
* Copyright (c) 2002-2003 by OpenSymphony
* All rights reserved.
*/
package com.opensymphony.module.random;
/* ====================================================================
* The OpenSymphony Software License, Version 1.1
*
* (this license is derived and fully compatible with the Apache Software
* License - see http://www.apache.org/LICENSE.txt)
*
* Copyright (c) 2001 The OpenSymphony Group. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* OpenSymphony Group (http://www.opensymphony.com/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "OpenSymphony" and "The OpenSymphony Group"
* must not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact [email protected] .
*
* 5. Products derived from this software may not be called "OpenSymphony"
* or "OSCore", nor may "OpenSymphony" or "OSCore" appear in their
* name, without prior written permission of the OpenSymphony Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*/
import java.io.PrintWriter;
import java.security.InvalidKeyException;
/**
* DOCUMENT ME!
*
* @author $author$
* @version $Revision: 131 $
*/
public final class Rijndael_Algorithm // implicit no-argument constructor
{
//~ Static fields/initializers /////////////////////////////////////////////
static final String NAME = "Rijndael_Algorithm";
static final boolean IN = true;
static final boolean OUT = false;
static final boolean DEBUG = Rijndael_Properties.GLOBAL_DEBUG;
static final int debuglevel = DEBUG ? Rijndael_Properties.getLevel(NAME) : 0;
static final PrintWriter err = DEBUG ? Rijndael_Properties.getOutput() : null;
static final boolean TRACE = Rijndael_Properties.isTraceable(NAME);
// Constants and variables
//...........................................................................
static final int BLOCK_SIZE = 16; // default block size in bytes
static final int[] alog = new int[256];
static final int[] log = new int[256];
static final byte[] S = new byte[256];
static final byte[] Si = new byte[256];
static final int[] T1 = new int[256];
static final int[] T2 = new int[256];
static final int[] T3 = new int[256];
static final int[] T4 = new int[256];
static final int[] T5 = new int[256];
static final int[] T6 = new int[256];
static final int[] T7 = new int[256];
static final int[] T8 = new int[256];
static final int[] U1 = new int[256];
static final int[] U2 = new int[256];
static final int[] U3 = new int[256];
static final int[] U4 = new int[256];
static final byte[] rcon = new byte[30];
static final int[][][] shifts = new int[][][] {
{
{0, 0},
{1, 3},
{2, 2},
{3, 1}
},
{
{0, 0},
{1, 5},
{2, 4},
{3, 3}
},
{
{0, 0},
{1, 7},
{3, 5},
{4, 4}
}
};
private static final char[] HEX_DIGITS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F'
};
// Static code - to intialise S-boxes and T-boxes
//...........................................................................
static {
long time = System.currentTimeMillis();
if (DEBUG && (debuglevel > 6)) {
System.out.println("Algorithm Name: " + Rijndael_Properties.FULL_NAME);
System.out.println("Electronic Codebook (ECB) Mode");
System.out.println();
}
int ROOT = 0x11B;
int i;
int j = 0;
//
// produce log and alog tables, needed for multiplying in the
// field GF(2^m) (generator = 3)
//
alog[0] = 1;
for (i = 1; i < 256; i++) {
j = (alog[i - 1] << 1) ^ alog[i - 1];
if ((j & 0x100) != 0) {
j ^= ROOT;
}
alog[i] = j;
}
for (i = 1; i < 255; i++) {
log[alog[i]] = i;
}
byte[][] A = new byte[][] {
{1, 1, 1, 1, 1, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 0},
{0, 0, 0, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 1, 1, 1},
{1, 1, 0, 0, 0, 1, 1, 1},
{1, 1, 1, 0, 0, 0, 1, 1},
{1, 1, 1, 1, 0, 0, 0, 1}
};
byte[] B = new byte[] {0, 1, 1, 0, 0, 0, 1, 1};
//
// substitution box based on F^{-1}(x)
//
int t;
byte[][] box = new byte[256][8];
box[1][7] = 1;
for (i = 2; i < 256; i++) {
j = alog[255 - log[i]];
for (t = 0; t < 8; t++) {
box[i][t] = (byte) ((j >>> (7 - t)) & 0x01);
}
}
//
// affine transform: box[i] <- B + A*box[i]
//
byte[][] cox = new byte[256][8];
for (i = 0; i < 256; i++) {
for (t = 0; t < 8; t++) {
cox[i][t] = B[t];
for (j = 0; j < 8; j++) {
cox[i][t] ^= (A[t][j] * box[i][j]);
}
}
}
//
// S-boxes and inverse S-boxes
//
for (i = 0; i < 256; i++) {
S[i] = (byte) (cox[i][0] << 7);
for (t = 1; t < 8; t++) {
S[i] ^= (cox[i][t] << (7 - t));
}
Si[S[i] & 0xFF] = (byte) i;
}
//
// T-boxes
//
byte[][] G = new byte[][] {
{2, 1, 1, 3},
{3, 2, 1, 1},
{1, 3, 2, 1},
{1, 1, 3, 2}
};
byte[][] AA = new byte[4][8];
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
AA[i][j] = G[i][j];
}
AA[i][i + 4] = 1;
}
byte pivot;
byte tmp;
byte[][] iG = new byte[4][4];
for (i = 0; i < 4; i++) {
pivot = AA[i][i];
if (pivot == 0) {
t = i + 1;
while ((AA[t][i] == 0) && (t < 4)) {
t++;
}
if (t == 4) {
throw new RuntimeException("G matrix is not invertible");
} else {
for (j = 0; j < 8; j++) {
tmp = AA[i][j];
AA[i][j] = AA[t][j];
AA[t][j] = (byte) tmp;
}
pivot = AA[i][i];
}
}
for (j = 0; j < 8; j++) {
if (AA[i][j] != 0) {
AA[i][j] = (byte) alog[((255 + log[AA[i][j] & 0xFF]) - log[pivot & 0xFF]) % 255];
}
}
for (t = 0; t < 4; t++) {
if (i != t) {
for (j = i + 1; j < 8; j++) {
AA[t][j] ^= mul(AA[i][j], AA[t][i]);
}
AA[t][i] = 0;
}
}
}
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
iG[i][j] = AA[i][j + 4];
}
}
int s;
for (t = 0; t < 256; t++) {
s = S[t];
T1[t] = mul4(s, G[0]);
T2[t] = mul4(s, G[1]);
T3[t] = mul4(s, G[2]);
T4[t] = mul4(s, G[3]);
s = Si[t];
T5[t] = mul4(s, iG[0]);
T6[t] = mul4(s, iG[1]);
T7[t] = mul4(s, iG[2]);
T8[t] = mul4(s, iG[3]);
U1[t] = mul4(t, iG[0]);
U2[t] = mul4(t, iG[1]);
U3[t] = mul4(t, iG[2]);
U4[t] = mul4(t, iG[3]);
}
//
// round constants
//
rcon[0] = 1;
int r = 1;
for (t = 1; t < 30;) {
rcon[t++] = (byte) (r = mul(2, r));
}
time = System.currentTimeMillis() - time;
if (DEBUG && (debuglevel > 8)) {
System.out.println("==========");
System.out.println();
System.out.println("Static Data");
System.out.println();
System.out.println("S[]:");
for (i = 0; i < 16; i++) {
for (j = 0; j < 16; j++) {
System.out.print("0x" + byteToString(S[(i * 16) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("Si[]:");
for (i = 0; i < 16; i++) {
for (j = 0; j < 16; j++) {
System.out.print("0x" + byteToString(Si[(i * 16) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("iG[]:");
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + byteToString(iG[i][j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("T1[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(T1[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("T2[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(T2[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("T3[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(T3[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("T4[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(T4[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("T5[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(T5[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("T6[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(T6[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("T7[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(T7[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("T8[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(T8[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("U1[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(U1[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("U2[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(U2[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("U3[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(U3[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("U4[]:");
for (i = 0; i < 64; i++) {
for (j = 0; j < 4; j++) {
System.out.print("0x" + intToString(U4[(i * 4) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("rcon[]:");
for (i = 0; i < 5; i++) {
for (j = 0; j < 6; j++) {
System.out.print("0x" + byteToString(rcon[(i * 6) + j]) + ", ");
}
System.out.println();
}
System.out.println();
System.out.println("Total initialization time: " + time + " ms.");
System.out.println();
}
}
//~ Methods ////////////////////////////////////////////////////////////////
/**
* Return The number of rounds for a given Rijndael's key and block sizes.
*
* @param keySize The size of the user key material in bytes.
* @param blockSize The desired block size in bytes.
* @return The number of rounds for a given Rijndael's key and
* block sizes.
*/
public static int getRounds(int keySize, int blockSize) {
switch (keySize) {
case 16:
return (blockSize == 16) ? 10 : ((blockSize == 24) ? 12 : 14);
case 24:
return (blockSize != 32) ? 12 : 14;
default: // 32 bytes = 256 bits
return 14;
}
}
/**
* Convenience method to decrypt exactly one block of plaintext, assuming
* Rijndael's default block size (128-bit).
*
* @param in The ciphertext.
* @param inOffset Index of in from which to start considering data.
* @param sessionKey The session key to use for decryption.
* @return The plaintext generated from a ciphertext using the session key.
*/
public static byte[] blockDecrypt(byte[] in, int inOffset, Object sessionKey) {
if (DEBUG) {
trace(IN, "blockDecrypt(" + in + ", " + inOffset + ", " + sessionKey + ")");
}
int[][] Kd = (int[][]) ((Object[]) sessionKey)[1]; // extract decryption round keys
int ROUNDS = Kd.length - 1;
int[] Kdr = Kd[0];
// ciphertext to ints + key
int t0 = (((in[inOffset++] & 0xFF) << 24) | ((in[inOffset++] & 0xFF) << 16) | ((in[inOffset++] & 0xFF) << 8) | (in[inOffset++] & 0xFF)) ^ Kdr[0];
int t1 = (((in[inOffset++] & 0xFF) << 24) | ((in[inOffset++] & 0xFF) << 16) | ((in[inOffset++] & 0xFF) << 8) | (in[inOffset++] & 0xFF)) ^ Kdr[1];
int t2 = (((in[inOffset++] & 0xFF) << 24) | ((in[inOffset++] & 0xFF) << 16) | ((in[inOffset++] & 0xFF) << 8) | (in[inOffset++] & 0xFF)) ^ Kdr[2];
int t3 = (((in[inOffset++] & 0xFF) << 24) | ((in[inOffset++] & 0xFF) << 16) | ((in[inOffset++] & 0xFF) << 8) | (in[inOffset++] & 0xFF)) ^ Kdr[3];
int a0;
int a1;
int a2;
int a3;
for (int r = 1; r < ROUNDS; r++) { // apply round transforms
Kdr = Kd[r];
a0 = (T5[(t0 >>> 24) & 0xFF] ^ T6[(t3 >>> 16) & 0xFF] ^ T7[(t2 >>> 8) & 0xFF] ^ T8[t1 & 0xFF]) ^ Kdr[0];
a1 = (T5[(t1 >>> 24) & 0xFF] ^ T6[(t0 >>> 16) & 0xFF] ^ T7[(t3 >>> 8) & 0xFF] ^ T8[t2 & 0xFF]) ^ Kdr[1];
a2 = (T5[(t2 >>> 24) & 0xFF] ^ T6[(t1 >>> 16) & 0xFF] ^ T7[(t0 >>> 8) & 0xFF] ^ T8[t3 & 0xFF]) ^ Kdr[2];
a3 = (T5[(t3 >>> 24) & 0xFF] ^ T6[(t2 >>> 16) & 0xFF] ^ T7[(t1 >>> 8) & 0xFF] ^ T8[t0 & 0xFF]) ^ Kdr[3];
t0 = a0;
t1 = a1;
t2 = a2;
t3 = a3;
if (DEBUG && (debuglevel > 6)) {
System.out.println("PT" + r + "=" + intToString(t0) + intToString(t1) + intToString(t2) + intToString(t3));
}
}
// last round is special
byte[] result = new byte[16]; // the resulting plaintext
Kdr = Kd[ROUNDS];
int tt = Kdr[0];
result[0] = (byte) (Si[(t0 >>> 24) & 0xFF] ^ (tt >>> 24));
result[1] = (byte) (Si[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
result[2] = (byte) (Si[(t2 >>> 8) & 0xFF] ^ (tt >>> 8));
result[3] = (byte) (Si[t1 & 0xFF] ^ tt);
tt = Kdr[1];
result[4] = (byte) (Si[(t1 >>> 24) & 0xFF] ^ (tt >>> 24));
result[5] = (byte) (Si[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
result[6] = (byte) (Si[(t3 >>> 8) & 0xFF] ^ (tt >>> 8));
result[7] = (byte) (Si[t2 & 0xFF] ^ tt);
tt = Kdr[2];
result[8] = (byte) (Si[(t2 >>> 24) & 0xFF] ^ (tt >>> 24));
result[9] = (byte) (Si[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
result[10] = (byte) (Si[(t0 >>> 8) & 0xFF] ^ (tt >>> 8));
result[11] = (byte) (Si[t3 & 0xFF] ^ tt);
tt = Kdr[3];
result[12] = (byte) (Si[(t3 >>> 24) & 0xFF] ^ (tt >>> 24));
result[13] = (byte) (Si[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
result[14] = (byte) (Si[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
result[15] = (byte) (Si[t0 & 0xFF] ^ tt);
if (DEBUG && (debuglevel > 6)) {
System.out.println("PT=" + toString(result));
System.out.println();
}
if (DEBUG) {
trace(OUT, "blockDecrypt()");
}
return result;
}
/**
* Decrypt exactly one block of ciphertext.
*
* @param in The ciphertext.
* @param inOffset Index of in from which to start considering data.
* @param sessionKey The session key to use for decryption.
* @param blockSize The block size in bytes of this Rijndael.
* @return The plaintext generated from a ciphertext using the session key.
*/
public static byte[] blockDecrypt(byte[] in, int inOffset, Object sessionKey, int blockSize) {
if (blockSize == BLOCK_SIZE) {
return blockDecrypt(in, inOffset, sessionKey);
}
if (DEBUG) {
trace(IN, "blockDecrypt(" + in + ", " + inOffset + ", " + sessionKey + ", " + blockSize + ")");
}
Object[] sKey = (Object[]) sessionKey; // extract decryption round keys
int[][] Kd = (int[][]) sKey[1];
int BC = blockSize / 4;
int ROUNDS = Kd.length - 1;
int SC = (BC == 4) ? 0 : ((BC == 6) ? 1 : 2);
int s1 = shifts[SC][1][1];
int s2 = shifts[SC][2][1];
int s3 = shifts[SC][3][1];
int[] a = new int[BC];
int[] t = new int[BC]; // temporary work array
int i;
byte[] result = new byte[blockSize]; // the resulting plaintext
int j = 0;
int tt;
for (i = 0; i < BC; i++) // ciphertext to ints + key
{
t[i] = (((in[inOffset++] & 0xFF) << 24) | ((in[inOffset++] & 0xFF) << 16) | ((in[inOffset++] & 0xFF) << 8) | (in[inOffset++] & 0xFF)) ^ Kd[0][i];
}
for (int r = 1; r < ROUNDS; r++) { // apply round transforms
for (i = 0; i < BC; i++) {
a[i] = (T5[(t[i] >>> 24) & 0xFF] ^ T6[(t[(i + s1) % BC] >>> 16) & 0xFF] ^ T7[(t[(i + s2) % BC] >>> 8) & 0xFF] ^ T8[t[(i + s3) % BC] & 0xFF]) ^ Kd[r][i];
}
System.arraycopy(a, 0, t, 0, BC);
if (DEBUG && (debuglevel > 6)) {
System.out.println("PT" + r + "=" + toString(t));
}
}
for (i = 0; i < BC; i++) { // last round is special
tt = Kd[ROUNDS][i];
result[j++] = (byte) (Si[(t[i] >>> 24) & 0xFF] ^ (tt >>> 24));
result[j++] = (byte) (Si[(t[(i + s1) % BC] >>> 16) & 0xFF] ^ (tt >>> 16));
result[j++] = (byte) (Si[(t[(i + s2) % BC] >>> 8) & 0xFF] ^ (tt >>> 8));
result[j++] = (byte) (Si[t[(i + s3) % BC] & 0xFF] ^ tt);
}
if (DEBUG && (debuglevel > 6)) {
System.out.println("PT=" + toString(result));
System.out.println();
}
if (DEBUG) {
trace(OUT, "blockDecrypt()");
}
return result;
}
/**
* Convenience method to encrypt exactly one block of plaintext, assuming
* Rijndael's default block size (128-bit).
*
* @param in The plaintext.
* @param inOffset Index of in from which to start considering data.
* @param sessionKey The session key to use for encryption.
* @return The ciphertext generated from a plaintext using the session key.
*/
public static byte[] blockEncrypt(byte[] in, int inOffset, Object sessionKey) {
if (DEBUG) {
trace(IN, "blockEncrypt(" + in + ", " + inOffset + ", " + sessionKey + ")");
}
int[][] Ke = (int[][]) ((Object[]) sessionKey)[0]; // extract encryption round keys
int ROUNDS = Ke.length - 1;
int[] Ker = Ke[0];
// plaintext to ints + key
int t0 = (((in[inOffset++] & 0xFF) << 24) | ((in[inOffset++] & 0xFF) << 16) | ((in[inOffset++] & 0xFF) << 8) | (in[inOffset++] & 0xFF)) ^ Ker[0];
int t1 = (((in[inOffset++] & 0xFF) << 24) | ((in[inOffset++] & 0xFF) << 16) | ((in[inOffset++] & 0xFF) << 8) | (in[inOffset++] & 0xFF)) ^ Ker[1];
int t2 = (((in[inOffset++] & 0xFF) << 24) | ((in[inOffset++] & 0xFF) << 16) | ((in[inOffset++] & 0xFF) << 8) | (in[inOffset++] & 0xFF)) ^ Ker[2];
int t3 = (((in[inOffset++] & 0xFF) << 24) | ((in[inOffset++] & 0xFF) << 16) | ((in[inOffset++] & 0xFF) << 8) | (in[inOffset++] & 0xFF)) ^ Ker[3];
int a0;
int a1;
int a2;
int a3;
for (int r = 1; r < ROUNDS; r++) { // apply round transforms
Ker = Ke[r];
a0 = (T1[(t0 >>> 24) & 0xFF] ^ T2[(t1 >>> 16) & 0xFF] ^ T3[(t2 >>> 8) & 0xFF] ^ T4[t3 & 0xFF]) ^ Ker[0];
a1 = (T1[(t1 >>> 24) & 0xFF] ^ T2[(t2 >>> 16) & 0xFF] ^ T3[(t3 >>> 8) & 0xFF] ^ T4[t0 & 0xFF]) ^ Ker[1];
a2 = (T1[(t2 >>> 24) & 0xFF] ^ T2[(t3 >>> 16) & 0xFF] ^ T3[(t0 >>> 8) & 0xFF] ^ T4[t1 & 0xFF]) ^ Ker[2];
a3 = (T1[(t3 >>> 24) & 0xFF] ^ T2[(t0 >>> 16) & 0xFF] ^ T3[(t1 >>> 8) & 0xFF] ^ T4[t2 & 0xFF]) ^ Ker[3];
t0 = a0;
t1 = a1;
t2 = a2;
t3 = a3;
if (DEBUG && (debuglevel > 6)) {
System.out.println("CT" + r + "=" + intToString(t0) + intToString(t1) + intToString(t2) + intToString(t3));
}
}
// last round is special
byte[] result = new byte[BLOCK_SIZE]; // the resulting ciphertext
Ker = Ke[ROUNDS];
int tt = Ker[0];
result[0] = (byte) (S[(t0 >>> 24) & 0xFF] ^ (tt >>> 24));
result[1] = (byte) (S[(t1 >>> 16) & 0xFF] ^ (tt >>> 16));
result[2] = (byte) (S[(t2 >>> 8) & 0xFF] ^ (tt >>> 8));
result[3] = (byte) (S[t3 & 0xFF] ^ tt);
tt = Ker[1];
result[4] = (byte) (S[(t1 >>> 24) & 0xFF] ^ (tt >>> 24));
result[5] = (byte) (S[(t2 >>> 16) & 0xFF] ^ (tt >>> 16));
result[6] = (byte) (S[(t3 >>> 8) & 0xFF] ^ (tt >>> 8));
result[7] = (byte) (S[t0 & 0xFF] ^ tt);
tt = Ker[2];
result[8] = (byte) (S[(t2 >>> 24) & 0xFF] ^ (tt >>> 24));
result[9] = (byte) (S[(t3 >>> 16) & 0xFF] ^ (tt >>> 16));
result[10] = (byte) (S[(t0 >>> 8) & 0xFF] ^ (tt >>> 8));
result[11] = (byte) (S[t1 & 0xFF] ^ tt);
tt = Ker[3];
result[12] = (byte) (S[(t3 >>> 24) & 0xFF] ^ (tt >>> 24));
result[13] = (byte) (S[(t0 >>> 16) & 0xFF] ^ (tt >>> 16));
result[14] = (byte) (S[(t1 >>> 8) & 0xFF] ^ (tt >>> 8));
result[15] = (byte) (S[t2 & 0xFF] ^ tt);
if (DEBUG && (debuglevel > 6)) {
System.out.println("CT=" + toString(result));
System.out.println();
}
if (DEBUG) {
trace(OUT, "blockEncrypt()");
}
return result;
}
/**
* Encrypt exactly one block of plaintext.
*
* @param in The plaintext.
* @param inOffset Index of in from which to start considering data.
* @param sessionKey The session key to use for encryption.
* @param blockSize The block size in bytes of this Rijndael.
* @return The ciphertext generated from a plaintext using the session key.
*/
public static byte[] blockEncrypt(byte[] in, int inOffset, Object sessionKey, int blockSize) {
if (blockSize == BLOCK_SIZE) {
return blockEncrypt(in, inOffset, sessionKey);
}
if (DEBUG) {
trace(IN, "blockEncrypt(" + in + ", " + inOffset + ", " + sessionKey + ", " + blockSize + ")");
}
Object[] sKey = (Object[]) sessionKey; // extract encryption round keys
int[][] Ke = (int[][]) sKey[0];
int BC = blockSize / 4;
int ROUNDS = Ke.length - 1;
int SC = (BC == 4) ? 0 : ((BC == 6) ? 1 : 2);
int s1 = shifts[SC][1][0];
int s2 = shifts[SC][2][0];
int s3 = shifts[SC][3][0];
int[] a = new int[BC];
int[] t = new int[BC]; // temporary work array
int i;
byte[] result = new byte[blockSize]; // the resulting ciphertext
int j = 0;
int tt;
for (i = 0; i < BC; i++) // plaintext to ints + key
{
t[i] = (((in[inOffset++] & 0xFF) << 24) | ((in[inOffset++] & 0xFF) << 16) | ((in[inOffset++] & 0xFF) << 8) | (in[inOffset++] & 0xFF)) ^ Ke[0][i];
}
for (int r = 1; r < ROUNDS; r++) { // apply round transforms
for (i = 0; i < BC; i++) {
a[i] = (T1[(t[i] >>> 24) & 0xFF] ^ T2[(t[(i + s1) % BC] >>> 16) & 0xFF] ^ T3[(t[(i + s2) % BC] >>> 8) & 0xFF] ^ T4[t[(i + s3) % BC] & 0xFF]) ^ Ke[r][i];
}
System.arraycopy(a, 0, t, 0, BC);
if (DEBUG && (debuglevel > 6)) {
System.out.println("CT" + r + "=" + toString(t));
}
}
for (i = 0; i < BC; i++) { // last round is special
tt = Ke[ROUNDS][i];
result[j++] = (byte) (S[(t[i] >>> 24) & 0xFF] ^ (tt >>> 24));
result[j++] = (byte) (S[(t[(i + s1) % BC] >>> 16) & 0xFF] ^ (tt >>> 16));
result[j++] = (byte) (S[(t[(i + s2) % BC] >>> 8) & 0xFF] ^ (tt >>> 8));
result[j++] = (byte) (S[t[(i + s3) % BC] & 0xFF] ^ tt);
}
if (DEBUG && (debuglevel > 6)) {
System.out.println("CT=" + toString(result));
System.out.println();
}
if (DEBUG) {
trace(OUT, "blockEncrypt()");
}
return result;
}
// Rijndael own methods
//...........................................................................
/** @return The default length in bytes of the Algorithm input block. */
public static int blockSize() {
return BLOCK_SIZE;
}
// main(): use to generate the Intermediate Values KAT
//...........................................................................
public static void main(String[] args) {
self_test(16);
self_test(24);
self_test(32);
}
// Basic API methods
//...........................................................................
/**
* Convenience method to expand a user-supplied key material into a
* session key, assuming Rijndael's default block size (128-bit).
*
* @param k The 128/192/256-bit user-key to use.
* @exception InvalidKeyException If the key is invalid.
*/
public static Object makeKey(byte[] k) throws InvalidKeyException {
return makeKey(k, BLOCK_SIZE);
}
/**
* Expand a user-supplied key material into a session key.
*
* @param k The 128/192/256-bit user-key to use.
* @param blockSize The block size in bytes of this Rijndael.
* @exception InvalidKeyException If the key is invalid.
*/
public static synchronized Object makeKey(byte[] k, int blockSize) throws InvalidKeyException {
if (DEBUG) {
trace(IN, "makeKey(" + k + ", " + blockSize + ")");
}
if (k == null) {
throw new InvalidKeyException("Empty key");
}
if (!((k.length == 16) || (k.length == 24) || (k.length == 32))) {
throw new InvalidKeyException("Incorrect key length");
}
int ROUNDS = getRounds(k.length, blockSize);
int BC = blockSize / 4;
int[][] Ke = new int[ROUNDS + 1][BC]; // encryption round keys
int[][] Kd = new int[ROUNDS + 1][BC]; // decryption round keys
int ROUND_KEY_COUNT = (ROUNDS + 1) * BC;
int KC = k.length / 4;
int[] tk = new int[KC];
int i;
int j;
// copy user material bytes into temporary ints
for (i = 0, j = 0; i < KC;) {
tk[i++] = ((k[j++] & 0xFF) << 24) | ((k[j++] & 0xFF) << 16) | ((k[j++] & 0xFF) << 8) | (k[j++] & 0xFF);
}
// copy values into round key arrays
int t = 0;
for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
Ke[t / BC][t % BC] = tk[j];
Kd[ROUNDS - (t / BC)][t % BC] = tk[j];
}
int tt;
int rconpointer = 0;
while (t < ROUND_KEY_COUNT) {
// extrapolate using phi (the round key evolution function)
tt = tk[KC - 1];
tk[0] ^= (((S[(tt >>> 16) & 0xFF] & 0xFF) << 24) ^ ((S[(tt >>> 8) & 0xFF] & 0xFF) << 16) ^ ((S[tt & 0xFF] & 0xFF) << 8) ^ (S[(tt >>> 24) & 0xFF] & 0xFF) ^ ((rcon[rconpointer++] & 0xFF) << 24));
if (KC != 8) {
for (i = 1, j = 0; i < KC;) {
tk[i++] ^= tk[j++];
}
} else {
for (i = 1, j = 0; i < (KC / 2);) {
tk[i++] ^= tk[j++];
}
tt = tk[(KC / 2) - 1];
tk[KC / 2] ^= ((S[tt & 0xFF] & 0xFF) ^ ((S[(tt >>> 8) & 0xFF] & 0xFF) << 8) ^ ((S[(tt >>> 16) & 0xFF] & 0xFF) << 16) ^ ((S[(tt >>> 24) & 0xFF] & 0xFF) << 24));
for (j = KC / 2, i = j + 1; i < KC;) {
tk[i++] ^= tk[j++];
}
}
// copy values into round key arrays
for (j = 0; (j < KC) && (t < ROUND_KEY_COUNT); j++, t++) {
Ke[t / BC][t % BC] = tk[j];
Kd[ROUNDS - (t / BC)][t % BC] = tk[j];
}
}
for (int r = 1; r < ROUNDS; r++) // inverse MixColumn where needed
{
for (j = 0; j < BC; j++) {
tt = Kd[r][j];
Kd[r][j] = U1[(tt >>> 24) & 0xFF] ^ U2[(tt >>> 16) & 0xFF] ^ U3[(tt >>> 8) & 0xFF] ^ U4[tt & 0xFF];
}
}
// assemble the encryption (Ke) and decryption (Kd) round keys into
// one sessionKey object
Object[] sessionKey = new Object[] {Ke, Kd};
if (DEBUG) {
trace(OUT, "makeKey()");
}
return sessionKey;
}
/** A basic symmetric encryption/decryption test. */
public static boolean self_test() {
return self_test(BLOCK_SIZE);
}
static void debug(String s) {
err.println(">>> " + NAME + ": " + s);
}
// multiply two elements of GF(2^m)
static final int mul(int a, int b) {
return ((a != 0) && (b != 0)) ? alog[(log[a & 0xFF] + log[b & 0xFF]) % 255] : 0;
}
// convenience method used in generating Transposition boxes
static final int mul4(int a, byte[] b) {
if (a == 0) {
return 0;
}
a = log[a & 0xFF];
int a0 = (b[0] != 0) ? (alog[(a + log[b[0] & 0xFF]) % 255] & 0xFF) : 0;
int a1 = (b[1] != 0) ? (alog[(a + log[b[1] & 0xFF]) % 255] & 0xFF) : 0;
int a2 = (b[2] != 0) ? (alog[(a + log[b[2] & 0xFF]) % 255] & 0xFF) : 0;
int a3 = (b[3] != 0) ? (alog[(a + log[b[3] & 0xFF]) % 255] & 0xFF) : 0;
return (a0 << 24) | (a1 << 16) | (a2 << 8) | a3;
}
static void trace(boolean in, String s) {
if (TRACE) {
err.println((in ? "==> " : "<== ") + NAME + "." + s);
}
}
static void trace(String s) {
if (TRACE) {
err.println("<=> " + NAME + "." + s);
}
}
// utility static methods (from cryptix.util.core ArrayUtil and Hex classes)
//...........................................................................
/**
* Compares two byte arrays for equality.
*
* @return true if the arrays have identical contents
*/
private static boolean areEqual(byte[] a, byte[] b) {
int aLength = a.length;
if (aLength != b.length) {
return false;
}
for (int i = 0; i < aLength; i++) {
if (a[i] != b[i]) {
return false;
}
}
return true;
}
/**
* Returns a string of 2 hexadecimal digits (most significant
* digit first) corresponding to the lowest 8 bits of <i>n</i>.
*/
private static String byteToString(int n) {
char[] buf = {HEX_DIGITS[(n >>> 4) & 0x0F], HEX_DIGITS[n & 0x0F]};
return new String(buf);
}
/**
* Returns a string of 8 hexadecimal digits (most significant
* digit first) corresponding to the integer <i>n</i>, which is
* treated as unsigned.
*/
private static String intToString(int n) {
char[] buf = new char[8];
for (int i = 7; i >= 0; i--) {
buf[i] = HEX_DIGITS[n & 0x0F];
n >>>= 4;
}
return new String(buf);
}
/** A basic symmetric encryption/decryption test for a given key size. */
private static boolean self_test(int keysize) {
if (DEBUG) {
trace(IN, "self_test(" + keysize + ")");
}
boolean ok = false;
try {
byte[] kb = new byte[keysize];
byte[] pt = new byte[BLOCK_SIZE];
int i;
for (i = 0; i < keysize; i++) {
kb[i] = (byte) i;
}
for (i = 0; i < BLOCK_SIZE; i++) {
pt[i] = (byte) i;
}
if (DEBUG && (debuglevel > 6)) {
System.out.println("==========");
System.out.println();
System.out.println("KEYSIZE=" + (8 * keysize));
System.out.println("KEY=" + toString(kb));
System.out.println();
}
Object key = makeKey(kb, BLOCK_SIZE);
if (DEBUG && (debuglevel > 6)) {
System.out.println("Intermediate Ciphertext Values (Encryption)");
System.out.println();
System.out.println("PT=" + toString(pt));
}
byte[] ct = blockEncrypt(pt, 0, key, BLOCK_SIZE);
if (DEBUG && (debuglevel > 6)) {
System.out.println("Intermediate Plaintext Values (Decryption)");
System.out.println();
System.out.println("CT=" + toString(ct));
}
byte[] cpt = blockDecrypt(ct, 0, key, BLOCK_SIZE);
ok = areEqual(pt, cpt);
if (!ok) {
throw new RuntimeException("Symmetric operation failed");
}
} catch (Exception x) {
if (DEBUG && (debuglevel > 0)) {
debug("Exception encountered during self-test: " + x.getMessage());
x.printStackTrace();
}
}
if (DEBUG && (debuglevel > 0)) {
debug("Self-test OK? " + ok);
}
if (DEBUG) {
trace(OUT, "self_test()");
}
return ok;
}
/**
* Returns a string of hexadecimal digits from a byte array. Each
* byte is converted to 2 hex symbols.
*/
private static String toString(byte[] ba) {
int length = ba.length;
char[] buf = new char[length * 2];
for (int i = 0, j = 0, k; i < length;) {
k = ba[i++];
buf[j++] = HEX_DIGITS[(k >>> 4) & 0x0F];
buf[j++] = HEX_DIGITS[k & 0x0F];
}
return new String(buf);
}
/**
* Returns a string of hexadecimal digits from an integer array. Each
* int is converted to 4 hex symbols.
*/
private static String toString(int[] ia) {
int length = ia.length;
char[] buf = new char[length * 8];
for (int i = 0, j = 0, k; i < length; i++) {
k = ia[i];
buf[j++] = HEX_DIGITS[(k >>> 28) & 0x0F];
buf[j++] = HEX_DIGITS[(k >>> 24) & 0x0F];
buf[j++] = HEX_DIGITS[(k >>> 20) & 0x0F];
buf[j++] = HEX_DIGITS[(k >>> 16) & 0x0F];
buf[j++] = HEX_DIGITS[(k >>> 12) & 0x0F];
buf[j++] = HEX_DIGITS[(k >>> 8) & 0x0F];
buf[j++] = HEX_DIGITS[(k >>> 4) & 0x0F];
buf[j++] = HEX_DIGITS[k & 0x0F];
}
return new String(buf);
}
}
|