Provides a high-level API for combining and using GNU Crypto cipher, mode, and padding primitives.

## Package overview

This package describes two patterns implemneted by the GNU Crypto library that allow users to combine the basic cipher (and other) primitives into higher level components in order to offer more flexible functionalities. These two patterns are: Cascade and Assembly.

The Cascade is a means of assembling block cipher Modes of Operations into an ordered sequence of stages. A stage is a representation of a Mode (of Operations) wired in a designated direction: FORWARD or REVERSED. A Mode staged in the FORWARD direction would encrypt input blocks, producing ciphertext, while the same Mode, wired in the REVERSED direction would do the opposite; i.e. decrypt an input text producing a plaintext.

In the simplest case, all stages in a Cascade have k-bit keys, and the stage inputs and outputs are all n-bit quantities. The stage ciphers may differ (general cascade of ciphers), or all be identical (cascade of identical ciphers).

An Assembly is a construction of an ordered set of Transformer objects. Each Transformer is wired to operate in PRE_PROCESSING or POST_PROCESSING mode --the Transformer's operation. In PRE_PROCESSING, the input is first processed by the Transformer before being passed to the rest of the chain, while in POST_PROCESSING state, the Transformer first passes the input to the rest of the chain and only processes the output of the returned data.

### Cascade pattern

The following diagram shows the important classes participating in the Cascade pattern:

Here is an example of how a Cascade is used to construct a DES-EDE symetric-key block cipher from three independent DES cipher instances:

    HashMap map = new HashMap();
    HashMap map1 = new HashMap();
    HashMap map2 = new HashMap();
    HashMap map3 = new HashMap();

    Cascade new3DES = new Cascade();
    Object des1 = new3DES.append(
        Stage.getInstance(
            ModeFactory.getInstance(Registry.ECB_MODE, new DES(), 8),
            Direction.FORWARD));
    Object des2 = new3DES.append(
        Stage.getInstance(
            ModeFactory.getInstance(Registry.ECB_MODE, new DES(), 8),
            Direction.REVERSED));
    Object des3 = new3DES.append(
        Stage.getInstance(
            ModeFactory.getInstance(Registry.ECB_MODE, new DES(), 8),
            Direction.FORWARD));

    map.put(des1, map1);
    map.put(des2, map2);
    map.put(des3, map3);

    map1.put(IBlockCipher.KEY_MATERIAL, key1material);
    map2.put(IBlockCipher.KEY_MATERIAL, key2material);
    map3.put(IBlockCipher.KEY_MATERIAL, key3material);

    // encryption
    map.put(Cascade.DIRECTION, Direction.FORWARD);
    byte[] pt = ...; // some plaintext to encrypt
    byte[] ct = new byte[pt.length]; // where ciphertext is returned
    try
      {
        new3DES.init(map);
        new3DES.update(pt, 0, ct, 0);
      }
    catch (InvalidKeyException x)
      {
        x.printStackTrace(System.err);
      }

### Assembly pattern

The following diagram shows the important classes participating in the Assembly pattern:

Here is an example of how to compress and encrypt a stream of plaintext:

    import gnu.crypto.Registry;
    import gnu.crypto.util.Util;
    import gnu.crypto.assembly.Assembly;
    import gnu.crypto.assembly.Cascade;
    import gnu.crypto.assembly.Direction;
    import gnu.crypto.assembly.Stage;
    import gnu.crypto.assembly.Transformer;
    import gnu.crypto.assembly.TransformerException;
    import gnu.crypto.cipher.Blowfish;
    import gnu.crypto.cipher.IBlockCipher;
    import gnu.crypto.mode.IMode;
    import gnu.crypto.mode.ModeFactory;
    import gnu.crypto.pad.IPad;
    import gnu.crypto.pad.PadFactory;

    HashMap attributes = new HashMap();
    HashMap modeAttributes = new HashMap();

    Cascade ofbBlowfish = new Cascade();
    Object modeNdx = ofbBlowfish.append(
        Stage.getInstance(
            ModeFactory.getInstance(Registry.OFB_MODE, new Blowfish(), 8),
            Direction.FORWARD));

    attributes.put(modeNdx, modeAttributes);
    IPad pkcs7 = PadFactory.getInstance(Registry.PKCS7_PAD);

    Assembly asm = new Assembly();
    asm.addPreTransformer(Transformer.getCascadeTransformer(ofbBlowfish));
    asm.addPreTransformer(Transformer.getPaddingTransformer(pkcs7));
    asm.addPreTransformer(Transformer.getDeflateTransformer());

    // plaintext and key material
    byte[] km = new byte[] { 0,  1,  2,  3,  4,  5,  6,  7,  8};
    byte[] iv = new byte[] {-1, -2, -3, -4, -5, -6, -7, -8, -9};
    byte[] pt = new byte[] { 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10,  11};
    byte[] tpt = new byte[11 * pt.length];

    // forward transformation
    modeAttributes.put(IBlockCipher.KEY_MATERIAL, km);
    modeAttributes.put(IMode.IV, iv);
    attributes.put(Assembly.DIRECTION, Direction.FORWARD);
    try
      {
        asm.init(attributes);
      }
    catch (TransformerException x)
      {
        x.printStackTrace(System.err);
      }

    byte[] ct = null;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try
      {
        for (int i = 0; i < 10; i++)
          { // transform in parts of 12-byte a time
            System.arraycopy(pt, 0, tpt, i * pt.length, pt.length);
            ct = asm.update(pt);
            baos.write(ct, 0, ct.length);
          }
      }
    catch (TransformerException x)
      {
        x.printStackTrace(System.err);
      }

    try
      {
        System.arraycopy(pt, 0, tpt, 10 * pt.length, pt.length);
        ct = asm.lastUpdate(pt);
      }
    catch (TransformerException x)
      {
        x.printStackTrace(System.err);
      }

    baos.write(ct, 0, ct.length);
    ct = baos.toByteArray();

    // reversed transformation
    attributes.put(Assembly.DIRECTION, Direction.REVERSED);
    try
      {
        asm.init(attributes);
      }
    catch (TransformerException x)
      {
        x.printStackTrace(System.err);
      }

    byte[] ot = null;
    try
      {
        ot = asm.lastUpdate(ct); // transform the lot in one go
      }
    catch (TransformerException x)
      {
        x.printStackTrace(System.err);
      }