package uk.ac.cam.db538.crypto;
import java.util.*;
/** AES - implementation of the AES block cipher in Java.
* <p> Illustrative code for the AES block cipher (Rijndael).
* Handles a single block encryption or decryption, with diagnostic
* logging of intermediate values if required.
* <p> AES is a block cipher with a key length of 16/24/32 bytes
* and a block length 16 bytes.
* <p>
* AES (Rijndael) was designed by Joan Daemen and Vincent Rijmen,
* and was accepted as the US NIST's Advanced Encryption Standard in 2000.
* <p>
* This is the <b>slow, obvious code</b> version, written to follow the
* algorithm specification as closely and clearly as possible.
* It's code was originally derived from the illustrative Rijndael Java
* implementation submitted to the AES process, and sourced from the
* <a href="http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael.zip">
* Rijndael sample Java code</a>, but has been extensively adapted by Lawrie Brown
* to suit the illustrative requirements of the crypto calc applets
* he has written for his Cryptography courses at ADFA.
* The code has been tested using the AES triples published in FIPS-197 App C.
*
* @author Joan Daemen, Vincent Rijmen, Lawrie Brown, Feb 2005
* @see <a href="http://www.unsw.adfa.edu.au/~lpb/">Lawrie Brown</a>
* @see <a href="http://csrc.nist.gov/encryption/aes/">AES home page</a>
* @see <a href="http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf">FIPS-197 Standard</a>
* @see <a href="http://www.esat.kuleuven.ac.be/~rijmen/rijndael/">Rijndael Home Page</a>
* @see <a href="http://www.esat.kuleuven.ac.be/~rijmen/rijndael/rijndael.zip">Rijndael example Java code</a>
*/
class AesAlgorithm {
/** specify whether diagnostic trace output is required.
* <p>
* Available levels are:<br>
* 0: no trace info is generated<br>
* 1: trace major calls with params (setKey, encrypt, decrypt)<br>
* 2: + trace round values whilst en/decrypting<br>
* 3: + trace all steps within each round<br>
* 4: + trace subkey generation<br>
* 5: + trace static table values<br>
*/
public int traceLevel = 0;
/** string which accumulates diagnostic output for display.
* <p>
* Contents are reset on each major call (setKey, encrypt, decrypt)
* and should be used after each of these calls returns for display.
*/
public String traceInfo = "";
/** AES constants and variables. */
public static final int
ROUNDS = 14, // AES has 10-14 rounds
BLOCK_SIZE = 16, // AES uses 128-bit (16 byte) key
KEY_LENGTH = 32; // AES uses 128/192/256-bit (16/24/32 byte) key
// Define key attributes for current AES instance
/** number of rounds used given AES key set on this instance. */
int numRounds;
/** encryption round keys derived from AES key set on this instance. */
byte[][] Ke;
/** decryption round keys derived from AES key set on this instance. */
byte[][] Kd;
/** AES encryption S-box.
* <p>See FIPS-197 section 5.1.1 or Stallings section 5.2.
* Note that hex values have been converted to decimal for easy table
* specification in Java.
*/
static final byte[] S = {
99, 124, 119, 123, -14, 107, 111, -59, 48, 1, 103, 43, -2, -41, -85, 118,
-54, -126, -55, 125, -6, 89, 71, -16, -83, -44, -94, -81, -100, -92, 114, -64,
-73, -3, -109, 38, 54, 63, -9, -52, 52, -91, -27, -15, 113, -40, 49, 21,
4, -57, 35, -61, 24, -106, 5, -102, 7, 18, -128, -30, -21, 39, -78, 117,
9, -125, 44, 26, 27, 110, 90, -96, 82, 59, -42, -77, 41, -29, 47, -124,
83, -47, 0, -19, 32, -4, -79, 91, 106, -53, -66, 57, 74, 76, 88, -49,
-48, -17, -86, -5, 67, 77, 51, -123, 69, -7, 2, 127, 80, 60, -97, -88,
81, -93, 64, -113, -110, -99, 56, -11, -68, -74, -38, 33, 16, -1, -13, -46,
-51, 12, 19, -20, 95, -105, 68, 23, -60, -89, 126, 61, 100, 93, 25, 115,
96, -127, 79, -36, 34, 42, -112, -120, 70, -18, -72, 20, -34, 94, 11, -37,
-32, 50, 58, 10, 73, 6, 36, 92, -62, -45, -84, 98, -111, -107, -28, 121,
-25, -56, 55, 109, -115, -43, 78, -87, 108, 86, -12, -22, 101, 122, -82, 8,
-70, 120, 37, 46, 28, -90, -76, -58, -24, -35, 116, 31, 75, -67, -117, -118,
112, 62, -75, 102, 72, 3, -10, 14, 97, 53, 87, -71, -122, -63, 29, -98,
-31, -8, -104, 17, 105, -39, -114, -108, -101, 30, -121, -23, -50, 85, 40, -33,
-116, -95, -119, 13, -65, -26, 66, 104, 65, -103, 45, 15, -80, 84, -69, 22 };
/** AES decryption S-box.
* <p>See FIPS-197 section 5.1.1 or Stallings section 5.2.
* Note that hex values have been converted to decimal for easy table
* specification in Java.
*/
static final byte[] Si = {
82, 9, 106, -43, 48, 54, -91, 56, -65, 64, -93, -98, -127, -13, -41, -5,
124, -29, 57, -126, -101, 47, -1, -121, 52, -114, 67, 68, -60, -34, -23, -53,
84, 123, -108, 50, -90, -62, 35, 61, -18, 76, -107, 11, 66, -6, -61, 78,
8, 46, -95, 102, 40, -39, 36, -78, 118, 91, -94, 73, 109, -117, -47, 37,
114, -8, -10, 100, -122, 104, -104, 22, -44, -92, 92, -52, 93, 101, -74, -110,
108, 112, 72, 80, -3, -19, -71, -38, 94, 21, 70, 87, -89, -115, -99, -124,
-112, -40, -85, 0, -116, -68, -45, 10, -9, -28, 88, 5, -72, -77, 69, 6,
-48, 44, 30, -113, -54, 63, 15, 2, -63, -81, -67, 3, 1, 19, -118, 107,
58, -111, 17, 65, 79, 103, -36, -22, -105, -14, -49, -50, -16, -76, -26, 115,
-106, -84, 116, 34, -25, -83, 53, -123, -30, -7, 55, -24, 28, 117, -33, 110,
71, -15, 26, 113, 29, 41, -59, -119, 111, -73, 98, 14, -86, 24, -66, 27,
-4, 86, 62, 75, -58, -46, 121, 32, -102, -37, -64, -2, 120, -51, 90, -12,
31, -35, -88, 51, -120, 7, -57, 49, -79, 18, 16, 89, 39, -128, -20, 95,
96, 81, 127, -87, 25, -75, 74, 13, 45, -27, 122, -97, -109, -55, -100, -17,
-96, -32, 59, 77, -82, 42, -11, -80, -56, -21, -69, 60, -125, 83, -103, 97,
23, 43, 4, 126, -70, 119, -42, 38, -31, 105, 20, 99, 85, 33, 12, 125 };
/** AES key schedule round constant table.
* <p>See FIPS-197 section 5.1.1 or Stallings section 5.2.
* Note that hex values have been converted to decimal for easy table
* specification in Java, and that indexes start at 1, hence initial 0 entry.
*/
static final byte[] rcon = {
0,
1, 2, 4, 8, 16, 32,
64, -128, 27, 54, 108, -40,
-85, 77, -102, 47, 94, -68,
99, -58, -105, 53, 106, -44,
-77, 125, -6, -17, -59, -111 };
/** Internal AES constants and variables. */
public static final int
COL_SIZE = 4, // depth of each column in AES state variable
NUM_COLS = BLOCK_SIZE / COL_SIZE, // number of columns in AES state variable
ROOT = 0x11B; // generator polynomial used in GF(2^8)
/** define ShiftRows transformation as shift amount for each row in state. */
static final int[] row_shift = {0, 1, 2, 3};
/* alog table for field GF(2^m) used to speed up multiplications. */
static final int[] alog = new int[256];
/* log table for field GF(2^m) used to speed up multiplications. */
static final int[] log = new int[256];
/** static code to initialise the log and alog tables.
* Used to implement multiplication in GF(2^8).
*/
static {
int i, j;
// produce log and alog tables, needed for multiplying in the field GF(2^8)
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;
}
/** Construct AES object. */
public AesAlgorithm() {
}
/** return number of rounds for a given AES key size.
*
* @param keySize size of the user key material in bytes.
* @return number of rounds for a given AES key size.
*/
public static int getRounds (int keySize) {
switch (keySize) {
case 16: // 16 byte = 128 bit key
return 10;
case 24: // 24 byte = 192 bit key
return 12;
default: // 32 byte = 256 bit key
return 14;
}
}
/** multiply two elements of GF(2^8).
* <p>Using pre-computed log and alog tables for speed.
*
* @param a 1st value to multiply
* @param b 2nd value to multiply
* @return product of a * b module its generator polynomial
*/
static final int mul (int a, int b) {
return (a != 0 && b != 0) ?
alog[(log[a & 0xFF] + log[b & 0xFF]) % 255] :
0;
}
/** diagnostic trace of static tables. */
public static void trace_static() {
int i,j;
System.out.print("AES Static Tablesn");
System.out.print("S[] = n"); for(i=0;i<16;i++) { for(j=0;j<16;j++) System.out.print(Util.toHEX1(S[i*16+j])+", "); System.out.println();}
System.out.print("Si[] = n"); for(i=0;i<16;i++) { for(j=0;j<16;j++) System.out.print(Util.toHEX1(Si[i*16+j])+", "); System.out.println();}
System.out.print("rcon[] = n"); for(i=0;i<5;i++) {for(j=0;j<6;j++) System.out.print(Util.toHEX1(rcon[i*6+j])+", "); System.out.println();}
System.out.print("log[] = n"); for(i=0;i<32;i++) {for(j=0;j<8;j++) System.out.print(Util.toHEX1(log[i*8+j])+", "); System.out.println();}
System.out.print("alog[] = n"); for(i=0;i<32;i++) {for(j=0;j<8;j++) System.out.print(Util.toHEX1(alog[i*8+j])+", "); System.out.println();}
}
//......................................................................
/**
* AES encrypt 128-bit plaintext using key previously set.
*
* <p>Follows cipher specification given in FIPS-197 section 5.1
* See pseudo code in Fig 5, and details in this section.
*
* @param plain the 128-bit plaintext value to encrypt.
* @return the encrypted 128-bit ciphertext value.
*/
public byte[] encrypt(byte[] plain) {
// define working variables
byte [] a = new byte[BLOCK_SIZE]; // AES state variable
byte [] ta = new byte[BLOCK_SIZE]; // AES temp state variable
byte [] Ker; // encrypt keys for current round
int i, k, row, col;
traceInfo = ""; // reset trace info
if (traceLevel > 0) traceInfo = "encryptAES(" + Util.toHEX1(plain) + ")";
// check for bad arguments
if (plain == null)
throw new IllegalArgumentException("Empty plaintext");
if (plain.length != BLOCK_SIZE)
throw new IllegalArgumentException("Incorrect plaintext length");
// copy plaintext bytes into state and do initial AddRoundKey(state)
Ker = Ke[0];
for (i = 0; i < BLOCK_SIZE; i++) a[i] = (byte)(plain[i] ^ Ker[i]);
if (traceLevel > 2)
traceInfo += "n R0 (Key = "+Util.toHEX1(Ker)+")ntAK = "+Util.toHEX1(a);
else if (traceLevel > 1)
traceInfo += "n R0 (Key = "+Util.toHEX1(Ker)+")t = "+Util.toHEX1(a);
// for each round except last, apply round transforms
for (int r = 1; r < numRounds; r++) {
Ker = Ke[r]; // get session keys for this round
if (traceLevel > 1) traceInfo += "n R"+r+" (Key = "+Util.toHEX1(Ker)+")t";
// SubBytes(state) into ta using S-Box S
for (i = 0; i < BLOCK_SIZE; i++) ta[i] = S[a[i] & 0xFF];
if (traceLevel > 2) traceInfo += "ntSB = "+Util.toHEX1(ta);
// ShiftRows(state) into a
for (i = 0; i < BLOCK_SIZE; i++) {
row = i % COL_SIZE;
k = (i + (row_shift[row] * COL_SIZE)) % BLOCK_SIZE; // get shifted byte index
a[i] = ta[k];
}
if (traceLevel > 2) traceInfo += "ntSR = "+Util.toHEX1(a);
// MixColumns(state) into ta
// implemented by expanding matrix mult for each column
// see FIPS-197 section 5.1.3
for (col = 0; col < NUM_COLS; col++) {
i = col * COL_SIZE; // start index for this col
ta[i] = (byte)(mul(2,a[i]) ^ mul(3,a[i+1]) ^ a[i+2] ^ a[i+3]);
ta[i+1] = (byte)(a[i] ^ mul(2,a[i+1]) ^ mul(3,a[i+2]) ^ a[i+3]);
ta[i+2] = (byte)(a[i] ^ a[i+1] ^ mul(2,a[i+2]) ^ mul(3,a[i+3]));
ta[i+3] = (byte)(mul(3,a[i]) ^ a[i+1] ^ a[i+2] ^ mul(2,a[i+3]));
}
if (traceLevel > 2) traceInfo += "ntMC = "+Util.toHEX1(ta);
// AddRoundKey(state) into a
for (i = 0; i < BLOCK_SIZE; i++) a[i] = (byte)(ta[i] ^ Ker[i]);
if (traceLevel > 2) traceInfo += "ntAK";
if (traceLevel > 1) traceInfo += " = "+Util.toHEX1(a);
}
// last round is special - only has SubBytes, ShiftRows and AddRoundKey
Ker = Ke[numRounds]; // get session keys for final round
if (traceLevel > 1) traceInfo += "n R"+numRounds+" (Key = "+Util.toHEX1(Ker)+")t";
// SubBytes(state) into a using S-Box S
for (i = 0; i < BLOCK_SIZE; i++) a[i] = S[a[i] & 0xFF];
if (traceLevel > 2) traceInfo += "ntSB = "+Util.toHEX1(a);
// ShiftRows(state) into ta
for (i = 0; i < BLOCK_SIZE; i++) {
row = i % COL_SIZE;
k = (i + (row_shift[row] * COL_SIZE)) % BLOCK_SIZE; // get shifted byte index
ta[i] = a[k];
}
if (traceLevel > 2) traceInfo += "ntSR = "+Util.toHEX1(a);
// AddRoundKey(state) into a
for (i = 0; i < BLOCK_SIZE; i++) a[i] = (byte)(ta[i] ^ Ker[i]);
if (traceLevel > 2) traceInfo += "ntAK";
if (traceLevel > 1) traceInfo += " = "+Util.toHEX1(a)+"n";
if (traceLevel > 0) traceInfo += " = "+Util.toHEX1(a)+"n";
return (a);
}
//......................................................................
/**
* AES decrypt 128-bit ciphertext using key previously set.
*
* <p>Follows cipher specification given in FIPS-197 section 5.3
* See pseudo code in Fig 5, and details in this section.
*
* @param cipher the 128-bit ciphertext value to decrypt.
* @return the decrypted 128-bit plaintext value.
*/
public byte[] decrypt(byte[] cipher) {
// define working variables
byte [] a = new byte[BLOCK_SIZE]; // AES state variable
byte [] ta = new byte[BLOCK_SIZE]; // AES temp state variable
byte [] Kdr; // encrypt keys for current round
int i, k, row, col;
traceInfo = ""; // reset trace info
if (traceLevel > 0) traceInfo = "decryptAES(" + Util.toHEX1(cipher) + ")";
// check for bad arguments
if (cipher == null)
throw new IllegalArgumentException("Empty ciphertext");
if (cipher.length != BLOCK_SIZE)
throw new IllegalArgumentException("Incorrect ciphertext length");
// copy ciphertext bytes into state and do initial AddRoundKey(state)
Kdr = Kd[0];
for (i = 0; i < BLOCK_SIZE; i++) a[i] = (byte)(cipher[i] ^ Kdr[i]);
if (traceLevel > 2)
traceInfo += "n R0 (Key = "+Util.toHEX1(Kdr)+")nt AK = "+Util.toHEX1(a);
else if (traceLevel > 1)
traceInfo += "n R0 (Key = "+Util.toHEX1(Kdr)+")t = "+Util.toHEX1(a);
// for each round except last, apply round transforms
for (int r = 1; r < numRounds; r++) {
Kdr = Kd[r]; // get session keys for this round
if (traceLevel > 1) traceInfo += "n R"+r+" (Key = "+Util.toHEX1(Kdr)+")t";
// InvShiftRows(state) into ta (nb. same shift as encrypt but subtract)
for (i = 0; i < BLOCK_SIZE; i++) {
row = i % COL_SIZE;
// get shifted byte index
k = (i + BLOCK_SIZE - (row_shift[row] * COL_SIZE)) % BLOCK_SIZE;
ta[i] = a[k];
}
if (traceLevel > 2) traceInfo += "ntISR = "+Util.toHEX1(ta);
// InvSubBytes(state) into a using inverse S-box Si
for (i = 0; i < BLOCK_SIZE; i++) a[i] = Si[ta[i] & 0xFF];
if (traceLevel > 2) traceInfo += "ntISB = "+Util.toHEX1(a);
// AddRoundKey(state) into ta
for (i = 0; i < BLOCK_SIZE; i++) ta[i] = (byte)(a[i] ^ Kdr[i]);
if (traceLevel > 2) traceInfo += "nt AK = "+Util.toHEX1(ta);
// InvMixColumns(state) into a
// implemented by expanding matrix mult for each column
// see FIPS-197 section 5.3.3
for (col = 0; col < NUM_COLS; col++) {
i = col * COL_SIZE; // start index for this col
a[i] = (byte)(mul(0x0e,ta[i]) ^ mul(0x0b,ta[i+1]) ^ mul(0x0d,ta[i+2]) ^ mul(0x09,ta[i+3]));
a[i+1] = (byte)(mul(0x09,ta[i]) ^ mul(0x0e,ta[i+1]) ^ mul(0x0b,ta[i+2]) ^ mul(0x0d,ta[i+3]));
a[i+2] = (byte)(mul(0x0d,ta[i]) ^ mul(0x09,ta[i+1]) ^ mul(0x0e,ta[i+2]) ^ mul(0x0b,ta[i+3]));
a[i+3] = (byte)(mul(0x0b,ta[i]) ^ mul(0x0d,ta[i+1]) ^ mul(0x09,ta[i+2]) ^ mul(0x0e,ta[i+3]));
}
if (traceLevel > 2) traceInfo += "ntIMC";
if (traceLevel > 1) traceInfo += " = "+Util.toHEX1(a);
}
// last round is special - only has InvShiftRows, InvSubBytes and AddRoundKey
Kdr = Kd[numRounds]; // get session keys for final round
if (traceLevel > 1) traceInfo += "n R"+numRounds+" (Key = "+Util.toHEX1(Kdr)+")t";
// InvShiftRows(state) into ta
for (i = 0; i < BLOCK_SIZE; i++) {
row = i % COL_SIZE;
// get shifted byte index
k = (i + BLOCK_SIZE - (row_shift[row] * COL_SIZE)) % BLOCK_SIZE;
ta[i] = a[k];
}
if (traceLevel > 2) traceInfo += "ntISR = "+Util.toHEX1(a);
// InvSubBytes(state) into ta using inverse S-box Si
for (i = 0; i < BLOCK_SIZE; i++) ta[i] = Si[ta[i] & 0xFF];
if (traceLevel > 2) traceInfo += "ntISB = "+Util.toHEX1(a);
// AddRoundKey(state) into a
for (i = 0; i < BLOCK_SIZE; i++) a[i] = (byte)(ta[i] ^ Kdr[i]);
if (traceLevel > 2) traceInfo += "nt AK";
if (traceLevel > 1) traceInfo += " = "+Util.toHEX1(a)+"n";
if (traceLevel > 0) traceInfo += " = "+Util.toHEX1(a)+"n";
return (a);
}
//......................................................................
/**
* Expand a user-supplied key material into a session key.
* <p>See FIPS-197 Section 5.3 Fig 11 for details of the key expansion.
* <p>Session keys will be saved in Ke and Kd instance variables,
* along with numRounds being the number of rounds for this sized key.
*
* @param key The 128/192/256-bit AES key to use.
*/
@SuppressWarnings("unused")
public void setKey(byte[] key) {
// assorted internal constants
final int BC = BLOCK_SIZE / 4;
final int Klen = key.length;
final int Nk = Klen / 4;
int i, j, r;
traceInfo = ""; // reset trace info
if (traceLevel > 0) traceInfo = "setKey(" + Util.toHEX1(key) + ")n";
// check for bad arguments
if (key == null)
throw new IllegalArgumentException("Empty key");
if (!(key.length == 16 || key.length == 24 || key.length == 32))
throw new IllegalArgumentException("Incorrect key length");
// set master number of rounds given size of this key
numRounds = getRounds(Klen);
final int ROUND_KEY_COUNT = (numRounds + 1) * BC;
// allocate 4 arrays of bytes to hold the session key values
// each array holds 1 of the 4 bytes [b0 b1 b2 b3] in each word w
byte[] w0 = new byte[ROUND_KEY_COUNT];
byte[] w1 = new byte[ROUND_KEY_COUNT];
byte[] w2 = new byte[ROUND_KEY_COUNT];
byte[] w3 = new byte[ROUND_KEY_COUNT];
// allocate arrays to hold en/decrypt session keys (by byte rather than word)
Ke = new byte[numRounds + 1][BLOCK_SIZE]; // encryption round keys
Kd = new byte[numRounds + 1][BLOCK_SIZE]; // decryption round keys
// copy key into start of session array (by word, each byte in own array)
for (i=0, j=0; i < Nk; i++) {
w0[i] = key[j++]; w1[i] = key[j++]; w2[i] = key[j++]; w3[i] = key[j++];
}
// implement key expansion algorithm
byte t0, t1, t2, t3, old0; // temp byte values for each word
for (i = Nk; i < ROUND_KEY_COUNT; i++) {
t0 = w0[i-1]; t1 = w1[i-1]; t2 = w2[i-1]; t3 = w3[i-1]; // temp = w[i-1]
if (i % Nk == 0) {
// temp = SubWord(RotWord(temp)) ^ Rcon[i/Nk]
old0 = t0; // save old 1st byte value for t3 calc
t0 = (byte)(S[t1 & 0xFF] ^ rcon[i/Nk]); // nb. constant XOR 1st byte only
t1 = (byte)(S[t2 & 0xFF]);
t2 = (byte)(S[t3 & 0xFF]); // nb. RotWord done by reordering bytes used
t3 = (byte)(S[old0 & 0xFF]);
}
else if ((Nk > 6) && (i % Nk == 4)) {
// temp = SubWord(temp)
t0 = S[t0 & 0xFF]; t1 = S[t1 & 0xFF]; t2 = S[t2 & 0xFF]; t3 = S[t3 & 0xFF];
}
// w[i] = w[i-Nk] ^ temp
w0[i] = (byte)(w0[i-Nk] ^ t0);
w1[i] = (byte)(w1[i-Nk] ^ t1);
w2[i] = (byte)(w2[i-Nk] ^ t2);
w3[i] = (byte)(w3[i-Nk] ^ t3);
}
// now copy values into en/decrypt session arrays by round & byte in round
for (r = 0, i = 0; r < numRounds + 1; r++) { // for each round
for (j = 0; j < BC; j++) { // for each word in round
Ke[r][4*j] = w0[i];
Ke[r][4*j+1] = w1[i];
Ke[r][4*j+2] = w2[i];
Ke[r][4*j+3] = w3[i];
Kd[numRounds - r][4*j] = w0[i];
Kd[numRounds - r][4*j+1] = w1[i];
Kd[numRounds - r][4*j+2] = w2[i];
Kd[numRounds - r][4*j+3] = w3[i];
i++;
}
}
// create trace info if needed
if (traceLevel > 3) {
traceInfo += " Encrypt Round keys:n";
for(r=0;r<numRounds+1;r++) traceInfo += " R"+r+"t = "+Util.toHEX1(Ke[r])+"n";
traceInfo += " Decrypt Round keys:n";
for(r=0;r<numRounds+1;r++) traceInfo += " R"+r+"t = "+Util.toHEX1(Kd[r])+"n";
}
}
/** self-test routine for AES cipher
* @param hkey key to test in hex
* @param hplain plaintext to test in hex
* @param hcipher ciphertext to test in hex
* @param lev trace level to use
*/
public static void self_test (String hkey, String hplain, String hcipher, int lev) {
// AES test triple (128-bit key test value from FIPS-197)
byte [] key = Util.hex2byte(hkey);
byte [] plain = Util.hex2byte(hplain);
byte [] cipher = Util.hex2byte(hcipher);
byte [] result;
AesAlgorithm testAES = new AesAlgorithm(); // create new AES instance to test triple
testAES.traceLevel = lev; // select level of trace info
testAES.setKey(key); // set key and display trace info
System.out.print(testAES.traceInfo);
result = testAES.encrypt(plain); // test encryption
System.out.print(testAES.traceInfo);
if (Arrays.equals(result, cipher))
System.out.print("Test OKn");
else
System.out.print("Test Failed. Result was "+Util.toHEX(result)+"n");
result = testAES.decrypt(cipher); // test decryption
System.out.print(testAES.traceInfo);
if (Arrays.equals(result, plain))
System.out.print("Test OKn");
else
System.out.print("Test Failed. Result was "+Util.toHEX(result)+"n");
System.out.println();
}
public static String static_byteArrayToString(byte[] data) {
String res = "";
StringBuffer sb = new StringBuffer();
for(int i=0; i<data.length; i++) {
int n = (int) data[i];
if(n<0) n += 256;
sb.append((char) n);
}
res = sb.toString();
return res;
}
public static byte[] static_stringToByteArray(String s){
byte[] temp = new byte[s.length()];
for(int i=0;i<s.length();i++){
temp[i] = (byte) s.charAt(i);
}
return temp;
}
public static String static_intArrayToString(int[]t){
StringBuffer sb = new StringBuffer();
for(int i=0;i<t.length;i++){
sb.append((char)t[i]);
}
return sb.toString();
}
/** self-test routine for AES cipher
@param args command line arguments
*/
public String _cryptAll(String data, int mode) {
AesAlgorithm aesAlgorithm = this;
if(data.length()/16 > ((int) data.length()/16)) {
int rest = data.length()-((int) data.length()/16)*16;
for(int i=0; i<rest; i++)
data += " ";
}
int nParts = (int) data.length()/16;
byte[] res = new byte[data.length()];
String partStr = "";
byte[] partByte = new byte[16];
for(int p=0; p<nParts; p++) {
partStr = data.substring(p*16, p*16+16);
partByte = static_stringToByteArray(partStr);
if(mode==1) partByte = aesAlgorithm.encrypt(partByte);
if(mode==2) partByte = aesAlgorithm.decrypt(partByte);
for(int b=0; b<16; b++)
res[p*16+b] = partByte[b];
}
return static_byteArrayToString(res);
}
public String Encrypt(String data) {
return _cryptAll(data, 1);
}
public String Decrypt(String data) {
return _cryptAll(data, 2);
}
public void setKey(String key) {
//System.out.println("CRYPT KEY IS "+key);
setKey(static_stringToByteArray(key));
}
}