package gnu.crypto.assembly; // ---------------------------------------------------------------------------- // $Id: $ // // Copyright (C) 2003, Free Software Foundation, Inc. // // This file is part of GNU Crypto. // // GNU Crypto is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2, or (at your option) // any later version. // // GNU Crypto is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; see the file COPYING. If not, write to the // // Free Software Foundation Inc., // 59 Temple Place - Suite 330, // Boston, MA 02111-1307 // USA // // Linking this library statically or dynamically with other modules is // making a combined work based on this library. Thus, the terms and // conditions of the GNU General Public License cover the whole // combination. // // As a special exception, the copyright holders of this library give // you permission to link this library with independent modules to // produce an executable, regardless of the license terms of these // independent modules, and to copy and distribute the resulting // executable under terms of your choice, provided that you also meet, // for each linked independent module, the terms and conditions of the // license of that module. An independent module is a module which is // not derived from or based on this library. If you modify this // library, you may extend this exception to your version of the // library, but you are not obligated to do so. If you do not wish to // do so, delete this exception statement from your version. // ---------------------------------------------------------------------------- import gnu.crypto.pad.IPad; import java.io.ByteArrayOutputStream; import java.util.Map; /** *

A Transformer is an abstract representation of a two-way * transformation that can be chained together with other instances of * this type. Examples of such transformations in this library are: * address@hidden Cascade} cipher, address@hidden gnu.crypto.pad.IPad} algorithm, and a * ZLib-based deflater/inflater algorithm. A special implementation of a * Transformer to close a chain is also provided.

* *

A Transformer is characterised by the followings:

*

* *

To allow wiring Transformer instances together, a * minimal-output-size in bytes is necessary. The trivial case of a * value of 1 for such attribute practically means that no output * buffering, from the previous element, is needed --which is independant of * buffering the input if the Transformer implementation itself is * block-based.

* * @see CascadeTransformer * @see PaddingTransformer * @see DeflateTransformer * @version $Revision: $ */ public abstract class Transformer { // Constants and variables // ------------------------------------------------------------------------- public static final String DIRECTION = "gnu.crypto.assembly.transformer.direction"; public static final String MODE = "gnu.crypto.assembly.transformer.mode"; protected Direction wired; protected Operation mode; protected Transformer tail = null; protected ByteArrayOutputStream inBuffer = new ByteArrayOutputStream(2048); protected ByteArrayOutputStream outBuffer = new ByteArrayOutputStream(2048); // Constructor(s) // ------------------------------------------------------------------------- /** Trivial protected constructor. */ protected Transformer() { super(); this.wired = null; } // Class methods // ------------------------------------------------------------------------- public static final Transformer getCascadeTransformer(Cascade cascade) { return new CascadeTransformer(cascade); } public static final Transformer getPaddingTransformer(IPad padding) { return new PaddingTransformer(padding); } public static final Transformer getDeflateTransformer() { return new DeflateTransformer(); } // Instance methods // ------------------------------------------------------------------------- /** * Sets the operational mode of this Transformer. * * @param mode the processing mode this Transformer is required * to operate in. * @throws IllegalStateException if this instance has already been assigned * an operational mode. */ public void setMode(final Operation mode) { if (this.mode != null) { throw new IllegalStateException(); } this.mode = mode; } /** * Returns true if this Transformer was wired in * pre-processing mode; false otherwise. * * @return true if this Transformer has been wired * in pre-processing mode; false otherwise. * @throws IllegalStateException if this instance has not yet been assigned * an operational type. */ public boolean isPreProcessing() { if (mode == null) { throw new IllegalStateException(); } return (mode == Operation.PRE_PROCESSING); } /** * Returns true if this Transformer was wired in * post-processing mode; false otherwise. * * @return true if this Transformer has been wired * in post-processing mode; false otherwise. * @throws IllegalStateException if this instance has not yet been assigned * an operational type. */ public boolean isPostProcessing() { return !isPreProcessing(); } /** * Initialises the Transformer for operation with specific * characteristics. * * @param attributes a set of name-value pairs that describes the desired * future behaviour of this instance. * @exception IllegalStateException if the instance is already initialised. */ public void init(Map attributes) throws TransformerException { if (wired != null) { throw new IllegalStateException(); } Direction flow = (Direction) attributes.get(DIRECTION); if (flow == null) { flow = Direction.FORWARD; } wired = flow; inBuffer.reset(); outBuffer.reset(); tail.init(attributes); // initialise tail first initDelegate(attributes); // initialise this instance } /** * Returns the block-size of this Transformer. A value of * 1 indicates that this instance is block-agnostic. * * @return the current minimal required block size. */ public int currentBlockSize() { if (wired == null) { throw new IllegalStateException(); } return delegateBlockSize(); } /** * Resets the Transformer for re-initialisation and use with * other characteristics. This method always succeeds. */ public void reset() { resetDelegate(); wired = null; inBuffer.reset(); outBuffer.reset(); tail.reset(); // reset tail last } /** * Convenience method that calls the method with same name and three * arguments, using a byte array of length 1 whose contents are * the designated byte. * * @param b the byte to process. * @return the result of transformation. * @throws IllegalStateException if the instance is not initialised. * @throws TransformerException if a transformation-related exception occurs * during the operation. * @see #update(byte[], int, int) */ public byte[] update(byte b) throws TransformerException { return update(new byte[] { b }, 0, 1); } /** * Convenience method that calls the same method with three arguments. All * bytes in in, starting from index position 0 are * considered. * * @param in the input data bytes. * @return the result of transformation. * @throws IllegalStateException if the instance is not initialised. * @throws TransformerException if a transformation-related exception occurs * during the operation. * @see #update(byte[], int, int) */ public byte[] update(byte[] in) throws TransformerException { return update(in, 0, in.length); } /** * Processes a designated number of bytes from a given byte array. * * @param in the input data bytes. * @param offset index of in from which to start considering * data. * @param length the count of bytes to process. * @return the result of transformation. * @throws IllegalStateException if the instance is not initialised. * @throws TransformerException if a transformation-related exception occurs * during the operation. */ public byte[] update(byte[] in, int offset, int length) throws TransformerException { if (wired == null) { throw new IllegalStateException(); } byte[] result = (wired == Direction.FORWARD ? forwardUpdate(in, offset, length) : inverseUpdate(in, offset, length)); return result; } /** * Convenience method that calls the same method with three arguments. A * zero-long byte array is used. * * @return the result of transformation. * @throws IllegalStateException if the instance is not initialised. * @throws TransformerException if a transformation-related exception occurs * during the operation. * @see #lastUpdate(byte[], int, int) */ public byte[] lastUpdate() throws TransformerException { byte[] result = (wired == Direction.FORWARD ? lastForwardUpdate() : lastInverseUpdate()); if (inBuffer.size() != 0) { // we still have some buffered bytes throw new TransformerException("lastUpdate(): input buffer not empty"); } return result; } /** * Convenience method that calls the method with same name and three * arguments, using a byte array of length 1 whose contents are * the designated byte. * * @param b the byte to process. * @return the result of transformation. * @throws IllegalStateException if the instance is not initialised. * @throws TransformerException if a transformation-related exception occurs * during the operation. * @see #lastUpdate(byte[], int, int) */ public byte[] lastUpdate(byte b) throws TransformerException { return lastUpdate(new byte[] { b }, 0, 1); } /** * Convenience method that calls the same method with three arguments. All * bytes in in, starting from index position 0 are * considered. * * @param in the input data bytes. * @return the result of transformation. * @throws IllegalStateException if the instance is not initialised. * @throws TransformerException if a transformation-related exception occurs * during the operation. * @see #lastUpdate(byte[], int, int) */ public byte[] lastUpdate(byte[] in) throws TransformerException { return lastUpdate(in, 0, in.length); } /** * Processes a designated number of bytes from a given byte array and * signals, at the same time, that this is the last push operation on * this Transformer. * * @param in the input data bytes. * @param offset index of in from which to start considering * data. * @param length the count of bytes to process. * @return the result of transformation. * @throws IllegalStateException if the instance is not initialised. * @throws TransformerException if a transformation-related exception occurs * during the operation. */ public byte[] lastUpdate(byte[] in, int offset, int length) throws TransformerException { byte[] result = update(in, offset, length); byte[] rest = lastUpdate(); if (rest.length > 0) { byte[] newResult = new byte[result.length + rest.length]; System.arraycopy(result, 0, newResult, 0, result.length); System.arraycopy(rest, 0, newResult, result.length, rest.length); result = newResult; } return result; } // helper methods ---------------------------------------------------------- private byte[] forwardUpdate(byte[] in, int off, int len) throws TransformerException { return (isPreProcessing() ? preTransform(in, off, len) : postTransform(in, off, len)); } private byte[] inverseUpdate(byte[] in, int off, int len) throws TransformerException { return (isPreProcessing() ? postTransform(in, off, len) : preTransform(in, off, len)); } private byte[] preTransform(byte[] in, int off, int len) throws TransformerException { byte[] result = updateDelegate(in, off, len); result = tail.update(result); return result; } private byte[] postTransform(byte[] in, int off, int len) throws TransformerException { byte[] result = tail.update(in, off, len); result = updateDelegate(result, 0, result.length); return result; } private byte[] lastForwardUpdate() throws TransformerException { return (isPreProcessing() ? preLastTransform() : postLastTransform()); } private byte[] lastInverseUpdate() throws TransformerException { return (isPreProcessing() ? postLastTransform() : preLastTransform()); } private byte[] preLastTransform() throws TransformerException { byte[] result = lastUpdateDelegate(); result = tail.lastUpdate(result); return result; } private byte[] postLastTransform() throws TransformerException { byte[] result = tail.lastUpdate(); result = updateDelegate(result, 0, result.length); byte[] rest = lastUpdateDelegate(); if (rest.length > 0) { byte[] newResult = new byte[result.length + rest.length]; System.arraycopy(result, 0, newResult, 0, result.length); System.arraycopy(rest, 0, newResult, result.length, rest.length); result = newResult; } return result; } // abstract methods to be implemented by concrete subclasses --------------- abstract void initDelegate(Map attributes) throws TransformerException; abstract int delegateBlockSize(); abstract void resetDelegate(); abstract byte[] updateDelegate(byte[] in, int off, int len) throws TransformerException; abstract byte[] lastUpdateDelegate() throws TransformerException; }