diff --git a/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java new file mode 100644 index 0000000000000000000000000000000000000000..eb1e0af46b62981516419b6e01e750619076ffac --- /dev/null +++ b/core/java/src/net/i2p/client/datagram/I2PDatagramDissector.java @@ -0,0 +1,160 @@ +package net.i2p.client.datagram; +/* + * free (adj.): unencumbered; not under the control of others + * Written by human in 2004 and released into the public domain + * with no warranty of any kind, either expressed or implied. + * It probably won't make your computer catch on fire, or eat + * your children, but it might. Use at your own risk. + * + */ + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import net.i2p.crypto.DSAEngine; +import net.i2p.crypto.SHA256Generator; +import net.i2p.data.DataFormatException; +import net.i2p.data.Destination; +import net.i2p.data.Signature; +import net.i2p.util.HexDump; +import net.i2p.util.Log; + +/** + * Class for dissecting I2P repliable datagrams, checking the authenticity of + * the sender. Note that objects of this class are NOT THREAD SAFE! + * + * @author human + */ +public final class I2PDatagramDissector { + + private static Log _log = new Log(I2PDatagramDissector.class); + + private static int DGRAM_BUFSIZE = 32768; + + private DSAEngine dsaEng = DSAEngine.getInstance(); + private SHA256Generator hashGen = SHA256Generator.getInstance(); + + private byte[] rxHashBytes = null; + + private Signature rxSign = new Signature(); + + private Destination rxDest = new Destination(); + + private byte[] rxPayload = new byte[DGRAM_BUFSIZE]; + + private int rxPayloadLen = 0; + + /** + * Crate a new I2P repliable datagram dissector. + */ + public I2PDatagramDissector() {} + + /** + * Load an I2P repliable datagram into the dissector. + * + * @param dgram I2P repliable datagram to be loader + * + * @throws DataFormatException If there's an error in the datagram format + */ + public void loadI2PDatagram(byte[] dgram) throws DataFormatException { + ByteArrayInputStream dgStream = new ByteArrayInputStream(dgram); + byte[] hashedData; + + + try { + rxSign.readBytes(dgStream); + + rxDest.readBytes(dgStream); + rxPayloadLen = dgStream.read(rxPayload); + + hashedData = new byte[dgram.length - Signature.SIGNATURE_BYTES]; + System.arraycopy(dgram, Signature.SIGNATURE_BYTES, + hashedData, 0, + hashedData.length); + rxHashBytes = hashGen.calculateHash(hashedData).toByteArray(); + } catch (IOException e) { + _log.error("Caught IOException - INCONSISTENT STATE!", e); + } + + //_log.debug("Datagram payload size: " + rxPayloadLen + "; content:\n" + // + HexDump.dump(rxPayload, 0, rxPayloadLen)); + } + + /** + * Get the payload carried by an I2P repliable datagram (previously loaded + * with the loadI2PDatagram() method), verifying the datagram signature. + * + * @return A byte array containing the datagram payload + * + * @throws I2PInvalidDatagramException if the signature verification fails + */ + public byte[] getPayload() throws I2PInvalidDatagramException { + if (!dsaEng.verifySignature(rxSign, rxHashBytes, + rxDest.getSigningPublicKey())) { + throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature"); + } + + byte[] retPayload = new byte[rxPayloadLen]; + System.arraycopy(rxPayload, 0, retPayload, 0, rxPayloadLen); + + return retPayload; + } + + /** + * Get the sender of an I2P repliable datagram (previously loaded with the + * loadI2PDatagram() method), verifying the datagram signature. + * + * @return The Destination of the I2P repliable datagram sender + * + * @throws I2PInvalidDatagramException if the signature verification fails + */ + public Destination getSender() throws I2PInvalidDatagramException { + if (!dsaEng.verifySignature(rxSign, rxHashBytes, + rxDest.getSigningPublicKey())) { + throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature"); + } + + Destination retDest = new Destination(); + try { + retDest.fromByteArray(rxDest.toByteArray()); + } catch (DataFormatException e) { + _log.error("Caught DataFormatException", e); + return null; + } + + return retDest; + } + + /** + * Extract the payload carried by an I2P repliable datagram (previously + * loaded with the loadI2PDatagram() method), without verifying the + * datagram signature. + * + * @return A byte array containing the datagram payload + */ + public byte[] extractPayload() { + byte[] retPayload = new byte[rxPayloadLen]; + System.arraycopy(rxPayload, 0, retPayload, 0, rxPayloadLen); + + return retPayload; + } + + /** + * Extract the sender of an I2P repliable datagram (previously loaded with + * the loadI2PDatagram() method), without verifying the datagram signature. + * + * @return The Destination of the I2P repliable datagram sender + */ + public Destination extractSender() { + Destination retDest = new Destination(); + try { + retDest.fromByteArray(rxDest.toByteArray()); + } catch (DataFormatException e) { + _log.error("Caught DataFormatException", e); + return null; + } + + return retDest; + } +} diff --git a/core/java/src/net/i2p/client/datagram/I2PDatagramMaker.java b/core/java/src/net/i2p/client/datagram/I2PDatagramMaker.java new file mode 100644 index 0000000000000000000000000000000000000000..600397948d129511e5c0825c360cce5696dce437 --- /dev/null +++ b/core/java/src/net/i2p/client/datagram/I2PDatagramMaker.java @@ -0,0 +1,84 @@ +package net.i2p.client.datagram; +/* + * free (adj.): unencumbered; not under the control of others + * Written by human in 2004 and released into the public domain + * with no warranty of any kind, either expressed or implied. + * It probably won't make your computer catch on fire, or eat + * your children, but it might. Use at your own risk. + * + */ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import net.i2p.client.I2PSession; +import net.i2p.crypto.DSAEngine; +import net.i2p.crypto.SHA256Generator; +import net.i2p.data.DataFormatException; +import net.i2p.data.SigningPrivateKey; +import net.i2p.util.Log; + +/** + * Class for creating I2P repliable datagrams. Note that objects of this class + * are NOT THREAD SAFE! + * + * @author human + */ +public final class I2PDatagramMaker { + + private static Log _log = new Log(I2PDatagramMaker.class); + + private static int DGRAM_BUFSIZE = 32768; + + private SHA256Generator hashGen = SHA256Generator.getInstance(); + private DSAEngine dsaEng = DSAEngine.getInstance(); + + private SigningPrivateKey sxPrivKey = null; + private byte[] sxDestBytes = null; + + private ByteArrayOutputStream sxBuf = new ByteArrayOutputStream(DGRAM_BUFSIZE); + private ByteArrayOutputStream sxDGram = new ByteArrayOutputStream(DGRAM_BUFSIZE); + + /** + * Construct a new I2PDatagramMaker that will be able to create I2P + * repliable datagrams going to be sent through the specified I2PSession. + * + * @param session I2PSession used to send I2PDatagrams through + */ + public I2PDatagramMaker(I2PSession session) { + sxPrivKey = session.getPrivateKey(); + sxDestBytes = session.getMyDestination().toByteArray(); + } + + /** + * Make a repliable I2P datagram containing the specified payload. + * + * @param payload Bytes to be contained in the I2P datagram. + */ + public byte[] makeI2PDatagram(byte[] payload) { + byte[] hashedData; + + sxBuf.reset(); + sxDGram.reset(); + + try { + sxBuf.write(sxDestBytes); + sxBuf.write(payload); + + hashedData = sxBuf.toByteArray(); + + dsaEng.sign(hashGen.calculateHash(hashedData).toByteArray(), + sxPrivKey).writeBytes(sxDGram); + + sxDGram.write(hashedData); + + return sxDGram.toByteArray(); + } catch (IOException e) { + _log.error("Caught IOException", e); + return null; + } catch (DataFormatException e) { + _log.error("Caught DataFormatException", e); + return null; + } + } +} diff --git a/core/java/src/net/i2p/client/datagram/I2PInvalidDatagramException.java b/core/java/src/net/i2p/client/datagram/I2PInvalidDatagramException.java new file mode 100644 index 0000000000000000000000000000000000000000..97756a7768c57f3490544abc7859106540358f8a --- /dev/null +++ b/core/java/src/net/i2p/client/datagram/I2PInvalidDatagramException.java @@ -0,0 +1,25 @@ +package net.i2p.client.datagram; +/* + * free (adj.): unencumbered; not under the control of others + * Written by human in 2004 and released into the public domain + * with no warranty of any kind, either expressed or implied. + * It probably won't make your computer catch on fire, or eat + * your children, but it might. Use at your own risk. + * + */ + +/** + * Exception thrown when I2P repliable datagram signature verification fails. + * + * @author human + */ +public class I2PInvalidDatagramException extends Exception { + + public I2PInvalidDatagramException() { + super(); + } + + public I2PInvalidDatagramException(String s) { + super(s); + } +}