aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/org/gnunet
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2014-02-25 11:15:15 +0000
committerFlorian Dold <florian.dold@gmail.com>2014-02-25 11:15:15 +0000
commit85d0048c22271fac371729ba7b14417a28bb6b48 (patch)
treede231f5453baed5b406bb88b19496d16530f3850 /src/main/java/org/gnunet
parent989f297285b843cb631836bf42e47f25fb567418 (diff)
downloadgnunet-java-85d0048c22271fac371729ba7b14417a28bb6b48.tar.gz
gnunet-java-85d0048c22271fac371729ba7b14417a28bb6b48.zip
- 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
Diffstat (limited to 'src/main/java/org/gnunet')
-rw-r--r--src/main/java/org/gnunet/construct/Construct.java20
-rw-r--r--src/main/java/org/gnunet/construct/parsers/NestedParser.java4
-rw-r--r--src/main/java/org/gnunet/dht/ClientGetStopMessage.java9
-rw-r--r--src/main/java/org/gnunet/mesh/Mesh.java25
-rw-r--r--src/main/java/org/gnunet/mesh/RejectMessage.java49
-rw-r--r--src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java3
-rw-r--r--src/main/java/org/gnunet/secretsharing/Ciphertext.java50
-rw-r--r--src/main/java/org/gnunet/secretsharing/Plaintext.java36
-rw-r--r--src/main/java/org/gnunet/secretsharing/ThresholdPublicKey.java29
-rw-r--r--src/main/java/org/gnunet/secretsharing/messages/DecryptDoneMessage.java5
-rw-r--r--src/main/java/org/gnunet/util/BigIntegers.java56
-rw-r--r--src/main/java/org/gnunet/util/MessageStreamTokenizer.java5
-rw-r--r--src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java1
-rw-r--r--src/main/java/org/gnunet/voting/Ballot.java183
-rw-r--r--src/main/java/org/gnunet/voting/BallotTool.java173
-rw-r--r--src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java226
-rw-r--r--src/main/java/org/gnunet/voting/messages/KeyQueryFailureMessage.java11
-rw-r--r--src/main/java/org/gnunet/voting/messages/KeyQueryMessage.java12
-rw-r--r--src/main/java/org/gnunet/voting/messages/KeyQueryResponseMessage.java51
-rw-r--r--src/main/java/org/gnunet/voting/messages/ResultQueryFailureMessage.java31
-rw-r--r--src/main/java/org/gnunet/voting/messages/ResultQueryMessage.java12
-rw-r--r--src/main/java/org/gnunet/voting/messages/ResultQueryResponseMessage.java12
-rw-r--r--src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java16
-rw-r--r--src/main/java/org/gnunet/voting/messages/SubmitMessage.java11
24 files changed, 867 insertions, 163 deletions
diff --git a/src/main/java/org/gnunet/construct/Construct.java b/src/main/java/org/gnunet/construct/Construct.java
index ac3c804..82e6fcb 100644
--- a/src/main/java/org/gnunet/construct/Construct.java
+++ b/src/main/java/org/gnunet/construct/Construct.java
@@ -85,15 +85,21 @@ public class Construct {
85 * @return instance of the desired object type 85 * @return instance of the desired object type
86 */ 86 */
87 public static <T extends Message> T parseAs(ByteBuffer srcBuf, Class<T> c) { 87 public static <T extends Message> T parseAs(ByteBuffer srcBuf, Class<T> c) {
88 T m = ReflectUtil.justInstantiate(c);
89
90 try { 88 try {
91 getParser(c).parse(srcBuf, 0, m, m, null); 89 T m = ReflectUtil.justInstantiate(c);
92 } catch (ProtocolViolationException e) { 90
93 e.augmentPath("on " + c); 91 try {
94 } 92 getParser(c).parse(srcBuf, 0, m, m, null);
93 } catch (ProtocolViolationException e) {
94 e.augmentPath("on " + c);
95 }
95 96
96 return m; 97 return m;
98 } catch (Error e) {
99 System.err.println(
100 String.format("Exception while parsing message for class '%s':", c.getCanonicalName()));
101 throw e;
102 }
97 } 103 }
98 104
99 /** 105 /**
diff --git a/src/main/java/org/gnunet/construct/parsers/NestedParser.java b/src/main/java/org/gnunet/construct/parsers/NestedParser.java
index 76aa397..476225b 100644
--- a/src/main/java/org/gnunet/construct/parsers/NestedParser.java
+++ b/src/main/java/org/gnunet/construct/parsers/NestedParser.java
@@ -80,7 +80,9 @@ public class NestedParser implements Parser {
80 return 0; 80 return 0;
81 } 81 }
82 } 82 }
83 83 if (targetField.getType().isInterface()) {
84 throw new AssertionError(String.format("Target field '%s' is an interface, not a class.", targetField));
85 }
84 ReflectUtil.justSet(dstObj, targetField, ReflectUtil.justInstantiate(targetField.getType())); 86 ReflectUtil.justSet(dstObj, targetField, ReflectUtil.justInstantiate(targetField.getType()));
85 87
86 try { 88 try {
diff --git a/src/main/java/org/gnunet/dht/ClientGetStopMessage.java b/src/main/java/org/gnunet/dht/ClientGetStopMessage.java
index 39915de..5aa5cc4 100644
--- a/src/main/java/org/gnunet/dht/ClientGetStopMessage.java
+++ b/src/main/java/org/gnunet/dht/ClientGetStopMessage.java
@@ -28,12 +28,9 @@ import org.gnunet.util.GnunetMessage;
28import org.gnunet.util.HashCode; 28import org.gnunet.util.HashCode;
29 29
30/** 30/**
31* Created with IntelliJ IDEA. 31 * Sent by the client to the service.
32* User: dold 32 * Requests that the service stops a 'get' operation.
33* Date: 5/2/12 33 */
34* Time: 7:05 PM
35* To change this template use File | Settings | File Templates.
36*/
37@UnionCase(144) 34@UnionCase(144)
38public class ClientGetStopMessage implements GnunetMessage.Body { 35public class ClientGetStopMessage implements GnunetMessage.Body {
39 @UInt32 36 @UInt32
diff --git a/src/main/java/org/gnunet/mesh/Mesh.java b/src/main/java/org/gnunet/mesh/Mesh.java
index 6f8f4b3..0a7369b 100644
--- a/src/main/java/org/gnunet/mesh/Mesh.java
+++ b/src/main/java/org/gnunet/mesh/Mesh.java
@@ -133,8 +133,7 @@ public class Mesh {
133 * @param nobuffer Flag for disabling buffering on relay nodes. 133 * @param nobuffer Flag for disabling buffering on relay nodes.
134 * @param reliable Flag for end-to-end reliability. 134 * @param reliable Flag for end-to-end reliability.
135 */ 135 */
136 public Tunnel(PeerIdentity peer, int port, boolean nobuffer, boolean reliable, T context) 136 public Tunnel(PeerIdentity peer, int port, boolean nobuffer, boolean reliable, T context) {
137 {
138 this(peer, 0, port, nobuffer, reliable); 137 this(peer, 0, port, nobuffer, reliable);
139 TunnelCreateMessage tcm = new TunnelCreateMessage(); 138 TunnelCreateMessage tcm = new TunnelCreateMessage();
140 tcm.otherEnd = peer; 139 tcm.otherEnd = peer;
@@ -144,6 +143,7 @@ public class Mesh {
144 client.send(tcm); 143 client.send(tcm);
145 } 144 }
146 145
146
147 /** 147 /**
148 * Private tunnel constructor, for creating tunnel objects for 148 * Private tunnel constructor, for creating tunnel objects for
149 * incoming tunnels. 149 * incoming tunnels.
@@ -188,7 +188,7 @@ public class Mesh {
188 if (!destroyedByService) { 188 if (!destroyedByService) {
189 TunnelDestroyMessage m = new TunnelDestroyMessage(); 189 TunnelDestroyMessage m = new TunnelDestroyMessage();
190 m.tunnelId = tunnelId; 190 m.tunnelId = tunnelId;
191 m.reserved = new byte[64]; 191 m.reserved = new byte[32];
192 client.send(m); 192 client.send(m);
193 } 193 }
194 tunnelMap.remove(tunnelId); 194 tunnelMap.remove(tunnelId);
@@ -258,7 +258,10 @@ public class Mesh {
258 logger.warn("got unexpected message from service"); 258 logger.warn("got unexpected message from service");
259 t.receiveDoneExpected = true; 259 t.receiveDoneExpected = true;
260 messageReceiver.setSender(t); 260 messageReceiver.setSender(t);
261 messageReceiver.visitAppropriate(Construct.parseAs(m.payload, GnunetMessage.class).body); 261 GnunetMessage gnunetMessage = Construct.parseAs(m.payload, GnunetMessage.class);
262 logger.debug("received message of size {} and type {}",
263 gnunetMessage.header.messageSize, gnunetMessage.header.messageType);
264 messageReceiver.visitAppropriate(gnunetMessage.body);
262 messageReceiver.setSender(null); 265 messageReceiver.setSender(null);
263 } 266 }
264 } 267 }
@@ -286,6 +289,20 @@ public class Mesh {
286 } 289 }
287 } 290 }
288 291
292 public void visit(RejectMessage m) {
293 // FIXME: C code indicates that the nack/reject message might change ...
294 Tunnel t = tunnelMap.get(m.tunnelId);
295 if (null == t) {
296 logger.warn("server got confused with tunnel IDs on destroy, ignoring message");
297 return;
298 }
299 t.destroyedByService = true;
300 t.destroy();
301 if (null != tunnelEndHandler) {
302 tunnelEndHandler.onTunnelEnd(t);
303 }
304 }
305
289 @Override 306 @Override
290 public void handleError() { 307 public void handleError() {
291 logger.warn("lost connection to mesh service, reconnecting"); 308 logger.warn("lost connection to mesh service, reconnecting");
diff --git a/src/main/java/org/gnunet/mesh/RejectMessage.java b/src/main/java/org/gnunet/mesh/RejectMessage.java
new file mode 100644
index 0000000..aaf63c7
--- /dev/null
+++ b/src/main/java/org/gnunet/mesh/RejectMessage.java
@@ -0,0 +1,49 @@
1/*
2 This file is part of GNUnet.
3 (C) 2014 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.mesh;
22
23import org.gnunet.construct.FixedSizeIntegerArray;
24import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UnionCase;
26import org.gnunet.util.GnunetMessage;
27
28/**
29 * Message sent by the server to indicate that a tunnel could not
30 * be created.
31 *
32 * (GNUNET_MESSAGE_TYPE_MESH_CHANNEL_NACK)
33 *
34 * @author Florian Dold
35 */
36@UnionCase(276)
37public class RejectMessage implements GnunetMessage.Body {
38 @UInt32
39 public long tunnelId;
40
41 @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
42 public byte[] reserved;
43
44 @UInt32
45 public int port;
46
47 @UInt32
48 public int opt;
49}
diff --git a/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java b/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
index 81472bf..b6e2fc9 100644
--- a/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
+++ b/src/main/java/org/gnunet/mesh/TunnelDestroyMessage.java
@@ -17,8 +17,9 @@ public class TunnelDestroyMessage implements GnunetMessage.Body {
17 @UInt32 17 @UInt32
18 public long tunnelId; 18 public long tunnelId;
19 19
20 @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 64) 20 @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
21 public byte[] reserved; 21 public byte[] reserved;
22
22 @UInt32 23 @UInt32
23 public int port; 24 public int port;
24 25
diff --git a/src/main/java/org/gnunet/secretsharing/Ciphertext.java b/src/main/java/org/gnunet/secretsharing/Ciphertext.java
index bb2962e..b940f21 100644
--- a/src/main/java/org/gnunet/secretsharing/Ciphertext.java
+++ b/src/main/java/org/gnunet/secretsharing/Ciphertext.java
@@ -22,6 +22,10 @@ package org.gnunet.secretsharing;
22 22
23import org.gnunet.construct.FixedSizeIntegerArray; 23import org.gnunet.construct.FixedSizeIntegerArray;
24import org.gnunet.construct.Message; 24import org.gnunet.construct.Message;
25import org.gnunet.util.BigIntegers;
26import org.gnunet.util.Strings;
27
28import java.math.BigInteger;
25 29
26/** 30/**
27 * ElGamal ciphertext 31 * ElGamal ciphertext
@@ -32,4 +36,50 @@ public class Ciphertext implements Message {
32 36
33 @FixedSizeIntegerArray(signed = true, bitSize = 8, length = Parameters.elgamalBits / 8) 37 @FixedSizeIntegerArray(signed = true, bitSize = 8, length = Parameters.elgamalBits / 8)
34 public byte[] c_2; 38 public byte[] c_2;
39
40 /**
41 * Allocate the ciphertext with zeros.
42 */
43 public void allocate() {
44 c_1 = new byte[Parameters.elgamalBits / 8];
45 c_2 = new byte[Parameters.elgamalBits / 8];
46 }
47
48 @Override
49 public String toString() {
50 byte[] allBytes = new byte[c_1.length + c_2.length];
51 System.arraycopy(c_1, 0, allBytes, 0, c_1.length);
52 System.arraycopy(c_2, 0, allBytes, c_1.length, c_2.length);
53 return Strings.dataToString(allBytes);
54 }
55
56 public static Ciphertext fromString(String s) {
57 byte[] allBytes = new byte[2 * Parameters.elgamalBits / 8];
58 if (!Strings.stringToData(s, allBytes))
59 return null;
60 Ciphertext ciphertext = new Ciphertext();
61 ciphertext.allocate();
62 System.arraycopy(allBytes, 0, ciphertext.c_1, 0, ciphertext.c_1.length);
63 System.arraycopy(allBytes, ciphertext.c_1.length, ciphertext.c_2, 0, ciphertext.c_2.length);
64 return ciphertext;
65 }
66
67 /**
68 * Multiply two elgamal ciphertexts.
69 *
70 * @param v the other ciphertext
71 * @return the product of two ciphertexts
72 */
73 public Ciphertext multiply(Ciphertext v) {
74 BigInteger xc_1 = new BigInteger(1, this.c_1);
75 BigInteger xc_2 = new BigInteger(1, this.c_2);
76 BigInteger yc_1 = new BigInteger(1, v.c_1);
77 BigInteger yc_2 = new BigInteger(1, v.c_2);
78 Ciphertext ciphertext = new Ciphertext();
79 ciphertext.c_1 = BigIntegers.serializeUnsigned(xc_1.multiply(yc_1).mod(Parameters.elgamalP),
80 Parameters.elgamalBits);
81 ciphertext.c_2 = BigIntegers.serializeUnsigned(xc_2.multiply(yc_2).mod(Parameters.elgamalP),
82 Parameters.elgamalBits);
83 return ciphertext;
84 }
35} 85}
diff --git a/src/main/java/org/gnunet/secretsharing/Plaintext.java b/src/main/java/org/gnunet/secretsharing/Plaintext.java
index c4fdd3e..c2eacc2 100644
--- a/src/main/java/org/gnunet/secretsharing/Plaintext.java
+++ b/src/main/java/org/gnunet/secretsharing/Plaintext.java
@@ -23,6 +23,7 @@ package org.gnunet.secretsharing;
23import com.google.common.base.Preconditions; 23import com.google.common.base.Preconditions;
24import org.gnunet.construct.FixedSizeIntegerArray; 24import org.gnunet.construct.FixedSizeIntegerArray;
25import org.gnunet.construct.Message; 25import org.gnunet.construct.Message;
26import org.gnunet.util.BigIntegers;
26 27
27import java.math.BigInteger; 28import java.math.BigInteger;
28import java.security.SecureRandom; 29import java.security.SecureRandom;
@@ -35,32 +36,19 @@ public class Plaintext implements Message {
35 BigInteger val; 36 BigInteger val;
36 val = Parameters.elgamalG.modPow(exp, Parameters.elgamalP); 37 val = Parameters.elgamalG.modPow(exp, Parameters.elgamalP);
37 Plaintext plaintext = new Plaintext(); 38 Plaintext plaintext = new Plaintext();
38 plaintext.bits = serializeBigIntUnsigned(val, Parameters.elgamalBits); 39 plaintext.bits = BigIntegers.serializeUnsigned(val, Parameters.elgamalBits);
39 return plaintext; 40 return plaintext;
40 } 41 }
41 42
42 /** 43 public long bruteForceDiscreteLog(final long l) {
43 * Serialize a BigInteger, but do not add an extra bit for a 44 BigInteger needle = new BigInteger(1, bits);
44 * sign. 45 for (long i = -l; i < l; i++) {
45 * 46 BigInteger val;
46 * @param bigInteger 47 val = Parameters.elgamalG.modPow(BigInteger.valueOf(l), Parameters.elgamalP);
47 * @param bits 48 if (val.equals(needle))
48 * @return 49 return i;
49 */
50 private static byte[] serializeBigIntUnsigned(BigInteger bigInteger, int bits) {
51 byte[] bytes = bigInteger.toByteArray();
52 int start;
53 Preconditions.checkArgument(bigInteger.bitCount() <= bits);
54 // skip byte that was only added to fit the sign
55 if (bytes[0] == 0) {
56 start = 1;
57 } else {
58 start = 0;
59 } 50 }
60 byte[] fixedBytes = new byte[(bits + 7) / 8]; 51 throw new ArithmeticException("discrete log has no solution in given range");
61 System.arraycopy(bytes, start, fixedBytes,
62 fixedBytes.length - bytes.length + start, bytes.length - start);
63 return fixedBytes;
64 } 52 }
65 53
66 public Ciphertext encrypt(ThresholdPublicKey publicKey) { 54 public Ciphertext encrypt(ThresholdPublicKey publicKey) {
@@ -82,8 +70,8 @@ public class Plaintext implements Message {
82 c_2 = m.multiply(h.modPow(y, Parameters.elgamalP)).mod(Parameters.elgamalP); 70 c_2 = m.multiply(h.modPow(y, Parameters.elgamalP)).mod(Parameters.elgamalP);
83 71
84 Ciphertext ciphertext = new Ciphertext(); 72 Ciphertext ciphertext = new Ciphertext();
85 ciphertext.c_1 = serializeBigIntUnsigned(c_1, Parameters.elgamalBits); 73 ciphertext.c_1 = BigIntegers.serializeUnsigned(c_1, Parameters.elgamalBits);
86 ciphertext.c_2 = serializeBigIntUnsigned(c_2, Parameters.elgamalBits); 74 ciphertext.c_2 = BigIntegers.serializeUnsigned(c_2, Parameters.elgamalBits);
87 75
88 return ciphertext; 76 return ciphertext;
89 } 77 }
diff --git a/src/main/java/org/gnunet/secretsharing/ThresholdPublicKey.java b/src/main/java/org/gnunet/secretsharing/ThresholdPublicKey.java
index b7075be..4560788 100644
--- a/src/main/java/org/gnunet/secretsharing/ThresholdPublicKey.java
+++ b/src/main/java/org/gnunet/secretsharing/ThresholdPublicKey.java
@@ -22,11 +22,40 @@ package org.gnunet.secretsharing;
22 22
23import org.gnunet.construct.FixedSizeIntegerArray; 23import org.gnunet.construct.FixedSizeIntegerArray;
24import org.gnunet.construct.Message; 24import org.gnunet.construct.Message;
25import org.gnunet.util.Strings;
26
27import java.util.Arrays;
25 28
26/** 29/**
27 * Threshold public key. 30 * Threshold public key.
28 */ 31 */
29public class ThresholdPublicKey implements Message { 32public class ThresholdPublicKey implements Message {
33
30 @FixedSizeIntegerArray(signed = true, bitSize = 8, length = Parameters.elgamalBits / 8) 34 @FixedSizeIntegerArray(signed = true, bitSize = 8, length = Parameters.elgamalBits / 8)
31 public byte[] bits; 35 public byte[] bits;
36
37 @Override
38 public boolean equals(Object o) {
39 if (this == o) return true;
40 if (o == null || getClass() != o.getClass()) return false;
41 ThresholdPublicKey that = (ThresholdPublicKey) o;
42 return Arrays.equals(bits, that.bits);
43 }
44
45 @Override
46 public int hashCode() {
47 return bits != null ? Arrays.hashCode(bits) : 0;
48 }
49
50 public static ThresholdPublicKey fromString(String value) {
51 ThresholdPublicKey pk = new ThresholdPublicKey();
52 pk.bits = new byte[Parameters.elgamalBits / 8];
53 Strings.stringToData(value, pk.bits);
54 return pk;
55 }
56
57 @Override
58 public String toString() {
59 return Strings.dataToString(bits);
60 }
32} 61}
diff --git a/src/main/java/org/gnunet/secretsharing/messages/DecryptDoneMessage.java b/src/main/java/org/gnunet/secretsharing/messages/DecryptDoneMessage.java
index 71f24da..488e28b 100644
--- a/src/main/java/org/gnunet/secretsharing/messages/DecryptDoneMessage.java
+++ b/src/main/java/org/gnunet/secretsharing/messages/DecryptDoneMessage.java
@@ -1,5 +1,4 @@
1/* 1/*
2
3 This file is part of GNUnet. 2 This file is part of GNUnet.
4 (C) 2014 Christian Grothoff (and other contributing authors) 3 (C) 2014 Christian Grothoff (and other contributing authors)
5 4
@@ -17,7 +16,6 @@
17 along with GNUnet; see the file COPYING. If not, write to the 16 along with GNUnet; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place - Suite 330, 17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. 18 Boston, MA 02111-1307, USA.
20
21 */ 19 */
22 20
23package org.gnunet.secretsharing.messages; 21package org.gnunet.secretsharing.messages;
@@ -29,7 +27,8 @@ import org.gnunet.secretsharing.Plaintext;
29import org.gnunet.util.GnunetMessage; 27import org.gnunet.util.GnunetMessage;
30 28
31/** 29/**
32 * Created by dold on 2/2/14. 30 * Sent by the service to the client.
31 * Contains the plaintext for a successfully decrypted ciphertext.
33 */ 32 */
34@UnionCase(782) 33@UnionCase(782)
35public class DecryptDoneMessage implements GnunetMessage.Body{ 34public class DecryptDoneMessage implements GnunetMessage.Body{
diff --git a/src/main/java/org/gnunet/util/BigIntegers.java b/src/main/java/org/gnunet/util/BigIntegers.java
new file mode 100644
index 0000000..473534f
--- /dev/null
+++ b/src/main/java/org/gnunet/util/BigIntegers.java
@@ -0,0 +1,56 @@
1/*
2 This file is part of GNUnet.
3 (C) 2014 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.util;
22
23import com.google.common.base.Preconditions;
24
25import java.math.BigInteger;
26
27/**
28 * Helper class for BigIntegers.
29 */
30public class BigIntegers {
31
32 /**
33 * Serialize a BigInteger, but do not add an extra bit for a
34 * sign.
35 *
36 * @param bigInteger big integer to serialize
37 * @param bits how many bits should the binary representation have?
38 * rounded up to the next multiple of 8.
39 * @return big endian representation of the given BigInteger, without a sign bit
40 */
41 public static byte[] serializeUnsigned(BigInteger bigInteger, int bits) {
42 byte[] bytes = bigInteger.toByteArray();
43 int start;
44 Preconditions.checkArgument(bigInteger.bitCount() <= bits);
45 // skip byte that was only added to fit the sign
46 if (bytes[0] == 0) {
47 start = 1;
48 } else {
49 start = 0;
50 }
51 byte[] fixedBytes = new byte[(bits + 7) / 8];
52 System.arraycopy(bytes, start, fixedBytes,
53 fixedBytes.length - bytes.length + start, bytes.length - start);
54 return fixedBytes;
55 }
56}
diff --git a/src/main/java/org/gnunet/util/MessageStreamTokenizer.java b/src/main/java/org/gnunet/util/MessageStreamTokenizer.java
index d4a9910..a2da4c9 100644
--- a/src/main/java/org/gnunet/util/MessageStreamTokenizer.java
+++ b/src/main/java/org/gnunet/util/MessageStreamTokenizer.java
@@ -70,8 +70,9 @@ public class MessageStreamTokenizer {
70 } 70 }
71 int parsedSize = buffer.position() - oldPos; 71 int parsedSize = buffer.position() - oldPos;
72 if (parsedSize != msg.header.messageSize) { 72 if (parsedSize != msg.header.messageSize) {
73 throw new AssertionError("mismatch between parsed message and header for " + 73 throw new AssertionError(
74 msg.body.getClass()); 74 String.format("mismatch between parsed message and header for %s: parsed size %s, header size %s",
75 msg.body.getClass(), parsedSize, msg.header.messageSize));
75 } 76 }
76 mstCalllback.onKnownMessage(msg); 77 mstCalllback.onKnownMessage(msg);
77 } else { 78 } else {
diff --git a/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java b/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java
index eebcd4d..9142164 100644
--- a/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java
+++ b/src/main/java/org/gnunet/util/crypto/EcdsaPublicKey.java
@@ -65,6 +65,7 @@ public class EcdsaPublicKey implements Message {
65 */ 65 */
66 public static EcdsaPublicKey fromString(String s) { 66 public static EcdsaPublicKey fromString(String s) {
67 EcdsaPublicKey publicKey = new EcdsaPublicKey(); 67 EcdsaPublicKey publicKey = new EcdsaPublicKey();
68 publicKey.y = new byte[32];
68 if (Strings.stringToData(s, publicKey.y)) 69 if (Strings.stringToData(s, publicKey.y))
69 return publicKey; 70 return publicKey;
70 return null; 71 return null;
diff --git a/src/main/java/org/gnunet/voting/Ballot.java b/src/main/java/org/gnunet/voting/Ballot.java
index e300010..10bf25a 100644
--- a/src/main/java/org/gnunet/voting/Ballot.java
+++ b/src/main/java/org/gnunet/voting/Ballot.java
@@ -25,12 +25,12 @@ import com.google.common.base.Optional;
25import com.google.common.base.Preconditions; 25import com.google.common.base.Preconditions;
26import com.google.common.collect.BiMap; 26import com.google.common.collect.BiMap;
27import com.google.common.collect.HashBiMap; 27import com.google.common.collect.HashBiMap;
28import com.google.common.collect.Maps;
28import com.google.common.primitives.Longs; 29import com.google.common.primitives.Longs;
30import org.gnunet.secretsharing.ThresholdPublicKey;
29import org.gnunet.util.*; 31import org.gnunet.util.*;
30import org.gnunet.util.crypto.EcdsaPrivateKey; 32import org.gnunet.util.crypto.*;
31import org.gnunet.util.crypto.EcdsaPublicKey; 33import org.gnunet.voting.messages.KeyQueryResponseMessage;
32import org.gnunet.util.crypto.EcdsaSignature;
33import org.gnunet.util.crypto.EddsaSignature;
34 34
35import java.security.MessageDigest; 35import java.security.MessageDigest;
36import java.security.NoSuchAlgorithmException; 36import java.security.NoSuchAlgorithmException;
@@ -51,6 +51,14 @@ public class Ballot {
51 */ 51 */
52 String group; 52 String group;
53 /** 53 /**
54 * When will the distributed key generation among the authorities start?
55 */
56 AbsoluteTime keygenStartTime;
57 /**
58 * When will the distributed key generation among the authorities be finished?
59 */
60 AbsoluteTime keygenEndTime;
61 /**
54 * List of choices for the election. 62 * List of choices for the election.
55 */ 63 */
56 List<String> choices; 64 List<String> choices;
@@ -74,20 +82,52 @@ public class Ballot {
74 * When are the election results discarded by the authority? 82 * When are the election results discarded by the authority?
75 */ 83 */
76 AbsoluteTime endTime; 84 AbsoluteTime endTime;
85 /**
86 * Mapping from authority alias to peer identity.
87 */
77 BiMap<String,PeerIdentity> authorities; 88 BiMap<String,PeerIdentity> authorities;
89 /**
90 * Signatures from the authority certifying that it
91 * will be available for this election.
92 */
78 SortedMap<String,EddsaSignature> registrationSigs; 93 SortedMap<String,EddsaSignature> registrationSigs;
94 /**
95 * Public key of the certificate authority for group membership.
96 */
79 EcdsaPublicKey caPub; 97 EcdsaPublicKey caPub;
98 /**
99 * Public key of the issues.
100 */
80 EcdsaPublicKey issuerPub; 101 EcdsaPublicKey issuerPub;
102 /**
103 * Signature of the issuer on the basic data of the ballot.
104 */
81 EcdsaSignature issuerSig; 105 EcdsaSignature issuerSig;
106 /**
107 * Public key of the voter that fills out this ballot.
108 */
82 EcdsaPublicKey voterPub; 109 EcdsaPublicKey voterPub;
110 /**
111 * Confirmation for the fact that a vote was accepted by an authority.
112 */
83 SortedMap<String,EddsaSignature> confirmationSigs; 113 SortedMap<String,EddsaSignature> confirmationSigs;
114 /**
115 * Certificate that a voter belongs to a certain group.
116 */
84 GroupCert groupCert; 117 GroupCert groupCert;
118 /**
119 * Threshold public keys per authority alias.
120 * Ideally this should be the same for each authority, but with malicious authorities
121 * we can't trust that, so we must collect all the keys.
122 */
123 SortedMap<String,KeyQueryResponseMessage> thresholdPublicKeys;
85 124
86 /** 125 /**
87 * Choice in plaintext. 126 * Threshold for the election.
88 * This field would be encrypted in a secure version.
89 */ 127 */
90 int choiceId = -1; 128 public int threshold;
129
130 EncryptedVote encryptedVote;
91 131
92 /** 132 /**
93 * Load a ballot from file. 133 * Load a ballot from file.
@@ -163,6 +203,14 @@ public class Ballot {
163 throw new InvalidBallotException("ballot must have elegibility group"); 203 throw new InvalidBallotException("ballot must have elegibility group");
164 } 204 }
165 group = optGroup.get(); 205 group = optGroup.get();
206 Optional<Long> optThreshold = cfg.getValueNumber("election", "THRESHOLD");
207 if (!optThreshold.isPresent()) {
208 throw new InvalidBallotException("ballot must have threshold");
209 }
210 if (optThreshold.get() <= 0) {
211 throw new InvalidBallotException("threshold must be positive");
212 }
213 threshold = optThreshold.get().intValue();
166 authorities = HashBiMap.create(); 214 authorities = HashBiMap.create();
167 for (Map.Entry<String,String> e : cfg.getSection("authorities").entrySet()) { 215 for (Map.Entry<String,String> e : cfg.getSection("authorities").entrySet()) {
168 String alias = e.getKey(); 216 String alias = e.getKey();
@@ -215,16 +263,7 @@ public class Ballot {
215 } 263 }
216 confirmationSigs.put(e.getKey(), sig); 264 confirmationSigs.put(e.getKey(), sig);
217 } 265 }
218 Optional<String> optChoiceId = cfg.getValueString("vote", "CHOICE_ID"); 266 encryptedVote = EncryptedVote.parseFromConfiguration(cfg);
219 if (optChoiceId.isPresent()) {
220 choiceId = Integer.parseInt(optChoiceId.get());
221 if (choiceId < 0 || choiceId >= choices.size()) {
222 throw new InvalidBallotException("invalid choice in vote");
223 }
224 } else {
225 choiceId = -1;
226 }
227
228 Optional<String> optVoterPub = cfg.getValueString("vote", "VOTER_PUB"); 267 Optional<String> optVoterPub = cfg.getValueString("vote", "VOTER_PUB");
229 if (optVoterPub.isPresent()) { 268 if (optVoterPub.isPresent()) {
230 voterPub = EcdsaPublicKey.fromString(optVoterPub.get()); 269 voterPub = EcdsaPublicKey.fromString(optVoterPub.get());
@@ -238,10 +277,33 @@ public class Ballot {
238 concludeTime = getTime(cfg, "CONCLUDE"); 277 concludeTime = getTime(cfg, "CONCLUDE");
239 queryTime = getTime(cfg, "QUERY"); 278 queryTime = getTime(cfg, "QUERY");
240 endTime = getTime(cfg, "END"); 279 endTime = getTime(cfg, "END");
280 keygenStartTime = getTime(cfg, "KEYGEN_START");
281 keygenEndTime = getTime(cfg, "KEYGEN_END");
241 282
242 if (cfg.haveValue("vote", "GROUP_SIG")) { 283 if (cfg.haveValue("vote", "GROUP_SIG")) {
243 groupCert = GroupCert.fromBallotConfig(this, cfg); 284 groupCert = GroupCert.fromBallotConfig(this, cfg);
244 } 285 }
286
287 thresholdPublicKeys = new TreeMap<String, KeyQueryResponseMessage>();
288 for (Map.Entry<String,String> e : cfg.getSection("threshold-pubkeys").entrySet()) {
289 String alias = e.getKey();
290 if (!authorities.containsKey(alias)) {
291 throw new InvalidBallotException(String.format(
292 "Alias '%s' has threshold pubkey, but alias does not belong to any authority.", alias));
293 }
294 Optional<String> optSig = cfg.getValueString("threshold-pubkey-sigs", alias);
295 if (!optSig.isPresent()) {
296 throw new InvalidBallotException(String.format(
297 "no signature present for threshold pubkey from authority '%s'", alias));
298 }
299
300 KeyQueryResponseMessage m = new KeyQueryResponseMessage();
301 m.signature = EddsaSignature.fromString(optSig.get());
302 m.signedGuidKey = new KeyQueryResponseMessage.BallotPublicKey();
303 m.signedGuidKey.ballotGuid = getBallotGuid();
304 m.signedGuidKey.publicKey = ThresholdPublicKey.fromString(e.getValue());
305 }
306
245 } 307 }
246 308
247 /** 309 /**
@@ -270,6 +332,8 @@ public class Ballot {
270 } 332 }
271 digest.update(issuerPub.y); 333 digest.update(issuerPub.y);
272 digest.update(caPub.y); 334 digest.update(caPub.y);
335 digest.update(Longs.toByteArray(keygenStartTime.getSeconds()));
336 digest.update(Longs.toByteArray(keygenEndTime.getSeconds()));
273 digest.update(Longs.toByteArray(startTime.getSeconds())); 337 digest.update(Longs.toByteArray(startTime.getSeconds()));
274 digest.update(Longs.toByteArray(endTime.getSeconds())); 338 digest.update(Longs.toByteArray(endTime.getSeconds()));
275 digest.update(Longs.toByteArray(closingTime.getSeconds())); 339 digest.update(Longs.toByteArray(closingTime.getSeconds()));
@@ -278,14 +342,46 @@ public class Ballot {
278 } 342 }
279 343
280 /** 344 /**
345 * Get the threshold public key that the majority of authorities
346 * advertise. The majority key must have at least 'threshold' peers that
347 * advertise it in order to be valid
348 *
349 * @return the majority threshold public key
350 */
351 public ThresholdPublicKey getMajorityThresholdPublicKey() {
352 HashMap<ThresholdPublicKey,Integer> counts = Maps.newHashMap();
353 for (Map.Entry<String,KeyQueryResponseMessage> e : thresholdPublicKeys.entrySet()) {
354 ThresholdPublicKey pk = e.getValue().signedGuidKey.publicKey;
355 if (counts.containsKey(pk)) {
356 counts.put(pk, counts.get(pk) + 1);
357 } else {
358 counts.put(pk, 0);
359 }
360 }
361 int maxCount = 0;
362 ThresholdPublicKey bestKey = null;
363 for (Map.Entry<ThresholdPublicKey, Integer> e : counts.entrySet()) {
364 if (e.getValue() > maxCount) {
365 maxCount = e.getValue();
366 bestKey = e.getKey();
367 }
368 }
369 if (maxCount < threshold) {
370 return null;
371 }
372 return bestKey;
373 }
374
375 /**
281 * Encode the given choice permanently in the ballot. 376 * Encode the given choice permanently in the ballot.
282 * Also encodes the voter's public key. 377 * Also encodes the voter's public key.
283 * 378 *
284 * @param choice the choice to encode the ballot 379 * @param choice the choice to encode the ballot
285 * @param privateKey the private key to use for encoding 380 * @param voterPrivateKey the private key to use for encoding
286 */ 381 */
287 public void encodeChoice(String choice, EcdsaPrivateKey privateKey) { 382 public void encodeChoice(String choice, ThresholdPublicKey thresholdPublicKey,
288 choiceId = -1; 383 EcdsaPrivateKey voterPrivateKey) {
384 int choiceId = -1;
289 int i = 0; 385 int i = 0;
290 for (String possibleChoice : choices) { 386 for (String possibleChoice : choices) {
291 if (choice.equals(possibleChoice)) { 387 if (choice.equals(possibleChoice)) {
@@ -293,10 +389,12 @@ public class Ballot {
293 } 389 }
294 i++; 390 i++;
295 } 391 }
296 voterPub = privateKey.getPublicKey(); 392 voterPub = voterPrivateKey.getPublicKey();
297 if (choiceId == -1) { 393 if (choiceId < 0 || choiceId > 1) {
298 throw new InvalidBallotException(String.format("choice '%s' not valid", choice)); 394 throw new InvalidBallotException(String.format("choice '%s' not valid", choice));
299 } 395 }
396
397 encryptedVote = EncryptedVote.fromChoice(choiceId, thresholdPublicKey, voterPrivateKey);
300 } 398 }
301 399
302 /** 400 /**
@@ -310,6 +408,9 @@ public class Ballot {
310 cfg.setValueString("election", "GROUP", group); 408 cfg.setValueString("election", "GROUP", group);
311 cfg.setValueString("election", "CHOICES", Joiner.on("//").join(choices)); 409 cfg.setValueString("election", "CHOICES", Joiner.on("//").join(choices));
312 cfg.setValueString("election", "CA_PUB", caPub.toString()); 410 cfg.setValueString("election", "CA_PUB", caPub.toString());
411 cfg.setValueNumber("election", "THRESHOLD", threshold);
412 cfg.setValueNumber("election", "TIMESTAMP_KEYGEN_START", keygenStartTime.getSeconds());
413 cfg.setValueNumber("election", "TIMESTAMP_KEYGEN_END", keygenEndTime.getSeconds());
313 cfg.setValueNumber("election", "TIMESTAMP_START", startTime.getSeconds()); 414 cfg.setValueNumber("election", "TIMESTAMP_START", startTime.getSeconds());
314 cfg.setValueNumber("election", "TIMESTAMP_CLOSING", closingTime.getSeconds()); 415 cfg.setValueNumber("election", "TIMESTAMP_CLOSING", closingTime.getSeconds());
315 cfg.setValueNumber("election", "TIMESTAMP_QUERY", queryTime.getSeconds()); 416 cfg.setValueNumber("election", "TIMESTAMP_QUERY", queryTime.getSeconds());
@@ -332,8 +433,8 @@ public class Ballot {
332 cfg.setValueString("election", "ISSUER_PUB", issuerPub.toString()); 433 cfg.setValueString("election", "ISSUER_PUB", issuerPub.toString());
333 cfg.setValueString("election", "ISSUER_SIG", issuerSig.toString()); 434 cfg.setValueString("election", "ISSUER_SIG", issuerSig.toString());
334 } 435 }
335 if (-1 != choiceId) { 436 if (null != encryptedVote) {
336 cfg.setValueNumber("vote", "CHOICE_ID", choiceId); 437 encryptedVote.writeToConfiguration(cfg);
337 } 438 }
338 if (null != voterPub) { 439 if (null != voterPub) {
339 cfg.setValueString("vote", "VOTER_PUB", voterPub.toString()); 440 cfg.setValueString("vote", "VOTER_PUB", voterPub.toString());
@@ -341,6 +442,14 @@ public class Ballot {
341 if (null != groupCert) { 442 if (null != groupCert) {
342 groupCert.writeBallotConfig(cfg); 443 groupCert.writeBallotConfig(cfg);
343 } 444 }
445 System.out.println("thresh set when writing: " + thresholdPublicKeys.size());
446 for (Map.Entry<String,KeyQueryResponseMessage> e : thresholdPublicKeys.entrySet()) {
447 System.out.println("writing tresh");
448 cfg.setValueString("threshold-pubkeys", e.getKey(),
449 e.getValue().signedGuidKey.publicKey.toString());
450 cfg.setValueString("threshold-pubkey-sigs", e.getKey(),
451 e.getValue().signature.toString());
452 }
344 return cfg; 453 return cfg;
345 } 454 }
346 455
@@ -371,6 +480,10 @@ public class Ballot {
371 buf.append(group); 480 buf.append(group);
372 buf.append("\n"); 481 buf.append("\n");
373 482
483 buf.append("Threshold: ");
484 buf.append(threshold);
485 buf.append("\n");
486
374 buf.append("Start Time: "); 487 buf.append("Start Time: ");
375 buf.append(startTime.toFancyString()); 488 buf.append(startTime.toFancyString());
376 buf.append("\n"); 489 buf.append("\n");
@@ -389,7 +502,7 @@ public class Ballot {
389 502
390 buf.append("Choices:\n"); 503 buf.append("Choices:\n");
391 for (int i = 0; i < choices.size(); i++) { 504 for (int i = 0; i < choices.size(); i++) {
392 buf.append(""+(i+1)); 505 buf.append(i + 1);
393 buf.append(". '"); 506 buf.append(". '");
394 buf.append(choices.get(i)); 507 buf.append(choices.get(i));
395 buf.append("'\n"); 508 buf.append("'\n");
@@ -430,7 +543,7 @@ public class Ballot {
430 else { 543 else {
431 buf.append("ballot not submitted\n"); 544 buf.append("ballot not submitted\n");
432 } 545 }
433 if (choiceId != -1) { 546 if (encryptedVote != null) {
434 buf.append("choice selected\n"); 547 buf.append("choice selected\n");
435 } else { 548 } else {
436 buf.append("no choice selected\n"); 549 buf.append("no choice selected\n");
@@ -538,4 +651,22 @@ public class Ballot {
538 voterPub = groupCert.getMemberPublicKey(); 651 voterPub = groupCert.getMemberPublicKey();
539 this.groupCert = groupCert; 652 this.groupCert = groupCert;
540 } 653 }
654
655 /**
656 * Get the list of authorities that this ballot does not have a signature
657 * on the threshold key from.
658 */
659 public List<PeerIdentity> getRemainingKeyAuthorities() {
660 LinkedList<PeerIdentity> remaining = new LinkedList<PeerIdentity>();
661 for (Map.Entry<String,PeerIdentity> x : authorities.entrySet()) {
662 if (!thresholdPublicKeys.containsKey(x.getKey()))
663 remaining.add(x.getValue());
664 }
665 return remaining;
666 }
667
668 public void addThresholdPublicKey(PeerIdentity currentAuthority, KeyQueryResponseMessage m) {
669 String alias = authorities.inverse().get(currentAuthority);
670 thresholdPublicKeys.put(alias, m);
671 }
541} 672}
diff --git a/src/main/java/org/gnunet/voting/BallotTool.java b/src/main/java/org/gnunet/voting/BallotTool.java
index 0047d0e..c0767a4 100644
--- a/src/main/java/org/gnunet/voting/BallotTool.java
+++ b/src/main/java/org/gnunet/voting/BallotTool.java
@@ -30,14 +30,14 @@ import org.gnunet.identity.IdentityCallback;
30import org.gnunet.mesh.Mesh; 30import org.gnunet.mesh.Mesh;
31import org.gnunet.mesh.MeshRunabout; 31import org.gnunet.mesh.MeshRunabout;
32import org.gnunet.mesh.TunnelEndHandler; 32import org.gnunet.mesh.TunnelEndHandler;
33import org.gnunet.secretsharing.ThresholdPublicKey;
33import org.gnunet.testbed.CompressedConfig; 34import org.gnunet.testbed.CompressedConfig;
34import org.gnunet.util.AbsoluteTime; 35import org.gnunet.util.*;
35import org.gnunet.util.Configuration;
36import org.gnunet.util.PeerIdentity;
37import org.gnunet.util.Program;
38import org.gnunet.util.getopt.Argument; 36import org.gnunet.util.getopt.Argument;
39import org.gnunet.util.getopt.ArgumentAction; 37import org.gnunet.util.getopt.ArgumentAction;
40import org.gnunet.voting.messages.*; 38import org.gnunet.voting.messages.*;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41 41
42import java.io.File; 42import java.io.File;
43import java.io.FileOutputStream; 43import java.io.FileOutputStream;
@@ -50,6 +50,9 @@ import java.util.Random;
50 * Tool for creating, manipulating and submitting ballot files. 50 * Tool for creating, manipulating and submitting ballot files.
51 */ 51 */
52public class BallotTool extends Program { 52public class BallotTool extends Program {
53 private static final Logger logger = LoggerFactory
54 .getLogger(BallotTool.class);
55
53 @Argument( 56 @Argument(
54 shortname = "e", 57 shortname = "e",
55 longname = "ego", 58 longname = "ego",
@@ -107,6 +110,12 @@ public class BallotTool extends Program {
107 description = "incorporate the group cert into the ballot") 110 description = "incorporate the group cert into the ballot")
108 String groupCertFile = null; 111 String groupCertFile = null;
109 112
113 @Argument(
114 shortname = "k",
115 longname = "request-key",
116 action = ArgumentAction.SET,
117 description = "request the threshold public key from authorities")
118 boolean requestKey = false;
110 119
111 @Argument( 120 @Argument(
112 shortname = "t", 121 shortname = "t",
@@ -115,14 +124,35 @@ public class BallotTool extends Program {
115 description = "write a template ballot to the give ballot file") 124 description = "write a template ballot to the give ballot file")
116 boolean template = false; 125 boolean template = false;
117 126
127 /**
128 * The ego to use for the currently executing action.
129 */
118 private Identity.Ego ego; 130 private Identity.Ego ego;
119 131
132 /**
133 * The (possibly modified) ballot originally loaded
134 * from 'ballotFilename'
135 */
120 private Ballot ballot; 136 private Ballot ballot;
137
138 /**
139 * Filename to read the ballot from, and write modifications to.
140 */
121 private String ballotFilename; 141 private String ballotFilename;
122 142
143 /**
144 * Our handle to MESH.
145 */
123 private Mesh mesh; 146 private Mesh mesh;
147
148 /**
149 * A tunnel to 'currentAuthority' or null.
150 */
124 private Mesh.Tunnel tunnel; 151 private Mesh.Tunnel tunnel;
125 152
153 /**
154 * The authority we are currently communicating with.
155 */
126 private PeerIdentity currentAuthority; 156 private PeerIdentity currentAuthority;
127 157
128 /** 158 /**
@@ -131,10 +161,39 @@ public class BallotTool extends Program {
131 */ 161 */
132 private boolean tunnelCommunicationFinished; 162 private boolean tunnelCommunicationFinished;
133 163
164 private RelativeTime tunnelReconnectBackoff = RelativeTime.STD_BACKOFF;
165
134 public class BallotTunnelEndHandler implements TunnelEndHandler { 166 public class BallotTunnelEndHandler implements TunnelEndHandler {
135 @Override 167 @Override
136 public void onTunnelEnd(Mesh.Tunnel tunnel) { 168 public void onTunnelEnd(final Mesh.Tunnel tunnel) {
137 // FIXME 169 // FIXME: just re-running 'doCommands' is a bit of a hack
170 BallotTool.this.tunnel = null;
171 if (!tunnelCommunicationFinished) {
172 logger.warn("mesh tunnel disconnected, but operation not finished");
173 Scheduler.addDelayed(tunnelReconnectBackoff, new Scheduler.Task() {
174 @Override
175 public void run(Scheduler.RunContext ctx) {
176 doCommands();
177 }
178 });
179 tunnelReconnectBackoff = tunnelReconnectBackoff.backoff();
180 }
181 }
182 }
183
184 /**
185 * Destroy the tunnel to the authority as well
186 * as the mesh handle.
187 */
188 private void endMesh() {
189 tunnelCommunicationFinished = true;
190 if (null != tunnel) {
191 tunnel.destroy();
192 tunnel = null;
193 }
194 if (null != mesh) {
195 mesh.destroy();
196 mesh = null;
138 } 197 }
139 } 198 }
140 199
@@ -143,20 +202,18 @@ public class BallotTool extends Program {
143 System.out.println("ballot successfully registered"); 202 System.out.println("ballot successfully registered");
144 ballot.addRegistrationSignature(currentAuthority, m.registrationSignature); 203 ballot.addRegistrationSignature(currentAuthority, m.registrationSignature);
145 writeBallot(); 204 writeBallot();
146 tunnel.destroy(); 205 endMesh();
147 mesh.destroy();
148 } 206 }
149 207
150 public void visit(BallotRegisterFailureMessage m) { 208 public void visit(BallotRegisterFailureMessage m) {
151 System.out.println("registering failed: " + m.reason); 209 System.out.println("registering failed: " + m.reason);
152 tunnel.destroy(); 210 endMesh();
153 mesh.destroy(); 211 setReturnValue(1);
154 } 212 }
155
156 } 213 }
157 214
158 public class QueryReceiver extends MeshRunabout { 215 public class QueryReceiver extends MeshRunabout {
159 public void visit(QueryResponseMessage m) { 216 public void visit(ResultQueryResponseMessage m) {
160 if (m.results.length != ballot.choices.size()) { 217 if (m.results.length != ballot.choices.size()) {
161 System.out.println("failure to query result: malformed response"); 218 System.out.println("failure to query result: malformed response");
162 } else { 219 } else {
@@ -165,16 +222,30 @@ public class BallotTool extends Program {
165 System.out.println("'" + ballot.choices.get(i) + "': " + m.results[i]); 222 System.out.println("'" + ballot.choices.get(i) + "': " + m.results[i]);
166 } 223 }
167 } 224 }
225 endMesh();
226 }
168 227
169 tunnel.destroy(); 228 public void visit(ResultQueryFailureMessage m) {
170 mesh.destroy(); 229 System.out.println("failure to query result: " + m.reason);
230 endMesh();
231 setReturnValue(1);
171 } 232 }
233 }
234
172 235
173 public void visit(QueryFailureMessage m) { 236 public class PublicKeyReceiver extends MeshRunabout {
237 public void visit(KeyQueryResponseMessage m) {
238 System.out.println("got threshold public key!");
239 ballot.addThresholdPublicKey(currentAuthority, m);
240 writeBallot();
241 endMesh();
242 }
243 public void visit(KeyQueryFailureMessage m) {
174 System.out.println("failure to query result: " + m.reason); 244 System.out.println("failure to query result: " + m.reason);
175 tunnel.destroy(); 245 endMesh();
176 mesh.destroy(); 246 setReturnValue(1);
177 } 247 }
248
178 } 249 }
179 250
180 public class SubmitReceiver extends MeshRunabout { 251 public class SubmitReceiver extends MeshRunabout {
@@ -183,19 +254,18 @@ public class BallotTool extends Program {
183 System.out.println("vote successfully submitted"); 254 System.out.println("vote successfully submitted");
184 ballot.addConfirmation(currentAuthority, m.confirmationSig); 255 ballot.addConfirmation(currentAuthority, m.confirmationSig);
185 writeBallot(); 256 writeBallot();
186 tunnel.destroy(); 257 endMesh();
187 mesh.destroy();
188 } 258 }
189 259
190 public void visit(SubmitFailureMessage m) { 260 public void visit(SubmitFailureMessage m) {
191 System.out.println("vote not submitted: " + m.reason); 261 System.out.println("vote not submitted: " + m.reason);
192 if (m.authorityTime != null) { 262 if (m.signedAuthorityTime != null) {
193 // FIXME: verify 263 // FIXME: verify
194 System.out.println("authority time: " + 264 System.out.println("authority time: " +
195 AbsoluteTime.fromNetwork(m.authorityTime.innerMessage).toFancyString()); 265 AbsoluteTime.fromNetwork(m.signedAuthorityTime.time).toFancyString());
196 } 266 }
197 tunnel.destroy(); 267 endMesh();
198 mesh.destroy(); 268 setReturnValue(1);
199 } 269 }
200 } 270 }
201 271
@@ -220,6 +290,9 @@ public class BallotTool extends Program {
220 } 290 }
221 } 291 }
222 292
293 /**
294 * Write the ballot back to disk.
295 */
223 private void writeBallot() { 296 private void writeBallot() {
224 try { 297 try {
225 Files.write(ballot.serialize(), new File(ballotFilename), Charsets.UTF_8); 298 Files.write(ballot.serialize(), new File(ballotFilename), Charsets.UTF_8);
@@ -228,6 +301,9 @@ public class BallotTool extends Program {
228 } 301 }
229 } 302 }
230 303
304 /**
305 * Actually execute the action the user requested.
306 */
231 void doCommands() { 307 void doCommands() {
232 if (null != groupCertFile) { 308 if (null != groupCertFile) {
233 Configuration groupCertConfig = new Configuration(); 309 Configuration groupCertConfig = new Configuration();
@@ -270,7 +346,13 @@ public class BallotTool extends Program {
270 setReturnValue(1); 346 setReturnValue(1);
271 return; 347 return;
272 } 348 }
273 ballot.encodeChoice(select, ego.getPrivateKey()); 349 ThresholdPublicKey thresholdPublicKey = ballot.getMajorityThresholdPublicKey();
350 if (null == thresholdPublicKey) {
351 System.err.println("no threshold public key in ballot");
352 setReturnValue(1);
353 return;
354 }
355 ballot.encodeChoice(select, thresholdPublicKey, ego.getPrivateKey());
274 writeBallot(); 356 writeBallot();
275 return; 357 return;
276 } 358 }
@@ -297,7 +379,10 @@ public class BallotTool extends Program {
297 m.groupCertExpiration = ballot.groupCert.getExpiration().asMessage(); 379 m.groupCertExpiration = ballot.groupCert.getExpiration().asMessage();
298 m.groupCert = ballot.groupCert.getSignature(); 380 m.groupCert = ballot.groupCert.getSignature();
299 m.ballotGuid = ballot.getBallotGuid(); 381 m.ballotGuid = ballot.getBallotGuid();
300 m.choiceId = ballot.choiceId; 382 if (null == ballot.encryptedVote) {
383 throw new InvalidBallotException("no encrypted vote in ballot");
384 }
385 m.encryptedVote = ballot.encryptedVote;
301 tunnel.send(m); 386 tunnel.send(m);
302 return; 387 return;
303 } 388 }
@@ -313,15 +398,33 @@ public class BallotTool extends Program {
313 return; 398 return;
314 } 399 }
315 Random r = new Random(); 400 Random r = new Random();
316 PeerIdentity authority = remainingAuthorities.get(r.nextInt(remainingAuthorities.size())); 401 currentAuthority = remainingAuthorities.get(r.nextInt(remainingAuthorities.size()));
317 System.out.println("querying authority" + authority.toString()); 402 System.out.println("querying authority " + currentAuthority.toString());
318 mesh = new Mesh(cfg, new BallotTunnelEndHandler(), new QueryReceiver()); 403 mesh = new Mesh(cfg, new BallotTunnelEndHandler(), new QueryReceiver());
319 tunnel = mesh.createTunnel(authority, TallyAuthorityDaemon.MESH_PORT, true, true, null); 404 tunnel = mesh.createTunnel(currentAuthority, TallyAuthorityDaemon.MESH_PORT, true, true, null);
320 QueryMessage m = new QueryMessage(); 405 ResultQueryMessage m = new ResultQueryMessage();
321 m.ballotGUID = ballot.getBallotGuid(); 406 m.ballotGuid = ballot.getBallotGuid();
322 tunnel.send(m); 407 tunnel.send(m);
323 return; 408 return;
324 } 409 }
410 if (requestKey) {
411 List<PeerIdentity> remainingAuthorities = ballot.getRemainingKeyAuthorities();
412 if (remainingAuthorities.isEmpty()) {
413 System.err.println("all authorities already signed group key");
414 return;
415 }
416 Random r = new Random();
417 currentAuthority = remainingAuthorities.get(r.nextInt(remainingAuthorities.size()));
418 System.out.println("asking authority for key " + currentAuthority.toString());
419 mesh = new Mesh(cfg, new BallotTunnelEndHandler(), new PublicKeyReceiver());
420 tunnel = mesh.createTunnel(currentAuthority, TallyAuthorityDaemon.MESH_PORT, true, true, null);
421 KeyQueryMessage m = new KeyQueryMessage();
422 m.ballotGuid = ballot.getBallotGuid();
423 tunnel.send(m);
424 return;
425 }
426 setReturnValue(1);
427 System.err.println("no action specified");
325 } 428 }
326 429
327 @Override 430 @Override
@@ -342,7 +445,15 @@ public class BallotTool extends Program {
342 System.err.println("ballot file does not exist"); 445 System.err.println("ballot file does not exist");
343 return; 446 return;
344 } 447 }
345 ballot = new Ballot(ballotFilename); 448 try {
449 ballot = new Ballot(ballotFilename);
450 } catch (InvalidBallotException e) {
451 System.err.println("Invalid or incomplete ballot:");
452 System.err.println(e.getMessage());
453 setReturnValue(1);
454 return;
455 }
456 // if there's an ego name, look it up ...
346 if (null != egoName) { 457 if (null != egoName) {
347 Identity.lookup(getConfiguration(), egoName, new IdentityCallback() { 458 Identity.lookup(getConfiguration(), egoName, new IdentityCallback() {
348 @Override 459 @Override
diff --git a/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java b/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
index 5fd21ba..6fa6b8e 100644
--- a/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
+++ b/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
@@ -30,6 +30,9 @@ import org.gnunet.construct.NestedMessage;
30import org.gnunet.construct.UInt32; 30import org.gnunet.construct.UInt32;
31import org.gnunet.mesh.Mesh; 31import org.gnunet.mesh.Mesh;
32import org.gnunet.mesh.MeshRunabout; 32import org.gnunet.mesh.MeshRunabout;
33import org.gnunet.secretsharing.*;
34import org.gnunet.secretsharing.callbacks.DecryptCallback;
35import org.gnunet.secretsharing.callbacks.SecretReadyCallback;
33import org.gnunet.testbed.CompressedConfig; 36import org.gnunet.testbed.CompressedConfig;
34import org.gnunet.util.*; 37import org.gnunet.util.*;
35import org.gnunet.util.crypto.*; 38import org.gnunet.util.crypto.*;
@@ -37,27 +40,36 @@ import org.gnunet.voting.messages.*;
37import org.slf4j.Logger; 40import org.slf4j.Logger;
38import org.slf4j.LoggerFactory; 41import org.slf4j.LoggerFactory;
39 42
43import java.math.BigInteger;
40import java.util.*; 44import java.util.*;
41 45
42 46
43/** 47/**
44 * Daemon that is responsible for counting votes. 48 * Daemon that is responsible for accepting and counting votes.
45 */ 49 */
46public class TallyAuthorityDaemon extends Program { 50public class TallyAuthorityDaemon extends Program {
47 private static final Logger logger = LoggerFactory 51 private static final Logger logger = LoggerFactory
48 .getLogger(TallyAuthorityDaemon.class); 52 .getLogger(TallyAuthorityDaemon.class);
49 53
54 /**
55 * Mesh port used to connect to to the tally authority daemon.
56 */
50 public static final int MESH_PORT = 1002; 57 public static final int MESH_PORT = 1002;
58
59 /**
60 * Mesh handle.
61 */
51 private Mesh mesh; 62 private Mesh mesh;
63
64 /**
65 * Private key of the local peer.
66 */
52 private EddsaPrivateKey authorityPrivateKey; 67 private EddsaPrivateKey authorityPrivateKey;
53 private EddsaPublicKey authorityPublicKey;
54 68
55 public static class Vote implements Message { 69 /**
56 @UInt32 70 * Public key of the local peer.
57 public int choice; 71 */
58 @NestedMessage 72 private EddsaPublicKey authorityPublicKey;
59 public EcdsaPublicKey voterPub;
60 }
61 73
62 /** 74 /**
63 * All elections known to this authority 75 * All elections known to this authority
@@ -74,24 +86,53 @@ public class TallyAuthorityDaemon extends Program {
74 Ballot ballot; 86 Ballot ballot;
75 87
76 /** 88 /**
77 * Set of voters that have submitted their ballot. 89 * The threshold crypto share, null if the key has not yet been
90 * established.
91 */
92 Share share;
93
94 /**
95 * A voter is in this set if its vote has been in the consensus.
96 */
97 Set<EcdsaPublicKey> countedVoters = new HashSet<EcdsaPublicKey>();
98
99 /**
100 * Key generation session.
78 */ 101 */
79 Set<EcdsaPublicKey> voters = new HashSet<EcdsaPublicKey>(); 102 KeyGeneration keyGeneration;
80 103
81 /** 104 /**
82 * Consensus with the other authorities on the set of ballots. 105 * Consensus with the other authorities on the set of ballots.
83 */ 106 */
84 Consensus consensus; 107 Consensus consensus;
85 108
109 /**
110 * Are we done with the vote consensus?
111 */
86 boolean consensusDone; 112 boolean consensusDone;
87 113
88 /** 114 /**
115 * Product of all encrypted votes (mod q), used to compute the final tally.
116 */
117 Ciphertext voteProduct;
118
119 /**
89 * Maping from choice to number of votes for that choice. 120 * Maping from choice to number of votes for that choice.
121 * In our currently simplified implementation, tally.length is always 2.
122 * If the tally has not been counted yet, 'tally' is null.
90 */ 123 */
91 int[] tally; 124 long[] tally;
125
126 /**
127 * The decrypt session.
128 */
129 Decryption decryption;
92 } 130 }
93 131
94 static class ElectionConsensusConclude implements ConsensusCallback { 132 /**
133 * Callbacks for the vote consensus.
134 */
135 class ElectionConsensusConclude implements ConsensusCallback {
95 private final ElectionState electionState; 136 private final ElectionState electionState;
96 137
97 public ElectionConsensusConclude(ElectionState electionState) { 138 public ElectionConsensusConclude(ElectionState electionState) {
@@ -100,22 +141,39 @@ public class TallyAuthorityDaemon extends Program {
100 @Override 141 @Override
101 public void onElement(ConsensusElement element) { 142 public void onElement(ConsensusElement element) {
102 System.out.println("got element from consensus"); 143 System.out.println("got element from consensus");
103 Vote vote = Construct.parseAs(element.data, Vote.class); 144 EncryptedVote vote = Construct.parseAs(element.data, EncryptedVote.class);
104 if (vote.choice >= 0 && vote.choice < electionState.tally.length) { 145 electionState.voteProduct = electionState.voteProduct.multiply(vote.v);
105 electionState.tally[vote.choice] += 1;
106 }
107 } 146 }
108 147
109 @Override 148 @Override
110 public void onDone() { 149 public void onDone() {
111 System.out.println("got element from consensus"); 150 System.out.println("consensus concluded");
112 electionState.consensusDone = true; 151 electionState.consensusDone = true;
113 electionState.consensus.destroy(); 152 electionState.consensus.destroy();
114 electionState.consensus = null; 153 electionState.consensus = null;
154
155 electionState.decryption = new Decryption(
156 getConfiguration(),
157 electionState.share,
158 electionState.voteProduct,
159 electionState.ballot.concludeTime,
160 electionState.ballot.queryTime,
161 new DecryptCallback() {
162 @Override
163 public void onResult(Plaintext plaintext) {
164 logger.info("got decypt result");
165 long l = electionState.countedVoters.size();
166 long t = plaintext.bruteForceDiscreteLog(l);
167 logger.info("brute-forced result");
168 electionState.tally = new long[2];
169 electionState.tally[0] = (l - t) / 2;
170 electionState.tally[1] = l - electionState.tally[0];
171 }
172 });
115 } 173 }
116 } 174 }
117 175
118 static class ConsensusConcludeTask implements Scheduler.Task { 176 class ConsensusConcludeTask implements Scheduler.Task {
119 /** 177 /**
120 * Which election on this authority is the consensus conclude for? 178 * Which election on this authority is the consensus conclude for?
121 */ 179 */
@@ -130,9 +188,28 @@ public class TallyAuthorityDaemon extends Program {
130 } 188 }
131 } 189 }
132 190
133 private EddsaSignedMessage<AbsoluteTimeMessage> getTimeSigMessage() { 191 static class SecretReady implements SecretReadyCallback {
134 AbsoluteTimeMessage m = AbsoluteTime.now().asMessage(); 192 private final ElectionState electionState;
135 return EddsaSignedMessage.signMessage(m, 0, authorityPrivateKey, authorityPublicKey); 193
194 public SecretReady(ElectionState electionState) {
195 this.electionState = electionState;
196 }
197
198 @Override
199 public void onSecretReady(Share share) {
200 electionState.keyGeneration = null;
201 electionState.share = share;
202 }
203 }
204
205 private SubmitFailureMessage.SignedAuthorityTime getTimeSigMessage() {
206 SubmitFailureMessage.SignedAuthorityTime tm = new SubmitFailureMessage.SignedAuthorityTime();
207 // FIXME!
208 tm.purpose = 0;
209 tm.time = AbsoluteTime.now().asMessage();
210 tm.signature = authorityPrivateKey.sign(authorityPublicKey, tm.purpose,
211 Construct.toBinary(tm.time));
212 return tm;
136 } 213 }
137 214
138 private class TallyMeshReceiver extends MeshRunabout { 215 private class TallyMeshReceiver extends MeshRunabout {
@@ -143,32 +220,23 @@ public class TallyAuthorityDaemon extends Program {
143 SubmitFailureMessage fm = new SubmitFailureMessage(); 220 SubmitFailureMessage fm = new SubmitFailureMessage();
144 fm.reason = "no matching ballot found"; 221 fm.reason = "no matching ballot found";
145 getSender().send(fm); 222 getSender().send(fm);
146 } else if (m.choiceId < 0 || m.choiceId >= electionState.tally.length) {
147 SubmitFailureMessage fm = new SubmitFailureMessage();
148 fm.reason = "invalid vote";
149 getSender().send(fm);
150 } else if (electionState.voters.contains(m.voterPub)) {
151 SubmitFailureMessage fm = new SubmitFailureMessage();
152 fm.reason = "duplicate vote detected";
153 getSender().send(fm);
154 } else if (!electionState.ballot.startTime.isDue()) { 223 } else if (!electionState.ballot.startTime.isDue()) {
155 SubmitFailureMessage fm = new SubmitFailureMessage(); 224 SubmitFailureMessage fm = new SubmitFailureMessage();
156 fm.reason = "too early to submit vote"; 225 fm.reason = "too early to submit vote";
157 fm.authorityTime = getTimeSigMessage(); 226 fm.signedAuthorityTime = getTimeSigMessage();
158 getSender().send(fm); 227 getSender().send(fm);
159 } else if (electionState.ballot.closingTime.isDue()) { 228 } else if (electionState.ballot.closingTime.isDue()) {
160 SubmitFailureMessage fm = new SubmitFailureMessage(); 229 SubmitFailureMessage fm = new SubmitFailureMessage();
161 fm.reason = "too late to submit vote"; 230 fm.reason = "too late to submit vote";
162 fm.authorityTime = getTimeSigMessage(); 231 fm.signedAuthorityTime = getTimeSigMessage();
163 getSender().send(fm); 232 getSender().send(fm);
164 } 233 }
165 // FIXME: check signatures of voter and CA 234 // FIXME: check signatures of voter and CA
166 else { 235 else {
167 electionState.voters.add(m.voterPub); 236 // we do *not* check for duplicate votes here,
168 Vote vote = new Vote(); 237 // as consensus takes care of this, and there is no harm in sending
169 vote.choice = m.choiceId; 238 // exact duplicates
170 vote.voterPub = m.voterPub; 239 byte[] elem = Construct.toBinary(m.encryptedVote);
171 byte[] elem = Construct.toBinary(vote);
172 electionState.consensus.insertElement(new ConsensusElement(elem, 0)); 240 electionState.consensus.insertElement(new ConsensusElement(elem, 0));
173 SubmitSuccessMessage sm = new SubmitSuccessMessage(); 241 SubmitSuccessMessage sm = new SubmitSuccessMessage();
174 sm.confirmationSig = EddsaSignature.randomGarbage(); 242 sm.confirmationSig = EddsaSignature.randomGarbage();
@@ -200,11 +268,11 @@ public class TallyAuthorityDaemon extends Program {
200 return; 268 return;
201 } 269 }
202 ElectionState electionState = new ElectionState(); 270 ElectionState electionState = new ElectionState();
203 electionState.tally = new int[b.choices.size()];
204 electionState.ballot = b; 271 electionState.ballot = b;
205 PeerIdentity[] ids = new PeerIdentity[b.getAuthorities().size()]; 272 PeerIdentity[] ids = new PeerIdentity[b.getAuthorities().size()];
206 ids = b.getAuthorities().toArray(ids); 273 ids = b.getAuthorities().toArray(ids);
207 electionState.consensus = new Consensus(getConfiguration(), 274 electionState.consensus = new Consensus(
275 getConfiguration(),
208 ids, 276 ids,
209 b.getBallotGuid(), 277 b.getBallotGuid(),
210 electionState.ballot.closingTime, 278 electionState.ballot.closingTime,
@@ -218,37 +286,83 @@ public class TallyAuthorityDaemon extends Program {
218 logger.info("concluding in {}", b.closingTime.getRemaining().getSeconds()); 286 logger.info("concluding in {}", b.closingTime.getRemaining().getSeconds());
219 Scheduler.addDelayed(b.closingTime.getRemaining(), t); 287 Scheduler.addDelayed(b.closingTime.getRemaining(), t);
220 } 288 }
289 // we hash the GUID a second time, so that there's no
290 // collision with the consensus (as secretsharing also uses consensus internally)
291 electionState.keyGeneration = new KeyGeneration(
292 getConfiguration(),
293 ids,
294 HashCode.hash(b.getBallotGuid().data),
295 electionState.ballot.keygenStartTime,
296 electionState.ballot.keygenEndTime,
297 electionState.ballot.threshold, new SecretReady(electionState));
221 elections.put(guid, electionState); 298 elections.put(guid, electionState);
299
222 BallotRegisterSuccessMessage rm = new BallotRegisterSuccessMessage(); 300 BallotRegisterSuccessMessage rm = new BallotRegisterSuccessMessage();
223 rm.registrationSignature = EddsaSignature.randomGarbage(); 301 rm.registrationSignature = EddsaSignature.randomGarbage();
224 getSender().send(rm); 302 getSender().send(rm);
225
226 } 303 }
227 304
228 public void visit(QueryMessage m) { 305 public void visit(ResultQueryMessage m) {
229 ElectionState electionState = elections.get(m.ballotGUID); 306 logger.debug("got result query message");
307 ElectionState electionState = elections.get(m.ballotGuid);
230 if (null == electionState) { 308 if (null == electionState) {
231 QueryFailureMessage rm = new QueryFailureMessage(); 309 ResultQueryFailureMessage rm = new ResultQueryFailureMessage();
232 rm.reason = "no matching ballot found"; 310 rm.reason = "no matching ballot found";
233 getSender().send(rm); 311 getSender().send(rm);
234 } else { 312 } else {
235 if (!electionState.consensusDone) { 313 if (!electionState.ballot.queryTime.isDue()) {
236 QueryFailureMessage rm = new QueryFailureMessage(); 314 ResultQueryFailureMessage rm = new ResultQueryFailureMessage();
237 rm.reason = "consensus not finished (sorry...)"; 315 rm.reason = "result query not allowed yet";
238 getSender().send(rm); 316 getSender().send(rm);
239 } 317 }
240 else if (electionState.ballot.queryTime.isDue()) { 318 else if (null == electionState.tally) {
241 QueryResponseMessage rm = new QueryResponseMessage(); 319 ResultQueryFailureMessage rm = new ResultQueryFailureMessage();
242 rm.results = electionState.tally; 320 rm.reason = "tally not yet available";
243 getSender().send(rm); 321 getSender().send(rm);
244 } else { 322 }
245 QueryFailureMessage rm = new QueryFailureMessage(); 323 else {
246 rm.reason = "result query not allowed yet"; 324 ResultQueryResponseMessage rm = new ResultQueryResponseMessage();
325 rm.results = electionState.tally;
247 getSender().send(rm); 326 getSender().send(rm);
248 } 327 }
249 } 328 }
250 getSender().receiveDone(); 329 getSender().receiveDone();
251 } 330 }
331
332 public void visit(KeyQueryMessage m) {
333 logger.debug("got key query message");
334 getSender().receiveDone();
335 ElectionState electionState = elections.get(m.ballotGuid);
336 if (null == electionState) {
337 KeyQueryFailureMessage rm = new KeyQueryFailureMessage();
338 rm.reason = "no matching ballot found";
339 getSender().send(rm);
340 return;
341 }
342 if (!electionState.ballot.keygenEndTime.isDue()) {
343 KeyQueryFailureMessage rm = new KeyQueryFailureMessage();
344 rm.reason = "key query not allowed yet";
345 getSender().send(rm);
346 return;
347 }
348 if (null == electionState.share) {
349 KeyQueryFailureMessage rm = new KeyQueryFailureMessage();
350 rm.reason = "key not yet established";
351 getSender().send(rm);
352 return;
353 }
354 KeyQueryResponseMessage.BallotPublicKey ballotPublicKey = new KeyQueryResponseMessage.BallotPublicKey();
355 ballotPublicKey.ballotGuid = electionState.ballot.getBallotGuid();
356 ballotPublicKey.publicKey = electionState.share.publicKey;
357
358 KeyQueryResponseMessage rm = new KeyQueryResponseMessage();
359 rm.signedGuidKey = ballotPublicKey;
360 // FIXME!
361 rm.purpose = 0;
362 rm.signature = authorityPrivateKey.sign(authorityPublicKey, rm.purpose,
363 Construct.toBinary(rm.signedGuidKey));
364 getSender().send(rm);
365 }
252 } 366 }
253 367
254 public TallyAuthorityDaemon(String[] args) { 368 public TallyAuthorityDaemon(String[] args) {
@@ -266,5 +380,15 @@ public class TallyAuthorityDaemon extends Program {
266 public void run() { 380 public void run() {
267 logger.info("running tally daemon"); 381 logger.info("running tally daemon");
268 mesh = new Mesh(getConfiguration(), null, null, new TallyMeshReceiver(), MESH_PORT); 382 mesh = new Mesh(getConfiguration(), null, null, new TallyMeshReceiver(), MESH_PORT);
383
384 Scheduler.addDelayed(RelativeTime.FOREVER, new Scheduler.Task() {
385 @Override
386 public void run(Scheduler.RunContext ctx) {
387 if (null != mesh) {
388 mesh.destroy();
389 mesh = null;
390 }
391 }
392 });
269 } 393 }
270} 394}
diff --git a/src/main/java/org/gnunet/voting/messages/KeyQueryFailureMessage.java b/src/main/java/org/gnunet/voting/messages/KeyQueryFailureMessage.java
new file mode 100644
index 0000000..ae48bad
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/messages/KeyQueryFailureMessage.java
@@ -0,0 +1,11 @@
1package org.gnunet.voting.messages;
2
3import org.gnunet.construct.UnionCase;
4import org.gnunet.construct.ZeroTerminatedString;
5import org.gnunet.util.GnunetMessage;
6
7@UnionCase(42015)
8public class KeyQueryFailureMessage implements GnunetMessage.Body {
9 @ZeroTerminatedString
10 public String reason;
11}
diff --git a/src/main/java/org/gnunet/voting/messages/KeyQueryMessage.java b/src/main/java/org/gnunet/voting/messages/KeyQueryMessage.java
new file mode 100644
index 0000000..37da763
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/messages/KeyQueryMessage.java
@@ -0,0 +1,12 @@
1package org.gnunet.voting.messages;
2
3import org.gnunet.construct.NestedMessage;
4import org.gnunet.construct.UnionCase;
5import org.gnunet.util.GnunetMessage;
6import org.gnunet.util.HashCode;
7
8@UnionCase(42013)
9public class KeyQueryMessage implements GnunetMessage.Body {
10 @NestedMessage
11 public HashCode ballotGuid;
12}
diff --git a/src/main/java/org/gnunet/voting/messages/KeyQueryResponseMessage.java b/src/main/java/org/gnunet/voting/messages/KeyQueryResponseMessage.java
new file mode 100644
index 0000000..c94332a
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/messages/KeyQueryResponseMessage.java
@@ -0,0 +1,51 @@
1/*
2 This file is part of GNUnet.
3 (C) 2014 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.voting.messages;
22
23import org.gnunet.construct.Message;
24import org.gnunet.construct.NestedMessage;
25import org.gnunet.construct.UInt32;
26import org.gnunet.construct.UnionCase;
27import org.gnunet.secretsharing.ThresholdPublicKey;
28import org.gnunet.util.GnunetMessage;
29import org.gnunet.util.HashCode;
30import org.gnunet.util.crypto.EddsaSignature;
31
32@UnionCase(42014)
33public class KeyQueryResponseMessage implements GnunetMessage.Body {
34
35 public static class BallotPublicKey implements Message {
36 @NestedMessage
37 public HashCode ballotGuid;
38
39 @NestedMessage
40 public ThresholdPublicKey publicKey;
41 }
42
43 @NestedMessage
44 public EddsaSignature signature;
45
46 @UInt32
47 public int purpose;
48
49 @NestedMessage
50 public BallotPublicKey signedGuidKey;
51}
diff --git a/src/main/java/org/gnunet/voting/messages/ResultQueryFailureMessage.java b/src/main/java/org/gnunet/voting/messages/ResultQueryFailureMessage.java
new file mode 100644
index 0000000..9b9ef49
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/messages/ResultQueryFailureMessage.java
@@ -0,0 +1,31 @@
1/*
2 This file is part of GNUnet.
3 (C) 2012, 2013 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21package org.gnunet.voting.messages;
22
23import org.gnunet.construct.UnionCase;
24import org.gnunet.construct.ZeroTerminatedString;
25import org.gnunet.util.GnunetMessage;
26
27@UnionCase(42009)
28public class ResultQueryFailureMessage implements GnunetMessage.Body {
29 @ZeroTerminatedString
30 public String reason;
31}
diff --git a/src/main/java/org/gnunet/voting/messages/ResultQueryMessage.java b/src/main/java/org/gnunet/voting/messages/ResultQueryMessage.java
new file mode 100644
index 0000000..13f0b04
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/messages/ResultQueryMessage.java
@@ -0,0 +1,12 @@
1package org.gnunet.voting.messages;
2
3import org.gnunet.construct.NestedMessage;
4import org.gnunet.construct.UnionCase;
5import org.gnunet.util.GnunetMessage;
6import org.gnunet.util.HashCode;
7
8@UnionCase(42005)
9public class ResultQueryMessage implements GnunetMessage.Body {
10 @NestedMessage
11 public HashCode ballotGuid;
12}
diff --git a/src/main/java/org/gnunet/voting/messages/ResultQueryResponseMessage.java b/src/main/java/org/gnunet/voting/messages/ResultQueryResponseMessage.java
new file mode 100644
index 0000000..96ad4b5
--- /dev/null
+++ b/src/main/java/org/gnunet/voting/messages/ResultQueryResponseMessage.java
@@ -0,0 +1,12 @@
1package org.gnunet.voting.messages;
2
3
4import org.gnunet.construct.*;
5import org.gnunet.util.GnunetMessage;
6import org.gnunet.util.HashCode;
7
8@UnionCase(42006)
9public class ResultQueryResponseMessage implements GnunetMessage.Body {
10 @IntegerFill(signed = false, bitSize = 32)
11 public long[] results;
12}
diff --git a/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java b/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java
index 50751fb..9aec8b2 100644
--- a/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java
+++ b/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java
@@ -22,17 +22,27 @@ package org.gnunet.voting.messages;
22 22
23 23
24import org.gnunet.construct.NestedMessage; 24import org.gnunet.construct.NestedMessage;
25import org.gnunet.construct.UInt32;
25import org.gnunet.construct.UnionCase; 26import org.gnunet.construct.UnionCase;
26import org.gnunet.construct.ZeroTerminatedString; 27import org.gnunet.construct.ZeroTerminatedString;
27import org.gnunet.util.AbsoluteTimeMessage; 28import org.gnunet.util.AbsoluteTimeMessage;
28import org.gnunet.util.GnunetMessage; 29import org.gnunet.util.GnunetMessage;
29import org.gnunet.util.crypto.EcdsaSignedMessage; 30import org.gnunet.util.crypto.EddsaSignature;
30import org.gnunet.util.crypto.EddsaSignedMessage;
31 31
32@UnionCase(42010) 32@UnionCase(42010)
33public class SubmitFailureMessage implements GnunetMessage.Body { 33public class SubmitFailureMessage implements GnunetMessage.Body {
34 public static class SignedAuthorityTime {
35 @NestedMessage
36 public EddsaSignature signature;
37 @UInt32
38 public int purpose;
39 @NestedMessage
40 public AbsoluteTimeMessage time;
41 }
42
34 @ZeroTerminatedString 43 @ZeroTerminatedString
35 public String reason; 44 public String reason;
45
36 @NestedMessage(optional = true) 46 @NestedMessage(optional = true)
37 public EddsaSignedMessage<AbsoluteTimeMessage> authorityTime; 47 public SignedAuthorityTime signedAuthorityTime;
38} 48}
diff --git a/src/main/java/org/gnunet/voting/messages/SubmitMessage.java b/src/main/java/org/gnunet/voting/messages/SubmitMessage.java
index 52bf894..342aa0b 100644
--- a/src/main/java/org/gnunet/voting/messages/SubmitMessage.java
+++ b/src/main/java/org/gnunet/voting/messages/SubmitMessage.java
@@ -7,6 +7,7 @@ import org.gnunet.util.*;
7import org.gnunet.util.crypto.EcdsaPublicKey; 7import org.gnunet.util.crypto.EcdsaPublicKey;
8import org.gnunet.util.crypto.EcdsaSignature; 8import org.gnunet.util.crypto.EcdsaSignature;
9import org.gnunet.util.crypto.EddsaSignature; 9import org.gnunet.util.crypto.EddsaSignature;
10import org.gnunet.voting.EncryptedVote;
10 11
11/** 12/**
12 * Message send by the voter to the election authority to submit a vote. 13 * Message send by the voter to the election authority to submit a vote.
@@ -33,10 +34,12 @@ public class SubmitMessage implements GnunetMessage.Body {
33 */ 34 */
34 @NestedMessage 35 @NestedMessage
35 public AbsoluteTimeMessage groupCertExpiration; 36 public AbsoluteTimeMessage groupCertExpiration;
37
36 /** 38 /**
37 * The actual vote. 39 * The encrypted vote, including zero knowledge proofs
38 * FIXME: this will be encrypted! 40 * for correctness.
41 * FIXME: wrap in a signature container
39 */ 42 */
40 @UInt32 43 @NestedMessage
41 public int choiceId; 44 public EncryptedVote encryptedVote;
42} 45}