commit f4fca35135482979f85f7891b075b46a194ef37e Author: Karsten Loesing karsten.loesing@gmx.net Date: Wed Dec 16 16:09:17 2015 +0100
Support (ntor-)onion-key crosscerts. --- CHANGELOG.md | 2 + .../torproject/descriptor/ServerDescriptor.java | 17 +++++ .../descriptor/impl/ServerDescriptorImpl.java | 50 +++++++++++++- .../descriptor/impl/ServerDescriptorImplTest.java | 73 ++++++++++++++++++++ 4 files changed, 139 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md index bfda75e..73e9a1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ - Include RSA-1024 signatures of SHA-1 digests of extra-info descriptors, which were parsed and discarded before. - Support hidden-service statistics in extra-info descriptors. + - Support onion-key and ntor-onion-key cross certificates in server + descriptors.
# Changes in version 1.0.0 - 2015-12-05 diff --git a/src/org/torproject/descriptor/ServerDescriptor.java b/src/org/torproject/descriptor/ServerDescriptor.java index 3266e9d..49eaa92 100644 --- a/src/org/torproject/descriptor/ServerDescriptor.java +++ b/src/org/torproject/descriptor/ServerDescriptor.java @@ -174,5 +174,22 @@ public interface ServerDescriptor extends Descriptor { * the first space after the "router-sig-ed25519" string, prefixed with * the string "Tor router descriptor signature v1". */ public String getRouterSignatureEd25519(); + + /* Return an RSA signature, generated using the onion-key, that proves + * that the party creating the descriptor had control over the secret + * key corresponding to the onion-key, or null if the descriptor does + * not contain such a signature. */ + public String getOnionKeyCrosscert(); + + /* Return an Ed25519 signature, generated using the ntor-onion-key, that + * proves that the party creating the descriptor had control over the + * secret key corresponding to the ntor-onion-key, or null if the + * descriptor does not contain such a signature. */ + public String getNtorOnionKeyCrosscert(); + + /* Return the sign of the Ed25519 public key corresponding to the ntor + * onion key as 0 or 1, or -1 if the descriptor does not contain this + * information. */ + public int getNtorOnionKeyCrosscertSign(); }
diff --git a/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java b/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java index 3dd6c40..1484866 100644 --- a/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java +++ b/src/org/torproject/descriptor/impl/ServerDescriptorImpl.java @@ -37,9 +37,9 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl + "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(","))); + + "signing-key,ipv6-policy,ntor-onion-key,onion-key-crosscert," + + "ntor-onion-key-crosscert,router-sig-ed25519,router-signature," + + "router-digest-sha256,router-digest").split(","))); this.checkAtMostOnceKeywords(atMostOnceKeywords); this.checkFirstKeyword("router"); if (this.getKeywordCount("accept") == 0 && @@ -131,6 +131,12 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl this.parseMasterKeyEd25519Line(line, lineNoOpt, partsNoOpt); } else if (keyword.equals("router-sig-ed25519")) { this.parseRouterSigEd25519Line(line, lineNoOpt, partsNoOpt); + } else if (keyword.equals("onion-key-crosscert")) { + this.parseOnionKeyCrosscert(line, lineNoOpt, partsNoOpt); + nextCrypto = "onion-key-crosscert"; + } else if (keyword.equals("ntor-onion-key-crosscert")) { + this.parseNtorOnionKeyCrosscert(line, lineNoOpt, partsNoOpt); + nextCrypto = "ntor-onion-key-crosscert"; } else if (line.startsWith("-----BEGIN")) { cryptoLines = new ArrayList<String>(); cryptoLines.add(line); @@ -150,6 +156,10 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl } else if ("identity-ed25519".equals(nextCrypto)) { this.identityEd25519 = cryptoString; this.parseIdentityEd25519CryptoBlock(cryptoString); + } else if ("onion-key-crosscert".equals(nextCrypto)) { + this.onionKeyCrosscert = cryptoString; + } else if ("ntor-onion-key-crosscert".equals(nextCrypto)) { + this.ntorOnionKeyCrosscert = cryptoString; } else if (this.failUnrecognizedDescriptorLines) { throw new DescriptorParseException("Unrecognized crypto " + "block '" + cryptoString + "' in server descriptor."); @@ -534,6 +544,25 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl } }
+ private void parseOnionKeyCrosscert(String line, String lineNoOpt, + String[] partsNoOpt) throws DescriptorParseException { + if (partsNoOpt.length != 1) { + throw new DescriptorParseException("Illegal line '" + line + "'."); + } + } + + private void parseNtorOnionKeyCrosscert(String line, String lineNoOpt, + String[] partsNoOpt) throws DescriptorParseException { + if (partsNoOpt.length != 2) { + throw new DescriptorParseException("Illegal line '" + line + "'."); + } + try { + this.ntorOnionKeyCrosscertSign = Integer.parseInt(partsNoOpt[1]); + } catch (NumberFormatException e) { + throw new DescriptorParseException("Illegal line '" + line + "'."); + } + } + private void parseIdentityEd25519CryptoBlock(String cryptoString) throws DescriptorParseException { String masterKeyEd25519FromIdentityEd25519 = @@ -834,5 +863,20 @@ public abstract class ServerDescriptorImpl extends DescriptorImpl public String getRouterSignatureEd25519() { return this.routerSignatureEd25519; } + + private String onionKeyCrosscert; + public String getOnionKeyCrosscert() { + return this.onionKeyCrosscert; + } + + private String ntorOnionKeyCrosscert; + public String getNtorOnionKeyCrosscert() { + return this.ntorOnionKeyCrosscert; + } + + private int ntorOnionKeyCrosscertSign = -1; + public int getNtorOnionKeyCrosscertSign() { + return ntorOnionKeyCrosscertSign; + } }
diff --git a/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java b/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java index a98df9f..56f1419 100644 --- a/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java +++ b/test/org/torproject/descriptor/impl/ServerDescriptorImplTest.java @@ -104,6 +104,20 @@ public class ServerDescriptorImplTest { db.signingKeyLines = lines; return new RelayServerDescriptorImpl(db.buildDescriptor(), true); } + private String onionKeyCrosscertLines = null; + private static ServerDescriptor createWithOnionKeyCrosscertLines( + String lines) throws DescriptorParseException { + DescriptorBuilder db = new DescriptorBuilder(); + db.onionKeyCrosscertLines = lines; + return new RelayServerDescriptorImpl(db.buildDescriptor(), true); + } + private String ntorOnionKeyCrosscertLines = null; + private static ServerDescriptor createWithNtorOnionKeyCrosscertLines( + String lines) throws DescriptorParseException { + DescriptorBuilder db = new DescriptorBuilder(); + db.ntorOnionKeyCrosscertLines = lines; + return new RelayServerDescriptorImpl(db.buildDescriptor(), true); + } private String exitPolicyLines = "reject *:*"; private static ServerDescriptor createWithExitPolicyLines( String lines) throws DescriptorParseException { @@ -274,6 +288,12 @@ public class ServerDescriptorImplTest { if (this.signingKeyLines != null) { sb.append(this.signingKeyLines + "\n"); } + if (this.onionKeyCrosscertLines != null) { + sb.append(this.onionKeyCrosscertLines + "\n"); + } + if (this.ntorOnionKeyCrosscertLines != null) { + sb.append(this.ntorOnionKeyCrosscertLines + "\n"); + } if (this.exitPolicyLines != null) { sb.append(this.exitPolicyLines + "\n"); } @@ -1481,5 +1501,58 @@ public class ServerDescriptorImplTest { MASTER_KEY_ED25519_LINE, ROUTER_SIG_ED25519_LINE + "\n" + ROUTER_SIG_ED25519_LINE); } + + private static final String ONION_KEY_CROSSCERT_LINES = + "onion-key-crosscert\n" + + "-----BEGIN CROSSCERT-----\n" + + "gVWpiNgG2FekW1uonr4KKoqykjr4bqUBKGZfu6s9rvsV1TThnquZNP6ZhX2IPdQA" + + "\nlfKtzFggGu/4BiJ5oTSDj2sK2DMjY3rjrMQZ3I/wJ25yhc9gxjqYqUYO9MmJwA" + + "Lp\nfYkqp/t4WchJpyva/4hK8vITsI6eT2BfY/DWMy/suIE=\n" + + "-----END CROSSCERT-----"; + + private static final String NTOR_ONION_KEY_CROSSCERT_LINES = + "ntor-onion-key-crosscert 1\n" + + "-----BEGIN ED25519 CERT-----\n" + + "AQoABiUeAdauu1MxYGMmGLTCPaoes0RvW7udeLc1t8LZ4P3CDo5bAN4nrRfbCfOt" + + "\nz2Nwqn8tER1a+Ry6Vs+ilMZA55Rag4+f6Zdb1fmHWknCxbQlLHpqHACMtemPda" + + "Ka\nErPtMuiEqAc=\n" + + "-----END ED25519 CERT-----"; + + @Test() + public void testOnionKeyCrosscert() throws DescriptorParseException { + ServerDescriptor descriptor = + DescriptorBuilder.createWithOnionKeyCrosscertLines( + ONION_KEY_CROSSCERT_LINES); + assertEquals(ONION_KEY_CROSSCERT_LINES.substring( + ONION_KEY_CROSSCERT_LINES.indexOf("\n") + 1), + descriptor.getOnionKeyCrosscert()); + } + + @Test(expected = DescriptorParseException.class) + public void testOnionKeyCrosscertDuplicate() + throws DescriptorParseException { + DescriptorBuilder.createWithOnionKeyCrosscertLines( + ONION_KEY_CROSSCERT_LINES + "\n" + ONION_KEY_CROSSCERT_LINES); + } + + @Test() + public void testNtorOnionKeyCrosscert() + throws DescriptorParseException { + ServerDescriptor descriptor = + DescriptorBuilder.createWithNtorOnionKeyCrosscertLines( + NTOR_ONION_KEY_CROSSCERT_LINES); + assertEquals(NTOR_ONION_KEY_CROSSCERT_LINES.substring( + NTOR_ONION_KEY_CROSSCERT_LINES.indexOf("\n") + 1), + descriptor.getNtorOnionKeyCrosscert()); + assertEquals(1, descriptor.getNtorOnionKeyCrosscertSign()); + } + + @Test(expected = DescriptorParseException.class) + public void testNtorOnionKeyCrosscertDuplicate() + throws DescriptorParseException { + DescriptorBuilder.createWithOnionKeyCrosscertLines( + NTOR_ONION_KEY_CROSSCERT_LINES + "\n" + + NTOR_ONION_KEY_CROSSCERT_LINES); + } }
tor-commits@lists.torproject.org