/* KeyLoader.java             nov 2000
 *
 * Erwan Lemonnier & Eric Nordenstam
 *
 * IO interface to read/write clear and encrypted data
 * to and from files.
 ************************************************************/
 
import java.math.BigInteger;
import java.io.*;

/**
 * DataLoader is an IO interface used by RSA to read
 * and write clear and encrypted data. Its default is
 * that it is not bufferised, thus each call to a read
 * method results in a disk access. As well for writes.
 * <p>
 * It was necessary to define a format for blocks of
 * data. In this implementation, clear data is read by
 * blocks of at most 126 bytes, and inserted in a 128 bytes
 * large integer whose first byte is the length of the block
 * of clear data read. This integer is then encrypted using rsa,
 * and can be written to and read from a file. When decrypted 
 * and written as clear data to a file, only the number of bytes
 * specified in the first bytes is indeed written to the file.
 * <p>
 * @author E.Lemonnier & E.Nordenstam
 */

public class DataLoader {
	
	private static FileInputStream in;
	private static FileOutputStream out;
	
	//size of key in bits
	private static int SIZE_KEY = RSA.SIZE_KEY;
	
	//size of clear data block, in bytes
	private static int SIZE_CLEAR_BLOCK = (2*SIZE_KEY-16)/8;
	
	//size of crypted data block, in bytes
	private static int SIZE_CRYPTED_BLOCK = (2*SIZE_KEY)/8;
	
	/**
	 * Create a DataLoader defined by an file from which to read
	 * and a file in which to write.
	 * <p>
	 * @param filein a file from which to read.
	 * @param fileout a file in which to write.
	 */
	public DataLoader(String filein, String fileout) throws Exception {
		in = new FileInputStream(filein);
		out = new FileOutputStream(fileout);
	}
	
	/**
	 * Close the streams manipulated by a DataLoader.
	 */
	public void close() {
		try {
			in.close();
			out.flush();
			out.close();
		} catch(Exception e) {}
	}
	
	/**
	 * Read a block of data from a clear file.
	 * <p>
	 * @return the block of data as a big integer of 1024 bits.
	 */
	public BigInteger readClearBlock() throws IOException {
		//one more byte to store the number of read bytes
		byte[] b = new byte[SIZE_CLEAR_BLOCK+1];	
		int l = in.read(b, 1, SIZE_CLEAR_BLOCK);
		if (l == -1)
			throw new IOException();
		//set the most significant byte to the number of bytes read
		b[0] = (byte)l;			
		return new BigInteger(b);
	}
	
	/**
	 * Read a block of encrypted data from an encrypted file.
	 * <p>
	 * @return the block of data as a big integer of 1024 bits.
	 */
	public BigInteger readEncryptedBlock() throws IOException {
		byte[] b = new byte[SIZE_CRYPTED_BLOCK];
		if (in.read(b) == -1)
			throw new IOException();
		return new BigInteger(b);
	}

	/**
	 * Write a block of clear data to a file.
	 * <p>
	 * @param b clear data encapsulated in a 128 bytes integer.
	 */
	public void writeClearBlock(BigInteger b) throws IOException {
		byte[] bytes = b.toByteArray();
		out.write(bytes, 1, (int)bytes[0]);
	}
	
	/**
	 * Write a block of encrypted data to a file.
	 * <p>
	 * @param a 1024 bits integer representing encrypted data.
	 */
	public void writeEncryptedBlock(BigInteger b) throws IOException {
		byte[] bytes = b.toByteArray();
		out.write(bytes);
		if (bytes.length<SIZE_CRYPTED_BLOCK) {
			for (int i=bytes.length; i<SIZE_CRYPTED_BLOCK; i++) {
				out.write(0);
			}
		}
	}
} 
	
