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