diff options
author | Florian Dold <florian.dold@gmail.com> | 2014-02-25 11:15:15 +0000 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2014-02-25 11:15:15 +0000 |
commit | 85d0048c22271fac371729ba7b14417a28bb6b48 (patch) | |
tree | de231f5453baed5b406bb88b19496d16530f3850 /src/main/java/org/gnunet | |
parent | 989f297285b843cb631836bf42e47f25fb567418 (diff) | |
download | gnunet-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')
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; | |||
28 | import org.gnunet.util.HashCode; | 28 | import 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) |
38 | public class ClientGetStopMessage implements GnunetMessage.Body { | 35 | public 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 | |||
21 | package org.gnunet.mesh; | ||
22 | |||
23 | import org.gnunet.construct.FixedSizeIntegerArray; | ||
24 | import org.gnunet.construct.UInt32; | ||
25 | import org.gnunet.construct.UnionCase; | ||
26 | import 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) | ||
37 | public 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 | ||
23 | import org.gnunet.construct.FixedSizeIntegerArray; | 23 | import org.gnunet.construct.FixedSizeIntegerArray; |
24 | import org.gnunet.construct.Message; | 24 | import org.gnunet.construct.Message; |
25 | import org.gnunet.util.BigIntegers; | ||
26 | import org.gnunet.util.Strings; | ||
27 | |||
28 | import 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; | |||
23 | import com.google.common.base.Preconditions; | 23 | import com.google.common.base.Preconditions; |
24 | import org.gnunet.construct.FixedSizeIntegerArray; | 24 | import org.gnunet.construct.FixedSizeIntegerArray; |
25 | import org.gnunet.construct.Message; | 25 | import org.gnunet.construct.Message; |
26 | import org.gnunet.util.BigIntegers; | ||
26 | 27 | ||
27 | import java.math.BigInteger; | 28 | import java.math.BigInteger; |
28 | import java.security.SecureRandom; | 29 | import 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 | ||
23 | import org.gnunet.construct.FixedSizeIntegerArray; | 23 | import org.gnunet.construct.FixedSizeIntegerArray; |
24 | import org.gnunet.construct.Message; | 24 | import org.gnunet.construct.Message; |
25 | import org.gnunet.util.Strings; | ||
26 | |||
27 | import java.util.Arrays; | ||
25 | 28 | ||
26 | /** | 29 | /** |
27 | * Threshold public key. | 30 | * Threshold public key. |
28 | */ | 31 | */ |
29 | public class ThresholdPublicKey implements Message { | 32 | public 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 | ||
23 | package org.gnunet.secretsharing.messages; | 21 | package org.gnunet.secretsharing.messages; |
@@ -29,7 +27,8 @@ import org.gnunet.secretsharing.Plaintext; | |||
29 | import org.gnunet.util.GnunetMessage; | 27 | import 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) |
35 | public class DecryptDoneMessage implements GnunetMessage.Body{ | 34 | public 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 | |||
21 | package org.gnunet.util; | ||
22 | |||
23 | import com.google.common.base.Preconditions; | ||
24 | |||
25 | import java.math.BigInteger; | ||
26 | |||
27 | /** | ||
28 | * Helper class for BigIntegers. | ||
29 | */ | ||
30 | public 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; | |||
25 | import com.google.common.base.Preconditions; | 25 | import com.google.common.base.Preconditions; |
26 | import com.google.common.collect.BiMap; | 26 | import com.google.common.collect.BiMap; |
27 | import com.google.common.collect.HashBiMap; | 27 | import com.google.common.collect.HashBiMap; |
28 | import com.google.common.collect.Maps; | ||
28 | import com.google.common.primitives.Longs; | 29 | import com.google.common.primitives.Longs; |
30 | import org.gnunet.secretsharing.ThresholdPublicKey; | ||
29 | import org.gnunet.util.*; | 31 | import org.gnunet.util.*; |
30 | import org.gnunet.util.crypto.EcdsaPrivateKey; | 32 | import org.gnunet.util.crypto.*; |
31 | import org.gnunet.util.crypto.EcdsaPublicKey; | 33 | import org.gnunet.voting.messages.KeyQueryResponseMessage; |
32 | import org.gnunet.util.crypto.EcdsaSignature; | ||
33 | import org.gnunet.util.crypto.EddsaSignature; | ||
34 | 34 | ||
35 | import java.security.MessageDigest; | 35 | import java.security.MessageDigest; |
36 | import java.security.NoSuchAlgorithmException; | 36 | import 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; | |||
30 | import org.gnunet.mesh.Mesh; | 30 | import org.gnunet.mesh.Mesh; |
31 | import org.gnunet.mesh.MeshRunabout; | 31 | import org.gnunet.mesh.MeshRunabout; |
32 | import org.gnunet.mesh.TunnelEndHandler; | 32 | import org.gnunet.mesh.TunnelEndHandler; |
33 | import org.gnunet.secretsharing.ThresholdPublicKey; | ||
33 | import org.gnunet.testbed.CompressedConfig; | 34 | import org.gnunet.testbed.CompressedConfig; |
34 | import org.gnunet.util.AbsoluteTime; | 35 | import org.gnunet.util.*; |
35 | import org.gnunet.util.Configuration; | ||
36 | import org.gnunet.util.PeerIdentity; | ||
37 | import org.gnunet.util.Program; | ||
38 | import org.gnunet.util.getopt.Argument; | 36 | import org.gnunet.util.getopt.Argument; |
39 | import org.gnunet.util.getopt.ArgumentAction; | 37 | import org.gnunet.util.getopt.ArgumentAction; |
40 | import org.gnunet.voting.messages.*; | 38 | import org.gnunet.voting.messages.*; |
39 | import org.slf4j.Logger; | ||
40 | import org.slf4j.LoggerFactory; | ||
41 | 41 | ||
42 | import java.io.File; | 42 | import java.io.File; |
43 | import java.io.FileOutputStream; | 43 | import 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 | */ |
52 | public class BallotTool extends Program { | 52 | public 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; | |||
30 | import org.gnunet.construct.UInt32; | 30 | import org.gnunet.construct.UInt32; |
31 | import org.gnunet.mesh.Mesh; | 31 | import org.gnunet.mesh.Mesh; |
32 | import org.gnunet.mesh.MeshRunabout; | 32 | import org.gnunet.mesh.MeshRunabout; |
33 | import org.gnunet.secretsharing.*; | ||
34 | import org.gnunet.secretsharing.callbacks.DecryptCallback; | ||
35 | import org.gnunet.secretsharing.callbacks.SecretReadyCallback; | ||
33 | import org.gnunet.testbed.CompressedConfig; | 36 | import org.gnunet.testbed.CompressedConfig; |
34 | import org.gnunet.util.*; | 37 | import org.gnunet.util.*; |
35 | import org.gnunet.util.crypto.*; | 38 | import org.gnunet.util.crypto.*; |
@@ -37,27 +40,36 @@ import org.gnunet.voting.messages.*; | |||
37 | import org.slf4j.Logger; | 40 | import org.slf4j.Logger; |
38 | import org.slf4j.LoggerFactory; | 41 | import org.slf4j.LoggerFactory; |
39 | 42 | ||
43 | import java.math.BigInteger; | ||
40 | import java.util.*; | 44 | import 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 | */ |
46 | public class TallyAuthorityDaemon extends Program { | 50 | public 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 @@ | |||
1 | package org.gnunet.voting.messages; | ||
2 | |||
3 | import org.gnunet.construct.UnionCase; | ||
4 | import org.gnunet.construct.ZeroTerminatedString; | ||
5 | import org.gnunet.util.GnunetMessage; | ||
6 | |||
7 | @UnionCase(42015) | ||
8 | public 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 @@ | |||
1 | package org.gnunet.voting.messages; | ||
2 | |||
3 | import org.gnunet.construct.NestedMessage; | ||
4 | import org.gnunet.construct.UnionCase; | ||
5 | import org.gnunet.util.GnunetMessage; | ||
6 | import org.gnunet.util.HashCode; | ||
7 | |||
8 | @UnionCase(42013) | ||
9 | public 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 | |||
21 | package org.gnunet.voting.messages; | ||
22 | |||
23 | import org.gnunet.construct.Message; | ||
24 | import org.gnunet.construct.NestedMessage; | ||
25 | import org.gnunet.construct.UInt32; | ||
26 | import org.gnunet.construct.UnionCase; | ||
27 | import org.gnunet.secretsharing.ThresholdPublicKey; | ||
28 | import org.gnunet.util.GnunetMessage; | ||
29 | import org.gnunet.util.HashCode; | ||
30 | import org.gnunet.util.crypto.EddsaSignature; | ||
31 | |||
32 | @UnionCase(42014) | ||
33 | public 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 | |||
21 | package org.gnunet.voting.messages; | ||
22 | |||
23 | import org.gnunet.construct.UnionCase; | ||
24 | import org.gnunet.construct.ZeroTerminatedString; | ||
25 | import org.gnunet.util.GnunetMessage; | ||
26 | |||
27 | @UnionCase(42009) | ||
28 | public 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 @@ | |||
1 | package org.gnunet.voting.messages; | ||
2 | |||
3 | import org.gnunet.construct.NestedMessage; | ||
4 | import org.gnunet.construct.UnionCase; | ||
5 | import org.gnunet.util.GnunetMessage; | ||
6 | import org.gnunet.util.HashCode; | ||
7 | |||
8 | @UnionCase(42005) | ||
9 | public 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 @@ | |||
1 | package org.gnunet.voting.messages; | ||
2 | |||
3 | |||
4 | import org.gnunet.construct.*; | ||
5 | import org.gnunet.util.GnunetMessage; | ||
6 | import org.gnunet.util.HashCode; | ||
7 | |||
8 | @UnionCase(42006) | ||
9 | public 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 | ||
24 | import org.gnunet.construct.NestedMessage; | 24 | import org.gnunet.construct.NestedMessage; |
25 | import org.gnunet.construct.UInt32; | ||
25 | import org.gnunet.construct.UnionCase; | 26 | import org.gnunet.construct.UnionCase; |
26 | import org.gnunet.construct.ZeroTerminatedString; | 27 | import org.gnunet.construct.ZeroTerminatedString; |
27 | import org.gnunet.util.AbsoluteTimeMessage; | 28 | import org.gnunet.util.AbsoluteTimeMessage; |
28 | import org.gnunet.util.GnunetMessage; | 29 | import org.gnunet.util.GnunetMessage; |
29 | import org.gnunet.util.crypto.EcdsaSignedMessage; | 30 | import org.gnunet.util.crypto.EddsaSignature; |
30 | import org.gnunet.util.crypto.EddsaSignedMessage; | ||
31 | 31 | ||
32 | @UnionCase(42010) | 32 | @UnionCase(42010) |
33 | public class SubmitFailureMessage implements GnunetMessage.Body { | 33 | public 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.*; | |||
7 | import org.gnunet.util.crypto.EcdsaPublicKey; | 7 | import org.gnunet.util.crypto.EcdsaPublicKey; |
8 | import org.gnunet.util.crypto.EcdsaSignature; | 8 | import org.gnunet.util.crypto.EcdsaSignature; |
9 | import org.gnunet.util.crypto.EddsaSignature; | 9 | import org.gnunet.util.crypto.EddsaSignature; |
10 | import 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 | } |