public byte[] unwrap(byte[] in, int inOff, int inLen) throws InvalidCipherTextException {
    if (forWrapping) {
      throw new IllegalStateException("not set for unwrapping");
    }

    int n = inLen / 8;

    if ((n * 8) != inLen) {
      throw new InvalidCipherTextException("unwrap data must be a multiple of 8 bytes");
    }

    byte[] block = new byte[inLen - iv.length];
    byte[] a = new byte[iv.length];
    byte[] buf = new byte[8 + iv.length];

    System.arraycopy(in, inOff, a, 0, iv.length);
    System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);

    engine.init(false, param);

    n = n - 1;

    for (int j = 5; j >= 0; j--) {
      for (int i = n; i >= 1; i--) {
        System.arraycopy(a, 0, buf, 0, iv.length);
        System.arraycopy(block, 8 * (i - 1), buf, iv.length, 8);

        int t = n * j + i;
        for (int k = 1; t != 0; k++) {
          byte v = (byte) t;

          buf[iv.length - k] ^= v;

          t >>>= 8;
        }

        engine.processBlock(buf, 0, buf, 0);
        System.arraycopy(buf, 0, a, 0, 8);
        System.arraycopy(buf, 8, block, 8 * (i - 1), 8);
      }
    }

    if (!Arrays.constantTimeAreEqual(a, iv)) {
      throw new InvalidCipherTextException("checksum failed");
    }

    return block;
  }
  public byte[] wrap(byte[] in, int inOff, int inLen) {
    if (!forWrapping) {
      throw new IllegalStateException("not set for wrapping");
    }

    int n = inLen / 8;

    if ((n * 8) != inLen) {
      throw new DataLengthException("wrap data must be a multiple of 8 bytes");
    }

    byte[] block = new byte[inLen + iv.length];
    byte[] buf = new byte[8 + iv.length];

    System.arraycopy(iv, 0, block, 0, iv.length);
    System.arraycopy(in, inOff, block, iv.length, inLen);

    engine.init(true, param);

    for (int j = 0; j != 6; j++) {
      for (int i = 1; i <= n; i++) {
        System.arraycopy(block, 0, buf, 0, iv.length);
        System.arraycopy(block, 8 * i, buf, iv.length, 8);
        engine.processBlock(buf, 0, buf, 0);

        int t = n * j + i;
        for (int k = 1; t != 0; k++) {
          byte v = (byte) t;

          buf[iv.length - k] ^= v;

          t >>>= 8;
        }

        System.arraycopy(buf, 0, block, 0, 8);
        System.arraycopy(buf, 8, block, 8 * i, 8);
      }
    }

    return block;
  }
    public void init(boolean forEncryption, CipherParameters params)
        throws IllegalArgumentException {
      byte[] k = Arrays.clone(((KeyParameter) params).getKey());

      DESedeParameters.setOddParity(k);

      if (!Arrays.areEqual(((KeyParameter) params).getKey(), k)) {
        fail("key not odd parity");
      }

      cipher.init(forEncryption, params);
    }
  /**
   * Create a buffered block cipher that uses Cipher Text Stealing
   *
   * @param cipher the underlying block cipher this buffering object wraps.
   */
  public CTSBlockCipher(BlockCipher cipher) {
    if ((cipher instanceof OFBBlockCipher)
        || (cipher instanceof CFBBlockCipher)
        || (cipher instanceof SICBlockCipher)) {
      // TODO: This is broken - need to introduce marker interface to differentiate block cipher
      // primitive from mode?
      throw new IllegalArgumentException("CTSBlockCipher can only accept ECB, or CBC ciphers");
    }

    this.cipher = cipher;

    blockSize = cipher.getBlockSize();

    buf = new byte[blockSize * 2];
    bufOff = 0;
  }
  /**
   * Create a buffered block cipher with, or without, padding.
   *
   * @param cipher the underlying block cipher this buffering object wraps.
   */
  public PaddedBlockCipher(BlockCipher cipher) {
    this.cipher = cipher;

    buf = new byte[cipher.getBlockSize()];
    bufOff = 0;
  }
 public String getAlgorithmName() {
   return engine.getAlgorithmName();
 }
 public void reset() {
   cipher.reset();
 }
 public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
     throws DataLengthException, IllegalStateException {
   return cipher.processBlock(in, inOff, out, outOff);
 }
 public int getBlockSize() {
   return cipher.getBlockSize();
 }
 public String getAlgorithmName() {
   return cipher.getAlgorithmName();
 }
  /**
   * Process the last block in the buffer.
   *
   * @param out the array the block currently being held is copied into.
   * @param outOff the offset at which the copying starts.
   * @return the number of output bytes copied to out.
   * @exception DataLengthException if there is insufficient space in out for the output.
   * @exception IllegalStateException if the underlying cipher is not initialised.
   * @exception InvalidCipherTextException if cipher text decrypts wrongly (in case the exception
   *     will never get thrown).
   */
  public int doFinal(byte[] out, int outOff)
      throws DataLengthException, IllegalStateException, InvalidCipherTextException {
    if (bufOff + outOff > out.length) {
      throw new DataLengthException("output buffer to small in doFinal");
    }

    int blockSize = cipher.getBlockSize();
    int len = bufOff - blockSize;
    byte[] block = new byte[blockSize];

    if (forEncryption) {
      if (bufOff < blockSize) {
        throw new DataLengthException("need at least one block of input for CTS");
      }

      cipher.processBlock(buf, 0, block, 0);

      if (bufOff > blockSize) {
        for (int i = bufOff; i != buf.length; i++) {
          buf[i] = block[i - blockSize];
        }

        for (int i = blockSize; i != bufOff; i++) {
          buf[i] ^= block[i - blockSize];
        }

        if (cipher instanceof CBCBlockCipher) {
          BlockCipher c = ((CBCBlockCipher) cipher).getUnderlyingCipher();

          c.processBlock(buf, blockSize, out, outOff);
        } else {
          cipher.processBlock(buf, blockSize, out, outOff);
        }

        System.arraycopy(block, 0, out, outOff + blockSize, len);
      } else {
        System.arraycopy(block, 0, out, outOff, blockSize);
      }
    } else {
      if (bufOff < blockSize) {
        throw new DataLengthException("need at least one block of input for CTS");
      }

      byte[] lastBlock = new byte[blockSize];

      if (bufOff > blockSize) {
        if (cipher instanceof CBCBlockCipher) {
          BlockCipher c = ((CBCBlockCipher) cipher).getUnderlyingCipher();

          c.processBlock(buf, 0, block, 0);
        } else {
          cipher.processBlock(buf, 0, block, 0);
        }

        for (int i = blockSize; i != bufOff; i++) {
          lastBlock[i - blockSize] = (byte) (block[i - blockSize] ^ buf[i]);
        }

        System.arraycopy(buf, blockSize, block, 0, len);

        cipher.processBlock(block, 0, out, outOff);
        System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
      } else {
        cipher.processBlock(buf, 0, block, 0);

        System.arraycopy(block, 0, out, outOff, blockSize);
      }
    }

    int offset = bufOff;

    reset();

    return offset;
  }