|
BIAES |
|
/*
* BIAES - Tim Tyler 2001.
*
* BIAES - a bijective version of AES (Rijndael).
*
*/
/*
* ToDo
* ====
*
* Add compression algorithm...
*
* Consider chaining mode to prevent attacker getting plaintext/cyphertext pairs...?
* Option to add random padding - designed to obscure the length of messages...
*
* Make an API that descends from the "Cipher" class...
* Make generic padding wrapper - to allow use of the bijective padding scheme with other cyphers...
* A better way to avoid null file?
* Use some particular package...
*
*
* Done
* ====
* Make thread-safe...
* Seed RNG with time...
* IV -> simply "add" constant (based on IV) to key...
* Change key at regular intervals...
* NO IV -> simply "add" constant to key...
* IV -> simply "add" constant (based on IV) to key...
* If using IV, use "John Savard" padding, using secure RNG...
* Secure RNG...
* Key size configurable...
* Hex keys with no IV...???
* Hash IV with key...
* Clean up code...
* Java 1.1 note
* Javadoc...
* Web pages...
* Documentation...
* Command line FrEnd...
* Programmer's API
* Padding currently pads files with > 128-bit keys to the nearest 256-bit block boundary (not maximally efficient)...
* Whitening...
* Hex key...
* Different key sizes -> different block sizes...
* Use RNG to make IV...
* Test with null file...
* Add and subtract one to deal with null file...
* Key...
* Hash key...
* Use CBC
* Allow for IV to be set to zero and not transmitted...
*
*/
import java.awt.*;
import java.applet.*;
import java.awt.event.*;
import java.net.URL;
import java.io.*;
import java.util.Random;
import java.security.SecureRandom;
import java.math.BigInteger;
/**
* BIAES - a bijective version of AES (Rijndael).<p>
*
* This program uses bijective padding in conjunction with AES (Rijndael).<p>
* It uses either a hashed keyphrase - or 128, 192, or 256-bit keys.<p>
*
* This code has been placed in the public domain.<p>
* You can do what you like with it.<p>
* Note that this code comes with no warranty.<p>
*
* Note that the "Rijndael_Algorithm" file has additional copyright notices.<p>
*
* @author Tim Tyler tim@tt1.org
*/
public class BIAES {
// PRNG code deliberately not thread-safe - any lack of determinism from multiple thread access is welcomed...
static int byte_c = 0;
static int byte_v = 0;
static SecureRandom srng;
int granularity; // in bytes...
TerminatedFile file = new TerminatedFile();
Random rnd = new Random();
byte[] output_array;
final int RANDOM = 0;
final int BIJECTIVE = 1;
final int pad_size = 96; // in bytes...
// ENCRYPT
/**
* Encrypt data using Rijndael in CBC mode, with padding...
*
*/
public byte[] encrypt(byte[] input_array, String key_phrase, boolean iv) {
file.length = input_array.length;
file.data = new byte[file.length + pad_size];
System.arraycopy(input_array, 0, file.data, 0, file.length);
file = encrypt(file, key_phrase, iv);
byte[] temp_array;
temp_array = new byte[file.length];
System.arraycopy(file.data, 0, temp_array, 0, file.length);
return temp_array;
}
/**
* Encrypt data from input_file and store the result in output_file using Rijndael in CBC mode, with padding...
*
*/
public void encrypt(String input_file, String output_file, String key_phrase, boolean iv) {
int i;
int iv_size = 16;
byte[] temp_array;
try {
File src = new File(input_file);
FileInputStream in = new FileInputStream(input_file);
ByteArrayOutputStream bytes;
bytes = new ByteArrayOutputStream();
int _array_size = 1024; // choose a size...
byte[] _array = new byte[_array_size];
int rb;
while ((rb = in.read(_array, 0, _array_size)) > -1) {
bytes.write(_array, 0, rb);
}
// pad with <pad_size> 00 bytes...
for (i = 0; i < pad_size; i++) {
bytes.write(new byte[] {0}, 0, 1);
}
bytes.close();
in.close();
file.data = bytes.toByteArray();
file.length = file.data.length - pad_size;
}
catch (Exception e) {
Log.log("Error while getting file for encryption:");
e.printStackTrace(Log.getPrintStream());
}
file = encrypt(file, key_phrase, iv);
temp_array = new byte[file.length];
System.arraycopy(file.data, 0, temp_array, 0, file.length);
try {
FileOutputStream fos = new FileOutputStream(output_file);
fos.write(temp_array);
fos.close();
}
catch (Exception e) {
Log.log("Error while outputting encrypted file:");
e.printStackTrace(Log.getPrintStream());
}
}
TerminatedFile encrypt(TerminatedFile input_file, String key_phrase, boolean iv) {
int i;
int iv_size = 16;
Object key = null;
Block initial_value;
int key_size;
int padding = iv ? RANDOM : BIJECTIVE;
int block_count;
KeyManager km = new KeyManager();
initial_value = InitialValue.generateIV(iv);
km.getHexKey(key_phrase);
key_size = km.getKeySize();
if (iv) {
key_phrase = km.appendByteArrayToString(key_phrase,initial_value.data);
}
byte[] key_byte_array = km.getKeyByteArray(key_phrase);
key = makeKey(key_byte_array);
km.setUpKeyConstants(initial_value);
granularity = km.getBestBlockSize();
if (padding == BIJECTIVE) {
file.setGranularity(granularity);
file.addConstant(1);
file.makePaddableByteFile();
file.addPadding(iv ? iv_size : 0);
file.unmakePaddableBlockFile();
if (granularity > 16) {
if (file.length > 32) {
file.start = 32;
file.makePaddableBlockFile();
file.removePadding();
file.unmakePaddableByteFile();
file.setGranularity(16);
file.makePaddableByteFile();
file.addPadding(iv ? iv_size : 0);
file.unmakePaddableBlockFile();
file.start = 0;
}
}
}
else
{
int nob2p = 16 - (file.length & 15);
if (file.length < 16) {
nob2p += 16;
}
file.addRandomPadding(nob2p);
file.data[file.length - 1] = (byte)((file.data[file.length - 1] & ((nob2p <= 16) ? 0xF0 : 0xE0)) | (nob2p - 1));
}
int new_length = file.length + iv_size; // allow room for IV...
output_array = new byte[new_length + pad_size]; // more space...
// insert the IV...
System.arraycopy(initial_value.data, 0, output_array, 0, initial_value.block_size);
if (!iv) {
file.xorChecksum();
}
byte[] temp_array = new byte[16];
block_count = iv ? 0 : 0;
// encryption loop
for (i = iv_size; i < new_length; i += 16) {
for (int j = 0; j < 16; j++) {
temp_array[j] = (byte)(output_array[i + j - iv_size] ^ file.data[i + j - iv_size]); // chaining operation...
}
temp_array = Rijndael_Algorithm.blockEncrypt(temp_array, 0, key); // Rijndael block encryption...
System.arraycopy(temp_array, 0, output_array, i, 16); // copy a block across...
if ((++block_count & 31) == 31) {
// change key...
key_byte_array = km.key_block.addByteArray(key_byte_array);
key = makeKey(key_byte_array);
}
}
if (iv) { // leave IV in place in the output...
file.data = output_array;
file.length = new_length;
}
else // chop off IV...
{
System.arraycopy(output_array, 16, file.data, 0, new_length - iv_size); // copy a block across...
file.length = new_length - iv_size;
}
if (padding == BIJECTIVE) {
if (granularity > 16) {
if (file.length > 32) {
file.start = file.start = 32 + (iv ? iv_size : 0);
file.makePaddableBlockFile();
file.removePadding();
file.unmakePaddableByteFile();
file.setGranularity(granularity);
file.makePaddableByteFile();
file.addPadding(0);
file.unmakePaddableBlockFile();
}
}
file.start = iv ? iv_size : 0;
// file.setGranularity(granularity); // overkill
file.makePaddableBlockFile();
file.removePadding();
file.unmakePaddableByteFile();
file.subtractConstant(1);
}
return file;
}
// ==========================================================================================
// DECRYPT
/**
* Decrypt data using Rijndael in CBC mode, with padding...
*
*/
public byte[] decrypt(byte[] input_array, String key_phrase, boolean iv) {
byte[] temp_array;
file.length = input_array.length;
file.data = new byte[file.length + pad_size];
System.arraycopy(input_array, 0, file.data, 0, file.length);
file = decrypt(file, key_phrase, iv);
temp_array = new byte[file.length];
System.arraycopy(file.data, 0, temp_array, 0, file.length);
return temp_array;
}
/**
* Decrypt data from input_file and store the result in output_file using Rijndael in CBC mode, with padding...
*
*/
public void decrypt(String input_file, String output_file, String key_phrase, boolean iv) {
int i;
int iv_size = 16;
byte[] temp_array;
try {
File src = new File(input_file);
FileInputStream in = new FileInputStream(input_file);
ByteArrayOutputStream bytes;
bytes = new ByteArrayOutputStream();
int _array_size = 1024; // choose a size...
byte[] _array = new byte[_array_size];
int rb;
while ((rb = in.read(_array, 0, _array_size)) > -1) {
bytes.write(_array, 0, rb);
}
// pad with <pad_size> 00 bytes...
for (i = 0; i < pad_size; i++) {
bytes.write(new byte[] {0}, 0, 1);
}
bytes.close();
in.close();
file.data = bytes.toByteArray();
file.length = file.data.length - pad_size;
}
catch (Exception e) {
Log.log("Error while getting file for encryption:");
e.printStackTrace(Log.getPrintStream());
}
file = decrypt(file, key_phrase, iv);
temp_array = new byte[file.length];
System.arraycopy(file.data, 0, temp_array, 0, file.length);
try {
FileOutputStream fos = new FileOutputStream(output_file);
fos.write(temp_array);
fos.close();
}
catch (Exception e) {
Log.log("Error while outputting encrypted file:");
e.printStackTrace(Log.getPrintStream());
}
}
TerminatedFile decrypt(TerminatedFile input_file, String key_phrase, boolean iv) {
int i;
int iv_size = 16;
Object key = null;
Block initial_value;
int key_size;
int padding = iv ? RANDOM : BIJECTIVE;
int block_count;
KeyManager km = new KeyManager();
// insert IV
if (!iv) { // add 00s as IV...
System.arraycopy(file.data, 0, file.data, iv_size, file.length); // move data up...
for (int j = 0; j < iv_size; j++) { // insert 00 00 ... 00 00 IV...
file.data[j] = (byte)(0x00);
}
file.length += iv_size;
}
initial_value = InitialValue.returnIV(file.data);
km.getHexKey(key_phrase);
key_size = km.getKeySize();
if (iv) {
key_phrase = km.appendByteArrayToString(key_phrase,initial_value.data);
}
byte[] key_byte_array = km.getKeyByteArray(key_phrase);
key = makeKey(key_byte_array);
km.setUpKeyConstants(initial_value);
block_count = 0;
granularity = km.getBestBlockSize();
file.start = 16; // after IV...
if (padding == BIJECTIVE) {
file.setGranularity(granularity);
file.addConstant(1);
file.makePaddableByteFile();
file.addPadding(iv ? iv_size : 0);
file.unmakePaddableBlockFile();
if (granularity > 16) {
if (file.length > 48) {
file.start = 48;
file.setGranularity(granularity);
file.makePaddableBlockFile();
file.removePadding();
file.unmakePaddableByteFile();
file.setGranularity(16);
file.makePaddableByteFile();
file.addPadding(0);
file.unmakePaddableBlockFile();
}
}
}
int new_length = file.length - iv_size;
output_array = new byte[new_length + pad_size];
byte[] temp_array;
for (i = 0; i < new_length; i += 16) {
temp_array = Rijndael_Algorithm.blockDecrypt(file.data, i + iv_size, key);
for (int j = 0; j < 16; j++) {
output_array[i + j] = (byte)(temp_array[j] ^ file.data[i + j]);
}
if ((++block_count & 31) == 31) {
// change key...
key_byte_array = km.key_block.addByteArray(key_byte_array);
key = makeKey(key_byte_array);
}
}
file.data = output_array;
file.length = new_length;
file.start = 0;
if (!iv) {
file.xorChecksum();
}
if (padding == BIJECTIVE) {
if (granularity > 16) {
if (file.length > 32) {
file.start = 32;
file.setGranularity(16);
file.makePaddableBlockFile();
file.removePadding();
file.unmakePaddableByteFile();
file.setGranularity(granularity);
file.makePaddableByteFile();
file.addPadding(0); // was 0...
file.unmakePaddableBlockFile();
}
}
file.start = 0;
// file.setGranularity(granularity); // overkill...
file.makePaddableBlockFile();
file.removePadding();
file.unmakePaddableByteFile();
file.subtractConstant(1);
}
else
{
int strip_len;
strip_len = file.data[file.length - 1] & ((file.length <= 32) ? 0x1F : 0x0F);
file.length -= (strip_len + 1);
}
return file;
}
Object makeKey(byte[] key_byte_array) {
Object key = null;
try {
key = Rijndael_Algorithm.makeKey(key_byte_array);
}
catch (Exception e) {
// can never happen...
}
return key;
}
static byte getRandomByte() {
if (srng == null) {
// seed based on current time - done this way for speed :-|
long l = System.currentTimeMillis();
byte[] ba = new byte[8];
for (int i = 0; i < 8; i++) {
ba[i] = (byte)l;
l = l >> 8;
}
srng = new SecureRandom(ba);
// srng = new SecureRandom(); // this would be the more secure (but slow) way...
}
if ((byte_c) != 0) {
byte_c -= 1;
byte_v = byte_v >>> 8;
return (byte)byte_v;
}
else
{
byte_c = 3;
byte_v = srng.nextInt();
return (byte)byte_v;
}
}
void runCLI(String args[]) {
boolean allowed = true;
allowed = !(args.length == 0);
if (allowed) {
allowed = !("-?".equals(args[0]));
if (allowed) {
if (args.length < 2) {
Log.log("Not enough arguments");
allowed = false;
}
else
{
if (args.length > 5) {
Log.log("Too many arguments");
allowed = false;
}
}
if (allowed) {
int i = 0;
boolean iv = false;
boolean encrypt = false;
encrypt = ("-e".equals(args[i]));
if (encrypt) {
i++;
}
iv = ("-iv".equals(args[i]));
if (iv) {
i++;
}
if (args.length - i == 3) {
String f_in = args[i++];
String f_out = args[i++];
String key = args[i++];
if (encrypt) {
encrypt(f_in, f_out, key, iv);
}
else
{
decrypt(f_in, f_out, key, iv);
}
}
else
{
Log.log("Incorrect arguments.");
}
}
}
}
if (!allowed) {
Log.log("BIAES");
Log.log("=====");
Log.log("BIAES - a desktop encryption program using Rijndael (AES).");
Log.log("Syntax: BIAES [-e] [-iv] <infile> <outfile> <key>");
Log.log("Flags: -e - encrypt (default is decryption);");
Log.log(" -iv - use random initial value;");
Log.log("Any fields with spaces in should be enclosed in quotation marks.");
Log.log("This is version 1.0 of BIAES.");
}
}
public static void main(String args[]) {
BIAES biaes = new BIAES();
biaes.runCLI(args);
}
}
|
BIAES |
|