de.uniks.networkparser.bytes.AES Maven / Gradle / Ivy
package de.uniks.networkparser.bytes;
import de.uniks.networkparser.buffer.BufferedBuffer;
import de.uniks.networkparser.buffer.ByteBuffer;
import de.uniks.networkparser.buffer.CharacterBuffer;
import de.uniks.networkparser.converter.ByteConverterString;
/**
* AES - implementation of the AES block cipher in Java.
*
* Illustrative code for the AES block cipher (Rijndael). Handles a single block
* encryption or decryption, with diagnostic logging of intermediate values if
* required.
*
* AES is a block cipher with a key length of 16/24/32 bytes and a block length
* 16 bytes.
*
* AES (Rijndael) was designed by Joan Daemen and Vincent Rijmen, and was
* accepted as the US NIST's Advanced Encryption Standard in 2000.
*
* This is the slow, obvious code 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 Rijndael
* sample Java code, 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 Improved by
* Olivier Ligny, Aug 2012
* @see Lawrie Brown
* @see AES home page
* @see FIPS-197 Standard
* @see Rijndael Home Page
* @see Rijndael example Java code
*/
public class AES {
/** 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.
*
* 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.
*
* 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.
*
* 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;
}
private ByteConverterString converter = new ByteConverterString();
/**
* 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).
*
* Using pre-computed log and alog tables for speed.
*
* @param a first value to multiply
* @param b second value to multiply
* @return product of a * b module its generator polynomial
*/
private int mul(int a, int b) {
return (a != 0 && b != 0) ? alog[(log[a & 0xFF] + log[b & 0xFF]) % 255]
: 0;
}
public CharacterBuffer encode(BufferedBuffer plain) {
CharacterBuffer result = new CharacterBuffer().withBufferLength(plain.length());
byte[] partByte;
for (int p = 0; p < plain.length(); p+=16) {
partByte = encodeBlock(plain);
if(partByte != null) {
for(int pos =0;pos < BLOCK_SIZE; pos++) {
result.with((char)partByte[pos]);
}
}
}
return result;
}
public CharacterBuffer encode(String data) {
CharacterBuffer string = new CharacterBuffer().with(data);
int rest = ((int) data.length() / 32) * 32;
if(rest < data.length()) {
rest = 32 - data.length() + rest;
string.withRepeat(" ", rest);
}
CharacterBuffer result = new CharacterBuffer().withBufferLength(string.length());
byte[] partByte;
ByteBuffer buffer = new ByteBuffer().with(string.array(string.length(), true));
for (int p = 0; p < string.length(); p+=16) {
buffer.withPosition(p);
partByte = encodeBlock(buffer);
for(int pos =0;pos < BLOCK_SIZE; pos++) {
result.with((char)partByte[pos]);
}
}
return result;
}
/**
* AES encrypt 128-bit plaintext using key previously set.
*
*
* 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.
* @param from fromIndex of Array
* @return the encrypted 128-bit ciphertext value.
*/
private byte[] encodeBlock(BufferedBuffer plain) {
byte[] Ker; // encrypt keys for current round
byte[] a = new byte[BLOCK_SIZE];
int i;
Ker = Ke[0];
int from = plain.position();
for (i = 0; i < BLOCK_SIZE; i++) {
a[i] = (byte) (plain.byteAt(i + from) ^ Ker[i]);
}
return encodeBlock(a);
}
/**
* AES encrypt 128-bit plaintext using key previously set.
*
*
* 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[] encodeBlock(byte[] plain) {
// check for bad arguments
if (plain == null || plain.length != BLOCK_SIZE)
return null;
// define working variables
byte[] ta = new byte[BLOCK_SIZE]; // AES temp state variable
byte[] Ker; // encrypt keys for current round
int i, k, row, col;
// copy plaintext bytes into state and do initial AddRoundKey(state)
Ker = Ke[0];
// for each round except last, apply round transforms
for (int r = 1; r < numRounds; r++) {
Ker = Ke[r]; // get session keys for this round
// SubBytes(state) into ta using S-Box S
for (i = 0; i < BLOCK_SIZE; i++) {
int pos = plain[i] & 0xFF;
ta[i] = S[pos];
}
// 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
plain[i] = ta[k];
}
// 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, plain[i]) ^ mul(3, plain[i + 1]) ^ plain[i + 2] ^ plain[i + 3]);
ta[i + 1] = (byte) (plain[i] ^ mul(2, plain[i + 1]) ^ mul(3, plain[i + 2]) ^ plain[i + 3]);
ta[i + 2] = (byte) (plain[i] ^ plain[i + 1] ^ mul(2, plain[i + 2]) ^ mul(3,
plain[i + 3]));
ta[i + 3] = (byte) (mul(3, plain[i]) ^ plain[i + 1] ^ plain[i + 2] ^ mul(2,
plain[i + 3]));
}
// AddRoundKey(state) into a
for (i = 0; i < BLOCK_SIZE; i++)
plain[i] = (byte) (ta[i] ^ Ker[i]);
}
// last round is special - only has SubBytes, ShiftRows and AddRoundKey
Ker = Ke[numRounds]; // get session keys for final round
// SubBytes(state) into a using S-Box S
for (i = 0; i < BLOCK_SIZE; i++) {
int pos = plain[i] & 0xFF;
plain[i] = S[pos];
}
// 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] = plain[k];
}
// AddRoundKey(state) into a
for (i = 0; i < BLOCK_SIZE; i++)
plain[i] = (byte) (ta[i] ^ Ker[i]);
return plain;
}
public CharacterBuffer decode(String data) {
CharacterBuffer string = new CharacterBuffer().with(data);
int rest = ((int) data.length() / 32) * 32;
if(rest < data.length()) {
rest = 32 - data.length() + rest;
string.withRepeat(" ", rest);
}
CharacterBuffer result = new CharacterBuffer().withBufferLength(string.length());
byte[] partByte;
for (int p = 0; p < string.length(); p+=16) {
partByte = decodeBlock(string.toCharArray(), p);
if(partByte != null) {
for(int pos =0;pos < BLOCK_SIZE; pos++) {
result.with((char)partByte[pos]);
}
}
}
return result.trim();
}
public byte[] decodeString(String value) {
return decode(value).array(-2, true);
}
/**
* AES encrypt 128-bit plaintext using key previously set.
*
*
* 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.
* @param from fromIndex of Array
* @return the encrypted 128-bit ciphertext value.
*/
public byte[] decodeBlock(char[] plain, int from) {
byte[] // copy ciphertext bytes into state and do initial AddRoundKey(state)
Kdr = Kd[0];
byte[] a = new byte[BLOCK_SIZE];
int i;
for (i = 0; i < BLOCK_SIZE; i++) {
a[i] = (byte) (plain[i +from] ^ Kdr[i]);
}
return decodeBlock(a);
}
/**
* AES decrypt 128-bit ciphertext using key previously set.
*
*
* 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[] decodeBlock(byte[] cipher) {
// check for bad arguments
if (cipher == null || cipher.length != BLOCK_SIZE)
return null;
// define working variables
byte[] ta = new byte[BLOCK_SIZE]; // AES temp state variable
byte[] Kdr; // encrypt keys for current round
int i, k, row, col;
// for each round except last, apply round transforms
for (int r = 1; r < numRounds; r++) {
Kdr = Kd[r]; // get session keys for this round
// 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] = cipher[k];
}
// InvSubBytes(state) into a using inverse S-box Si
for (i = 0; i < BLOCK_SIZE; i++) {
int pos = ta[i] & 0xFF;
cipher[i] = Si[pos];
}
// AddRoundKey(state) into ta
for (i = 0; i < BLOCK_SIZE; i++)
ta[i] = (byte) (cipher[i] ^ Kdr[i]);
// 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
cipher[i] = (byte) (mul(0x0e, ta[i]) ^ mul(0x0b, ta[i + 1])
^ mul(0x0d, ta[i + 2]) ^ mul(0x09, ta[i + 3]));
cipher[i + 1] = (byte) (mul(0x09, ta[i]) ^ mul(0x0e, ta[i + 1])
^ mul(0x0b, ta[i + 2]) ^ mul(0x0d, ta[i + 3]));
cipher[i + 2] = (byte) (mul(0x0d, ta[i]) ^ mul(0x09, ta[i + 1])
^ mul(0x0e, ta[i + 2]) ^ mul(0x0b, ta[i + 3]));
cipher[i + 3] = (byte) (mul(0x0b, ta[i]) ^ mul(0x0d, ta[i + 1])
^ mul(0x09, ta[i + 2]) ^ mul(0x0e, ta[i + 3]));
}
}
// last round is special - only has InvShiftRows, InvSubBytes and
// AddRoundKey
Kdr = Kd[numRounds]; // get session keys for final round
// 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] = cipher[k];
}
// InvSubBytes(state) into ta using inverse S-box Si
for (i = 0; i < BLOCK_SIZE; i++)
ta[i] = Si[ta[i] & 0xFF];
// AddRoundKey(state) into a
for (i = 0; i < BLOCK_SIZE; i++)
cipher[i] = (byte) (ta[i] ^ Kdr[i]);
return cipher;
}
public AES withKey(String key) {
withKey(converter.decode(key));
return this;
}
/**
* Expand a user-supplied key material into a session key.
*
* See FIPS-197 Section 5.3 Fig 11 for details of the key expansion.
*
* 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.
* @return success
*/
public AES withKey(byte[] key) throws IllegalArgumentException {
// assorted internal constants
final int BC = BLOCK_SIZE / 4;
int Klen;
int i, j, r;
if (key.length == 16 || key.length == 24 || key.length == 32) {
// ok is valid
Klen = key.length;
} else if(key.length>32) {
Klen = 32;
} else {
if(key.length>24) {
Klen = 32;
} else if(key.length>16) {
Klen = 24;
} else {
Klen = 16;
}
byte[] old = key;
key = new byte[Klen];
for(i=0;i 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++;
}
}
return this;
}
}