diff --git a/LICENSE.txt b/LICENSE.txt index b769f516e..acf614965 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -72,6 +72,9 @@ Public domain except as listed below: Copyright (c) 2006, Matthew Estes See licenses/LICENSE-BlockFile.txt + SipHashInline: + Copyright 2012 Hiroshi Nakamura + See licenses/LICENSE-Apache2.0.txt Router (router.jar): Public domain except as listed below: diff --git a/core/java/src/net/i2p/data/SessionTag.java b/core/java/src/net/i2p/data/SessionTag.java index c8179771e..a4fb59b4a 100644 --- a/core/java/src/net/i2p/data/SessionTag.java +++ b/core/java/src/net/i2p/data/SessionTag.java @@ -9,8 +9,12 @@ package net.i2p.data; * */ +import java.io.InputStream; +import java.io.IOException; + import net.i2p.util.RandomSource; import net.i2p.util.SimpleByteCache; +import net.i2p.util.SipHash; /** * 32 bytes, usually of random data. @@ -18,6 +22,7 @@ import net.i2p.util.SimpleByteCache; */ public class SessionTag extends SimpleDataStructure { public final static int BYTE_LENGTH = 32; + private int _cachedHashCode; public SessionTag() { super(); @@ -31,6 +36,7 @@ public class SessionTag extends SimpleDataStructure { if (create) { _data = SimpleByteCache.acquire(BYTE_LENGTH); RandomSource.getInstance().nextBytes(_data); + _cachedHashCode = SipHash.hashCode(_data); } } @@ -41,4 +47,27 @@ public class SessionTag extends SimpleDataStructure { public int length() { return BYTE_LENGTH; } + + @Override + public void setData(byte[] data) { + super.setData(data); + _cachedHashCode = SipHash.hashCode(data); + } + + @Override + public void readBytes(InputStream in) throws DataFormatException, IOException { + super.readBytes(in); + _cachedHashCode = SipHash.hashCode(_data); + } + + /** + * SessionTags are generated both locally and by peers, in quantity, + * and are used as keys in several datastructures (see TransientSessionKeyManager), + * so we use a secure hashCode function. + */ + @Override + public int hashCode() { + return _cachedHashCode; + } + } diff --git a/core/java/src/net/i2p/util/SipHash.java b/core/java/src/net/i2p/util/SipHash.java new file mode 100644 index 000000000..36efce006 --- /dev/null +++ b/core/java/src/net/i2p/util/SipHash.java @@ -0,0 +1,75 @@ +package net.i2p.util; + +// uncomment to test reference implementation +//import com.github.emboss.siphash.*; + +/** + * Wrapper around SipHashInline + * + * @since 0.9.5 + */ +public abstract class SipHash { + + private static final long K0 = RandomSource.getInstance().nextLong(); + private static final long K1 = RandomSource.getInstance().nextLong(); + + /** + * @param data non-null + */ + public static long digest(byte[] data) { + return SipHashInline.hash24(K0, K1, data); + } + + /** + * @param data non-null + */ + public static long digest(byte[] data, int off, int len) { + return SipHashInline.hash24(K0, K1, data, off, len); + } + + /** + * Secure replacement for DataHelper.hashCode(byte[]); + * caching recommended + * + * @param data may be null + */ + public static int hashCode(byte[] data) { + if (data == null) return 0; + return (int) SipHashInline.hash24(K0, K1, data); + } + +/**** + public static void main(String args[]) { + final int warmup = 10000; + final int runs = 1000000; + final byte[] b = new byte[32]; + RandomSource.getInstance().nextBytes(b); + + // inline implementation + for (int i = 0; i < warmup; i++) { + digest(b); + } + long begin = System.currentTimeMillis(); + for (int i = 0; i < runs; i++) { + digest(b); + } + System.out.println("Inline impl. time per hash (us): " + (1000d * (System.currentTimeMillis() - begin) / runs)); + + // reference implementation + final byte[] key = new byte[16]; + RandomSource.getInstance().nextBytes(key); + final SipKey sk = new SipKey(key); + for (int i = 0; i < warmup; i++) { + com.github.emboss.siphash.SipHash.digest(sk, b); + } + begin = System.currentTimeMillis(); + for (int i = 0; i < runs; i++) { + com.github.emboss.siphash.SipHash.digest(sk, b); + } + System.out.println("Ref. impl. time per hash (us): " + (1000d * (System.currentTimeMillis() - begin) / runs)); + + // test results (eeepc openjdk 6) ~2.05 us inline, 2.75 us stock, inline ~25% faster + // test results (hexcore openjdk 7) ~0.07 us inline, 0.11 us stock, inline ~35% faster + } +****/ +} diff --git a/core/java/src/net/i2p/util/SipHashInline.java b/core/java/src/net/i2p/util/SipHashInline.java index 1b310a1e8..398f1402a 100644 --- a/core/java/src/net/i2p/util/SipHashInline.java +++ b/core/java/src/net/i2p/util/SipHashInline.java @@ -1,3 +1,26 @@ +package net.i2p.util; + +/* + * As pulled from https://github.com/nahi/siphash-java-inline + * Last commit was https://github.com/nahi/siphash-java-inline/commit/5be5c84851a28f800fcac66ced658bdbd01f31ef + * 2012-11-06 + * + * Copyright 2012 Hiroshi Nakamura + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /** * SipHash implementation with hand inlining the SIPROUND. * @@ -7,17 +30,25 @@ * SIPROUND is defined in siphash24.c that can be downloaded from the above * site. Following license notice is subject to change based on the licensing * policy of siphash24.c. + * + * I2P mods: add off/len version + * + * @since 0.9.5 */ -public class SipHashInline { +abstract class SipHashInline { public static long hash24(long k0, long k1, byte[] data) { + return hash24(k0, k1, data, 0, data.length); + } + + public static long hash24(long k0, long k1, byte[] data, int off, int len) { long v0 = 0x736f6d6570736575L ^ k0; long v1 = 0x646f72616e646f6dL ^ k1; long v2 = 0x6c7967656e657261L ^ k0; long v3 = 0x7465646279746573L ^ k1; long m; - int last = data.length / 8 * 8; - int i = 0; + int last = len / 8 * 8; + int i = off; // processing 8 bytes blocks in data while (i < last) { @@ -100,10 +131,10 @@ public class SipHashInline { // packing the last block to long, as LE 0-7 bytes + the length in the top byte m = 0; - for (i = data.length - 1; i >= last; --i) { + for (i = off + len - 1; i >= last; --i) { m <<= 8; m |= (long) data[i]; } - m |= (long) data.length << 56; + m |= (long) len << 56; // MSGROUND { v3 ^= m; // SIPROUND { diff --git a/core/java/test/junit/net/i2p/util/SipHashInlineTest.java b/core/java/test/junit/net/i2p/util/SipHashInlineTest.java index e49d93d87..8cdce6ca0 100644 --- a/core/java/test/junit/net/i2p/util/SipHashInlineTest.java +++ b/core/java/test/junit/net/i2p/util/SipHashInlineTest.java @@ -1,3 +1,26 @@ +package net.i2p.util; + +/* + * As pulled from https://github.com/nahi/siphash-java-inline + * Last commit was https://github.com/nahi/siphash-java-inline/commit/5be5c84851a28f800fcac66ced658bdbd01f31ef + * 2012-11-06 + * + * Copyright 2012 Hiroshi Nakamura + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + import junit.framework.TestCase; public class SipHashInlineTest extends TestCase { diff --git a/core/java/test/junit/net/i2p/util/UtilTestSuite.java b/core/java/test/junit/net/i2p/util/UtilTestSuite.java index 4bffcffb0..e7abd7807 100644 --- a/core/java/test/junit/net/i2p/util/UtilTestSuite.java +++ b/core/java/test/junit/net/i2p/util/UtilTestSuite.java @@ -19,7 +19,8 @@ public class UtilTestSuite { suite.addTestSuite(ResettableGZIPOutputStreamTest.class); suite.addTestSuite(ReusableGZIPInputStreamTest.class); suite.addTestSuite(ReusableGZIPOutputStreamTest.class); + suite.addTestSuite(SipHashInlineTest.class); return suite; } -} \ No newline at end of file +} diff --git a/history.txt b/history.txt index 1c62a43f4..7b95ec4d6 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,16 @@ +2013-01-02 zzz + * DataHelper: new getASCII() method + * DataStructures: + - Convert SessionTag.hashCode() to SipHash to prevent collision attacks + - Improve equals() + * I2CP: + - Remove unused equals() methods for message classes + - Remove static logs + - Fix leak if nonce = 0 but reliability != none + - More work on failure codes (ticket #788) + * SAM: Synchronize dissector + * Transport: Fix early NPE (ticket #824) + 2013-01-01 kytv * Update Java Service Wrapper to v3.5.17 (ticket #826). - Windows: Self-compiled with VS2010 in Windows 7. The icon has been diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 0725033fa..f6e6df3a5 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 = 3; + public final static long BUILD = 4; /** for example "-test" */ public final static String EXTRA = "";