diff --git a/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java
index d22bafd2e5244dfdfd9e66a100692cc5a07ad2a6..246af5403a13eb4a609eb577677ed6170941612a 100644
--- a/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java
+++ b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java
@@ -19,6 +19,7 @@ import net.i2p.data.DataFormatException;
 import net.i2p.data.Destination;
 import net.i2p.data.Hash;
 import net.i2p.data.Signature;
+import net.i2p.data.SigningPublicKey;
 import net.i2p.util.Log;
 
 /**
@@ -34,16 +35,11 @@ public final class I2PDatagramDissector {
     private final DSAEngine dsaEng = DSAEngine.getInstance();
     private final SHA256Generator hashGen = SHA256Generator.getInstance();
 
-    private Hash rxHash;
-
+    private byte[] rxHash;
     private Signature rxSign;
-
     private Destination rxDest;
-
     private final byte[] rxPayload = new byte[DGRAM_BUFSIZE];
-
     private int rxPayloadLen;
-    
     private boolean valid;
 
     /**
@@ -56,6 +52,17 @@ public final class I2PDatagramDissector {
      * Load an I2P repliable datagram into the dissector.
      * Does NOT verify the signature.
      *
+     * Format is:
+     * <ol>
+     * <li>Destination (387+ bytes)
+     * <li>Signature (40+ bytes, type and length as implied by signing key type in the Destination)
+     * <li>Payload
+     * </ol>
+     *
+     * For DSA_SHA1 Destinations, the signature is of the SHA-256 Hash of the payload.
+     *
+     * As of 0.9.14, for non-DSA_SHA1 Destinations, the signature is of the payload itself.
+     *
      * @param dgram non-null I2P repliable datagram to be loaded
      *
      * @throws DataFormatException If there's an error in the datagram format
@@ -79,14 +86,21 @@ public final class I2PDatagramDissector {
             rxPayloadLen = dgStream.read(rxPayload);
             
             // calculate the hash of the payload
-            this.rxHash = hashGen.calculateHash(rxPayload, 0, rxPayloadLen);
-            assert this.hashGen.calculateHash(this.extractPayload()).equals(this.rxHash);
+            if (type == SigType.DSA_SHA1) {
+                if (rxHash == null)
+                    rxHash = new byte[Hash.HASH_LENGTH];
+                // non-caching
+                hashGen.calculateHash(rxPayload, 0, rxPayloadLen, rxHash, 0);
+                //assert this.hashGen.calculateHash(this.extractPayload()).equals(this.rxHash);
+            } else {
+                rxHash = null;
+            }
         } catch (IOException e) {
             Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramDissector.class);
             log.error("Caught IOException - INCONSISTENT STATE!", e);
-        } catch(AssertionError e) {
-            Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramDissector.class);
-            log.error("Assertion failed!", e);
+        //} catch(AssertionError e) {
+        //    Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramDissector.class);
+        //    log.error("Assertion failed!", e);
         }
         
         //_log.debug("Datagram payload size: " + rxPayloadLen + "; content:\n"
@@ -125,14 +139,16 @@ public final class I2PDatagramDissector {
      * Extract the hash of the payload of an I2P repliable datagram (previously
      * loaded with the loadI2PDatagram() method), verifying the datagram
      * signature.
+     *
+     * As of 0.9.14, for signature types other than DSA_SHA1, this returns null.
+     *
      * @return The hash of the payload of the I2P repliable datagram
      * @throws I2PInvalidDatagramException if the signature verification fails
      */
     public Hash getHash() throws I2PInvalidDatagramException {
         // make sure it has a valid signature
         this.verifySignature();
-        
-        return this.extractHash();
+        return extractHash();
     }
     
     /**
@@ -178,10 +194,18 @@ public final class I2PDatagramDissector {
      * Extract the hash of the payload of an I2P repliable datagram (previously
      * loaded with the loadI2PDatagram() method), without verifying the datagram
      * signature.
+     *
+     * As of 0.9.14, for signature types other than DSA_SHA1, this returns null.
+     *
      * @return The hash of the payload of the I2P repliable datagram
      */
     public Hash extractHash() {
-        return this.rxHash;
+        if (rxHash == null)
+            return null;
+        // make a copy as we will reuse rxHash
+        byte[] hash = new byte[Hash.HASH_LENGTH];
+        System.arraycopy(rxHash, 0, hash, 0, Hash.HASH_LENGTH);
+        return new Hash(hash);
     }
     
     /**
@@ -194,12 +218,21 @@ public final class I2PDatagramDissector {
         if(this.valid)
             return;
         
-        if (rxSign == null || rxSign.getData() == null || rxDest == null || rxDest.getSigningPublicKey() == null)
+        if (rxSign == null || rxSign.getData() == null || rxDest == null)
             throw new I2PInvalidDatagramException("Datagram not yet read");
-        
+
         // now validate
-        if (!this.dsaEng.verifySignature(rxSign, rxHash.getData(), rxDest.getSigningPublicKey()))
-            throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature");
+        SigningPublicKey spk = rxDest.getSigningPublicKey();
+        SigType type = spk.getType();
+        if (type == null)
+            throw new I2PInvalidDatagramException("unsupported sig type");
+        if (type == SigType.DSA_SHA1) {
+            if (!this.dsaEng.verifySignature(rxSign, rxHash, spk))
+                throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature");
+        } else {
+            if (!this.dsaEng.verifySignature(rxSign, rxPayload, 0, rxPayloadLen, spk))
+                throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature");
+        }
         
         // set validated
         this.valid = true;
diff --git a/core/java/src/net/i2p/client/datagram/I2PDatagramMaker.java b/core/java/src/net/i2p/client/datagram/I2PDatagramMaker.java
index 28336e4126f6e47190b041eda262b4601eba13bb..015bbdbcd881111d6db487614d6a9c6df38cf81b 100644
--- a/core/java/src/net/i2p/client/datagram/I2PDatagramMaker.java
+++ b/core/java/src/net/i2p/client/datagram/I2PDatagramMaker.java
@@ -16,8 +16,12 @@ import net.i2p.client.I2PSession;
 import net.i2p.crypto.DSAEngine;
 import net.i2p.crypto.SHA256Generator;
 import net.i2p.data.DataFormatException;
+import net.i2p.data.Hash;
+import net.i2p.data.Signature;
 import net.i2p.data.SigningPrivateKey;
+import net.i2p.crypto.SigType;
 import net.i2p.util.Log;
+import net.i2p.util.SimpleByteCache;
 
 /**
  * Class for creating I2P repliable datagrams.  Note that objects of this class
@@ -44,9 +48,9 @@ public final class I2PDatagramMaker {
      * @param session I2PSession used to send I2PDatagrams through
      */
     public I2PDatagramMaker(I2PSession session) {
-        this();
         this.setI2PDatagramMaker(session);
     }
+
     /**
      * Construct a new I2PDatagramMaker that is null.
      * Use setI2PDatagramMaker to set the parameters.
@@ -59,22 +63,53 @@ public final class I2PDatagramMaker {
         sxPrivKey = session.getPrivateKey();
         sxDestBytes = session.getMyDestination().toByteArray();
     }
+
     /**
      * Make a repliable I2P datagram containing the specified payload.
      *
+     * Format is:
+     * <ol>
+     * <li>Destination (387+ bytes)
+     * <li>Signature (40+ bytes, type and length as implied by signing key type in the Destination)
+     * <li>Payload
+     * </ol>
+     *
+     * Maximum datagram size is 32768, so maximum payload size is 32341, or less for
+     * non-DSA_SHA1 destinations. Practical maximum is a few KB less due to
+     * ElGamal/AES overhead. 10 KB or less is recommended for best results.
+     *
+     * For DSA_SHA1 Destinations, the signature is of the SHA-256 Hash of the payload.
+     *
+     * As of 0.9.14, for non-DSA_SHA1 Destinations, the signature is of the payload itself.
+     *
      * @param payload non-null Bytes to be contained in the I2P datagram.
+     * @return null on error
+     * @throws IllegalArgumentException if payload is too big
+     * @throws IllegalStateException if Destination signature type unsupported
      */
     public byte[] makeI2PDatagram(byte[] payload) {
         sxDGram.reset();
         
         try {
             sxDGram.write(sxDestBytes);
+            SigType type = sxPrivKey.getType();
+            if (type == null)
+                throw new IllegalStateException("Unsupported sig type");
             
-            dsaEng.sign(hashGen.calculateHash(payload).toByteArray(),
-                        sxPrivKey).writeBytes(sxDGram);
-            
+            Signature sig;
+            if (type == SigType.DSA_SHA1) {
+                byte[] hash = SimpleByteCache.acquire(Hash.HASH_LENGTH);
+                // non-caching
+                hashGen.calculateHash(payload, 0, payload.length, hash, 0);
+                sig = dsaEng.sign(hash, sxPrivKey);
+                SimpleByteCache.release(hash);
+            } else {
+                sig = dsaEng.sign(payload, sxPrivKey);
+            }
+            sig.writeBytes(sxDGram);
             sxDGram.write(payload);
-            
+            if (sxDGram.size() > DGRAM_BUFSIZE)
+                throw new IllegalArgumentException("Too big");
             return sxDGram.toByteArray();
         } catch (IOException e) {
             Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramMaker.class);
diff --git a/core/java/src/net/i2p/client/datagram/package.html b/core/java/src/net/i2p/client/datagram/package.html
index da3a87140013f4fa8d123a53babf17b718f2f6eb..085df6568c6198c4b298b5f129fa9d610a407844 100644
--- a/core/java/src/net/i2p/client/datagram/package.html
+++ b/core/java/src/net/i2p/client/datagram/package.html
@@ -10,6 +10,9 @@ in turn, use the {@link net.i2p.client.datagram.I2PDatagramMaker} to build a
 message that can be parsed. </p>
 
 <p>The datagram format implemented here includes
-the sender's {@link net.i2p.data.Destination}, the payload, and a hash of the 
-payload (signed by the sender's {@link net.i2p.data.SigningPrivateKey}).</p>
+the sender's {@link net.i2p.data.Destination}, the payload, and a signature
+(signed by the sender's {@link net.i2p.data.SigningPrivateKey}).
+For DSA_SHA1 destinations, the signature is of the SHA-256 Hash of the payload.
+For other destination types, the signature is of the payload itself.
+</p>
 </body></html>
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 31c35f8c3e7f27dae7384d36e3443978e8ab5e40..ea17ad55f3270be34b24fd56bd0b1495da601896 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
     /** deprecated */
     public final static String ID = "Monotone";
     public final static String VERSION = CoreVersion.VERSION;
-    public final static long BUILD = 14;
+    public final static long BUILD = 15;
 
     /** for example "-test" */
     public final static String EXTRA = "";