Frames | No Frames |
1: /* Transformer.java -- 2: Copyright (C) 2003, 2006 Free Software Foundation, Inc. 3: 4: This file is a part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2 of the License, or (at 9: your option) any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; if not, write to the Free Software 18: Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 19: USA 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package gnu.javax.crypto.assembly; 40: 41: import gnu.javax.crypto.pad.IPad; 42: 43: import java.io.ByteArrayOutputStream; 44: import java.util.Map; 45: 46: /** 47: * A <code>Transformer</code> is an abstract representation of a two-way 48: * <i>transformation</i> that can be chained together with other instances of 49: * this type. Examples of such transformations in this library are: 50: * {@link Cascade} cipher, {@link gnu.javax.crypto.pad.IPad} algorithm, and a 51: * ZLib-based deflater/inflater algorithm. A special implementation of a 52: * <code>Transformer</code> to close a chain is also provided. 53: * <p> 54: * A <code>Transformer</code> is characterised by the followings: 55: * <ul> 56: * <li>It can be chained to other instances, to form an {@link Assembly}.</li> 57: * <li>When configured in an {@link Assembly}, it can be set to apply its 58: * internal transformation on the input data stream before (pre-processing) or 59: * after (post-processing) passing the input data to the next element in the 60: * chain. Note that the same type <code>Transformer</code> can be used as 61: * either in pre-processing or a post-processing modes.</li> 62: * <li>A special transformer --<code>LoopbackTransformer</code>-- is used 63: * to close the chain.</li> 64: * <li>A useful type of <code>Transformer</code> --one we're interested in-- 65: * has internal buffers. The distinction between a casual push (update) 66: * operation and the last one allows to correctly flush any intermediate bytes 67: * that may exist in those buffers.</li> 68: * </ul> 69: * <p> 70: * To allow wiring <code>Transformer</code> instances together, a 71: * <i>minimal-output-size</i> in bytes is necessary. The trivial case of a 72: * value of <code>1</code> for such attribute practically means that no output 73: * buffering, from the previous element, is needed --which is independant of 74: * buffering the input if the <code>Transformer</code> implementation itself 75: * is block-based. 76: * 77: * @see CascadeTransformer 78: * @see PaddingTransformer 79: * @see DeflateTransformer 80: */ 81: public abstract class Transformer 82: { 83: public static final String DIRECTION = "gnu.crypto.assembly.transformer.direction"; 84: 85: protected Direction wired; 86: 87: protected Operation mode; 88: 89: protected Transformer tail = null; 90: 91: protected ByteArrayOutputStream inBuffer = new ByteArrayOutputStream(2048); 92: 93: protected ByteArrayOutputStream outBuffer = new ByteArrayOutputStream(2048); 94: 95: /** Trivial protected constructor. */ 96: protected Transformer() 97: { 98: super(); 99: 100: this.wired = null; 101: } 102: 103: public static final Transformer getCascadeTransformer(Cascade cascade) 104: { 105: return new CascadeTransformer(cascade); 106: } 107: 108: public static final Transformer getPaddingTransformer(IPad padding) 109: { 110: return new PaddingTransformer(padding); 111: } 112: 113: public static final Transformer getDeflateTransformer() 114: { 115: return new DeflateTransformer(); 116: } 117: 118: /** 119: * Sets the operational mode of this <code>Transformer</code>. 120: * 121: * @param mode the processing mode this <code>Transformer</code> is required 122: * to operate in. 123: * @throws IllegalStateException if this instance has already been assigned an 124: * operational mode. 125: */ 126: public void setMode(final Operation mode) 127: { 128: if (this.mode != null) 129: throw new IllegalStateException(); 130: this.mode = mode; 131: } 132: 133: /** 134: * Returns <code>true</code> if this <code>Transformer</code> was wired in 135: * pre-processing mode; <code>false</code> otherwise. 136: * 137: * @return <code>true</code> if this <code>Transformer</code> has been 138: * wired in pre-processing mode; <code>false</code> otherwise. 139: * @throws IllegalStateException if this instance has not yet been assigned an 140: * operational <i>type</i>. 141: */ 142: public boolean isPreProcessing() 143: { 144: if (mode == null) 145: throw new IllegalStateException(); 146: return (mode == Operation.PRE_PROCESSING); 147: } 148: 149: /** 150: * Returns <code>true</code> if this <code>Transformer</code> was wired in 151: * post-processing mode; <code>false</code> otherwise. 152: * 153: * @return <code>true</code> if this <code>Transformer</code> has been 154: * wired in post-processing mode; <code>false</code> otherwise. 155: * @throws IllegalStateException if this instance has not yet been assigned an 156: * operational <i>type</i>. 157: */ 158: public boolean isPostProcessing() 159: { 160: return ! isPreProcessing(); 161: } 162: 163: /** 164: * Initialises the <code>Transformer</code> for operation with specific 165: * characteristics. 166: * 167: * @param attributes a set of name-value pairs that describes the desired 168: * future behaviour of this instance. 169: * @throws IllegalStateException if the instance is already initialised. 170: */ 171: public void init(Map attributes) throws TransformerException 172: { 173: if (wired != null) 174: throw new IllegalStateException(); 175: Direction flow = (Direction) attributes.get(DIRECTION); 176: if (flow == null) 177: flow = Direction.FORWARD; 178: wired = flow; 179: inBuffer.reset(); 180: outBuffer.reset(); 181: tail.init(attributes); // initialise tail first 182: initDelegate(attributes); // initialise this instance 183: } 184: 185: /** 186: * Returns the block-size of this <code>Transformer</code>. A value of 187: * <code>1</code> indicates that this instance is block-agnostic. 188: * 189: * @return the current minimal required block size. 190: */ 191: public int currentBlockSize() 192: { 193: if (wired == null) 194: throw new IllegalStateException(); 195: return delegateBlockSize(); 196: } 197: 198: /** 199: * Resets the <code>Transformer</code> for re-initialisation and use with 200: * other characteristics. This method always succeeds. 201: */ 202: public void reset() 203: { 204: resetDelegate(); 205: wired = null; 206: inBuffer.reset(); 207: outBuffer.reset(); 208: tail.reset(); // reset tail last 209: } 210: 211: /** 212: * Convenience method that calls the method with same name and three 213: * arguments, using a byte array of length <code>1</code> whose contents are 214: * the designated byte. 215: * 216: * @param b the byte to process. 217: * @return the result of transformation. 218: * @throws IllegalStateException if the instance is not initialised. 219: * @throws TransformerException if a transformation-related exception occurs 220: * during the operation. 221: * @see #update(byte[], int, int) 222: */ 223: public byte[] update(byte b) throws TransformerException 224: { 225: return update(new byte[] { b }, 0, 1); 226: } 227: 228: /** 229: * Convenience method that calls the same method with three arguments. All 230: * bytes in <code>in</code>, starting from index position <code>0</code> 231: * are considered. 232: * 233: * @param in the input data bytes. 234: * @return the result of transformation. 235: * @throws IllegalStateException if the instance is not initialised. 236: * @throws TransformerException if a transformation-related exception occurs 237: * during the operation. 238: * @see #update(byte[], int, int) 239: */ 240: public byte[] update(byte[] in) throws TransformerException 241: { 242: return update(in, 0, in.length); 243: } 244: 245: /** 246: * Processes a designated number of bytes from a given byte array. 247: * 248: * @param in the input data bytes. 249: * @param offset index of <code>in</code> from which to start considering 250: * data. 251: * @param length the count of bytes to process. 252: * @return the result of transformation. 253: * @throws IllegalStateException if the instance is not initialised. 254: * @throws TransformerException if a transformation-related exception occurs 255: * during the operation. 256: */ 257: public byte[] update(byte[] in, int offset, int length) 258: throws TransformerException 259: { 260: if (wired == null) 261: throw new IllegalStateException(); 262: byte[] result = (wired == Direction.FORWARD ? forwardUpdate(in, offset, length) 263: : inverseUpdate(in, offset, length)); 264: return result; 265: } 266: 267: /** 268: * Convenience method that calls the same method with three arguments. A 269: * zero-long byte array is used. 270: * 271: * @return the result of transformation. 272: * @throws IllegalStateException if the instance is not initialised. 273: * @throws TransformerException if a transformation-related exception occurs 274: * during the operation. 275: * @see #lastUpdate(byte[], int, int) 276: */ 277: public byte[] lastUpdate() throws TransformerException 278: { 279: byte[] result = (wired == Direction.FORWARD ? lastForwardUpdate() 280: : lastInverseUpdate()); 281: if (inBuffer.size() != 0) // we still have some buffered bytes 282: throw new TransformerException("lastUpdate(): input buffer not empty"); 283: return result; 284: } 285: 286: /** 287: * Convenience method that calls the method with same name and three 288: * arguments, using a byte array of length <code>1</code> whose contents are 289: * the designated byte. 290: * 291: * @param b the byte to process. 292: * @return the result of transformation. 293: * @throws IllegalStateException if the instance is not initialised. 294: * @throws TransformerException if a transformation-related exception occurs 295: * during the operation. 296: * @see #lastUpdate(byte[], int, int) 297: */ 298: public byte[] lastUpdate(byte b) throws TransformerException 299: { 300: return lastUpdate(new byte[] { b }, 0, 1); 301: } 302: 303: /** 304: * Convenience method that calls the same method with three arguments. All 305: * bytes in <code>in</code>, starting from index position <code>0</code> 306: * are considered. 307: * 308: * @param in the input data bytes. 309: * @return the result of transformation. 310: * @throws IllegalStateException if the instance is not initialised. 311: * @throws TransformerException if a transformation-related exception occurs 312: * during the operation. 313: * @see #lastUpdate(byte[], int, int) 314: */ 315: public byte[] lastUpdate(byte[] in) throws TransformerException 316: { 317: return lastUpdate(in, 0, in.length); 318: } 319: 320: /** 321: * Processes a designated number of bytes from a given byte array and signals, 322: * at the same time, that this is the last <i>push</i> operation on this 323: * <code>Transformer</code>. 324: * 325: * @param in the input data bytes. 326: * @param offset index of <code>in</code> from which to start considering 327: * data. 328: * @param length the count of bytes to process. 329: * @return the result of transformation. 330: * @throws IllegalStateException if the instance is not initialised. 331: * @throws TransformerException if a transformation-related exception occurs 332: * during the operation. 333: */ 334: public byte[] lastUpdate(byte[] in, int offset, int length) 335: throws TransformerException 336: { 337: byte[] result = update(in, offset, length); 338: byte[] rest = lastUpdate(); 339: if (rest.length > 0) 340: { 341: byte[] newResult = new byte[result.length + rest.length]; 342: System.arraycopy(result, 0, newResult, 0, result.length); 343: System.arraycopy(rest, 0, newResult, result.length, rest.length); 344: result = newResult; 345: } 346: return result; 347: } 348: 349: private byte[] forwardUpdate(byte[] in, int off, int len) 350: throws TransformerException 351: { 352: return (isPreProcessing() ? preTransform(in, off, len) 353: : postTransform(in, off, len)); 354: } 355: 356: private byte[] inverseUpdate(byte[] in, int off, int len) 357: throws TransformerException 358: { 359: return (isPreProcessing() ? postTransform(in, off, len) 360: : preTransform(in, off, len)); 361: } 362: 363: private byte[] preTransform(byte[] in, int off, int len) 364: throws TransformerException 365: { 366: byte[] result = updateDelegate(in, off, len); 367: result = tail.update(result); 368: return result; 369: } 370: 371: private byte[] postTransform(byte[] in, int off, int len) 372: throws TransformerException 373: { 374: byte[] result = tail.update(in, off, len); 375: result = updateDelegate(result, 0, result.length); 376: return result; 377: } 378: 379: private byte[] lastForwardUpdate() throws TransformerException 380: { 381: return (isPreProcessing() ? preLastTransform() : postLastTransform()); 382: } 383: 384: private byte[] lastInverseUpdate() throws TransformerException 385: { 386: return (isPreProcessing() ? postLastTransform() : preLastTransform()); 387: } 388: 389: private byte[] preLastTransform() throws TransformerException 390: { 391: byte[] result = lastUpdateDelegate(); 392: result = tail.lastUpdate(result); 393: return result; 394: } 395: 396: private byte[] postLastTransform() throws TransformerException 397: { 398: byte[] result = tail.lastUpdate(); 399: result = updateDelegate(result, 0, result.length); 400: byte[] rest = lastUpdateDelegate(); 401: if (rest.length > 0) 402: { 403: byte[] newResult = new byte[result.length + rest.length]; 404: System.arraycopy(result, 0, newResult, 0, result.length); 405: System.arraycopy(rest, 0, newResult, result.length, rest.length); 406: result = newResult; 407: } 408: return result; 409: } 410: 411: abstract void initDelegate(Map attributes) throws TransformerException; 412: 413: abstract int delegateBlockSize(); 414: 415: abstract void resetDelegate(); 416: 417: abstract byte[] updateDelegate(byte[] in, int off, int len) 418: throws TransformerException; 419: 420: abstract byte[] lastUpdateDelegate() throws TransformerException; 421: }