diff --git a/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java b/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java index 5889db89ec3a91f03dd54edc173e164fd07c907d..28677addaf2d361a291eee0590ef0187558feb39 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java +++ b/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java @@ -24,7 +24,6 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -401,7 +400,7 @@ public class MetaInfo ****/ private boolean fast_checkPiece(int piece, byte[] bs, int off, int length) { - SHA1 sha1 = new SHA1(); + MessageDigest sha1 = SHA1.getInstance(); sha1.update(bs, off, length); byte[] hash = sha1.digest(); @@ -519,18 +518,11 @@ public class MetaInfo } byte[] infoBytes = BEncoder.bencode(info); //_log.debug("info bencoded: [" + Base64.encode(infoBytes, true) + "]"); - try - { - MessageDigest digest = MessageDigest.getInstance("SHA"); + MessageDigest digest = SHA1.getInstance(); byte hash[] = digest.digest(infoBytes); if (_log.shouldLog(Log.DEBUG)) _log.debug("info hash: " + I2PSnarkUtil.toHex(hash)); return hash; - } - catch(NoSuchAlgorithmException nsa) - { - throw new InternalError(nsa.toString()); - } } /** @since 0.8.5 */ diff --git a/apps/i2psnark/java/src/org/klomp/snark/Storage.java b/apps/i2psnark/java/src/org/klomp/snark/Storage.java index 1183e9bf352aa19ccee9652b9ab3bde398da4f5a..9c8f02cc4b25f477893253717406782fd830ed13 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Storage.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Storage.java @@ -23,6 +23,7 @@ package org.klomp.snark; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -165,7 +166,7 @@ public class Storage */ private byte[] fast_digestCreate() throws IOException { // Calculate piece_hashes - SHA1 digest = new SHA1(); + MessageDigest digest = SHA1.getInstance(); byte[] piece_hashes = new byte[20 * pieces]; diff --git a/core/java/src/net/i2p/crypto/DSAEngine.java b/core/java/src/net/i2p/crypto/DSAEngine.java index d09d58ce16f7f6e15dfad91023d00a4c1eeb8493..e59e56343568a175ae96967313d97d2b109ac8f1 100644 --- a/core/java/src/net/i2p/crypto/DSAEngine.java +++ b/core/java/src/net/i2p/crypto/DSAEngine.java @@ -32,6 +32,7 @@ package net.i2p.crypto; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; +import java.security.MessageDigest; import net.i2p.I2PAppContext; import net.i2p.data.Hash; @@ -228,25 +229,25 @@ public class DSAEngine { /** @return hash SHA-1 hash, NOT a SHA-256 hash */ public SHA1Hash calculateHash(InputStream in) { - SHA1 digest = new SHA1(); + MessageDigest digest = SHA1.getInstance(); byte buf[] = new byte[64]; int read = 0; try { while ( (read = in.read(buf)) != -1) { - digest.engineUpdate(buf, 0, read); + digest.update(buf, 0, read); } } catch (IOException ioe) { if (_log.shouldLog(Log.WARN)) _log.warn("Unable to hash the stream", ioe); return null; } - return new SHA1Hash(digest.engineDigest()); + return new SHA1Hash(digest.digest()); } /** @return hash SHA-1 hash, NOT a SHA-256 hash */ public static SHA1Hash calculateHash(byte[] source, int offset, int len) { - SHA1 h = new SHA1(); - h.engineUpdate(source, offset, len); + MessageDigest h = SHA1.getInstance(); + h.update(source, offset, len); byte digested[] = h.digest(); return new SHA1Hash(digested); } diff --git a/core/java/src/net/i2p/crypto/SHA1.java b/core/java/src/net/i2p/crypto/SHA1.java index 6dbdee074bae5567607c70b86d315f7b28475ddc..c8c54306da51ffba55c6f671ceb639dc7975373a 100644 --- a/core/java/src/net/i2p/crypto/SHA1.java +++ b/core/java/src/net/i2p/crypto/SHA1.java @@ -15,10 +15,17 @@ package net.i2p.crypto; * put("MessageDigest.SHA-1", "com.bitzi.util.SHA1"); */ //package com.bitzi.util; + import java.security.DigestException; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import net.i2p.data.Base64; + /** + * NOTE: As of 0.8.7, use getInstance() instead of new SHA1(), which will + * return the JVM's MessageDigest if it is faster. + * * <p>The FIPS PUB 180-2 standard specifies four secure hash algorithms (SHA-1, * SHA-256, SHA-384 and SHA-512) for computing a condensed representation of * electronic data (message). When a message of any length < 2^^64 bits (for @@ -85,8 +92,27 @@ public final class SHA1 extends MessageDigest implements Cloneable { */ private int hA, hB, hC, hD, hE; + private static final boolean _useBitzi; + static { + // oddly, Bitzi is faster than Oracle - see test results below + boolean useBitzi = true; + String vendor = System.getProperty("java.vendor"); + if (vendor.startsWith("Apache") || // Harmony + vendor.startsWith("GNU Classpath") || // JamVM + vendor.startsWith("Free Software Foundation")) { // gij + try { + MessageDigest.getInstance("SHA-1"); + useBitzi = false; + } catch (NoSuchAlgorithmException e) {} + } + //if (useBitzi) + // System.out.println("INFO: Using Bitzi SHA-1"); + _useBitzi = useBitzi; + } + /** * Creates a SHA1 object with default initial state. + * NOTE: Use getInstance() to get the fastest implementation. */ public SHA1() { super("SHA-1"); @@ -94,6 +120,19 @@ public final class SHA1 extends MessageDigest implements Cloneable { init(); } + /** + * @return the fastest digest, either new SHA1() or MessageDigest.getInstance("SHA-1") + * @since 0.8.7 + */ + public static MessageDigest getInstance() { + if (!_useBitzi) { + try { + return MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) {} + } + return new SHA1(); + } + /** * Clones this object. */ @@ -699,4 +738,63 @@ public final class SHA1 extends MessageDigest implements Cloneable { hD += d; hC += /* c= */ (c << 30) | (c >>> 2); } + + private static final int RUNS = 100000; + + /** + * Test the GNU and the JVM's implementations for speed + * + * Results: 2011-05 eeepc Atom + * <pre> + * JVM strlen GNU ms JVM ms + * Oracle 387 1406 2357 + * Oracle 40 522 475 + * Harmony 387 5504 3474 + * Harmony 40 4396 1593 + * JamVM 387 25578 21966 + * JamVM 40 5380 4195 + * gij 387 47225 3501 + * gij 40 9861 919 + * </pre> + * + * @since 0.8.7 + */ + public static void main(String args[]) { + if (args.length <= 0) { + System.err.println("Usage: SHA1 string"); + return; + } + + byte[] data = args[0].getBytes(); + SHA1 gnu = new SHA1(); + long start = System.currentTimeMillis(); + for (int i = 0; i < RUNS; i++) { + gnu.update(data, 0, data.length); + byte[] sha = gnu.digest(); + if (i == 0) + System.out.println("SHA1 [" + args[0] + "] = [" + Base64.encode(sha) + "]"); + gnu.reset(); + } + long time = System.currentTimeMillis() - start; + System.out.println("Time for " + RUNS + " SHA-256 computations:"); + System.out.println("GNU time (ms): " + time); + + start = System.currentTimeMillis(); + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + System.err.println("Fatal: " + e); + return; + } + for (int i = 0; i < RUNS; i++) { + md.reset(); + byte[] sha = md.digest(data); + if (i == 0) + System.out.println("SHA1 [" + args[0] + "] = [" + Base64.encode(sha) + "]"); + } + time = System.currentTimeMillis() - start; + + System.out.println("JVM time (ms): " + time); + } } diff --git a/core/java/src/net/i2p/crypto/SHA256Generator.java b/core/java/src/net/i2p/crypto/SHA256Generator.java index 87955646beff602b64caf979a24229882ee9062d..a62f2129b87db3c109f0452221e7dadba31a7677 100644 --- a/core/java/src/net/i2p/crypto/SHA256Generator.java +++ b/core/java/src/net/i2p/crypto/SHA256Generator.java @@ -2,6 +2,8 @@ package net.i2p.crypto; import gnu.crypto.hash.Sha256Standalone; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.concurrent.LinkedBlockingQueue; import net.i2p.I2PAppContext; @@ -9,14 +11,29 @@ import net.i2p.data.Base64; import net.i2p.data.Hash; /** - * Defines a wrapper for SHA-256 operation. All the good stuff occurs - * in the GNU-Crypto {@link gnu.crypto.hash.Sha256Standalone} + * Defines a wrapper for SHA-256 operation. * + * As of release 0.8.7, uses java.security.MessageDigest by default. + * If that is unavailable, it uses + * GNU-Crypto {@link gnu.crypto.hash.Sha256Standalone} */ public final class SHA256Generator { - private final LinkedBlockingQueue<Sha256Standalone> _digestsGnu; + private final LinkedBlockingQueue<MessageDigest> _digests; + + private static final boolean _useGnu; + static { + boolean useGnu = false; + try { + MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + useGnu = true; + System.out.println("INFO: Using GNU SHA-256"); + } + _useGnu = useGnu; + } + public SHA256Generator(I2PAppContext context) { - _digestsGnu = new LinkedBlockingQueue(32); + _digests = new LinkedBlockingQueue(32); } public static final SHA256Generator getInstance() { @@ -36,10 +53,10 @@ public final class SHA256Generator { * Calculate the hash and cache the result. */ public final Hash calculateHash(byte[] source, int start, int len) { - Sha256Standalone digest = acquireGnu(); + MessageDigest digest = acquire(); digest.update(source, start, len); byte rv[] = digest.digest(); - releaseGnu(digest); + release(digest); //return new Hash(rv); return Hash.create(rv); } @@ -47,31 +64,121 @@ public final class SHA256Generator { /** * Use this if you only need the data, not a Hash object. * Does not cache. + * @param out needs 32 bytes starting at outOffset */ public final void calculateHash(byte[] source, int start, int len, byte out[], int outOffset) { - Sha256Standalone digest = acquireGnu(); + MessageDigest digest = acquire(); digest.update(source, start, len); byte rv[] = digest.digest(); - releaseGnu(digest); + release(digest); System.arraycopy(rv, 0, out, outOffset, rv.length); } - private Sha256Standalone acquireGnu() { - Sha256Standalone rv = _digestsGnu.poll(); + private MessageDigest acquire() { + MessageDigest rv = _digests.poll(); if (rv != null) rv.reset(); else - rv = new Sha256Standalone(); + rv = getDigestInstance(); return rv; } - private void releaseGnu(Sha256Standalone digest) { - _digestsGnu.offer(digest); + private void release(MessageDigest digest) { + _digests.offer(digest); } + private static MessageDigest getDigestInstance() { + if (!_useGnu) { + try { + return MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) {} + } + return new GnuMessageDigest(); + } + + /** + * Wrapper to make Sha256Standalone a MessageDigest + * @since 0.8.7 + */ + private static class GnuMessageDigest extends MessageDigest { + private final Sha256Standalone _gnu; + + protected GnuMessageDigest() { + super("SHA-256"); + _gnu = new Sha256Standalone(); + } + + protected byte[] engineDigest() { + return _gnu.digest(); + } + + protected void engineReset() { + _gnu.reset(); + } + + protected void engineUpdate(byte input) { + _gnu.update(input); + } + + protected void engineUpdate(byte[] input, int offset, int len) { + _gnu.update(input, offset, len); + } + } + + private static final int RUNS = 100000; + + /** + * Test the GNU and the JVM's implementations for speed + * + * Results: 2011-05 eeepc Atom + * <pre> + * JVM strlen GNU ms JVM ms + * Oracle 387 3861 3565 + * Oracle 40 825 635 + * Harmony 387 8082 5158 + * Harmony 40 4137 1753 + * JamVM 387 36301 34100 + * JamVM 40 7022 6016 + * gij 387 125833 4342 + * gij 40 22417 988 + * </pre> + */ public static void main(String args[]) { - I2PAppContext ctx = I2PAppContext.getGlobalContext(); - for (int i = 0; i < args.length; i++) - System.out.println("SHA256 [" + args[i] + "] = [" + Base64.encode(ctx.sha().calculateHash(args[i].getBytes()).getData()) + "]"); + if (args.length <= 0) { + System.err.println("Usage: SHA256Generator string"); + return; + } + + byte[] data = args[0].getBytes(); + Sha256Standalone gnu = new Sha256Standalone(); + long start = System.currentTimeMillis(); + for (int i = 0; i < RUNS; i++) { + gnu.update(data, 0, data.length); + byte[] sha = gnu.digest(); + if (i == 0) + System.out.println("SHA256 [" + args[0] + "] = [" + Base64.encode(sha) + "]"); + gnu.reset(); + } + long time = System.currentTimeMillis() - start; + System.out.println("Time for " + RUNS + " SHA-256 computations:"); + System.out.println("GNU time (ms): " + time); + + start = System.currentTimeMillis(); + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + System.err.println("Fatal: " + e); + return; + } + for (int i = 0; i < RUNS; i++) { + md.reset(); + byte[] sha = md.digest(data); + if (i == 0) + System.out.println("SHA256 [" + args[0] + "] = [" + Base64.encode(sha) + "]"); + } + time = System.currentTimeMillis() - start; + + System.out.println("JVM time (ms): " + time); } } diff --git a/history.txt b/history.txt index 941f5b433ad4437f361bb655b26468f863c0bfe0..5e4612090c115bbfb6e2d47229beb4c553bda3c6 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,14 @@ +2011-06-01 zzz + * Crypto: + - Use java.security.MessageDigest instead of bundled GNU SHA-256 code + if available, which it should always be. + 5 to 20% faster on Oracle JVM; 40 to 60% on Harmony; + 5 to 15% on JamVM; 20x (!) on GIJ. + - Use java.security.MessageDigest instead of bundled Bitzi SHA-1 code + if available on non-Oracle JVMs, which it should always be. + Not faster on Oracle JVM; 30 to 60% faster on Harmony; + 15 to 20% on JamVM; 10-15x (!) on GIJ. + 2011-06-01 sponge * ConfigClients stopClient stubbed out. diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 31c35f8c3e7f27dae7384d36e3443978e8ab5e40..ea17ad55f3270be34b24fd56bd0b1495da601896 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 14; + public final static long BUILD = 15; /** for example "-test" */ public final static String EXTRA = "";