merge of '6ad44c8ad61fa5a65207491cab744ddfa8f0f3ca'
and 'db7c4479731f94687d64bea76eafeb4c62e14f1a'
This commit is contained in:
@ -28,7 +28,7 @@ ant debug
|
||||
../../android-sdk-linux_86/tools/emulator -avd i2p &
|
||||
|
||||
#then wait a couple minutes until the emulator is up
|
||||
#then install the I2P app
|
||||
#then install the I2P app (ONE TIME ONLY)
|
||||
ant install
|
||||
|
||||
#then run the debugger
|
||||
@ -36,3 +36,5 @@ ant install
|
||||
|
||||
#to rebuild and reinstall to emulator:
|
||||
ant reinstall
|
||||
|
||||
# Now click on the I2P icon on your phone!
|
||||
|
1
android/build.properties
Normal file
1
android/build.properties
Normal file
@ -0,0 +1 @@
|
||||
application-package=net.i2p.router
|
@ -76,6 +76,9 @@
|
||||
<mkdir dir="tmp" />
|
||||
<unjar src="../build/i2p.jar" dest="tmp/" />
|
||||
<delete file="tmp/net/i2p/util/LogWriter.class" />
|
||||
<delete file="tmp/net/i2p/util/SecureDirectory.class" />
|
||||
<delete file="tmp/net/i2p/util/SecureFile.class" />
|
||||
<delete file="tmp/net/i2p/util/SecureFileOutputStream.class" />
|
||||
<!-- org.bouncycastle.crypto already in android
|
||||
but we need a little trickery because our HMac is incompatible...
|
||||
and the libs aren't in the SDK to compile against??? -->
|
||||
@ -237,6 +240,7 @@
|
||||
<target name="compile" depends="buildrouter, resource-src, aidl">
|
||||
<javac encoding="ascii" target="1.5" debug="true" extdirs=""
|
||||
destdir="${out-classes}"
|
||||
includeantruntime="false"
|
||||
bootclasspathref="android.target.classpath">
|
||||
<src path="${source-folder}" />
|
||||
<src path="${gen-folder}" />
|
||||
@ -280,6 +284,12 @@
|
||||
|
||||
<!-- Package the application and sign it with a debug key.
|
||||
This is the default target when building. It is used for debug. -->
|
||||
<!--
|
||||
I2P when this fails 365 days later because the key expired, delete ~/.android/debug.keystore
|
||||
Then do 'ant uninstall' (since the new key doesn't match the old key)
|
||||
Then do 'ant install'
|
||||
See http://developer.android.com/guide/publishing/app-signing.html for more info
|
||||
-->
|
||||
<target name="debug" depends="dex, package-resources">
|
||||
<apkbuilder
|
||||
outfolder="${out-folder}"
|
||||
@ -327,12 +337,12 @@
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- Uinstall the package from the default emulator -->
|
||||
<!-- Uninstall the package from the default emulator -->
|
||||
<target name="uninstall">
|
||||
<echo>Uninstalling ${application-package} from the default emulator...</echo>
|
||||
<exec executable="${adb}" failonerror="true">
|
||||
<arg value="uninstall" />
|
||||
<arg path="${application-package}" />
|
||||
<arg value="${application-package}" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
|
@ -6,17 +6,27 @@ i2p.dir.pid=/data/data/net.i2p.router/files/tmp
|
||||
prng.buffers=2
|
||||
router.decayingBloomFilterM=20
|
||||
stat.full=false
|
||||
i2np.udp.maxConnections=30
|
||||
#
|
||||
# no I2CP
|
||||
#
|
||||
i2p.dummyClientFacade=true
|
||||
# for now
|
||||
#
|
||||
##### Transport
|
||||
#
|
||||
#
|
||||
# NTCP
|
||||
#
|
||||
#i2np.ntcp.enable=false
|
||||
i2np.ntcp.maxConnections=8
|
||||
#
|
||||
# UDP crashes the JVM, don't know why
|
||||
#
|
||||
i2np.udp.enable=false
|
||||
i2np.udp.maxConnections=12
|
||||
#
|
||||
# no COMM at all!!!
|
||||
#i2p.vmCommSystem=true
|
||||
#
|
||||
# not on android
|
||||
i2np.upnp.enable=false
|
||||
routerconsole.geoip.enable=false
|
||||
|
@ -46,6 +46,7 @@ public class I2PAndroid extends Activity
|
||||
{
|
||||
System.err.println("onStart called");
|
||||
super.onStart();
|
||||
// net.i2p.crypto.DSAEngine.main(null);
|
||||
RouterLaunch.main(null);
|
||||
System.err.println("Router.main finished");
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* bridge to android logging
|
||||
@ -56,12 +57,22 @@ class LogWriter implements Runnable {
|
||||
public void flushRecords() { flushRecords(true); }
|
||||
public void flushRecords(boolean shouldWait) {
|
||||
try {
|
||||
List records = _manager._removeAll();
|
||||
// zero copy, drain the manager queue directly
|
||||
Queue<LogRecord> records = _manager.getQueue();
|
||||
if (records == null) return;
|
||||
for (int i = 0; i < records.size(); i++) {
|
||||
LogRecord rec = (LogRecord) records.get(i);
|
||||
if (!records.isEmpty()) {
|
||||
LogRecord rec;
|
||||
while ((rec = records.poll()) != null) {
|
||||
writeRecord(rec);
|
||||
}
|
||||
try {
|
||||
if (_currentOut != null)
|
||||
_currentOut.flush();
|
||||
} catch (IOException ioe) {
|
||||
//if (++_diskFullMessageCount < MAX_DISKFULL_MESSAGES)
|
||||
System.err.println("Error writing the router log - disk full? " + ioe);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
} finally {
|
||||
|
22
android/src/net/i2p/util/SecureDirectory.java
Normal file
22
android/src/net/i2p/util/SecureDirectory.java
Normal file
@ -0,0 +1,22 @@
|
||||
package net.i2p.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* setXXX() not available until API level 9 (Platform Version 2.3)
|
||||
* @since 0.8.7
|
||||
*/
|
||||
public class SecureDirectory extends File {
|
||||
|
||||
public SecureDirectory(String pathname) {
|
||||
super(pathname);
|
||||
}
|
||||
|
||||
public SecureDirectory(String parent, String child) {
|
||||
super(parent, child);
|
||||
}
|
||||
|
||||
public SecureDirectory(File parent, String child) {
|
||||
super(parent, child);
|
||||
}
|
||||
}
|
22
android/src/net/i2p/util/SecureFile.java
Normal file
22
android/src/net/i2p/util/SecureFile.java
Normal file
@ -0,0 +1,22 @@
|
||||
package net.i2p.util;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* setXXX() not available until API level 9 (Platform Version 2.3)
|
||||
* @since 0.8.7
|
||||
*/
|
||||
public class SecureFile extends SecureDirectory {
|
||||
|
||||
public SecureFile(String pathname) {
|
||||
super(pathname);
|
||||
}
|
||||
|
||||
public SecureFile(String parent, String child) {
|
||||
super(parent, child);
|
||||
}
|
||||
|
||||
public SecureFile(File parent, String child) {
|
||||
super(parent, child);
|
||||
}
|
||||
}
|
53
android/src/net/i2p/util/SecureFileOutputStream.java
Normal file
53
android/src/net/i2p/util/SecureFileOutputStream.java
Normal file
@ -0,0 +1,53 @@
|
||||
package net.i2p.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* setXXX() not available until API level 9 (Platform Version 2.3)
|
||||
* @since 0.8.7
|
||||
*/
|
||||
public class SecureFileOutputStream extends FileOutputStream {
|
||||
|
||||
/**
|
||||
* super()
|
||||
*/
|
||||
public SecureFileOutputStream(String file) throws FileNotFoundException {
|
||||
super(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* super()
|
||||
*/
|
||||
public SecureFileOutputStream(String file, boolean append) throws FileNotFoundException {
|
||||
super(file, append);
|
||||
}
|
||||
|
||||
/**
|
||||
* super()
|
||||
*/
|
||||
public SecureFileOutputStream(File file) throws FileNotFoundException {
|
||||
super(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* super()
|
||||
*/
|
||||
public SecureFileOutputStream(File file, boolean append) throws FileNotFoundException {
|
||||
super(file, append);
|
||||
}
|
||||
|
||||
/** @return false */
|
||||
static boolean canSetPerms() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* noop
|
||||
*/
|
||||
public static void setPerms(File f) {
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ dist.dir=dist
|
||||
dist.jar=${dist.dir}/BOB.jar
|
||||
dist.javadoc.dir=${dist.dir}/javadoc
|
||||
endorsed.classpath=
|
||||
excludes=**/*.html
|
||||
excludes=**/*.html,**/*.txt
|
||||
file.reference.build-javadoc=../../i2p.i2p/build/javadoc
|
||||
file.reference.i2p.jar=../../core/java/build/i2p.jar
|
||||
file.reference.i2ptunnel.jar=../i2ptunnel/java/build/i2ptunnel.jar
|
||||
|
@ -557,7 +557,8 @@ public class Storage
|
||||
private static final char[] ILLEGAL = new char[] {
|
||||
'<', '>', ':', '"', '/', '\\', '|', '?', '*',
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
0x7f };
|
||||
|
||||
/**
|
||||
* Removes 'suspicious' characters from the given file name.
|
||||
|
@ -824,7 +824,8 @@
|
||||
<!-- thazzit -->
|
||||
</target>
|
||||
|
||||
<target name="test" depends="buildProperties" >
|
||||
<target name="test" depends="buildProperties, jbigi" >
|
||||
<mkdir dir="reports/core/junit/" />
|
||||
<ant dir="core/java/" target="test" />
|
||||
</target>
|
||||
<target name="junit.report" depends="buildProperties" >
|
||||
|
@ -99,7 +99,7 @@
|
||||
<pathelement path="${classpath}" />
|
||||
<pathelement location="./build/obj_test" />
|
||||
<pathelement location="./build/obj" />
|
||||
<pathelement location="../../installer/lib/jbigi/jbigi.jar" />
|
||||
<pathelement location="../../build/jbigi.jar" />
|
||||
<pathelement location="${with.cobertura}" />
|
||||
</classpath>
|
||||
<batchtest todir="../../reports/core/junit/">
|
||||
|
@ -687,14 +687,14 @@ public class I2PAppContext {
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated unused */
|
||||
/** @deprecated used only by syndie */
|
||||
public HMAC256Generator hmac256() {
|
||||
if (!_hmac256Initialized)
|
||||
initializeHMAC256();
|
||||
return _hmac256;
|
||||
}
|
||||
|
||||
/** @deprecated unused */
|
||||
/** @deprecated used only by syndie */
|
||||
private void initializeHMAC256() {
|
||||
synchronized (this) {
|
||||
if (_hmac256 == null) {
|
||||
|
@ -91,14 +91,12 @@ public class ElGamalAESEngine {
|
||||
SessionTag st = new SessionTag(tag);
|
||||
SessionKey key = keyManager.consumeTag(st);
|
||||
SessionKey foundKey = new SessionKey();
|
||||
foundKey.setData(null);
|
||||
SessionKey usedKey = new SessionKey();
|
||||
Set foundTags = new HashSet();
|
||||
byte decrypted[] = null;
|
||||
boolean wasExisting = false;
|
||||
if (key != null) {
|
||||
//if (_log.shouldLog(Log.DEBUG)) _log.debug("Key is known for tag " + st);
|
||||
usedKey.setData(key.getData());
|
||||
long id = _context.random().nextLong();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(id + ": Decrypting existing session encrypted with tag: " + st.toString() + ": key: " + key.toBase64() + ": " + data.length + " bytes: " + Base64.encode(data, 0, 64));
|
||||
@ -138,7 +136,7 @@ public class ElGamalAESEngine {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Found key: " + foundKey.toBase64() + " tags: " + foundTags + " wasExisting? " + wasExisting);
|
||||
keyManager.tagsReceived(foundKey, foundTags);
|
||||
} else {
|
||||
} else if (usedKey.getData() != null) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Used key: " + usedKey.toBase64() + " tags: " + foundTags + " wasExisting? " + wasExisting);
|
||||
keyManager.tagsReceived(usedKey, foundTags);
|
||||
@ -160,11 +158,12 @@ public class ElGamalAESEngine {
|
||||
* the decryptAESBlock method & structure.
|
||||
*
|
||||
* @param foundTags set which is filled with any sessionTags found during decryption
|
||||
* @param foundKey session key which may be filled with a new sessionKey found during decryption
|
||||
* @param foundKey out parameter. Data must be unset when called; may be filled with a new sessionKey found during decryption
|
||||
* @param usedKey out parameter. Data must be unset when called; usedKey.setData() will be called by this method on success.
|
||||
*
|
||||
* @return null if decryption fails
|
||||
*/
|
||||
byte[] decryptNewSession(byte data[], PrivateKey targetPrivateKey, Set foundTags, SessionKey usedKey,
|
||||
private byte[] decryptNewSession(byte data[], PrivateKey targetPrivateKey, Set foundTags, SessionKey usedKey,
|
||||
SessionKey foundKey) throws DataFormatException {
|
||||
if (data == null) {
|
||||
//if (_log.shouldLog(Log.WARN)) _log.warn("Data is null, unable to decrypt new session");
|
||||
@ -231,11 +230,13 @@ public class ElGamalAESEngine {
|
||||
* If anything doesn't match up in decryption, it falls back to decryptNewSession
|
||||
*
|
||||
* @param foundTags set which is filled with any sessionTags found during decryption
|
||||
* @param foundKey session key which may be filled with a new sessionKey found during decryption
|
||||
* @param foundKey out parameter. Data must be unset when called; may be filled with a new sessionKey found during decryption
|
||||
* @param usedKey out parameter. Data must be unset when called; usedKey.setData() will be called by this method on success.
|
||||
*
|
||||
* @return decrypted data or null on failure
|
||||
*
|
||||
*/
|
||||
byte[] decryptExistingSession(byte data[], SessionKey key, PrivateKey targetPrivateKey, Set foundTags,
|
||||
private byte[] decryptExistingSession(byte data[], SessionKey key, PrivateKey targetPrivateKey, Set foundTags,
|
||||
SessionKey usedKey, SessionKey foundKey) throws DataFormatException {
|
||||
byte preIV[] = new byte[32];
|
||||
System.arraycopy(data, 0, preIV, 0, preIV.length);
|
||||
@ -243,8 +244,6 @@ public class ElGamalAESEngine {
|
||||
byte iv[] = new byte[16];
|
||||
System.arraycopy(ivHash.getData(), 0, iv, 0, 16);
|
||||
|
||||
usedKey.setData(key.getData());
|
||||
|
||||
//_log.debug("Pre IV for decryptExistingSession: " + DataHelper.toString(preIV, 32));
|
||||
//_log.debug("SessionKey for decryptNewSession: " + DataHelper.toString(key.getData(), 32));
|
||||
byte decrypted[] = decryptAESBlock(data, 32, data.length-32, key, iv, preIV, foundTags, foundKey);
|
||||
@ -267,6 +266,7 @@ public class ElGamalAESEngine {
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("Decrypt with an EXISTING session tag successfull, # tags read: " + foundTags.size(),
|
||||
// new Exception("Decrypted by"));
|
||||
usedKey.setData(key.getData());
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
@ -287,13 +287,17 @@ public class ElGamalAESEngine {
|
||||
* consume it, but if it is null, record the keys, etc as part of a new session.
|
||||
*
|
||||
* @param foundTags set which is filled with any sessionTags found during decryption
|
||||
* @param foundKey session key which may be filled with a new sessionKey found during decryption
|
||||
* @param foundKey out parameter. Data must be unset when called; may be filled with a new sessionKey found during decryption
|
||||
* @return decrypted data or null on failure
|
||||
*/
|
||||
byte[] decryptAESBlock(byte encrypted[], SessionKey key, byte iv[],
|
||||
private byte[] decryptAESBlock(byte encrypted[], SessionKey key, byte iv[],
|
||||
byte sentTag[], Set foundTags, SessionKey foundKey) throws DataFormatException {
|
||||
return decryptAESBlock(encrypted, 0, encrypted.length, key, iv, sentTag, foundTags, foundKey);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: package private for ElGamalTest.testAES()
|
||||
*/
|
||||
byte[] decryptAESBlock(byte encrypted[], int offset, int encryptedLen, SessionKey key, byte iv[],
|
||||
byte sentTag[], Set foundTags, SessionKey foundKey) throws DataFormatException {
|
||||
//_log.debug("iv for decryption: " + DataHelper.toString(iv, 16));
|
||||
@ -448,7 +452,7 @@ public class ElGamalAESEngine {
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
byte[] encryptNewSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
|
||||
private byte[] encryptNewSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
|
||||
SessionKey newKey, long paddedSize) {
|
||||
//_log.debug("Encrypting to a NEW session");
|
||||
byte elgSrcData[] = new byte[SessionKey.KEYSIZE_BYTES+32+158];
|
||||
@ -511,7 +515,7 @@ public class ElGamalAESEngine {
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
byte[] encryptExistingSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
|
||||
private byte[] encryptExistingSession(byte data[], PublicKey target, SessionKey key, Set tagsForDelivery,
|
||||
SessionTag currentTag, SessionKey newKey, long paddedSize) {
|
||||
//_log.debug("Encrypting to an EXISTING session");
|
||||
byte rawTag[] = currentTag.getData();
|
||||
@ -542,12 +546,14 @@ public class ElGamalAESEngine {
|
||||
* - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
|
||||
* </pre>
|
||||
*
|
||||
* Note: package private for ElGamalTest.testAES()
|
||||
*/
|
||||
final byte[] encryptAESBlock(byte data[], SessionKey key, byte[] iv, Set tagsForDelivery, SessionKey newKey,
|
||||
long paddedSize) {
|
||||
return encryptAESBlock(data, key, iv, tagsForDelivery, newKey, paddedSize, 0);
|
||||
}
|
||||
final byte[] encryptAESBlock(byte data[], SessionKey key, byte[] iv, Set tagsForDelivery, SessionKey newKey,
|
||||
|
||||
private final byte[] encryptAESBlock(byte data[], SessionKey key, byte[] iv, Set tagsForDelivery, SessionKey newKey,
|
||||
long paddedSize, int prefixBytes) {
|
||||
//_log.debug("iv for encryption: " + DataHelper.toString(iv, 16));
|
||||
//_log.debug("Encrypting AES");
|
||||
@ -616,6 +622,7 @@ public class ElGamalAESEngine {
|
||||
context.random().nextBytes(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
final static int getPaddingSize(int curSize, long minPaddedSize) {
|
||||
int diff = 0;
|
||||
if (curSize < minPaddedSize) {
|
||||
|
@ -13,9 +13,12 @@ import org.bouncycastle.crypto.macs.I2PHMac;
|
||||
/**
|
||||
* Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs
|
||||
* in {@link org.bouncycastle.crypto.macs.I2PHMac} and
|
||||
* {@link org.bouncycastle.crypto.digests.MD5Digest}.
|
||||
* {@link net.i2p.crypto.Sha256Standalone}.
|
||||
*
|
||||
* deprecated unused
|
||||
* This should be compatible with javax.crypto.Mac.getInstance("HmacSHA256")
|
||||
* but that is untested.
|
||||
*
|
||||
* deprecated used only by syndie
|
||||
*/
|
||||
public class HMAC256Generator extends HMACGenerator {
|
||||
public HMAC256Generator(I2PAppContext context) { super(context); }
|
||||
|
@ -3,41 +3,67 @@ package net.i2p.crypto;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
// following are for main() tests
|
||||
//import java.security.InvalidKeyException;
|
||||
//import java.security.Key;
|
||||
//import java.security.NoSuchAlgorithmException;
|
||||
//import javax.crypto.spec.SecretKeySpec;
|
||||
//import net.i2p.data.Base64;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.SessionKey;
|
||||
import net.i2p.util.SimpleByteCache;
|
||||
|
||||
import org.bouncycastle.crypto.digests.MD5Digest;
|
||||
import org.bouncycastle.crypto.Mac;
|
||||
import org.bouncycastle.crypto.macs.I2PHMac;
|
||||
|
||||
/**
|
||||
* Calculate the HMAC-MD5 of a key+message. All the good stuff occurs
|
||||
* Calculate the HMAC-MD5-128 of a key+message. All the good stuff occurs
|
||||
* in {@link org.bouncycastle.crypto.macs.I2PHMac} and
|
||||
* {@link org.bouncycastle.crypto.digests.MD5Digest}.
|
||||
*
|
||||
* Keys are always 32 bytes.
|
||||
* This is used only by UDP.
|
||||
* Use deprecated outside the router, this may move to router.jar.
|
||||
*
|
||||
* NOTE THIS IS NOT COMPATIBLE with javax.crypto.Mac.getInstance("HmacMD5")
|
||||
* as we tell I2PHMac that the digest length is 32 bytes, so it generates
|
||||
* a different result.
|
||||
*
|
||||
* Quote jrandom:
|
||||
* "The HMAC is hardcoded to use SHA256 digest size
|
||||
* for backwards compatability. next time we have a backwards
|
||||
* incompatible change, we should update this."
|
||||
*
|
||||
* Does this mean he intended it to be compatible with MD5?
|
||||
* See also 2005-07-05 status notes.
|
||||
*
|
||||
*/
|
||||
public class HMACGenerator {
|
||||
private I2PAppContext _context;
|
||||
/** set of available HMAC instances for calculate */
|
||||
protected final LinkedBlockingQueue<I2PHMac> _available;
|
||||
/** set of available byte[] buffers for verify */
|
||||
private final LinkedBlockingQueue<byte[]> _availableTmp;
|
||||
|
||||
/**
|
||||
* @param context unused
|
||||
*/
|
||||
public HMACGenerator(I2PAppContext context) {
|
||||
_context = context;
|
||||
_available = new LinkedBlockingQueue(32);
|
||||
_availableTmp = new LinkedBlockingQueue(32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public Hash calculate(SessionKey key, byte data[]) {
|
||||
if ((key == null) || (key.getData() == null) || (data == null))
|
||||
throw new NullPointerException("Null arguments for HMAC");
|
||||
byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
byte rv[] = acquireTmp();
|
||||
Arrays.fill(rv, (byte)0x0);
|
||||
calculate(key, data, 0, data.length, rv, 0);
|
||||
return new Hash(rv);
|
||||
}
|
||||
@ -52,10 +78,8 @@ public class HMACGenerator {
|
||||
I2PHMac mac = acquire();
|
||||
mac.init(key.getData());
|
||||
mac.update(data, offset, length);
|
||||
//byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
mac.doFinal(target, targetOffset);
|
||||
release(mac);
|
||||
//return new Hash(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,7 +101,6 @@ public class HMACGenerator {
|
||||
mac.init(key.getData());
|
||||
mac.update(curData, curOffset, curLength);
|
||||
byte rv[] = acquireTmp();
|
||||
//byte rv[] = new byte[Hash.HASH_LENGTH];
|
||||
mac.doFinal(rv, 0);
|
||||
release(mac);
|
||||
|
||||
@ -93,6 +116,7 @@ public class HMACGenerator {
|
||||
// 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"
|
||||
// SEE NOTES ABOVE
|
||||
return new I2PHMac(new MD5Digest(), 32);
|
||||
}
|
||||
|
||||
@ -100,17 +124,74 @@ public class HMACGenerator {
|
||||
_available.offer(mac);
|
||||
}
|
||||
|
||||
// temp buffers for verify(..)
|
||||
/**
|
||||
* Not really tmp, just from the byte array cache.
|
||||
* Does NOT zero.
|
||||
*/
|
||||
private byte[] acquireTmp() {
|
||||
byte rv[] = _availableTmp.poll();
|
||||
if (rv != null)
|
||||
Arrays.fill(rv, (byte)0x0);
|
||||
else
|
||||
rv = new byte[Hash.HASH_LENGTH];
|
||||
byte rv[] = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
||||
return rv;
|
||||
}
|
||||
|
||||
private void releaseTmp(byte tmp[]) {
|
||||
_availableTmp.offer(tmp);
|
||||
SimpleByteCache.release(tmp);
|
||||
}
|
||||
|
||||
//private static final int RUNS = 100000;
|
||||
|
||||
/**
|
||||
* Test the BC and the JVM's implementations for speed
|
||||
*/
|
||||
/**** All this did was prove that we aren't compatible with standard HmacMD5
|
||||
public static void main(String args[]) {
|
||||
if (args.length != 2) {
|
||||
System.err.println("Usage: HMACGenerator keySeedString dataString");
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] rand = SHA256Generator.getInstance().calculateHash(args[0].getBytes()).getData();
|
||||
byte[] data = args[1].getBytes();
|
||||
Key keyObj = new SecretKeySpec(rand, "HmacMD5");
|
||||
|
||||
byte[] keyBytes = keyObj.getEncoded();
|
||||
System.out.println("key bytes (" + keyBytes.length + ") is [" + Base64.encode(keyBytes) + "]");
|
||||
SessionKey key = new SessionKey(keyBytes);
|
||||
System.out.println("session key is [" + key);
|
||||
System.out.println("key object is [" + keyObj);
|
||||
|
||||
HMACGenerator gen = new HMACGenerator(I2PAppContext.getGlobalContext());
|
||||
byte[] result = new byte[16];
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < RUNS; i++) {
|
||||
gen.calculate(key, data, 0, data.length, result, 0);
|
||||
if (i == 0)
|
||||
System.out.println("MAC [" + Base64.encode(result) + "]");
|
||||
}
|
||||
long time = System.currentTimeMillis() - start;
|
||||
System.out.println("Time for " + RUNS + " HMAC-MD5 computations:");
|
||||
System.out.println("BC time (ms): " + time);
|
||||
|
||||
start = System.currentTimeMillis();
|
||||
javax.crypto.Mac mac;
|
||||
try {
|
||||
mac = javax.crypto.Mac.getInstance("HmacMD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
System.err.println("Fatal: " + e);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < RUNS; i++) {
|
||||
try {
|
||||
mac.init(keyObj);
|
||||
} catch (InvalidKeyException e) {
|
||||
System.err.println("Fatal: " + e);
|
||||
}
|
||||
byte[] sha = mac.doFinal(data);
|
||||
if (i == 0)
|
||||
System.out.println("MAC [" + Base64.encode(sha) + "]");
|
||||
}
|
||||
time = System.currentTimeMillis() - start;
|
||||
|
||||
System.out.println("JVM time (ms): " + time);
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import net.i2p.data.SessionKey;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.data.SimpleDataStructure;
|
||||
import net.i2p.util.Clock;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.NativeBigInteger;
|
||||
@ -29,19 +30,18 @@ import net.i2p.util.RandomSource;
|
||||
* @author jrandom
|
||||
*/
|
||||
public class KeyGenerator {
|
||||
private Log _log;
|
||||
private I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final I2PAppContext _context;
|
||||
|
||||
public KeyGenerator(I2PAppContext context) {
|
||||
_log = context.logManager().getLog(KeyGenerator.class);
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public static KeyGenerator getInstance() {
|
||||
return I2PAppContext.getGlobalContext().keyGenerator();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Generate a private 256 bit session key
|
||||
* @return session key
|
||||
*/
|
||||
@ -84,11 +84,11 @@ public class KeyGenerator {
|
||||
* index 1 is a PrivateKey
|
||||
* @return pair of keys
|
||||
*/
|
||||
public Object[] generatePKIKeypair() {
|
||||
public SimpleDataStructure[] generatePKIKeypair() {
|
||||
BigInteger a = new NativeBigInteger(PUBKEY_EXPONENT_SIZE, _context.random());
|
||||
BigInteger aalpha = CryptoConstants.elgg.modPow(a, CryptoConstants.elgp);
|
||||
|
||||
Object[] keys = new Object[2];
|
||||
SimpleDataStructure[] keys = new SimpleDataStructure[2];
|
||||
keys[0] = new PublicKey();
|
||||
keys[1] = new PrivateKey();
|
||||
byte[] k0 = aalpha.toByteArray();
|
||||
@ -97,8 +97,8 @@ public class KeyGenerator {
|
||||
// bigInteger.toByteArray returns SIGNED integers, but since they'return positive,
|
||||
// signed two's complement is the same as unsigned
|
||||
|
||||
((PublicKey) keys[0]).setData(padBuffer(k0, PublicKey.KEYSIZE_BYTES));
|
||||
((PrivateKey) keys[1]).setData(padBuffer(k1, PrivateKey.KEYSIZE_BYTES));
|
||||
keys[0].setData(padBuffer(k0, PublicKey.KEYSIZE_BYTES));
|
||||
keys[1].setData(padBuffer(k1, PrivateKey.KEYSIZE_BYTES));
|
||||
|
||||
return keys;
|
||||
}
|
||||
@ -120,8 +120,8 @@ public class KeyGenerator {
|
||||
* index 1 is a SigningPrivateKey
|
||||
* @return pair of keys
|
||||
*/
|
||||
public Object[] generateSigningKeypair() {
|
||||
Object[] keys = new Object[2];
|
||||
public SimpleDataStructure[] generateSigningKeypair() {
|
||||
SimpleDataStructure[] keys = new SimpleDataStructure[2];
|
||||
BigInteger x = null;
|
||||
|
||||
// make sure the random key is less than the DSA q
|
||||
@ -135,8 +135,8 @@ public class KeyGenerator {
|
||||
byte k0[] = padBuffer(y.toByteArray(), SigningPublicKey.KEYSIZE_BYTES);
|
||||
byte k1[] = padBuffer(x.toByteArray(), SigningPrivateKey.KEYSIZE_BYTES);
|
||||
|
||||
((SigningPublicKey) keys[0]).setData(k0);
|
||||
((SigningPrivateKey) keys[1]).setData(k1);
|
||||
keys[0].setData(k0);
|
||||
keys[1].setData(k1);
|
||||
return keys;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,9 @@ public final class SHA256Generator {
|
||||
_useGnu = useGnu;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param context unused
|
||||
*/
|
||||
public SHA256Generator(I2PAppContext context) {
|
||||
_digests = new LinkedBlockingQueue(32);
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ public class SessionKey extends SimpleDataStructure {
|
||||
*/
|
||||
@Override
|
||||
public void setData(byte[] data) {
|
||||
_data = data;
|
||||
super.setData(data);
|
||||
_preparedKey = null;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,9 @@ import net.i2p.data.DataHelper;
|
||||
*/
|
||||
public class ReusableGZIPOutputStream extends ResettableGZIPOutputStream {
|
||||
// Apache Harmony 5.0M13 Deflater doesn't work after reset()
|
||||
private static final boolean ENABLE_CACHING = !System.getProperty("java.vendor").startsWith("Apache");
|
||||
// Neither does Android
|
||||
private static final boolean ENABLE_CACHING = !(System.getProperty("java.vendor").startsWith("Apache") ||
|
||||
System.getProperty("java.vendor").contains("Android"));
|
||||
private static final LinkedBlockingQueue<ReusableGZIPOutputStream> _available;
|
||||
static {
|
||||
if (ENABLE_CACHING)
|
||||
|
@ -27,9 +27,10 @@ package org.bouncycastle.crypto.macs;
|
||||
*/
|
||||
|
||||
//import org.bouncycastle.crypto.CipherParameters;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.i2p.util.SimpleByteCache;
|
||||
|
||||
import org.bouncycastle.crypto.Digest;
|
||||
import org.bouncycastle.crypto.Mac;
|
||||
|
||||
@ -65,6 +66,11 @@ implements Mac
|
||||
{
|
||||
this(digest, digest.getDigestSize());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sz override the digest's size
|
||||
* SEE NOTES in HMACGenerator about why this isn't compatible with standard HmacMD5
|
||||
*/
|
||||
public I2PHMac(
|
||||
Digest digest, int sz)
|
||||
{
|
||||
@ -165,28 +171,14 @@ implements Mac
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* list of buffers - index 0 is the cache for 32 byte arrays, while index 1 is the cache for 16 byte arrays
|
||||
*/
|
||||
private static ArrayList _tmpBuf[] = new ArrayList[] { new ArrayList(), new ArrayList() };
|
||||
private static byte[] acquireTmp(int sz) {
|
||||
byte rv[] = null;
|
||||
synchronized (_tmpBuf[sz == 32 ? 0 : 1]) {
|
||||
if (!_tmpBuf[sz == 32 ? 0 : 1].isEmpty())
|
||||
rv = (byte[])_tmpBuf[sz == 32 ? 0 : 1].remove(0);
|
||||
}
|
||||
if (rv != null)
|
||||
byte[] rv = SimpleByteCache.acquire(sz);
|
||||
Arrays.fill(rv, (byte)0x0);
|
||||
else
|
||||
rv = new byte[sz];
|
||||
return rv;
|
||||
}
|
||||
|
||||
private static void releaseTmp(byte buf[]) {
|
||||
if (buf == null) return;
|
||||
synchronized (_tmpBuf[buf.length == 32 ? 0 : 1]) {
|
||||
if (_tmpBuf[buf.length == 32 ? 0 : 1].size() < 100)
|
||||
_tmpBuf[buf.length == 32 ? 0 : 1].add((Object)buf);
|
||||
}
|
||||
SimpleByteCache.release(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
|
12
history.txt
12
history.txt
@ -1,3 +1,15 @@
|
||||
2011-06-02 zzz
|
||||
* Android: Build fixes
|
||||
* Crypto:
|
||||
- HMAC Javadocs and cleanups
|
||||
- HMAC Use SimpleByteCache
|
||||
* ElGamalAESEngine: Fixups required after SessionKey enforcement
|
||||
* Reseed: Give up on a seed after 90% of fetches fail
|
||||
* SessionKey: Enforce data size and prevent reuse
|
||||
|
||||
2011-06-02 sponge
|
||||
* Remove txt file in BOB.jar as per zzz's request.
|
||||
|
||||
2011-06-01 zzz
|
||||
* Crypto:
|
||||
- Use java.security.MessageDigest instead of bundled GNU SHA-256 code
|
||||
|
@ -150,12 +150,15 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
int len = Hash.HASH_LENGTH + 1 + 4; // key+type+replyToken
|
||||
if (_replyToken > 0)
|
||||
len += 4 + Hash.HASH_LENGTH; // replyTunnel+replyGateway
|
||||
if (_dbEntry.getType() == DatabaseEntry.KEY_TYPE_LEASESET) {
|
||||
int type = _dbEntry.getType();
|
||||
if (type == DatabaseEntry.KEY_TYPE_LEASESET) {
|
||||
_byteCache = _dbEntry.toByteArray();
|
||||
} else if (_dbEntry.getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
||||
} else if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
||||
byte uncompressed[] = _dbEntry.toByteArray();
|
||||
_byteCache = DataHelper.compress(uncompressed);
|
||||
len += 2;
|
||||
} else {
|
||||
throw new IllegalStateException("Invalid key type " + type);
|
||||
}
|
||||
len += _byteCache.length;
|
||||
return len;
|
||||
@ -166,7 +169,7 @@ public class DatabaseStoreMessage extends I2NPMessageImpl {
|
||||
if (_dbEntry == null) throw new I2NPMessageException("Missing entry");
|
||||
int type = _dbEntry.getType();
|
||||
if (type != DatabaseEntry.KEY_TYPE_LEASESET && type != DatabaseEntry.KEY_TYPE_ROUTERINFO)
|
||||
throw new I2NPMessageException("Invalid key type");
|
||||
throw new I2NPMessageException("Invalid key type " + type);
|
||||
|
||||
// Use the hash of the DatabaseEntry
|
||||
System.arraycopy(getKey().getData(), 0, out, curIndex, Hash.HASH_LENGTH);
|
||||
|
@ -311,8 +311,11 @@ public class Router {
|
||||
}
|
||||
|
||||
public RouterInfo getRouterInfo() { return _routerInfo; }
|
||||
|
||||
public void setRouterInfo(RouterInfo info) {
|
||||
_routerInfo = info;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("setRouterInfo() : " + info, new Exception("I did it"));
|
||||
if (info != null)
|
||||
_context.jobQueue().addJob(new PersistRouterInfoJob(_context));
|
||||
}
|
||||
@ -614,6 +617,10 @@ public class Router {
|
||||
}
|
||||
}
|
||||
// hard and ugly
|
||||
if (System.getProperty("wrapper.version") != null)
|
||||
_log.log(Log.CRIT, "Restarting with new router identity");
|
||||
else
|
||||
_log.log(Log.CRIT, "Shutting down because old router identity was invalid - restart I2P");
|
||||
finalShutdown(EXIT_HARD_RESTART);
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ public class RouterContext extends I2PAppContext {
|
||||
|
||||
public void initAll() {
|
||||
if (getBooleanProperty("i2p.dummyClientFacade"))
|
||||
System.err.println("i2p.dummpClientFacade currently unsupported");
|
||||
System.err.println("i2p.dummyClientFacade currently unsupported");
|
||||
_clientManagerFacade = new ClientManagerFacadeImpl(this);
|
||||
// removed since it doesn't implement InternalClientManager for now
|
||||
//else
|
||||
|
@ -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 = 15;
|
||||
public final static long BUILD = 16;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
@ -65,7 +65,7 @@ public class PublishLocalRouterInfoJob extends JobImpl {
|
||||
try {
|
||||
getContext().netDb().publish(ri);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
_log.log(Log.CRIT, "Error publishing our identity - corrupt?", iae);
|
||||
_log.log(Log.CRIT, "Error publishing our identity - corrupt? Restart required", iae);
|
||||
getContext().router().rebuildNewIdentity();
|
||||
}
|
||||
} catch (DataFormatException dfe) {
|
||||
|
@ -344,6 +344,9 @@ public class Reseeder {
|
||||
} catch (IOException e) {
|
||||
errors++;
|
||||
}
|
||||
// Give up on this one after 10 with only 0 or 1 good
|
||||
if (errors >= 10 && fetched <= 1)
|
||||
break;
|
||||
}
|
||||
System.err.println("Reseed got " + fetched + " router infos from " + seedURL + " with " + errors + " errors");
|
||||
|
||||
|
Reference in New Issue
Block a user