From 761ad38bcc58e81abe8c55f9ab8824affd71b103 Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Thu, 2 Jun 2011 13:37:35 +0000
Subject: [PATCH]     * HMAC:       - Javadocs and cleanups       - Use
 SimpleByteCache       - Comments and speculation

---
 .../src/net/i2p/crypto/HMACGenerator.java     | 115 +++++++++++++++---
 .../org/bouncycastle/crypto/macs/I2PHMac.java |  30 ++---
 2 files changed, 109 insertions(+), 36 deletions(-)

diff --git a/core/java/src/net/i2p/crypto/HMACGenerator.java b/core/java/src/net/i2p/crypto/HMACGenerator.java
index 237c650550..3fe36c61e2 100644
--- a/core/java/src/net/i2p/crypto/HMACGenerator.java
+++ b/core/java/src/net/i2p/crypto/HMACGenerator.java
@@ -3,41 +3,67 @@ package net.i2p.crypto;
 import java.util.Arrays;
 import java.util.concurrent.LinkedBlockingQueue;
 
+// following are for main() tests
+//import java.security.InvalidKeyException;
+//import java.security.Key;
+//import java.security.NoSuchAlgorithmException;
+//import javax.crypto.spec.SecretKeySpec;
+//import net.i2p.data.Base64;
+
 import net.i2p.I2PAppContext;
 import net.i2p.data.DataHelper;
 import net.i2p.data.Hash;
 import net.i2p.data.SessionKey;
+import net.i2p.util.SimpleByteCache;
 
 import org.bouncycastle.crypto.digests.MD5Digest;
 import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.macs.I2PHMac;
 
 /**
- * Calculate the HMAC-MD5 of a key+message.  All the good stuff occurs
+ * Calculate the HMAC-MD5-128 of a key+message.  All the good stuff occurs
  * in {@link org.bouncycastle.crypto.macs.I2PHMac} and 
  * {@link org.bouncycastle.crypto.digests.MD5Digest}.
  *
+ * Keys are always 32 bytes.
+ * This is used only by UDP.
+ * Use deprecated outside the router, this may move to router.jar.
+ *
+ * NOTE THIS IS NOT COMPATIBLE with javax.crypto.Mac.getInstance("HmacMD5")
+ * as we tell I2PHMac that the digest length is 32 bytes, so it generates
+ * a different result.
+ *
+ * Quote jrandom:
+ * "The HMAC is hardcoded to use SHA256 digest size
+ * for backwards compatability.  next time we have a backwards
+ * incompatible change, we should update this."
+ *
+ * Does this mean he intended it to be compatible with MD5?
+ * See also 2005-07-05 status notes.
+ *
  */
 public class HMACGenerator {
-    private I2PAppContext _context;
     /** set of available HMAC instances for calculate */
     protected final LinkedBlockingQueue<I2PHMac> _available;
-    /** set of available byte[] buffers for verify */
-    private final LinkedBlockingQueue<byte[]> _availableTmp;
     
+    /**
+     *  @param context unused
+     */
     public HMACGenerator(I2PAppContext context) {
-        _context = context;
         _available = new LinkedBlockingQueue(32);
-        _availableTmp = new LinkedBlockingQueue(32);
     }
     
     /**
      * Calculate the HMAC of the data with the given key
+     *
+     * @return the first 16 bytes contain the HMAC, the last 16 bytes are zero
+     * @deprecated unused
      */
     public Hash calculate(SessionKey key, byte data[]) {
         if ((key == null) || (key.getData() == null) || (data == null))
             throw new NullPointerException("Null arguments for HMAC");
-        byte rv[] = new byte[Hash.HASH_LENGTH];
+        byte rv[] = acquireTmp();
+        Arrays.fill(rv, (byte)0x0);
         calculate(key, data, 0, data.length, rv, 0);
         return new Hash(rv);
     }
@@ -52,10 +78,8 @@ public class HMACGenerator {
         I2PHMac mac = acquire();
         mac.init(key.getData());
         mac.update(data, offset, length);
-        //byte rv[] = new byte[Hash.HASH_LENGTH];
         mac.doFinal(target, targetOffset);
         release(mac);
-        //return new Hash(rv);
     }
     
     /**
@@ -77,7 +101,6 @@ public class HMACGenerator {
         mac.init(key.getData());
         mac.update(curData, curOffset, curLength);
         byte rv[] = acquireTmp();
-        //byte rv[] = new byte[Hash.HASH_LENGTH];
         mac.doFinal(rv, 0);
         release(mac);
         
@@ -93,6 +116,7 @@ public class HMACGenerator {
         // the HMAC is hardcoded to use SHA256 digest size
         // for backwards compatability.  next time we have a backwards
         // incompatible change, we should update this by removing ", 32"
+        // SEE NOTES ABOVE
         return new I2PHMac(new MD5Digest(), 32);
     }
 
@@ -100,17 +124,74 @@ public class HMACGenerator {
         _available.offer(mac);
     }
 
-    // temp buffers for verify(..)
+    /**
+     * Not really tmp, just from the byte array cache.
+     * Does NOT zero.
+     */
     private byte[] acquireTmp() {
-        byte rv[] = _availableTmp.poll();
-        if (rv != null)
-            Arrays.fill(rv, (byte)0x0);
-        else
-            rv = new byte[Hash.HASH_LENGTH];
+        byte rv[] = SimpleByteCache.acquire(Hash.HASH_LENGTH);
         return rv;
     }
 
     private void releaseTmp(byte tmp[]) {
-        _availableTmp.offer(tmp);
+        SimpleByteCache.release(tmp);
+    }
+
+    //private static final int RUNS = 100000;
+
+    /**
+     *  Test the BC and the JVM's implementations for speed
+     */
+/****  All this did was prove that we aren't compatible with standard HmacMD5
+    public static void main(String args[]) {
+        if (args.length != 2) {
+            System.err.println("Usage: HMACGenerator keySeedString dataString");
+            return;
+        }
+
+        byte[] rand = SHA256Generator.getInstance().calculateHash(args[0].getBytes()).getData();
+        byte[] data = args[1].getBytes();
+        Key keyObj = new SecretKeySpec(rand, "HmacMD5");
+
+        byte[] keyBytes = keyObj.getEncoded();
+        System.out.println("key bytes (" + keyBytes.length + ") is [" + Base64.encode(keyBytes) + "]");
+        SessionKey key = new SessionKey(keyBytes);
+        System.out.println("session key is [" + key);
+        System.out.println("key object is [" + keyObj);
+
+        HMACGenerator gen = new HMACGenerator(I2PAppContext.getGlobalContext());
+        byte[] result = new byte[16];
+        long start = System.currentTimeMillis();
+        for (int i = 0; i < RUNS; i++) {
+            gen.calculate(key, data, 0, data.length, result, 0);
+            if (i == 0)
+                System.out.println("MAC [" + Base64.encode(result) + "]");
+        }
+        long time = System.currentTimeMillis() - start;
+        System.out.println("Time for " + RUNS + " HMAC-MD5 computations:");
+        System.out.println("BC time (ms): " + time);
+
+        start = System.currentTimeMillis();
+        javax.crypto.Mac mac;
+        try {
+            mac = javax.crypto.Mac.getInstance("HmacMD5");
+        } catch (NoSuchAlgorithmException e) {
+            System.err.println("Fatal: " + e);
+            return;
+        }
+        for (int i = 0; i < RUNS; i++) {
+            try {
+                mac.init(keyObj);
+            } catch (InvalidKeyException e) {
+                System.err.println("Fatal: " + e);
+            }
+            byte[] sha = mac.doFinal(data);
+            if (i == 0)
+                System.out.println("MAC [" + Base64.encode(sha) + "]");
+        }
+        time = System.currentTimeMillis() - start;
+
+        System.out.println("JVM time (ms): " + time);
     }
+****/
 }
diff --git a/core/java/src/org/bouncycastle/crypto/macs/I2PHMac.java b/core/java/src/org/bouncycastle/crypto/macs/I2PHMac.java
index 1255836135..b8d9c073ee 100644
--- a/core/java/src/org/bouncycastle/crypto/macs/I2PHMac.java
+++ b/core/java/src/org/bouncycastle/crypto/macs/I2PHMac.java
@@ -27,9 +27,10 @@ package org.bouncycastle.crypto.macs;
  */
 
 //import org.bouncycastle.crypto.CipherParameters;
-import java.util.ArrayList;
 import java.util.Arrays;
 
+import net.i2p.util.SimpleByteCache;
+
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Mac;
 
@@ -65,6 +66,11 @@ implements Mac
     {
         this(digest, digest.getDigestSize()); 
     }
+
+    /**
+     *  @param sz override the digest's size
+     *  SEE NOTES in HMACGenerator about why this isn't compatible with standard HmacMD5
+     */
     public I2PHMac(
         Digest digest, int sz)
     {
@@ -165,28 +171,14 @@ implements Mac
         return len;
     }
     
-    /**
-     * list of buffers - index 0 is the cache for 32 byte arrays, while index 1 is the cache for 16 byte arrays
-     */
-    private static ArrayList _tmpBuf[] = new ArrayList[] { new ArrayList(), new ArrayList() };
     private static byte[] acquireTmp(int sz) {
-        byte rv[] = null;
-        synchronized (_tmpBuf[sz == 32 ? 0 : 1]) {
-            if (!_tmpBuf[sz == 32 ? 0 : 1].isEmpty())
-                rv = (byte[])_tmpBuf[sz == 32 ? 0 : 1].remove(0);
-        }
-        if (rv != null)
-            Arrays.fill(rv, (byte)0x0);
-        else
-            rv = new byte[sz];
+        byte[] rv = SimpleByteCache.acquire(sz);
+        Arrays.fill(rv, (byte)0x0);
         return rv;
     }
+
     private static void releaseTmp(byte buf[]) {
-        if (buf == null) return;
-        synchronized (_tmpBuf[buf.length == 32 ? 0 : 1]) {
-            if (_tmpBuf[buf.length == 32 ? 0 : 1].size() < 100) 
-                _tmpBuf[buf.length == 32 ? 0 : 1].add((Object)buf);
-        }
+        SimpleByteCache.release(buf);
     }
 
     /**
-- 
GitLab