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.I2PAppContext;
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
* are NOT THREAD SAFE!
*
* @author human
*/
public final class I2PDatagramMaker {
private static final int DGRAM_BUFSIZE = 32768;
private final SHA256Generator hashGen = SHA256Generator.getInstance();
private final DSAEngine dsaEng = DSAEngine.getInstance();
private SigningPrivateKey sxPrivKey;
private byte[] sxDestBytes;
private final 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) {
this.setI2PDatagramMaker(session);
}
/**
* Construct a new I2PDatagramMaker that is null.
* Use setI2PDatagramMaker to set the parameters.
*/
public I2PDatagramMaker() {
// nop
}
public void setI2PDatagramMaker(I2PSession session) {
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");
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);
log.error("Caught IOException", e);
return null;
} catch (DataFormatException e) {
Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramMaker.class);
log.error("Caught DataFormatException", e);
return null;
}
}
}