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 = "";