summaryrefslogtreecommitdiff
path: root/src/org/noreply/fancydress/type3/SingleLeg.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/noreply/fancydress/type3/SingleLeg.java')
-rw-r--r--src/org/noreply/fancydress/type3/SingleLeg.java144
1 files changed, 144 insertions, 0 deletions
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;
+ }
+}
+