diff --git a/router/doc/tunnel.html b/router/doc/tunnel.html
index a7dc686290faaf36526f0ff2662fb84e3e3171ce..fd12648050181007a05967c048ecfb7d9aa031be 100644
--- a/router/doc/tunnel.html
+++ b/router/doc/tunnel.html
@@ -1,4 +1,4 @@
-<code>$Id: tunnel.html,v 1.8 2005/01/15 01:43:35 jrandom Exp $</code>
+<code>$Id: tunnel.html,v 1.9 2005/01/15 19:08:14 jrandom Exp $</code>
 <pre>
 1) <a href="#tunnel.overview">Tunnel overview</a>
 2) <a href="#tunnel.operation">Tunnel operation</a>
@@ -146,8 +146,8 @@ verify the integrity of the checksum block.  The specific details follow.</p>
 
 <p>The encryption used is such that decryption
 merely requires running over the data with AES in CBC mode, calculating the
-SHA256 of a certain fixed portion of the message (bytes 16 through $size-288),
-and searching for that hash in the checksum block.  There is a fixed number 
+SHA256 of a certain fixed portion of the message (bytes 16 through $size-144),
+and searching for the first 16 bytes of that hash in the checksum block.  There is a fixed number 
 of hops defined (8 peers) so that we can verify the message
 without either leaking the position in the tunnel or having the message 
 continually "shrink" as layers are peeled off.  For tunnels shorter than 8
@@ -239,7 +239,7 @@ To visualize this a bit:</p>
 </table>
 
 <p>In the above, P[7] is the same as the original data being passed through the
-tunnel (the preprocessed messages), and V[7] is the SHA256 of eH[0-7] as seen on
+tunnel (the preprocessed messages), and V[7] is the first 16 bytes of the SHA256 of eH[0-7] as seen on
 peer7 after decryption.  For
 cells in the matrix "higher up" than the hash, their value is derived by encrypting
 the cell below it with the key for the peer below it, using the end of the column 
@@ -268,8 +268,8 @@ peer who is the first hop (usually the peer1.recv row) and forward that entirely
 
 <p>When a participant in a tunnel receives a message, they decrypt a layer with their
 tunnel key using AES256 in CBC mode with the first 16 bytes as the IV.  They then
-calculate the hash of what they see as the payload (bytes 16 through $size-288) and
-search for that hash within the decrypted checksum block.  If no match is found, the
+calculate the hash of what they see as the payload (bytes 16 through $size-144) and
+search for that first 16 bytes of that hash within the decrypted checksum block.  If no match is found, the
 message is discarded.  Otherwise, the IV is updated by decrypting it, XORing that value
 with the IV_WHITENER, and replacing it with the first 16 bytes of its hash.  The 
 resulting message is then forwarded on to the next peer for processing.</p>
@@ -287,7 +287,7 @@ contained in the tunnel.</p>
 <p>When a message reaches the tunnel endpoint, they decrypts and verifies it like
 a normal participant.  If the checksum block has a valid match, the endpoint then
 computes the hash of the checksum block itself (as seen after decryption) and compares
-that to the decrypted verification hash (the last 32 bytes).  If that verification
+that to the decrypted verification hash (the last 16 bytes).  If that verification
 hash does not match, the endpoint takes note of the tagging attempt by one of the
 tunnel participants and perhaps discards the message.</p>
 
@@ -328,7 +328,7 @@ and handling fragmentation will not immediately be implemented.</p>
 
 <p>One alternative to the above process is to remove the checksum block
 completely and replace the verification hash with a plain hash of the payload.
-This would simplify processing at the tunnel gateway and save 256 bytes of
+This would simplify processing at the tunnel gateway and save 144 bytes of
 bandwidth at each hop.  On the other hand, attackers within the tunnel could
 trivially adjust the message size to one which is easily traceable by 
 colluding external observers in addition to later tunnel participants.  The
@@ -344,7 +344,7 @@ there are three alternatives that can be explored:</p>
 <ul>
 <li>Delay a message within a tunnel at an arbitrary hop for either a specified
 amount of time or a randomized period.  This could be achieved by replacing the
-hash in the checksum block with e.g. the first 16 bytes of the hash, followed by
+hash in the checksum block with e.g. the first 8 bytes of the hash, followed by
 some delay instructions.  Alternately, the instructions could tell the 
 participant to actually interpret the raw payload as it is, and either discard
 the message or continue to forward it down the path (where it would be
@@ -378,12 +378,14 @@ minimize the worries of the predecessor attack, though if it were desired,
 it wouldn't be much trouble to build both the inbound and outbound tunnels
 along the same peers.</p>
 
-<h4>2.7.4) <a name="tunnel.smallerhashes">Use smaller hashes</a></h4>
+<h4>2.7.4) <a name="tunnel.smallerhashes">Use smaller blocksize</a></h4>
 
-<p>At the moment, the plan is to reuse the existing SHA256 code and build
-all of the checksum and verification hashes as 32 byte SHA256 values.  20
-byte SHA1 would likely be more than sufficient, or perhaps smaller - first
-4 bytes of the SHA256.</p>
+<p>At the moment, our use of AES limits our block size to 16 bytes, which
+in turn provides the minimum size for each of the checksum block columns.
+If another algorithm was used with a smaller block size, or could otherwise
+allow the safe building of the checksum block with smaller portions of the
+hash, it might be worth exploring.  The 16 bytes used now at each hop should
+be more than sufficient.</p>
 
 <h2>3) <a name="tunnel.building">Tunnel building</a></h2>
 
diff --git a/router/java/src/net/i2p/router/tunnel/GatewayMessage.java b/router/java/src/net/i2p/router/tunnel/GatewayMessage.java
index 12f122afac41f155b8b7d13fa6245bb306e16602..8f9ffbfde5a1bc9660c47baff06d72cd71817967 100644
--- a/router/java/src/net/i2p/router/tunnel/GatewayMessage.java
+++ b/router/java/src/net/i2p/router/tunnel/GatewayMessage.java
@@ -53,6 +53,10 @@ public class GatewayMessage {
     private static final byte EMPTY[] = new byte[0];
     private static final int COLUMNS = HOPS;
     private static final int HASH_ROWS = HOPS;
+    /** # bytes of the hash to maintain in each column */
+    static final int COLUMN_WIDTH = IV_SIZE;
+    /** # bytes of the verification hash to maintain */
+    static final int VERIFICATION_WIDTH = IV_SIZE;
     
     /** used to munge the IV during per-hop translations */
     static final byte IV_WHITENER[] = new byte[] { (byte)0x31, (byte)0xd6, (byte)0x74, (byte)0x17, 
@@ -70,9 +74,9 @@ public class GatewayMessage {
         _iv = new byte[HOPS-1][IV_SIZE];
         _eIV = new byte[HOPS-1][IV_SIZE];
         _H = new byte[HOPS][Hash.HASH_LENGTH];
-        _eH = new byte[COLUMNS][HASH_ROWS][Hash.HASH_LENGTH];
-        _preV = new byte[HOPS*Hash.HASH_LENGTH];
-        _V = new byte[Hash.HASH_LENGTH];
+        _eH = new byte[COLUMNS][HASH_ROWS][COLUMN_WIDTH];
+        _preV = new byte[HOPS*COLUMN_WIDTH];
+        _V = new byte[VERIFICATION_WIDTH];
         _order = new int[HOPS];
         _encrypted = false;
         _payloadSet = false;
@@ -224,24 +228,19 @@ public class GatewayMessage {
             // which _H[hash] value are we rendering in this column?
             int hash = _order[column];
             // fill in the cleartext version for this column
-            System.arraycopy(_H[hash], 0, _eH[column][hash], 0, Hash.HASH_LENGTH);
+            System.arraycopy(_H[hash], 0, _eH[column][hash], 0, COLUMN_WIDTH);
             
             // now fill in the "earlier" _eH[column][row-1] values for earlier hops 
-            // by encrypting _eH[column][row] with the peer's key, using the end 
-            // of the previous column (or _eIV[row-1]) as the IV
+            // by encrypting _eH[column][row] with the peer's key, using the 
+            // previous column (or _eIV[row-1]) as the IV
             for (int row = hash; row > 0; row--) {
                 SessionKey key = cfg.getSessionKey(row);
-                // first half
                 if (column == 0) {
                     DataHelper.xor(_eIV[row-1], 0, _eH[column][row], 0, _eH[column][row-1], 0, IV_SIZE);
                 } else {
-                    DataHelper.xor(_eH[column-1][row-1], IV_SIZE, _eH[column][row], 0, _eH[column][row-1], 0, IV_SIZE);
+                    DataHelper.xor(_eH[column-1][row-1], 0, _eH[column][row], 0, _eH[column][row-1], 0, IV_SIZE);
                 }
                 _context.aes().encryptBlock(_eH[column][row-1], 0, key, _eH[column][row-1], 0);
-                
-                // second half
-                DataHelper.xor(_eH[column][row-1], 0, _eH[column][row], IV_SIZE, _eH[column][row-1], IV_SIZE, IV_SIZE);
-                _context.aes().encryptBlock(_eH[column][row-1], IV_SIZE, key, _eH[column][row-1], IV_SIZE);
             }
             
             // fill in the "later" rows by encrypting the previous rows with the 
@@ -255,10 +254,7 @@ public class GatewayMessage {
                 if (column == 0)
                     DataHelper.xor(_eIV[row-1], 0, _eH[column][row], 0, _eH[column][row], 0, IV_SIZE);
                 else
-                    DataHelper.xor(_eH[column-1][row-1], IV_SIZE, _eH[column][row], 0, _eH[column][row], 0, IV_SIZE);
-                
-                _context.aes().decryptBlock(_eH[column][row-1], IV_SIZE, key, _eH[column][row], IV_SIZE);
-                DataHelper.xor(_eH[column][row-1], 0, _eH[column][row], IV_SIZE, _eH[column][row], IV_SIZE, IV_SIZE);
+                    DataHelper.xor(_eH[column-1][row-1], 0, _eH[column][row], 0, _eH[column][row], 0, IV_SIZE);
             }
         }
         
@@ -268,7 +264,7 @@ public class GatewayMessage {
                 for (int column = 0; column < COLUMNS; column++) {
                     try {
                         _log.debug("_eH[" + column + "][" + peer + "] = " + Base64.encode(_eH[column][peer]) 
-                                   + (peer == 0 ? "" : DataHelper.eq(_H[peer-1], _eH[column][peer]) ? " CLEARTEXT" : ""));
+                                   + (peer == 0 ? "" : DataHelper.eq(_H[peer-1], 0, _eH[column][peer], 0, COLUMN_WIDTH) ? " CLEARTEXT" : ""));
                     } catch (Exception e) {
                         e.printStackTrace();
                         System.out.println("column="+column + " peer=" + peer);
@@ -285,9 +281,9 @@ public class GatewayMessage {
      */
     private final void encryptVerificationHash(GatewayTunnelConfig cfg) {
         for (int i = 0; i < COLUMNS; i++)
-            System.arraycopy(_eH[i][HASH_ROWS-1], 0, _preV, i * Hash.HASH_LENGTH, Hash.HASH_LENGTH);
+            System.arraycopy(_eH[i][HASH_ROWS-1], 0, _preV, i * COLUMN_WIDTH, COLUMN_WIDTH);
         Hash v = _context.sha().calculateHash(_preV);
-        System.arraycopy(v.getData(), 0, _V, 0, Hash.HASH_LENGTH);
+        System.arraycopy(v.getData(), 0, _V, 0, VERIFICATION_WIDTH);
 
         if (_log.shouldLog(Log.DEBUG))
             _log.debug("_V final = " + Base64.encode(_V));
@@ -296,10 +292,8 @@ public class GatewayMessage {
             SessionKey key = cfg.getSessionKey(i);
             // xor the last block of the encrypted payload with the first block of _V to
             // continue the CTR operation
-            DataHelper.xor(_V, 0, _eH[COLUMNS-1][i-1], IV_SIZE, _V, 0, IV_SIZE);
+            DataHelper.xor(_V, 0, _eH[COLUMNS-1][i-1], 0, _V, 0, IV_SIZE);
             _context.aes().encryptBlock(_V, 0, key, _V, 0);
-            DataHelper.xor(_V, 0, _V, IV_SIZE, _V, IV_SIZE, IV_SIZE);
-            _context.aes().encryptBlock(_V, IV_SIZE, key, _V, IV_SIZE);
             
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("_V at peer " + i + " = " + Base64.encode(_V));
@@ -317,8 +311,8 @@ public class GatewayMessage {
     public final int getExportedSize() { 
         return IV_SIZE +
                _payload.length +
-               COLUMNS * Hash.HASH_LENGTH +
-               Hash.HASH_LENGTH; // verification hash
+               COLUMNS * COLUMN_WIDTH +
+               VERIFICATION_WIDTH; // verification hash
     }
     
     /**
@@ -343,11 +337,11 @@ public class GatewayMessage {
         System.arraycopy(_payload, 0, target, cur, _payload.length);
         cur += _payload.length;
         for (int column = 0; column < COLUMNS; column++) {
-            System.arraycopy(_eH[column][0], 0, target, cur, Hash.HASH_LENGTH);
-            cur += Hash.HASH_LENGTH;
+            System.arraycopy(_eH[column][0], 0, target, cur, COLUMN_WIDTH);
+            cur += COLUMN_WIDTH;
         }
-        System.arraycopy(_V, 0, target, cur, Hash.HASH_LENGTH);
-        cur += Hash.HASH_LENGTH;
+        System.arraycopy(_V, 0, target, cur, VERIFICATION_WIDTH);
+        cur += VERIFICATION_WIDTH;
         return cur;
     }
     
@@ -360,13 +354,13 @@ public class GatewayMessage {
         Log log = ctx.logManager().getLog(GatewayMessage.class);
         boolean match = true;
         
-        int off = message.length - (COLUMNS + 1) * Hash.HASH_LENGTH;
+        int off = message.length - (COLUMNS + 1) * COLUMN_WIDTH;
         for (int column = 0; column < COLUMNS; column++) {
-            boolean ok = DataHelper.eq(_eH[column][peer], 0, message, off, Hash.HASH_LENGTH);
+            boolean ok = DataHelper.eq(_eH[column][peer], 0, message, off, COLUMN_WIDTH);
             if (log.shouldLog(Log.DEBUG))
                 log.debug("checksum[" + column + "][" + (peer) + "] matches?  " + ok);
             
-            off += Hash.HASH_LENGTH;
+            off += COLUMN_WIDTH;
             match = match && ok;
         }
         
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelMessageProcessor.java b/router/java/src/net/i2p/router/tunnel/TunnelMessageProcessor.java
index f6ef511dd29f9df3ccd5aa8810c494b8ddb9633a..1c17f66c44274d78e578ef5629f587c313874201 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelMessageProcessor.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelMessageProcessor.java
@@ -14,6 +14,8 @@ import net.i2p.util.Log;
 public class TunnelMessageProcessor {
     private static final int IV_SIZE = GatewayMessage.IV_SIZE;
     private static final int HOPS = GatewayMessage.HOPS;
+    private static final int COLUMN_WIDTH = GatewayMessage.COLUMN_WIDTH;
+    private static final int VERIFICATION_WIDTH = GatewayMessage.VERIFICATION_WIDTH;
     
     /**
      * Unwrap the tunnel message, overwriting it with the decrypted version.
@@ -28,8 +30,8 @@ public class TunnelMessageProcessor {
         
         int payloadLength = data.length 
                             - IV_SIZE // IV
-                            - HOPS * Hash.HASH_LENGTH // checksum blocks 
-                            - Hash.HASH_LENGTH; // verification of the checksum blocks
+                            - HOPS * COLUMN_WIDTH // checksum blocks 
+                            - VERIFICATION_WIDTH; // verification of the checksum blocks
         
         Hash recvPayloadHash = ctx.sha().calculateHash(data, IV_SIZE, payloadLength);
         if (log.shouldLog(Log.DEBUG))
@@ -60,7 +62,7 @@ public class TunnelMessageProcessor {
         
         int numBlocks = (data.length - IV_SIZE) / IV_SIZE;
         // for debugging, so we can compare eIV
-        int numPayloadBlocks = (data.length - IV_SIZE - 2 * IV_SIZE * (GatewayMessage.HOPS + 1)) / IV_SIZE;
+        int numPayloadBlocks = (data.length - IV_SIZE - COLUMN_WIDTH * HOPS - VERIFICATION_WIDTH) / IV_SIZE;
         
         // prev == previous encrypted block (or IV for the first block)
         byte prev[] = new byte[IV_SIZE];
@@ -103,23 +105,23 @@ public class TunnelMessageProcessor {
         Log log = getLog(ctx);
         int matchFound = -1;
         
-        int off = data.length - (GatewayMessage.HOPS + 1) * Hash.HASH_LENGTH;
-        for (int i = 0; i < GatewayMessage.HOPS; i++) {
-            if (DataHelper.eq(payloadHash.getData(), 0, data, off, Hash.HASH_LENGTH)) {
+        int off = data.length - HOPS * COLUMN_WIDTH - VERIFICATION_WIDTH;
+        for (int i = 0; i < HOPS; i++) {
+            if (DataHelper.eq(payloadHash.getData(), 0, data, off, COLUMN_WIDTH)) {
                 matchFound = i;
                 break;
             }
             
-            off += Hash.HASH_LENGTH;
+            off += COLUMN_WIDTH;
         }
         
         if (log.shouldLog(Log.DEBUG)) {
-            off = data.length - (GatewayMessage.HOPS + 1) * Hash.HASH_LENGTH;
+            off = data.length - HOPS * COLUMN_WIDTH - VERIFICATION_WIDTH;
             for (int i = 0; i < HOPS; i++)
-                log.debug("checksum[" + i + "] = " + Base64.encode(data, off + i*Hash.HASH_LENGTH, Hash.HASH_LENGTH)
+                log.debug("checksum[" + i + "] = " + Base64.encode(data, off + i*COLUMN_WIDTH, COLUMN_WIDTH)
                           + (i == matchFound ? " * MATCH" : ""));
             
-            log.debug("verification = " + Base64.encode(data, data.length - Hash.HASH_LENGTH, Hash.HASH_LENGTH));
+            log.debug("verification = " + Base64.encode(data, data.length - VERIFICATION_WIDTH, VERIFICATION_WIDTH));
         }
         
         return matchFound != -1;
@@ -132,15 +134,15 @@ public class TunnelMessageProcessor {
      * @return true if the checksum is valid, false if it has been modified
      */
     public boolean verifyChecksum(I2PAppContext ctx, byte message[]) {
-        int checksumSize = GatewayMessage.HOPS * Hash.HASH_LENGTH;
-        int offset = message.length - (checksumSize + Hash.HASH_LENGTH);
+        int checksumSize = HOPS * COLUMN_WIDTH;
+        int offset = message.length - (checksumSize + VERIFICATION_WIDTH);
         Hash checksumHash = ctx.sha().calculateHash(message, offset, checksumSize);
         getLog(ctx).debug("Measured checksum: " + checksumHash.toBase64());
-        byte expected[] = new byte[Hash.HASH_LENGTH];
-        System.arraycopy(message, message.length-Hash.HASH_LENGTH, expected, 0, Hash.HASH_LENGTH);
+        byte expected[] = new byte[VERIFICATION_WIDTH];
+        System.arraycopy(message, message.length-VERIFICATION_WIDTH, expected, 0, VERIFICATION_WIDTH);
         getLog(ctx).debug("Expected checksum: " + Base64.encode(expected));
         
-        return DataHelper.eq(checksumHash.getData(), 0, message, message.length-Hash.HASH_LENGTH, Hash.HASH_LENGTH);
+        return DataHelper.eq(checksumHash.getData(), 0, message, message.length-VERIFICATION_WIDTH, VERIFICATION_WIDTH);
     }
     
     private static final Log getLog(I2PAppContext ctx) {