From 18cbf3d253e0a938bfd19cf7c09f48a50ff5537b Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 17 Feb 2014 12:03:22 +0000 Subject: [PATCH] * 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 --- LICENSE.txt | 2 +- .../src/net/i2p/crypto/HMAC256Generator.java | 173 +++++++++- .../src/net/i2p/crypto/HMACGenerator.java | 25 +- .../src/net/i2p/util/PasswordManager.java | 41 +++ .../org/bouncycastle/oldcrypto/Digest.java | 77 ----- .../oldcrypto/digests/GeneralDigest.java | 154 --------- .../oldcrypto/digests/MD5Digest.java | 303 ------------------ .../bouncycastle/oldcrypto/macs/I2PHMac.java | 76 ++--- history.txt | 6 + .../src/net/i2p/router/RouterVersion.java | 2 +- 10 files changed, 262 insertions(+), 597 deletions(-) delete mode 100644 core/java/src/org/bouncycastle/oldcrypto/Digest.java delete mode 100644 core/java/src/org/bouncycastle/oldcrypto/digests/GeneralDigest.java delete mode 100644 core/java/src/org/bouncycastle/oldcrypto/digests/MD5Digest.java diff --git a/LICENSE.txt b/LICENSE.txt index 5882f38bd6..c3a79dfb3d 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -36,7 +36,7 @@ Public domain except as listed below: Copyright (c) 2003, TheCrypto See licenses/LICENSE-ElGamalDSA.txt - SHA256 and HMAC-SHA256: + SHA256 and HMAC: Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle See licenses/LICENSE-SHA256.txt diff --git a/core/java/src/net/i2p/crypto/HMAC256Generator.java b/core/java/src/net/i2p/crypto/HMAC256Generator.java index 5d38e5e1de..688e3148dc 100644 --- a/core/java/src/net/i2p/crypto/HMAC256Generator.java +++ b/core/java/src/net/i2p/crypto/HMAC256Generator.java @@ -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); + } +****/ } diff --git a/core/java/src/net/i2p/crypto/HMACGenerator.java b/core/java/src/net/i2p/crypto/HMACGenerator.java index f5643ca1b9..eb3533033d 100644 --- a/core/java/src/net/i2p/crypto/HMACGenerator.java +++ b/core/java/src/net/i2p/crypto/HMACGenerator.java @@ -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); } diff --git a/core/java/src/net/i2p/util/PasswordManager.java b/core/java/src/net/i2p/util/PasswordManager.java index e0f75a095d..5e51dcb7df 100644 --- a/core/java/src/net/i2p/util/PasswordManager.java +++ b/core/java/src/net/i2p/util/PasswordManager.java @@ -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)); + } +*****/ } diff --git a/core/java/src/org/bouncycastle/oldcrypto/Digest.java b/core/java/src/org/bouncycastle/oldcrypto/Digest.java deleted file mode 100644 index 0ecc1bd65d..0000000000 --- a/core/java/src/org/bouncycastle/oldcrypto/Digest.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.bouncycastle.oldcrypto; -/* - * Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle - * (http://www.bouncycastle.org) - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - */ - -/** - * interface that a message digest conforms to. - */ -public interface Digest -{ - /** - * return the algorithm name - * - * @return the algorithm name - */ - public String getAlgorithmName(); - - /** - * return the size, in bytes, of the digest produced by this message digest. - * - * @return the size, in bytes, of the digest produced by this message digest. - */ - public int getDigestSize(); - - /** - * update the message digest with a single byte. - * - * @param in the input byte to be entered. - */ - public void update(byte in); - - /** - * update the message digest with a block of bytes. - * - * @param in the byte array containing the data. - * @param inOff the offset into the byte array where the data starts. - * @param len the length of the data. - */ - public void update(byte[] in, int inOff, int len); - - /** - * close the digest, producing the final digest value. The doFinal - * call leaves the digest reset. - * - * @param out the array the digest is to be copied into. - * @param outOff the offset into the out array the digest is to start at. - */ - public int doFinal(byte[] out, int outOff); - - /** - * reset the digest back to it's initial state. - */ - public void reset(); -} diff --git a/core/java/src/org/bouncycastle/oldcrypto/digests/GeneralDigest.java b/core/java/src/org/bouncycastle/oldcrypto/digests/GeneralDigest.java deleted file mode 100644 index 85324b9763..0000000000 --- a/core/java/src/org/bouncycastle/oldcrypto/digests/GeneralDigest.java +++ /dev/null @@ -1,154 +0,0 @@ -package org.bouncycastle.oldcrypto.digests; -/* - * Copyright (c) 2000 - 2004 The Legion Of The Bouncy Castle - * (http://www.bouncycastle.org) - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software - * without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - */ - -import org.bouncycastle.oldcrypto.Digest; - -/** - * base implementation of MD4 family style digest as outlined in - * "Handbook of Applied Cryptography", pages 344 - 347. - */ -public abstract class GeneralDigest - implements Digest -{ - private final byte[] xBuf; - private int xBufOff; - - private long byteCount; - - /** - * Standard constructor - */ - protected GeneralDigest() - { - xBuf = new byte[4]; - xBufOff = 0; - } - - /** - * Copy constructor. We are using copy constructors in place - * of the Object.clone() interface as this interface is not - * supported by J2ME. - */ - protected GeneralDigest(GeneralDigest t) - { - xBuf = new byte[t.xBuf.length]; - System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); - - xBufOff = t.xBufOff; - byteCount = t.byteCount; - } - - public void update( - byte in) - { - xBuf[xBufOff++] = in; - - if (xBufOff == xBuf.length) - { - processWord(xBuf, 0); - xBufOff = 0; - } - - byteCount++; - } - - public void update( - byte[] in, - int inOff, - int len) - { - // - // fill the current word - // - while ((xBufOff != 0) && (len > 0)) - { - update(in[inOff]); - - inOff++; - len--; - } - - // - // process whole words. - // - while (len > xBuf.length) - { - processWord(in, inOff); - - inOff += xBuf.length; - len -= xBuf.length; - byteCount += xBuf.length; - } - - // - // load in the remainder. - // - while (len > 0) - { - update(in[inOff]); - - inOff++; - len--; - } - } - - public void finish() - { - long bitLength = (byteCount << 3); - - // - // add the pad bytes. - // - update((byte)128); - - while (xBufOff != 0) - { - update((byte)0); - } - - processLength(bitLength); - - processBlock(); - } - - public void reset() - { - byteCount = 0; - - xBufOff = 0; - for ( int i = 0; i < xBuf.length; i++ ) { - xBuf[i] = 0; - } - } - - protected abstract void processWord(byte[] in, int inOff); - - protected abstract void processLength(long bitLength); - - protected abstract void processBlock(); -} diff --git a/core/java/src/org/bouncycastle/oldcrypto/digests/MD5Digest.java b/core/java/src/org/bouncycastle/oldcrypto/digests/MD5Digest.java deleted file mode 100644 index 250fb256f4..0000000000 --- a/core/java/src/org/bouncycastle/oldcrypto/digests/MD5Digest.java +++ /dev/null @@ -1,303 +0,0 @@ -package org.bouncycastle.oldcrypto.digests; - - -/** - * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347. - */ -public class MD5Digest - extends GeneralDigest -{ - private static final int DIGEST_LENGTH = 16; - - private int H1, H2, H3, H4; // IV's - - private final int[] X = new int[16]; - private int xOff; - - /** - * Standard constructor - */ - public MD5Digest() - { - reset(); - } - - /** - * Copy constructor. This will copy the state of the provided - * message digest. - */ - public MD5Digest(MD5Digest t) - { - super(t); - - H1 = t.H1; - H2 = t.H2; - H3 = t.H3; - H4 = t.H4; - - System.arraycopy(t.X, 0, X, 0, t.X.length); - xOff = t.xOff; - } - - public String getAlgorithmName() - { - return "MD5"; - } - - public int getDigestSize() - { - return DIGEST_LENGTH; - } - - protected void processWord( - byte[] in, - int inOff) - { - X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) - | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); - - if (xOff == 16) - { - processBlock(); - } - } - - protected void processLength( - long bitLength) - { - if (xOff > 14) - { - processBlock(); - } - - X[14] = (int)(bitLength & 0xffffffff); - X[15] = (int)(bitLength >>> 32); - } - - private static void unpackWord( - int word, - byte[] out, - int outOff) - { - out[outOff] = (byte)word; - out[outOff + 1] = (byte)(word >>> 8); - out[outOff + 2] = (byte)(word >>> 16); - out[outOff + 3] = (byte)(word >>> 24); - } - - public int doFinal( - byte[] out, - int outOff) - { - finish(); - - unpackWord(H1, out, outOff); - unpackWord(H2, out, outOff + 4); - unpackWord(H3, out, outOff + 8); - unpackWord(H4, out, outOff + 12); - - reset(); - - return DIGEST_LENGTH; - } - - /** - * reset the chaining variables to the IV values. - */ - @Override - public void reset() - { - super.reset(); - - H1 = 0x67452301; - H2 = 0xefcdab89; - H3 = 0x98badcfe; - H4 = 0x10325476; - - xOff = 0; - - for (int i = 0; i != X.length; i++) - { - X[i] = 0; - } - } - - // - // round 1 left rotates - // - private static final int S11 = 7; - private static final int S12 = 12; - private static final int S13 = 17; - private static final int S14 = 22; - - // - // round 2 left rotates - // - private static final int S21 = 5; - private static final int S22 = 9; - private static final int S23 = 14; - private static final int S24 = 20; - - // - // round 3 left rotates - // - private static final int S31 = 4; - private static final int S32 = 11; - private static final int S33 = 16; - private static final int S34 = 23; - - // - // round 4 left rotates - // - private static final int S41 = 6; - private static final int S42 = 10; - private static final int S43 = 15; - private static final int S44 = 21; - - /* - * rotate int x left n bits. - */ - private static int rotateLeft( - int x, - int n) - { - return (x << n) | (x >>> (32 - n)); - } - - /* - * F, G, H and I are the basic MD5 functions. - */ - private static int F( - int u, - int v, - int w) - { - return (u & v) | (~u & w); - } - - private static int G( - int u, - int v, - int w) - { - return (u & w) | (v & ~w); - } - - private static int H( - int u, - int v, - int w) - { - return u ^ v ^ w; - } - - private static int K( - int u, - int v, - int w) - { - return v ^ (u | ~w); - } - - protected void processBlock() - { - int a = H1; - int b = H2; - int c = H3; - int d = H4; - - // - // Round 1 - F cycle, 16 times. - // - a = rotateLeft((a + F(b, c, d) + X[ 0] + 0xd76aa478), S11) + b; - d = rotateLeft((d + F(a, b, c) + X[ 1] + 0xe8c7b756), S12) + a; - c = rotateLeft((c + F(d, a, b) + X[ 2] + 0x242070db), S13) + d; - b = rotateLeft((b + F(c, d, a) + X[ 3] + 0xc1bdceee), S14) + c; - a = rotateLeft((a + F(b, c, d) + X[ 4] + 0xf57c0faf), S11) + b; - d = rotateLeft((d + F(a, b, c) + X[ 5] + 0x4787c62a), S12) + a; - c = rotateLeft((c + F(d, a, b) + X[ 6] + 0xa8304613), S13) + d; - b = rotateLeft((b + F(c, d, a) + X[ 7] + 0xfd469501), S14) + c; - a = rotateLeft((a + F(b, c, d) + X[ 8] + 0x698098d8), S11) + b; - d = rotateLeft((d + F(a, b, c) + X[ 9] + 0x8b44f7af), S12) + a; - c = rotateLeft((c + F(d, a, b) + X[10] + 0xffff5bb1), S13) + d; - b = rotateLeft((b + F(c, d, a) + X[11] + 0x895cd7be), S14) + c; - a = rotateLeft((a + F(b, c, d) + X[12] + 0x6b901122), S11) + b; - d = rotateLeft((d + F(a, b, c) + X[13] + 0xfd987193), S12) + a; - c = rotateLeft((c + F(d, a, b) + X[14] + 0xa679438e), S13) + d; - b = rotateLeft((b + F(c, d, a) + X[15] + 0x49b40821), S14) + c; - - // - // Round 2 - G cycle, 16 times. - // - a = rotateLeft((a + G(b, c, d) + X[ 1] + 0xf61e2562), S21) + b; - d = rotateLeft((d + G(a, b, c) + X[ 6] + 0xc040b340), S22) + a; - c = rotateLeft((c + G(d, a, b) + X[11] + 0x265e5a51), S23) + d; - b = rotateLeft((b + G(c, d, a) + X[ 0] + 0xe9b6c7aa), S24) + c; - a = rotateLeft((a + G(b, c, d) + X[ 5] + 0xd62f105d), S21) + b; - d = rotateLeft((d + G(a, b, c) + X[10] + 0x02441453), S22) + a; - c = rotateLeft((c + G(d, a, b) + X[15] + 0xd8a1e681), S23) + d; - b = rotateLeft((b + G(c, d, a) + X[ 4] + 0xe7d3fbc8), S24) + c; - a = rotateLeft((a + G(b, c, d) + X[ 9] + 0x21e1cde6), S21) + b; - d = rotateLeft((d + G(a, b, c) + X[14] + 0xc33707d6), S22) + a; - c = rotateLeft((c + G(d, a, b) + X[ 3] + 0xf4d50d87), S23) + d; - b = rotateLeft((b + G(c, d, a) + X[ 8] + 0x455a14ed), S24) + c; - a = rotateLeft((a + G(b, c, d) + X[13] + 0xa9e3e905), S21) + b; - d = rotateLeft((d + G(a, b, c) + X[ 2] + 0xfcefa3f8), S22) + a; - c = rotateLeft((c + G(d, a, b) + X[ 7] + 0x676f02d9), S23) + d; - b = rotateLeft((b + G(c, d, a) + X[12] + 0x8d2a4c8a), S24) + c; - - // - // Round 3 - H cycle, 16 times. - // - a = rotateLeft((a + H(b, c, d) + X[ 5] + 0xfffa3942), S31) + b; - d = rotateLeft((d + H(a, b, c) + X[ 8] + 0x8771f681), S32) + a; - c = rotateLeft((c + H(d, a, b) + X[11] + 0x6d9d6122), S33) + d; - b = rotateLeft((b + H(c, d, a) + X[14] + 0xfde5380c), S34) + c; - a = rotateLeft((a + H(b, c, d) + X[ 1] + 0xa4beea44), S31) + b; - d = rotateLeft((d + H(a, b, c) + X[ 4] + 0x4bdecfa9), S32) + a; - c = rotateLeft((c + H(d, a, b) + X[ 7] + 0xf6bb4b60), S33) + d; - b = rotateLeft((b + H(c, d, a) + X[10] + 0xbebfbc70), S34) + c; - a = rotateLeft((a + H(b, c, d) + X[13] + 0x289b7ec6), S31) + b; - d = rotateLeft((d + H(a, b, c) + X[ 0] + 0xeaa127fa), S32) + a; - c = rotateLeft((c + H(d, a, b) + X[ 3] + 0xd4ef3085), S33) + d; - b = rotateLeft((b + H(c, d, a) + X[ 6] + 0x04881d05), S34) + c; - a = rotateLeft((a + H(b, c, d) + X[ 9] + 0xd9d4d039), S31) + b; - d = rotateLeft((d + H(a, b, c) + X[12] + 0xe6db99e5), S32) + a; - c = rotateLeft((c + H(d, a, b) + X[15] + 0x1fa27cf8), S33) + d; - b = rotateLeft((b + H(c, d, a) + X[ 2] + 0xc4ac5665), S34) + c; - - // - // Round 4 - K cycle, 16 times. - // - a = rotateLeft((a + K(b, c, d) + X[ 0] + 0xf4292244), S41) + b; - d = rotateLeft((d + K(a, b, c) + X[ 7] + 0x432aff97), S42) + a; - c = rotateLeft((c + K(d, a, b) + X[14] + 0xab9423a7), S43) + d; - b = rotateLeft((b + K(c, d, a) + X[ 5] + 0xfc93a039), S44) + c; - a = rotateLeft((a + K(b, c, d) + X[12] + 0x655b59c3), S41) + b; - d = rotateLeft((d + K(a, b, c) + X[ 3] + 0x8f0ccc92), S42) + a; - c = rotateLeft((c + K(d, a, b) + X[10] + 0xffeff47d), S43) + d; - b = rotateLeft((b + K(c, d, a) + X[ 1] + 0x85845dd1), S44) + c; - a = rotateLeft((a + K(b, c, d) + X[ 8] + 0x6fa87e4f), S41) + b; - d = rotateLeft((d + K(a, b, c) + X[15] + 0xfe2ce6e0), S42) + a; - c = rotateLeft((c + K(d, a, b) + X[ 6] + 0xa3014314), S43) + d; - b = rotateLeft((b + K(c, d, a) + X[13] + 0x4e0811a1), S44) + c; - a = rotateLeft((a + K(b, c, d) + X[ 4] + 0xf7537e82), S41) + b; - d = rotateLeft((d + K(a, b, c) + X[11] + 0xbd3af235), S42) + a; - c = rotateLeft((c + K(d, a, b) + X[ 2] + 0x2ad7d2bb), S43) + d; - b = rotateLeft((b + K(c, d, a) + X[ 9] + 0xeb86d391), S44) + c; - - H1 += a; - H2 += b; - H3 += c; - H4 += d; - - // - // reset the offset and clean out the word buffer. - // - xOff = 0; - for (int i = 0; i != X.length; i++) - { - X[i] = 0; - } - } -} diff --git a/core/java/src/org/bouncycastle/oldcrypto/macs/I2PHMac.java b/core/java/src/org/bouncycastle/oldcrypto/macs/I2PHMac.java index 031f029551..09ccb63941 100644 --- a/core/java/src/org/bouncycastle/oldcrypto/macs/I2PHMac.java +++ b/core/java/src/org/bouncycastle/oldcrypto/macs/I2PHMac.java @@ -27,11 +27,12 @@ package org.bouncycastle.oldcrypto.macs; */ //import org.bouncycastle.crypto.CipherParameters; +import java.security.DigestException; +import java.security.MessageDigest; import java.util.Arrays; import net.i2p.util.SimpleByteCache; -import org.bouncycastle.oldcrypto.Digest; import org.bouncycastle.oldcrypto.Mac; /** @@ -47,6 +48,10 @@ import org.bouncycastle.oldcrypto.Mac; * in the standard bouncycastle library, thus it conflicts in JVMs that contain the * standard library (Android). * + * As of 0.9.12, refactored to use standard MessageDigest. + * + * Deprecated - Do not use outside of router or Syndie. + * To be moved to router. */ public class I2PHMac implements Mac @@ -56,34 +61,34 @@ implements Mac private final static byte IPAD = (byte)0x36; private final static byte OPAD = (byte)0x5C; - private final Digest digest; + private final MessageDigest digest; private final int digestSize; private final byte[] inputPad = new byte[BLOCK_LENGTH]; private final byte[] outputPad = new byte[BLOCK_LENGTH]; - public I2PHMac( - Digest digest) - { - this(digest, digest.getDigestSize()); + /** + * Standard HMAC, size == digest size. + * @deprecated Use javax.crypto.Mac + */ + public I2PHMac(MessageDigest digest) { + this(digest, digest.getDigestLength()); } /** - * @param sz override the digest's size + * @param sz override the digest's size, nonstandard if different. * SEE NOTES in HMACGenerator about why this isn't compatible with standard HmacMD5 */ - public I2PHMac( - Digest digest, int sz) - { + public I2PHMac(MessageDigest digest, int sz) { this.digest = digest; this.digestSize = sz; } public String getAlgorithmName() { - return digest.getAlgorithmName() + "/HMAC"; + return digest.getAlgorithm() + "/HMAC"; } - public Digest getUnderlyingDigest() + public MessageDigest getUnderlyingDigest() { return digest; } @@ -100,7 +105,12 @@ implements Mac if (key.length > BLOCK_LENGTH) { digest.update(key, 0, key.length); - digest.doFinal(inputPad, 0); + try { + digest.digest(inputPad, 0, digestSize); + } catch (DigestException de) { + digest.reset(); + throw new IllegalArgumentException(de); + } for (int i = digestSize; i < inputPad.length; i++) { inputPad[i] = 0; @@ -133,42 +143,32 @@ implements Mac digest.update(inputPad, 0, inputPad.length); } - public int getMacSize() - { + public int getMacSize() { return digestSize; } - public void update( - byte in) - { + public void update(byte in) { digest.update(in); } - public void update( - byte[] in, - int inOff, - int len) - { + public void update(byte[] in, int inOff, int len) { digest.update(in, inOff, len); } - public int doFinal( - byte[] out, - int outOff) - { + public int doFinal(byte[] out, int outOff) { byte[] tmp = acquireTmp(digestSize); //byte[] tmp = new byte[digestSize]; - digest.doFinal(tmp, 0); - - digest.update(outputPad, 0, outputPad.length); - digest.update(tmp, 0, tmp.length); - releaseTmp(tmp); - - int len = digest.doFinal(out, outOff); - - reset(); - - return len; + try { + digest.digest(tmp, 0, digestSize); + digest.update(outputPad, 0, outputPad.length); + digest.update(tmp, 0, tmp.length); + return digest.digest(out, outOff, digestSize); + } catch (DigestException de) { + throw new IllegalArgumentException(de); + } finally { + releaseTmp(tmp); + reset(); + } } private static byte[] acquireTmp(int sz) { diff --git a/history.txt b/history.txt index d9ca6d25f1..b8a81b093a 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,9 @@ +2014-02-17 zzz + * 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 + 2014-02-14 zzz * I2CP: - Add session limit, add new status code for refused diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index f6e6df3a5c..4e28a2c8d2 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 = 4; + public final static long BUILD = 5; /** for example "-test" */ public final static String EXTRA = "";