aboutsummaryrefslogtreecommitdiff
path: root/src/main/java
diff options
context:
space:
mode:
authorFlorian Dold <florian.dold@gmail.com>2013-10-29 02:37:48 +0000
committerFlorian Dold <florian.dold@gmail.com>2013-10-29 02:37:48 +0000
commit56991ab6ca6a639cc9cfa6bd3b9a195a95f13d69 (patch)
tree17e6e85abef96fe8e1c135b80fd3d8d51731d38c /src/main/java
parent93c0e940bcfc83681cce859aac7f07c4470fe88a (diff)
downloadgnunet-java-56991ab6ca6a639cc9cfa6bd3b9a195a95f13d69.tar.gz
gnunet-java-56991ab6ca6a639cc9cfa6bd3b9a195a95f13d69.zip
- automatic test for voting
- signed messages
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/org/gnunet/mesh/Mesh.java13
-rw-r--r--src/main/java/org/gnunet/util/RelativeTime.java1
-rw-r--r--src/main/java/org/gnunet/util/crypto/EcdsaPrivateKey.java12
-rw-r--r--src/main/java/org/gnunet/util/crypto/EcdsaSignature.java2
-rw-r--r--src/main/java/org/gnunet/util/crypto/EcdsaSignedMessage.java60
-rw-r--r--src/main/java/org/gnunet/util/crypto/EddsaPrivateKey.java4
-rw-r--r--src/main/java/org/gnunet/util/crypto/EddsaSignedMessage.java80
-rw-r--r--src/main/java/org/gnunet/voting/Ballot.java7
-rw-r--r--src/main/java/org/gnunet/voting/BallotTool.java12
-rw-r--r--src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java16
-rw-r--r--src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java5
11 files changed, 198 insertions, 14 deletions
diff --git a/src/main/java/org/gnunet/mesh/Mesh.java b/src/main/java/org/gnunet/mesh/Mesh.java
index ad6686d..6f8f4b3 100644
--- a/src/main/java/org/gnunet/mesh/Mesh.java
+++ b/src/main/java/org/gnunet/mesh/Mesh.java
@@ -320,13 +320,17 @@ public class Mesh {
320 this.ports = ports; 320 this.ports = ports;
321 this.inboundTunnelHandler = inboundTunnelHandler; 321 this.inboundTunnelHandler = inboundTunnelHandler;
322 322
323 logger.debug("mesh handle created");
324
325 client = new Client("mesh", cfg); 323 client = new Client("mesh", cfg);
326 client.installReceiver(new MeshMessageReceiver()); 324 client.installReceiver(new MeshMessageReceiver());
327 ClientConnectMessage ccm = new ClientConnectMessage(); 325 ClientConnectMessage ccm = new ClientConnectMessage();
328 ccm.applicationList = ports; 326 ccm.applicationList = ports;
327 String portList = "";
328 for (int p : ports) {
329 portList += "" + p + " ";
330 }
329 client.send(ccm); 331 client.send(ccm);
332
333 logger.debug("mesh handle created, listening on ports {}", portList);
330 } 334 }
331 335
332 /** 336 /**
@@ -345,6 +349,11 @@ public class Mesh {
345 return new Tunnel<T>(peer, port, nobuffer, reliable, initialContext); 349 return new Tunnel<T>(peer, port, nobuffer, reliable, initialContext);
346 } 350 }
347 351
352 public <T> Tunnel<T> createTunnel(PeerIdentity peer, int port, boolean nobuffer, boolean reliable) {
353 logger.debug("creating tunnel to peer {} over port {}", peer.toString(), port);
354 return new Tunnel<T>(peer, port, nobuffer, reliable, null);
355 }
356
348 /** 357 /**
349 * Disconnect from the mesh service. 358 * Disconnect from the mesh service.
350 * All tunnels will be destroyed. 359 * All tunnels will be destroyed.
diff --git a/src/main/java/org/gnunet/util/RelativeTime.java b/src/main/java/org/gnunet/util/RelativeTime.java
index 512ed01..083d343 100644
--- a/src/main/java/org/gnunet/util/RelativeTime.java
+++ b/src/main/java/org/gnunet/util/RelativeTime.java
@@ -210,7 +210,6 @@ public final class RelativeTime implements Comparable<RelativeTime> {
210 210
211 211
212 212
213
214 public RelativeTimeMessage toNetwork() { 213 public RelativeTimeMessage toNetwork() {
215 long rval = this.rel_value_us; 214 long rval = this.rel_value_us;
216 assert rval >= 0; 215 assert rval >= 0;
diff --git a/src/main/java/org/gnunet/util/crypto/EcdsaPrivateKey.java b/src/main/java/org/gnunet/util/crypto/EcdsaPrivateKey.java
index dca60b2..43bd55a 100644
--- a/src/main/java/org/gnunet/util/crypto/EcdsaPrivateKey.java
+++ b/src/main/java/org/gnunet/util/crypto/EcdsaPrivateKey.java
@@ -23,7 +23,17 @@ public class EcdsaPrivateKey implements Message {
23 } 23 }
24 24
25 public EcdsaPublicKey getPublicKey() { 25 public EcdsaPublicKey getPublicKey() {
26 return EcdsaPublicKey.random(); 26 // FIXME: this is not the real implementation,
27 // beware that this dummy implementation leaks the key!
28 EcdsaPublicKey publicKey = new EcdsaPublicKey();
29 byte[] v = new byte[32];
30 System.arraycopy(d, 0, v, 0, 8);
31 System.arraycopy(d, 0, v, 8, 8);
32 System.arraycopy(d, 0, v, 16, 8);
33 System.arraycopy(d, 0, v, 24, 8);
34 System.arraycopy(v, 0, publicKey.x, 0, 32);
35 System.arraycopy(v, 0, publicKey.y, 0, 32);
36 return publicKey;
27 } 37 }
28 38
29 public static EcdsaPrivateKey createRandom() { 39 public static EcdsaPrivateKey createRandom() {
diff --git a/src/main/java/org/gnunet/util/crypto/EcdsaSignature.java b/src/main/java/org/gnunet/util/crypto/EcdsaSignature.java
index 1589b67..b668a3f 100644
--- a/src/main/java/org/gnunet/util/crypto/EcdsaSignature.java
+++ b/src/main/java/org/gnunet/util/crypto/EcdsaSignature.java
@@ -48,7 +48,7 @@ public class EcdsaSignature implements Message {
48 this.s = new byte[32]; 48 this.s = new byte[32];
49 } 49 }
50 50
51 public boolean verify(byte[] m, int purpose, EddsaPublicKey publicKey) { 51 public boolean verify(byte[] m, int purpose, EcdsaPublicKey publicKey) {
52 return false; 52 return false;
53 } 53 }
54 54
diff --git a/src/main/java/org/gnunet/util/crypto/EcdsaSignedMessage.java b/src/main/java/org/gnunet/util/crypto/EcdsaSignedMessage.java
new file mode 100644
index 0000000..0cb293a
--- /dev/null
+++ b/src/main/java/org/gnunet/util/crypto/EcdsaSignedMessage.java
@@ -0,0 +1,60 @@
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.util.crypto;
22
23import org.gnunet.construct.Construct;
24import org.gnunet.construct.Message;
25import org.gnunet.construct.NestedMessage;
26import org.gnunet.construct.UInt32;
27
28/**
29 * A message together with a signature on the message and it's purpose.
30 */
31public class EcdsaSignedMessage<M extends Message> implements Message {
32 @NestedMessage
33 public EcdsaSignature signature;
34 @UInt32
35 public int purpose;
36 @NestedMessage
37 public M innerMessage;
38
39 public EcdsaSignedMessage() {
40 // empty constructor required by org.gnunet.construct
41 }
42
43 public boolean verify(EcdsaPublicKey signerPublicKey) {
44 return signature.verify(Construct.toBinary(innerMessage), purpose, signerPublicKey);
45 }
46
47 public static <T extends Message> EcdsaSignedMessage<T> signMessage(T innerMessage, int purpose,
48 EcdsaPrivateKey privateKey, EcdsaPublicKey publicKey) {
49 EcdsaSignedMessage<T> esm = new EcdsaSignedMessage<T>();
50 esm.purpose = purpose;
51 esm.innerMessage = innerMessage;
52 esm.signature = privateKey.sign(purpose, Construct.toBinary(innerMessage));
53 return esm;
54 }
55
56 public static <T extends Message> EcdsaSignedMessage<T> signMessage(T innerMessage, int purpose,
57 EcdsaPrivateKey privateKey) {
58 return signMessage(innerMessage, purpose, privateKey, privateKey.getPublicKey());
59 }
60}
diff --git a/src/main/java/org/gnunet/util/crypto/EddsaPrivateKey.java b/src/main/java/org/gnunet/util/crypto/EddsaPrivateKey.java
index 973706f..21aa647 100644
--- a/src/main/java/org/gnunet/util/crypto/EddsaPrivateKey.java
+++ b/src/main/java/org/gnunet/util/crypto/EddsaPrivateKey.java
@@ -13,6 +13,10 @@ public class EddsaPrivateKey implements Message {
13 @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32) 13 @FixedSizeIntegerArray(bitSize = 8, signed = false, length = 32)
14 public byte[] d; 14 public byte[] d;
15 15
16 public EddsaSignature sign(int purpose, byte[] m) {
17 return sign(getPublicKey(), purpose, m);
18 }
19
16 public EddsaSignature sign(EddsaPublicKey publicKey, int purpose, byte[] m) { 20 public EddsaSignature sign(EddsaPublicKey publicKey, int purpose, byte[] m) {
17 MessageDigest sha512; 21 MessageDigest sha512;
18 try { 22 try {
diff --git a/src/main/java/org/gnunet/util/crypto/EddsaSignedMessage.java b/src/main/java/org/gnunet/util/crypto/EddsaSignedMessage.java
new file mode 100644
index 0000000..27f969a
--- /dev/null
+++ b/src/main/java/org/gnunet/util/crypto/EddsaSignedMessage.java
@@ -0,0 +1,80 @@
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/*
22 This file is part of GNUnet.
23 (C) 2012, 2013 Christian Grothoff (and other contributing authors)
24
25 GNUnet is free software; you can redistribute it and/or modify
26 it under the terms of the GNU General Public License as published
27 by the Free Software Foundation; either version 3, or (at your
28 option) any later version.
29
30 GNUnet is distributed in the hope that it will be useful, but
31 WITHOUT ANY WARRANTY; without even the implied warranty of
32 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
33 General Public License for more details.
34
35 You should have received a copy of the GNU General Public License
36 along with GNUnet; see the file COPYING. If not, write to the
37 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
38 Boston, MA 02111-1307, USA.
39 */
40
41package org.gnunet.util.crypto;
42
43import org.gnunet.construct.Construct;
44import org.gnunet.construct.Message;
45import org.gnunet.construct.NestedMessage;
46import org.gnunet.construct.UInt32;
47
48/**
49 * A message together with a signature on the message and it's purpose.
50 */
51public class EddsaSignedMessage<M extends Message> implements Message {
52 @NestedMessage
53 public EddsaSignature signature;
54 @UInt32
55 public int purpose;
56 @NestedMessage
57 public M innerMessage;
58
59 public EddsaSignedMessage() {
60 // empty constructor required by org.gnunet.construct
61 }
62
63 public boolean verify(EddsaPublicKey signerPublicKey) {
64 return signature.verify(Construct.toBinary(innerMessage), purpose, signerPublicKey);
65 }
66
67 public static <T extends Message> EddsaSignedMessage<T> signMessage(T innerMessage, int purpose,
68 EddsaPrivateKey privateKey, EddsaPublicKey publicKey) {
69 EddsaSignedMessage<T> esm = new EddsaSignedMessage<T>();
70 esm.purpose = purpose;
71 esm.innerMessage = innerMessage;
72 esm.signature = privateKey.sign(publicKey, purpose, Construct.toBinary(innerMessage));
73 return esm;
74 }
75
76 public static <T extends Message> EddsaSignedMessage<T> signMessage(T innerMessage, int purpose,
77 EddsaPrivateKey privateKey) {
78 return signMessage(innerMessage, purpose, privateKey, privateKey.getPublicKey());
79 }
80}
diff --git a/src/main/java/org/gnunet/voting/Ballot.java b/src/main/java/org/gnunet/voting/Ballot.java
index 852acc6..72d444f 100644
--- a/src/main/java/org/gnunet/voting/Ballot.java
+++ b/src/main/java/org/gnunet/voting/Ballot.java
@@ -418,6 +418,8 @@ public class Ballot {
418 buf.append(" "); 418 buf.append(" ");
419 } 419 }
420 buf.append("\n"); 420 buf.append("\n");
421 } else {
422 buf.append("ballot not registered");
421 } 423 }
422 if (!confirmationSigs.isEmpty()) { 424 if (!confirmationSigs.isEmpty()) {
423 buf.append("ballot's vote has been submitted to with the following authorities:\n"); 425 buf.append("ballot's vote has been submitted to with the following authorities:\n");
@@ -428,7 +430,7 @@ public class Ballot {
428 buf.append("\n"); 430 buf.append("\n");
429 } 431 }
430 else { 432 else {
431 buf.append("ballot not registered\n"); 433 buf.append("ballot not submitted\n");
432 } 434 }
433 if (choiceId != -1) { 435 if (choiceId != -1) {
434 buf.append("choice selected\n"); 436 buf.append("choice selected\n");
@@ -529,9 +531,12 @@ public class Ballot {
529 if (this.groupCert != null) { 531 if (this.groupCert != null) {
530 throw new InvalidBallotException("ballot already has group information"); 532 throw new InvalidBallotException("ballot already has group information");
531 } 533 }
534 // FIXME: add this check again once the crypto is in sync
535 /*
532 if (voterPub != null && !groupCert.getMemberPublicKey().equals(voterPub)) { 536 if (voterPub != null && !groupCert.getMemberPublicKey().equals(voterPub)) {
533 throw new InvalidBallotException("group and voter public key do not match"); 537 throw new InvalidBallotException("group and voter public key do not match");
534 } 538 }
539 */
535 voterPub = groupCert.getMemberPublicKey(); 540 voterPub = groupCert.getMemberPublicKey();
536 this.groupCert = groupCert; 541 this.groupCert = groupCert;
537 } 542 }
diff --git a/src/main/java/org/gnunet/voting/BallotTool.java b/src/main/java/org/gnunet/voting/BallotTool.java
index 23e7f4a..4883acc 100644
--- a/src/main/java/org/gnunet/voting/BallotTool.java
+++ b/src/main/java/org/gnunet/voting/BallotTool.java
@@ -241,7 +241,7 @@ public class BallotTool extends Program {
241 currentAuthority = remainingAuthorities.get(r.nextInt(remainingAuthorities.size())); 241 currentAuthority = remainingAuthorities.get(r.nextInt(remainingAuthorities.size()));
242 System.out.println("registering ballot with authority " + currentAuthority.toString()); 242 System.out.println("registering ballot with authority " + currentAuthority.toString());
243 mesh = new Mesh(getConfiguration(), new BallotTunnelEndHandler(), new BallotRegisterReceiver()); 243 mesh = new Mesh(getConfiguration(), new BallotTunnelEndHandler(), new BallotRegisterReceiver());
244 tunnel = mesh.createTunnel(currentAuthority, TallyAuthorityDaemon.MESH_PORT, true, true, null); 244 tunnel = mesh.createTunnel(currentAuthority, TallyAuthorityDaemon.MESH_PORT, true, true);
245 BallotRegisterRequestMessage m = new BallotRegisterRequestMessage(); 245 BallotRegisterRequestMessage m = new BallotRegisterRequestMessage();
246 CompressedConfig ccfg = new CompressedConfig(ballot.toConfiguration()); 246 CompressedConfig ccfg = new CompressedConfig(ballot.toConfiguration());
247 m.compressedBallotConfig = ccfg.compressedData; 247 m.compressedBallotConfig = ccfg.compressedData;
@@ -250,7 +250,8 @@ public class BallotTool extends Program {
250 } 250 }
251 if (issue) { 251 if (issue) {
252 if (null == ego) { 252 if (null == ego) {
253 System.out.println("no ego given"); 253 System.err.println("no ego given");
254 setReturnValue(1);
254 return; 255 return;
255 } 256 }
256 ballot.issue(ego.getPrivateKey()); 257 ballot.issue(ego.getPrivateKey());
@@ -259,7 +260,8 @@ public class BallotTool extends Program {
259 } 260 }
260 if (select != null) { 261 if (select != null) {
261 if (null == ego) { 262 if (null == ego) {
262 System.out.println("no ego given"); 263 System.err.println("no ego given");
264 setReturnValue(1);
263 return; 265 return;
264 } 266 }
265 ballot.encodeChoice(select, ego.getPrivateKey()); 267 ballot.encodeChoice(select, ego.getPrivateKey());
@@ -301,6 +303,7 @@ public class BallotTool extends Program {
301 List<PeerIdentity> remainingAuthorities = ballot.getAuthorities(); 303 List<PeerIdentity> remainingAuthorities = ballot.getAuthorities();
302 if (remainingAuthorities.isEmpty()) { 304 if (remainingAuthorities.isEmpty()) {
303 System.err.println("no authorities available"); 305 System.err.println("no authorities available");
306 setReturnValue(2);
304 return; 307 return;
305 } 308 }
306 Random r = new Random(); 309 Random r = new Random();
@@ -358,6 +361,7 @@ public class BallotTool extends Program {
358 361
359 public static void main(String args[]) { 362 public static void main(String args[]) {
360 Program tool = new BallotTool(args); 363 Program tool = new BallotTool(args);
361 tool.start(); 364 int ret = tool.start();
365 System.exit(ret);
362 } 366 }
363} 367}
diff --git a/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java b/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
index 6ae2860..7ae5846 100644
--- a/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
+++ b/src/main/java/org/gnunet/voting/TallyAuthorityDaemon.java
@@ -107,6 +107,7 @@ public class TallyAuthorityDaemon extends Program {
107 107
108 @Override 108 @Override
109 public void onDone() { 109 public void onDone() {
110 System.out.println("got element from consensus");
110 electionState.consensusDone = true; 111 electionState.consensusDone = true;
111 electionState.consensus.destroy(); 112 electionState.consensus.destroy();
112 electionState.consensus = null; 113 electionState.consensus = null;
@@ -151,7 +152,7 @@ public class TallyAuthorityDaemon extends Program {
151 + electionState.ballot.startTime.toFancyString() + ", now " 152 + electionState.ballot.startTime.toFancyString() + ", now "
152 + AbsoluteTime.now().toFancyString() + ")"; 153 + AbsoluteTime.now().toFancyString() + ")";
153 getSender().send(fm); 154 getSender().send(fm);
154 } else if (!electionState.ballot.closingTime.isDue()) { 155 } else if (electionState.ballot.closingTime.isDue()) {
155 SubmitFailureMessage fm = new SubmitFailureMessage(); 156 SubmitFailureMessage fm = new SubmitFailureMessage();
156 fm.reason = "too late to submit vote"; 157 fm.reason = "too late to submit vote";
157 getSender().send(fm); 158 getSender().send(fm);
@@ -196,15 +197,17 @@ public class TallyAuthorityDaemon extends Program {
196 ElectionState electionState = new ElectionState(); 197 ElectionState electionState = new ElectionState();
197 electionState.tally = new int[b.choices.size()]; 198 electionState.tally = new int[b.choices.size()];
198 electionState.ballot = b; 199 electionState.ballot = b;
200 PeerIdentity[] ids = new PeerIdentity[b.getAuthorities().size()];
201 ids = b.getAuthorities().toArray(ids);
199 electionState.consensus = new Consensus(getConfiguration(), 202 electionState.consensus = new Consensus(getConfiguration(),
200 (PeerIdentity[]) b.getAuthorities().toArray(), 203 ids,
201 b.getBallotGuid()); 204 b.getBallotGuid());
202 205
203 ConsensusConcludeTask t = new ConsensusConcludeTask(electionState); 206 ConsensusConcludeTask t = new ConsensusConcludeTask(electionState);
204 if (b.concludeTime.isDue()) { 207 if (b.concludeTime.isDue()) {
205 Scheduler.add(t); 208 Scheduler.add(t);
206 } else { 209 } else {
207 Scheduler.addDelayed(b.concludeTime.getRemaining(), t); 210 Scheduler.addDelayed(b.closingTime.getRemaining(), t);
208 } 211 }
209 elections.put(guid, electionState); 212 elections.put(guid, electionState);
210 BallotRegisterSuccessMessage rm = new BallotRegisterSuccessMessage(); 213 BallotRegisterSuccessMessage rm = new BallotRegisterSuccessMessage();
@@ -220,7 +223,12 @@ public class TallyAuthorityDaemon extends Program {
220 rm.reason = "no matching ballot found"; 223 rm.reason = "no matching ballot found";
221 getSender().send(rm); 224 getSender().send(rm);
222 } else { 225 } else {
223 if (electionState.ballot.queryTime.isDue()) { 226 if (!electionState.consensusDone) {
227 QueryFailureMessage rm = new QueryFailureMessage();
228 rm.reason = "consensus not finished (sorry...)";
229 getSender().send(rm);
230 }
231 else if (electionState.ballot.queryTime.isDue()) {
224 QueryResponseMessage rm = new QueryResponseMessage(); 232 QueryResponseMessage rm = new QueryResponseMessage();
225 rm.results = electionState.tally; 233 rm.results = electionState.tally;
226 getSender().send(rm); 234 getSender().send(rm);
diff --git a/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java b/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java
index 6e5a345..3409e2b 100644
--- a/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java
+++ b/src/main/java/org/gnunet/voting/messages/SubmitFailureMessage.java
@@ -21,12 +21,17 @@
21package org.gnunet.voting.messages; 21package org.gnunet.voting.messages;
22 22
23 23
24import org.gnunet.construct.NestedMessage;
24import org.gnunet.construct.UnionCase; 25import org.gnunet.construct.UnionCase;
25import org.gnunet.construct.ZeroTerminatedString; 26import org.gnunet.construct.ZeroTerminatedString;
27import org.gnunet.util.AbsoluteTimeMessage;
26import org.gnunet.util.GnunetMessage; 28import org.gnunet.util.GnunetMessage;
29import org.gnunet.util.crypto.EcdsaSignedMessage;
27 30
28@UnionCase(42010) 31@UnionCase(42010)
29public class SubmitFailureMessage implements GnunetMessage.Body { 32public class SubmitFailureMessage implements GnunetMessage.Body {
30 @ZeroTerminatedString 33 @ZeroTerminatedString
31 public String reason; 34 public String reason;
35 @NestedMessage(optional = true)
36 public EcdsaSignedMessage<AbsoluteTimeMessage> authorityTime;
32} 37}