/*
  * The PRF is defined as:
  *
  *   PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
  *                              P_SHA-1(S2, label + seed);
  *
  * P_hash is defined as:
  *
  *   P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) +
  *                          HMAC_hash(secret, A(2) + seed) +
  *                          HMAC_hash(secret, A(3) + seed) + ...
  *
  * And A() is defined as:
  *
  *   A(0) = seed
  *   A(i) = HMAC_hash(secret, A(i-1))
  *
  * For simplicity, we compute an 80-byte block on each call, which
  * corresponds to five iterations of MD5, and four of SHA-1.
  */
 private synchronized void fillBuffer() {
   int len = hmac_md5.macSize();
   for (int i = 0; i < buffer.length; i += len) {
     hmac_md5.update(md5_a, 0, md5_a.length);
     hmac_md5.update(seed, 0, seed.length);
     byte[] b = hmac_md5.digest();
     hmac_md5.reset();
     System.arraycopy(b, 0, buffer, i, len);
     hmac_md5.update(md5_a, 0, md5_a.length);
     md5_a = hmac_md5.digest();
     hmac_md5.reset();
   }
   len = hmac_sha.macSize();
   for (int i = 0; i < buffer.length; i += len) {
     hmac_sha.update(sha_a, 0, sha_a.length);
     hmac_sha.update(seed, 0, seed.length);
     byte[] b = hmac_sha.digest();
     hmac_sha.reset();
     for (int j = 0; j < len; j++) {
       buffer[j + i] ^= b[j];
     }
     hmac_sha.update(sha_a, 0, sha_a.length);
     sha_a = hmac_sha.digest();
     hmac_sha.reset();
   }
   idx = 0;
 }
  public void init(Map attributes) {
    HashMap sha_attr = new HashMap();
    HashMap md5_attr = new HashMap();
    byte[] secret = (byte[]) attributes.get(SECRET);
    if (secret != null) {
      int l = (secret.length >>> 1) + (secret.length & 1);
      byte[] s1 = Util.trim(secret, 0, l);
      byte[] s2 = Util.trim(secret, secret.length - l, l);
      md5_attr.put(IMac.MAC_KEY_MATERIAL, s1);
      sha_attr.put(IMac.MAC_KEY_MATERIAL, s2);
      try {
        hmac_md5.init(md5_attr);
        hmac_sha.init(sha_attr);
      } catch (InvalidKeyException ike) {
        throw new Error(ike.toString());
      }
    } else if (!init) {
      throw new IllegalArgumentException("no secret supplied");
    }
    // else re-use

    byte[] seeed = (byte[]) attributes.get(SEED);
    if (seeed != null) {
      seed = (byte[]) seeed.clone();
    } else if (!init) {
      throw new IllegalArgumentException("no seed supplied");
    }
    // else re-use

    // A(0) is the seed, A(1) = HMAC_hash(secret, A(0)).
    hmac_md5.update(seed, 0, seed.length);
    md5_a = hmac_md5.digest();
    hmac_md5.reset();
    hmac_sha.update(seed, 0, seed.length);
    sha_a = hmac_sha.digest();
    hmac_sha.reset();
    fillBuffer();
    init = true;
  }