* HMAC:
- Javadocs and cleanups - Use SimpleByteCache - Comments and speculation
This commit is contained in:
@ -3,41 +3,67 @@ package net.i2p.crypto;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
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.I2PAppContext;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.SessionKey;
|
import net.i2p.data.SessionKey;
|
||||||
|
import net.i2p.util.SimpleByteCache;
|
||||||
|
|
||||||
import org.bouncycastle.crypto.digests.MD5Digest;
|
import org.bouncycastle.crypto.digests.MD5Digest;
|
||||||
import org.bouncycastle.crypto.Mac;
|
import org.bouncycastle.crypto.Mac;
|
||||||
import org.bouncycastle.crypto.macs.I2PHMac;
|
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
|
* in {@link org.bouncycastle.crypto.macs.I2PHMac} and
|
||||||
* {@link org.bouncycastle.crypto.digests.MD5Digest}.
|
* {@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 {
|
public class HMACGenerator {
|
||||||
private I2PAppContext _context;
|
|
||||||
/** set of available HMAC instances for calculate */
|
/** set of available HMAC instances for calculate */
|
||||||
protected final LinkedBlockingQueue<I2PHMac> _available;
|
protected final LinkedBlockingQueue<I2PHMac> _available;
|
||||||
/** set of available byte[] buffers for verify */
|
|
||||||
private final LinkedBlockingQueue<byte[]> _availableTmp;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context unused
|
||||||
|
*/
|
||||||
public HMACGenerator(I2PAppContext context) {
|
public HMACGenerator(I2PAppContext context) {
|
||||||
_context = context;
|
|
||||||
_available = new LinkedBlockingQueue(32);
|
_available = new LinkedBlockingQueue(32);
|
||||||
_availableTmp = new LinkedBlockingQueue(32);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the HMAC of the data with the given key
|
* 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[]) {
|
public Hash calculate(SessionKey key, byte data[]) {
|
||||||
if ((key == null) || (key.getData() == null) || (data == null))
|
if ((key == null) || (key.getData() == null) || (data == null))
|
||||||
throw new NullPointerException("Null arguments for HMAC");
|
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);
|
calculate(key, data, 0, data.length, rv, 0);
|
||||||
return new Hash(rv);
|
return new Hash(rv);
|
||||||
}
|
}
|
||||||
@ -52,10 +78,8 @@ public class HMACGenerator {
|
|||||||
I2PHMac mac = acquire();
|
I2PHMac mac = acquire();
|
||||||
mac.init(key.getData());
|
mac.init(key.getData());
|
||||||
mac.update(data, offset, length);
|
mac.update(data, offset, length);
|
||||||
//byte rv[] = new byte[Hash.HASH_LENGTH];
|
|
||||||
mac.doFinal(target, targetOffset);
|
mac.doFinal(target, targetOffset);
|
||||||
release(mac);
|
release(mac);
|
||||||
//return new Hash(rv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,7 +101,6 @@ public class HMACGenerator {
|
|||||||
mac.init(key.getData());
|
mac.init(key.getData());
|
||||||
mac.update(curData, curOffset, curLength);
|
mac.update(curData, curOffset, curLength);
|
||||||
byte rv[] = acquireTmp();
|
byte rv[] = acquireTmp();
|
||||||
//byte rv[] = new byte[Hash.HASH_LENGTH];
|
|
||||||
mac.doFinal(rv, 0);
|
mac.doFinal(rv, 0);
|
||||||
release(mac);
|
release(mac);
|
||||||
|
|
||||||
@ -93,6 +116,7 @@ public class HMACGenerator {
|
|||||||
// the HMAC is hardcoded to use SHA256 digest size
|
// the HMAC is hardcoded to use SHA256 digest size
|
||||||
// for backwards compatability. next time we have a backwards
|
// for backwards compatability. next time we have a backwards
|
||||||
// incompatible change, we should update this by removing ", 32"
|
// incompatible change, we should update this by removing ", 32"
|
||||||
|
// SEE NOTES ABOVE
|
||||||
return new I2PHMac(new MD5Digest(), 32);
|
return new I2PHMac(new MD5Digest(), 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,17 +124,74 @@ public class HMACGenerator {
|
|||||||
_available.offer(mac);
|
_available.offer(mac);
|
||||||
}
|
}
|
||||||
|
|
||||||
// temp buffers for verify(..)
|
/**
|
||||||
|
* Not really tmp, just from the byte array cache.
|
||||||
|
* Does NOT zero.
|
||||||
|
*/
|
||||||
private byte[] acquireTmp() {
|
private byte[] acquireTmp() {
|
||||||
byte rv[] = _availableTmp.poll();
|
byte rv[] = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
||||||
if (rv != null)
|
|
||||||
Arrays.fill(rv, (byte)0x0);
|
|
||||||
else
|
|
||||||
rv = new byte[Hash.HASH_LENGTH];
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releaseTmp(byte tmp[]) {
|
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);
|
||||||
|
}
|
||||||
|
****/
|
||||||
}
|
}
|
||||||
|
@ -27,9 +27,10 @@ package org.bouncycastle.crypto.macs;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
//import org.bouncycastle.crypto.CipherParameters;
|
//import org.bouncycastle.crypto.CipherParameters;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import net.i2p.util.SimpleByteCache;
|
||||||
|
|
||||||
import org.bouncycastle.crypto.Digest;
|
import org.bouncycastle.crypto.Digest;
|
||||||
import org.bouncycastle.crypto.Mac;
|
import org.bouncycastle.crypto.Mac;
|
||||||
|
|
||||||
@ -65,6 +66,11 @@ implements Mac
|
|||||||
{
|
{
|
||||||
this(digest, digest.getDigestSize());
|
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(
|
public I2PHMac(
|
||||||
Digest digest, int sz)
|
Digest digest, int sz)
|
||||||
{
|
{
|
||||||
@ -165,28 +171,14 @@ implements Mac
|
|||||||
return len;
|
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) {
|
private static byte[] acquireTmp(int sz) {
|
||||||
byte rv[] = null;
|
byte[] rv = SimpleByteCache.acquire(sz);
|
||||||
synchronized (_tmpBuf[sz == 32 ? 0 : 1]) {
|
Arrays.fill(rv, (byte)0x0);
|
||||||
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];
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void releaseTmp(byte buf[]) {
|
private static void releaseTmp(byte buf[]) {
|
||||||
if (buf == null) return;
|
SimpleByteCache.release(buf);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user