diff --git a/src/freenet/client/Metadata.java b/src/freenet/client/Metadata.java index 2665bf7efdf..c8240087386 100644 --- a/src/freenet/client/Metadata.java +++ b/src/freenet/client/Metadata.java @@ -12,7 +12,6 @@ import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; -import java.security.MessageDigest; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; @@ -748,12 +747,10 @@ public static byte[] getCryptoKey(HashResult[] hashes) { public static byte[] getCryptoKey(byte[] hash) { // This is exactly the same algorithm used by e.g. JFK for generating multiple session keys from a single generated value. // The only difference is we use a constant of more than one byte's length here, to avoid having to keep a registry. - MessageDigest md = SHA256.getMessageDigest(); - md.update(hash); - md.update(SPLITKEY); - byte[] buf = md.digest(); - SHA256.returnMessageDigest(md); - return buf; + return SHA256.digest(md -> { + md.update(hash); + md.update(SPLITKEY); + }); } public static byte[] getCrossSegmentSeed(HashResult[] hashes, byte[] hashThisLayerOnly) { @@ -769,12 +766,10 @@ public static byte[] getCrossSegmentSeed(HashResult[] hashes, byte[] hashThisLay public static byte[] getCrossSegmentSeed(byte[] hash) { // This is exactly the same algorithm used by e.g. JFK for generating multiple session keys from a single generated value. // The only difference is we use a constant of more than one byte's length here, to avoid having to keep a registry. - MessageDigest md = SHA256.getMessageDigest(); - md.update(hash); - md.update(CROSS_SEGMENT_SEED); - byte[] buf = md.digest(); - SHA256.returnMessageDigest(md); - return buf; + return SHA256.digest(md -> { + md.update(hash); + md.update(CROSS_SEGMENT_SEED); + }); } /** diff --git a/src/freenet/client/async/KeyListenerTracker.java b/src/freenet/client/async/KeyListenerTracker.java index 72efa9769dd..ade72b005af 100644 --- a/src/freenet/client/async/KeyListenerTracker.java +++ b/src/freenet/client/async/KeyListenerTracker.java @@ -5,7 +5,6 @@ import static java.lang.String.format; -import java.security.MessageDigest; import java.util.Arrays; import java.util.ArrayList; import java.util.HashMap; @@ -20,7 +19,6 @@ import freenet.keys.KeyBlock; import freenet.keys.NodeSSK; import freenet.node.SendableGet; -import freenet.node.SendableRequest; import freenet.support.ByteArrayWrapper; import freenet.support.LogThresholdCallback; import freenet.support.Logger; @@ -464,14 +462,13 @@ public byte[] saltKey(Key key) { } private byte[] saltKey(byte[] key) { - if (isSSKScheduler) + if (isSSKScheduler) { return key; - MessageDigest md = SHA256.getMessageDigest(); - md.update(key); - md.update(globalSalt); - byte[] ret = md.digest(); - SHA256.returnMessageDigest(md); - return ret; + } + return SHA256.digest(md -> { + md.update(key); + md.update(globalSalt); + }); } protected void hintGlobalSalt(byte[] globalSalt2) { diff --git a/src/freenet/client/async/SplitFileFetcherKeyListener.java b/src/freenet/client/async/SplitFileFetcherKeyListener.java index 656b1a28e47..bd8f534b114 100644 --- a/src/freenet/client/async/SplitFileFetcherKeyListener.java +++ b/src/freenet/client/async/SplitFileFetcherKeyListener.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.security.MessageDigest; import freenet.client.FetchException; import freenet.client.FetchException.FetchExceptionMode; @@ -201,12 +200,10 @@ synchronized void finishedSetup() { } private byte[] localSaltKey(Key key) { - MessageDigest md = SHA256.getMessageDigest(); - md.update(key.getRoutingKey()); - md.update(localSalt); - byte[] ret = md.digest(); - SHA256.returnMessageDigest(md); - return ret; + return SHA256.digest(md -> { + md.update(key.getRoutingKey()); + md.update(localSalt); + }); } /** The segment bloom filters should only need to be written ONCE, and can all be written at diff --git a/src/freenet/crypt/SHA256.java b/src/freenet/crypt/SHA256.java index e68a8e0fa10..ab60caf4f0c 100644 --- a/src/freenet/crypt/SHA256.java +++ b/src/freenet/crypt/SHA256.java @@ -41,6 +41,7 @@ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING import java.security.Provider; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.Consumer; import org.tanukisoftware.wrapper.WrapperManager; @@ -119,10 +120,26 @@ public static void returnMessageDigest(MessageDigest md256) { } public static byte[] digest(byte[] data) { + return digest(md -> md.update(data)); + } + + public static byte[] digest(Consumer updater) { + MessageDigest md = null; + try { + md = getMessageDigest(); + updater.accept(md); + return md.digest(); + } finally { + returnMessageDigest(md); + } + } + + public static byte[] digest(InputStream is) throws IOException { MessageDigest md = null; try { md = getMessageDigest(); - return md.digest(data); + hash(is, md); + return md.digest(); } finally { returnMessageDigest(md); } diff --git a/src/freenet/keys/CHKBlock.java b/src/freenet/keys/CHKBlock.java index f3940e9322e..2f5cf301985 100644 --- a/src/freenet/keys/CHKBlock.java +++ b/src/freenet/keys/CHKBlock.java @@ -3,7 +3,6 @@ * http://www.gnu.org/ for further details of the GPL. */ package freenet.keys; -import java.security.MessageDigest; import java.util.Arrays; import freenet.crypt.SHA256; @@ -76,12 +75,12 @@ public CHKBlock(byte[] data2, byte[] header2, NodeCHK key, boolean verify, byte // Check the hash if(hashIdentifier != HASH_SHA256) throw new CHKVerifyException("Hash not SHA-256"); - MessageDigest md = SHA256.getMessageDigest(); - - md.update(headers); - md.update(data); - byte[] hash = md.digest(); - SHA256.returnMessageDigest(md); + + byte[] hash = SHA256.digest(md -> { + md.update(headers); + md.update(data); + }); + if(key == null) { chk = new NodeCHK(hash, cryptoAlgorithm); } else { diff --git a/src/freenet/keys/ClientCHKBlock.java b/src/freenet/keys/ClientCHKBlock.java index a9f57c6f9ba..ede58b0ef44 100644 --- a/src/freenet/keys/ClientCHKBlock.java +++ b/src/freenet/keys/ClientCHKBlock.java @@ -138,11 +138,10 @@ public Bucket decodeOld(BucketFactory bf, int maxLength, boolean dontCompress) t // Decipher header first - functions as IV pcfb.blockDecipher(hbuf, 0, hbuf.length); pcfb.blockDecipher(dbuf, 0, dbuf.length); - MessageDigest md256 = SHA256.getMessageDigest(); byte[] dkey = key.cryptoKey; // Check: IV == hash of decryption key - byte[] predIV = md256.digest(dkey); - SHA256.returnMessageDigest(md256); md256 = null; + byte[] predIV = SHA256.digest(dkey); + // Extract the IV byte[] iv = Arrays.copyOf(hbuf, 32); if(!Arrays.equals(iv, predIV)) diff --git a/src/freenet/keys/ClientKSK.java b/src/freenet/keys/ClientKSK.java index 0326b29a713..f21c14ea57d 100644 --- a/src/freenet/keys/ClientKSK.java +++ b/src/freenet/keys/ClientKSK.java @@ -7,6 +7,7 @@ * requested and inserted. */ import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import freenet.support.math.MersenneTwister; @@ -43,25 +44,15 @@ public static InsertableClientSSK create(FreenetURI uri) { } public static ClientKSK create(String keyword) { - MessageDigest md256 = SHA256.getMessageDigest(); + byte[] keywordHash = SHA256.digest(keyword.getBytes(StandardCharsets.UTF_8)); + MersenneTwister mt = new MersenneTwister(keywordHash); + DSAPrivateKey privKey = new DSAPrivateKey(Global.DSAgroupBigA, mt); + DSAPublicKey pubKey = new DSAPublicKey(Global.DSAgroupBigA, privKey); + byte[] pubKeyHash = SHA256.digest(pubKey.asBytes()); try { - byte[] keywordHash; - try { - keywordHash = md256.digest(keyword.getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new Error("Impossible: JVM doesn't support UTF-8: " + e, e); - } - MersenneTwister mt = new MersenneTwister(keywordHash); - DSAPrivateKey privKey = new DSAPrivateKey(Global.DSAgroupBigA, mt); - DSAPublicKey pubKey = new DSAPublicKey(Global.DSAgroupBigA, privKey); - byte[] pubKeyHash = md256.digest(pubKey.asBytes()); - try { - return new ClientKSK(keyword, pubKeyHash, pubKey, privKey, keywordHash); - } catch (MalformedURLException e) { - throw new Error(e); - } - } finally { - SHA256.returnMessageDigest(md256); + return new ClientKSK(keyword, pubKeyHash, pubKey, privKey, keywordHash); + } catch (MalformedURLException e) { + throw new Error(e); } } diff --git a/src/freenet/keys/ClientSSK.java b/src/freenet/keys/ClientSSK.java index f060f618cb0..e07b05ff4e5 100644 --- a/src/freenet/keys/ClientSSK.java +++ b/src/freenet/keys/ClientSSK.java @@ -5,6 +5,7 @@ import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Arrays; @@ -73,33 +74,26 @@ public ClientSSK(String docName, byte[] pubKeyHash, byte[] extras, DSAPublicKey throw new MalformedURLException("Pubkey hash wrong length: "+pubKeyHash.length+" should be "+NodeSSK.PUBKEY_HASH_SIZE); if(cryptoKey.length != CRYPTO_KEY_LENGTH) throw new MalformedURLException("Decryption key wrong length: "+cryptoKey.length+" should be "+CRYPTO_KEY_LENGTH); - MessageDigest md = SHA256.getMessageDigest(); - try { - if (pubKey != null) { - byte[] pubKeyAsBytes = pubKey.asBytes(); - md.update(pubKeyAsBytes); - byte[] otherPubKeyHash = md.digest(); - if (!Arrays.equals(otherPubKeyHash, pubKeyHash)) - throw new IllegalArgumentException(); - } - this.cryptoKey = cryptoKey; - try { - md.update(docName.getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new Error("Impossible: JVM doesn't support UTF-8: " + e, e); - } - byte[] buf = md.digest(); - try { - Rijndael aes = new Rijndael(256, 256); - aes.initialize(cryptoKey); - aes.encipher(buf, buf); - ehDocname = buf; - } catch (UnsupportedCipherException e) { - throw new Error(e); + + if (pubKey != null) { + byte[] otherPubKeyHash = SHA256.digest(pubKey.asBytes()); + if (!Arrays.equals(otherPubKeyHash, pubKeyHash)) { + throw new IllegalArgumentException(); } - } finally { - SHA256.returnMessageDigest(md); } + + this.cryptoKey = cryptoKey; + + byte[] buf = SHA256.digest(docName.getBytes(StandardCharsets.UTF_8)); + try { + Rijndael aes = new Rijndael(256, 256); + aes.initialize(cryptoKey); + aes.encipher(buf, buf); + ehDocname = buf; + } catch (UnsupportedCipherException e) { + throw new Error(e); + } + if(ehDocname == null) throw new NullPointerException(); hashCode = Fields.hashCode(pubKeyHash) ^ Fields.hashCode(cryptoKey) ^ Fields.hashCode(ehDocname) ^ docName.hashCode(); diff --git a/src/freenet/keys/Key.java b/src/freenet/keys/Key.java index 18011111272..f57a1d32ca7 100644 --- a/src/freenet/keys/Key.java +++ b/src/freenet/keys/Key.java @@ -8,7 +8,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.security.MessageDigest; import java.util.Arrays; import freenet.crypt.CryptFormatException; @@ -126,17 +125,22 @@ public static KeyBlock createBlock(short keyType, byte[] keyBytes, byte[] header * make chosen-key attacks harder. */ public synchronized double toNormalizedDouble() { - if(cachedNormalizedDouble > 0) return cachedNormalizedDouble; - MessageDigest md = SHA256.getMessageDigest(); - if(routingKey == null) throw new NullPointerException(); - md.update(routingKey); - int TYPE = getType(); - md.update((byte)(TYPE >> 8)); - md.update((byte)TYPE); - byte[] digest = md.digest(); - SHA256.returnMessageDigest(md); md = null; - cachedNormalizedDouble = Util.keyDigestAsNormalizedDouble(digest); + if (cachedNormalizedDouble > 0) { return cachedNormalizedDouble; + } + if (routingKey == null) { + throw new NullPointerException(); + } + + byte[] digest = SHA256.digest(md -> { + md.update(routingKey); + int TYPE = getType(); + md.update((byte) (TYPE >> 8)); + md.update((byte) TYPE); + }); + + cachedNormalizedDouble = Util.keyDigestAsNormalizedDouble(digest); + return cachedNormalizedDouble; } /** diff --git a/src/freenet/keys/NodeSSK.java b/src/freenet/keys/NodeSSK.java index e5ed87e2277..01ed4ad1079 100644 --- a/src/freenet/keys/NodeSSK.java +++ b/src/freenet/keys/NodeSSK.java @@ -104,12 +104,10 @@ public Key cloneKey() { // routingKey = H( E(H(docname)) + H(pubkey) ) private static byte[] makeRoutingKey(byte[] pkHash, byte[] ehDocname) { - MessageDigest md256 = SHA256.getMessageDigest(); - md256.update(ehDocname); - md256.update(pkHash); - byte[] key = md256.digest(); - SHA256.returnMessageDigest(md256); - return key; + return SHA256.digest(md -> { + md.update(ehDocname); + md.update(pkHash); + }); } @Override diff --git a/src/freenet/node/FNPPacketMangler.java b/src/freenet/node/FNPPacketMangler.java index 9575926a110..1c4989a0619 100644 --- a/src/freenet/node/FNPPacketMangler.java +++ b/src/freenet/node/FNPPacketMangler.java @@ -6,6 +6,7 @@ import java.io.File; import java.io.UnsupportedEncodingException; import java.net.InetAddress; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.interfaces.ECPublicKey; import java.util.Arrays; @@ -1766,31 +1767,21 @@ public void run() { } private int getInitialMessageID(byte[] identity) { - MessageDigest md = SHA256.getMessageDigest(); - md.update(identity); - // Similar to JFK keygen, should be safe enough. - try { - md.update("INITIAL0".getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new Error(e); - } - byte[] hashed = md.digest(); - SHA256.returnMessageDigest(md); + byte[] hashed = SHA256.digest(md -> { + md.update(identity); + // Similar to JFK keygen, should be safe enough. + md.update("INITIAL0".getBytes(StandardCharsets.UTF_8)); + }); return Fields.bytesToInt(hashed, 0); } private int getInitialMessageID(byte[] identity, byte[] otherIdentity) { - MessageDigest md = SHA256.getMessageDigest(); - md.update(identity); - md.update(otherIdentity); - // Similar to JFK keygen, should be safe enough. - try { - md.update("INITIAL1".getBytes("UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new Error(e); - } - byte[] hashed = md.digest(); - SHA256.returnMessageDigest(md); + byte[] hashed = SHA256.digest(md -> { + md.update(identity); + md.update(otherIdentity); + // Similar to JFK keygen, should be safe enough. + md.update("INITIAL1".getBytes(StandardCharsets.UTF_8)); + }); return Fields.bytesToInt(hashed, 0); } diff --git a/src/freenet/node/LocationManager.java b/src/freenet/node/LocationManager.java index c6d439a5465..547ea6362d6 100644 --- a/src/freenet/node/LocationManager.java +++ b/src/freenet/node/LocationManager.java @@ -633,10 +633,10 @@ public void run() { // Now decode it long[] hisBufLong = Fields.bytesToLongs(hisBuf); - if(hisBufLong.length < 2) { - Logger.error(this, "Bad buffer length (no random, no location)- malicious node? on "+uid); - return; - } + if(hisBufLong.length < 2) { + Logger.error(this, "Bad buffer length (no random, no location)- malicious node? on "+uid); + return; + } long hisRandom = hisBufLong[0]; @@ -694,10 +694,10 @@ public void run() { node.writeNodeFile(); } - SHA256.returnMessageDigest(md); } catch (Throwable t) { Logger.error(this, "Caught "+t, t); } finally { + SHA256.returnMessageDigest(md); unlock(reachedEnd); // we only count the time taken by our outgoing swap requests removeRecentlyForwardedItem(item); } diff --git a/src/freenet/node/MasterKeys.java b/src/freenet/node/MasterKeys.java index bd084ecbb99..181d820286c 100644 --- a/src/freenet/node/MasterKeys.java +++ b/src/freenet/node/MasterKeys.java @@ -11,6 +11,7 @@ import java.io.IOException; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Arrays; import java.util.Random; @@ -197,11 +198,12 @@ private static MasterKeys readOldFormat(DataInputStream dis, int length, Random byte[] dataAndHash = new byte[length - salt.length - iv.length]; dis.readFully(dataAndHash); // System.err.println("Data and hash: "+HexUtil.bytesToHex(dataAndHash)); - byte[] pwd = password.getBytes("UTF-8"); - MessageDigest md = SHA256.getMessageDigest(); - md.update(pwd); - md.update(salt); - byte[] outerKey = md.digest(); + byte[] pwd = password.getBytes(StandardCharsets.UTF_8); + byte[] outerKey = SHA256.digest(md -> { + md.update(pwd); + md.update(salt); + }); + BlockCipher cipher; try { cipher = new Rijndael(256, 256); @@ -219,7 +221,7 @@ private static MasterKeys readOldFormat(DataInputStream dis, int length, Random // System.err.println("Data: "+HexUtil.bytesToHex(data)); // System.err.println("Hash: "+HexUtil.bytesToHex(hash)); clear(dataAndHash); - byte[] checkHash = md.digest(data); + byte[] checkHash = SHA256.digest(data); // System.err.println("Check hash: "+HexUtil.bytesToHex(checkHash)); if(!Fields.byteArrayEqual(checkHash, hash, 0, 0, OLD_HASH_LENGTH)) { clear(data); @@ -247,7 +249,6 @@ private static MasterKeys readOldFormat(DataInputStream dis, int length, Random MasterKeys ret = new MasterKeys(clientCacheKey, databaseKey, tempfilesMasterSecret, flags); clear(data); clear(hash); - SHA256.returnMessageDigest(md); return ret; } diff --git a/src/freenet/node/updater/MainJarDependenciesChecker.java b/src/freenet/node/updater/MainJarDependenciesChecker.java index 77cd505200a..9ecce637bb4 100644 --- a/src/freenet/node/updater/MainJarDependenciesChecker.java +++ b/src/freenet/node/updater/MainJarDependenciesChecker.java @@ -21,7 +21,6 @@ import java.io.UnsupportedEncodingException; import java.io.Writer; import java.net.MalformedURLException; -import java.security.MessageDigest; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1670,31 +1669,25 @@ public static boolean validFile(File filename, byte[] expectedHash, long size, b System.out.println("File exists while updating but length is wrong ("+filename.length()+" should be "+size+") for "+filename); return false; } - FileInputStream fis = null; try { - fis = new FileInputStream(filename); - MessageDigest md = SHA256.getMessageDigest(); - SHA256.hash(fis, md); - byte[] hash = md.digest(); - SHA256.returnMessageDigest(md); - fis.close(); - fis = null; - if(Arrays.equals(hash, expectedHash)) { - if(executable && !filename.canExecute()) { - filename.setExecutable(true); - } - return true; - } else { + byte[] hash; + try (FileInputStream fis = new FileInputStream(filename)) { + hash = SHA256.digest(fis); + } + if (!Arrays.equals(hash, expectedHash)) { return false; } + if(executable && !filename.canExecute()) { + filename.setExecutable(true); + } + return true; + } catch (FileNotFoundException e) { Logger.error(MainJarDependencies.class, "File not found: "+filename); return false; } catch (IOException e) { System.err.println("Unable to read "+filename+" for updater"); return false; - } finally { - Closer.close(fis); } } diff --git a/src/freenet/store/saltedhash/CipherManager.java b/src/freenet/store/saltedhash/CipherManager.java index 1ffddf4c4ff..9341bb8dbf8 100644 --- a/src/freenet/store/saltedhash/CipherManager.java +++ b/src/freenet/store/saltedhash/CipherManager.java @@ -3,7 +3,6 @@ * http://www.gnu.org/ for further details of the GPL. */ package freenet.store.saltedhash; -import java.security.MessageDigest; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; @@ -54,8 +53,7 @@ byte[] getDiskSalt() { /** * Cache for digested keys */ - @SuppressWarnings("serial") - private Map digestRoutingKeyCache = new LinkedHashMap() { + private final Map digestRoutingKeyCache = new LinkedHashMap() { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > 128; @@ -76,22 +74,17 @@ byte[] getDigestedKey(byte[] plainKey) { return dk; } - MessageDigest digest = SHA256.getMessageDigest(); - try { - digest.update(plainKey); - digest.update(salt); - - byte[] hashedRoutingKey = digest.digest(); - assert hashedRoutingKey.length == 0x20; - - synchronized (digestRoutingKeyCache) { - digestRoutingKeyCache.put(key, hashedRoutingKey); - } + byte[] hashedRoutingKey = SHA256.digest(md -> { + md.update(plainKey); + md.update(salt); + }); + assert hashedRoutingKey.length == 0x20; - return hashedRoutingKey; - } finally { - SHA256.returnMessageDigest(digest); + synchronized (digestRoutingKeyCache) { + digestRoutingKeyCache.put(key, hashedRoutingKey); } + + return hashedRoutingKey; } /**