diff options
Diffstat (limited to 'src/org/noreply/fancydress/type3')
16 files changed, 885 insertions, 0 deletions
diff --git a/src/org/noreply/fancydress/type3/ForwardLeg.java b/src/org/noreply/fancydress/type3/ForwardLeg.java new file mode 100644 index 0000000..4088d11 --- /dev/null +++ b/src/org/noreply/fancydress/type3/ForwardLeg.java @@ -0,0 +1,30 @@ +package org.noreply.fancydress.type3; + +import org.noreply.fancydress.type3.routing.*; +import org.noreply.fancydress.status.*; +import org.noreply.fancydress.crypto.*; + +public class ForwardLeg extends SingleLeg { + private byte[][] sharedKeys; + + public ForwardLeg (HalfPath path, Routing finalRouting) + throws Mix3BadArgumentsChainTooLongException + { + super(); + Hop[] hops = path.getHops(); + + initSharedKeys(hops.length); + makeLeg(hops, sharedKeys, finalRouting); + }; + + private void initSharedKeys(int n) { + sharedKeys = new byte[n][]; + + for (int i=0; i < n; i++) + sharedKeys[i] = CryptoPrimitives.rand(CryptoPrimitives.KEY_LEN); + } + + public byte[][] getSharedKeys() { + return sharedKeys; + } +} diff --git a/src/org/noreply/fancydress/type3/HalfPath.java b/src/org/noreply/fancydress/type3/HalfPath.java new file mode 100644 index 0000000..66e712b --- /dev/null +++ b/src/org/noreply/fancydress/type3/HalfPath.java @@ -0,0 +1,12 @@ +package org.noreply.fancydress.type3; + +public class HalfPath { + private Hop[] hops; + public HalfPath(Hop[] hops) { /* FIXME */ + this.hops = hops; + } + public Hop[] getHops() { + return hops; + } +} + diff --git a/src/org/noreply/fancydress/type3/Hop.java b/src/org/noreply/fancydress/type3/Hop.java new file mode 100644 index 0000000..a9d7dda --- /dev/null +++ b/src/org/noreply/fancydress/type3/Hop.java @@ -0,0 +1,22 @@ +package org.noreply.fancydress.type3; + +import org.noreply.fancydress.type3.routing.*; +import org.noreply.fancydress.crypto.*; + +public class Hop { + private Routing routing; + private RSAPublicKey pubKey; + + public Hop(Routing routing, RSAPublicKey pubKey) { + this.routing = routing; + this.pubKey = pubKey; + } + + public Routing getRouting() { + return routing; + } + + public RSAPublicKey getPubKey() { + return pubKey; + } +} diff --git a/src/org/noreply/fancydress/type3/Packet.java b/src/org/noreply/fancydress/type3/Packet.java new file mode 100644 index 0000000..3d76cbe --- /dev/null +++ b/src/org/noreply/fancydress/type3/Packet.java @@ -0,0 +1,78 @@ +package org.noreply.fancydress.type3; + +import org.noreply.fancydress.misc.Util; +import org.noreply.fancydress.type3.routing.*; +import org.noreply.fancydress.crypto.*; +import org.noreply.fancydress.status.*; +import java.util.*; + +public class Packet { + + private byte[] packet; + private Routing thisRoute; + + public Packet( + Path path, + RoutingDestination address, + Payload payload) + throws Mix3BadArgumentsChainTooLongException + { + ForwardLeg leg2 = new ForwardLeg(path.getSecondHalf(), address); + if (!(leg2.getRoute() instanceof RoutingForward)) + throw new Error("Routing Type of second leg is not a forward type."); + ForwardLeg leg1 = new ForwardLeg(path.getFirstHalf(), ((RoutingForward)leg2.getRoute()).asSwap()); + makePacket(leg1, leg2, payload); + } + + public Packet( + HalfPath path, + SURB surb, + Payload payload) + throws Mix3BadArgumentsChainTooLongException + { + ForwardLeg leg1 = new ForwardLeg(path, surb.getRoute()); + makePacket(leg1, surb, payload); + } + + public static boolean isPacketVersionSupported(String s) { + return s.equals(SingleLeg.MAJOR_VERSION + "." + SingleLeg.MINOR_VERSION); + } + + private void makePacket(ForwardLeg leg1, SingleLeg leg2, Payload payload) { + byte p[] = payload.asOctets(); + byte h1[] = leg1.asOctets(); + byte h2[] = leg2.asOctets(); + + // Phase 1 + if (leg2 instanceof SURB) { + byte[] k = ((SURB) leg2).getEncryptionKey(); + p = CryptoPrimitives.sprpDecrypt(k, "PAYLOAD ENCRYPT", p); + } else { + byte[][] sk = ((ForwardLeg) leg2).getSharedKeys(); + for (int i=sk.length-1; i>=0; i--) + p = CryptoPrimitives.sprpEncrypt(sk[i], "PAYLOAD ENCRYPT", p); + } + + // Phase 2 + h2 = CryptoPrimitives.sprpEncrypt(CryptoPrimitives.hash(p), "HIDE HEADER", h2); + p = CryptoPrimitives.sprpEncrypt(CryptoPrimitives.hash(h2), "HIDE PAYLOAD", p); + + byte[][] sk = leg1.getSharedKeys(); + for (int i=sk.length-1; i>=0; i--) { + h2 = CryptoPrimitives.sprpEncrypt(sk[i], "HEADER ENCRYPT", h2); + p = CryptoPrimitives.sprpEncrypt(sk[i], "PAYLOAD ENCRYPT", p); + } + + packet = Util.concat(Util.concat(h1,h2),p); + thisRoute = leg1.getRoute(); + } + + public Routing getRoute() { + return thisRoute; + } + + public byte[] asOctets() { + return packet; + } +} + diff --git a/src/org/noreply/fancydress/type3/Path.java b/src/org/noreply/fancydress/type3/Path.java new file mode 100644 index 0000000..11fab1f --- /dev/null +++ b/src/org/noreply/fancydress/type3/Path.java @@ -0,0 +1,39 @@ +package org.noreply.fancydress.type3; + +import org.noreply.fancydress.crypto.*; +import org.noreply.fancydress.directory.*; +import org.noreply.fancydress.misc.Util; +import org.noreply.fancydress.type3.routing.*; +import java.net.InetAddress; + +public class Path { + HalfPath first; + HalfPath second; + + public Path(Directory dir) throws Exception { + Hop[] hops1 = new Hop[4]; + Hop[] hops2 = new Hop[2]; + + Server test = dir.getServer("test1"); + ServerDescriptor desc = test.getDescriptor(); + + Routing route1 = new RoutingIP4(InetAddress.getByName("172.22.118.2"), 48099, test.getKeyID()); + hops1[0] = new Hop(route1, desc.getPacketKey()); + hops1[1] = new Hop(route1, desc.getPacketKey()); + hops1[2] = new Hop(route1, desc.getPacketKey()); + hops1[3] = new Hop(route1, desc.getPacketKey()); + hops2[0] = new Hop(route1, desc.getPacketKey()); + hops2[1] = new Hop(route1, desc.getPacketKey()); + + first = new HalfPath(hops1); + second = new HalfPath(hops2); + } + + public HalfPath getFirstHalf() { + return first; + } + public HalfPath getSecondHalf() { + return second; + } +} + diff --git a/src/org/noreply/fancydress/type3/Payload.java b/src/org/noreply/fancydress/type3/Payload.java new file mode 100644 index 0000000..f0cf583 --- /dev/null +++ b/src/org/noreply/fancydress/type3/Payload.java @@ -0,0 +1,93 @@ +package org.noreply.fancydress.type3; + +import java.io.*; +import java.util.zip.*; +import org.noreply.fancydress.crypto.*; +import org.noreply.fancydress.misc.Util; +import org.noreply.fancydress.type3.routing.*; + +public class Payload { + byte[] thisPayload; + + public Payload() { + RoutingSMTP route = new RoutingSMTP("peter@palfrader.org"); + String body = "FROM:Peter\n" + + "SUBJECT:test fancydress\n" + + "\n" + + "blubb\n"; + + /* + // Encode a String into bytes + String inputString = "blahblahblah??"; + byte[] input = inputString.getBytes("UTF-8"); + + // Compress the bytes + byte[] output = new byte[100]; + Deflater compresser = new Deflater(); + compresser.setInput(input); + compresser.finish(); + int compressedDataLength = compresser.deflate(output); + + // Decompress the bytes + Inflater decompresser = new Inflater(); + decompresser.setInput(output, 0, compressedDataLength); + byte[] result = new byte[100]; + int resultLength = decompresser.inflate(result); + decompresser.end(); + */ + try { + PipedOutputStream poMessage = new PipedOutputStream(); + PipedInputStream piMessage = new PipedInputStream( poMessage ); + + Deflater compresser = new Deflater(Deflater.BEST_COMPRESSION); + DeflaterOutputStream compresserStream = new DeflaterOutputStream(poMessage, compresser); + + /* FIXME, do a more clever and robust way of reading/writing */ + + byte[] message = Util.toOctets(body); + compresserStream.write(message, 0, message.length); + compresserStream.close(); + + byte[] compressed = new byte[message.length]; // UGH + int read = piMessage.read(compressed,0,compressed.length); + compressed = Util.slice(compressed, 0, read); + + byte[] padding = CryptoPrimitives.rand( 28*1024 - compressed.length - 22); + + byte[] payload = new byte[28*1024]; + int pos = 0; + + int firstWord = compressed.length; + + payload[pos] = (byte) ( (firstWord >> 8) & 0xff); + pos++; + payload[pos] = (byte) ( firstWord & 0xff); + pos++; + + System.arraycopy(CryptoPrimitives.hash(compressed, padding), 0, payload, pos, CryptoPrimitives.HASH_LEN); + pos += CryptoPrimitives.HASH_LEN; + + System.arraycopy(compressed, 0, payload, pos, compressed.length); + pos += compressed.length; + + System.arraycopy(padding, 0, payload, pos, padding.length); + pos += padding.length; + + if (pos != 28*1024) + throw new Error("did not fill in 28k bytes"); + + thisPayload = payload; + } catch (Exception e) { + throw new Error(e); + }; + + // Return a singleton payload containing: + // Flag 0 | Int(15,LEN(M_C)) | Hash(M_C | PADDING) | M_C | PADDING + + } + + public byte[] asOctets() { + return thisPayload; + } +} + diff --git a/src/org/noreply/fancydress/type3/SURB.java b/src/org/noreply/fancydress/type3/SURB.java new file mode 100644 index 0000000..914c3da --- /dev/null +++ b/src/org/noreply/fancydress/type3/SURB.java @@ -0,0 +1,25 @@ +package org.noreply.fancydress.type3; + +import org.noreply.fancydress.type3.routing.*; +import org.noreply.fancydress.status.*; +import org.noreply.fancydress.crypto.*; + +public class SURB extends SingleLeg { + + public SURB (Path path, RoutingDestination address) throws Mix3BadArgumentsChainTooLongException { + super(); + + byte[][] sharedKeys = {}; + Hop[] hops = new Hop[0]; // path.getHops(); + + /* FIXME */ + + makeLeg ( hops, sharedKeys, address); + + }; + + public byte[] getEncryptionKey() { + return new byte[16]; + } +} + diff --git a/src/org/noreply/fancydress/type3/SingleLeg.java b/src/org/noreply/fancydress/type3/SingleLeg.java new file mode 100644 index 0000000..f4a68c6 --- /dev/null +++ b/src/org/noreply/fancydress/type3/SingleLeg.java @@ -0,0 +1,144 @@ +package org.noreply.fancydress.type3; + +import org.noreply.fancydress.misc.Util; +import org.noreply.fancydress.type3.routing.*; +import org.noreply.fancydress.crypto.*; +import org.noreply.fancydress.status.*; +import java.util.*; + +public abstract class SingleLeg { + public static final int MIN_SH = 42; /* length of the invariant part of a subheader */ + public static final int SINGLE_HEADER_LEN = 2048; /* length of the invariant part of a subheader */ + private byte[] thisLeg = null; + private Routing thisRoute = null; + + public static final int MAJOR_VERSION = 0; + public static final int MINOR_VERSION = 3; + + public SingleLeg() { + } + + public byte[] asOctets() { + if (thisLeg == null) + throw new Error("Leg not yet initialized"); + return thisLeg; + } + + protected void makeLeg( + Hop[] hops, + byte[][] sharedKeys, + Routing finalRouting) + throws Mix3BadArgumentsChainTooLongException + { + if (thisLeg != null) + throw new Error("Leg already initialized"); + + int n = hops.length; + int[] size = new int[n]; + byte[][] junkKey = new byte[n][]; + byte[][] key = new byte[n][]; + byte[][] junk = new byte[n][]; + byte[][] subHeader = new byte[n+1][]; + + // Calculate the sizes of the subheaders + int paddingLength = SINGLE_HEADER_LEN; + for (int i=0; i < n; i++) { + Routing routing = i == n-1 ? finalRouting : hops[i+1].getRouting(); + + size[i] = MIN_SH + RSAPublicKey.PK_OVERHEAD_LEN + routing.getRoutingInformationLength(); + junkKey[i] = CryptoPrimitives.subKey(sharedKeys[i], "RANDOM JUNK"); + key[i] = CryptoPrimitives.subKey(sharedKeys[i], "HEADER SECRET KEY"); + + paddingLength -= size[i]; + } + + if (paddingLength < 0) + throw new Mix3BadArgumentsChainTooLongException("Chain too long"); + + // Calculate the Junk that will be appended during processing. + // J_i is the junk that node i will append, and node i+1 will see. + for (int i=0; i < n; i++) { + byte[] tmp = CryptoPrimitives.prng(junkKey[i], size[i]); + junk[i] = i == 0 ? tmp : Util.concat(junk[i-1], tmp); + byte[] stream = CryptoPrimitives.prng(key[i], SINGLE_HEADER_LEN + size[i]); + // Before we encrypt the junk, we encrypt all the data, and all + // the initial padding, but not the RSA-encrypted part. + int offset = SINGLE_HEADER_LEN - RSAPublicKey.PK_ENC_LEN - (i==0 ? 0 : junk[i-1].length); + junk[i] = CryptoPrimitives.xor(junk[i], Util.slice(stream, offset, junk[i].length)); + } + + // Create the Header, starting with the padding + subHeader[n] = CryptoPrimitives.rand(paddingLength); + for (int i=n-1; i>=0; i--) { + Routing routing = i == n-1 ? finalRouting : hops[i+1].getRouting(); + + byte[] sh0 = makeSHS(sharedKeys[i], CryptoPrimitives.zero(CryptoPrimitives.HASH_LEN), routing); + int shLength = sh0.length; + byte[] h0 = Util.concat( sh0, subHeader[i+1] ); + + byte[] rest = Util.slice( h0, RSAPublicKey.PK_MAX_DATA_LEN, h0.length - RSAPublicKey.PK_MAX_DATA_LEN); + byte[] encryptedRest = CryptoPrimitives.encrypt(key[i], rest); + + byte[] digest = CryptoPrimitives.hash( i == 0 ? encryptedRest : Util.concat(encryptedRest, junk[i-1])); + + byte[] sh = makeSHS(sharedKeys[i], digest, routing); + int underflow = Util.max(RSAPublicKey.PK_MAX_DATA_LEN - shLength, 0); + byte[] rsaPart = Util.concat( sh, Util.slice(h0, RSAPublicKey.PK_MAX_DATA_LEN - underflow, underflow)); + + RSAPublicKey pk = hops[i].getPubKey(); + byte[] esh = pk.encrypt(rsaPart); + subHeader[i] = Util.concat(esh, encryptedRest); + } + byte[] result = subHeader[0]; + + thisLeg = result; + thisRoute = hops[0].getRouting(); + } + + + /** + * create a sub header structure. + */ + static private byte[] makeSHS( + byte[] sharedSecret, + byte[] digest, + Routing routing) + { + if (sharedSecret.length != CryptoPrimitives.KEY_LEN) + throw new Error("sharedSecret must be KEY_LEN bytes long."); + if (digest.length != CryptoPrimitives.HASH_LEN) + throw new Error("digest must be HASH_LEN bytes long."); + byte[] fixedPart = new byte[MIN_SH]; + byte[] dynamicPart = routing.getRoutingInformation(); + int routingSize = dynamicPart.length; + int routingType = routing.getRoutingType(); + + int pos = 0; + fixedPart[pos] = MAJOR_VERSION; + pos++; + fixedPart[pos] = MINOR_VERSION; + pos++; + System.arraycopy(sharedSecret, 0, fixedPart, pos, CryptoPrimitives.KEY_LEN); + pos +=CryptoPrimitives.KEY_LEN; + System.arraycopy(digest, 0, fixedPart, pos, CryptoPrimitives.HASH_LEN); + pos +=CryptoPrimitives.HASH_LEN; + fixedPart[pos] = (byte) ( (routingSize >> 8) & 0xff); + pos++; + fixedPart[pos] = (byte) ( routingSize & 0xff); + pos++; + fixedPart[pos] = (byte) ( (routingType >> 8) & 0xff); + pos++; + fixedPart[pos] = (byte) ( routingType & 0xff); + pos++; + + if (pos != MIN_SH) + throw new Error("Did not fill in MIN_SH bytes!"); + + return Util.concat(fixedPart, dynamicPart); + } + + public Routing getRoute() { + return thisRoute; + } +} + diff --git a/src/org/noreply/fancydress/type3/routing/Routing.java b/src/org/noreply/fancydress/type3/routing/Routing.java new file mode 100644 index 0000000..c87bb53 --- /dev/null +++ b/src/org/noreply/fancydress/type3/routing/Routing.java @@ -0,0 +1,44 @@ +package org.noreply.fancydress.type3.routing; + +/** + * Base class for all Routing classes. + * + * Routing Type and Routing Information are pooled into this one entity. + * + * @see RoutingType + */ +public abstract class Routing { + protected RoutingType type; + + /** + * Default constructor. + * + * @param type The routing type as integer. + */ + protected Routing(int type) { + this.type = new RoutingType(type); + } + + /** + * Return the total length in octets of the routing information. + * + * @return total length in octets of the routing information + */ + public abstract int getRoutingInformationLength(); + + /** + * Return the routing information. + * + * @return routing information + */ + public abstract byte[] getRoutingInformation(); + + /** + * Get the routing type of this instance. + * + * @return routing type + */ + public int getRoutingType() { + return type.getType(); + } +} diff --git a/src/org/noreply/fancydress/type3/routing/RoutingDROP.java b/src/org/noreply/fancydress/type3/routing/RoutingDROP.java new file mode 100644 index 0000000..942a640 --- /dev/null +++ b/src/org/noreply/fancydress/type3/routing/RoutingDROP.java @@ -0,0 +1,39 @@ +package org.noreply.fancydress.type3.routing; + +/** + * The DROP routing as specified in the type III spec. + * + * DROP packets are dropped by the handling server. Their purpose + * is to generate dummy traffic. + * + * The payload of a DROP packet should be random garbage. + */ +public class RoutingDROP extends RoutingDestination { + + /** + * Construct a DROP routing. + */ + public RoutingDROP() { + super (RoutingType.DROP); + } + + /** + * Return the total length in octets of the routing information. + * + * @return total length in octets of the routing information + */ + public int getRoutingInformationLength() { + return 0; + } + + /** + * Get the routing type of this instance. + * + * @return routing type + */ + public byte[] getRoutingInformation() { + byte[] result = {}; + return result; + } +} + diff --git a/src/org/noreply/fancydress/type3/routing/RoutingDestination.java b/src/org/noreply/fancydress/type3/routing/RoutingDestination.java new file mode 100644 index 0000000..306a0bc --- /dev/null +++ b/src/org/noreply/fancydress/type3/routing/RoutingDestination.java @@ -0,0 +1,26 @@ +package org.noreply.fancydress.type3.routing; + +/** + * Base class for all Routings that are final destinations. + * + * Routings of this type usually mean that the message leaves the Type III + * network at the server processing them (for instance to be delivered via + * SMTP). In case of a DROP type it means that the packet is dropped at this + * node. + */ +public abstract class RoutingDestination extends Routing { + /** + * The size of decoding handles for payloads + */ + public static final int DECODINGHANDLE_LEN = 20; + + /** + * Default constructor. + * + * @param type The routing type as integer. + */ + protected RoutingDestination(int type) { + super (type); + } +} + diff --git a/src/org/noreply/fancydress/type3/routing/RoutingForward.java b/src/org/noreply/fancydress/type3/routing/RoutingForward.java new file mode 100644 index 0000000..0c5d567 --- /dev/null +++ b/src/org/noreply/fancydress/type3/routing/RoutingForward.java @@ -0,0 +1,26 @@ +package org.noreply.fancydress.type3.routing; + +/** + * Base class for all Routings that appear within a chain. + * + * Routings of this type mean that the packet is forwarded + * (possible after a SWAP operation) to a next node. + */ +public abstract class RoutingForward extends Routing { + /** + * Default constructor. + * + * @param type The routing type as integer. + */ + protected RoutingForward(int type) { + super (type); + } + + /** + * Get the same routing information as a SWAP type. + * + * @return A routing class with the same information, but with a SWAP routing type. + */ + public abstract RoutingForward asSwap(); +} + diff --git a/src/org/noreply/fancydress/type3/routing/RoutingHOST.java b/src/org/noreply/fancydress/type3/routing/RoutingHOST.java new file mode 100644 index 0000000..f950e76 --- /dev/null +++ b/src/org/noreply/fancydress/type3/routing/RoutingHOST.java @@ -0,0 +1,102 @@ +package org.noreply.fancydress.type3.routing; + +import org.noreply.fancydress.misc.Util; +import org.noreply.fancydress.crypto.*; + +/** + * The FWD/HOST and SWAP-FWD/HOST routing as specified in the type III spec. + * + * FWD/HOST packets are forwarded to another type III node given by + * a hostname/port pair. The delivering node (i.e. the node parsing + * this routing type) is responsible for resolving the hostname to an + * IP address. + */ +public class RoutingHOST extends RoutingForward { + /** + * The hostname of the next hop + */ + private String hostname; + /** + * Port at which the Type III server is listening + */ + private int port; + /** + * Keyid of the Packet Key + */ + private byte[] keyid; + + /** + * Constructor that creates the routing as either FWD/HOST or SWAP-FWD/HOST + * + * @param hostname host name of the next hop + * @param port TCP port at which the next hop is listening + * @param keyid keyid of the packet key + * @param boolean if true, have a SWAP-FWD/HOST routing type, FWD/HOST otherwhise + */ + private RoutingHOST(String hostname, int port, byte[] keyid, boolean asSwap) { + super (asSwap ? RoutingType.SWAP_FWD_HOST : RoutingType.FWD_HOST); + + if (keyid.length != CryptoPrimitives.HASH_LEN) + throw new Error("keyid must be HASH_LEN bytes long."); + + this.hostname = hostname; + this.port = port; + this.keyid = keyid; + } + + /** + * Create a FWD/HOST routing. + * + * @param hostname host name of the next hop + * @param port TCP port at which the next hop is listening + * @param keyid keyid of the packet key + */ + public RoutingHOST(String hostname, int port, byte[] keyid) { + this(hostname, port, keyid, false); + } + + /** + * Return a routing with the same information but with a SWAP_FWD_HOST routing type. + * + * @return a SWAP_FWD_HOST routing. + */ + public RoutingForward asSwap() { + RoutingHOST swap = new RoutingHOST(hostname, port, keyid, true); + return swap; + } + + /** + * Return the total length in octets of the routing information. + * + * @return total length in octets of the routing information + */ + public int getRoutingInformationLength() { + return 2 + keyid.length + hostname.length(); + } + + /** + * Get the routing type of this instance. + * + * @return routing type + */ + public byte[] getRoutingInformation() { + int length = getRoutingInformationLength(); + byte[] result = new byte[length]; + int pos = 0; + + result[pos] = (byte) ( (port >> 8) & 0xff); + pos++; + result[pos] = (byte) ( port & 0xff); + pos++; + System.arraycopy(keyid, 0, result, pos, keyid.length); + pos += keyid.length; + System.arraycopy(Util.toOctets(hostname), 0, result, pos, hostname.length()); + pos += hostname.length(); + + if (pos != length) + throw new Error("Did not fill in expected amount of bytes!"); + + return result; + } +} + diff --git a/src/org/noreply/fancydress/type3/routing/RoutingIP4.java b/src/org/noreply/fancydress/type3/routing/RoutingIP4.java new file mode 100644 index 0000000..05d65ee --- /dev/null +++ b/src/org/noreply/fancydress/type3/routing/RoutingIP4.java @@ -0,0 +1,110 @@ +package org.noreply.fancydress.type3.routing; + +import org.noreply.fancydress.misc.Util; +import org.noreply.fancydress.crypto.*; +import java.net.InetAddress; +import java.net.Inet4Address; + +/** + * The FWD/IP4 and SWAP-FWD/IP4 routing as specified in the type III spec. + * + * FWD/IP4 packets are forwarded to another type III node given by + * an ip/port pair. + * + * This routing type is going to go away RSN. FIXME + */ +public class RoutingIP4 extends RoutingForward { + /** + * The IP address of the next hop + */ + private InetAddress ip; + /** + * Port at which the Type III server is listening + */ + private int port; + /** + * Keyid of the next hop's Packet Key + */ + private byte[] keyid; + + /** + * Constructor that creates the routing as either FWD/IP4 or SWAP-FWD/IP4 + * + * @param ip IP address of the next hop + * @param port TCP port at which the next hop is listening + * @param keyid keyid of the packet key + * @param boolean if true, have a SWAP-FWD/IP4 routing type, FWD/IP4 otherwhise + */ + private RoutingIP4(InetAddress ip, int port, byte[] keyid, boolean asSwap) { + super (asSwap ? RoutingType.SWAP_FWD_IP4 : RoutingType.FWD_IP4); + + if (keyid.length != CryptoPrimitives.HASH_LEN) + throw new Error("keyid must be HASH_LEN bytes long."); + if (! (ip instanceof Inet4Address)) + throw new Error("IP must be an instance of Inet4Address."); + + this.ip = ip; + this.port = port; + this.keyid = keyid; + } + + /** + * Create a FWD/IP4 routing. + * + * @param ip IP address of the next hop + * @param port TCP port at which the next hop is listening + * @param keyid keyid of the packet key + */ + public RoutingIP4(InetAddress ip, int port, byte[] keyid) { + this(ip, port, keyid, false); + } + + /** + * Return a routing with the same information but with a SWAP_FWD_IP4 routing type. + * + * @return a SWAP_FWD_IP4 routing. + */ + public RoutingForward asSwap() { + RoutingIP4 swap = new RoutingIP4(ip, port, keyid, true); + return swap; + } + + /** + * Return the total length in octets of the routing information. + * + * @return total length in octets of the routing information + */ + public int getRoutingInformationLength() { + return 4 + 2 + keyid.length; + } + + /** + * Get the routing type of this instance. + * + * @return routing type + */ + public byte[] getRoutingInformation() { + int length = getRoutingInformationLength(); + byte[] result = new byte[length]; + byte[] address = ip.getAddress(); + int pos = 0; + + if (address.length != 4) + throw new Error("Expected 4 bytes from ip.getAddress()."); + + System.arraycopy(address, 0, result, pos, address.length); + pos += address.length; + result[pos] = (byte) ( (port >> 8) & 0xff); + pos++; + result[pos] = (byte) ( port & 0xff); + pos++; + System.arraycopy(keyid, 0, result, pos, keyid.length); + pos += keyid.length; + + if (pos != length) + throw new Error("Did not fill in expected amount of bytes!"); + + return result; + } +} + diff --git a/src/org/noreply/fancydress/type3/routing/RoutingSMTP.java b/src/org/noreply/fancydress/type3/routing/RoutingSMTP.java new file mode 100644 index 0000000..acfe435 --- /dev/null +++ b/src/org/noreply/fancydress/type3/routing/RoutingSMTP.java @@ -0,0 +1,63 @@ +package org.noreply.fancydress.type3.routing; + +import org.noreply.fancydress.misc.Util; +import org.noreply.fancydress.crypto.*; + +/** + * The SMTP routing as specified in the type III spec. + * + * SMTP packets are delivered by email by the handling server. + * + * The payload of a SMTP packet should be constructed according + * to the E2E spec. + * + * @see org.noreply.fancydress.type3.Payload + */ +public class RoutingSMTP extends RoutingDestination { + /** + * Recipient mailbox. + */ + String mailbox; + + /** + * Construct an SMTP routing. + */ + public RoutingSMTP(String mailbox) { + super (RoutingType.SMTP); + + // FIXME: syntax check mailbox + + this.mailbox = mailbox; + } + + /** + * Return the total length in octets of the routing information. + * + * @return total length in octets of the routing information + */ + public int getRoutingInformationLength() { + return DECODINGHANDLE_LEN + mailbox.length(); + } + + /** + * Get the routing type of this instance. + * + * @return routing type + */ + public byte[] getRoutingInformation() { + int length = getRoutingInformationLength(); + byte[] result = new byte[length]; + int pos = 0; + + System.arraycopy(CryptoPrimitives.rand(DECODINGHANDLE_LEN), 0, result, pos, DECODINGHANDLE_LEN); + pos += DECODINGHANDLE_LEN; + System.arraycopy(Util.toOctets(mailbox), 0, result, pos, mailbox.length()); + pos += mailbox.length(); + + if (pos != length) + throw new Error("Did not fill in expected amount of bytes!"); + + return result; + } +} + diff --git a/src/org/noreply/fancydress/type3/routing/RoutingType.java b/src/org/noreply/fancydress/type3/routing/RoutingType.java new file mode 100644 index 0000000..daf3281 --- /dev/null +++ b/src/org/noreply/fancydress/type3/routing/RoutingType.java @@ -0,0 +1,32 @@ +package org.noreply.fancydress.type3.routing; + +import org.noreply.fancydress.misc.Util; +import org.noreply.fancydress.crypto.*; +import org.noreply.fancydress.status.*; +import java.util.*; + +/** + * RoutingTypes as specified in the type III spec. + */ +public class RoutingType { + public static final int DROP = 0x0000; + public static final int FWD_IP4 = 0x0001; + public static final int SWAP_FWD_IP4 = 0x0002; + public static final int FWD_HOST = 0x0003; + public static final int SWAP_FWD_HOST = 0x0004; + + public static final int SMTP = 0x0100; + public static final int MBOX = 0x0101; + public static final int MIX2 = 0x0102; + public static final int FRAGMENT = 0x0103; + + private int type; + + public RoutingType(int type) { + this.type = type; + } + + public int getType() { + return type; + } +} |