gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r32476 - in gnunet-java: . src/main/java/org/gnunet/constru


From: gnunet
Subject: [GNUnet-SVN] r32476 - in gnunet-java: . src/main/java/org/gnunet/construct src/main/java/org/gnunet/construct/parsers src/main/java/org/gnunet/dht src/main/java/org/gnunet/mesh src/main/java/org/gnunet/secretsharing src/main/java/org/gnunet/secretsharing/messages src/main/java/org/gnunet/util src/main/java/org/gnunet/util/crypto src/main/java/org/gnunet/voting src/main/java/org/gnunet/voting/messages src/main/resources/org/gnunet/construct src/main/resources/org/gnunet/voting src/test src/test/python
Date: Tue, 25 Feb 2014 12:15:15 +0100

Author: dold
Date: 2014-02-25 12:15:15 +0100 (Tue, 25 Feb 2014)
New Revision: 32476

Added:
   gnunet-java/src/main/java/org/gnunet/mesh/RejectMessage.java
   gnunet-java/src/main/java/org/gnunet/util/BigIntegers.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryFailureMessage.java
   gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryMessage.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryResponseMessage.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryFailureMessage.java
   gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryMessage.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryResponseMessage.java
   gnunet-java/src/test/python/
   gnunet-java/src/test/python/test_voting.conf
   gnunet-java/src/test/python/test_voting_single.py
Modified:
   gnunet-java/ISSUES
   gnunet-java/src/main/java/org/gnunet/construct/Construct.java
   gnunet-java/src/main/java/org/gnunet/construct/parsers/NestedParser.java
   gnunet-java/src/main/java/org/gnunet/dht/ClientGetStopMessage.java
   gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java
   gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
   gnunet-java/src/main/java/org/gnunet/secretsharing/Ciphertext.java
   gnunet-java/src/main/java/org/gnunet/secretsharing/Plaintext.java
   gnunet-java/src/main/java/org/gnunet/secretsharing/ThresholdPublicKey.java
   
gnunet-java/src/main/java/org/gnunet/secretsharing/messages/DecryptDoneMessage.java
   gnunet-java/src/main/java/org/gnunet/util/MessageStreamTokenizer.java
   gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java
   gnunet-java/src/main/java/org/gnunet/voting/Ballot.java
   gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java
   gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
   
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java
   gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java
   gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt
   gnunet-java/src/main/resources/org/gnunet/voting/ballot-template.espec
Log:
- support mesh's new NACK message
- remove 
- python test skeleton for voting
- encrypted votes
- establish threshold key during ballot registration
- ballot tool can request threshold public keys from authorities



Modified: gnunet-java/ISSUES
===================================================================
--- gnunet-java/ISSUES  2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/ISSUES  2014-02-25 11:15:15 UTC (rev 32476)
@@ -1,16 +1,38 @@
+testbed:
+Could it be possible that there are some issues with operation scheduling?
+I'm currently having problems executing a ~15-peer testbed
 
-secretsharing:
- * almost all ZKPs are implemented now
- * crypto works (even for decryption :-)
+crypto in java:
+public class EddsaSignedMessage<M extends Message> implements Message {
+    @NestedMessage
+    public EddsaSignature signature;
+    @UInt32
+    public int purpose;
+    @NestedMessage
+    public M innerMessage;
+// ...
+}
 
-mesh:
-* 10 minute delay problem
+Unfortunately, this does not work, as the type of 'M' is erased,
+reflection will give 'Message' as type of the 'innerMessage' field
+=> ditch generics
 
-Consensus:
- * fixed some problems with set and consensus
-  * Timeouts are now correctly implemented. 
- * Round synchronization: How can a peer know to skip a (sub-)round without 
waiting for the timeout?
-  * when our peer is "outgoing", there's no problem -- the other peer will 
reject
-  * otherwise there's just the timeout
-  * do you have any suggestions?
+voting:
+As discussed, the key generation is done after the ballot has been registered,
+one threshold key pair per election.
 
+gnunet-ballot -k <ballotfile> # fetch threshold pubkey from authority
+(preferably the issuer should do that)
+
+ballots are now actually encrypted
+
+testing voting:
+python test case with arbitrary number of authorities and voters
+
+close to working ...
+
+misc:
+gnunet-config always writes the whole default configuration with the
+requested change. What if, however, I don't want the default part to be 
written?
+
+

Modified: gnunet-java/src/main/java/org/gnunet/construct/Construct.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/Construct.java       
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/construct/Construct.java       
2014-02-25 11:15:15 UTC (rev 32476)
@@ -85,15 +85,21 @@
      * @return instance of the desired object type
      */
     public static <T extends Message> T parseAs(ByteBuffer srcBuf, Class<T> c) 
{
-        T m = ReflectUtil.justInstantiate(c);
+        try {
+            T m = ReflectUtil.justInstantiate(c);
 
-        try {
-            getParser(c).parse(srcBuf, 0, m, m, null);
-        } catch (ProtocolViolationException e) {
-            e.augmentPath("on " + c);
+            try {
+                getParser(c).parse(srcBuf, 0, m, m, null);
+            } catch (ProtocolViolationException e) {
+                e.augmentPath("on " + c);
+            }
+
+            return m;
+        } catch (Error e) {
+            System.err.println(
+                    String.format("Exception while parsing message for class 
'%s':", c.getCanonicalName()));
+            throw e;
         }
-
-        return m;
     }
 
     /**

Modified: 
gnunet-java/src/main/java/org/gnunet/construct/parsers/NestedParser.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/construct/parsers/NestedParser.java    
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/construct/parsers/NestedParser.java    
2014-02-25 11:15:15 UTC (rev 32476)
@@ -80,7 +80,9 @@
                 return 0;
             }
         }
-
+        if (targetField.getType().isInterface()) {
+            throw new AssertionError(String.format("Target field '%s' is an 
interface, not a class.", targetField));
+        }
         ReflectUtil.justSet(dstObj, targetField, 
ReflectUtil.justInstantiate(targetField.getType()));
 
         try {

Modified: gnunet-java/src/main/java/org/gnunet/dht/ClientGetStopMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/dht/ClientGetStopMessage.java  
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/dht/ClientGetStopMessage.java  
2014-02-25 11:15:15 UTC (rev 32476)
@@ -28,12 +28,9 @@
 import org.gnunet.util.HashCode;
 
 /**
-* Created with IntelliJ IDEA.
-* User: dold
-* Date: 5/2/12
-* Time: 7:05 PM
-* To change this template use File | Settings | File Templates.
-*/
+ * Sent by the client to the service.
+ * Requests that the service stops a 'get' operation.
+ */
 @UnionCase(144)
 public class ClientGetStopMessage implements GnunetMessage.Body {
     @UInt32

Modified: gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java 2014-02-25 09:42:06 UTC 
(rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/mesh/Mesh.java 2014-02-25 11:15:15 UTC 
(rev 32476)
@@ -133,8 +133,7 @@
          * @param nobuffer Flag for disabling buffering on relay nodes.
          * @param reliable Flag for end-to-end reliability.
          */
-        public Tunnel(PeerIdentity peer, int port, boolean nobuffer, boolean 
reliable, T context)
-        {
+        public Tunnel(PeerIdentity peer, int port, boolean nobuffer, boolean 
reliable, T context) {
             this(peer, 0, port, nobuffer, reliable);
             TunnelCreateMessage tcm = new TunnelCreateMessage();
             tcm.otherEnd = peer;
@@ -144,6 +143,7 @@
             client.send(tcm);
         }
 
+
         /**
          * Private tunnel constructor, for creating tunnel objects for
          * incoming tunnels.
@@ -188,7 +188,7 @@
             if (!destroyedByService) {
                 TunnelDestroyMessage m = new TunnelDestroyMessage();
                 m.tunnelId = tunnelId;
-                m.reserved = new byte[64];
+                m.reserved = new byte[32];
                 client.send(m);
             }
             tunnelMap.remove(tunnelId);
@@ -258,7 +258,10 @@
                     logger.warn("got unexpected message from service");
                 t.receiveDoneExpected = true;
                 messageReceiver.setSender(t);
-                messageReceiver.visitAppropriate(Construct.parseAs(m.payload, 
GnunetMessage.class).body);
+                GnunetMessage gnunetMessage = Construct.parseAs(m.payload, 
GnunetMessage.class);
+                logger.debug("received message of size {} and type {}",
+                        gnunetMessage.header.messageSize, 
gnunetMessage.header.messageType);
+                messageReceiver.visitAppropriate(gnunetMessage.body);
                 messageReceiver.setSender(null);
             }
         }
@@ -286,6 +289,20 @@
             }
         }
 
+        public void visit(RejectMessage m) {
+            // FIXME: C code indicates that the nack/reject message might 
change ...
+            Tunnel t = tunnelMap.get(m.tunnelId);
+            if (null == t) {
+                logger.warn("server got confused with tunnel IDs on destroy, 
ignoring message");
+                return;
+            }
+            t.destroyedByService = true;
+            t.destroy();
+            if (null != tunnelEndHandler) {
+                tunnelEndHandler.onTunnelEnd(t);
+            }
+        }
+
         @Override
         public void handleError() {
             logger.warn("lost connection to mesh service, reconnecting");

Added: gnunet-java/src/main/java/org/gnunet/mesh/RejectMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/RejectMessage.java                
                (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/mesh/RejectMessage.java        
2014-02-25 11:15:15 UTC (rev 32476)
@@ -0,0 +1,49 @@
+/*
+ This file is part of GNUnet.
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.mesh;
+
+import org.gnunet.construct.FixedSizeIntegerArray;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+
+/**
+ * Message sent by the server to indicate that a tunnel could not
+ * be created.
+ *
+ * (GNUNET_MESSAGE_TYPE_MESH_CHANNEL_NACK)
+ *
+ * @author Florian Dold
+ */
address@hidden(276)
+public class RejectMessage implements GnunetMessage.Body {
+    @UInt32
+    public long tunnelId;
+
+    @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
+    public byte[] reserved;
+    
+    @UInt32
+    public int port;
+
+    @UInt32
+    public int opt;
+}

Modified: gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java 
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java 
2014-02-25 11:15:15 UTC (rev 32476)
@@ -17,8 +17,9 @@
     @UInt32
     public long tunnelId;
 
-    @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 64)
+    @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
     public byte[] reserved;
+
     @UInt32
     public int port;
 

Modified: gnunet-java/src/main/java/org/gnunet/secretsharing/Ciphertext.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/secretsharing/Ciphertext.java  
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/secretsharing/Ciphertext.java  
2014-02-25 11:15:15 UTC (rev 32476)
@@ -22,7 +22,11 @@
 
 import org.gnunet.construct.FixedSizeIntegerArray;
 import org.gnunet.construct.Message;
+import org.gnunet.util.BigIntegers;
+import org.gnunet.util.Strings;
 
+import java.math.BigInteger;
+
 /**
  * ElGamal ciphertext
  */
@@ -32,4 +36,50 @@
 
     @FixedSizeIntegerArray(signed = true, bitSize = 8, length = 
Parameters.elgamalBits / 8)
     public byte[] c_2;
+
+    /**
+     * Allocate the ciphertext with zeros.
+     */
+    public void allocate() {
+        c_1 = new byte[Parameters.elgamalBits / 8];
+        c_2 = new byte[Parameters.elgamalBits / 8];
+    }
+
+    @Override
+    public String toString() {
+        byte[] allBytes = new byte[c_1.length + c_2.length];
+        System.arraycopy(c_1, 0, allBytes, 0, c_1.length);
+        System.arraycopy(c_2, 0, allBytes, c_1.length, c_2.length);
+        return Strings.dataToString(allBytes);
+    }
+
+    public static Ciphertext fromString(String s) {
+        byte[] allBytes = new byte[2 * Parameters.elgamalBits / 8];
+        if (!Strings.stringToData(s, allBytes))
+            return null;
+        Ciphertext ciphertext = new Ciphertext();
+        ciphertext.allocate();
+        System.arraycopy(allBytes, 0, ciphertext.c_1, 0, 
ciphertext.c_1.length);
+        System.arraycopy(allBytes, ciphertext.c_1.length, ciphertext.c_2, 0, 
ciphertext.c_2.length);
+        return ciphertext;
+    }
+
+    /**
+     * Multiply two elgamal ciphertexts.
+     *
+     * @param v the other ciphertext
+     * @return the product of two ciphertexts
+     */
+    public Ciphertext multiply(Ciphertext v) {
+        BigInteger xc_1 = new BigInteger(1, this.c_1);
+        BigInteger xc_2 = new BigInteger(1, this.c_2);
+        BigInteger yc_1 = new BigInteger(1, v.c_1);
+        BigInteger yc_2 = new BigInteger(1, v.c_2);
+        Ciphertext ciphertext = new Ciphertext();
+        ciphertext.c_1 = 
BigIntegers.serializeUnsigned(xc_1.multiply(yc_1).mod(Parameters.elgamalP),
+                Parameters.elgamalBits);
+        ciphertext.c_2 = 
BigIntegers.serializeUnsigned(xc_2.multiply(yc_2).mod(Parameters.elgamalP),
+                Parameters.elgamalBits);
+        return ciphertext;
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/secretsharing/Plaintext.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/secretsharing/Plaintext.java   
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/secretsharing/Plaintext.java   
2014-02-25 11:15:15 UTC (rev 32476)
@@ -23,6 +23,7 @@
 import com.google.common.base.Preconditions;
 import org.gnunet.construct.FixedSizeIntegerArray;
 import org.gnunet.construct.Message;
+import org.gnunet.util.BigIntegers;
 
 import java.math.BigInteger;
 import java.security.SecureRandom;
@@ -35,32 +36,19 @@
         BigInteger val;
         val = Parameters.elgamalG.modPow(exp, Parameters.elgamalP);
         Plaintext plaintext = new Plaintext();
-        plaintext.bits = serializeBigIntUnsigned(val, Parameters.elgamalBits);
+        plaintext.bits = BigIntegers.serializeUnsigned(val, 
Parameters.elgamalBits);
         return plaintext;
     }
 
-    /**
-     * Serialize a BigInteger, but do not add an extra bit for a
-     * sign.
-     *
-     * @param bigInteger
-     * @param bits
-     * @return
-     */
-    private static byte[] serializeBigIntUnsigned(BigInteger bigInteger, int 
bits) {
-        byte[] bytes = bigInteger.toByteArray();
-        int start;
-        Preconditions.checkArgument(bigInteger.bitCount() <= bits);
-        // skip byte that was only added to fit the sign
-        if (bytes[0] == 0) {
-            start = 1;
-        } else {
-            start = 0;
+    public long bruteForceDiscreteLog(final long l) {
+        BigInteger needle = new BigInteger(1, bits);
+        for (long i = -l; i < l; i++) {
+            BigInteger val;
+            val = Parameters.elgamalG.modPow(BigInteger.valueOf(l), 
Parameters.elgamalP);
+            if (val.equals(needle))
+                return i;
         }
-        byte[] fixedBytes = new byte[(bits + 7) / 8];
-        System.arraycopy(bytes, start, fixedBytes,
-                fixedBytes.length - bytes.length + start, bytes.length - 
start);
-        return fixedBytes;
+        throw new ArithmeticException("discrete log has no solution in given 
range");
     }
 
     public Ciphertext encrypt(ThresholdPublicKey publicKey) {
@@ -82,8 +70,8 @@
         c_2 = m.multiply(h.modPow(y, 
Parameters.elgamalP)).mod(Parameters.elgamalP);
 
         Ciphertext ciphertext = new Ciphertext();
-        ciphertext.c_1 = serializeBigIntUnsigned(c_1, Parameters.elgamalBits);
-        ciphertext.c_2 = serializeBigIntUnsigned(c_2, Parameters.elgamalBits);
+        ciphertext.c_1 = BigIntegers.serializeUnsigned(c_1, 
Parameters.elgamalBits);
+        ciphertext.c_2 = BigIntegers.serializeUnsigned(c_2, 
Parameters.elgamalBits);
 
         return ciphertext;
     }

Modified: 
gnunet-java/src/main/java/org/gnunet/secretsharing/ThresholdPublicKey.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/secretsharing/ThresholdPublicKey.java  
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/secretsharing/ThresholdPublicKey.java  
2014-02-25 11:15:15 UTC (rev 32476)
@@ -22,11 +22,40 @@
 
 import org.gnunet.construct.FixedSizeIntegerArray;
 import org.gnunet.construct.Message;
+import org.gnunet.util.Strings;
 
+import java.util.Arrays;
+
 /**
  * Threshold public key.
  */
 public class ThresholdPublicKey implements Message {
+
     @FixedSizeIntegerArray(signed = true, bitSize = 8, length = 
Parameters.elgamalBits / 8)
     public byte[] bits;
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ThresholdPublicKey that = (ThresholdPublicKey) o;
+        return Arrays.equals(bits, that.bits);
+    }
+
+    @Override
+    public int hashCode() {
+        return bits != null ? Arrays.hashCode(bits) : 0;
+    }
+
+    public static ThresholdPublicKey fromString(String value) {
+        ThresholdPublicKey pk = new ThresholdPublicKey();
+        pk.bits = new byte[Parameters.elgamalBits / 8];
+        Strings.stringToData(value, pk.bits);
+        return pk;
+    }
+
+    @Override
+    public String toString() {
+        return Strings.dataToString(bits);
+    }
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/secretsharing/messages/DecryptDoneMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/secretsharing/messages/DecryptDoneMessage.java
 2014-02-25 09:42:06 UTC (rev 32475)
+++ 
gnunet-java/src/main/java/org/gnunet/secretsharing/messages/DecryptDoneMessage.java
 2014-02-25 11:15:15 UTC (rev 32476)
@@ -1,5 +1,4 @@
 /*
-
   This file is part of GNUnet.
    (C) 2014 Christian Grothoff (and other contributing authors)
 
@@ -17,7 +16,6 @@
    along with GNUnet; see the file COPYING.  If not, write to the
    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.
-
  */
 
 package org.gnunet.secretsharing.messages;
@@ -29,7 +27,8 @@
 import org.gnunet.util.GnunetMessage;
 
 /**
- * Created by dold on 2/2/14.
+ * Sent by the service to the client.
+ * Contains the plaintext for a successfully decrypted ciphertext.
  */
 @UnionCase(782)
 public class DecryptDoneMessage implements GnunetMessage.Body{

Added: gnunet-java/src/main/java/org/gnunet/util/BigIntegers.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/BigIntegers.java                  
        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/util/BigIntegers.java  2014-02-25 
11:15:15 UTC (rev 32476)
@@ -0,0 +1,56 @@
+/*
+ This file is part of GNUnet.
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.util;
+
+import com.google.common.base.Preconditions;
+
+import java.math.BigInteger;
+
+/**
+ * Helper class for BigIntegers.
+ */
+public class BigIntegers {
+
+    /**
+     * Serialize a BigInteger, but do not add an extra bit for a
+     * sign.
+     *
+     * @param bigInteger big integer to serialize
+     * @param bits how many bits should the binary representation have?
+     *             rounded up to the next multiple of 8.
+     * @return big endian representation of the given BigInteger, without a 
sign bit
+     */
+    public static byte[] serializeUnsigned(BigInteger bigInteger, int bits) {
+        byte[] bytes = bigInteger.toByteArray();
+        int start;
+        Preconditions.checkArgument(bigInteger.bitCount() <= bits);
+        // skip byte that was only added to fit the sign
+        if (bytes[0] == 0) {
+            start = 1;
+        } else {
+            start = 0;
+        }
+        byte[] fixedBytes = new byte[(bits + 7) / 8];
+        System.arraycopy(bytes, start, fixedBytes,
+                fixedBytes.length - bytes.length + start, bytes.length - 
start);
+        return fixedBytes;
+    }
+}

Modified: gnunet-java/src/main/java/org/gnunet/util/MessageStreamTokenizer.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/MessageStreamTokenizer.java       
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/util/MessageStreamTokenizer.java       
2014-02-25 11:15:15 UTC (rev 32476)
@@ -70,8 +70,9 @@
             }
             int parsedSize = buffer.position() - oldPos;
             if (parsedSize != msg.header.messageSize) {
-                throw new AssertionError("mismatch between parsed message and 
header for " +
-                  msg.body.getClass());
+                throw new AssertionError(
+                        String.format("mismatch between parsed message and 
header for %s: parsed size %s, header size %s",
+                                msg.body.getClass(), parsedSize, 
msg.header.messageSize));
             }
             mstCalllback.onKnownMessage(msg);
         } else {

Modified: gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java        
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java        
2014-02-25 11:15:15 UTC (rev 32476)
@@ -65,6 +65,7 @@
      */
     public static EcdsaPublicKey fromString(String s) {
         EcdsaPublicKey publicKey = new EcdsaPublicKey();
+        publicKey.y = new byte[32];
         if (Strings.stringToData(s, publicKey.y))
             return publicKey;
         return null;

Modified: gnunet-java/src/main/java/org/gnunet/voting/Ballot.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/Ballot.java     2014-02-25 
09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/voting/Ballot.java     2014-02-25 
11:15:15 UTC (rev 32476)
@@ -25,12 +25,12 @@
 import com.google.common.base.Preconditions;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Maps;
 import com.google.common.primitives.Longs;
+import org.gnunet.secretsharing.ThresholdPublicKey;
 import org.gnunet.util.*;
-import org.gnunet.util.crypto.EcdsaPrivateKey;
-import org.gnunet.util.crypto.EcdsaPublicKey;
-import org.gnunet.util.crypto.EcdsaSignature;
-import org.gnunet.util.crypto.EddsaSignature;
+import org.gnunet.util.crypto.*;
+import org.gnunet.voting.messages.KeyQueryResponseMessage;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -51,6 +51,14 @@
      */
     String group;
     /**
+     * When will the distributed key generation among the authorities start?
+     */
+    AbsoluteTime keygenStartTime;
+    /**
+     * When will the distributed key generation among the authorities be 
finished?
+     */
+    AbsoluteTime keygenEndTime;
+    /**
      * List of choices for the election.
      */
     List<String> choices;
@@ -74,21 +82,53 @@
      * When are the election results discarded by the authority?
      */
     AbsoluteTime endTime;
+    /**
+     * Mapping from authority alias to peer identity.
+     */
     BiMap<String,PeerIdentity> authorities;
+    /**
+     * Signatures from the authority certifying that it
+     * will be available for this election.
+     */
     SortedMap<String,EddsaSignature> registrationSigs;
+    /**
+     * Public key of the certificate authority for group membership.
+     */
     EcdsaPublicKey caPub;
+    /**
+     * Public key of the issues.
+     */
     EcdsaPublicKey issuerPub;
+    /**
+     * Signature of the issuer on the basic data of the ballot.
+     */
     EcdsaSignature issuerSig;
+    /**
+     * Public key of the voter that fills out this ballot.
+     */
     EcdsaPublicKey voterPub;
+    /**
+     * Confirmation for the fact that a vote was accepted by an authority.
+     */
     SortedMap<String,EddsaSignature> confirmationSigs;
+    /**
+     * Certificate that a voter belongs to a certain group.
+     */
     GroupCert groupCert;
+    /**
+     * Threshold public keys per authority alias.
+     * Ideally this should be the same for each authority, but with malicious 
authorities
+     * we can't trust that, so we must collect all the keys.
+     */
+    SortedMap<String,KeyQueryResponseMessage> thresholdPublicKeys;
 
     /**
-     * Choice in plaintext.
-     * This field would be encrypted in a secure version.
+     * Threshold for the election.
      */
-    int choiceId = -1;
+    public int threshold;
 
+    EncryptedVote encryptedVote;
+
     /**
      * Load a ballot from file.
      *
@@ -163,6 +203,14 @@
             throw new InvalidBallotException("ballot must have elegibility 
group");
         }
         group = optGroup.get();
+        Optional<Long> optThreshold = cfg.getValueNumber("election", 
"THRESHOLD");
+        if (!optThreshold.isPresent()) {
+            throw new InvalidBallotException("ballot must have threshold");
+        }
+        if (optThreshold.get() <= 0) {
+            throw new InvalidBallotException("threshold must be positive");
+        }
+        threshold = optThreshold.get().intValue();
         authorities = HashBiMap.create();
         for (Map.Entry<String,String> e : 
cfg.getSection("authorities").entrySet()) {
             String alias = e.getKey();
@@ -215,16 +263,7 @@
             }
             confirmationSigs.put(e.getKey(), sig);
         }
-        Optional<String> optChoiceId = cfg.getValueString("vote", "CHOICE_ID");
-        if (optChoiceId.isPresent()) {
-            choiceId = Integer.parseInt(optChoiceId.get());
-            if (choiceId < 0 || choiceId >= choices.size()) {
-                throw new InvalidBallotException("invalid choice in vote");
-            }
-        } else {
-            choiceId = -1;
-        }
-
+        encryptedVote = EncryptedVote.parseFromConfiguration(cfg);
         Optional<String> optVoterPub = cfg.getValueString("vote", "VOTER_PUB");
         if (optVoterPub.isPresent()) {
             voterPub = EcdsaPublicKey.fromString(optVoterPub.get());
@@ -238,10 +277,33 @@
         concludeTime = getTime(cfg, "CONCLUDE");
         queryTime = getTime(cfg, "QUERY");
         endTime = getTime(cfg, "END");
+        keygenStartTime = getTime(cfg, "KEYGEN_START");
+        keygenEndTime = getTime(cfg, "KEYGEN_END");
 
         if (cfg.haveValue("vote", "GROUP_SIG")) {
             groupCert = GroupCert.fromBallotConfig(this, cfg);
         }
+
+        thresholdPublicKeys = new TreeMap<String, KeyQueryResponseMessage>();
+        for (Map.Entry<String,String> e : 
cfg.getSection("threshold-pubkeys").entrySet()) {
+            String alias = e.getKey();
+            if (!authorities.containsKey(alias)) {
+                throw new InvalidBallotException(String.format(
+                        "Alias '%s' has threshold pubkey, but alias does not 
belong to any authority.", alias));
+            }
+            Optional<String> optSig = 
cfg.getValueString("threshold-pubkey-sigs", alias);
+            if (!optSig.isPresent()) {
+                throw new InvalidBallotException(String.format(
+                        "no signature present for threshold pubkey from 
authority '%s'", alias));
+            }
+
+            KeyQueryResponseMessage m = new KeyQueryResponseMessage();
+            m.signature = EddsaSignature.fromString(optSig.get());
+            m.signedGuidKey = new KeyQueryResponseMessage.BallotPublicKey();
+            m.signedGuidKey.ballotGuid = getBallotGuid();
+            m.signedGuidKey.publicKey = 
ThresholdPublicKey.fromString(e.getValue());
+        }
+
     }
 
     /**
@@ -270,6 +332,8 @@
         }
         digest.update(issuerPub.y);
         digest.update(caPub.y);
+        digest.update(Longs.toByteArray(keygenStartTime.getSeconds()));
+        digest.update(Longs.toByteArray(keygenEndTime.getSeconds()));
         digest.update(Longs.toByteArray(startTime.getSeconds()));
         digest.update(Longs.toByteArray(endTime.getSeconds()));
         digest.update(Longs.toByteArray(closingTime.getSeconds()));
@@ -278,14 +342,46 @@
     }
 
     /**
+     * Get the threshold public key that the majority of authorities
+     * advertise.  The majority key must have at least 'threshold' peers that
+     * advertise it in order to be valid
+     *
+     * @return the majority threshold public key
+     */
+    public ThresholdPublicKey getMajorityThresholdPublicKey() {
+        HashMap<ThresholdPublicKey,Integer> counts = Maps.newHashMap();
+        for (Map.Entry<String,KeyQueryResponseMessage> e : 
thresholdPublicKeys.entrySet()) {
+            ThresholdPublicKey pk = e.getValue().signedGuidKey.publicKey;
+            if (counts.containsKey(pk)) {
+                counts.put(pk, counts.get(pk) + 1);
+            } else {
+                counts.put(pk, 0);
+            }
+        }
+        int maxCount = 0;
+        ThresholdPublicKey bestKey = null;
+        for (Map.Entry<ThresholdPublicKey, Integer> e : counts.entrySet()) {
+            if (e.getValue() > maxCount) {
+                maxCount = e.getValue();
+                bestKey = e.getKey();
+            }
+        }
+        if (maxCount < threshold) {
+            return null;
+        }
+        return bestKey;
+    }
+
+    /**
      * Encode the given choice permanently in the ballot.
      * Also encodes the voter's public key.
      *
      * @param choice the choice to encode the ballot
-     * @param privateKey the private key to use for encoding
+     * @param voterPrivateKey the private key to use for encoding
      */
-    public void encodeChoice(String choice, EcdsaPrivateKey privateKey) {
-        choiceId = -1;
+    public void encodeChoice(String choice, ThresholdPublicKey 
thresholdPublicKey,
+                             EcdsaPrivateKey voterPrivateKey) {
+        int choiceId = -1;
         int i = 0;
         for (String possibleChoice : choices) {
             if (choice.equals(possibleChoice)) {
@@ -293,10 +389,12 @@
             }
             i++;
         }
-        voterPub = privateKey.getPublicKey();
-        if (choiceId == -1) {
+        voterPub = voterPrivateKey.getPublicKey();
+        if (choiceId < 0 || choiceId > 1) {
             throw new InvalidBallotException(String.format("choice '%s' not 
valid", choice));
         }
+
+        encryptedVote = EncryptedVote.fromChoice(choiceId, thresholdPublicKey, 
voterPrivateKey);
     }
 
     /**
@@ -310,6 +408,9 @@
         cfg.setValueString("election", "GROUP", group);
         cfg.setValueString("election", "CHOICES", 
Joiner.on("//").join(choices));
         cfg.setValueString("election", "CA_PUB", caPub.toString());
+        cfg.setValueNumber("election", "THRESHOLD", threshold);
+        cfg.setValueNumber("election", "TIMESTAMP_KEYGEN_START", 
keygenStartTime.getSeconds());
+        cfg.setValueNumber("election", "TIMESTAMP_KEYGEN_END", 
keygenEndTime.getSeconds());
         cfg.setValueNumber("election", "TIMESTAMP_START", 
startTime.getSeconds());
         cfg.setValueNumber("election", "TIMESTAMP_CLOSING", 
closingTime.getSeconds());
         cfg.setValueNumber("election", "TIMESTAMP_QUERY", 
queryTime.getSeconds());
@@ -332,8 +433,8 @@
             cfg.setValueString("election", "ISSUER_PUB", issuerPub.toString());
             cfg.setValueString("election", "ISSUER_SIG", issuerSig.toString());
         }
-        if (-1 != choiceId) {
-            cfg.setValueNumber("vote", "CHOICE_ID", choiceId);
+        if (null != encryptedVote) {
+            encryptedVote.writeToConfiguration(cfg);
         }
         if (null != voterPub) {
             cfg.setValueString("vote", "VOTER_PUB", voterPub.toString());
@@ -341,6 +442,14 @@
         if (null != groupCert) {
             groupCert.writeBallotConfig(cfg);
         }
+        System.out.println("thresh set when writing: " + 
thresholdPublicKeys.size());
+        for (Map.Entry<String,KeyQueryResponseMessage> e : 
thresholdPublicKeys.entrySet()) {
+            System.out.println("writing tresh");
+            cfg.setValueString("threshold-pubkeys", e.getKey(),
+                    e.getValue().signedGuidKey.publicKey.toString());
+            cfg.setValueString("threshold-pubkey-sigs", e.getKey(),
+                    e.getValue().signature.toString());
+        }
         return cfg;
     }
 
@@ -371,6 +480,10 @@
         buf.append(group);
         buf.append("\n");
 
+        buf.append("Threshold: ");
+        buf.append(threshold);
+        buf.append("\n");
+
         buf.append("Start Time: ");
         buf.append(startTime.toFancyString());
         buf.append("\n");
@@ -389,7 +502,7 @@
 
         buf.append("Choices:\n");
         for (int i = 0; i < choices.size(); i++) {
-            buf.append(""+(i+1));
+            buf.append(i + 1);
             buf.append(". '");
             buf.append(choices.get(i));
             buf.append("'\n");
@@ -430,7 +543,7 @@
         else {
             buf.append("ballot not submitted\n");
         }
-        if (choiceId != -1) {
+        if (encryptedVote != null) {
             buf.append("choice selected\n");
         } else {
             buf.append("no choice selected\n");
@@ -538,4 +651,22 @@
         voterPub = groupCert.getMemberPublicKey();
         this.groupCert = groupCert;
     }
+
+    /**
+     * Get the list of authorities that this ballot does not have a signature
+     * on the threshold key from.
+     */
+    public List<PeerIdentity> getRemainingKeyAuthorities() {
+        LinkedList<PeerIdentity> remaining = new LinkedList<PeerIdentity>();
+        for (Map.Entry<String,PeerIdentity> x : authorities.entrySet()) {
+            if (!thresholdPublicKeys.containsKey(x.getKey()))
+                remaining.add(x.getValue());
+        }
+        return remaining;
+    }
+
+    public void addThresholdPublicKey(PeerIdentity currentAuthority, 
KeyQueryResponseMessage m) {
+        String alias = authorities.inverse().get(currentAuthority);
+        thresholdPublicKeys.put(alias, m);
+    }
 }

Modified: gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java 2014-02-25 
09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/voting/BallotTool.java 2014-02-25 
11:15:15 UTC (rev 32476)
@@ -30,14 +30,14 @@
 import org.gnunet.mesh.Mesh;
 import org.gnunet.mesh.MeshRunabout;
 import org.gnunet.mesh.TunnelEndHandler;
+import org.gnunet.secretsharing.ThresholdPublicKey;
 import org.gnunet.testbed.CompressedConfig;
-import org.gnunet.util.AbsoluteTime;
-import org.gnunet.util.Configuration;
-import org.gnunet.util.PeerIdentity;
-import org.gnunet.util.Program;
+import org.gnunet.util.*;
 import org.gnunet.util.getopt.Argument;
 import org.gnunet.util.getopt.ArgumentAction;
 import org.gnunet.voting.messages.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.io.File;
 import java.io.FileOutputStream;
@@ -50,6 +50,9 @@
  * Tool for creating, manipulating and submitting ballot files.
  */
 public class BallotTool extends Program {
+    private static final Logger logger = LoggerFactory
+            .getLogger(BallotTool.class);
+
     @Argument(
             shortname = "e",
             longname = "ego",
@@ -107,6 +110,12 @@
             description = "incorporate the group cert into the ballot")
     String groupCertFile = null;
 
+    @Argument(
+            shortname = "k",
+            longname = "request-key",
+            action = ArgumentAction.SET,
+            description = "request the threshold public key from authorities")
+    boolean requestKey = false;
 
     @Argument(
             shortname = "t",
@@ -115,14 +124,35 @@
             description = "write a template ballot to the give ballot file")
     boolean template = false;
 
+    /**
+     * The ego to use for the currently executing action.
+     */
     private Identity.Ego ego;
 
+    /**
+     * The (possibly modified) ballot originally loaded
+     * from 'ballotFilename'
+     */
     private Ballot ballot;
+
+    /**
+     * Filename to read the ballot from, and write modifications to.
+     */
     private String ballotFilename;
 
+    /**
+     * Our handle to MESH.
+     */
     private Mesh mesh;
+
+    /**
+     * A tunnel to 'currentAuthority' or null.
+     */
     private Mesh.Tunnel tunnel;
 
+    /**
+     * The authority we are currently communicating with.
+     */
     private PeerIdentity currentAuthority;
 
     /**
@@ -131,32 +161,59 @@
      */
     private boolean tunnelCommunicationFinished;
 
+    private RelativeTime tunnelReconnectBackoff = RelativeTime.STD_BACKOFF;
+
     public class BallotTunnelEndHandler implements TunnelEndHandler {
         @Override
-        public void onTunnelEnd(Mesh.Tunnel tunnel) {
-            // FIXME
+        public void onTunnelEnd(final Mesh.Tunnel tunnel) {
+            // FIXME: just re-running 'doCommands' is a bit of a hack
+            BallotTool.this.tunnel = null;
+            if (!tunnelCommunicationFinished) {
+                logger.warn("mesh tunnel disconnected, but operation not 
finished");
+                Scheduler.addDelayed(tunnelReconnectBackoff, new 
Scheduler.Task() {
+                    @Override
+                    public void run(Scheduler.RunContext ctx) {
+                        doCommands();
+                    }
+                });
+                tunnelReconnectBackoff = tunnelReconnectBackoff.backoff();
+            }
         }
     }
 
+    /**
+     * Destroy the tunnel to the authority as well
+     * as the mesh handle.
+     */
+    private void endMesh() {
+        tunnelCommunicationFinished = true;
+        if (null != tunnel) {
+            tunnel.destroy();
+            tunnel = null;
+        }
+        if (null != mesh) {
+            mesh.destroy();
+            mesh = null;
+        }
+    }
+
     public class BallotRegisterReceiver extends MeshRunabout {
         public void visit(BallotRegisterSuccessMessage m) {
             System.out.println("ballot successfully registered");
             ballot.addRegistrationSignature(currentAuthority, 
m.registrationSignature);
             writeBallot();
-            tunnel.destroy();
-            mesh.destroy();
+            endMesh();
         }
 
         public void visit(BallotRegisterFailureMessage m) {
             System.out.println("registering failed: " + m.reason);
-            tunnel.destroy();
-            mesh.destroy();
+            endMesh();
+            setReturnValue(1);
         }
-
     }
 
     public class QueryReceiver extends MeshRunabout {
-        public void visit(QueryResponseMessage m) {
+        public void visit(ResultQueryResponseMessage m) {
             if (m.results.length != ballot.choices.size()) {
                 System.out.println("failure to query result: malformed 
response");
             } else {
@@ -165,16 +222,30 @@
                     System.out.println("'" + ballot.choices.get(i) + "': " + 
m.results[i]);
                 }
             }
+            endMesh();
+        }
 
-            tunnel.destroy();
-            mesh.destroy();
+        public void visit(ResultQueryFailureMessage m) {
+            System.out.println("failure to query result: " + m.reason);
+            endMesh();
+            setReturnValue(1);
         }
+    }
 
-        public void visit(QueryFailureMessage m) {
+
+    public class PublicKeyReceiver extends MeshRunabout {
+        public void visit(KeyQueryResponseMessage m) {
+            System.out.println("got threshold public key!");
+            ballot.addThresholdPublicKey(currentAuthority, m);
+            writeBallot();
+            endMesh();
+        }
+        public void visit(KeyQueryFailureMessage m) {
             System.out.println("failure to query result: " + m.reason);
-            tunnel.destroy();
-            mesh.destroy();
+            endMesh();
+            setReturnValue(1);
         }
+
     }
 
     public class SubmitReceiver extends MeshRunabout {
@@ -183,19 +254,18 @@
             System.out.println("vote successfully submitted");
             ballot.addConfirmation(currentAuthority, m.confirmationSig);
             writeBallot();
-            tunnel.destroy();
-            mesh.destroy();
+            endMesh();
         }
 
         public void visit(SubmitFailureMessage m) {
             System.out.println("vote not submitted: " + m.reason);
-            if (m.authorityTime != null) {
+            if (m.signedAuthorityTime != null) {
                 // FIXME: verify
                 System.out.println("authority time: " +
-                        
AbsoluteTime.fromNetwork(m.authorityTime.innerMessage).toFancyString());
+                        
AbsoluteTime.fromNetwork(m.signedAuthorityTime.time).toFancyString());
             }
-            tunnel.destroy();
-            mesh.destroy();
+            endMesh();
+            setReturnValue(1);
         }
     }
 
@@ -220,6 +290,9 @@
         }
     }
 
+    /**
+     * Write the ballot back to disk.
+     */
     private void writeBallot() {
         try {
             Files.write(ballot.serialize(), new File(ballotFilename), 
Charsets.UTF_8);
@@ -228,6 +301,9 @@
         }
     }
 
+    /**
+     * Actually execute the action the user requested.
+     */
     void doCommands() {
         if (null != groupCertFile) {
             Configuration groupCertConfig = new Configuration();
@@ -270,7 +346,13 @@
                 setReturnValue(1);
                 return;
             }
-            ballot.encodeChoice(select, ego.getPrivateKey());
+            ThresholdPublicKey thresholdPublicKey = 
ballot.getMajorityThresholdPublicKey();
+            if (null == thresholdPublicKey) {
+                System.err.println("no threshold public key in ballot");
+                setReturnValue(1);
+                return;
+            }
+            ballot.encodeChoice(select, thresholdPublicKey, 
ego.getPrivateKey());
             writeBallot();
             return;
         }
@@ -297,7 +379,10 @@
             m.groupCertExpiration = 
ballot.groupCert.getExpiration().asMessage();
             m.groupCert = ballot.groupCert.getSignature();
             m.ballotGuid = ballot.getBallotGuid();
-            m.choiceId = ballot.choiceId;
+            if (null == ballot.encryptedVote) {
+                throw new InvalidBallotException("no encrypted vote in 
ballot");
+            }
+            m.encryptedVote = ballot.encryptedVote;
             tunnel.send(m);
             return;
         }
@@ -313,15 +398,33 @@
                 return;
             }
             Random r = new Random();
-            PeerIdentity authority = 
remainingAuthorities.get(r.nextInt(remainingAuthorities.size()));
-            System.out.println("querying authority" + authority.toString());
+            currentAuthority = 
remainingAuthorities.get(r.nextInt(remainingAuthorities.size()));
+            System.out.println("querying authority " + 
currentAuthority.toString());
             mesh = new Mesh(cfg, new BallotTunnelEndHandler(), new 
QueryReceiver());
-            tunnel = mesh.createTunnel(authority, 
TallyAuthorityDaemon.MESH_PORT, true, true, null);
-            QueryMessage m = new QueryMessage();
-            m.ballotGUID = ballot.getBallotGuid();
+            tunnel = mesh.createTunnel(currentAuthority, 
TallyAuthorityDaemon.MESH_PORT, true, true, null);
+            ResultQueryMessage m = new ResultQueryMessage();
+            m.ballotGuid = ballot.getBallotGuid();
             tunnel.send(m);
             return;
         }
+        if (requestKey) {
+            List<PeerIdentity> remainingAuthorities = 
ballot.getRemainingKeyAuthorities();
+            if (remainingAuthorities.isEmpty()) {
+                System.err.println("all authorities already signed group key");
+                return;
+            }
+            Random r = new Random();
+            currentAuthority = 
remainingAuthorities.get(r.nextInt(remainingAuthorities.size()));
+            System.out.println("asking authority for key " + 
currentAuthority.toString());
+            mesh = new Mesh(cfg, new BallotTunnelEndHandler(), new 
PublicKeyReceiver());
+            tunnel = mesh.createTunnel(currentAuthority, 
TallyAuthorityDaemon.MESH_PORT, true, true, null);
+            KeyQueryMessage m = new KeyQueryMessage();
+            m.ballotGuid = ballot.getBallotGuid();
+            tunnel.send(m);
+            return;
+        }
+        setReturnValue(1);
+        System.err.println("no action specified");
     }
 
     @Override
@@ -342,7 +445,15 @@
             System.err.println("ballot file does not exist");
             return;
         }
-        ballot = new Ballot(ballotFilename);
+        try {
+            ballot = new Ballot(ballotFilename);
+        } catch (InvalidBallotException e) {
+            System.err.println("Invalid or incomplete ballot:");
+            System.err.println(e.getMessage());
+            setReturnValue(1);
+            return;
+        }
+        // if there's an ego name, look it up ...
         if (null != egoName) {
             Identity.lookup(getConfiguration(), egoName, new 
IdentityCallback() {
                 @Override

Modified: gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java       
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java       
2014-02-25 11:15:15 UTC (rev 32476)
@@ -30,6 +30,9 @@
 import org.gnunet.construct.UInt32;
 import org.gnunet.mesh.Mesh;
 import org.gnunet.mesh.MeshRunabout;
+import org.gnunet.secretsharing.*;
+import org.gnunet.secretsharing.callbacks.DecryptCallback;
+import org.gnunet.secretsharing.callbacks.SecretReadyCallback;
 import org.gnunet.testbed.CompressedConfig;
 import org.gnunet.util.*;
 import org.gnunet.util.crypto.*;
@@ -37,28 +40,37 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.math.BigInteger;
 import java.util.*;
 
 
 /**
- * Daemon that is responsible for counting votes.
+ * Daemon that is responsible for accepting and counting votes.
  */
 public class TallyAuthorityDaemon extends Program {
     private static final Logger logger = LoggerFactory
             .getLogger(TallyAuthorityDaemon.class);
 
+    /**
+     * Mesh port used to connect to to the tally authority daemon.
+     */
     public static final int MESH_PORT = 1002;
+
+    /**
+     * Mesh handle.
+     */
     private Mesh mesh;
+
+    /**
+     * Private key of the local peer.
+     */
     private EddsaPrivateKey authorityPrivateKey;
+
+    /**
+     * Public key of the local peer.
+     */
     private EddsaPublicKey authorityPublicKey;
 
-    public static class Vote implements Message {
-        @UInt32
-        public int choice;
-        @NestedMessage
-        public EcdsaPublicKey voterPub;
-    }
-
     /**
      * All elections known to this authority
      */
@@ -74,24 +86,53 @@
         Ballot ballot;
 
         /**
-         * Set of voters that have submitted their ballot.
+         * The threshold crypto share, null if the key has not yet been
+         * established.
          */
-        Set<EcdsaPublicKey> voters = new HashSet<EcdsaPublicKey>();
+        Share share;
 
         /**
+         * A voter is in this set if its vote has been in the consensus.
+         */
+        Set<EcdsaPublicKey> countedVoters = new HashSet<EcdsaPublicKey>();
+
+        /**
+         * Key generation session.
+         */
+        KeyGeneration keyGeneration;
+
+        /**
          * Consensus with the other authorities on the set of ballots.
          */
         Consensus consensus;
 
+        /**
+         * Are we done with the vote consensus?
+         */
         boolean consensusDone;
 
         /**
+         * Product of all encrypted votes (mod q), used to compute the final 
tally.
+         */
+        Ciphertext voteProduct;
+
+        /**
          * Maping from choice to number of votes for that choice.
+         * In our currently simplified implementation, tally.length is always 
2.
+         * If the tally has not been counted yet, 'tally' is null.
          */
-        int[] tally;
+        long[] tally;
+
+        /**
+         * The decrypt session.
+         */
+        Decryption decryption;
     }
 
-    static class ElectionConsensusConclude implements ConsensusCallback {
+    /**
+     * Callbacks for the vote consensus.
+     */
+    class ElectionConsensusConclude implements ConsensusCallback {
         private final ElectionState electionState;
 
         public ElectionConsensusConclude(ElectionState electionState) {
@@ -100,22 +141,39 @@
         @Override
         public void onElement(ConsensusElement element) {
             System.out.println("got element from consensus");
-            Vote vote = Construct.parseAs(element.data, Vote.class);
-            if (vote.choice >= 0 && vote.choice < electionState.tally.length) {
-                electionState.tally[vote.choice] += 1;
-            }
+            EncryptedVote vote = Construct.parseAs(element.data, 
EncryptedVote.class);
+            electionState.voteProduct = 
electionState.voteProduct.multiply(vote.v);
         }
 
         @Override
         public void onDone() {
-            System.out.println("got element from consensus");
+            System.out.println("consensus concluded");
             electionState.consensusDone = true;
             electionState.consensus.destroy();
             electionState.consensus = null;
+
+            electionState.decryption = new Decryption(
+                    getConfiguration(),
+                    electionState.share,
+                    electionState.voteProduct,
+                    electionState.ballot.concludeTime,
+                    electionState.ballot.queryTime,
+                    new DecryptCallback() {
+                        @Override
+                        public void onResult(Plaintext plaintext) {
+                            logger.info("got decypt result");
+                            long l = electionState.countedVoters.size();
+                            long t = plaintext.bruteForceDiscreteLog(l);
+                            logger.info("brute-forced result");
+                            electionState.tally = new long[2];
+                            electionState.tally[0] = (l - t) / 2;
+                            electionState.tally[1] = l - 
electionState.tally[0];
+                        }
+                    });
         }
     }
 
-    static class ConsensusConcludeTask implements Scheduler.Task {
+    class ConsensusConcludeTask implements Scheduler.Task {
         /**
          * Which election on this authority is the consensus conclude for?
          */
@@ -130,11 +188,30 @@
         }
     }
 
-    private EddsaSignedMessage<AbsoluteTimeMessage> getTimeSigMessage() {
-        AbsoluteTimeMessage m = AbsoluteTime.now().asMessage();
-        return EddsaSignedMessage.signMessage(m, 0, authorityPrivateKey, 
authorityPublicKey);
+    static class SecretReady implements SecretReadyCallback {
+        private final ElectionState electionState;
+
+        public SecretReady(ElectionState electionState) {
+            this.electionState = electionState;
+        }
+
+        @Override
+        public void onSecretReady(Share share) {
+            electionState.keyGeneration = null;
+            electionState.share = share;
+        }
     }
 
+    private SubmitFailureMessage.SignedAuthorityTime getTimeSigMessage() {
+        SubmitFailureMessage.SignedAuthorityTime tm = new 
SubmitFailureMessage.SignedAuthorityTime();
+        // FIXME!
+        tm.purpose = 0;
+        tm.time = AbsoluteTime.now().asMessage();
+        tm.signature = authorityPrivateKey.sign(authorityPublicKey, tm.purpose,
+                Construct.toBinary(tm.time));
+        return tm;
+    }
+
     private class TallyMeshReceiver extends MeshRunabout {
         public void visit(SubmitMessage m) {
             logger.debug("got submit message");
@@ -143,32 +220,23 @@
                 SubmitFailureMessage fm = new SubmitFailureMessage();
                 fm.reason = "no matching ballot found";
                 getSender().send(fm);
-            } else if (m.choiceId < 0 || m.choiceId >= 
electionState.tally.length) {
-                SubmitFailureMessage fm = new SubmitFailureMessage();
-                fm.reason = "invalid vote";
-                getSender().send(fm);
-            } else if (electionState.voters.contains(m.voterPub)) {
-                SubmitFailureMessage fm = new SubmitFailureMessage();
-                fm.reason = "duplicate vote detected";
-                getSender().send(fm);
             } else if (!electionState.ballot.startTime.isDue()) {
                 SubmitFailureMessage fm = new SubmitFailureMessage();
                 fm.reason = "too early to submit vote";
-                fm.authorityTime = getTimeSigMessage();
+                fm.signedAuthorityTime = getTimeSigMessage();
                 getSender().send(fm);
             } else if (electionState.ballot.closingTime.isDue()) {
                 SubmitFailureMessage fm = new SubmitFailureMessage();
                 fm.reason = "too late to submit vote";
-                fm.authorityTime = getTimeSigMessage();
+                fm.signedAuthorityTime = getTimeSigMessage();
                 getSender().send(fm);
             }
             // FIXME: check signatures of voter and CA
             else {
-                electionState.voters.add(m.voterPub);
-                Vote vote = new Vote();
-                vote.choice = m.choiceId;
-                vote.voterPub = m.voterPub;
-                byte[] elem = Construct.toBinary(vote);
+                // we do *not* check for duplicate votes here,
+                // as consensus takes care of this, and there is no harm in 
sending
+                // exact duplicates
+                byte[] elem = Construct.toBinary(m.encryptedVote);
                 electionState.consensus.insertElement(new 
ConsensusElement(elem, 0));
                 SubmitSuccessMessage sm = new SubmitSuccessMessage();
                 sm.confirmationSig = EddsaSignature.randomGarbage();
@@ -200,11 +268,11 @@
                 return;
             }
             ElectionState electionState = new ElectionState();
-            electionState.tally = new int[b.choices.size()];
             electionState.ballot = b;
             PeerIdentity[] ids = new PeerIdentity[b.getAuthorities().size()];
             ids = b.getAuthorities().toArray(ids);
-            electionState.consensus = new Consensus(getConfiguration(),
+            electionState.consensus = new Consensus(
+                    getConfiguration(),
                     ids,
                     b.getBallotGuid(),
                     electionState.ballot.closingTime,
@@ -218,37 +286,83 @@
                 logger.info("concluding in {}", 
b.closingTime.getRemaining().getSeconds());
                 Scheduler.addDelayed(b.closingTime.getRemaining(), t);
             }
+            // we hash the GUID a second time, so that there's no
+            // collision with the consensus (as secretsharing also uses 
consensus internally)
+            electionState.keyGeneration = new KeyGeneration(
+                    getConfiguration(),
+                    ids,
+                    HashCode.hash(b.getBallotGuid().data),
+                    electionState.ballot.keygenStartTime,
+                    electionState.ballot.keygenEndTime,
+                    electionState.ballot.threshold, new 
SecretReady(electionState));
             elections.put(guid, electionState);
+
             BallotRegisterSuccessMessage rm = new 
BallotRegisterSuccessMessage();
             rm.registrationSignature = EddsaSignature.randomGarbage();
             getSender().send(rm);
-
         }
 
-        public void visit(QueryMessage m) {
-            ElectionState electionState = elections.get(m.ballotGUID);
+        public void visit(ResultQueryMessage m) {
+            logger.debug("got result query message");
+            ElectionState electionState = elections.get(m.ballotGuid);
             if (null == electionState) {
-                QueryFailureMessage rm = new QueryFailureMessage();
+                ResultQueryFailureMessage rm = new ResultQueryFailureMessage();
                 rm.reason = "no matching ballot found";
                 getSender().send(rm);
             } else {
-                if (!electionState.consensusDone) {
-                    QueryFailureMessage rm = new QueryFailureMessage();
-                    rm.reason = "consensus not finished (sorry...)";
+                if (!electionState.ballot.queryTime.isDue()) {
+                    ResultQueryFailureMessage rm = new 
ResultQueryFailureMessage();
+                    rm.reason = "result query not allowed yet";
                     getSender().send(rm);
                 }
-                else if (electionState.ballot.queryTime.isDue()) {
-                    QueryResponseMessage rm = new QueryResponseMessage();
+                else if (null == electionState.tally) {
+                    ResultQueryFailureMessage rm = new 
ResultQueryFailureMessage();
+                    rm.reason = "tally not yet available";
+                    getSender().send(rm);
+                }
+                else {
+                    ResultQueryResponseMessage rm = new 
ResultQueryResponseMessage();
                     rm.results = electionState.tally;
                     getSender().send(rm);
-                } else {
-                    QueryFailureMessage rm = new QueryFailureMessage();
-                    rm.reason = "result query not allowed yet";
-                    getSender().send(rm);
                 }
             }
             getSender().receiveDone();
         }
+
+        public void visit(KeyQueryMessage m) {
+            logger.debug("got key query message");
+            getSender().receiveDone();
+            ElectionState electionState = elections.get(m.ballotGuid);
+            if (null == electionState) {
+                KeyQueryFailureMessage rm = new KeyQueryFailureMessage();
+                rm.reason = "no matching ballot found";
+                getSender().send(rm);
+                return;
+            }
+            if (!electionState.ballot.keygenEndTime.isDue()) {
+                KeyQueryFailureMessage rm = new KeyQueryFailureMessage();
+                rm.reason = "key query not allowed yet";
+                getSender().send(rm);
+                return;
+            }
+            if (null == electionState.share) {
+                KeyQueryFailureMessage rm = new KeyQueryFailureMessage();
+                rm.reason = "key not yet established";
+                getSender().send(rm);
+                return;
+            }
+            KeyQueryResponseMessage.BallotPublicKey ballotPublicKey = new 
KeyQueryResponseMessage.BallotPublicKey();
+            ballotPublicKey.ballotGuid = electionState.ballot.getBallotGuid();
+            ballotPublicKey.publicKey = electionState.share.publicKey;
+
+            KeyQueryResponseMessage rm = new KeyQueryResponseMessage();
+            rm.signedGuidKey = ballotPublicKey;
+            // FIXME!
+            rm.purpose = 0;
+            rm.signature = authorityPrivateKey.sign(authorityPublicKey, 
rm.purpose,
+                    Construct.toBinary(rm.signedGuidKey));
+            getSender().send(rm);
+        }
     }
 
     public TallyAuthorityDaemon(String[] args) {
@@ -266,5 +380,15 @@
     public void run() {
         logger.info("running tally daemon");
         mesh = new Mesh(getConfiguration(), null, null, new 
TallyMeshReceiver(), MESH_PORT);
+
+        Scheduler.addDelayed(RelativeTime.FOREVER, new Scheduler.Task() {
+            @Override
+            public void run(Scheduler.RunContext ctx) {
+                if (null != mesh) {
+                    mesh.destroy();
+                    mesh = null;
+                }
+            }
+        });
     }
 }

Added: 
gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryFailureMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryFailureMessage.java
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryFailureMessage.java
    2014-02-25 11:15:15 UTC (rev 32476)
@@ -0,0 +1,11 @@
+package org.gnunet.voting.messages;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(42015)
+public class KeyQueryFailureMessage implements GnunetMessage.Body {
+    @ZeroTerminatedString
+    public String reason;
+}

Added: gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryMessage.java   
                        (rev 0)
+++ gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryMessage.java   
2014-02-25 11:15:15 UTC (rev 32476)
@@ -0,0 +1,12 @@
+package org.gnunet.voting.messages;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+
address@hidden(42013)
+public class KeyQueryMessage implements GnunetMessage.Body {
+    @NestedMessage
+    public HashCode ballotGuid;
+}

Added: 
gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryResponseMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryResponseMessage.java
                           (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/KeyQueryResponseMessage.java
   2014-02-25 11:15:15 UTC (rev 32476)
@@ -0,0 +1,51 @@
+/*
+ This file is part of GNUnet.
+ (C) 2014 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING.  If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.voting.messages;
+
+import org.gnunet.construct.Message;
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.secretsharing.ThresholdPublicKey;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+import org.gnunet.util.crypto.EddsaSignature;
+
address@hidden(42014)
+public class KeyQueryResponseMessage implements GnunetMessage.Body {
+
+    public static class BallotPublicKey implements Message {
+        @NestedMessage
+        public HashCode ballotGuid;
+
+        @NestedMessage
+        public ThresholdPublicKey publicKey;
+    }
+
+    @NestedMessage
+    public EddsaSignature signature;
+
+    @UInt32
+    public int purpose;
+
+    @NestedMessage
+    public BallotPublicKey signedGuidKey;
+}

Added: 
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryFailureMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryFailureMessage.java
                         (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryFailureMessage.java
 2014-02-25 11:15:15 UTC (rev 32476)
@@ -0,0 +1,31 @@
+/*
+ This file is part of GNUnet.
+  (C) 2012, 2013 Christian Grothoff (and other contributing authors)
+
+  GNUnet is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  GNUnet is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with GNUnet; see the file COPYING.  If not, write to the
+  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+  Boston, MA 02111-1307, USA.
+ */
+
+package org.gnunet.voting.messages;
+
+import org.gnunet.construct.UnionCase;
+import org.gnunet.construct.ZeroTerminatedString;
+import org.gnunet.util.GnunetMessage;
+
address@hidden(42009)
+public class ResultQueryFailureMessage implements GnunetMessage.Body {
+    @ZeroTerminatedString
+    public String reason;
+}

Added: 
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryMessage.java    
                            (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryMessage.java    
    2014-02-25 11:15:15 UTC (rev 32476)
@@ -0,0 +1,12 @@
+package org.gnunet.voting.messages;
+
+import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UnionCase;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+
address@hidden(42005)
+public class ResultQueryMessage implements GnunetMessage.Body {
+    @NestedMessage
+    public HashCode ballotGuid;
+}

Added: 
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryResponseMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryResponseMessage.java
                                (rev 0)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/ResultQueryResponseMessage.java
        2014-02-25 11:15:15 UTC (rev 32476)
@@ -0,0 +1,12 @@
+package org.gnunet.voting.messages;
+
+
+import org.gnunet.construct.*;
+import org.gnunet.util.GnunetMessage;
+import org.gnunet.util.HashCode;
+
address@hidden(42006)
+public class ResultQueryResponseMessage implements GnunetMessage.Body {
+    @IntegerFill(signed = false, bitSize = 32)
+    public long[] results;
+}

Modified: 
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java
===================================================================
--- 
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java  
    2014-02-25 09:42:06 UTC (rev 32475)
+++ 
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java  
    2014-02-25 11:15:15 UTC (rev 32476)
@@ -22,17 +22,27 @@
 
 
 import org.gnunet.construct.NestedMessage;
+import org.gnunet.construct.UInt32;
 import org.gnunet.construct.UnionCase;
 import org.gnunet.construct.ZeroTerminatedString;
 import org.gnunet.util.AbsoluteTimeMessage;
 import org.gnunet.util.GnunetMessage;
-import org.gnunet.util.crypto.EcdsaSignedMessage;
-import org.gnunet.util.crypto.EddsaSignedMessage;
+import org.gnunet.util.crypto.EddsaSignature;
 
 @UnionCase(42010)
 public class SubmitFailureMessage implements GnunetMessage.Body {
+    public static class SignedAuthorityTime {
+        @NestedMessage
+        public EddsaSignature signature;
+        @UInt32
+        public int purpose;
+        @NestedMessage
+        public AbsoluteTimeMessage time;
+    }
+
     @ZeroTerminatedString
     public String reason;
+
     @NestedMessage(optional = true)
-    public EddsaSignedMessage<AbsoluteTimeMessage> authorityTime;
+    public SignedAuthorityTime signedAuthorityTime;
 }

Modified: 
gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java
===================================================================
--- gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java     
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/java/org/gnunet/voting/messages/SubmitMessage.java     
2014-02-25 11:15:15 UTC (rev 32476)
@@ -7,6 +7,7 @@
 import org.gnunet.util.crypto.EcdsaPublicKey;
 import org.gnunet.util.crypto.EcdsaSignature;
 import org.gnunet.util.crypto.EddsaSignature;
+import org.gnunet.voting.EncryptedVote;
 
 /**
  * Message send by the voter to the election authority to submit a vote.
@@ -33,10 +34,12 @@
      */
     @NestedMessage
     public AbsoluteTimeMessage groupCertExpiration;
+
     /**
-     * The actual vote.
-     * FIXME: this will be encrypted!
+     * The encrypted vote, including zero knowledge proofs
+     * for correctness.
+     * FIXME: wrap in a signature container
      */
-    @UInt32
-    public int choiceId;
+    @NestedMessage
+    public EncryptedVote encryptedVote;
 }

Modified: gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt
===================================================================
--- gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt      
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/resources/org/gnunet/construct/MsgMap.txt      
2014-02-25 11:15:15 UTC (rev 32476)
@@ -1,41 +1,30 @@
 org.gnunet.util.Resolver$Address|0=org.gnunet.util.Resolver$TextualAddress
 org.gnunet.util.Resolver$Address|1=org.gnunet.util.Resolver$NumericAddress
+org.gnunet.util.GnunetMessage$Body|1=org.gnunet.util.TestMessage
 org.gnunet.util.GnunetMessage$Body|274=org.gnunet.mesh.TunnelDestroyMessage
-org.gnunet.util.GnunetMessage$Body|1=org.gnunet.util.TestMessage
 org.gnunet.util.GnunetMessage$Body|273=org.gnunet.mesh.TunnelCreateMessage
 org.gnunet.util.GnunetMessage$Body|272=org.gnunet.mesh.ClientConnectMessage
 org.gnunet.util.GnunetMessage$Body|4=org.gnunet.util.Resolver$GetMessage
 org.gnunet.util.GnunetMessage$Body|5=org.gnunet.util.Resolver$ResolverResponse
-org.gnunet.util.GnunetMessage$Body|143=org.gnunet.dht.ClientGetMessage
-org.gnunet.util.GnunetMessage$Body|142=org.gnunet.dht.ClientPutMessage
+org.gnunet.util.GnunetMessage$Body|276=org.gnunet.mesh.RejectMessage
 org.gnunet.util.GnunetMessage$Body|10=org.gnunet.arm.messages.ResultMessage
 org.gnunet.util.GnunetMessage$Body|11=org.gnunet.arm.messages.StatusMessage
+org.gnunet.util.GnunetMessage$Body|13=org.gnunet.arm.messages.ListResultMessage
 org.gnunet.util.GnunetMessage$Body|286=org.gnunet.mesh.LocalAckMessage
-org.gnunet.util.GnunetMessage$Body|13=org.gnunet.arm.messages.ListResultMessage
 org.gnunet.util.GnunetMessage$Body|285=org.gnunet.mesh.DataMessage
 org.gnunet.util.GnunetMessage$Body|17=org.gnunet.hello.HelloMessage
-org.gnunet.util.GnunetMessage$Body|153=org.gnunet.dht.MonitorStartStop
 
org.gnunet.util.GnunetMessage$Body|42002=org.gnunet.voting.messages.BallotRegisterFailureMessage
 
org.gnunet.util.GnunetMessage$Body|42001=org.gnunet.voting.messages.BallotRegisterRequestMessage
-org.gnunet.util.GnunetMessage$Body|155=org.gnunet.dht.ClientPutConfirmationMessage
 
org.gnunet.util.GnunetMessage$Body|42007=org.gnunet.voting.messages.SubmitMessage
-org.gnunet.util.GnunetMessage$Body|42006=org.gnunet.voting.messages.QueryResponseMessage
-org.gnunet.util.GnunetMessage$Body|42005=org.gnunet.voting.messages.QueryMessage
-org.gnunet.util.GnunetMessage$Body|144=org.gnunet.dht.ClientGetStopMessage
-org.gnunet.util.GnunetMessage$Body|145=org.gnunet.dht.ClientResultMessage
+org.gnunet.util.GnunetMessage$Body|42006=org.gnunet.voting.messages.ResultQueryResponseMessage
+org.gnunet.util.GnunetMessage$Body|42005=org.gnunet.voting.messages.ResultQueryMessage
 
org.gnunet.util.GnunetMessage$Body|42010=org.gnunet.voting.messages.SubmitFailureMessage
-org.gnunet.util.GnunetMessage$Body|42009=org.gnunet.voting.messages.QueryFailureMessage
+org.gnunet.util.GnunetMessage$Body|42009=org.gnunet.voting.messages.ResultQueryFailureMessage
 
org.gnunet.util.GnunetMessage$Body|42008=org.gnunet.voting.messages.SubmitSuccessMessage
-org.gnunet.util.GnunetMessage$Body|149=org.gnunet.dht.MonitorGetMessage
-org.gnunet.util.GnunetMessage$Body|150=org.gnunet.dht.MonitorGetRespMessage
-org.gnunet.util.GnunetMessage$Body|151=org.gnunet.dht.MonitorPutMessage
+org.gnunet.util.GnunetMessage$Body|42015=org.gnunet.voting.messages.KeyQueryFailureMessage
+org.gnunet.util.GnunetMessage$Body|42014=org.gnunet.voting.messages.KeyQueryResponseMessage
+org.gnunet.util.GnunetMessage$Body|42013=org.gnunet.voting.messages.KeyQueryMessage
 
org.gnunet.util.GnunetMessage$Body|42012=org.gnunet.voting.messages.BallotRegisterSuccessMessage
-org.gnunet.util.GnunetMessage$Body|171=org.gnunet.statistics.GetResponseEndMessage
-org.gnunet.util.GnunetMessage$Body|170=org.gnunet.statistics.GetResponseMessage
-org.gnunet.util.GnunetMessage$Body|169=org.gnunet.statistics.GetMessage
-org.gnunet.util.GnunetMessage$Body|168=org.gnunet.statistics.SetMessage
-org.gnunet.util.GnunetMessage$Body|173=org.gnunet.statistics.WatchResponseMessage
-org.gnunet.util.GnunetMessage$Body|172=org.gnunet.statistics.WatchMessage
 
org.gnunet.util.GnunetMessage$Body|524=org.gnunet.consensus.messages.ConcludeMessage
 
org.gnunet.util.GnunetMessage$Body|525=org.gnunet.consensus.messages.ConcludeDoneMessage
 
org.gnunet.util.GnunetMessage$Body|520=org.gnunet.consensus.messages.JoinMessage
@@ -47,37 +36,23 @@
 
org.gnunet.util.GnunetMessage$Body|781=org.gnunet.secretsharing.messages.ClientDecryptMessage
 org.gnunet.util.GnunetMessage$Body|68=org.gnunet.core.DisconnectNotifyMessage
 
org.gnunet.util.GnunetMessage$Body|70=org.gnunet.core.NotifyInboundTrafficMessage
-org.gnunet.util.GnunetMessage$Body|476=org.gnunet.testbed.messages.PeerGetInformationMessage
 
org.gnunet.util.GnunetMessage$Body|71=org.gnunet.core.NotifyOutboundTrafficMessage
-org.gnunet.util.GnunetMessage$Body|477=org.gnunet.testbed.messages.PeerInformationMessage
 org.gnunet.util.GnunetMessage$Body|64=org.gnunet.core.InitMessage
-org.gnunet.util.GnunetMessage$Body|474=org.gnunet.testbed.messages.PeerCreateSuccessMessage
 org.gnunet.util.GnunetMessage$Body|65=org.gnunet.core.InitReplyMessage
-org.gnunet.util.GnunetMessage$Body|475=org.gnunet.testbed.messages.GenericOperationSuccessMessage
-org.gnunet.util.GnunetMessage$Body|472=org.gnunet.testbed.messages.ConnectionEventMessage
 org.gnunet.util.GnunetMessage$Body|67=org.gnunet.core.ConnectNotifyMessage
-org.gnunet.util.GnunetMessage$Body|473=org.gnunet.testbed.messages.OperationFailEventMessage
 org.gnunet.util.GnunetMessage$Body|76=org.gnunet.core.SendMessage
-org.gnunet.util.GnunetMessage$Body|470=org.gnunet.testbed.messages.OverlayConnectMessage
-org.gnunet.util.GnunetMessage$Body|471=org.gnunet.testbed.messages.PeerEventMessage
-org.gnunet.util.GnunetMessage$Body|468=org.gnunet.testbed.messages.PeerDestroyMessage
-org.gnunet.util.GnunetMessage$Body|466=org.gnunet.testbed.messages.PeerStartMessage
-org.gnunet.util.GnunetMessage$Body|467=org.gnunet.testbed.messages.PeerStopMessage
 org.gnunet.util.GnunetMessage$Body|74=org.gnunet.core.SendMessageRequest
-org.gnunet.util.GnunetMessage$Body|464=org.gnunet.testbed.messages.PeerCreateMessage
 org.gnunet.util.GnunetMessage$Body|75=org.gnunet.core.SendMessageReady
-org.gnunet.util.GnunetMessage$Body|465=org.gnunet.testbed.messages.PeerReconfigureMessage
 
org.gnunet.util.GnunetMessage$Body|627=org.gnunet.identity.messages.GetDefaultMessage
 
org.gnunet.util.GnunetMessage$Body|626=org.gnunet.identity.messages.UpdateListMessage
 
org.gnunet.util.GnunetMessage$Body|625=org.gnunet.identity.messages.ResultCodeMessage
-org.gnunet.util.GnunetMessage$Body|460=org.gnunet.testbed.messages.ControllerInitMessage
 
org.gnunet.util.GnunetMessage$Body|624=org.gnunet.identity.messages.StartMessage
 
org.gnunet.util.GnunetMessage$Body|631=org.gnunet.identity.messages.DeleteMessage
+org.gnunet.util.GnunetMessage$Body|323=org.gnunet.nse.UpdateMessage
 
org.gnunet.util.GnunetMessage$Body|630=org.gnunet.identity.messages.RenameMessage
-org.gnunet.util.GnunetMessage$Body|323=org.gnunet.nse.UpdateMessage
 
org.gnunet.util.GnunetMessage$Body|629=org.gnunet.identity.messages.CreateRequestMessage
+org.gnunet.util.GnunetMessage$Body|321=org.gnunet.nse.StartMessage
 
org.gnunet.util.GnunetMessage$Body|628=org.gnunet.identity.messages.SetDefaultMessage
-org.gnunet.util.GnunetMessage$Body|321=org.gnunet.nse.StartMessage
 org.gnunet.util.GnunetMessage$Body|332=org.gnunet.peerinfo.InfoMessage
 org.gnunet.util.GnunetMessage$Body|333=org.gnunet.peerinfo.InfoEnd
 org.gnunet.util.GnunetMessage$Body|331=org.gnunet.peerinfo.ListAllPeersMessage
@@ -85,15 +60,44 @@
 
org.gnunet.util.GnunetMessage$Body|369=org.gnunet.transport.messages.BlacklistInitMessage
 
org.gnunet.util.GnunetMessage$Body|371=org.gnunet.transport.messages.BlacklistReplyMessage
 
org.gnunet.util.GnunetMessage$Body|370=org.gnunet.transport.messages.BlacklistQueryMessage
+org.gnunet.util.GnunetMessage$Body|380=org.gnunet.transport.messages.AddressIterateMessage
+org.gnunet.util.GnunetMessage$Body|383=org.gnunet.transport.messages.AddressIterateResponseMessage
+org.gnunet.util.GnunetMessage$Body|366=org.gnunet.transport.messages.SetQuotaMessage
+org.gnunet.util.GnunetMessage$Body|360=org.gnunet.transport.messages.StartMessage
+org.gnunet.util.GnunetMessage$Body|143=org.gnunet.dht.ClientGetMessage
+org.gnunet.util.GnunetMessage$Body|142=org.gnunet.dht.ClientPutMessage
+org.gnunet.util.GnunetMessage$Body|153=org.gnunet.dht.MonitorStartStop
+org.gnunet.util.GnunetMessage$Body|155=org.gnunet.dht.ClientPutConfirmationMessage
+org.gnunet.util.GnunetMessage$Body|144=org.gnunet.dht.ClientGetStopMessage
+org.gnunet.util.GnunetMessage$Body|145=org.gnunet.dht.ClientResultMessage
+org.gnunet.util.GnunetMessage$Body|149=org.gnunet.dht.MonitorGetMessage
+org.gnunet.util.GnunetMessage$Body|150=org.gnunet.dht.MonitorGetRespMessage
+org.gnunet.util.GnunetMessage$Body|151=org.gnunet.dht.MonitorPutMessage
+org.gnunet.util.GnunetMessage$Body|171=org.gnunet.statistics.GetResponseEndMessage
+org.gnunet.util.GnunetMessage$Body|170=org.gnunet.statistics.GetResponseMessage
+org.gnunet.util.GnunetMessage$Body|169=org.gnunet.statistics.GetMessage
+org.gnunet.util.GnunetMessage$Body|168=org.gnunet.statistics.SetMessage
+org.gnunet.util.GnunetMessage$Body|173=org.gnunet.statistics.WatchResponseMessage
+org.gnunet.util.GnunetMessage$Body|172=org.gnunet.statistics.WatchMessage
+org.gnunet.util.GnunetMessage$Body|476=org.gnunet.testbed.messages.PeerGetInformationMessage
+org.gnunet.util.GnunetMessage$Body|477=org.gnunet.testbed.messages.PeerInformationMessage
+org.gnunet.util.GnunetMessage$Body|474=org.gnunet.testbed.messages.PeerCreateSuccessMessage
+org.gnunet.util.GnunetMessage$Body|475=org.gnunet.testbed.messages.GenericOperationSuccessMessage
+org.gnunet.util.GnunetMessage$Body|472=org.gnunet.testbed.messages.ConnectionEventMessage
+org.gnunet.util.GnunetMessage$Body|473=org.gnunet.testbed.messages.OperationFailEventMessage
+org.gnunet.util.GnunetMessage$Body|470=org.gnunet.testbed.messages.OverlayConnectMessage
+org.gnunet.util.GnunetMessage$Body|471=org.gnunet.testbed.messages.PeerEventMessage
+org.gnunet.util.GnunetMessage$Body|468=org.gnunet.testbed.messages.PeerDestroyMessage
+org.gnunet.util.GnunetMessage$Body|466=org.gnunet.testbed.messages.PeerStartMessage
+org.gnunet.util.GnunetMessage$Body|467=org.gnunet.testbed.messages.PeerStopMessage
+org.gnunet.util.GnunetMessage$Body|464=org.gnunet.testbed.messages.PeerCreateMessage
+org.gnunet.util.GnunetMessage$Body|465=org.gnunet.testbed.messages.PeerReconfigureMessage
+org.gnunet.util.GnunetMessage$Body|460=org.gnunet.testbed.messages.ControllerInitMessage
 
org.gnunet.util.GnunetMessage$Body|500=org.gnunet.gns.messages.ClientLookupMessage
 
org.gnunet.util.GnunetMessage$Body|501=org.gnunet.gns.messages.ClientLookupResultMessage
-org.gnunet.util.GnunetMessage$Body|380=org.gnunet.transport.messages.AddressIterateMessage
-org.gnunet.util.GnunetMessage$Body|383=org.gnunet.transport.messages.AddressIterateResponseMessage
 
org.gnunet.util.GnunetMessage$Body|496=org.gnunet.testbed.messages.HelperReplyMessage
 
org.gnunet.util.GnunetMessage$Body|495=org.gnunet.testbed.messages.HelperInitMessage
-org.gnunet.util.GnunetMessage$Body|366=org.gnunet.transport.messages.SetQuotaMessage
-org.gnunet.util.GnunetMessage$Body|360=org.gnunet.transport.messages.StartMessage
 
org.gnunet.util.GnunetMessage$Body|483=org.gnunet.testbed.messages.ManagePeerServiceMessage
 org.gnunet.gns.records.RecordData|65536=org.gnunet.gns.records.PkeyRecordData
 org.gnunet.gns.records.RecordData|1=org.gnunet.gns.records.ARecordData
-# generated 2014/02/03 23:33:43
+# generated 2014/02/25 01:43:32

Modified: gnunet-java/src/main/resources/org/gnunet/voting/ballot-template.espec
===================================================================
--- gnunet-java/src/main/resources/org/gnunet/voting/ballot-template.espec      
2014-02-25 09:42:06 UTC (rev 32475)
+++ gnunet-java/src/main/resources/org/gnunet/voting/ballot-template.espec      
2014-02-25 11:15:15 UTC (rev 32476)
@@ -36,6 +36,9 @@
 # public key of the election issuer
 # ISSUER_PUB =
 
+# how many authorities would have to collude to
+# deanonymize a voter?
+THRESHOLD =
 
 [authorities]
 # specified as <authority-alias> = <peer-identity>, one entry for each 
authority; e.g.
@@ -61,3 +64,12 @@
 
 [confirmations]
 # signatures by the authorities that counted the ballot, in the form of 
<alias> = <sig>
+
+[threshold-pubkeys]
+# <authority-alias> = <treshold-pubkey>
+
+[threshold-pubkey-signatures]
+# Signature of the authority which certifies
+# that the pubkey (from the threshold-pubkeys section)
+# belongs to this election (with its GUID)
+# <autority-alias> = <signature>
\ No newline at end of file

Added: gnunet-java/src/test/python/test_voting.conf
===================================================================
--- gnunet-java/src/test/python/test_voting.conf                                
(rev 0)
+++ gnunet-java/src/test/python/test_voting.conf        2014-02-25 11:15:15 UTC 
(rev 32476)
@@ -0,0 +1,5 @@
+[arm]
+DEFAULTSERVICES = mesh set consensus secretsharing
+
+[testbed]
+OVERLAY_TOPOLOGY = CLIQUE

Added: gnunet-java/src/test/python/test_voting_single.py
===================================================================
--- gnunet-java/src/test/python/test_voting_single.py                           
(rev 0)
+++ gnunet-java/src/test/python/test_voting_single.py   2014-02-25 11:15:15 UTC 
(rev 32476)
@@ -0,0 +1,154 @@
+"""
+Test the voting implementation with a single authority.
+"""
+import os
+import subprocess
+import time
+
+
+NUM_AUTHORITIES = 1
+NUM_VOTERS = 1
+
+
+def wait_for_after(ts):
+  now = time.time()
+  if now < ts:
+    time.sleep(ts - now)
+
+def get_config(section, option, filename=None, expand=False):
+  args = ["gnunet-config"]
+  if filename is not None:
+    args.extend(["-c", filename])
+  args.extend(["-s", section])
+  args.extend(["-o", option])
+  if expand:
+    args.extend(["-f"])
+  return subprocess.check_output(args).strip()
+
+
+def create_identity(name, config=None):
+  args = ["gnunet-identity", "-C", name]
+  if config is not None:
+    args.extend(["-c", config])
+  subprocess.check_call(args)
+
+def get_identity_pubkey(name, config=None):
+  args = ["gnunet-identity", "-d", name]
+  if config is not None:
+    args.extend(["-c", config])
+  out = subprocess.check_output(args)
+  components = out.split("-")
+  return components[-1].strip()
+
+testdir = subprocess.check_output(["mktemp", "-d", "test-voting-XXXXXXXXXX.d", 
"--tmpdir"])
+testdir = testdir.strip()
+ballot = os.path.join(testdir, "ballot")
+print "testdir", testdir
+
+
+testbed_conf = "test_voting.conf"
+env = os.environ.copy()
+env["GNUNET_TESTING_PREFIX"] = testdir
+testbed = subprocess.Popen(["gnunet-testbed-profiler", "-n", "-c", 
testbed_conf, "-p", "1"], env=env)
+
+ballot_filename = os.path.join(testdir, "ballot")
+
+conf = []
+conf.append(os.path.join(testdir, "0", "config"))
+
+for c in conf:
+  while not os.path.exists(c):
+    print "waiting for creation of", c
+    time.sleep(0.1)
+
+print "test dir:", testdir
+
+
+# start authority
+# FIXME: using gnunet-arm for this might be nicer,
+auth = subprocess.Popen(["gnunet-daemon-ballot-tally", "-c", conf[0]])
+
+private_key_filename = get_config("peer", "private_key", conf[0], expand=True)
+
+public_key = subprocess.check_output(["gnunet-ecc", "--print-public-key", 
private_key_filename]).strip()
+
+print "public key", public_key
+
+create_identity("voter0", conf[0])
+create_identity("issuer", conf[0])
+create_identity("groupca", conf[0])
+
+print "created identities"
+
+ballot = open(ballot_filename, "w")
+
+now = int(time.time())
+TS_KEYGEN_START = now + 10
+TS_KEYGEN_END = now + 20
+TS_START = now + 20
+TS_CLOSING = now + 25
+TS_CONCLUDE = now + 45
+TS_QUERY = now + 65
+TS_END = now + 65
+
+ballot.write("[authorities]\n")
+ballot.write("auth0 = %s\n" % public_key)
+ballot.write("[election]\n")
+ballot.write("TOPIC = mytopic\n")
+ballot.write("THRESHOLD = 1\n")
+ballot.write("CHOICES = yes//no\n")
+ballot.write("GROUP = mygroup\n")
+ballot.write("CA_PUB = %s\n" % get_identity_pubkey("groupca", conf[0]))
+ballot.write("TIMESTAMP_KEYGEN_START = %s\n" % TS_KEYGEN_START)
+ballot.write("TIMESTAMP_KEYGEN_END = %s\n" % TS_KEYGEN_END)
+ballot.write("TIMESTAMP_START = %s\n" % TS_START)
+ballot.write("TIMESTAMP_CLOSING = %s\n" % TS_CLOSING)
+ballot.write("TIMESTAMP_CONCLUDE = %s\n" % TS_CONCLUDE)
+ballot.write("TIMESTAMP_QUERY = %s\n" % TS_QUERY)
+ballot.write("TIMESTAMP_END = %s\n" % TS_END)
+
+ballot.close()
+
+groupcert_filename = os.path.join(testdir, "v0-cert")
+groupcert_file = open(groupcert_filename, "w")
+
+v0_pub = get_identity_pubkey("voter0", conf[0])
+
+subprocess.check_call(["gnunet-ballot-group-certify", "-c", conf[0],
+  "-g", "mygroup", "-e", "groupca", "-m", v0_pub], stdout=groupcert_file)
+
+groupcert_file.close()
+
+# register the ballot with authorities
+subprocess.check_call(["gnunet-ballot", "-LINFO", "-i", ballot_filename, "-e", 
"issuer", "-c", conf[0]])
+
+# register the ballot with authorities
+subprocess.check_call(["gnunet-ballot", "-LINFO", "-r", ballot_filename, "-c", 
conf[0]])
+
+wait_for_after(TS_KEYGEN_END)
+
+print "getting threshold public key"
+
+# get the threshold public key
+subprocess.check_call(["gnunet-ballot", "-LDEBUG", "-k", ballot_filename, 
"-c", conf[0]])
+
+print "threshold public key retrieved"
+
+# add voter's group information
+subprocess.check_call(["gnunet-ballot", "-g", groupcert_filename, 
ballot_filename, "-c", conf[0]])
+
+# actually vote ...
+subprocess.check_call(["gnunet-ballot", "-x", "yes", ballot_filename, "-e", 
"voter0", "-c", conf[0]])
+
+wait_for_after(TS_START)
+
+# submit the ballot with the vote
+subprocess.check_call(["gnunet-ballot", "-s", ballot_filename, "-c", conf[0]])
+
+wait_for_after(TS_QUERY)
+
+# query the result
+subprocess.check_call(["gnunet-ballot", "-q", ballot_filename, "-c", conf[0]])
+
+# FIXME: cleanup
+




reply via email to

[Prev in Thread] Current Thread [Next in Thread]