Crypto: Cache HMAC256 instances

Appx. 38% speedup
As suggested by jogger
This commit is contained in:
zzz
2020-09-09 21:13:37 +00:00
parent e466331407
commit 2fbbd8e7d1
2 changed files with 57 additions and 16 deletions

View File

@ -102,7 +102,8 @@ public final class HKDF {
try { try {
// here we use Java Mac directly rather than // here we use Java Mac directly rather than
// HMAC256Generator for efficiency. // HMAC256Generator for efficiency.
Mac mac = Mac.getInstance("HmacSHA256"); HMAC256Generator hmac = _context.hmac256();
Mac mac = hmac.acquire();
// SecretKeySpec copies the data, HMACKey doesn't // SecretKeySpec copies the data, HMACKey doesn't
SecretKey keyObj = new HMACKey(key); SecretKey keyObj = new HMACKey(key);
mac.init(keyObj); mac.init(keyObj);
@ -125,8 +126,7 @@ public final class HKDF {
mac.update(out, 0, 32); mac.update(out, 0, 32);
mac.update(tmp, 0, ilen + 1); mac.update(tmp, 0, ilen + 1);
mac.doFinal(out2, off2); mac.doFinal(out2, off2);
} catch (NoSuchAlgorithmException e) { hmac.release(mac);
throw new UnsupportedOperationException("HmacSHA256", e);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new IllegalArgumentException("HmacSHA256", e); throw new IllegalArgumentException("HmacSHA256", e);
} finally { } finally {

View File

@ -4,6 +4,7 @@ import java.security.GeneralSecurityException;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.LinkedBlockingQueue;
import javax.crypto.Mac; import javax.crypto.Mac;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
@ -23,10 +24,19 @@ import net.i2p.data.SessionKey;
*/ */
public final class HMAC256Generator extends HMACGenerator { public final class HMAC256Generator extends HMACGenerator {
private final LinkedBlockingQueue<Mac> _macs;
private static final boolean CACHE = true;
private static final int CACHE_SIZE = 8;
private static final SecretKey ZERO_KEY = new HMACKey(new byte[32]);
/** /**
* @param context unused * @param context unused
*/ */
public HMAC256Generator(I2PAppContext context) { super(); } public HMAC256Generator(I2PAppContext context) {
super();
_macs = new LinkedBlockingQueue<Mac>(CACHE_SIZE);
}
/** /**
* Calculate the HMAC of the data with the given key. * Calculate the HMAC of the data with the given key.
@ -52,13 +62,12 @@ public final class HMAC256Generator extends HMACGenerator {
*/ */
public void calculate(byte[] key, byte data[], int offset, int length, byte target[], int targetOffset) { public void calculate(byte[] key, byte data[], int offset, int length, byte target[], int targetOffset) {
try { try {
Mac mac = Mac.getInstance("HmacSHA256"); Mac mac = acquire();
SecretKey keyObj = new HMACKey(key); SecretKey keyObj = new HMACKey(key);
mac.init(keyObj); mac.init(keyObj);
mac.update(data, offset, length); mac.update(data, offset, length);
mac.doFinal(target, targetOffset); mac.doFinal(target, targetOffset);
} catch (NoSuchAlgorithmException e) { release(mac);
throw new UnsupportedOperationException("HmacSHA256", e);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new IllegalArgumentException("HmacSHA256", e); throw new IllegalArgumentException("HmacSHA256", e);
} }
@ -86,6 +95,41 @@ public final class HMAC256Generator extends HMACGenerator {
return eq; return eq;
} }
/**
* Package private for HKDF.
*
* @return cached or Mac.getInstance("HmacSHA256")
* @since 0.9.48
*/
Mac acquire() {
Mac rv = _macs.poll();
if (rv == null) {
try {
rv = Mac.getInstance("HmacSHA256");
} catch (NoSuchAlgorithmException e) {
throw new UnsupportedOperationException("HmacSHA256", e);
}
}
return rv;
}
/**
* Mac will be reset and initialized with a zero key.
* Package private for HKDF.
*
* @since 0.9.48
*/
void release(Mac mac) {
if (CACHE) {
try {
mac.init(ZERO_KEY);
} catch (GeneralSecurityException e) {
return;
}
_macs.offer(mac);
}
}
/** /**
* Like SecretKeySpec but doesn't copy the key in the construtor, for speed. * Like SecretKeySpec but doesn't copy the key in the construtor, for speed.
* It still returns a copy in getEncoded(), because Mac relies * It still returns a copy in getEncoded(), because Mac relies
@ -183,7 +227,8 @@ public final class HMAC256Generator extends HMACGenerator {
// real thing // real thing
System.out.println("Passed"); System.out.println("Passed");
System.out.println("BC Test:"); CACHE = false;
System.out.println("Without Cache Test:");
RUNS = 1000000; RUNS = 1000000;
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
for (int i = 0; i < RUNS; i++) { for (int i = 0; i < RUNS; i++) {
@ -193,15 +238,11 @@ public final class HMAC256Generator extends HMACGenerator {
System.out.println("Time for " + RUNS + " HMAC-SHA256 computations:"); System.out.println("Time for " + RUNS + " HMAC-SHA256 computations:");
System.out.println("BC time (ms): " + time); System.out.println("BC time (ms): " + time);
System.out.println("JVM Test:"); CACHE = true;
System.out.println("With Cache Test:");
start = System.currentTimeMillis(); start = System.currentTimeMillis();
for (int i = 0; i < RUNS; i++) { for (int i = 0; i < RUNS; i++) {
try { gen.calculate(rand, data, 0, data.length, result, 0);
mac.init(keyObj);
} catch (GeneralSecurityException e) {
System.err.println("Fatal: " + e);
}
byte[] sha = mac.doFinal(data);
} }
time = System.currentTimeMillis() - start; time = System.currentTimeMillis() - start;