Source for gnu.javax.crypto.assembly.Cascade

   1: /* Cascade.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 java.math.BigInteger;
  42: import java.security.InvalidKeyException;
  43: import java.util.Collections;
  44: import java.util.HashMap;
  45: import java.util.HashSet;
  46: import java.util.Iterator;
  47: import java.util.LinkedList;
  48: import java.util.Map;
  49: import java.util.Set;
  50: 
  51: /**
  52:  * A <i>Cascade</i> Cipher is the concatenation of two or more block ciphers
  53:  * each with independent keys. Plaintext is input to the first stage; the output
  54:  * of stage <code>i</code> is input to stage <code>i + 1</code>; and the
  55:  * output of the last stage is the <i>Cascade</i>'s ciphertext output.
  56:  * <p>
  57:  * In the simplest case, all stages in a <code>Cascade</code> have <i>k</i>-bit
  58:  * keys, and the stage inputs and outputs are all n-bit quantities. The stage
  59:  * ciphers may differ (general cascade of ciphers), or all be identical (cascade
  60:  * of identical ciphers).
  61:  * <p>
  62:  * The term "block ciphers" used above refers to implementations of
  63:  * {@link gnu.javax.crypto.mode.IMode}, including the
  64:  * {@link gnu.javax.crypto.mode.ECB} mode which basically exposes a
  65:  * symmetric-key block cipher algorithm as a <i>Mode</i> of Operations.
  66:  * <p>
  67:  * References:
  68:  * <ol>
  69:  * <li><a href="http://www.cacr.math.uwaterloo.ca/hac">[HAC]</a>: Handbook of
  70:  * Applied Cryptography.<br>
  71:  * CRC Press, Inc. ISBN 0-8493-8523-7, 1997<br>
  72:  * Menezes, A., van Oorschot, P. and S. Vanstone.</li>
  73:  * </ol>
  74:  */
  75: public class Cascade
  76: {
  77:   public static final String DIRECTION = "gnu.crypto.assembly.cascade.direction";
  78: 
  79:   /** The map of Stages chained in this cascade. */
  80:   protected HashMap stages;
  81: 
  82:   /** The ordered list of Stage UIDs to their attribute maps. */
  83:   protected LinkedList stageKeys;
  84: 
  85:   /** The current operational direction of this instance. */
  86:   protected Direction wired;
  87: 
  88:   /** The curently set block-size for this instance. */
  89:   protected int blockSize;
  90: 
  91:   public Cascade()
  92:   {
  93:     super();
  94: 
  95:     stages = new HashMap(3);
  96:     stageKeys = new LinkedList();
  97:     wired = null;
  98:     blockSize = 0;
  99:   }
 100: 
 101:   /**
 102:    * Returns the Least Common Multiple of two integers.
 103:    *
 104:    * @param a the first integer.
 105:    * @param b the second integer.
 106:    * @return the LCM of <code>abs(a)</code> and <code>abs(b)</code>.
 107:    */
 108:   private static final int lcm(int a, int b)
 109:   {
 110:     BigInteger A = BigInteger.valueOf(a * 1L);
 111:     BigInteger B = BigInteger.valueOf(b * 1L);
 112:     return A.multiply(B).divide(A.gcd(B)).abs().intValue();
 113:   }
 114: 
 115:   /**
 116:    * Adds to the end of the current chain, a designated {@link Stage}.
 117:    *
 118:    * @param stage the {@link Stage} to append to the chain.
 119:    * @return a unique identifier for this stage, within this cascade.
 120:    * @throws IllegalStateException if the instance is already initialised.
 121:    * @throws IllegalArgumentException if the designated stage is already in the
 122:    *           chain, or it has incompatible characteristics with the current
 123:    *           elements already in the chain.
 124:    */
 125:   public Object append(Stage stage) throws IllegalArgumentException
 126:   {
 127:     return insert(size(), stage);
 128:   }
 129: 
 130:   /**
 131:    * Adds to the begining of the current chain, a designated {@link Stage}.
 132:    *
 133:    * @param stage the {@link Stage} to prepend to the chain.
 134:    * @return a unique identifier for this stage, within this cascade.
 135:    * @throws IllegalStateException if the instance is already initialised.
 136:    * @throws IllegalArgumentException if the designated stage is already in the
 137:    *           chain, or it has incompatible characteristics with the current
 138:    *           elements already in the chain.
 139:    */
 140:   public Object prepend(Stage stage) throws IllegalArgumentException
 141:   {
 142:     return insert(0, stage);
 143:   }
 144: 
 145:   /**
 146:    * Inserts a {@link Stage} into the current chain, at the specified index
 147:    * (zero-based) position.
 148:    *
 149:    * @param stage the {@link Stage} to insert into the chain.
 150:    * @return a unique identifier for this stage, within this cascade.
 151:    * @throws IllegalArgumentException if the designated stage is already in the
 152:    *           chain, or it has incompatible characteristics with the current
 153:    *           elements already in the chain.
 154:    * @throws IllegalStateException if the instance is already initialised.
 155:    * @throws IndexOutOfBoundsException if <code>index</code> is less than
 156:    *           <code>0</code> or greater than the current size of this
 157:    *           cascade.
 158:    */
 159:   public Object insert(int index, Stage stage) throws IllegalArgumentException,
 160:       IndexOutOfBoundsException
 161:   {
 162:     if (stages.containsValue(stage))
 163:       throw new IllegalArgumentException();
 164:     if (wired != null || stage == null)
 165:       throw new IllegalStateException();
 166:     if (index < 0 || index > size())
 167:       throw new IndexOutOfBoundsException();
 168:     // check that there is a non-empty set of common block-sizes
 169:     Set set = stage.blockSizes();
 170:     if (stages.isEmpty())
 171:       {
 172:         if (set.isEmpty())
 173:           throw new IllegalArgumentException("1st stage with no block sizes");
 174:       }
 175:     else
 176:       {
 177:         Set common = this.blockSizes();
 178:         common.retainAll(set);
 179:         if (common.isEmpty())
 180:           throw new IllegalArgumentException("no common block sizes found");
 181:       }
 182:     Object result = new Object();
 183:     stageKeys.add(index, result);
 184:     stages.put(result, stage);
 185:     return result;
 186:   }
 187: 
 188:   /**
 189:    * Returns the current number of stages in this chain.
 190:    *
 191:    * @return the current count of stages in this chain.
 192:    */
 193:   public int size()
 194:   {
 195:     return stages.size();
 196:   }
 197: 
 198:   /**
 199:    * Returns an {@link Iterator} over the stages contained in this instance.
 200:    * Each element of this iterator is a concrete implementation of a {@link
 201:    * Stage}.
 202:    *
 203:    * @return an {@link Iterator} over the stages contained in this instance.
 204:    *         Each element of the returned iterator is a concrete instance of a
 205:    *         {@link Stage}.
 206:    */
 207:   public Iterator stages()
 208:   {
 209:     LinkedList result = new LinkedList();
 210:     for (Iterator it = stageKeys.listIterator(); it.hasNext();)
 211:       result.addLast(stages.get(it.next()));
 212:     return result.listIterator();
 213:   }
 214: 
 215:   /**
 216:    * Returns the {@link Set} of supported block sizes for this
 217:    * <code>Cascade</code> that are common to all of its chained stages. Each
 218:    * element in the returned {@link Set} is an instance of {@link Integer}.
 219:    *
 220:    * @return a {@link Set} of supported block sizes common to all the stages of
 221:    *         the chain.
 222:    */
 223:   public Set blockSizes()
 224:   {
 225:     HashSet result = null;
 226:     for (Iterator it = stages.values().iterator(); it.hasNext();)
 227:       {
 228:         Stage aStage = (Stage) it.next();
 229:         if (result == null) // first time
 230:           result = new HashSet(aStage.blockSizes());
 231:         else
 232:           result.retainAll(aStage.blockSizes());
 233:       }
 234:     return result == null ? Collections.EMPTY_SET : result;
 235:   }
 236: 
 237:   /**
 238:    * Initialises the chain for operation with specific characteristics.
 239:    *
 240:    * @param attributes a set of name-value pairs that describes the desired
 241:    *          future behaviour of this instance.
 242:    * @throws IllegalStateException if the chain, or any of its stages, is
 243:    *           already initialised.
 244:    * @throws InvalidKeyException if the intialisation data provided with the
 245:    *           stage is incorrect or causes an invalid key to be generated.
 246:    * @see Direction#FORWARD
 247:    * @see Direction#REVERSED
 248:    */
 249:   public void init(Map attributes) throws InvalidKeyException
 250:   {
 251:     if (wired != null)
 252:       throw new IllegalStateException();
 253:     Direction flow = (Direction) attributes.get(DIRECTION);
 254:     if (flow == null)
 255:       flow = Direction.FORWARD;
 256:     int optimalSize = 0;
 257:     for (Iterator it = stageKeys.listIterator(); it.hasNext();)
 258:       {
 259:         Object id = it.next();
 260:         Map attr = (Map) attributes.get(id);
 261:         attr.put(Stage.DIRECTION, flow);
 262:         Stage stage = (Stage) stages.get(id);
 263:         stage.init(attr);
 264:         optimalSize = optimalSize == 0 ? stage.currentBlockSize()
 265:                                        : lcm(optimalSize,
 266:                                              stage.currentBlockSize());
 267:       }
 268:     if (flow == Direction.REVERSED) // reverse order
 269:       Collections.reverse(stageKeys);
 270:     wired = flow;
 271:     blockSize = optimalSize;
 272:   }
 273: 
 274:   /**
 275:    * Returns the currently set block size for the chain.
 276:    *
 277:    * @return the current block size for the chain.
 278:    * @throws IllegalStateException if the instance is not initialised.
 279:    */
 280:   public int currentBlockSize()
 281:   {
 282:     if (wired == null)
 283:       throw new IllegalStateException();
 284:     return blockSize;
 285:   }
 286: 
 287:   /**
 288:    * Resets the chain for re-initialisation and use with other characteristics.
 289:    * This method always succeeds.
 290:    */
 291:   public void reset()
 292:   {
 293:     for (Iterator it = stageKeys.listIterator(); it.hasNext();)
 294:       ((Stage) stages.get(it.next())).reset();
 295:     if (wired == Direction.REVERSED) // reverse it back
 296:       Collections.reverse(stageKeys);
 297:     wired = null;
 298:     blockSize = 0;
 299:   }
 300: 
 301:   /**
 302:    * Processes exactly one block of <i>plaintext</i> (if initialised in the
 303:    * {@link Direction#FORWARD} state) or <i>ciphertext</i> (if initialised in
 304:    * the {@link Direction#REVERSED} state).
 305:    *
 306:    * @param in the plaintext.
 307:    * @param inOffset index of <code>in</code> from which to start considering
 308:    *          data.
 309:    * @param out the ciphertext.
 310:    * @param outOffset index of <code>out</code> from which to store result.
 311:    * @throws IllegalStateException if the instance is not initialised.
 312:    */
 313:   public void update(byte[] in, int inOffset, byte[] out, int outOffset)
 314:   {
 315:     if (wired == null)
 316:       throw new IllegalStateException();
 317:     int stageBlockSize, j, i = stages.size();
 318:     for (Iterator it = stageKeys.listIterator(); it.hasNext();)
 319:       {
 320:         Stage stage = (Stage) stages.get(it.next());
 321:         stageBlockSize = stage.currentBlockSize();
 322:         for (j = 0; j < blockSize; j += stageBlockSize)
 323:           stage.update(in, inOffset + j, out, outOffset + j);
 324:         i--;
 325:         if (i > 0)
 326:           System.arraycopy(out, outOffset, in, inOffset, blockSize);
 327:       }
 328:   }
 329: 
 330:   /**
 331:    * Conducts a simple <i>correctness</i> test that consists of basic symmetric
 332:    * encryption / decryption test(s) for all supported block and key sizes of
 333:    * underlying block cipher(s) wrapped by Mode leafs. The test also includes
 334:    * one (1) variable key Known Answer Test (KAT) for each block cipher.
 335:    *
 336:    * @return <code>true</code> if the implementation passes simple
 337:    *         <i>correctness</i> tests. Returns <code>false</code> otherwise.
 338:    */
 339:   public boolean selfTest()
 340:   {
 341:     for (Iterator it = stageKeys.listIterator(); it.hasNext();)
 342:       {
 343:         if (! ((Stage) stages.get(it.next())).selfTest())
 344:           return false;
 345:       }
 346:     return true;
 347:   }
 348: }