forked from I2P_Developers/i2p.i2p
* HMAC:
- Replace BC MD5 with JVM version, refactor I2PHMAC to use
MessageDigest instead of BC Digest (ticket #1189)
- Use JVM HmacSHA256 instead of I2PHMAC for Syndie since it is standard
This commit is contained in:
@@ -1,36 +1,101 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import gnu.crypto.hash.Sha256Standalone;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
import org.bouncycastle.oldcrypto.Digest;
|
||||
import org.bouncycastle.oldcrypto.macs.I2PHMac;
|
||||
|
||||
/**
|
||||
* Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs
|
||||
* in {@link org.bouncycastle.oldcrypto.macs.I2PHMac} and
|
||||
* {@link gnu.crypto.hash.Sha256Standalone}.
|
||||
* Calculate the HMAC-SHA256 of a key+message.
|
||||
* This is compatible with javax.crypto.Mac.getInstance("HmacSHA256").
|
||||
*
|
||||
* This should be compatible with javax.crypto.Mac.getInstance("HmacSHA256")
|
||||
* but that is untested.
|
||||
* As of 0.9.12, uses javax.crypto.Mac.
|
||||
*
|
||||
* deprecated used only by syndie
|
||||
* Deprecated, used only by Syndie.
|
||||
*/
|
||||
public class HMAC256Generator extends HMACGenerator {
|
||||
|
||||
/**
|
||||
* @param context unused
|
||||
*/
|
||||
public HMAC256Generator(I2PAppContext context) { super(context); }
|
||||
|
||||
/**
|
||||
* @deprecated unused (not even by Syndie)
|
||||
* @throws UnsupportedOperationException since 0.9.12
|
||||
*/
|
||||
@Override
|
||||
protected I2PHMac acquire() {
|
||||
I2PHMac rv = _available.poll();
|
||||
if (rv != null)
|
||||
return rv;
|
||||
// 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"
|
||||
return new I2PHMac(new Sha256ForMAC());
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (not even by Syndie)
|
||||
* @throws UnsupportedOperationException always
|
||||
* @since 0.9.12 overrides HMACGenerator
|
||||
*/
|
||||
@Override
|
||||
public Hash calculate(SessionKey key, byte data[]) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the HMAC of the data with the given key.
|
||||
* Outputs 32 bytes to target starting at targetOffset.
|
||||
*
|
||||
* @throws UnsupportedOperationException if the JVM does not support it
|
||||
* @throws IllegalArgumentException for bad key or target too small
|
||||
* @since 0.9.12 overrides HMACGenerator
|
||||
*/
|
||||
@Override
|
||||
public void calculate(SessionKey key, byte data[], int offset, int length, byte target[], int targetOffset) {
|
||||
try {
|
||||
javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA256");
|
||||
Key keyObj = new SecretKeySpec(key.getData(), "HmacSHA256");
|
||||
mac.init(keyObj);
|
||||
mac.update(data, offset, length);
|
||||
mac.doFinal(target, targetOffset);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new UnsupportedOperationException("HmacSHA256", e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
throw new IllegalArgumentException("HmacSHA256", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the MAC inline, reducing some unnecessary memory churn.
|
||||
*
|
||||
* @param key session key to verify the MAC with
|
||||
* @param curData MAC to verify
|
||||
* @param curOffset index into curData to MAC
|
||||
* @param curLength how much data in curData do we want to run the HMAC over
|
||||
* @param origMAC what do we expect the MAC of curData to equal
|
||||
* @param origMACOffset index into origMAC
|
||||
* @param origMACLength how much of the MAC do we want to verify, use 32 for HMAC256
|
||||
* @since 0.9.12 overrides HMACGenerator
|
||||
*/
|
||||
@Override
|
||||
public boolean verify(SessionKey key, byte curData[], int curOffset, int curLength,
|
||||
byte origMAC[], int origMACOffset, int origMACLength) {
|
||||
byte calc[] = acquireTmp();
|
||||
calculate(key, curData, curOffset, curLength, calc, 0);
|
||||
boolean eq = DataHelper.eq(calc, 0, origMAC, origMACOffset, origMACLength);
|
||||
releaseTmp(calc);
|
||||
return eq;
|
||||
}
|
||||
|
||||
/******
|
||||
private static class Sha256ForMAC extends Sha256Standalone implements Digest {
|
||||
public String getAlgorithmName() { return "sha256 for hmac"; }
|
||||
public int getDigestSize() { return 32; }
|
||||
@@ -43,7 +108,6 @@ public class HMAC256Generator extends HMACGenerator {
|
||||
|
||||
}
|
||||
|
||||
/******
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
byte data[] = new byte[64];
|
||||
@@ -53,4 +117,81 @@ public class HMAC256Generator extends HMACGenerator {
|
||||
System.out.println(Base64.encode(mac.getData()));
|
||||
}
|
||||
******/
|
||||
|
||||
/**
|
||||
* Test the BC and the JVM's implementations for speed
|
||||
*
|
||||
* Results on 2012 hexcore box, OpenJDK 7:
|
||||
* BC 9275 ms (before converting to MessageDigest)
|
||||
* BC 8500 ms (after converting to MessageDigest)
|
||||
* JVM 8065 ms
|
||||
*
|
||||
*/
|
||||
/****
|
||||
public static void main(String args[]) {
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
byte[] rand = new byte[32];
|
||||
byte[] data = new byte[1500];
|
||||
Key keyObj = new SecretKeySpec(rand, "HmacSHA256");
|
||||
SessionKey key = new SessionKey(rand);
|
||||
|
||||
HMAC256Generator gen = new HMAC256Generator(I2PAppContext.getGlobalContext());
|
||||
byte[] result = new byte[32];
|
||||
javax.crypto.Mac mac;
|
||||
try {
|
||||
mac = javax.crypto.Mac.getInstance("HmacSHA256");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
System.err.println("Fatal: " + e);
|
||||
return;
|
||||
}
|
||||
// warmup and comparison
|
||||
System.out.println("Warmup and comparison:");
|
||||
int RUNS = 25000;
|
||||
for (int i = 0; i < RUNS; i++) {
|
||||
ctx.random().nextBytes(rand);
|
||||
ctx.random().nextBytes(data);
|
||||
keyObj = new SecretKeySpec(rand, "HmacSHA256");
|
||||
byte[] keyBytes = keyObj.getEncoded();
|
||||
if (!DataHelper.eq(rand, keyBytes))
|
||||
System.out.println("secret key in != out");
|
||||
key = new SessionKey(rand);
|
||||
gen.calculate(key, data, 0, data.length, result, 0);
|
||||
try {
|
||||
mac.init(keyObj);
|
||||
} catch (GeneralSecurityException e) {
|
||||
System.err.println("Fatal: " + e);
|
||||
return;
|
||||
}
|
||||
byte[] result2 = mac.doFinal(data);
|
||||
if (!DataHelper.eq(result, result2))
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
// real thing
|
||||
System.out.println("Passed");
|
||||
System.out.println("BC Test:");
|
||||
RUNS = 500000;
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < RUNS; i++) {
|
||||
gen.calculate(key, data, 0, data.length, result, 0);
|
||||
}
|
||||
long time = System.currentTimeMillis() - start;
|
||||
System.out.println("Time for " + RUNS + " HMAC-SHA256 computations:");
|
||||
System.out.println("BC time (ms): " + time);
|
||||
|
||||
System.out.println("JVM Test:");
|
||||
start = System.currentTimeMillis();
|
||||
for (int i = 0; i < RUNS; i++) {
|
||||
try {
|
||||
mac.init(keyObj);
|
||||
} catch (GeneralSecurityException e) {
|
||||
System.err.println("Fatal: " + e);
|
||||
}
|
||||
byte[] sha = mac.doFinal(data);
|
||||
}
|
||||
time = System.currentTimeMillis() - start;
|
||||
|
||||
System.out.println("JVM time (ms): " + time);
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.i2p.crypto;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
@@ -16,7 +18,6 @@ import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.SimpleByteCache;
|
||||
|
||||
import org.bouncycastle.oldcrypto.digests.MD5Digest;
|
||||
import org.bouncycastle.oldcrypto.macs.I2PHMac;
|
||||
|
||||
/**
|
||||
@@ -56,7 +57,7 @@ public class HMACGenerator {
|
||||
* 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
|
||||
* @deprecated unused (not even by Syndie)
|
||||
*/
|
||||
public Hash calculate(SessionKey key, byte data[]) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
@@ -69,6 +70,9 @@ public class HMACGenerator {
|
||||
|
||||
/**
|
||||
* Calculate the HMAC of the data with the given key
|
||||
*
|
||||
* @return the first 16 bytes contain the HMAC, the last 16 bytes are zero
|
||||
* @throws IllegalArgumentException for bad key or target too small
|
||||
*/
|
||||
public void calculate(SessionKey key, byte data[], int offset, int length, byte target[], int targetOffset) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
@@ -91,8 +95,10 @@ public class HMACGenerator {
|
||||
* @param origMAC what do we expect the MAC of curData to equal
|
||||
* @param origMACOffset index into origMAC
|
||||
* @param origMACLength how much of the MAC do we want to verify
|
||||
* @throws IllegalArgumentException for bad key
|
||||
*/
|
||||
public boolean verify(SessionKey key, byte curData[], int curOffset, int curLength, byte origMAC[], int origMACOffset, int origMACLength) {
|
||||
public boolean verify(SessionKey key, byte curData[], int curOffset, int curLength,
|
||||
byte origMAC[], int origMACOffset, int origMACLength) {
|
||||
if ((key == null) || (key.getData() == null) || (curData == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
|
||||
@@ -116,7 +122,12 @@ public class HMACGenerator {
|
||||
// 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);
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
return new I2PHMac(md, 32);
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
throw new UnsupportedOperationException("MD5");
|
||||
}
|
||||
}
|
||||
|
||||
private void release(I2PHMac mac) {
|
||||
@@ -124,15 +135,15 @@ public class HMACGenerator {
|
||||
}
|
||||
|
||||
/**
|
||||
* Not really tmp, just from the byte array cache.
|
||||
* 32 bytes from the byte array cache.
|
||||
* Does NOT zero.
|
||||
*/
|
||||
private byte[] acquireTmp() {
|
||||
protected byte[] acquireTmp() {
|
||||
byte rv[] = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
||||
return rv;
|
||||
}
|
||||
|
||||
private void releaseTmp(byte tmp[]) {
|
||||
protected void releaseTmp(byte tmp[]) {
|
||||
SimpleByteCache.release(tmp);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.SessionKey;
|
||||
|
||||
//import org.bouncycastle.oldcrypto.digests.MD5Digest;
|
||||
|
||||
/**
|
||||
* Manage both plaintext and salted/hashed password storage in
|
||||
* router.config.
|
||||
@@ -197,4 +199,43 @@ public class PasswordManager {
|
||||
} catch (NoSuchAlgorithmException nsae) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* speed/comparison test before removing BC version;
|
||||
* JVM was slightly faster
|
||||
*/
|
||||
/*****
|
||||
public static void main(String[] args) {
|
||||
RandomSource rand = RandomSource.getInstance();
|
||||
byte[] d = new byte[1500];
|
||||
MD5Digest md = new MD5Digest();
|
||||
byte[] bc = new byte[16];
|
||||
// warmup and comparison
|
||||
int runs = 25000;
|
||||
for (int i = 0; i < runs; i++) {
|
||||
rand.nextBytes(d);
|
||||
byte[] jvm = md5Sum(d);
|
||||
md.update(d, 0, d.length);
|
||||
md.doFinal(bc, 0);
|
||||
if (!DataHelper.eq(jvm, bc))
|
||||
throw new IllegalStateException();
|
||||
md.reset();
|
||||
}
|
||||
|
||||
// real thing
|
||||
runs = 500000;
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < runs; i++) {
|
||||
md5Sum(d);
|
||||
}
|
||||
System.out.println("JVM " + (System.currentTimeMillis() - start));
|
||||
start = System.currentTimeMillis();
|
||||
for (int i = 0; i < runs; i++) {
|
||||
md.update(d, 0, d.length);
|
||||
md.doFinal(bc, 0);
|
||||
md.reset();
|
||||
}
|
||||
System.out.println("BC " + (System.currentTimeMillis() - start));
|
||||
}
|
||||
*****/
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user