Prechádzať zdrojové kódy

updateroot circuit part tested

Boyoung- 8 rokov pred
rodič
commit
85a2863530

+ 3 - 0
src/crypto/Crypto.java

@@ -1,16 +1,19 @@
 package crypto;
 
+import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 
 public class Crypto {
 	public static SecureRandom sr;
+	public static MessageDigest sha1;
 	public static int secParam;
 	public static int secParamBytes;
 
 	static {
 		try {
 			sr = SecureRandom.getInstance("SHA1PRNG");
+			sha1 = MessageDigest.getInstance("SHA-1");
 		} catch (NoSuchAlgorithmException e) {
 			e.printStackTrace();
 		}

+ 35 - 2
src/gc/GCLib.java

@@ -30,7 +30,40 @@ public class GCLib<T> extends IntegerLib<T> {
 		}
 	}
 
-	public T[][] deepestAndEmptyTuples(int i, byte[] Li, T[] E_feBits, T[] C_feBits, T[][] E_tupleLabels,
+	public T[][] rootFindDeepestAndEmpty(byte[] Li, T[] j1, T[] E_feBits, T[] C_feBits, T[][] E_tupleLabels,
+			T[][] C_tupleLabels) {
+		int sLogW = (int) Math.ceil(Math.log(w) / Math.log(2));
+		T[] pathLabel = toSignals(new BigInteger(1, Li).longValue(), d - 1); // no
+																				// sign
+																				// bit
+		T[] feBits = xor(E_feBits, C_feBits);
+		T[][] tupleLabels = env.newTArray(w, 0);
+		for (int j = 0; j < w; j++)
+			tupleLabels[j] = xor(E_tupleLabels[j], C_tupleLabels[j]);
+		
+		T[] l = padSignal(ones(d - 1), d); // has sign bit
+		T[] j2 = zeros(sLogW); // no sign bit
+
+		for (int j = 0; j < w; j++) {
+			T[] tupleIndex = toSignals(j, sLogW);
+			T[] lz = xor(pathLabel, tupleLabels[j]);
+			zerosFollowedByOnes(lz);
+			lz = padSignal(lz, d); // add sign bit
+
+			T firstIf = and(feBits[j], less(lz, l));
+			l = mux(l, lz, firstIf);
+			j1 = mux(j1, tupleIndex, firstIf);
+
+			j2 = mux(tupleIndex, j2, feBits[j]);
+		}
+		
+		T[][] output = env.newTArray(2, 0);
+		output[0] = j1;
+		output[1] = j2;
+		return output;
+	}
+
+	public T[][] findDeepestAndEmpty(int i, byte[] Li, T[] E_feBits, T[] C_feBits, T[][] E_tupleLabels,
 			T[][] C_tupleLabels) {
 		T[] pathLabel = toSignals(new BigInteger(1, Li).longValue(), d - 1); // no
 																				// sign
@@ -86,7 +119,7 @@ public class GCLib<T> extends IntegerLib<T> {
 			T[] index = toSignals(i, logD + 1);
 			deepest[i] = mux(deepest[i], src, geq(goal, index));
 
-			T[][] dae = deepestAndEmptyTuples(i, Li, E_feBits[i], C_feBits[i], E_tupleLabels[i], C_tupleLabels[i]);
+			T[][] dae = findDeepestAndEmpty(i, Li, E_feBits[i], C_feBits[i], E_tupleLabels[i], C_tupleLabels[i]);
 			T[] l = dae[0];
 			output[1][i] = dae[1];
 			output[2][i] = dae[2];

+ 87 - 0
src/gc/GCUtil.java

@@ -0,0 +1,87 @@
+package gc;
+
+import java.math.BigInteger;
+
+import com.oblivm.backend.gc.GCGenComp;
+import com.oblivm.backend.gc.GCSignal;
+
+import crypto.Crypto;
+import oram.Tuple;
+
+public class GCUtil {
+	
+	public static GCSignal[] genEmptyKeys(int n) {
+		GCSignal[] keys = new GCSignal[n];
+		for (int i=0; i<n; i++)
+			keys[i] = new GCSignal(new byte[GCSignal.len]);
+		return keys;
+	}
+	
+	public static GCSignal[][] genKeyPairs(int n) {
+		GCSignal[][] pairs = new GCSignal[n][];
+		for (int i = 0; i < n; i++)
+			pairs[i] = GCGenComp.genPair();
+		return pairs;
+	}
+
+	public static GCSignal[] getZeroKeys(GCSignal[][] pairs) {
+		GCSignal[] keys = new GCSignal[pairs.length];
+		for (int i = 0; i < keys.length; i++)
+			keys[i] = pairs[i][0];
+		return keys;
+	}
+
+	public static GCSignal[] revSelectKeys(GCSignal[][] pairs, byte[] input) {
+		BigInteger in = new BigInteger(1, input);
+		GCSignal[] out = new GCSignal[pairs.length];
+		for (int i = 0; i < pairs.length; i++)
+			out[i] = pairs[i][in.testBit(i) ? 1 : 0];
+		return out;
+	}
+
+	/*
+	public static GCSignal[] selectKeys(GCSignal[][] pairs, byte[] input) {
+		BigInteger in = new BigInteger(1, input);
+		GCSignal[] out = new GCSignal[pairs.length];
+		for (int i = 0; i < pairs.length; i++)
+			out[i] = pairs[i][in.testBit(pairs.length - 1 - i) ? 1 : 0];
+		return out;
+	}
+	*/
+	
+	public static GCSignal[][] selectLabelKeys(GCSignal[][][] labelPairs, Tuple[] tuples) {
+		GCSignal[][] out = new GCSignal[tuples.length][];
+		for (int i=0; i<tuples.length; i++) 
+			out[i] = revSelectKeys(labelPairs[i], tuples[i].getL());
+		return out;
+	}
+	
+	public static GCSignal[] selectFeKeys(GCSignal[][] pairs, Tuple[] tuples) {
+		GCSignal[] out = new GCSignal[pairs.length];
+		for (int i = 0; i < pairs.length; i++)
+			out[i] = pairs[i][new BigInteger(tuples[i].getF()).testBit(0)?1:0];
+		return out;
+	}
+	
+	public static BigInteger[] genOutKeyHashes(GCSignal[] outZeroKeys) {
+		BigInteger[] hashes = new BigInteger[outZeroKeys.length];
+		for (int i=0; i<outZeroKeys.length; i++) {
+			hashes[i] = new BigInteger(Crypto.sha1.digest(outZeroKeys[i].bytes));
+		}
+		return hashes;
+	}
+	
+	public static BigInteger evaOutKeys(GCSignal[] outKeys, BigInteger[] genHashes) {
+		BigInteger[] evaHashes = genOutKeyHashes(outKeys);
+		BigInteger output = BigInteger.ZERO;
+		for (int i = 0; i < outKeys.length; i++) {
+			if (outKeys[i].isPublic()) {
+				if (outKeys[i].v)
+					output = output.setBit(i);
+			} else if (genHashes[i].compareTo(evaHashes[i]) != 0) {
+				output = output.setBit(i);
+			} 
+		}
+		return output;
+	}
+}

+ 3 - 3
src/protocols/DeepestAndEmpty.java

@@ -117,7 +117,7 @@ public class DeepestAndEmpty extends Protocol {
 
 		Network channel = new Network(null, con1);
 		CompEnv<GCSignal> gen = new GCGen(channel);
-		GCSignal[][] E_out = new GCLib<GCSignal>(gen, d, w).deepestAndEmptyTuples(i, Li, E_feZeroKeys, C_feZeroKeys,
+		GCSignal[][] E_out = new GCLib<GCSignal>(gen, d, w).findDeepestAndEmpty(i, Li, E_feZeroKeys, C_feZeroKeys,
 				E_labelZeroKeys, C_labelZeroKeys);
 
 		GCSignal[][] D_out = con1.readObject();
@@ -166,10 +166,10 @@ public class DeepestAndEmpty extends Protocol {
 		Network channel = new Network(con1, null);
 		CompEnv<GCSignal> gen = new GCEva(channel);
 		GCLib<GCSignal> dae = new GCLib<GCSignal>(gen, d, w);
-		dae.deepestAndEmptyTuples(i, Li, E_feKeyInput, C_feZeroKeys, E_labelKeyInput, C_labelZeroKeys);
+		dae.findDeepestAndEmpty(i, Li, E_feKeyInput, C_feZeroKeys, E_labelKeyInput, C_labelZeroKeys);
 
 		gen.setEvaluate();
-		GCSignal[][] D_out = dae.deepestAndEmptyTuples(i, Li, E_feKeyInput, C_feZeroKeys, E_labelKeyInput,
+		GCSignal[][] D_out = dae.findDeepestAndEmpty(i, Li, E_feKeyInput, C_feZeroKeys, E_labelKeyInput,
 				C_labelZeroKeys);
 
 		con1.write(D_out);

+ 13 - 0
src/protocols/PreData.java

@@ -1,6 +1,11 @@
 package protocols;
 
+import java.math.BigInteger;
+
+import com.oblivm.backend.gc.GCSignal;
+
 import crypto.PRF;
+import gc.GCLib;
 import oram.Tuple;
 
 public class PreData {
@@ -37,4 +42,12 @@ public class PreData {
 	public Tuple[] reshuffle_p;
 	public Tuple[] reshuffle_r;
 	public Tuple[] reshuffle_a_prime;
+
+	public GCSignal[][] ur_j1KeyPairs;
+	public GCSignal[][] ur_E_feKeyPairs;
+	public GCSignal[][] ur_C_feKeyPairs;
+	public GCSignal[][][] ur_E_labelKeyPairs;
+	public GCSignal[][][] ur_C_labelKeyPairs;
+	public BigInteger[][] ur_outKeyHashes;
+	public GCLib<GCSignal> ur_gc;
 }

+ 7 - 7
src/protocols/PreRetrieve.java

@@ -14,11 +14,11 @@ public class PreRetrieve extends Protocol {
 		PreAccess preaccess = new PreAccess(con1, con2);
 		PreReshuffle prereshuffle = new PreReshuffle(con1, con2);
 		PrePostProcessT prepostprocesst = new PrePostProcessT(con1, con2);
-		
+
 		int numTuples = md.getStashSizeOfTree(ti) + md.getLBitsOfTree(ti) * md.getW();
 		int[] tupleParam = new int[] { ti == 0 ? 0 : 1, md.getNBytesOfTree(ti), md.getLBytesOfTree(ti),
 				md.getABytesOfTree(ti) };
-		
+
 		preaccess.runE(predata, md.getTwoTauPow(), numTuples, tupleParam, timer);
 		prereshuffle.runE(predata, timer);
 		prepostprocesst.runE(predata, timer);
@@ -26,12 +26,12 @@ public class PreRetrieve extends Protocol {
 
 	public void runD(PreData predata, Metadata md, int ti, PreData prev, Timer timer) {
 		PreAccess preaccess = new PreAccess(con1, con2);
-		PreReshuffle prereshuffle = new PreReshuffle(con1, con2); 
+		PreReshuffle prereshuffle = new PreReshuffle(con1, con2);
 		PrePostProcessT prepostprocesst = new PrePostProcessT(con1, con2);
-		
+
 		int[] tupleParam = new int[] { ti == 0 ? 0 : 1, md.getNBytesOfTree(ti), md.getLBytesOfTree(ti),
 				md.getABytesOfTree(ti) };
-		
+
 		preaccess.runD(predata, timer);
 		prereshuffle.runD(predata, tupleParam, timer);
 		prepostprocesst.runD(predata, prev, md.getLBytesOfTree(ti), md.getAlBytesOfTree(ti), md.getTau(), timer);
@@ -39,9 +39,9 @@ public class PreRetrieve extends Protocol {
 
 	public void runC(PreData predata, Metadata md, int ti, PreData prev, Timer timer) {
 		PreAccess preaccess = new PreAccess(con1, con2);
-		PreReshuffle prereshuffle = new PreReshuffle(con1, con2); 
+		PreReshuffle prereshuffle = new PreReshuffle(con1, con2);
 		PrePostProcessT prepostprocesst = new PrePostProcessT(con1, con2);
-		
+
 		preaccess.runC(timer);
 		prereshuffle.runC(predata, timer);
 		prepostprocesst.runC(predata, prev, md.getLBytesOfTree(ti), md.getAlBytesOfTree(ti), timer);

+ 86 - 0
src/protocols/PreUpdateRoot.java

@@ -0,0 +1,86 @@
+package protocols;
+
+import java.math.BigInteger;
+
+import com.oblivm.backend.flexsc.CompEnv;
+import com.oblivm.backend.gc.GCSignal;
+import com.oblivm.backend.gc.regular.GCEva;
+import com.oblivm.backend.gc.regular.GCGen;
+import com.oblivm.backend.network.Network;
+
+import communication.Communication;
+import gc.GCLib;
+import gc.GCUtil;
+import measure.Timer;
+import oram.Forest;
+import oram.Metadata;
+
+public class PreUpdateRoot extends Protocol {
+	public PreUpdateRoot(Communication con1, Communication con2) {
+		super(con1, con2);
+	}
+
+	public void runE(PreData predata, int sw, int lBits, byte[] Li, Timer timer) {
+		int sLogW = (int) Math.ceil(Math.log(sw) / Math.log(2));
+		predata.ur_j1KeyPairs = GCUtil.genKeyPairs(sLogW);
+		predata.ur_E_feKeyPairs = GCUtil.genKeyPairs(sw);
+		predata.ur_C_feKeyPairs = GCUtil.genKeyPairs(sw);
+		GCSignal[] j1ZeroKeys = GCUtil.getZeroKeys(predata.ur_j1KeyPairs);
+		GCSignal[] E_feZeroKeys = GCUtil.getZeroKeys(predata.ur_E_feKeyPairs);
+		GCSignal[] C_feZeroKeys = GCUtil.getZeroKeys(predata.ur_C_feKeyPairs);
+		predata.ur_E_labelKeyPairs = new GCSignal[sw][][];
+		predata.ur_C_labelKeyPairs = new GCSignal[sw][][];
+		GCSignal[][] E_labelZeroKeys = new GCSignal[sw][];
+		GCSignal[][] C_labelZeroKeys = new GCSignal[sw][];
+		for (int i = 0; i < sw; i++) {
+			predata.ur_E_labelKeyPairs[i] = GCUtil.genKeyPairs(lBits);
+			predata.ur_C_labelKeyPairs[i] = GCUtil.genKeyPairs(lBits);
+			E_labelZeroKeys[i] = GCUtil.getZeroKeys(predata.ur_E_labelKeyPairs[i]);
+			C_labelZeroKeys[i] = GCUtil.getZeroKeys(predata.ur_C_labelKeyPairs[i]);
+		}
+
+		Network channel = new Network(null, con1);
+		CompEnv<GCSignal> gen = new GCGen(channel);
+		GCSignal[][] outZeroKeys = new GCLib<GCSignal>(gen, lBits + 1, sw).rootFindDeepestAndEmpty(Li, j1ZeroKeys,
+				E_feZeroKeys, C_feZeroKeys, E_labelZeroKeys, C_labelZeroKeys);
+
+		predata.ur_outKeyHashes = new BigInteger[outZeroKeys.length][];
+		for (int i = 0; i < outZeroKeys.length; i++)
+			predata.ur_outKeyHashes[i] = GCUtil.genOutKeyHashes(outZeroKeys[i]);
+
+		con2.write(predata.ur_C_feKeyPairs);
+		con2.write(predata.ur_C_labelKeyPairs);
+		con1.write(predata.ur_outKeyHashes);
+	}
+
+	public void runD(PreData predata, int sw, int lBits, byte[] Li, Timer timer) {
+		int sLogW = (int) Math.ceil(Math.log(sw) / Math.log(2));
+		GCSignal[] j1ZeroKeys = GCUtil.genEmptyKeys(sLogW);
+		GCSignal[] E_feZeroKeys = GCUtil.genEmptyKeys(sw);
+		GCSignal[] C_feZeroKeys = GCUtil.genEmptyKeys(sw);
+		GCSignal[][] E_labelZeroKeys = new GCSignal[sw][];
+		GCSignal[][] C_labelZeroKeys = new GCSignal[sw][];
+		for (int i = 0; i < sw; i++) {
+			E_labelZeroKeys[i] = GCUtil.genEmptyKeys(lBits);
+			C_labelZeroKeys[i] = GCUtil.genEmptyKeys(lBits);
+		}
+
+		Network channel = new Network(con1, null);
+		CompEnv<GCSignal> eva = new GCEva(channel);
+		predata.ur_gc = new GCLib<GCSignal>(eva, lBits + 1, sw);
+		predata.ur_gc.rootFindDeepestAndEmpty(Li, j1ZeroKeys, E_feZeroKeys, C_feZeroKeys, E_labelZeroKeys,
+				C_labelZeroKeys);
+		eva.setEvaluate();
+
+		predata.ur_outKeyHashes = con1.readObject();
+	}
+
+	public void runC(PreData predata, Timer timer) {
+		predata.ur_C_feKeyPairs = con1.readObject();
+		predata.ur_C_labelKeyPairs = con1.readObject();
+	}
+
+	@Override
+	public void run(Party party, Metadata md, Forest forest) {
+	}
+}

+ 11 - 11
src/protocols/Retrieve.java

@@ -24,11 +24,11 @@ public class Retrieve extends Protocol {
 		Access access = new Access(con1, con2);
 		Reshuffle reshuffle = new Reshuffle(con1, con2);
 		PostProcessT postprocesst = new PostProcessT(con1, con2);
-		
-		OutAccess outaccess =  access.runE(predata, OTi, Ni, Nip1_pr, timer);
+
+		OutAccess outaccess = access.runE(predata, OTi, Ni, Nip1_pr, timer);
 		Tuple[] path = reshuffle.runE(predata, outaccess.E_P, OTi.getTreeIndex() == 0, timer);
-		postprocesst.runE(predata, outaccess.E_Ti, OTi.getTreeIndex() == h-1, timer);
-		
+		Tuple Ti = postprocesst.runE(predata, outaccess.E_Ti, OTi.getTreeIndex() == h - 1, timer);
+
 		return outaccess;
 	}
 
@@ -36,7 +36,7 @@ public class Retrieve extends Protocol {
 		Access access = new Access(con1, con2);
 		Reshuffle reshuffle = new Reshuffle(con1, con2);
 		PostProcessT postprocesst = new PostProcessT(con1, con2);
-		
+
 		access.runD(predata, OTi, Ni, Nip1_pr, timer);
 		reshuffle.runD();
 		postprocesst.runD();
@@ -46,11 +46,11 @@ public class Retrieve extends Protocol {
 		Access access = new Access(con1, con2);
 		Reshuffle reshuffle = new Reshuffle(con1, con2);
 		PostProcessT postprocesst = new PostProcessT(con1, con2);
-		
+
 		OutAccess outaccess = access.runC(md, ti, Li, timer);
 		Tuple[] path = reshuffle.runC(predata, outaccess.C_P, ti == 0, timer);
-		postprocesst.runC(predata, outaccess.C_Ti, Li, outaccess.C_Lip1, outaccess.C_j2, ti == h-1, timer);
-		
+		Tuple Ti = postprocesst.runC(predata, outaccess.C_Ti, Li, outaccess.C_Lip1, outaccess.C_j2, ti == h - 1, timer);
+
 		return outaccess;
 	}
 
@@ -155,9 +155,9 @@ public class Retrieve extends Protocol {
 			}
 		}
 
-		//timer.print();
+		// timer.print();
 
-		//System.out.println();
-		//System.out.println(sw.toMS());
+		// System.out.println();
+		// System.out.println(sw.toMS());
 	}
 }

+ 133 - 0
src/protocols/UpdateRoot.java

@@ -0,0 +1,133 @@
+package protocols;
+
+import java.math.BigInteger;
+
+import com.oblivm.backend.gc.GCSignal;
+
+import communication.Communication;
+import crypto.Crypto;
+import exceptions.NoSuchPartyException;
+import gc.GCUtil;
+import measure.Timer;
+import oram.Forest;
+import oram.Metadata;
+import oram.Tuple;
+import util.Util;
+
+public class UpdateRoot extends Protocol {
+	public UpdateRoot(Communication con1, Communication con2) {
+		super(con1, con2);
+	}
+
+	public void runE(PreData predata, byte[] Li, Tuple[] R, Timer timer) {
+		// step 1
+		int j1 = Crypto.sr.nextInt(R.length);
+		GCSignal[] j1InputKeys = GCUtil.revSelectKeys(predata.ur_j1KeyPairs, BigInteger.valueOf(j1).toByteArray());
+		GCSignal[] E_feInputKeys = GCUtil.selectFeKeys(predata.ur_E_feKeyPairs, R);
+		GCSignal[][] E_labelInputKeys = GCUtil.selectLabelKeys(predata.ur_E_labelKeyPairs, R);
+
+		con1.write(j1InputKeys);
+		con1.write(E_feInputKeys);
+		con1.write(E_labelInputKeys);
+	}
+
+	public void runD(PreData predata, byte[] Li, Timer timer) {
+		GCSignal[] j1InputKeys = con1.readObject();
+		GCSignal[] E_feInputKeys = con1.readObject();
+		GCSignal[][] E_labelInputKeys = con1.readObject();
+		GCSignal[] C_feInputKeys = con2.readObject();
+		GCSignal[][] C_labelInputKeys = con2.readObject();
+
+		GCSignal[][] outKeys = predata.ur_gc.rootFindDeepestAndEmpty(Li, j1InputKeys, E_feInputKeys, C_feInputKeys,
+				E_labelInputKeys, C_labelInputKeys);
+		int j1 = GCUtil.evaOutKeys(outKeys[0], predata.ur_outKeyHashes[0]).intValue();
+		int j2 = GCUtil.evaOutKeys(outKeys[1], predata.ur_outKeyHashes[1]).intValue();
+
+		System.out.println(j1 + " " + j2);
+	}
+
+	public void runC(PreData predata, Tuple[] R, Timer timer) {
+		// step 1
+		GCSignal[] C_feInputKeys = GCUtil.selectFeKeys(predata.ur_C_feKeyPairs, R);
+		GCSignal[][] C_labelInputKeys = GCUtil.selectLabelKeys(predata.ur_C_labelKeyPairs, R);
+
+		con2.write(C_feInputKeys);
+		con2.write(C_labelInputKeys);
+	}
+
+	// for testing correctness
+	@Override
+	public void run(Party party, Metadata md, Forest forest) {
+		Timer timer = new Timer();
+
+		for (int i = 0; i < 10; i++) {
+			
+			System.out.println("i=" + i);
+			
+			PreData predata = new PreData();
+			PreUpdateRoot preupdateroot = new PreUpdateRoot(con1, con2);
+			
+			if (party == Party.Eddie) {
+				int sw = Crypto.sr.nextInt(25) + 10;
+				int lBits = Crypto.sr.nextInt(30) + 5;
+				byte[] Li = Util.nextBytes((lBits+7)/8, Crypto.sr);
+				Tuple[] R = new Tuple[sw];
+				for (int j=0; j<sw; j++)
+					R[j] = new Tuple(1, 2, (lBits+7)/8, 3, Crypto.sr);
+				
+				System.out.println("sw,lBits: " + sw + " " + lBits);
+				
+				con1.write(sw);
+				con1.write(lBits);
+				con1.write(Li);
+				con2.write(sw);
+				con2.write(lBits);
+				
+				preupdateroot.runE(predata, sw, lBits, Li, timer);
+				runE(predata, Li, R, timer);
+				
+				int emptyIndex = 0;
+				for (int j=0; j<sw; j++) {
+					if (new BigInteger(R[j].getF()).testBit(0)) {
+						String l = Util.addZeros(Util.getSubBits(new BigInteger(1, Util.xor(R[j].getL(), Li)), lBits, 0).toString(2), lBits);
+						System.out.println(j + ":\t" + l);
+					}
+					else {
+						emptyIndex = j;
+					}
+				}
+				System.out.println("last empty: " + emptyIndex);
+
+			} else if (party == Party.Debbie) {
+				int sw = con1.readObject();
+				int lBits = con1.readObject();
+				byte[] Li = con1.read();
+				
+				preupdateroot.runD(predata, sw, lBits, Li, timer);
+				runD(predata, Li, timer);
+
+			} else if (party == Party.Charlie) {
+				int sw = con1.readObject();
+				int lBits = con1.readObject();
+				Tuple[] R = new Tuple[sw];
+				for (int j=0; j<sw; j++)
+					R[j] = new Tuple(1, 2, lBits, 3, null);
+				
+				preupdateroot.runC(predata, timer);
+				runC(predata, R, timer);
+				
+				/*
+				if (output.t == index && Util.equal(output.m_t, m[index]))
+					System.out.println("SSCOT test passed");
+				else
+					System.err.println("SSCOT test failed");
+					*/
+
+			} else {
+				throw new NoSuchPartyException(party + "");
+			}
+		}
+
+		// timer.print();
+	}
+}

+ 2 - 0
src/ui/CLI.java

@@ -87,6 +87,8 @@ public class CLI {
 			operation = PrepareTarget.class;
 		} else if (protocol.equals("mc")) {
 			operation = MakeCycle.class;
+		} else if (protocol.equals("update")) {
+			operation = UpdateRoot.class;
 		} else if (protocol.equals("retrieve")) {
 			operation = Retrieve.class;
 		} else {

+ 7 - 0
src/util/Util.java

@@ -137,6 +137,13 @@ public class Util {
 			return out;
 		}
 	}
+	
+	public static String addZeros(String a, int n) {
+		String out = a;
+		for (int i = 0; i < n - a.length(); i++)
+			out = "0" + out;
+		return out;
+	}
 
 	public static void debug(String s) {
 		// only to make Communication.java compile