/**
   * Process the <em>Clear</em> part of the message.
   *
   * @param in a byte array containing the <em>Clear</em> part of the message.
   * @param offset the starting index in <code>in</code> where the <em>Clear</em> message bytes
   *     should be considered.
   * @param length the count of bytes in <code>in</code>, starting from <code>offset</code> to
   *     consider.
   * @throws IllegalStateException if no start-of-message method has been invoked earlier or the
   *     Integrity Protection service has not been activated.
   */
  public void doClear(byte[] in, int offset, int length) {
    if (!ready) {
      throw new IllegalStateException();
    }
    if (!wantIntegrity) {
      throw new IllegalStateException();
    }

    mac.update(in, offset, length);
  }
  /**
   * Signals the end of the message transformation.
   *
   * @return the authentication tag bytes, if and when the Integrity Protection service was
   *     specified; otherwise a 0-long byte array is returned.
   * @throws IllegalStateException if no start-of-message method has been invoked earlier.
   */
  public byte[] endMessage() {
    if (!ready) {
      throw new IllegalStateException();
    }
    if (!wantIntegrity) {
      return new byte[0];
    }

    byte[] result = mac.digest();
    reset();
    return result;
  }
  /**
   * Signals the start of a new message to process with this <code>UST</code> with a designated
   * <emIndex</em> value.
   *
   * @param ndx the <em>Index</em> to use with the new message.
   * @throws LimitReachedException if the value of the <em>Index</em> has reached its allowed upper
   *     bound. To use this <code>UST</code> instance a new initialisation (with a new user-supplied
   *     key material) should occur.
   * @throws InvalidKeyException if the underlying cipher, used in either or both the Integrity
   *     Protection and Confidentiality Protection functions has detected an exception.
   */
  public void beginMessageWithIndex(BigInteger ndx)
      throws LimitReachedException, InvalidKeyException {
    if (ndx.compareTo(maxIndex) > 0) {
      throw new LimitReachedException();
    }

    index = ndx;

    // depending on the desired services, get keying material from the
    // internal keystream generator and complete the relevant attributes.
    // the key size shall be the same size as the user-supplied key material.
    // remember we need the 1st generator if integrity is required even when
    // confidentiality is not
    if (wantConfidentiality || wantIntegrity) {
      byte[] cpKey = new byte[keysize];
      keystream.nextBytes(cpKey, 0, keysize);
      cpAttributes.put(IBlockCipher.KEY_MATERIAL, cpKey);
      if (cpStream instanceof ICMGenerator) {
        cpAttributes.put(ICMGenerator.SEGMENT_INDEX, index);
      } else {
        cpAttributes.put(UMacGenerator.INDEX, new Integer(index.intValue()));
      }

      cpStream.init(cpAttributes);
    }

    if (wantIntegrity) {
      byte[] ipKey = new byte[keysize];
      keystream.nextBytes(ipKey, 0, keysize);
      ipAttributes.put(IBlockCipher.KEY_MATERIAL, ipKey);
      if (ipStream instanceof ICMGenerator) {
        ipAttributes.put(ICMGenerator.SEGMENT_INDEX, index);
      } else {
        ipAttributes.put(UMacGenerator.INDEX, new Integer(index.intValue()));
      }

      ipStream.init(ipAttributes);

      // get prefix bytes
      byte[] prefix = new byte[macLength];
      cpStream.nextBytes(prefix, 0, macLength);
      macAttributes.put(TMMH16.PREFIX, prefix);

      mac = new TMMH16();
      macAttributes.put(TMMH16.KEYSTREAM, ipStream);
      mac.init(macAttributes);
    }

    ready = true;
  }
  /**
   * Process the <em>Opaque</em> part of the message.
   *
   * @param in a byte array containing the <em>Clear</em> part of the message.
   * @param inOffset the starting index in <code>in</code> where the <em>Opaque</em> message bytes
   *     should be considered.
   * @param length the count of bytes in <code>in</code>, starting from <code>inOffset</code> to
   *     consider.
   * @param out the byte array where the enciphered opaque message should be stored.
   * @param outOffset the starting offset in <code>out</code> where the enciphered bytes should be
   *     stored.
   * @throws IllegalStateException if no start-of-message method has been invoked earlier.
   * @throws LimitReachedException if one or both of the underlying keystream generators have
   *     reached their limit.
   */
  public void doOpaque(byte[] in, int inOffset, int length, byte[] out, int outOffset)
      throws LimitReachedException {
    if (!ready) {
      throw new IllegalStateException();
    }
    if (wantIntegrity) {
      mac.update(in, inOffset, length);
    }

    if (wantConfidentiality) {
      byte[] suffix = new byte[length];
      cpStream.nextBytes(suffix, 0, length);
      for (int i = 0; i < length; ) {
        out[outOffset++] = (byte) (in[inOffset++] ^ suffix[i++]);
      }
    } else {
      System.arraycopy(in, inOffset, out, outOffset, length);
    }
  }
 /** Reset this instance and prepare for processing a new message. */
 public void reset() {
   ready = false;
   if (wantIntegrity) {
     mac.reset();
   }
 }