Commit 39a9c496 authored by Karsten Loesing's avatar Karsten Loesing
Browse files

Parse Ed25519 and SHA-256 elements in descriptors.

More precisely,
 - support Ed25519 certificates and Ed25519 master keys as well as
   SHA-256 digests and Ed25519 signatures thereof in server
   descriptors and extra-info descriptors,
 - parse RSA-1024 signatures of SHA-1 digests of extra-info
   descriptors,
 - parse Ed25519 master keys in votes, and
 - parse Ed25519 and RSA-1024 identity digests in microdescriptors.

This patch is based on metrics-db's bridge descriptor sanitizer.
parent 957d57be
......@@ -18,6 +18,11 @@
existing types, ServerDescriptor and ExtraInfoDescriptor, are
still usable and will not be deprecated, because applications may
not care whether a relay or a bridge published a descriptor.
- Support Ed25519 certificates, Ed25519 master keys, SHA-256
digests, and Ed25519 signatures thereof in server descriptors and
extra-info descriptors, and support Ed25519 master keys in votes.
- Include RSA-1024 signatures of SHA-1 digests of extra-info
descriptors, which were parsed and discarded before.
# Changes in version 1.0.0 - 2015-12-05
......
......@@ -12,6 +12,10 @@ public interface ExtraInfoDescriptor extends Descriptor {
* extra-info descriptor in a server descriptor. */
public String getExtraInfoDigest();
/* Return the base64-encoded SHA-256 descriptor digest that may be used
* to reference this extra-info descriptor in a server descriptor. */
public String getExtraInfoDigestSha256();
/* Return the relay's nickname. */
public String getNickname();
......@@ -260,5 +264,27 @@ public interface ExtraInfoDescriptor extends Descriptor {
/* Return the (possibly empty) list of transports supported by this
* bridge. */
public List<String> getTransports();
/* Return the signature of the PKCS1-padded extra-info descriptor
* digest, or null if the descriptor doesn't contain a signature (which
* is the case in sanitized bridge descriptors). */
public String getRouterSignature();
/* Return the base64-encoded Ed25519 certificate, or null if the
* descriptor doesn't contain one. */
public String getIdentityEd25519();
/* Return the base64-encoded Ed25519 master key, which may either be
* parsed from the optional "master-key-ed25519" line or derived from
* the (likewise optional) Ed25519 certificate following the
* "identity-ed25519" line, or null if the descriptor contains neither
* Ed25519 master key nor Ed25519 certificate. */
public String getMasterKeyEd25519();
/* Return the base64-encoded Ed25519 signature of a SHA-256 digest of
* the entire descriptor, from the first character up to and including
* the first space after the "router-sig-ed25519" string, prefixed with
* the string "Tor router descriptor signature v1". */
public String getRouterSignatureEd25519();
}
......@@ -43,5 +43,15 @@ public interface Microdescriptor extends Descriptor {
/* Return the port list of the IPv6 port summary or null if the
* microdescriptor didn't contain an IPv6 port summary line. */
public String getIpv6PortList();
/* Return the optional, base64-encoded RSA-1024 identity that is only
* included to prevent collisions between microdescriptors, or null if
* no such identity is included. */
public String getRsa1024Identity();
/* Return the optional, base64-encoded Ed25519 identity that is only
* included to prevent collisions between microdescriptors, or null if
* no such identity is included. */
public String getEd25519Identity();
}
......@@ -75,5 +75,10 @@ public interface NetworkStatusEntry {
/* Return the port list of the port summary or null if the status entry
* didn't contain a port summary line. */
public String getPortList();
/* Return the relay's base64-encoded Ed25519 master key, "none" if the
* relay doesn't have an Ed25519 identity, or null if the status entry
* didn't contain this information. Only included in votes. */
public String getMasterKeyEd25519();
}
......@@ -11,6 +11,10 @@ public interface ServerDescriptor extends Descriptor {
* descriptor in a network status. */
public String getServerDescriptorDigest();
/* Return the base64-encoded SHA-256 descriptor digest that may be used
* to reference this server descriptor in a network status. */
public String getServerDescriptorDigestSha256();
/* Return the relay's nickname. */
public String getNickname();
......@@ -119,6 +123,12 @@ public interface ServerDescriptor extends Descriptor {
* the relay did not upload a corresponding extra-info descriptor. */
public String getExtraInfoDigest();
/* Return the base64-encoded SHA-256 digest of the extra-info descriptor
* referenced from this server descriptor, or null if the relay either
* did not upload a corresponding extra-info descriptor or did not refer
* to it using a SHA-256 digest. */
public String getExtraInfoDigestSha256();
/* Return the hidden service descriptor version(s) that this relay
* stores and serves, or null if it doesn't store and serve any hidden
* service descriptors. */
......@@ -147,5 +157,22 @@ public interface ServerDescriptor extends Descriptor {
/* Return the ntor onion key base64 string with padding omitted, or null
* if the server descriptors didn't contain an ntor onion key line. */
public String getNtorOnionKey();
/* Return the base64-encoded Ed25519 certificate, or null if the
* descriptor doesn't contain one. */
public String getIdentityEd25519();
/* Return the base64-encoded Ed25519 master key, which may either be
* parsed from the optional "master-key-ed25519" line or derived from
* the (likewise optional) Ed25519 certificate following the
* "identity-ed25519" line, or null if the descriptor contains neither
* Ed25519 master key nor Ed25519 certificate. */
public String getMasterKeyEd25519();
/* Return the base64-encoded Ed25519 signature of a SHA-256 digest of
* the entire descriptor, from the first character up to and including
* the first space after the "router-sig-ed25519" string, prefixed with
* the string "Tor router descriptor signature v1". */
public String getRouterSignatureEd25519();
}
......@@ -31,6 +31,7 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
super(descriptorBytes, failUnrecognizedDescriptorLines, false);
this.parseDescriptorBytes();
this.calculateDigest();
this.calculateDigestSha256();
Set<String> exactlyOnceKeywords = new HashSet<String>(Arrays.asList((
"extra-info,published").split(",")));
this.checkExactlyOnceKeywords(exactlyOnceKeywords);
......@@ -52,8 +53,9 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
Set<String> bridgeStatsKeywords = new HashSet<String>(Arrays.asList(
"bridge-stats-end,bridge-stats-ips".split(",")));
Set<String> atMostOnceKeywords = new HashSet<String>(Arrays.asList((
"read-history,write-history,dirreq-read-history,"
+ "dirreq-write-history,geoip-db-digest,router-signature,"
"identity-ed25519,master-key-ed25519,read-history,write-history,"
+ "dirreq-read-history,dirreq-write-history,geoip-db-digest,"
+ "router-sig-ed25519,router-signature,router-digest-sha256,"
+ "router-digest").split(",")));
atMostOnceKeywords.addAll(dirreqStatsKeywords);
atMostOnceKeywords.addAll(entryStatsKeywords);
......@@ -75,7 +77,8 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
private void parseDescriptorBytes() throws DescriptorParseException {
Scanner s = new Scanner(new String(this.rawDescriptorBytes)).
useDelimiter("\n");
boolean skipCrypto = false;
String nextCrypto = null;
List<String> cryptoLines = null;
while (s.hasNext()) {
String line = s.next();
String lineNoOpt = line.startsWith("opt ") ?
......@@ -162,15 +165,49 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
this.parseBridgeIpTransportsLine(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("transport")) {
this.parseTransportLine(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("identity-ed25519")) {
this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
nextCrypto = "identity-ed25519";
} else if (keyword.equals("master-key-ed25519")) {
this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("router-sig-ed25519")) {
this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("router-signature")) {
this.parseRouterSignatureLine(line, lineNoOpt, partsNoOpt);
nextCrypto = "router-signature";
} else if (keyword.equals("router-digest")) {
this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("router-digest-sha256")) {
this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
} else if (line.startsWith("-----BEGIN")) {
skipCrypto = true;
cryptoLines = new ArrayList<String>();
cryptoLines.add(line);
} else if (line.startsWith("-----END")) {
skipCrypto = false;
} else if (!skipCrypto) {
cryptoLines.add(line);
StringBuilder sb = new StringBuilder();
for (String cryptoLine : cryptoLines) {
sb.append("\n" + cryptoLine);
}
String cryptoString = sb.toString().substring(1);
if ("router-signature".equals(nextCrypto)) {
this.routerSignature = cryptoString;
} else if ("identity-ed25519".equals(nextCrypto)) {
this.identityEd25519 = cryptoString;
this.parseIdentityEd25519CryptoBlock(cryptoString);
} else if (this.failUnrecognizedDescriptorLines) {
throw new DescriptorParseException("Unrecognized crypto "
+ "block '" + cryptoString + "' in extra-info descriptor.");
} else {
if (this.unrecognizedLines == null) {
this.unrecognizedLines = new ArrayList<String>();
}
this.unrecognizedLines.addAll(cryptoLines);
}
cryptoLines = null;
nextCrypto = null;
} else if (cryptoLines != null) {
cryptoLines.add(line);
} else {
ParseHelper.parseKeyword(line, partsNoOpt[0]);
if (this.failUnrecognizedDescriptorLines) {
throw new DescriptorParseException("Unrecognized line '"
......@@ -610,7 +647,6 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
if (!lineNoOpt.equals("router-signature")) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
/* Not parsing crypto parts (yet). */
}
private void parseRouterDigestLine(String line, String lineNoOpt,
......@@ -622,6 +658,57 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
partsNoOpt[1]);
}
private void parseIdentityEd25519Line(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
if (partsNoOpt.length != 1) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
}
private void parseIdentityEd25519CryptoBlock(String cryptoString)
throws DescriptorParseException {
String masterKeyEd25519FromIdentityEd25519 =
ParseHelper.parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
cryptoString);
if (this.masterKeyEd25519 != null && !this.masterKeyEd25519.equals(
masterKeyEd25519FromIdentityEd25519)) {
throw new DescriptorParseException("Mismatch between "
+ "identity-ed25519 and master-key-ed25519.");
}
this.masterKeyEd25519 = masterKeyEd25519FromIdentityEd25519;
}
private void parseMasterKeyEd25519Line(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
if (partsNoOpt.length != 2) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
String masterKeyEd25519FromMasterKeyEd25519Line = partsNoOpt[1];
if (this.masterKeyEd25519 != null && !masterKeyEd25519.equals(
masterKeyEd25519FromMasterKeyEd25519Line)) {
throw new DescriptorParseException("Mismatch between "
+ "identity-ed25519 and master-key-ed25519.");
}
this.masterKeyEd25519 = masterKeyEd25519FromMasterKeyEd25519Line;
}
private void parseRouterSigEd25519Line(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
if (partsNoOpt.length != 2) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
this.routerSignatureEd25519 = partsNoOpt[1];
}
private void parseRouterDigestSha256Line(String line, String lineNoOpt,
String[] partsNoOpt) throws DescriptorParseException {
if (partsNoOpt.length != 2) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[1]);
this.extraInfoDigestSha256 = partsNoOpt[1];
}
private void calculateDigest() throws DescriptorParseException {
if (this.extraInfoDigest != null) {
/* We already learned the descriptor digest of this bridge
......@@ -653,11 +740,47 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
}
}
private void calculateDigestSha256() throws DescriptorParseException {
if (this.extraInfoDigestSha256 != null) {
/* We already learned the descriptor digest of this bridge
* descriptor from a "router-digest-sha256" line. */
return;
}
try {
String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
String startToken = "extra-info ";
String sigToken = "\n-----END SIGNATURE-----\n";
int start = ascii.indexOf(startToken);
int sig = ascii.indexOf(sigToken) + sigToken.length();
if (start >= 0 && sig >= 0 && sig > start) {
byte[] forDigest = new byte[sig - start];
System.arraycopy(this.getRawDescriptorBytes(), start, forDigest,
0, sig - start);
this.extraInfoDigestSha256 = DatatypeConverter.printBase64Binary(
MessageDigest.getInstance("SHA-256").digest(forDigest)).
replaceAll("=", "");
}
} catch (UnsupportedEncodingException e) {
/* Handle below. */
} catch (NoSuchAlgorithmException e) {
/* Handle below. */
}
if (this.extraInfoDigestSha256 == null) {
throw new DescriptorParseException("Could not calculate extra-info "
+ "descriptor SHA-256 digest.");
}
}
private String extraInfoDigest;
public String getExtraInfoDigest() {
return this.extraInfoDigest;
}
private String extraInfoDigestSha256;
public String getExtraInfoDigestSha256() {
return this.extraInfoDigestSha256;
}
private String nickname;
public String getNickname() {
return this.nickname;
......@@ -933,5 +1056,25 @@ public abstract class ExtraInfoDescriptorImpl extends DescriptorImpl
public List<String> getTransports() {
return new ArrayList<String>(this.transports);
}
private String routerSignature;
public String getRouterSignature() {
return this.routerSignature;
}
private String identityEd25519;
public String getIdentityEd25519() {
return this.identityEd25519;
}
private String masterKeyEd25519;
public String getMasterKeyEd25519() {
return this.masterKeyEd25519;
}
private String routerSignatureEd25519;
public String getRouterSignatureEd25519() {
return this.routerSignatureEd25519;
}
}
......@@ -48,7 +48,7 @@ public class MicrodescriptorImpl extends DescriptorImpl
"onion-key".split(",")));
this.checkExactlyOnceKeywords(exactlyOnceKeywords);
Set<String> atMostOnceKeywords = new HashSet<String>(Arrays.asList((
"ntor-onion-key,family,p,p6").split(",")));
"ntor-onion-key,family,p,p6,id").split(",")));
this.checkAtMostOnceKeywords(atMostOnceKeywords);
this.checkFirstKeyword("onion-key");
this.clearParsedKeywords();
......@@ -80,6 +80,8 @@ public class MicrodescriptorImpl extends DescriptorImpl
this.parsePLine(line, parts);
} else if (keyword.equals("p6")) {
this.parseP6Line(line, parts);
} else if (keyword.equals("id")) {
this.parseIdLine(line, parts);
} else if (line.startsWith("-----BEGIN")) {
crypto = new StringBuilder();
crypto.append(line + "\n");
......@@ -196,6 +198,21 @@ public class MicrodescriptorImpl extends DescriptorImpl
}
}
private void parseIdLine(String line, String[] parts)
throws DescriptorParseException {
if (parts.length != 3) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
} else if ("ed25519".equals(parts[1])) {
ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
this.ed25519Identity = parts[2];
} else if ("rsa1024".equals(parts[1])) {
ParseHelper.parseTwentyByteBase64String(line, parts[2]);
this.rsa1024Identity = parts[2];
} else {
throw new DescriptorParseException("Illegal line '" + line + "'.");
}
}
private void calculateDigest() throws DescriptorParseException {
try {
String ascii = new String(this.getRawDescriptorBytes(), "US-ASCII");
......@@ -265,5 +282,15 @@ public class MicrodescriptorImpl extends DescriptorImpl
public String getIpv6PortList() {
return this.ipv6PortList;
}
private String rsa1024Identity;
public String getRsa1024Identity() {
return this.rsa1024Identity;
}
private String ed25519Identity;
public String getEd25519Identity() {
return this.ed25519Identity;
}
}
......@@ -91,6 +91,8 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
this.parsePLine(line, parts);
} else if (keyword.equals("m")) {
this.parseMLine(line, parts);
} else if (keyword.equals("id")) {
this.parseIdLine(line, parts);
} else if (this.failUnrecognizedDescriptorLines) {
throw new DescriptorParseException("Unrecognized line '" + line
+ "' in status entry.");
......@@ -237,6 +239,18 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
}
}
private void parseIdLine(String line, String[] parts)
throws DescriptorParseException {
if (parts.length != 3 || !"ed25519".equals(parts[1])) {
throw new DescriptorParseException("Illegal line '" + line + "'.");
} else if ("none".equals(parts[2])) {
this.masterKeyEd25519 = "none";
} else {
ParseHelper.parseThirtyTwoByteBase64String(line, parts[2]);
this.masterKeyEd25519 = parts[2];
}
}
private void clearAtMostOnceKeywords() {
this.atMostOnceKeywords = null;
}
......@@ -328,5 +342,10 @@ public class NetworkStatusEntryImpl implements NetworkStatusEntry {
public String getPortList() {
return this.portList;
}
private String masterKeyEd25519;
public String getMasterKeyEd25519() {
return this.masterKeyEd25519;
}
}
......@@ -434,5 +434,78 @@ public class ParseHelper {
}
return result;
}
public static String
parseMasterKeyEd25519FromIdentityEd25519CryptoBlock(
String identityEd25519CryptoBlock) throws DescriptorParseException {
String identityEd25519CryptoBlockNoNewlines =
identityEd25519CryptoBlock.replaceAll("\n", "");
String beginEd25519CertLine = "-----BEGIN ED25519 CERT-----",
endEd25519CertLine = "-----END ED25519 CERT-----";
if (!identityEd25519CryptoBlockNoNewlines.startsWith(
beginEd25519CertLine)) {
throw new DescriptorParseException("Illegal start of "
+ "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
+ "'.");
}
if (!identityEd25519CryptoBlockNoNewlines.endsWith(
endEd25519CertLine)) {
throw new DescriptorParseException("Illegal end of "
+ "identity-ed25519 crypto block '" + identityEd25519CryptoBlock
+ "'.");
}
String identityEd25519Base64 = identityEd25519CryptoBlockNoNewlines.
substring(beginEd25519CertLine.length(),
identityEd25519CryptoBlock.length()
- endEd25519CertLine.length()).replaceAll("=", "");
byte[] identityEd25519 = DatatypeConverter.parseBase64Binary(
identityEd25519Base64);
if (identityEd25519.length < 40) {
throw new DescriptorParseException("Invalid length of "
+ "identity-ed25519 (in bytes): " + identityEd25519.length);
} else if (identityEd25519[0] != 0x01) {
throw new DescriptorParseException("Unknown version in "
+ "identity-ed25519: " + identityEd25519[0]);
} else if (identityEd25519[1] != 0x04) {
throw new DescriptorParseException("Unknown cert type in "
+ "identity-ed25519: " + identityEd25519[1]);
} else if (identityEd25519[6] != 0x01) {
throw new DescriptorParseException("Unknown certified key type in "
+ "identity-ed25519: " + identityEd25519[1]);
} else if (identityEd25519[39] == 0x00) {
throw new DescriptorParseException("No extensions in "
+ "identity-ed25519 (which would contain the encoded "
+ "master-key-ed25519): " + identityEd25519[39]);
} else {
int extensionStart = 40;
for (int i = 0; i < (int) identityEd25519[39]; i++) {
if (identityEd25519.length < extensionStart + 4) {
throw new DescriptorParseException("Invalid extension with id "
+ i + " in identity-ed25519.");
}
int extensionLength = identityEd25519[extensionStart];
extensionLength <<= 8;
extensionLength += identityEd25519[extensionStart + 1];
int extensionType = identityEd25519[extensionStart + 2];
if (extensionLength == 32 && extensionType == 4) {
if (identityEd25519.length < extensionStart + 4 + 32) {
throw new DescriptorParseException("Invalid extension with "
+ "id " + i + " in identity-ed25519.");
}
byte[] masterKeyEd25519 = new byte[32];
System.arraycopy(identityEd25519, extensionStart + 4,
masterKeyEd25519, 0, masterKeyEd25519.length);
String masterKeyEd25519Base64 = DatatypeConverter.
printBase64Binary(masterKeyEd25519).replaceAll("=", "");
String masterKeyEd25519Base64NoTrailingEqualSigns =
masterKeyEd25519Base64.replaceAll("=", "");
return masterKeyEd25519Base64NoTrailingEqualSigns;
}
extensionStart += 4 + extensionLength;
}
}
throw new DescriptorParseException("Unable to locate "
+ "master-key-ed25519 in identity-ed25519.");
}
}
......@@ -28,15 +28,18 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
super(descriptorBytes, failUnrecognizedDescriptorLines, false);
this.parseDescriptorBytes();
this.calculateDigest();
this.calculateDigestSha256();
Set<String> exactlyOnceKeywords = new HashSet<String>(Arrays.asList(
"router,bandwidth,published".split(",")));
this.checkExactlyOnceKeywords(exactlyOnceKeywords);
Set<String> atMostOnceKeywords = new HashSet<String>(Arrays.asList((
"platform,fingerprint,hibernating,uptime,contact,family,"
+ "read-history,write-history,eventdns,caches-extra-info,"
+ "extra-info-digest,hidden-service-dir,protocols,"
+ "allow-single-hop-exits,onion-key,signing-key,ipv6-policy,"
+ "ntor-onion-key,router-signature,router-digest").split(",")));
"identity-ed25519,master-key-ed25519,platform,fingerprint,"
+ "hibernating,uptime,contact,family,read-history,write-history,"
+ "eventdns,caches-extra-info,extra-info-digest,"
+ "hidden-service-dir,protocols,allow-single-hop-exits,onion-key,"
+ "signing-key,ipv6-policy,ntor-onion-key,router-sig-ed25519,"
+ "router-signature,router-digest-sha256,router-digest").
split(",")));
this.checkAtMostOnceKeywords(atMostOnceKeywords);
this.checkFirstKeyword("router");
if (this.getKeywordCount("accept") == 0 &&
......@@ -115,10 +118,19 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
this.parseDircacheportLine(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("router-digest")) {
this.parseRouterDigestLine(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("router-digest-sha256")) {
this.parseRouterDigestSha256Line(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("ipv6-policy")) {
this.parseIpv6PolicyLine(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("ntor-onion-key")) {
this.parseNtorOnionKeyLine(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("identity-ed25519")) {
this.parseIdentityEd25519Line(line, lineNoOpt, partsNoOpt);
nextCrypto = "identity-ed25519";
} else if (keyword.equals("master-key-ed25519")) {
this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt);
} else if (keyword.equals("router-sig-ed25519")) {
this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt);
} else if (line.startsWith("-----BEGIN")) {
cryptoLines = new ArrayList<String>();
cryptoLines.add(line);
......@@ -135,6 +147,9 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
this.signingKey = cryptoString;
} else if ("router-signature".equals(nextCrypto)) {
this.routerSignature = cryptoString;
} else if ("identity-ed25519".equals(nextCrypto)) {
this.identityEd25519 = cryptoString;
this.parseIdentityEd25519CryptoBlock(cryptoString);
} else if (this.failUnrecognizedDescriptorLines) {
throw new DescriptorParseException("Unrecognized crypto "
+ "block '" + cryptoString + "' in server descriptor.");
......@@ -392,6 +407,10 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl
}
this.extraInfoDigest = ParseHelper.parseTwentyByteHexString(line,
partsNoOpt[1]);
if (partsNoOpt.length >= 3) {
ParseHelper.parseThirtyTwoByteBase64String(line, partsNoOpt[2]);
this.extraInfoDigestSha256 = partsNoOpt[2];
}
}