/*
  * 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 verify(char[] password) {
    if (!isMasked() || payload == null) {
      return;
    }
    IMac m = null;
    try {
      m = getMac(password);
    } catch (Exception x) {
      throw new IllegalArgumentException(x.toString());
    }

    m.update(payload, 0, payload.length - m.macSize());
    byte[] macValue = new byte[m.macSize()];
    System.arraycopy(payload, payload.length - macValue.length, macValue, 0, macValue.length);
    if (!Arrays.equals(macValue, m.digest())) {
      throw new IllegalArgumentException("MAC verification failed");
    }
    try {
      DataInputStream in =
          new DataInputStream(new ByteArrayInputStream(payload, 0, payload.length - m.macSize()));
      decodeEnvelope(in);
    } catch (IOException ioe) {
      throw new IllegalArgumentException("malformed keyring fragment");
    }
    setMasked(false);
    payload = null;
  }
  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;
  }
 public void authenticate(char[] password) throws IOException {
   if (isMasked()) {
     throw new IllegalStateException("entry is masked");
   }
   byte[] salt = new byte[8];
   PRNG.nextBytes(salt, 0, salt.length);
   properties.put("salt", Util.toString(salt));
   IMac m = getMac(password);
   ByteArrayOutputStream bout = new ByteArrayOutputStream(1024);
   MacOutputStream macout = new MacOutputStream(bout, m);
   DataOutputStream out2 = new DataOutputStream(macout);
   for (Iterator it = entries.iterator(); it.hasNext(); ) {
     Entry entry = (Entry) it.next();
     entry.encode(out2);
   }
   bout.write(m.digest());
   payload = bout.toByteArray();
 }
 public static PasswordAuthenticatedEntry decode(DataInputStream in, char[] password)
     throws IOException {
   PasswordAuthenticatedEntry entry = new PasswordAuthenticatedEntry();
   entry.properties.decode(in);
   IMac mac = entry.getMac(password);
   int len = in.readInt() - mac.macSize();
   MeteredInputStream min = new MeteredInputStream(in, len);
   MacInputStream macin = new MacInputStream(min, mac);
   DataInputStream in2 = new DataInputStream(macin);
   entry.setMasked(false);
   entry.decodeEnvelope(in2);
   byte[] macValue = new byte[mac.macSize()];
   in.readFully(macValue);
   if (!Arrays.equals(macValue, mac.digest())) {
     throw new MalformedKeyringException("MAC verification failed");
   }
   return entry;
 }
  public void test(TestHarness harness) {
    harness.checkPoint("TestOfTMMH16");

    /*
    KEY_LENGTH: 10
    TAG_LENGTH: 2
    key: { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc }
    message: { 0xca, 0xfe, 0xba, 0xbe, 0xba, 0xde }
    output: { 0x9d, 0x6a }
    */
    try {
      attributes.clear();
      keystream = new DummyKeystream();
      keystream.init(null);

      output = new byte[] {(byte) 0x9d, (byte) 0x6a};
      mac = new TMMH16();
      attributes.put(TMMH16.KEYSTREAM, keystream);
      attributes.put(TMMH16.TAG_LENGTH, new Integer(2));
      mac.init(attributes);
      message =
          new byte[] {(byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe, (byte) 0xba, (byte) 0xde};
      for (int i = 0; i < message.length; i++) {
        mac.update(message[i]);
      }
      result = mac.digest();
      harness.check(Arrays.equals(result, output), "testVector1");
    } catch (Exception x) {
      harness.debug(x);
      harness.fail("TestOfTMMH16.testVector1");
    }

    /*
    KEY_LENGTH: 10
    TAG_LENGTH: 2
    key: { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc }
    message: { 0xca, 0xfe, 0xba }
    output: { 0xc8, 0x8e }
    */
    try {
      attributes.clear();
      keystream = new DummyKeystream();
      keystream.init(null);

      output = new byte[] {(byte) 0xc8, (byte) 0x8e};
      mac = new TMMH16();
      attributes.put(TMMH16.KEYSTREAM, keystream);
      attributes.put(TMMH16.TAG_LENGTH, new Integer(2));
      mac.init(attributes);
      message = new byte[] {(byte) 0xca, (byte) 0xfe, (byte) 0xba};
      for (int i = 0; i < message.length; i++) {
        mac.update(message[i]);
      }
      result = mac.digest();
      harness.check(Arrays.equals(result, output), "testVector2");
    } catch (Exception x) {
      harness.debug(x);
      harness.fail("TestOfTMMH16.testVector2");
    }

    /*
    KEY_LENGTH: 10
    TAG_LENGTH: 4
    key: { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc }
    message: { 0xca, 0xfe, 0xba, 0xbe, 0xba, 0xde }
    output: { 0x9d, 0x6a, 0xc0, 0xd3 }
    */
    try {
      attributes.clear();
      keystream = new DummyKeystream();
      keystream.init(null);

      output = new byte[] {(byte) 0x9d, (byte) 0x6a, (byte) 0xc0, (byte) 0xd3};
      mac = new TMMH16();
      attributes.put(TMMH16.KEYSTREAM, keystream);
      attributes.put(TMMH16.TAG_LENGTH, new Integer(4));
      mac.init(attributes);
      message =
          new byte[] {(byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe, (byte) 0xba, (byte) 0xde};
      for (int i = 0; i < message.length; i++) {
        mac.update(message[i]);
      }
      result = mac.digest();
      harness.check(Arrays.equals(result, output), "testVector3");
    } catch (Exception x) {
      harness.debug(x);
      harness.fail("TestOfTMMH16.testVector3");
    }
  }