/* $Id$ */ 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 IllegalArgumentException("sharedSecret must be KEY_LEN bytes long."); if (digest.length != CryptoPrimitives.HASH_LEN) throw new IllegalArgumentException("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; } }