2006-02-26 jrandom

* Switch from the bouncycastle to the gnu-crypto implementation for
      SHA256, as benchmarks show a 10-30% speedup.
    * Removed some unnecessary object caches
    * Don't close i2psnark streams prematurely
This commit is contained in:
jrandom
2006-02-26 21:30:56 +00:00
committed by zzz
parent a2854cf6f6
commit eee21aa301
14 changed files with 677 additions and 479 deletions

View File

@ -93,9 +93,9 @@ public class I2PSnarkUtil {
if (opts.getProperty("i2p.streaming.inactivityTimeout") == null)
opts.setProperty("i2p.streaming.inactivityTimeout", "90000");
if (opts.getProperty("i2p.streaming.inactivityAction") == null)
opts.setProperty("i2p.streaming.inactivityAction", "1");
if (opts.getProperty("i2p.streaming.writeTimeout") == null)
opts.setProperty("i2p.streaming.writeTimeout", "90000");
opts.setProperty("i2p.streaming.inactivityAction", "2"); // 1 == disconnect, 2 == ping
//if (opts.getProperty("i2p.streaming.writeTimeout") == null)
// opts.setProperty("i2p.streaming.writeTimeout", "90000");
//if (opts.getProperty("i2p.streaming.readTimeout") == null)
// opts.setProperty("i2p.streaming.readTimeout", "120000");
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);

View File

@ -0,0 +1,198 @@
package gnu.crypto.hash;
// ----------------------------------------------------------------------------
// $Id: BaseHash.java,v 1.10 2005/10/06 04:24:14 rsdio Exp $
//
// Copyright (C) 2001, 2002, Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// GNU Crypto is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING. If not, write to the
//
// Free Software Foundation Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301
// USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module. An independent module is a module which is
// not derived from or based on this library. If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so. If you do not wish to
// do so, delete this exception statement from your version.
// ----------------------------------------------------------------------------
/**
* <p>A base abstract class to facilitate hash implementations.</p>
*
* @version $Revision: 1.10 $
*/
public abstract class BaseHash implements IMessageDigest {
// Constants and variables
// -------------------------------------------------------------------------
/** The canonical name prefix of the hash. */
protected String name;
/** The hash (output) size in bytes. */
protected int hashSize;
/** The hash (inner) block size in bytes. */
protected int blockSize;
/** Number of bytes processed so far. */
protected long count;
/** Temporary input buffer. */
protected byte[] buffer;
// Constructor(s)
// -------------------------------------------------------------------------
/**
* <p>Trivial constructor for use by concrete subclasses.</p>
*
* @param name the canonical name prefix of this instance.
* @param hashSize the block size of the output in bytes.
* @param blockSize the block size of the internal transform.
*/
protected BaseHash(String name, int hashSize, int blockSize) {
super();
this.name = name;
this.hashSize = hashSize;
this.blockSize = blockSize;
this.buffer = new byte[blockSize];
resetContext();
}
// Class methods
// -------------------------------------------------------------------------
// Instance methods
// -------------------------------------------------------------------------
// IMessageDigest interface implementation ---------------------------------
public String name() {
return name;
}
public int hashSize() {
return hashSize;
}
public int blockSize() {
return blockSize;
}
public void update(byte b) {
// compute number of bytes still unhashed; ie. present in buffer
int i = (int)(count % blockSize);
count++;
buffer[i] = b;
if (i == (blockSize - 1)) {
transform(buffer, 0);
}
}
public void update(byte[] b) {
update(b, 0, b.length);
}
public void update(byte[] b, int offset, int len) {
int n = (int)(count % blockSize);
count += len;
int partLen = blockSize - n;
int i = 0;
if (len >= partLen) {
System.arraycopy(b, offset, buffer, n, partLen);
transform(buffer, 0);
for (i = partLen; i + blockSize - 1 < len; i+= blockSize) {
transform(b, offset + i);
}
n = 0;
}
if (i < len) {
System.arraycopy(b, offset + i, buffer, n, len - i);
}
}
public byte[] digest() {
byte[] tail = padBuffer(); // pad remaining bytes in buffer
update(tail, 0, tail.length); // last transform of a message
byte[] result = getResult(); // make a result out of context
reset(); // reset this instance for future re-use
return result;
}
public void reset() { // reset this instance for future re-use
count = 0L;
for (int i = 0; i < blockSize; ) {
buffer[i++] = 0;
}
resetContext();
}
// methods to be implemented by concrete subclasses ------------------------
public abstract Object clone();
public abstract boolean selfTest();
/**
* <p>Returns the byte array to use as padding before completing a hash
* operation.</p>
*
* @return the bytes to pad the remaining bytes in the buffer before
* completing a hash operation.
*/
protected abstract byte[] padBuffer();
/**
* <p>Constructs the result from the contents of the current context.</p>
*
* @return the output of the completed hash operation.
*/
protected abstract byte[] getResult();
/** Resets the instance for future re-use. */
protected abstract void resetContext();
/**
* <p>The block digest transformation per se.</p>
*
* @param in the <i>blockSize</i> long block, as an array of bytes to digest.
* @param offset the index where the data to digest is located within the
* input buffer.
*/
protected abstract void transform(byte[] in, int offset);
}

View File

@ -0,0 +1,141 @@
package gnu.crypto.hash;
// ----------------------------------------------------------------------------
// $Id: IMessageDigest.java,v 1.11 2005/10/06 04:24:14 rsdio Exp $
//
// Copyright (C) 2001, 2002, Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// GNU Crypto is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING. If not, write to the
//
// Free Software Foundation Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301
// USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module. An independent module is a module which is
// not derived from or based on this library. If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so. If you do not wish to
// do so, delete this exception statement from your version.
// ----------------------------------------------------------------------------
/**
* <p>The basic visible methods of any hash algorithm.</p>
*
* <p>A hash (or message digest) algorithm produces its output by iterating a
* basic compression function on blocks of data.</p>
*
* @version $Revision: 1.11 $
*/
public interface IMessageDigest extends Cloneable {
// Constants
// -------------------------------------------------------------------------
// Methods
// -------------------------------------------------------------------------
/**
* <p>Returns the canonical name of this algorithm.</p>
*
* @return the canonical name of this instance.
*/
String name();
/**
* <p>Returns the output length in bytes of this message digest algorithm.</p>
*
* @return the output length in bytes of this message digest algorithm.
*/
int hashSize();
/**
* <p>Returns the algorithm's (inner) block size in bytes.</p>
*
* @return the algorithm's inner block size in bytes.
*/
int blockSize();
/**
* <p>Continues a message digest operation using the input byte.</p>
*
* @param b the input byte to digest.
*/
void update(byte b);
/**
* <p>Continues a message digest operation, by filling the buffer, processing
* data in the algorithm's HASH_SIZE-bit block(s), updating the context and
* count, and buffering the remaining bytes in buffer for the next
* operation.</p>
*
* @param in the input block.
*/
void update(byte[] in);
/**
* <p>Continues a message digest operation, by filling the buffer, processing
* data in the algorithm's HASH_SIZE-bit block(s), updating the context and
* count, and buffering the remaining bytes in buffer for the next
* operation.</p>
*
* @param in the input block.
* @param offset start of meaningful bytes in input block.
* @param length number of bytes, in input block, to consider.
*/
void update(byte[] in, int offset, int length);
/**
* <p>Completes the message digest by performing final operations such as
* padding and resetting the instance.</p>
*
* @return the array of bytes representing the hash value.
*/
byte[] digest();
/**
* <p>Resets the current context of this instance clearing any eventually cached
* intermediary values.</p>
*/
void reset();
/**
* <p>A basic test. Ensures that the digest of a pre-determined message is equal
* to a known pre-computed value.</p>
*
* @return <tt>true</tt> if the implementation passes a basic self-test.
* Returns <tt>false</tt> otherwise.
*/
boolean selfTest();
/**
* <p>Returns a clone copy of this instance.</p>
*
* @return a clone copy of this instance.
*/
Object clone();
}

View File

@ -0,0 +1,262 @@
package gnu.crypto.hash;
// ----------------------------------------------------------------------------
// $Id: Sha256.java,v 1.2 2005/10/06 04:24:14 rsdio Exp $
//
// Copyright (C) 2003 Free Software Foundation, Inc.
//
// This file is part of GNU Crypto.
//
// GNU Crypto is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//
// GNU Crypto is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; see the file COPYING. If not, write to the
//
// Free Software Foundation Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA 02110-1301
// USA
//
// Linking this library statically or dynamically with other modules is
// making a combined work based on this library. Thus, the terms and
// conditions of the GNU General Public License cover the whole
// combination.
//
// As a special exception, the copyright holders of this library give
// you permission to link this library with independent modules to
// produce an executable, regardless of the license terms of these
// independent modules, and to copy and distribute the resulting
// executable under terms of your choice, provided that you also meet,
// for each linked independent module, the terms and conditions of the
// license of that module. An independent module is a module which is
// not derived from or based on this library. If you modify this
// library, you may extend this exception to your version of the
// library, but you are not obligated to do so. If you do not wish to
// do so, delete this exception statement from your version.
// ----------------------------------------------------------------------------
//import gnu.crypto.util.Util;
/**
* <p>Implementation of SHA2-1 [SHA-256] per the IETF Draft Specification.</p>
*
* <p>References:</p>
* <ol>
* <li><a href="http://ftp.ipv4.heanet.ie/pub/ietf/internet-drafts/draft-ietf-ipsec-ciph-aes-cbc-03.txt">
* Descriptions of SHA-256, SHA-384, and SHA-512</a>,</li>
* <li>http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf</li>
* </ol>
*
* Modified by jrandom@i2p.net to remove unnecessary gnu-crypto dependencies, and
* renamed from Sha256 to avoid conflicts with JVMs using gnu-crypto as their JCE
* provider.
*
* @version $Revision: 1.2 $
*/
public class Sha256Standalone extends BaseHash {
// Constants and variables
// -------------------------------------------------------------------------
private static final int[] k = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
private static final int BLOCK_SIZE = 64; // inner block size in bytes
private static final String DIGEST0 =
"BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD";
private static final int[] w = new int[64];
/** caches the result of the correctness test, once executed. */
private static Boolean valid;
/** 256-bit interim result. */
private int h0, h1, h2, h3, h4, h5, h6, h7;
// Constructor(s)
// -------------------------------------------------------------------------
/** Trivial 0-arguments constructor. */
public Sha256Standalone() {
super("sha256/standalone", 32, BLOCK_SIZE);
}
/**
* <p>Private constructor for cloning purposes.</p>
*
* @param md the instance to clone.
*/
private Sha256Standalone(Sha256Standalone md) {
this();
this.h0 = md.h0;
this.h1 = md.h1;
this.h2 = md.h2;
this.h3 = md.h3;
this.h4 = md.h4;
this.h5 = md.h5;
this.h6 = md.h6;
this.h7 = md.h7;
this.count = md.count;
this.buffer = (byte[]) md.buffer.clone();
}
// Class methods
// -------------------------------------------------------------------------
public static final int[] G(int hh0, int hh1, int hh2, int hh3, int hh4,
int hh5, int hh6, int hh7, byte[] in, int offset) {
return sha(hh0, hh1, hh2, hh3, hh4, hh5, hh6, hh7, in, offset);
}
// Instance methods
// -------------------------------------------------------------------------
// java.lang.Cloneable interface implementation ----------------------------
public Object clone() {
return new Sha256Standalone(this);
}
// Implementation of concrete methods in BaseHash --------------------------
protected void transform(byte[] in, int offset) {
int[] result = sha(h0, h1, h2, h3, h4, h5, h6, h7, in, offset);
h0 = result[0];
h1 = result[1];
h2 = result[2];
h3 = result[3];
h4 = result[4];
h5 = result[5];
h6 = result[6];
h7 = result[7];
}
protected byte[] padBuffer() {
int n = (int) (count % BLOCK_SIZE);
int padding = (n < 56) ? (56 - n) : (120 - n);
byte[] result = new byte[padding + 8];
// padding is always binary 1 followed by binary 0s
result[0] = (byte) 0x80;
// save number of bits, casting the long to an array of 8 bytes
long bits = count << 3;
result[padding++] = (byte)(bits >>> 56);
result[padding++] = (byte)(bits >>> 48);
result[padding++] = (byte)(bits >>> 40);
result[padding++] = (byte)(bits >>> 32);
result[padding++] = (byte)(bits >>> 24);
result[padding++] = (byte)(bits >>> 16);
result[padding++] = (byte)(bits >>> 8);
result[padding ] = (byte) bits;
return result;
}
protected byte[] getResult() {
return new byte[] {
(byte)(h0 >>> 24), (byte)(h0 >>> 16), (byte)(h0 >>> 8), (byte) h0,
(byte)(h1 >>> 24), (byte)(h1 >>> 16), (byte)(h1 >>> 8), (byte) h1,
(byte)(h2 >>> 24), (byte)(h2 >>> 16), (byte)(h2 >>> 8), (byte) h2,
(byte)(h3 >>> 24), (byte)(h3 >>> 16), (byte)(h3 >>> 8), (byte) h3,
(byte)(h4 >>> 24), (byte)(h4 >>> 16), (byte)(h4 >>> 8), (byte) h4,
(byte)(h5 >>> 24), (byte)(h5 >>> 16), (byte)(h5 >>> 8), (byte) h5,
(byte)(h6 >>> 24), (byte)(h6 >>> 16), (byte)(h6 >>> 8), (byte) h6,
(byte)(h7 >>> 24), (byte)(h7 >>> 16), (byte)(h7 >>> 8), (byte) h7
};
}
protected void resetContext() {
// magic SHA-256 initialisation constants
h0 = 0x6a09e667;
h1 = 0xbb67ae85;
h2 = 0x3c6ef372;
h3 = 0xa54ff53a;
h4 = 0x510e527f;
h5 = 0x9b05688c;
h6 = 0x1f83d9ab;
h7 = 0x5be0cd19;
}
public boolean selfTest() {
if (valid == null) {
Sha256Standalone md = new Sha256Standalone();
md.update((byte) 0x61); // a
md.update((byte) 0x62); // b
md.update((byte) 0x63); // c
String result = "broken"; //Util.toString(md.digest());
valid = new Boolean(DIGEST0.equals(result));
}
return valid.booleanValue();
}
// SHA specific methods ----------------------------------------------------
private static final synchronized int[]
sha(int hh0, int hh1, int hh2, int hh3, int hh4, int hh5, int hh6, int hh7, byte[] in, int offset) {
int A = hh0;
int B = hh1;
int C = hh2;
int D = hh3;
int E = hh4;
int F = hh5;
int G = hh6;
int H = hh7;
int r, T, T2;
for (r = 0; r < 16; r++) {
w[r] = in[offset++] << 24 |
(in[offset++] & 0xFF) << 16 |
(in[offset++] & 0xFF) << 8 |
(in[offset++] & 0xFF);
}
for (r = 16; r < 64; r++) {
T = w[r - 2];
T2 = w[r - 15];
w[r] = (((T >>> 17) | (T << 15)) ^ ((T >>> 19) | (T << 13)) ^ (T >>> 10)) + w[r - 7] + (((T2 >>> 7) | (T2 << 25)) ^ ((T2 >>> 18) | (T2 << 14)) ^ (T2 >>> 3)) + w[r - 16];
}
for (r = 0; r < 64; r++) {
T = H + (((E >>> 6) | (E << 26)) ^ ((E >>> 11) | (E << 21)) ^ ((E >>> 25) | (E << 7))) + ((E & F) ^ (~E & G)) + k[r] + w[r];
T2 = (((A >>> 2) | (A << 30)) ^ ((A >>> 13) | (A << 19)) ^ ((A >>> 22) | (A << 10))) + ((A & B) ^ (A & C) ^ (B & C));
H = G;
G = F;
F = E;
E = D + T;
D = C;
C = B;
B = A;
A = T + T2;
}
return new int[] {
hh0 + A, hh1 + B, hh2 + C, hh3 + D, hh4 + E, hh5 + F, hh6 + G, hh7 + H
};
}
}

View File

@ -54,7 +54,7 @@ import java.util.Iterator;
import java.util.Map;
import java.util.HashMap;
import org.bouncycastle.crypto.digests.SHA256Digest;
import gnu.crypto.hash.Sha256Standalone;
import net.i2p.crypto.CryptixRijndael_Algorithm;
import net.i2p.crypto.CryptixAESKeyCache;
@ -91,7 +91,7 @@ import net.i2p.crypto.CryptixAESKeyCache;
* Bruce Schneier). ISBN 0-471-22357-3.</li>
* </ul>
*
* Modified by jrandom for I2P to use Bouncycastle's SHA256, Cryptix's AES,
* Modified by jrandom for I2P to use a standalone gnu-crypto SHA256, Cryptix's AES,
* to strip out some unnecessary dependencies and increase the buffer size.
* Renamed from Fortuna to FortunaStandalone so it doesn't conflict with the
* gnu-crypto implementation, which has been imported into GNU/classpath
@ -106,7 +106,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
private static final int NUM_POOLS = 32;
private static final int MIN_POOL_SIZE = 64;
private final Generator generator;
private final SHA256Digest[] pools;
private final Sha256Standalone[] pools;
private long lastReseed;
private int pool;
private int pool0Count;
@ -118,9 +118,9 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
{
super("Fortuna i2p");
generator = new Generator();
pools = new SHA256Digest[NUM_POOLS];
pools = new Sha256Standalone[NUM_POOLS];
for (int i = 0; i < NUM_POOLS; i++)
pools[i] = new SHA256Digest();
pools[i] = new Sha256Standalone();
lastReseed = 0;
pool = 0;
pool0Count = 0;
@ -143,8 +143,6 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
generator.init(attributes);
}
/** fillBlock is not thread safe, so will be locked anyway */
private byte fillBlockBuf[] = new byte[32];
public void fillBlock()
{
if (pool0Count >= MIN_POOL_SIZE
@ -155,9 +153,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
for (int i = 0; i < NUM_POOLS; i++)
{
if (reseedCount % (1 << i) == 0) {
byte buf[] = fillBlockBuf;//new byte[32];
pools[i].doFinal(buf, 0);
generator.addRandomBytes(buf);//pools[i].digest());
generator.addRandomBytes(pools[i].digest());
}
}
lastReseed = System.currentTimeMillis();
@ -221,7 +217,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
private static final int LIMIT = 1 << 20;
private final SHA256Digest hash;
private final Sha256Standalone hash;
private final byte[] counter;
private final byte[] key;
/** current encryption key built from the keying material */
@ -232,7 +228,7 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
public Generator ()
{
super("Fortuna.generator.i2p");
this.hash = new SHA256Digest();
this.hash = new Sha256Standalone();
counter = new byte[16]; //cipher.defaultBlockSize()];
buffer = new byte[16]; //cipher.defaultBlockSize()];
int keysize = 32;
@ -285,9 +281,9 @@ public class FortunaStandalone extends BasePRNGStandalone implements Serializabl
{
hash.update(key, 0, key.length);
hash.update(seed, offset, length);
//byte[] newkey = hash.digest();
//System.arraycopy(newkey, 0, key, 0, Math.min(key.length, newkey.length));
hash.doFinal(key, 0);
byte[] newkey = hash.digest();
System.arraycopy(newkey, 0, key, 0, Math.min(key.length, newkey.length));
//hash.doFinal(key, 0);
resetKey();
incrementCounter();
seeded = true;

View File

@ -11,11 +11,10 @@ import net.i2p.crypto.CryptixAESEngine;
import net.i2p.crypto.DSAEngine;
import net.i2p.crypto.DummyDSAEngine;
import net.i2p.crypto.DummyElGamalEngine;
import net.i2p.crypto.DummyHMACSHA256Generator;
import net.i2p.crypto.DummyPooledRandomSource;
import net.i2p.crypto.ElGamalAESEngine;
import net.i2p.crypto.ElGamalEngine;
import net.i2p.crypto.HMACSHA256Generator;
import net.i2p.crypto.HMACGenerator;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.PersistentSessionKeyManager;
import net.i2p.crypto.SHA256Generator;
@ -67,7 +66,7 @@ public class I2PAppContext {
private ElGamalAESEngine _elGamalAESEngine;
private AESEngine _AESEngine;
private LogManager _logManager;
private HMACSHA256Generator _hmac;
private HMACGenerator _hmac;
private SHA256Generator _sha;
private Clock _clock;
private DSAEngine _dsa;
@ -342,17 +341,14 @@ public class I2PAppContext {
* other than for consistency, and perhaps later we'll want to
* include some stats.
*/
public HMACSHA256Generator hmac() {
public HMACGenerator hmac() {
if (!_hmacInitialized) initializeHMAC();
return _hmac;
}
private void initializeHMAC() {
synchronized (this) {
if (_hmac == null) {
if ("true".equals(getProperty("i2p.fakeHMAC", "false")))
_hmac = new DummyHMACSHA256Generator(this);
else
_hmac= new HMACSHA256Generator(this);
_hmac= new HMACGenerator(this);
}
_hmacInitialized = true;
}

View File

@ -1,52 +0,0 @@
package net.i2p.crypto;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
/**
* Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs
* in {@link org.bouncycastle.crypto.macs.HMac} and
* {@link org.bouncycastle.crypto.digests.SHA256Digest}.
*
*/
public class DummyHMACSHA256Generator extends HMACSHA256Generator {
private I2PAppContext _context;
public DummyHMACSHA256Generator(I2PAppContext context) {
super(context);
_context = context;
}
public static HMACSHA256Generator getInstance() {
return I2PAppContext.getGlobalContext().hmac();
}
/**
* Calculate the HMAC of the data with the given key
*/
public Hash calculate(SessionKey key, byte data[]) {
if ((key == null) || (key.getData() == null) || (data == null))
throw new NullPointerException("Null arguments for HMAC");
return calculate(key, data, 0, data.length);
}
/**
* Calculate the HMAC of the data with the given key
*/
public Hash calculate(SessionKey key, byte data[], int offset, int length) {
if ((key == null) || (key.getData() == null) || (data == null))
throw new NullPointerException("Null arguments for HMAC");
byte rv[] = new byte[Hash.HASH_LENGTH];
System.arraycopy(key.getData(), 0, rv, 0, Hash.HASH_LENGTH);
if (Hash.HASH_LENGTH >= length)
DataHelper.xor(data, offset, rv, 0, rv, 0, length);
else
DataHelper.xor(data, offset, rv, 0, rv, 0, Hash.HASH_LENGTH);
return new Hash(rv);
}
}

View File

@ -8,46 +8,26 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.SessionKey;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.macs.HMac;
/**
* Calculate the HMAC-SHA256 of a key+message. All the good stuff occurs
* Calculate the HMAC-MD5 of a key+message. All the good stuff occurs
* in {@link org.bouncycastle.crypto.macs.HMac} and
* {@link org.bouncycastle.crypto.digests.SHA256Digest}. Alternately, if
* the context property "i2p.HMACMD5" is set to true, then this whole HMAC
* generator will be transformed into HMACMD5, maintaining the same size and
* using {@link org.bouncycastle.crypto.digests.MD5Digest}.
* {@link org.bouncycastle.crypto.digests.MD5Digest}.
*
*/
public class HMACSHA256Generator {
public class HMACGenerator {
private I2PAppContext _context;
/** set of available HMAC instances for calculate */
private List _available;
/** set of available byte[] buffers for verify */
private List _availableTmp;
private boolean _useMD5;
private int _macSize;
public static final boolean DEFAULT_USE_MD5 = true;
public HMACSHA256Generator(I2PAppContext context) {
public HMACGenerator(I2PAppContext context) {
_context = context;
_available = new ArrayList(32);
_availableTmp = new ArrayList(32);
if ("true".equals(context.getProperty("i2p.HMACMD5", Boolean.toString(DEFAULT_USE_MD5).toLowerCase())))
_useMD5 = true;
else
_useMD5 = false;
if ("true".equals(context.getProperty("i2p.HMACBrokenSize", "false")))
_macSize = 32;
else
_macSize = (_useMD5 ? 16 : 32);
}
public static HMACSHA256Generator getInstance() {
return I2PAppContext.getGlobalContext().hmac();
}
/**
@ -61,24 +41,6 @@ public class HMACSHA256Generator {
return new Hash(rv);
}
/**
* Calculate the HMAC of the data with the given key
*/
/*
public Hash calculate(SessionKey key, byte data[], int offset, int length) {
if ((key == null) || (key.getData() == null) || (data == null))
throw new NullPointerException("Null arguments for HMAC");
HMac mac = acquire();
mac.init(key.getData());
mac.update(data, offset, length);
byte rv[] = new byte[Hash.HASH_LENGTH];
mac.doFinal(rv, 0);
release(mac);
return new Hash(rv);
}
*/
/**
* Calculate the HMAC of the data with the given key
*/
@ -131,10 +93,7 @@ public class HMACSHA256Generator {
// 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"
if (_useMD5)
return new HMac(new MD5Digest(), 32);
else
return new HMac(new SHA256Digest(), 32);
return new HMac(new MD5Digest(), 32);
}
private void release(HMac mac) {
synchronized (_available) {

View File

@ -7,17 +7,19 @@ import net.i2p.I2PAppContext;
import net.i2p.data.Base64;
import net.i2p.data.Hash;
import org.bouncycastle.crypto.digests.SHA256Digest;
import gnu.crypto.hash.Sha256Standalone;
/**
* Defines a wrapper for SHA-256 operation. All the good stuff occurs
* in the Bouncycastle {@link org.bouncycastle.crypto.digests.SHA256Digest}
* in the GNU-Crypto {@link gnu.crypto.hash.Sha256Standalone}
*
*/
public final class SHA256Generator {
private List _digests;
private List _digestsGnu;
public SHA256Generator(I2PAppContext context) {
_digests = new ArrayList(32);
_digestsGnu = new ArrayList(32);
}
public static final SHA256Generator getInstance() {
@ -32,47 +34,44 @@ public final class SHA256Generator {
return calculateHash(source, 0, source.length);
}
public final Hash calculateHash(byte[] source, int start, int len) {
byte rv[] = new byte[Hash.HASH_LENGTH];
calculateHash(source, start, len, rv, 0);
Sha256Standalone digest = acquireGnu();
digest.update(source, start, len);
byte rv[] = digest.digest();
releaseGnu(digest);
return new Hash(rv);
}
public final void calculateHash(byte[] source, int start, int len, byte out[], int outOffset) {
SHA256Digest digest = acquire();
Sha256Standalone digest = acquireGnu();
digest.update(source, start, len);
digest.doFinal(out, outOffset);
release(digest);
byte rv[] = digest.digest();
releaseGnu(digest);
System.arraycopy(rv, 0, out, outOffset, rv.length);
}
private SHA256Digest acquire() {
SHA256Digest rv = null;
synchronized (_digests) {
if (_digests.size() > 0)
rv = (SHA256Digest)_digests.remove(0);
private Sha256Standalone acquireGnu() {
Sha256Standalone rv = null;
synchronized (_digestsGnu) {
if (_digestsGnu.size() > 0)
rv = (Sha256Standalone)_digestsGnu.remove(0);
}
if (rv != null)
rv.reset();
else
rv = new SHA256Digest();
rv = new Sha256Standalone();
return rv;
}
private void release(SHA256Digest digest) {
synchronized (_digests) {
if (_digests.size() < 32) {
_digests.add(digest);
private void releaseGnu(Sha256Standalone digest) {
synchronized (_digestsGnu) {
if (_digestsGnu.size() < 32) {
_digestsGnu.add(digest);
}
}
}
public static void main(String args[]) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
byte orig[] = new byte[4096];
ctx.random().nextBytes(orig);
Hash old = ctx.sha().calculateHash(orig);
SHA256Digest d = new SHA256Digest();
d.update(orig, 0, orig.length);
byte out[] = new byte[Hash.HASH_LENGTH];
d.doFinal(out, 0);
System.out.println("eq? " + net.i2p.data.DataHelper.eq(out, old.getData()));
for (int i = 0; i < args.length; i++)
System.out.println("SHA256 [" + args[i] + "] = [" + Base64.encode(ctx.sha().calculateHash(args[i].getBytes()).getData()) + "]");
}

View File

@ -1,292 +0,0 @@
package org.bouncycastle.crypto.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.
*
*/
/**
* FIPS 180-2 implementation of SHA-256.
*
* <pre>
* block word digest
* SHA-1 512 32 160
* SHA-256 512 32 256
* SHA-384 1024 64 384
* SHA-512 1024 64 512
* </pre>
*/
public class SHA256Digest
extends GeneralDigest
{
private static final int DIGEST_LENGTH = 32;
private int H1, H2, H3, H4, H5, H6, H7, H8;
private int[] X = new int[64];
private int xOff;
/**
* Standard constructor
*/
public SHA256Digest()
{
reset();
}
/**
* Copy constructor. This will copy the state of the provided
* message digest.
*/
public SHA256Digest(SHA256Digest t)
{
super(t);
H1 = t.H1;
H2 = t.H2;
H3 = t.H3;
H4 = t.H4;
H5 = t.H5;
H6 = t.H6;
H7 = t.H7;
H8 = t.H8;
System.arraycopy(t.X, 0, X, 0, t.X.length);
xOff = t.xOff;
}
public String getAlgorithmName()
{
return "SHA-256";
}
public int getDigestSize()
{
return DIGEST_LENGTH;
}
protected void processWord(
byte[] in,
int inOff)
{
X[xOff++] = ((in[inOff] & 0xff) << 24) | ((in[inOff + 1] & 0xff) << 16)
| ((in[inOff + 2] & 0xff) << 8) | ((in[inOff + 3] & 0xff));
if (xOff == 16)
{
processBlock();
}
}
private void unpackWord(
int word,
byte[] out,
int outOff)
{
out[outOff] = (byte)(word >>> 24);
out[outOff + 1] = (byte)(word >>> 16);
out[outOff + 2] = (byte)(word >>> 8);
out[outOff + 3] = (byte)word;
}
protected void processLength(
long bitLength)
{
if (xOff > 14)
{
processBlock();
}
X[14] = (int)(bitLength >>> 32);
X[15] = (int)(bitLength & 0xffffffff);
}
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);
unpackWord(H5, out, outOff + 16);
unpackWord(H6, out, outOff + 20);
unpackWord(H7, out, outOff + 24);
unpackWord(H8, out, outOff + 28);
reset();
return DIGEST_LENGTH;
}
/**
* reset the chaining variables
*/
public void reset()
{
super.reset();
/* SHA-256 initial hash value
* The first 32 bits of the fractional parts of the square roots
* of the first eight prime numbers
*/
H1 = 0x6a09e667;
H2 = 0xbb67ae85;
H3 = 0x3c6ef372;
H4 = 0xa54ff53a;
H5 = 0x510e527f;
H6 = 0x9b05688c;
H7 = 0x1f83d9ab;
H8 = 0x5be0cd19;
xOff = 0;
for (int i = 0; i != X.length; i++)
{
X[i] = 0;
}
}
protected void processBlock()
{
//
// expand 16 word block into 64 word blocks.
//
for (int t = 16; t <= 63; t++)
{
X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16];
}
//
// set up working variables.
//
int a = H1;
int b = H2;
int c = H3;
int d = H4;
int e = H5;
int f = H6;
int g = H7;
int h = H8;
for (int t = 0; t <= 63; t++)
{
int T1, T2;
T1 = h + Sum1(e) + Ch(e, f, g) + K[t] + X[t];
T2 = Sum0(a) + Maj(a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
}
H1 += a;
H2 += b;
H3 += c;
H4 += d;
H5 += e;
H6 += f;
H7 += g;
H8 += h;
//
// reset the offset and clean out the word buffer.
//
xOff = 0;
for (int i = 0; i != X.length; i++)
{
X[i] = 0;
}
}
private int rotateRight(
int x,
int n)
{
return (x >>> n) | (x << (32 - n));
}
/* SHA-256 functions */
private int Ch(
int x,
int y,
int z)
{
return ((x & y) ^ ((~x) & z));
}
private int Maj(
int x,
int y,
int z)
{
return ((x & y) ^ (x & z) ^ (y & z));
}
private int Sum0(
int x)
{
return rotateRight(x, 2) ^ rotateRight(x, 13) ^ rotateRight(x, 22);
}
private int Sum1(
int x)
{
return rotateRight(x, 6) ^ rotateRight(x, 11) ^ rotateRight(x, 25);
}
private int Theta0(
int x)
{
return rotateRight(x, 7) ^ rotateRight(x, 18) ^ (x >>> 3);
}
private int Theta1(
int x)
{
return rotateRight(x, 17) ^ rotateRight(x, 19) ^ (x >>> 10);
}
/* SHA-256 Constants
* (represent the first 32 bits of the fractional parts of the
* cube roots of the first sixty-four prime numbers)
*/
static final int K[] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
}

View File

@ -1,4 +1,10 @@
$Id: history.txt,v 1.419 2006/02/24 04:35:52 jrandom Exp $
$Id: history.txt,v 1.420 2006/02/25 15:41:52 jrandom Exp $
2006-02-26 jrandom
* Switch from the bouncycastle to the gnu-crypto implementation for
SHA256, as benchmarks show a 10-30% speedup.
* Removed some unnecessary object caches
* Don't close i2psnark streams prematurely
2006-02-25 jrandom
* Made the Syndie permalinks in the thread view point to the blog view

View File

@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
*
*/
public class RouterVersion {
public final static String ID = "$Revision: 1.360 $ $Date: 2006/02/24 04:35:52 $";
public final static String ID = "$Revision: 1.361 $ $Date: 2006/02/25 15:41:52 $";
public final static String VERSION = "0.6.1.11";
public final static long BUILD = 5;
public final static long BUILD = 6;
public static void main(String args[]) {
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
System.out.println("Router ID: " + RouterVersion.ID);

View File

@ -8,7 +8,6 @@ import java.util.ArrayList;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.crypto.HMACSHA256Generator;
import net.i2p.data.Base64;
import net.i2p.data.ByteArray;
import net.i2p.data.DataHelper;
@ -29,8 +28,9 @@ public class UDPPacket {
private volatile short _priority;
private volatile long _initializeTime;
private volatile long _expiration;
private volatile byte[] _data;
private volatile ByteArray _dataBuf;
private byte[] _data;
private byte[] _validateBuf;
private byte[] _ivBuf;
private volatile int _markedType;
private volatile RemoteHostId _remoteHost;
private volatile boolean _released;
@ -78,9 +78,6 @@ public class UDPPacket {
public static final byte BITFIELD_CONTINUATION = (byte)(1 << 7);
private static final int MAX_VALIDATE_SIZE = MAX_PACKET_SIZE;
private static final ByteCache _validateCache = ByteCache.getInstance(64, MAX_VALIDATE_SIZE);
private static final ByteCache _ivCache = ByteCache.getInstance(64, IV_SIZE);
private static final ByteCache _dataCache = ByteCache.getInstance(64, MAX_PACKET_SIZE);
private UDPPacket(I2PAppContext ctx, boolean inbound) {
ctx.statManager().createRateStat("udp.packetsLiveInbound", "Number of live inbound packets in memory", "udp", new long[] { 60*1000, 5*60*1000 });
@ -89,13 +86,16 @@ public class UDPPacket {
ctx.statManager().createRateStat("udp.packetsLivePendingHandleInbound", "Number of live inbound packets not yet handled fully by the PacketHandler", "udp", new long[] { 60*1000, 5*60*1000 });
ctx.statManager().createRateStat("udp.fetchRemoteSlow", "How long it takes to grab the remote ip info", "udp", new long[] { 60*1000 });
// the data buffer is clobbered on init(..), but we need it to bootstrap
_packet = new DatagramPacket(new byte[MAX_PACKET_SIZE], MAX_PACKET_SIZE);
_data = new byte[MAX_PACKET_SIZE];
_packet = new DatagramPacket(_data, MAX_PACKET_SIZE);
_validateBuf = new byte[MAX_VALIDATE_SIZE];
_ivBuf = new byte[IV_SIZE];
init(ctx, inbound);
}
private void init(I2PAppContext ctx, boolean inbound) {
_context = ctx;
_dataBuf = _dataCache.acquire();
_data = _dataBuf.getData();
//_dataBuf = _dataCache.acquire();
Arrays.fill(_data, (byte)0);
//_packet = new DatagramPacket(_data, MAX_PACKET_SIZE);
_packet.setData(_data);
_isInbound = inbound;
@ -106,21 +106,6 @@ public class UDPPacket {
_released = false;
}
/*
public void initialize(int priority, long expiration, InetAddress host, int port) {
_priority = (short)priority;
_expiration = expiration;
resetBegin();
Arrays.fill(_data, (byte)0x00);
//_packet.setLength(0);
_packet.setAddress(host);
_packet.setPort(port);
_remoteHost = null;
_released = false;
_releasedBy = null;
}
*/
public void writeData(byte src[], int offset, int len) {
verifyNotReleased();
System.arraycopy(src, offset, _data, 0, len);
@ -172,7 +157,7 @@ public class UDPPacket {
verifyNotReleased();
_beforeValidate = _context.clock().now();
boolean eq = false;
ByteArray buf = _validateCache.acquire();
Arrays.fill(_validateBuf, (byte)0);
// validate by comparing _data[0:15] and
// HMAC(payload + IV + (payloadLength ^ protocolVersion), macKey)
@ -180,14 +165,14 @@ public class UDPPacket {
int payloadLength = _packet.getLength() - MAC_SIZE - IV_SIZE;
if (payloadLength > 0) {
int off = 0;
System.arraycopy(_data, _packet.getOffset() + MAC_SIZE + IV_SIZE, buf.getData(), off, payloadLength);
System.arraycopy(_data, _packet.getOffset() + MAC_SIZE + IV_SIZE, _validateBuf, off, payloadLength);
off += payloadLength;
System.arraycopy(_data, _packet.getOffset() + MAC_SIZE, buf.getData(), off, IV_SIZE);
System.arraycopy(_data, _packet.getOffset() + MAC_SIZE, _validateBuf, off, IV_SIZE);
off += IV_SIZE;
DataHelper.toLong(buf.getData(), off, 2, payloadLength ^ PacketBuilder.PROTOCOL_VERSION);
DataHelper.toLong(_validateBuf, off, 2, payloadLength ^ PacketBuilder.PROTOCOL_VERSION);
off += 2;
eq = _context.hmac().verify(macKey, buf.getData(), 0, off, _data, _packet.getOffset(), MAC_SIZE);
eq = _context.hmac().verify(macKey, _validateBuf, 0, off, _data, _packet.getOffset(), MAC_SIZE);
/*
Hash hmac = _context.hmac().calculate(macKey, buf.getData(), 0, off);
@ -211,7 +196,6 @@ public class UDPPacket {
_log.warn("Payload length is " + payloadLength);
}
_validateCache.release(buf);
_afterValidate = _context.clock().now();
_validateCount++;
return eq;
@ -224,11 +208,10 @@ public class UDPPacket {
*/
public void decrypt(SessionKey cipherKey) {
verifyNotReleased();
ByteArray iv = _ivCache.acquire();
System.arraycopy(_data, MAC_SIZE, iv.getData(), 0, IV_SIZE);
Arrays.fill(_ivBuf, (byte)0);
System.arraycopy(_data, MAC_SIZE, _ivBuf, 0, IV_SIZE);
int len = _packet.getLength();
_context.aes().decrypt(_data, _packet.getOffset() + MAC_SIZE + IV_SIZE, _data, _packet.getOffset() + MAC_SIZE + IV_SIZE, cipherKey, iv.getData(), len - MAC_SIZE - IV_SIZE);
_ivCache.release(iv);
_context.aes().decrypt(_data, _packet.getOffset() + MAC_SIZE + IV_SIZE, _data, _packet.getOffset() + MAC_SIZE + IV_SIZE, cipherKey, _ivBuf, len - MAC_SIZE - IV_SIZE);
}
/** the UDPReceiver has tossed it onto the inbound queue */
@ -305,7 +288,7 @@ public class UDPPacket {
//_releasedBy = new Exception("released by");
//_acquiredBy = null;
//
_dataCache.release(_dataBuf);
//_dataCache.release(_dataBuf);
if (!CACHE)
return;
synchronized (_packetCache) {

View File

@ -1291,6 +1291,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
long idleIn = (now-peer.getLastReceiveTime())/1000;
long idleOut = (now-peer.getLastSendTime())/1000;
if (idleIn < 0) idleIn = 0;
if (idleOut < 0) idleOut = 0;
buf.append("<td valign=\"top\" ><code>");
buf.append(idleIn);