commit 110cb01250703d2825d956752ec84557583144ec Author: Karsten Loesing karsten.loesing@gmx.net Date: Mon Feb 13 15:54:06 2017 +0100
Parse "shared-rand-.*" lines in consensuses and votes. --- CHANGELOG.md | 1 + .../descriptor/RelayNetworkStatusConsensus.java | 34 +++++++ .../descriptor/RelayNetworkStatusVote.java | 55 +++++++++++ .../impl/RelayNetworkStatusConsensusImpl.java | 69 +++++++++++++- .../impl/RelayNetworkStatusVoteImpl.java | 104 ++++++++++++++++++++- .../descriptor/impl/ConsensusBuilder.java | 30 ++++++ .../impl/RelayNetworkStatusConsensusImplTest.java | 15 +++ .../impl/RelayNetworkStatusVoteImplTest.java | 97 +++++++++++++++++++ 8 files changed, 402 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bcfea9..a760143 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Parse "proto" lines in server descriptors, "pr" lines in status entries, and "(recommended|required)-(client|relay)-protocols" lines in consensuses and votes. + - Parse "shared-rand-.*" lines in consensuses and votes.
# Changes in version 1.5.0 - 2016-10-19 diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java index b004b66..57a8dd6 100644 --- a/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java +++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusConsensus.java @@ -179,6 +179,40 @@ public interface RelayNetworkStatusConsensus extends Descriptor { public SortedMap<String, Integer> getConsensusParams();
/** + * Return the number of commits used to generate the second-to-last shared + * random value, or -1 if the consensus does not contain a second-to-last + * shared random value. + * + * @since 1.6.0 + */ + public int getSharedRandPreviousNumReveals(); + + /** + * Return the second-to-last shared random value, encoded in base64, or null + * if the consensus does not contain a second-to-last shared random value. + * + * @since 1.6.0 + */ + public String getSharedRandPreviousValue(); + + /** + * Return the number of commits used to generate the latest shared random + * value, or -1 if the consensus does not contain the latest shared random + * value. + * + * @since 1.6.0 + */ + public int getSharedRandCurrentNumReveals(); + + /** + * Return the latest shared random value, encoded in base64, or null if the + * consensus does not contain the latest shared random value. + * + * @since 1.6.0 + */ + public String getSharedRandCurrentValue(); + + /** * Return directory source entries for each directory authority that * contributed to the consensus, with map keys being SHA-1 digests of * the authorities' identity keys in the version 3 directory protocol, diff --git a/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java index 9ea804d..cfb012b 100644 --- a/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java +++ b/src/main/java/org/torproject/descriptor/RelayNetworkStatusVote.java @@ -306,6 +306,61 @@ public interface RelayNetworkStatusVote extends Descriptor { public String getContactLine();
/** + * Return whether this directory authority supports and can participate in + * the shared random protocol. + * + * @since 1.6.0 + */ + public boolean isSharedRandParticipate(); + + /** + * Return all currently known directory authority commit lines for the shared + * randomness protocol in the original format as they are contained in this + * vote, or null if this vote does not contain any such line. + * + * <pre> + * "shared-rand-commit" SP Version SP AlgName SP Identity SP Commit + * [SP Reveal] NL + * </pre> + * + * @since 1.6.0 + */ + public List<String> getSharedRandCommitLines(); + + /** + * Return the number of commits used to generate the second-to-last shared + * random value, or -1 if this vote does not contain a second-to-last shared + * random value. + * + * @since 1.6.0 + */ + public int getSharedRandPreviousNumReveals(); + + /** + * Return the second-to-last shared random value, encoded in base64, or null + * if this vote does not contain a second-to-last shared random value. + * + * @since 1.6.0 + */ + public String getSharedRandPreviousValue(); + + /** + * Return the number of commits used to generate the latest shared random + * value, or -1 if this vote does not contain the latest shared random value. + * + * @since 1.6.0 + */ + public int getSharedRandCurrentNumReveals(); + + /** + * Return the latest shared random value, encoded in base64, or null if this + * vote does not contain the latest shared random value. + * + * @since 1.6.0 + */ + public String getSharedRandCurrentValue(); + + /** * Return the version of the directory key certificate used by this * authority, which must be 3 or higher. * diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java index fd4cf7e..dcb8938 100644 --- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImpl.java @@ -54,8 +54,9 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl Set<String> atMostOnceKeywords = new HashSet<>(Arrays.asList(( "client-versions,server-versions,recommended-client-protocols," + "recommended-relay-protocols,required-client-protocols," - + "required-relay-protocols,params,directory-footer," - + "bandwidth-weights").split(","))); + + "required-relay-protocols,params,shared-rand-previous-value," + + "shared-rand-current-value,directory-footer,bandwidth-weights") + .split(","))); this.checkAtMostOnceKeywords(atMostOnceKeywords); this.checkFirstKeyword("network-status-version"); this.clearParsedKeywords(); @@ -147,6 +148,12 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl case "params": this.parseParamsLine(line, parts); break; + case "shared-rand-previous-value": + this.parseSharedRandPreviousValueLine(line, parts); + break; + case "shared-rand-current-value": + this.parseSharedRandCurrentValueLine(line, parts); + break; default: if (this.failUnrecognizedDescriptorLines) { throw new DescriptorParseException("Unrecognized line '" @@ -350,6 +357,36 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl parts, 1, "="); }
+ private void parseSharedRandPreviousValueLine(String line, String[] parts) + throws DescriptorParseException { + if (parts.length != 3) { + throw new DescriptorParseException("Illegal line '" + line + + "' in vote."); + } + try { + this.sharedRandPreviousNumReveals = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + throw new DescriptorParseException("Illegal line '" + line + + "' in vote."); + } + this.sharedRandPreviousValue = parts[2]; + } + + private void parseSharedRandCurrentValueLine(String line, String[] parts) + throws DescriptorParseException { + if (parts.length != 3) { + throw new DescriptorParseException("Illegal line '" + line + + "' in vote."); + } + try { + this.sharedRandCurrentNumReveals = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + throw new DescriptorParseException("Illegal line '" + line + + "' in vote."); + } + this.sharedRandCurrentValue = parts[2]; + } + private void parseBandwidthWeightsLine(String line, String[] parts) throws DescriptorParseException { this.bandwidthWeights = ParseHelper.parseKeyValueIntegerPairs(line, @@ -486,6 +523,34 @@ public class RelayNetworkStatusConsensusImpl extends NetworkStatusImpl : new TreeMap<>(this.consensusParams); }
+ private int sharedRandPreviousNumReveals = -1; + + @Override + public int getSharedRandPreviousNumReveals() { + return this.sharedRandPreviousNumReveals; + } + + private String sharedRandPreviousValue = null; + + @Override + public String getSharedRandPreviousValue() { + return this.sharedRandPreviousValue; + } + + private int sharedRandCurrentNumReveals = -1; + + @Override + public int getSharedRandCurrentNumReveals() { + return this.sharedRandCurrentNumReveals; + } + + private String sharedRandCurrentValue = null; + + @Override + public String getSharedRandCurrentValue() { + return this.sharedRandCurrentValue; + } + private SortedMap<String, Integer> bandwidthWeights;
@Override diff --git a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java index 619b2c1..1f64173 100644 --- a/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java +++ b/src/main/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImpl.java @@ -54,7 +54,8 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl "consensus-methods,client-versions,server-versions," + "recommended-client-protocols,recommended-relay-protocols," + "required-client-protocols,required-relay-protocols," - + "flag-thresholds,params,contact," + + "flag-thresholds,params,contact,shared-rand-participate," + + "shared-rand-previous-value,shared-rand-current-value," + "legacy-key,dir-key-crosscert,dir-address,directory-footer") .split(","))); this.checkAtMostOnceKeywords(atMostOnceKeywords); @@ -149,6 +150,18 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl case "contact": this.parseContactLine(line, parts); break; + case "shared-rand-participate": + this.parseSharedRandParticipateLine(line, parts); + break; + case "shared-rand-commit": + this.parseSharedRandCommitLine(line, parts); + break; + case "shared-rand-previous-value": + this.parseSharedRandPreviousValueLine(line, parts); + break; + case "shared-rand-current-value": + this.parseSharedRandCurrentValueLine(line, parts); + break; case "dir-key-certificate-version": this.parseDirKeyCertificateVersionLine(line, parts); break; @@ -452,6 +465,53 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl } }
+ private void parseSharedRandParticipateLine(String line, String[] parts) + throws DescriptorParseException { + if (parts.length != 1) { + throw new DescriptorParseException("Illegal line '" + line + + "' in vote."); + } + this.sharedRandParticipate = true; + } + + private void parseSharedRandCommitLine(String line, String[] parts) + throws DescriptorParseException { + if (this.sharedRandCommitLines == null) { + this.sharedRandCommitLines = new ArrayList<>(); + } + this.sharedRandCommitLines.add(line); + } + + private void parseSharedRandPreviousValueLine(String line, String[] parts) + throws DescriptorParseException { + if (parts.length != 3) { + throw new DescriptorParseException("Illegal line '" + line + + "' in vote."); + } + try { + this.sharedRandPreviousNumReveals = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + throw new DescriptorParseException("Illegal line '" + line + + "' in vote."); + } + this.sharedRandPreviousValue = parts[2]; + } + + private void parseSharedRandCurrentValueLine(String line, String[] parts) + throws DescriptorParseException { + if (parts.length != 3) { + throw new DescriptorParseException("Illegal line '" + line + + "' in vote."); + } + try { + this.sharedRandCurrentNumReveals = Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + throw new DescriptorParseException("Illegal line '" + line + + "' in vote."); + } + this.sharedRandCurrentValue = parts[2]; + } + private void parseDirKeyCertificateVersionLine(String line, String[] parts) throws DescriptorParseException { if (parts.length != 2) { @@ -604,6 +664,48 @@ public class RelayNetworkStatusVoteImpl extends NetworkStatusImpl return this.contactLine; }
+ private boolean sharedRandParticipate = false; + + @Override + public boolean isSharedRandParticipate() { + return this.sharedRandParticipate; + } + + private List<String> sharedRandCommitLines = null; + + @Override + public List<String> getSharedRandCommitLines() { + return this.sharedRandCommitLines; + } + + private int sharedRandPreviousNumReveals = -1; + + @Override + public int getSharedRandPreviousNumReveals() { + return this.sharedRandPreviousNumReveals; + } + + private String sharedRandPreviousValue = null; + + @Override + public String getSharedRandPreviousValue() { + return this.sharedRandPreviousValue; + } + + private int sharedRandCurrentNumReveals = -1; + + @Override + public int getSharedRandCurrentNumReveals() { + return this.sharedRandCurrentNumReveals; + } + + private String sharedRandCurrentValue = null; + + @Override + public String getSharedRandCurrentValue() { + return this.sharedRandCurrentValue; + } + private int dirKeyCertificateVersion;
@Override diff --git a/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java b/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java index 24d5a02..d1cacc0 100644 --- a/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java +++ b/src/test/java/org/torproject/descriptor/impl/ConsensusBuilder.java @@ -188,6 +188,30 @@ public class ConsensusBuilder { return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true); }
+ private String sharedRandPreviousValueLine = + "shared-rand-previous-value 8 " + + "grwbnD6I40odtsdtWYxqs0DvPweCur6qG2Fo5p5ivS4="; + + protected static RelayNetworkStatusConsensus + createWithSharedRandPreviousValueLine(String line) + throws DescriptorParseException { + ConsensusBuilder cb = new ConsensusBuilder(); + cb.sharedRandPreviousValueLine = line; + return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true); + } + + private String sharedRandCurrentValueLine = + "shared-rand-current-value 8 " + + "D88plxd8YeLfCIVAR9gjiFlWB1WqpC53kWr350o1pzw="; + + protected static RelayNetworkStatusConsensus + createWithSharedRandCurrentValueLine(String line) + throws DescriptorParseException { + ConsensusBuilder cb = new ConsensusBuilder(); + cb.sharedRandCurrentValueLine = line; + return new RelayNetworkStatusConsensusImpl(cb.buildConsensus(), true); + } + List<String> dirSources = new ArrayList<>();
List<String> statusEntries = new ArrayList<>(); @@ -385,6 +409,12 @@ public class ConsensusBuilder { if (this.paramsLine != null) { sb.append(this.paramsLine).append("\n"); } + if (this.sharedRandPreviousValueLine != null) { + sb.append(this.sharedRandPreviousValueLine).append("\n"); + } + if (this.sharedRandCurrentValueLine != null) { + sb.append(this.sharedRandCurrentValueLine).append("\n"); + } if (this.unrecognizedHeaderLine != null) { sb.append(this.unrecognizedHeaderLine).append("\n"); } diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java index 786ae54..b150a6a 100644 --- a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java +++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusConsensusImplTest.java @@ -855,6 +855,21 @@ public class RelayNetworkStatusConsensusImplTest { ConsensusBuilder.createWithParamsLine("params max=2147483648"); }
+ @Test(expected = DescriptorParseException.class) + public void testSharedRandPreviousNumRevealsOnly() + throws DescriptorParseException { + ConsensusBuilder.createWithSharedRandPreviousValueLine( + "shared-rand-previous-value 8"); + } + + @Test(expected = DescriptorParseException.class) + public void testSharedRandPreviousExtraArg() + throws DescriptorParseException { + ConsensusBuilder.createWithSharedRandCurrentValueLine( + "shared-rand-current-value 8 " + + "D88plxd8YeLfCIVAR9gjiFlWB1WqpC53kWr350o1pzw= -1.0"); + } + @Test() public void testDirSourceLegacyNickname() throws DescriptorParseException { diff --git a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java index ea7b927..727d707 100644 --- a/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java +++ b/src/test/java/org/torproject/descriptor/impl/RelayNetworkStatusVoteImplTest.java @@ -252,6 +252,52 @@ public class RelayNetworkStatusVoteImplTest { return new RelayNetworkStatusVoteImpl(vb.buildVote(), true); }
+ private String sharedRandParticipateLine = "shared-rand-participate"; + + private static RelayNetworkStatusVote createWithSharedRandParticipateLine( + String line) throws DescriptorParseException { + VoteBuilder vb = new VoteBuilder(); + vb.sharedRandParticipateLine = line; + return new RelayNetworkStatusVoteImpl(vb.buildVote(), true); + } + + private List<String> sharedRandCommitLines = Arrays.asList(new String[] { + "shared-rand-commit 1 sha3-256 " + + "0232AF901C31A04EE9848595AF9BB7620D4C5B2E " + + "AAAAAFieVABh3Aauk2h31FVKaW0xIm28T7VPDkzP5nHwoMItxp7iQg==", + "shared-rand-commit 1 sha3-256 " + + "14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 " + + "AAAAAFieVAA26LuAu9z2UhalmV7zuczWauSkqp1c/bsPA3AkH85iGw==" }); + + private static RelayNetworkStatusVote createWithSharedRandCommitLines( + List<String> lines) throws DescriptorParseException { + VoteBuilder vb = new VoteBuilder(); + vb.sharedRandCommitLines = lines; + return new RelayNetworkStatusVoteImpl(vb.buildVote(), true); + } + + private String sharedRandPreviousValueLine = + "shared-rand-previous-value 8 " + + "grwbnD6I40odtsdtWYxqs0DvPweCur6qG2Fo5p5ivS4="; + + private static RelayNetworkStatusVote createWithSharedRandPreviousValueLine( + String line) throws DescriptorParseException { + VoteBuilder vb = new VoteBuilder(); + vb.sharedRandPreviousValueLine = line; + return new RelayNetworkStatusVoteImpl(vb.buildVote(), true); + } + + private String sharedRandCurrentValueLine = + "shared-rand-current-value 8 " + + "D88plxd8YeLfCIVAR9gjiFlWB1WqpC53kWr350o1pzw="; + + private static RelayNetworkStatusVote createWithSharedRandCurrentValueLine( + String line) throws DescriptorParseException { + VoteBuilder vb = new VoteBuilder(); + vb.sharedRandCurrentValueLine = line; + return new RelayNetworkStatusVoteImpl(vb.buildVote(), true); + } + private String legacyDirKeyLine = null;
private static RelayNetworkStatusVote @@ -572,6 +618,20 @@ public class RelayNetworkStatusVoteImplTest { if (this.contactLine != null) { sb.append(this.contactLine).append("\n"); } + if (this.sharedRandParticipateLine != null) { + sb.append(this.sharedRandParticipateLine).append("\n"); + } + if (this.sharedRandCommitLines != null) { + for (String line : this.sharedRandCommitLines) { + sb.append(line).append("\n"); + } + } + if (this.sharedRandPreviousValueLine != null) { + sb.append(this.sharedRandPreviousValueLine).append("\n"); + } + if (this.sharedRandCurrentValueLine != null) { + sb.append(this.sharedRandCurrentValueLine).append("\n"); + } if (this.legacyDirKeyLine != null) { sb.append(this.legacyDirKeyLine).append("\n"); } @@ -1252,6 +1312,43 @@ public class RelayNetworkStatusVoteImplTest { + "Appelbaum jacob@appelbaum.net"); }
+ @Test(expected = DescriptorParseException.class) + public void testSharedRandParticipateLineDuplicate() + throws DescriptorParseException { + VoteBuilder.createWithSharedRandParticipateLine("shared-rand-participate\n" + + "shared-rand-participate"); + } + + @Test(expected = DescriptorParseException.class) + public void testSharedRandParticipateLineArg() + throws DescriptorParseException { + VoteBuilder.createWithSharedRandParticipateLine( + "shared-rand-participate 1"); + } + + @Test() + public void testSharedRandCommitLinesEmpty() throws DescriptorParseException { + RelayNetworkStatusVote vote = + VoteBuilder.createWithSharedRandCommitLines(null); + assertNull(vote.getSharedRandCommitLines()); + } + + @Test(expected = DescriptorParseException.class) + public void testSharedRandPreviousValueBeforeNumReveals() + throws DescriptorParseException { + VoteBuilder.createWithSharedRandPreviousValueLine( + "shared-rand-previous-value " + + "grwbnD6I40odtsdtWYxqs0DvPweCur6qG2Fo5p5ivS4= 8"); + } + + @Test(expected = DescriptorParseException.class) + public void testSharedRandCurrentNoNumReveals() + throws DescriptorParseException { + VoteBuilder.createWithSharedRandCurrentValueLine( + "shared-rand-current-value " + + "D88plxd8YeLfCIVAR9gjiFlWB1WqpC53kWr350o1pzw="); + } + @Test() public void testLegacyDirKeyLine() throws DescriptorParseException { RelayNetworkStatusVote vote = VoteBuilder.createWithLegacyDirKeyLine(
tor-commits@lists.torproject.org