From 2c185ea76c456e3af26a1e0f47f566710f6ae7ee Mon Sep 17 00:00:00 2001
From: zzz
+ *
+ *
+ * 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 28336e412..015bbdbcd 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:
+ *
+ *
+ *
+ * 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 da3a87140..085df6568 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.
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}).
+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. +