|
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 |
|