diff --git a/.gitmodules b/.gitmodules
index dd07c911076d8dfd92a8470dc1e1b34737e7bfdc..e8390a68f1178c5b5c05dc162cff43ab5f8a336a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,6 @@
 [submodule "heliosverifier"]
 	path = heliosverifier
 	url = git://github.com/benadida/helios-verifier.git
+[submodule "helios/media/helios/jscrypto"]
+	path = helios/media/helios/jscrypto
+	url = git@github.com:benadida/jscrypto
diff --git a/helios/media/helios/bigint.class b/helios/media/helios/bigint.class
deleted file mode 100644
index f8e435bd63fd7fded0b46a12ac80fa843aa0d101..0000000000000000000000000000000000000000
Binary files a/helios/media/helios/bigint.class and /dev/null differ
diff --git a/helios/media/helios/bigint.java b/helios/media/helios/bigint.java
deleted file mode 100644
index 62dce5f614a01d11bfd3ca4f5b9db5bd45005012..0000000000000000000000000000000000000000
--- a/helios/media/helios/bigint.java
+++ /dev/null
@@ -1,25 +0,0 @@
-
-/*
- * A simple applet for generating Bigints from JavaScript
- *
- * inspired from Stanford's SRP, and extended for Prime Number generation.
- */
-
-public class bigint extends java.applet.Applet {
-    public java.security.SecureRandom newSecureRandom() {
-	    return new java.security.SecureRandom();
-    }
-
-    public java.math.BigInteger newBigInteger(String value, int radix) {
-	    return new java.math.BigInteger(value, radix);
-    }
-
-    public java.math.BigInteger randomBigInteger(int bitlen, java.util.Random rng) {
-	    return new java.math.BigInteger(bitlen, rng);
-    }
-
-    public java.math.BigInteger randomPrimeBigInteger(int bitlen, int certainty, java.util.Random rng) {
-	    return new java.math.BigInteger(bitlen, certainty, rng);
-    }
-}
- 
diff --git a/helios/media/helios/bigint.js b/helios/media/helios/bigint.js
deleted file mode 100644
index ebc3295072dcebe702ea51bb3d3bfa98a54ced84..0000000000000000000000000000000000000000
--- a/helios/media/helios/bigint.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * This software incorporates components derived from the
- * Secure Remote Password JavaScript demo developed by
- * Tom Wu (tjw@CS.Stanford.EDU).
- *
- * This library is almost entirely re-written by Ben Adida (ben@adida.net)
- * with a BigInt wrapper.
- */
-
-// A wrapper for java.math.BigInteger with some appropriate extra functions for JSON and 
-// generally being a nice JavaScript object.
-
-BigInt = Class.extend({
-  init: function(value, radix) {
-    if (value == null) {
-      throw "null value!";
-    }
-    
-    if (BigInt.use_applet) {
-      this._java_bigint = BigInt.APPLET.newBigInteger(value, radix);
-    } else {
-      try {
-        this._java_bigint = new java.math.BigInteger(value, radix);
-      } catch (e) {
-        // alert("oy " + e.toString() + " value=" + value + " , radix=" + radix);
-        throw TypeError
-      }
-    }
-  },
-  
-  toString: function() {
-    return this._java_bigint.toString() + "";
-  },
-  
-  toJSONObject: function() {
-    // toString is apparently not overridden in IE, so we reproduce it here.
-    return this._java_bigint.toString() + "";
-  },
-  
-  add: function(other) {
-    return BigInt._from_java_object(this._java_bigint.add(other._java_bigint));
-  },
-  
-  bitLength: function() {
-    return this._java_bigint.bitLength();
-  },
-  
-  mod: function(modulus) {
-    return BigInt._from_java_object(this._java_bigint.mod(modulus._java_bigint));
-  },
-  
-  equals: function(other) {
-    return this._java_bigint.equals(other._java_bigint);
-  },
-  
-  modPow: function(exp, modulus) {
-    return BigInt._from_java_object(this._java_bigint.modPow(exp._java_bigint, modulus._java_bigint));
-  },
-  
-  negate: function() {
-    return BigInt._from_java_object(this._java_bigint.negate());
-  },
-  
-  multiply: function(other) {
-    return BigInt._from_java_object(this._java_bigint.multiply(other._java_bigint));
-  },
-  
-  modInverse: function(modulus) {
-    return BigInt._from_java_object(this._java_bigint.modInverse(modulus._java_bigint));
-  }
-  
-});
-
-BigInt.ready_p = false;
-
-//
-// Some Class Methods
-//
-BigInt._from_java_object = function(jo) {
-  // bogus object
-  var obj = new BigInt("0",10);
-  obj._java_bigint = jo;
-  return obj;
-};
-
-BigInt.fromJSONObject = function(s) {
-  return new BigInt(s, 10);
-};
-
-BigInt.fromInt = function(i) {
-  return BigInt.fromJSONObject("" + i);
-};
-
-//
-// do the applet check
-//
-function check_applet() {
-  /* Is this Netscape 4.xx? */
-  var is_ns4 = (navigator.appName == "Netscape" && navigator.appVersion < "5");
-
-  /* Do we need the toString() workaround (requires applet)? */
-  var str_workaround = (navigator.appName == "Opera");
-
-  // stuff this in BigInt
-  BigInt.is_ie = (navigator.appName == "Microsoft Internet Explorer");
-
-  /* Decide whether we need the helper applet or not */
-  var use_applet = BigInt.is_ie || (!is_ns4 && navigator.platform.substr(0, 5) == "Linux") || str_workaround || typeof(java) == 'undefined';
-
-  // add the applet
-  if (use_applet) {
-    var applet_base = '/static/helios/helios/';
-    
-    var applet_html = '<applet codebase="' + applet_base + '" mayscript name="bigint" code="bigint.class" width=1 height=1 id="bigint_applet"></applet>';
-    // var applet_html = '<object classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" name="bigint" width="1" height="1" codebase="http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"> <param name="code" value="bigint.class"> <param name="codebase" value="' + applet_base + '"> <param name="archive" value="myapplet.jar"> <param name="type" value="application/x-java-applet;version=1.5.0"> <param name="scriptable" value="true"> <param name="mayscript" value="false"> <comment> <embed code="bigint.class" name="bigint" java_codebase="' + applet_base + '" width="1" height="1" scriptable="true" mayscript="false" type="application/x-java-applet;version=1.5.0" pluginspage="http://java.sun.com/j2se/1.5.0/download.html"> <noembed>No Java Support.</noembed> </embed> </comment> </object>';
-    $("#applet_div").html(applet_html);
-  }
-  
-  return use_applet;
-};
-
-// Set up the pointer to the applet if necessary, and some
-// basic Big Ints that everyone needs (0, 1, 2, and 42)
-BigInt._setup = function() {
-  if (BigInt.use_applet) {
-      BigInt.APPLET = document.applets["bigint"];
-  }
-
-  try {
-    BigInt.ZERO = new BigInt("0",10);
-    BigInt.ONE = new BigInt("1",10);
-    BigInt.TWO = new BigInt("2",10);
-    BigInt.FORTY_TWO = new BigInt("42",10);
-  
-    BigInt.ready_p = true;
-  } catch (e) {
-    // not ready
-    // count how many times we've tried
-    if (this.num_invocations == null)
-      this.num_invocations = 0;
-
-    this.num_invocations += 1;
-
-    if (this.num_invocations > 5) {
-      if (BigInt.setup_interval)
-        window.clearInterval(BigInt.setup_interval);
-      
-      if (BigInt.setup_fail) {
-        BigInt.setup_fail();
-      } else {
-        alert('bigint failed!');
-      }
-    }
-    return;
-  }
-  
-  if (BigInt.setup_interval)
-    window.clearInterval(BigInt.setup_interval);
-    
-  if (BigInt.setup_callback)
-    BigInt.setup_callback();
-};
-
-BigInt.setup = function(callback, fail_callback) {
-  if (callback)
-    BigInt.setup_callback = callback;
-  
-  if (fail_callback)
-    BigInt.setup_fail = fail_callback;
-  
-  BigInt.setup_interval = window.setInterval("BigInt._setup()", 1000);
-}
-
-// .onload instead of .ready, as I don't think the applet is ready until onload.
-// FIXME: something wrong here in the first load
-$(document).ready(function() {
-    BigInt.use_applet = check_applet();
-});
-
diff --git a/helios/media/helios/class.js b/helios/media/helios/class.js
deleted file mode 100644
index c1b33a002de5fb705c279fb92c03c2b0e66b9f22..0000000000000000000000000000000000000000
--- a/helios/media/helios/class.js
+++ /dev/null
@@ -1,65 +0,0 @@
-
-/*
- * John Resig's Class Inheritance
- */
- 
-// Inspired by base2 and Prototype
-(function(){
-  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
-
-  // The base Class implementation (does nothing)
-  this.Class = function(){};
- 
-  // Create a new Class that inherits from this class
-  Class.extend = function(prop) {
-    var _super = this.prototype;
-   
-    // Instantiate a base class (but only create the instance,
-    // don't run the init constructor)
-    initializing = true;
-    var prototype = new this();
-    initializing = false;
-   
-    // Copy the properties over onto the new prototype
-    for (var name in prop) {
-      // Check if we're overwriting an existing function
-      prototype[name] = typeof prop[name] == "function" &&
-        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
-        (function(name, fn){
-          return function() {
-            var tmp = this._super;
-           
-            // Add a new ._super() method that is the same method
-            // but on the super-class
-            this._super = _super[name];
-           
-            // The method only need to be bound temporarily, so we
-            // remove it when we're done executing
-            var ret = fn.apply(this, arguments);       
-            this._super = tmp;
-           
-            return ret;
-          };
-        })(name, prop[name]) :
-        prop[name];
-    }
-   
-    // The dummy class constructor
-    function Class() {
-      // All construction is actually done in the init method
-      if ( !initializing && this.init )
-        this.init.apply(this, arguments);
-    }
-   
-    // Populate our constructed prototype object
-    Class.prototype = prototype;
-   
-    // Enforce the constructor to be what we expect
-    Class.constructor = Class;
-
-    // And make this class extendable
-    Class.extend = arguments.callee;
-   
-    return Class;
-  };
-})();
diff --git a/helios/media/helios/elgamal.js b/helios/media/helios/elgamal.js
deleted file mode 100644
index dd022332ca4eab4efb028cd8065f04f1cd923b64..0000000000000000000000000000000000000000
--- a/helios/media/helios/elgamal.js
+++ /dev/null
@@ -1,538 +0,0 @@
-
-//
-// inspired by George Danezis, rewritten by Ben Adida.
-//
-
-ElGamal = {};
-
-ElGamal.Params = Class.extend({
-  init: function(p, q, g) {
-    this.p = p;
-    this.q = q;
-    this.g = g;
-  },
-  
-  generate: function() {
-    // get the value x
-    var x = Random.getRandomInteger(this.q);
-    var y = this.g.modPow(x, this.p);
-    var pk = new ElGamal.PublicKey(this.p, this.q, this.g, y);
-    var sk = new ElGamal.SecretKey(x, pk);
-    return sk;
-  },
-  
-  toJSONObject: function() {
-    return {g: this.g.toJSONObject(), p: this.p.toJSONObject(), q: this.q.toJSONObject()};
-  }
-});
-
-ElGamal.Params.fromJSONObject = function(d) {
-  var params = new ElGamal.Params();
-  params.p = BigInt.fromJSONObject(d.p);
-  params.q = BigInt.fromJSONObject(d.q);
-  params.g = BigInt.fromJSONObject(d.g);
-  return params;
-};
-
-ElGamal.PublicKey = Class.extend({
-  init : function(p,q,g,y) {
-    this.p = p;
-    this.q = q;
-    this.g = g;
-    this.y = y;
-  },
-  
-  toJSONObject: function() {
-    return {g : this.g.toJSONObject(), p : this.p.toJSONObject(), q : this.q.toJSONObject(), y : this.y.toJSONObject()};
-  },
-  
-  verifyKnowledgeOfSecretKey: function(proof, challenge_generator) {
-    // if challenge_generator is present, we have to check that the challenge was properly generated.
-    if (challenge_generator != null) {
-      if (!proof.challenge.equals(challenge_generator(proof.commitment))) {
-        return false;
-      }
-    }
-    
-    // verify that g^response = s * y^challenge
-    var check = this.g.modPow(proof.response, this.p).equals(this.y.modPow(proof.challenge, this.p).multiply(proof.commitment).mod(this.p));
-    
-    return check;
-  },
-  
-  // check if the decryption factor is correct for this public key, given the proof
-  verifyDecryptionFactor: function(ciphertext, decryption_factor, decryption_proof, challenge_generator) {
-    return decryption_proof.verify(this.g, ciphertext.alpha, this.y, decryption_factor, this.p, this.q, challenge_generator);
-  },
-  
-  multiply: function(other) {
-    // base condition
-    if (other == 0 || other == 1) {
-      return this;
-    }
-    
-    // check params
-    if (!this.p.equals(other.p))
-      throw "mismatched params";
-    if (!this.g.equals(other.g))
-      throw "mismatched params";
-    
-    var new_pk = new ElGamal.PublicKey(this.p, this.q, this.g, this.y.multiply(other.y).mod(this.p));
-    return new_pk;
-  },
-  
-  equals: function(other) {
-    return (this.p.equals(other.p) && this.q.equals(other.q) && this.g.equals(other.g) && this.y.equals(other.y));
-  }
-  
-});
-
-ElGamal.PublicKey.fromJSONObject = function(d) {
-  var pk = new ElGamal.PublicKey();
-  pk.p = BigInt.fromJSONObject(d.p);
-  pk.q = BigInt.fromJSONObject(d.q);
-  pk.g = BigInt.fromJSONObject(d.g);
-  pk.y = BigInt.fromJSONObject(d.y);
-  return pk;
-};
-
-ElGamal.SecretKey = Class.extend({
-  init: function(x, pk) {
-    this.x = x;
-    this.pk = pk;
-  },
-  
-  toJSONObject: function() {
-    return {public_key: this.pk.toJSONObject(), x: this.x.toJSONObject()};
-  },
-  
-  // a decryption factor is *not yet* mod-inverted, because it needs to be part of the proof.
-  decryptionFactor: function(ciphertext) {
-    var decryption_factor = ciphertext.alpha.modPow(this.x, this.pk.p);
-    return decryption_factor;
-  },
-  
-  decrypt: function(ciphertext, decryption_factor) {
-    if (!decryption_factor)
-      decryption_factor = this.decryptionFactor(ciphertext);
-
-    // use the ciphertext's built-in decryption given a list of decryption factors.
-    return ciphertext.decrypt([decryption_factor]);
-  },
-  
-  decryptAndProve: function(ciphertext, challenge_generator) {
-    var dec_factor_and_proof = this.decryptionFactorAndProof(ciphertext, challenge_generator);
-    
-    // decrypt, but using the already computed decryption factor
-    var plaintext = this.decrypt(ciphertext, dec_factor_and_proof.decryption_factor);
-
-    return {
-      'plaintext': plaintext,
-      'proof': dec_factor_and_proof.decryption_proof
-    };
-  },
-  
-  decryptionFactorAndProof: function(ciphertext, challenge_generator) {
-    var decryption_factor = this.decryptionFactor(ciphertext);
-    
-    // the DH tuple we need to prove, given the secret key x, is:
-    // g, alpha, y, beta/m
-    var proof = ElGamal.Proof.generate(this.pk.g, ciphertext.alpha, this.x, this.pk.p, this.pk.q, challenge_generator);
-    
-    return {
-      'decryption_factor' : decryption_factor,
-      'decryption_proof' : proof
-    }
-  },
-  
-  // generate a proof of knowledge of the secret exponent x
-  proveKnowledge: function(challenge_generator) {
-    // generate random w
-    var w = Random.getRandomInteger(this.pk.q);
-
-    // compute s = g^w for random w.
-    var s = this.pk.g.modPow(w, this.pk.p);
-    
-    // get challenge
-    var challenge = challenge_generator(s);
-    
-    // compute response = w +  x * challenge
-    var response = w.add(this.x.multiply(challenge).mod(this.pk.q));
-    
-    return new ElGamal.DLogProof(s, challenge, response);
-  }
-});
-
-ElGamal.SecretKey.fromJSONObject = function(d) {
-  var sk = new ElGamal.SecretKey();
-  sk.pk = ElGamal.PublicKey.fromJSONObject(d.public_key);
-  sk.x = BigInt.fromJSONObject(d.x);
-  return sk;
-}
-
-ElGamal.Ciphertext = Class.extend({
-  init: function(alpha, beta, pk) {
-    this.alpha = alpha;
-    this.beta = beta;
-    this.pk = pk;
-  },
-  
-  toString: function() {
-    return this.alpha.toString() + ',' + this.beta.toString();
-  },
-  
-  toJSONObject: function() {
-    return {alpha: this.alpha.toJSONObject(), beta: this.beta.toJSONObject()}
-  },
-  
-  multiply: function(other) {
-    // special case if other is 1 to enable easy aggregate ops
-    if (other == 1)
-      return this;
-    
-    // homomorphic multiply
-    return new ElGamal.Ciphertext(this.alpha.multiply(other.alpha).mod(this.pk.p),
-                                  this.beta.multiply(other.beta).mod(this.pk.p),
-                                  this.pk);
-  },
-  
-  // a decryption method by decryption factors
-  decrypt: function(list_of_dec_factors) {
-    var running_decryption = this.beta;
-    var self = this;
-    $(list_of_dec_factors).each(function(i, dec_factor) {
-      running_decryption = dec_factor.modInverse(self.pk.p).multiply(running_decryption).mod(self.pk.p);    
-    });
-    
-    return new ElGamal.Plaintext(running_decryption, this.pk, false);    
-  },
-  
-  generateProof: function(plaintext, randomness, challenge_generator) {
-    // DH tuple to prove is 
-    // g, y, alpha, beta/m
-    // with dlog randomness
-    var proof = ElGamal.Proof.generate(this.pk.g, this.pk.y, randomness, this.pk.p, this.pk.q, challenge_generator);
-    
-    return proof;
-  },
-  
-  simulateProof: function(plaintext, challenge) {
-    // compute beta/plaintext, the completion of the DH tuple
-    var beta_over_plaintext = this.beta.multiply(plaintext.m.modInverse(this.pk.p)).mod(this.pk.p);
-    
-    // the DH tuple we are simulating here is
-    // g, y, alpha, beta/m
-    return ElGamal.Proof.simulate(this.pk.g, this.pk.y, this.alpha, beta_over_plaintext, this.pk.p, this.pk.q, challenge);
-  },
-  
-  verifyProof: function(plaintext, proof, challenge_generator) {
-    // DH tuple to verify is 
-    // g, y, alpha, beta/m
-    var beta_over_m = this.beta.multiply(plaintext.m.modInverse(this.pk.p)).mod(this.pk.p);
-    
-    return proof.verify(this.pk.g, this.pk.y, this.alpha, beta_over_m, this.pk.p, this.pk.q, challenge_generator);
-  },
-
-  verifyDecryptionProof: function(plaintext, proof, challenge_generator) {
-    // DH tuple to verify is 
-    // g, alpha, y, beta/m
-    // since the proven dlog is the secret key x, y=g^x.
-    var beta_over_m = this.beta.multiply(plaintext.m.modInverse(this.pk.p)).mod(this.pk.p);
-
-    return proof.verify(this.pk.g, this.alpha, this.pk.y, beta_over_m, this.pk.p, this.pk.q, challenge_generator);
-  },
-  
-  generateDisjunctiveProof: function(list_of_plaintexts, real_index, randomness, challenge_generator) {
-    // go through all plaintexts and simulate the ones that must be simulated.
-    // note how the interface is as such so that the result does not reveal which is the real proof.
-    var self = this;
-    
-    var proofs = $(list_of_plaintexts).map(function(p_num, plaintext) {
-      if (p_num == real_index) {
-        // no real proof yet
-        return {};
-      } else {
-        // simulate!
-        return self.simulateProof(plaintext);
-      }
-    });
-    
-    // do the real proof
-    var real_proof = this.generateProof(list_of_plaintexts[real_index], randomness, function(commitment) {
-      // now we generate the challenge for the real proof by first determining
-      // the challenge for the whole disjunctive proof.
-      
-      // set up the partial real proof so we're ready to get the hash;
-      proofs[real_index] = {'commitment' : commitment};
-
-      // get the commitments in a list and generate the whole disjunctive challenge
-      var commitments = $(proofs).map(function(proof_num, proof) {
-        return proof.commitment;
-      });
-      
-      var disjunctive_challenge = challenge_generator(commitments);
-      
-      // now we must subtract all of the other challenges from this challenge.
-      var real_challenge = disjunctive_challenge;
-      $(proofs).each(function(proof_num, proof) {
-        if (proof_num != real_index)
-          real_challenge = real_challenge.add(proof.challenge.negate());
-      });
-      
-      // make sure we mod q, the exponent modulus
-      return real_challenge.mod(self.pk.q);
-    });
-    
-    // set the real proof
-    proofs[real_index] = real_proof;
-        
-    return new ElGamal.DisjunctiveProof(proofs);
-  },
-  
-  verifyDisjunctiveProof: function(list_of_plaintexts, disj_proof, challenge_generator) {
-    var result = true;
-    var proofs = disj_proof.proofs;
-    
-    // for loop because we want to bail out of the inner loop
-    // if we fail one of the verifications.
-    for (var i=0; i < list_of_plaintexts.length; i++) {
-      if (!this.verifyProof(list_of_plaintexts[i], proofs[i]))
-        return false;
-    }
-    
-    // check the overall challenge
-    
-    // first the one expected from the proofs
-    var commitments = $(proofs).map(function(proof_num, proof) {return proof.commitment;});
-    var expected_challenge = challenge_generator(commitments);
-    
-    // then the one that is the sum of the previous one.
-    var sum = new BigInt("0", 10); var self = this;
-    $(proofs).each(function(p_num, proof) {sum = sum.add(proof.challenge).mod(self.pk.q);});
-    
-    return expected_challenge.equals(sum);    
-  },
-  
-  equals: function(other) {
-    return (this.alpha.equals(other.alpha) && this.beta.equals(other.beta));
-  }
-});
-
-ElGamal.Ciphertext.fromJSONObject = function(d, pk) {
-  return new ElGamal.Ciphertext(BigInt.fromJSONObject(d.alpha), BigInt.fromJSONObject(d.beta), pk);
-};
-
-// we need the public key to figure out how to encode m
-ElGamal.Plaintext = Class.extend({
-  init: function(m, pk, encode_m) {
-    if (m == null) {
-      alert('oy null m');
-	    return;
-    }
-
-    this.pk = pk;
-
-    if (encode_m) {
-      // need to encode the message given that p = 2q+1
-      var y = m.add(BigInt.ONE);
-      var test = y.modPow(pk.q, pk.p);
-      if (test.equals(BigInt.ONE)) {
-    	  this.m = y;
-      } else {
-    	  this.m = y.negate().mod(pk.p);
-      }
-    } else {
-      this.m = m;
-    }    
-  },
-  
-  getPlaintext: function() {
-    var y;
-
-    // if m < q
-    if (this.m.compareTo(this.pk.q) < 0) {
-  	  y = this.m;
-    } else {
-  	  y = this.m.negate().mod(this.pk.p);
-    }
-
-    return y.subtract(BigInt.ONE);
-  },
-  
-  getM: function() {
-    return this.m;
-  }
-  
-});
-
-//
-// Proof abstraction
-//
-
-ElGamal.Proof = Class.extend({
-  init: function(A, B, challenge, response) {
-    this.commitment = {};
-    this.commitment.A = A;
-    this.commitment.B = B;
-    this.challenge = challenge;
-    this.response = response;
-  },
-  
-  toJSONObject: function() {
-    return {
-      challenge : this.challenge.toJSONObject(),
-      commitment : {A: this.commitment.A.toJSONObject(), B: this.commitment.B.toJSONObject()},
-      response : this.response.toJSONObject()
-    }
-  },
-  
-  // verify a DH tuple proof
-  verify: function(little_g, little_h, big_g, big_h, p, q, challenge_generator) {
-    // check that little_g^response = A * big_g^challenge
-    var first_check = little_g.modPow(this.response, p).equals(big_g.modPow(this.challenge, p).multiply(this.commitment.A).mod(p));
-
-    // check that little_h^response = B * big_h^challenge
-    var second_check = little_h.modPow(this.response, p).equals(big_h.modPow(this.challenge, p).multiply(this.commitment.B).mod(p));
-    
-    var third_check = true;
-    
-    if (challenge_generator) {
-      third_check = this.challenge.equals(challenge_generator(this.commitment));
-    }
-    
-    return (first_check && second_check && third_check);
-  }
-});
-
-ElGamal.Proof.fromJSONObject = function(d) {
-  return new ElGamal.Proof(
-    BigInt.fromJSONObject(d.commitment.A),
-    BigInt.fromJSONObject(d.commitment.B),
-    BigInt.fromJSONObject(d.challenge),
-    BigInt.fromJSONObject(d.response));
-};
-
-// a generic way to prove that four values are a DH tuple.
-// a DH tuple is g,h,G,H where G = g^x and H=h^x
-// challenge generator takes a commitment, whose subvalues are A and B
-// all modulo p, with group order q, which we provide just in case.
-// as it turns out, G and H are not necessary to generate this proof, given that they're implied by x.
-ElGamal.Proof.generate = function(little_g, little_h, x, p, q, challenge_generator) {
-  // generate random w
-  var w = Random.getRandomInteger(q);
-  
-  // create a proof instance
-  var proof = new ElGamal.Proof();
-  
-  // compute A=little_g^w, B=little_h^w
-  proof.commitment.A = little_g.modPow(w, p);
-  proof.commitment.B = little_h.modPow(w, p);
-  
-  // Get the challenge from the callback that generates it
-  proof.challenge = challenge_generator(proof.commitment);
-  
-  // Compute response = w + x * challenge
-  proof.response = w.add(x.multiply(proof.challenge).mod(q));
-  
-  return proof;
-};
-
-// simulate a a DH-tuple proof, with a potentially assigned challenge (but can be null)
-ElGamal.Proof.simulate = function(little_g, little_h, big_g, big_h, p, q, challenge) {
-  // generate a random challenge if not provided
-  if (challenge == null) {
-    challenge = Random.getRandomInteger(q);
-  }
-  
-  // random response, does not even need to depend on the challenge
-  var response = Random.getRandomInteger(q);
-
-  // now we compute A and B
-  // A = little_g ^ w, and at verification time, g^response = G^challenge * A, so A = (G^challenge)^-1 * g^response
-  var A = big_g.modPow(challenge, p).modInverse(p).multiply(little_g.modPow(response, p)).mod(p);
-
-  // B = little_h ^ w, and at verification time, h^response = H^challenge * B, so B = (H^challenge)^-1 * h^response
-  var B = big_h.modPow(challenge, p).modInverse(p).multiply(little_h.modPow(response, p)).mod(p);
-
-  return new ElGamal.Proof(A, B, challenge, response);  
-};
-
-ElGamal.DisjunctiveProof = Class.extend({
-  init: function(list_of_proofs) {
-    this.proofs = list_of_proofs;
-  },
-  
-  toJSONObject: function() {
-    return $(this.proofs).map(function(i, proof) {
-      return proof.toJSONObject();
-    });
-  }
-});
-
-ElGamal.DisjunctiveProof.fromJSONObject = function(d) {
-  if (d==null)
-    return null;
-    
-  return new ElGamal.DisjunctiveProof(
-    $(d).map(function(i, p) {
-      return ElGamal.Proof.fromJSONObject(p);
-    })
-  );
-};
-
-ElGamal.encrypt = function(pk, plaintext, r) {
-  if (plaintext.getM().equals(BigInt.ZERO))
-    throw "Can't encrypt 0 with El Gamal"
-
-  if (!r)
-    r = Random.getRandomInteger(pk.q);
-  
-  var alpha = pk.g.modPow(r, pk.p);
-  var beta = (pk.y.modPow(r, pk.p)).multiply(plaintext.m).mod(pk.p);
-  
-  return new ElGamal.Ciphertext(alpha, beta, pk);
-};
-
-//
-// DLog Proof
-//
-ElGamal.DLogProof = Class.extend({
-  init: function(commitment, challenge, response) {
-    this.commitment = commitment;
-    this.challenge = challenge;
-    this.response = response;
-  },
-  
-  toJSONObject: function() {
-    return {'challenge' : this.challenge.toJSONObject(), 'commitment': this.commitment.toJSONObject(), 'response': this.response.toJSONObject()};
-  }
-});
-
-ElGamal.DLogProof.fromJSONObject = function(d) {
-  return new ElGamal.DLogProof(BigInt.fromJSONObject(d.commitment || d.s), BigInt.fromJSONObject(d.challenge), BigInt.fromJSONObject(d.response));
-};
-
-// a challenge generator based on a list of commitments of
-// proofs of knowledge of plaintext. Just appends A and B with commas.
-ElGamal.disjunctive_challenge_generator = function(commitments) {
-  var strings_to_hash = [];
-
-  // go through all proofs and append the commitments
-  $(commitments).each(function(commitment_num, commitment) {
-    // toJSONObject instead of toString because of IE weirdness.
-    strings_to_hash[strings_to_hash.length] = commitment.A.toJSONObject();
-    strings_to_hash[strings_to_hash.length] = commitment.B.toJSONObject();
-  });
-  
-  // STRINGS = strings_to_hash;
-  return new BigInt(hex_sha1(strings_to_hash.join(",")), 16);
-};
-
-// a challenge generator for Fiat-Shamir
-ElGamal.fiatshamir_challenge_generator = function(commitment) {
-  return ElGamal.disjunctive_challenge_generator([commitment]);
-};
-
-ElGamal.fiatshamir_dlog_challenge_generator = function(commitment) {
-  return new BigInt(hex_sha1(commitment.toJSONObject()), 16);
-};
\ No newline at end of file
diff --git a/helios/media/helios/helios.js b/helios/media/helios/helios.js
deleted file mode 100644
index 7a71bab1a37dc2bae2fb30ae4ddc8df087d5b8a1..0000000000000000000000000000000000000000
--- a/helios/media/helios/helios.js
+++ /dev/null
@@ -1,609 +0,0 @@
-
-//
-// Helios Protocols
-// 
-// ben@adida.net
-//
-// FIXME: needs a healthy refactor/cleanup based on Class.extend()
-//
-
-// extend jquery to do object keys
-// from http://snipplr.com/view.php?codeview&id=10430
-$.extend({
-    keys: function(obj){
-        var a = [];
-        $.each(obj, function(k){ a.push(k) });
-        return a.sort();
-    }
-});
-
-var UTILS = {};
-
-UTILS.array_remove_value = function(arr, val) {
-  var new_arr = [];
-  $(arr).each(function(i, v) {
-    if (v != val) {
-	new_arr.push(v);
-    }
-  });
-
-  return new_arr;
-};
-
-UTILS.select_element_content = function(element) {
-  var range;
-  if (window.getSelection) { // FF, Safari, Opera
-    var sel = window.getSelection();
-    range = document.createRange();
-    range.selectNodeContents(element);
-    sel.removeAllRanges();
-    sel.addRange(range);
-  } else {
-    document.selection.empty();
-    range = document.body.createTextRange();
-    range.moveToElementText(el);
-    range.select();
-  }
-};
-
-// a progress tracker
-UTILS.PROGRESS = Class.extend({
-  init: function() {
-    this.n_ticks = 0.0;
-    this.current_tick = 0.0;
-  },
-  
-  addTicks: function(n_ticks) {
-    this.n_ticks += n_ticks;
-  },
-  
-  tick: function() {
-    this.current_tick += 1.0;
-  },
-  
-  progress: function() {
-    return Math.round((this.current_tick / this.n_ticks) * 100);
-  }
-});
-
-// produce the same object but with keys sorted
-UTILS.object_sort_keys = function(obj) {
-  var new_obj = {};
-  $($.keys(obj)).each(function(i, k) {
-    new_obj[k] = obj[k];
-  });
-  return new_obj;
-};
-
-//
-// Helios Stuff
-//
-
-HELIOS = {};
-
-// election
-HELIOS.Election = Class.extend({
-  init: function() {
-  },
-  
-  toJSONObject: function() {
-    var json_obj = {uuid : this.uuid,
-    description : this.description, short_name : this.short_name, name : this.name,
-    public_key: this.public_key.toJSONObject(), questions : this.questions,
-    cast_url: this.cast_url, frozen_at: this.frozen_at,
-    openreg: this.openreg, voters_hash: this.voters_hash,
-    use_voter_aliases: this.use_voter_aliases,
-    voting_starts_at: this.voting_starts_at,
-    voting_ends_at: this.voting_ends_at};
-    
-    return UTILS.object_sort_keys(json_obj);
-  },
-  
-  get_hash: function() {
-    if (this.election_hash)
-      return this.election_hash;
-    
-    // otherwise  
-    return b64_sha256(this.toJSON());
-  },
-  
-  toJSON: function() {
-    // FIXME: only way around the backslash thing for now.... how ugly
-    //return jQuery.toJSON(this.toJSONObject()).replace(/\//g,"\\/");
-    return jQuery.toJSON(this.toJSONObject());
-  }
-});
-
-HELIOS.Election.fromJSONString = function(raw_json) {
-  var json_object = $.secureEvalJSON(raw_json);
-  
-  // hash fix for the issue with re-json'ifying unicode chars
-  var election = HELIOS.Election.fromJSONObject(json_object);
-  election.election_hash = b64_sha256(election.toJSON());
-  
-  return election;
-};
-
-HELIOS.Election.fromJSONObject = function(d) {
-  var el = new HELIOS.Election();
-  jQuery.extend(el, d);
-  
-  // empty questions
-  if (!el.questions)
-    el.questions = [];
-  
-  if (el.public_key)
-    el.public_key = ElGamal.PublicKey.fromJSONObject(el.public_key);
-    
-  return el;
-};
-
-HELIOS.Election.setup = function(election) {
-  return ELECTION.fromJSONObject(election);
-};
-
-
-// ballot handling
-BALLOT = {};
-
-BALLOT.pretty_choices = function(election, ballot) {
-    var questions = election.questions;
-    var answers = ballot.answers;
-
-    // process the answers
-    var choices = $(questions).map(function(q_num) {
-	    return $(answers[q_num]).map(function(dummy, ans) {
-	      return questions[q_num].answers[ans];
-	    });
-    });
-
-    return choices;
-};
-
-
-// open up a new window and do something with it.
-UTILS.open_window_with_content = function(content, mime_type) {
-    if (!mime_type)
-	mime_type = "text/plain";
-    if (BigInt.is_ie) {
-	    w = window.open("");
-	    w.document.open(mime_type);
-	    w.document.write(content);
-	    w.document.close();
-    } else {
-	    w = window.open("data:" + mime_type + "," + encodeURIComponent(content));
-    }
-};
-
-// generate an array of the first few plaintexts
-UTILS.generate_plaintexts = function(pk, min, max) {
-  var last_plaintext = BigInt.ONE;
-
-  // an array of plaintexts
-  var plaintexts = [];
-  
-  if (min == null)
-    min = 0;
-  
-  // questions with more than one possible answer, add to the array.
-  for (var i=0; i<=max; i++) {
-    if (i >= min)
-      plaintexts.push(new ElGamal.Plaintext(last_plaintext, pk, false));
-    last_plaintext = last_plaintext.multiply(pk.g).mod(pk.p);
-  }
-  
-  return plaintexts;
-}
-
-
-//
-// crypto
-//
-
-
-HELIOS.EncryptedAnswer = Class.extend({
-  init: function(question, answer, pk, progress) {    
-    // if nothing in the constructor
-    if (question == null)
-      return;
-
-    // store answer
-    // CHANGE 2008-08-06: answer is now an *array* of answers, not just a single integer
-    this.answer = answer;
-
-    // do the encryption
-    var enc_result = this.doEncryption(question, answer, pk, null, progress);
-
-    this.choices = enc_result.choices;
-    this.randomness = enc_result.randomness;
-    this.individual_proofs = enc_result.individual_proofs;
-    this.overall_proof = enc_result.overall_proof;    
-  },
-  
-  doEncryption: function(question, answer, pk, randomness, progress) {
-    var choices = [];
-    var individual_proofs = [];
-    var overall_proof = null;
-    
-    // possible plaintexts [question.min .. , question.max]
-    var plaintexts = null;
-    if (question.max != null)
-      plaintexts = UTILS.generate_plaintexts(pk, question.min, question.max);
-    
-    var zero_one_plaintexts = UTILS.generate_plaintexts(pk, 0, 1);
-    
-    // keep track of whether we need to generate new randomness
-    var generate_new_randomness = false;    
-    if (!randomness) {
-      randomness = [];
-      generate_new_randomness = true;
-    }
-    
-    // keep track of number of options selected.
-    var num_selected_answers = 0;
-    
-    // go through each possible answer and encrypt either a g^0 or a g^1.
-    for (var i=0; i<question.answers.length; i++) {
-      var index, plaintext_index;
-      // if this is the answer, swap them so m is encryption 1 (g)
-      if (jQuery.inArray(i, answer) > -1) {
-        plaintext_index = 1;
-        num_selected_answers += 1;
-      } else {
-        plaintext_index = 0;
-      }
-
-      // generate randomness?
-      if (generate_new_randomness) {
-        randomness[i] = Random.getRandomInteger(pk.q);        
-      }
-
-      choices[i] = ElGamal.encrypt(pk, zero_one_plaintexts[plaintext_index], randomness[i]);
-      
-      // generate proof
-      if (generate_new_randomness) {
-        // generate proof that this ciphertext is a 0 or a 1
-        individual_proofs[i] = choices[i].generateDisjunctiveProof(zero_one_plaintexts, plaintext_index, randomness[i], ElGamal.disjunctive_challenge_generator);        
-      }
-      
-      if (progress)
-        progress.tick();
-    }
-
-    if (generate_new_randomness && question.max != null) {
-      // we also need proof that the whole thing sums up to the right number
-      // only if max is non-null, otherwise it's full approval voting
-    
-      // compute the homomorphic sum of all the options
-      var hom_sum = choices[0];
-      var rand_sum = randomness[0];
-      for (var i=1; i<question.answers.length; i++) {
-        hom_sum = hom_sum.multiply(choices[i]);
-        rand_sum = rand_sum.add(randomness[i]).mod(pk.q);
-      }
-    
-      // prove that the sum is 0 or 1 (can be "blank vote" for this answer)
-      // num_selected_answers is 0 or 1, which is the index into the plaintext that is actually encoded
-      //
-      // now that "plaintexts" only contains the array of plaintexts that are possible starting with min
-      // and going to max, the num_selected_answers needs to be reduced by min to be the proper index
-      var overall_plaintext_index = num_selected_answers;
-      if (question.min)
-        overall_plaintext_index -= question.min;
-        
-      overall_proof = hom_sum.generateDisjunctiveProof(plaintexts, overall_plaintext_index, rand_sum, ElGamal.disjunctive_challenge_generator);
-      if (progress) {
-        for (var i=0; i<question.max; i++)
-          progress.tick();
-      }
-    }
-    
-    return {
-      'choices' : choices,
-      'randomness' : randomness,
-      'individual_proofs' : individual_proofs,
-      'overall_proof' : overall_proof
-    };
-  },
-  
-  clearPlaintexts: function() {
-    this.answer = null;
-    this.randomness = null;
-  },
-  
-  // FIXME: should verifyEncryption really generate proofs? Overkill.
-  verifyEncryption: function(question, pk) {
-    var result = this.doEncryption(question, this.answer, pk, this.randomness);
-
-    // check that we have the same number of ciphertexts
-    if (result.choices.length != this.choices.length) {
-      return false;      
-    }
-      
-    // check the ciphertexts
-    for (var i=0; i<result.choices.length; i++) {
-      if (!result.choices[i].equals(this.choices[i])) {
-        // alert ("oy: " + result.choices[i] + "/" + this.choices[i]);
-        return false;
-      }
-    }
-    
-    // we made it, we're good
-    return true;
-  },
-  
-  toString: function() {
-    // get each ciphertext as a JSON string
-    var choices_strings = jQuery.makeArray($(this.choices).map(function(i,c) {return c.toString();}));
-    return choices_strings.join("|");
-  },
-  
-  toJSONObject: function(include_plaintext) {
-    var return_obj = {
-      'choices' : $(this.choices).map(function(i, choice) {
-        return choice.toJSONObject();
-      }),
-      'individual_proofs' : $(this.individual_proofs).map(function(i, disj_proof) {
-        return disj_proof.toJSONObject();
-      })
-    };
-    
-    if (this.overall_proof != null) {
-      return_obj.overall_proof = this.overall_proof.toJSONObject();
-    } else {
-      return_obj.overall_proof = null;
-    }
-    
-    if (include_plaintext) {
-      return_obj.answer = this.answer;
-      return_obj.randomness = $(this.randomness).map(function(i, r) {
-        return r.toJSONObject();
-      });
-    }
-    
-    return return_obj;
-  }
-});
-
-HELIOS.EncryptedAnswer.fromJSONObject = function(d, election) {
-  var ea = new HELIOS.EncryptedAnswer();
-  ea.choices = $(d.choices).map(function(i, choice) {
-    return ElGamal.Ciphertext.fromJSONObject(choice, election.public_key);
-  });
-  
-  ea.individual_proofs = $(d.individual_proofs).map(function (i, p) {
-    return ElGamal.DisjunctiveProof.fromJSONObject(p);
-  });
-  
-  ea.overall_proof = ElGamal.DisjunctiveProof.fromJSONObject(d.overall_proof);
-  
-  // possibly load randomness and plaintext
-  if (d.randomness) {
-    ea.randomness = $(d.randomness).map(function(i, r) {
-      return BigInt.fromJSONObject(r);
-    });
-    ea.answer = d.answer;
-  }
-  
-  return ea;
-};
-
-HELIOS.EncryptedVote = Class.extend({
-  init: function(election, answers, progress) {
-    // empty constructor
-    if (election == null)
-      return;
-
-    // keep information about the election around
-    this.election_uuid = election.uuid;
-    this.election_hash = election.get_hash();
-    this.election = election;
-     
-    if (answers == null)
-      return;
-      
-    var n_questions = election.questions.length;
-    this.encrypted_answers = [];
-
-    if (progress) {
-      // set up the number of ticks
-      $(election.questions).each(function(q_num, q) {
-        // + 1 for the overall proof
-        progress.addTicks(q.answers.length);
-        if (q.max != null)
-          progress.addTicks(q.max);
-      });
-    }
-    
-    progress.addTicks(0, n_questions);
-      
-    // loop through questions
-    for (var i=0; i<n_questions; i++) {
-      this.encrypted_answers[i] = new HELIOS.EncryptedAnswer(election.questions[i], answers[i], election.public_key, progress);
-    }    
-  },
-  
-  toString: function() {
-    // for each question, get the encrypted answer as a string
-    var answer_strings = jQuery.makeArray($(this.encrypted_answers).map(function(i,a) {return a.toString();}));
-    
-    return answer_strings.join("//");
-  },
-  
-  clearPlaintexts: function() {
-    $(this.encrypted_answers).each(function(i, ea) {
-      ea.clearPlaintexts();
-    });
-  },
-  
-  verifyEncryption: function(questions, pk) {
-    var overall_result = true;
-    $(this.encrypted_answers).each(function(i, ea) {
-      overall_result = overall_result && ea.verifyEncryption(questions[i], pk);
-    });
-    return overall_result;
-  },
-  
-  toJSONObject: function(include_plaintext) {
-    var answers = $(this.encrypted_answers).map(function(i,ea) {
-      return ea.toJSONObject(include_plaintext);
-    });
-    
-    return {
-      answers : answers,
-      election_hash : this.election_hash,
-      election_uuid : this.election_uuid
-    }
-  },
-  
-  get_hash: function() {
-    return b64_sha256(jQuery.toJSON(this));
-  },
-  
-  get_audit_trail: function() {
-    return this.toJSONObject(true);
-  },
-  
-  verifyProofs: function(pk, outcome_callback) {
-    var zero_or_one = UTILS.generate_plaintexts(pk, 0, 1);
-
-    var VALID_P = true;
-    
-    var self = this;
-    
-    // for each question and associate encrypted answer
-    $(this.encrypted_answers).each(function(ea_num, enc_answer) {
-        var overall_result = 1;
-
-        // the max number of answers (decides whether this is approval or not and requires an overall proof)
-        var max = self.election.questions[ea_num].max;
-
-        // go through each individual proof
-        $(enc_answer.choices).each(function(choice_num, choice) {
-          var result = choice.verifyDisjunctiveProof(zero_or_one, enc_answer.individual_proofs[choice_num], ElGamal.disjunctive_challenge_generator);
-          outcome_callback(ea_num, choice_num, result, choice);
-          
-          VALID_P = VALID_P && result;
-           
-          // keep track of homomorphic product, if needed
-          if (max != null)
-            overall_result = choice.multiply(overall_result);
-        });
-        
-        if (max != null) {
-          // possible plaintexts [0, 1, .. , question.max]
-          var plaintexts = UTILS.generate_plaintexts(pk, self.election.questions[ea_num].min, self.election.questions[ea_num].max);
-        
-          // check the proof on the overall product
-          var overall_check = overall_result.verifyDisjunctiveProof(plaintexts, enc_answer.overall_proof, ElGamal.disjunctive_challenge_generator);
-          outcome_callback(ea_num, null, overall_check, null);
-          VALID_P = VALID_P && overall_check;
-        } else {
-          // check to make sure the overall_proof is null, since it's approval voting
-          VALID_P = VALID_P && (enc_answer.overall_proof == null)
-        }
-    });
-    
-    return VALID_P;
-  }
-});
-
-HELIOS.EncryptedVote.fromJSONObject = function(d, election) {
-  if (d == null)
-    return null;
-    
-  var ev = new HELIOS.EncryptedVote(election);
-  
-  ev.encrypted_answers = $(d.answers).map(function(i, ea) {
-    return HELIOS.EncryptedAnswer.fromJSONObject(ea, election);
-  });
-  
-  ev.election_hash = d.election_hash;
-  ev.election_uuid = d.election_uuid;
-  
-  return ev;
-};
-
-//
-// Tally abstraction
-//
-
-HELIOS.Tally = Class.extend({
-  init: function(raw_tally, num_tallied) {
-    this.tally = raw_tally;
-    this.num_tallied = num_tallied;
-  },
-  
-  toJSONObject: function() {
-    var tally_json_obj = $(this.tally).map(function(i, one_q) {
-      return $(one_q).map(function(j, one_a) {
-        return one_a.toJSONObject();
-      });
-    });
-    
-    return {
-      num_tallied : this.num_tallied,
-      tally: tally_json_obj
-    };
-  }
-  
-});
-
-HELIOS.Tally.fromJSONObject = function(d, public_key) {
-  var num_tallied = d['num_tallied'];
-  
-  var raw_tally = $(d['tally']).map(function(i, one_q) {
-    return $(one_q).map(function(j, one_a) {
-      var new_val= ElGamal.Ciphertext.fromJSONObject(one_a, public_key);
-      return new_val;
-    });
-  });
-  
-  return new HELIOS.Tally(raw_tally, num_tallied);
-};
-
-//
-// distributed decryption : Trustees
-//
-
-// a utility function for jsonifying a list of lists of items
-HELIOS.jsonify_list_of_lists = function(lol) {
-  if (!lol)
-    return null;
-    
-  return $(lol).map(function(i, sublist) {return $(sublist).map(function(j, item) {return item.toJSONObject();})});
-};
-
-// a utility function for doing the opposite with an item-level de-jsonifier
-HELIOS.dejsonify_list_of_lists = function(lol, item_dejsonifier) {
-  if (!lol)
-    return null;
-    
-  return $(lol).map(function(i, sublist) {return $(sublist).map(function(j, item) {return item_dejsonifier(item);})});
-}
-
-HELIOS.Trustee = Class.extend({
-  init: function(uuid, public_key, public_key_hash, pok, decryption_factors, decryption_proofs) {
-    this.uuid = uuid;
-    this.public_key = public_key;
-    this.public_key_hash = public_key_hash;
-    this.pok = pok;
-    this.decryption_factors = decryption_factors;
-    this.decryption_proofs = decryption_proofs;
-  },
-  
-  toJSONObject: function() {
-    return {
-      'decryption_factors' : HELIOS.jsonify_list_of_lists(this.decryption_factors),
-      'decryption_proofs' : HELIOS.jsonify_list_of_list(this.decryption_proofs),
-      'email' : this.email, 'name' : this.name, 'pok' : this.pok.toJSONObject(), 'public_key' : this.public_key.toJSONObject()
-    }
-  }
-});
-
-HELIOS.Trustee.fromJSONObject = function(d) {
-  return new HELIOS.Trustee(d.uuid,
-    ElGamal.PublicKey.fromJSONObject(d.public_key), d.public_key_hash, ElGamal.DLogProof.fromJSONObject(d.pok),
-    HELIOS.dejsonify_list_of_lists(d.decryption_factors, BigInt.fromJSONObject),
-    HELIOS.dejsonify_list_of_lists(d.decryption_proofs, ElGamal.Proof.fromJSONObject));
-};
\ No newline at end of file
diff --git a/helios/media/helios/jscrypto b/helios/media/helios/jscrypto
new file mode 160000
index 0000000000000000000000000000000000000000..1f4037238216c8ffdb75ce6f4b5dc86a0e1cba98
--- /dev/null
+++ b/helios/media/helios/jscrypto
@@ -0,0 +1 @@
+Subproject commit 1f4037238216c8ffdb75ce6f4b5dc86a0e1cba98
diff --git a/helios/media/helios/random.js b/helios/media/helios/random.js
deleted file mode 100644
index 27760b03c29be316aaebb9bd69a7b2b8e1aa55b2..0000000000000000000000000000000000000000
--- a/helios/media/helios/random.js
+++ /dev/null
@@ -1,48 +0,0 @@
-
-/*
- * Random Number generation, now uses the glue to Java
- */
-
-Random = {};
-
-Random.GENERATOR = null;
-
-Random.setupGenerator = function() {
-    if (Random.GENERATOR == null) {
-	    if (BigInt.use_applet) {
-	      var foo = BigInt.APPLET.newSecureRandom();
-	      Random.GENERATOR = BigInt.APPLET.newSecureRandom();
-	    } else {
-	      // we do it twice because of some weird bug;
-	      var foo = new java.security.SecureRandom();
-	      Random.GENERATOR = new java.security.SecureRandom();
-	    }
-    }
-};
-
-Random.getRandomInteger = function(max) {
-  Random.setupGenerator();
-  var bit_length = max.bitLength();
-  var random;
-  if (BigInt.use_applet) {
-      random = BigInt.APPLET.randomBigInteger(bit_length, Random.GENERATOR);
-  } else {
-      random = new java.math.BigInteger(bit_length, Random.GENERATOR);
-  }
-  
-  return BigInt._from_java_object(random).mod(max);
-};
-
-Random.getRandomPrime = function(n_bits) {
-  Random.setupGenerator();
-  var certainty = 80;
-  var prime;
-  if (BigInt.use_applet) {
-      prime = BigInt.APPLET.randomPrimeBigInteger(n_bits, certainty, Random.GENERATOR);
-  } else {
-      prime = new java.math.BigInteger(n_bits, certainty, Random.GENERATOR);
-  }
-  
-  return BigInt._from_java_object(prime);
-};
-
diff --git a/helios/media/helios/sha1.js b/helios/media/helios/sha1.js
deleted file mode 100644
index ca6c8523a27b04fff3f297775d38ab75506d8134..0000000000000000000000000000000000000000
--- a/helios/media/helios/sha1.js
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
- * in FIPS PUB 180-1
- * Version 2.1a Copyright Paul Johnston 2000 - 2002.
- * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
- * Distributed under the BSD License
- * See http://pajhome.org.uk/crypt/md5 for details.
- */
-
-/*
- * Configurable variables. You may need to tweak these to be compatible with
- * the server-side, but the defaults work in most cases.
- */
-var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
-var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
-var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
-
-/*
- * These are the functions you'll usually want to call
- * They take string arguments and return either hex or base-64 encoded strings
- */
-function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
-function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
-function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
-function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
-function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
-function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
-
-/*
- * Perform a simple self-test to see if the VM is working
- */
-function sha1_vm_test()
-{
-  return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
-}
-
-/*
- * Calculate the SHA-1 of an array of big-endian words, and a bit length
- */
-function core_sha1(x, len)
-{
-  /* append padding */
-  x[len >> 5] |= 0x80 << (24 - len % 32);
-  x[((len + 64 >> 9) << 4) + 15] = len;
-
-  var w = Array(80);
-  var a =  1732584193;
-  var b = -271733879;
-  var c = -1732584194;
-  var d =  271733878;
-  var e = -1009589776;
-
-  for(var i = 0; i < x.length; i += 16)
-  {
-    var olda = a;
-    var oldb = b;
-    var oldc = c;
-    var oldd = d;
-    var olde = e;
-
-    for(var j = 0; j < 80; j++)
-    {
-      if(j < 16) w[j] = x[i + j];
-      else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
-      var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
-                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
-      e = d;
-      d = c;
-      c = rol(b, 30);
-      b = a;
-      a = t;
-    }
-
-    a = safe_add(a, olda);
-    b = safe_add(b, oldb);
-    c = safe_add(c, oldc);
-    d = safe_add(d, oldd);
-    e = safe_add(e, olde);
-  }
-  return Array(a, b, c, d, e);
-
-}
-
-/*
- * Perform the appropriate triplet combination function for the current
- * iteration
- */
-function sha1_ft(t, b, c, d)
-{
-  if(t < 20) return (b & c) | ((~b) & d);
-  if(t < 40) return b ^ c ^ d;
-  if(t < 60) return (b & c) | (b & d) | (c & d);
-  return b ^ c ^ d;
-}
-
-/*
- * Determine the appropriate additive constant for the current iteration
- */
-function sha1_kt(t)
-{
-  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
-         (t < 60) ? -1894007588 : -899497514;
-}
-
-/*
- * Calculate the HMAC-SHA1 of a key and some data
- */
-function core_hmac_sha1(key, data)
-{
-  var bkey = str2binb(key);
-  if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);
-
-  var ipad = Array(16), opad = Array(16);
-  for(var i = 0; i < 16; i++)
-  {
-    ipad[i] = bkey[i] ^ 0x36363636;
-    opad[i] = bkey[i] ^ 0x5C5C5C5C;
-  }
-
-  var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
-  return core_sha1(opad.concat(hash), 512 + 160);
-}
-
-/*
- * Add integers, wrapping at 2^32. This uses 16-bit operations internally
- * to work around bugs in some JS interpreters.
- */
-function safe_add(x, y)
-{
-  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
-  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
-  return (msw << 16) | (lsw & 0xFFFF);
-}
-
-/*
- * Bitwise rotate a 32-bit number to the left.
- */
-function rol(num, cnt)
-{
-  return (num << cnt) | (num >>> (32 - cnt));
-}
-
-/*
- * Convert an 8-bit or 16-bit string to an array of big-endian words
- * In 8-bit function, characters >255 have their hi-byte silently ignored.
- */
-function str2binb(str)
-{
-  var bin = Array();
-  var mask = (1 << chrsz) - 1;
-  for(var i = 0; i < str.length * chrsz; i += chrsz)
-    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
-  return bin;
-}
-
-/*
- * Convert an array of big-endian words to a string
- */
-function binb2str(bin)
-{
-  var str = "";
-  var mask = (1 << chrsz) - 1;
-  for(var i = 0; i < bin.length * 32; i += chrsz)
-    str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
-  return str;
-}
-
-/*
- * Convert an array of big-endian words to a hex string.
- */
-function binb2hex(binarray)
-{
-  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
-  var str = "";
-  for(var i = 0; i < binarray.length * 4; i++)
-  {
-    str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
-           hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
-  }
-  return str;
-}
-
-/*
- * Convert an array of big-endian words to a base-64 string
- */
-function binb2b64(binarray)
-{
-  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-  var str = "";
-  for(var i = 0; i < binarray.length * 4; i += 3)
-  {
-    var triplet = (((binarray[i   >> 2] >> 8 * (3 -  i   %4)) & 0xFF) << 16)
-                | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
-                |  ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
-    for(var j = 0; j < 4; j++)
-    {
-      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
-      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
-    }
-  }
-  return str;
-}
\ No newline at end of file
diff --git a/helios/media/helios/sha2.js b/helios/media/helios/sha2.js
deleted file mode 100644
index 3199a8c4e0f757ab6e612a4a429114c937a59a6a..0000000000000000000000000000000000000000
--- a/helios/media/helios/sha2.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/* A JavaScript implementation of the Secure Hash Standard
- * Version 0.3 Copyright Angel Marin 2003-2004 - http://anmar.eu.org/
- * Distributed under the BSD License
- * Some bits taken from Paul Johnston's SHA-1 implementation
- */
-var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
-var hexcase = 0;/* hex output format. 0 - lowercase; 1 - uppercase        */
-
-function safe_add (x, y) {
-  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
-  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
-  return (msw << 16) | (lsw & 0xFFFF);
-}
-
-function S (X, n) {return ( X >>> n ) | (X << (32 - n));}
-
-function R (X, n) {return ( X >>> n );}
-
-function Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
-
-function Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
-
-function Sigma0256(x) {return (S(x, 2) ^ S(x, 13) ^ S(x, 22));}
-
-function Sigma1256(x) {return (S(x, 6) ^ S(x, 11) ^ S(x, 25));}
-
-function Gamma0256(x) {return (S(x, 7) ^ S(x, 18) ^ R(x, 3));}
-
-function Gamma1256(x) {return (S(x, 17) ^ S(x, 19) ^ R(x, 10));}
-
-function Sigma0512(x) {return (S(x, 28) ^ S(x, 34) ^ S(x, 39));}
-
-function Sigma1512(x) {return (S(x, 14) ^ S(x, 18) ^ S(x, 41));}
-
-function Gamma0512(x) {return (S(x, 1) ^ S(x, 8) ^ R(x, 7));}
-
-function Gamma1512(x) {return (S(x, 19) ^ S(x, 61) ^ R(x, 6));}
-
-function core_sha256 (m, l) {
-    var K = new Array(0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0xFC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x6CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2);
-    var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19);
-    var W = new Array(64);
-    var a, b, c, d, e, f, g, h, i, j;
-    var T1, T2;
-
-    /* append padding */
-    m[l >> 5] |= 0x80 << (24 - l % 32);
-    m[((l + 64 >> 9) << 4) + 15] = l;
-
-    for ( var i = 0; i<m.length; i+=16 ) {
-        a = HASH[0];
-        b = HASH[1];
-        c = HASH[2];
-        d = HASH[3];
-        e = HASH[4];
-        f = HASH[5];
-        g = HASH[6];
-        h = HASH[7];
-
-        for ( var j = 0; j<64; j++) {
-            if (j < 16) W[j] = m[j + i];
-            else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
-
-            T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
-            T2 = safe_add(Sigma0256(a), Maj(a, b, c));
-
-            h = g;
-            g = f;
-            f = e;
-            e = safe_add(d, T1);
-            d = c;
-            c = b;
-            b = a;
-            a = safe_add(T1, T2);
-        }
-        
-        HASH[0] = safe_add(a, HASH[0]);
-        HASH[1] = safe_add(b, HASH[1]);
-        HASH[2] = safe_add(c, HASH[2]);
-        HASH[3] = safe_add(d, HASH[3]);
-        HASH[4] = safe_add(e, HASH[4]);
-        HASH[5] = safe_add(f, HASH[5]);
-        HASH[6] = safe_add(g, HASH[6]);
-        HASH[7] = safe_add(h, HASH[7]);
-    }
-    return HASH;
-}
-
-function core_sha512 (m, l) {
-    var K = new Array(0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65, 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b, 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec, 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817);
-    var HASH = new Array(0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179);
-    var W = new Array(80);
-    var a, b, c, d, e, f, g, h, i, j;
-    var T1, T2;
-
-}
-
-function str2binb (str) {
-  var bin = Array();
-  var mask = (1 << chrsz) - 1;
-  for(var i = 0; i < str.length * chrsz; i += chrsz)
-    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
-  return bin;
-}
-
-function binb2str (bin) {
-  var str = "";
-  var mask = (1 << chrsz) - 1;
-  for(var i = 0; i < bin.length * 32; i += chrsz)
-    str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask);
-  return str;
-}
-
-function binb2hex (binarray) {
-  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
-  var str = "";
-  for(var i = 0; i < binarray.length * 4; i++)
-  {
-    str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
-           hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
-  }
-  return str;
-}
-
-function binb2b64 (binarray) {
-  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-  var str = "";
-  for(var i = 0; i < binarray.length * 4; i += 3)
-  {
-    var triplet = (((binarray[i   >> 2] >> 8 * (3 -  i   %4)) & 0xFF) << 16)
-                | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
-                |  ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
-    for(var j = 0; j < 4; j++)
-    {
-      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
-      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
-    }
-  }
-  return str;
-}
-
-function hex_sha256(s){return binb2hex(core_sha256(str2binb(s),s.length * chrsz));}
-function b64_sha256(s){return binb2b64(core_sha256(str2binb(s),s.length * chrsz));}
-function str_sha256(s){return binb2str(core_sha256(str2binb(s),s.length * chrsz));}
diff --git a/helios/templates/cryptobase.html b/helios/templates/cryptobase.html
index f472f37d027201096d1c057994e88f06ec685bac..a33d329209ae62401084a94b863a6c163c5bf408 100644
--- a/helios/templates/cryptobase.html
+++ b/helios/templates/cryptobase.html
@@ -2,14 +2,20 @@
 
 {% block js %}
 <script language="javascript" src="{{ HELIOS_STATIC }}/jquery-jtemplates.js"></script>
-<script language="javascript" src="{{ HELIOS_STATIC }}/class.js"></script>  
 <script language="javascript" src="{{ HELIOS_STATIC }}/jquery.json.min.js"></script>
-<script language="javascript" src="{{ HELIOS_STATIC }}/bigint.js"></script>
-<script language="javascript" src="{{ HELIOS_STATIC }}/random.js"></script>
-<script language="javascript" src="{{ HELIOS_STATIC }}/elgamal.js"></script>
-<script language="javascript" src="{{ HELIOS_STATIC }}/sha1.js"></script>
-<script language="javascript" src="{{ HELIOS_STATIC }}/sha2.js"></script>
-<script language="javascript" src="{{ HELIOS_STATIC }}/helios.js"></script>
+
+<script language="javascript">
+  // required for jscrypto library
+  var JSCRYPTO_HOME = "{{HELIOS_STATIC}}/jscrypto";
+</script>
+
+<script language="javascript" src="{{ HELIOS_STATIC }}/jscrypto/class.js"></script>  
+<script language="javascript" src="{{ HELIOS_STATIC }}/jscrypto/bigint.js"></script>
+<script language="javascript" src="{{ HELIOS_STATIC }}/jscrypto/random.js"></script>
+<script language="javascript" src="{{ HELIOS_STATIC }}/jscrypto/elgamal.js"></script>
+<script language="javascript" src="{{ HELIOS_STATIC }}/jscrypto/sha1.js"></script>
+<script language="javascript" src="{{ HELIOS_STATIC }}/jscrypto/sha2.js"></script>
+<script language="javascript" src="{{ HELIOS_STATIC }}/jscrypto/helios.js"></script>
 {% endblock %}
 
 
diff --git a/helios/views.py b/helios/views.py
index da59a9ff16bd5b7c060bfb9fa375cbe5b8969011..56440fd1a9ab0aa4a5427dc86acc33e2fa66b1f9 100644
--- a/helios/views.py
+++ b/helios/views.py
@@ -391,7 +391,11 @@ def trustee_upload_pk(request, election, trustee):
     trustee.save()
     
     # send a note to admin
-    election.admin.send_message("%s - trustee pk upload" % election.name, "trustee %s (%s) uploaded a pk." % (trustee.name, trustee.email))
+    try:
+      election.admin.send_message("%s - trustee pk upload" % election.name, "trustee %s (%s) uploaded a pk." % (trustee.name, trustee.email))
+    except:
+      # oh well, no message sent
+      pass
     
   return HttpResponseRedirect(reverse(trustee_home, args=[election.uuid, trustee.uuid]))
 
@@ -834,8 +838,12 @@ def trustee_upload_decryption(request, election, trustee_uuid):
   if trustee.verify_decryption_proofs():
     trustee.save()
     
-    # send a note to admin
-    election.admin.send_message("%s - trustee partial decryption" % election.name, "trustee %s (%s) did their partial decryption." % (trustee.name, trustee.email))
+    try:
+      # send a note to admin
+      election.admin.send_message("%s - trustee partial decryption" % election.name, "trustee %s (%s) did their partial decryption." % (trustee.name, trustee.email))
+    except:
+      # ah well
+      pass
     
     return SUCCESS
   else: