Source for gnu.javax.crypto.assembly.Transformer

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