summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/address.c99
-rw-r--r--src/common/address.h3
-rw-r--r--src/common/aes.c36
-rw-r--r--src/common/compat.c20
-rw-r--r--src/common/compat.h10
-rw-r--r--src/common/compat_libevent.c88
-rw-r--r--src/common/compat_libevent.h7
-rw-r--r--src/common/compat_pthreads.c14
-rw-r--r--src/common/container.c13
-rw-r--r--src/common/container.h1
-rw-r--r--src/common/crypto.c763
-rw-r--r--src/common/crypto.h36
-rw-r--r--src/common/crypto_ed25519.c21
-rw-r--r--src/common/crypto_ed25519.h18
-rw-r--r--src/common/crypto_format.c41
-rw-r--r--src/common/include.am27
-rw-r--r--src/common/log.c26
-rw-r--r--src/common/sandbox.c12
-rw-r--r--src/common/tortls.c271
-rw-r--r--src/common/tortls.h33
-rw-r--r--src/common/util.c111
-rw-r--r--src/common/util.h16
-rw-r--r--src/common/util_codedigest.c13
-rw-r--r--src/common/workqueue.c5
-rw-r--r--src/ext/eventdns.c2
-rw-r--r--src/ext/trunnel/trunnel-impl.h2
-rw-r--r--src/ext/trunnel/trunnel.c2
-rw-r--r--src/ext/trunnel/trunnel.h2
-rw-r--r--src/or/buffers.c6
-rw-r--r--src/or/channel.c8
-rw-r--r--src/or/channel.h5
-rw-r--r--src/or/channeltls.c161
-rw-r--r--src/or/channeltls.h10
-rw-r--r--src/or/circuitbuild.c13
-rw-r--r--src/or/circuitstats.c6
-rw-r--r--src/or/circuituse.c29
-rw-r--r--src/or/config.c331
-rw-r--r--src/or/config.h11
-rw-r--r--src/or/config_codedigest.c13
-rw-r--r--src/or/connection.c30
-rw-r--r--src/or/connection_edge.c92
-rw-r--r--src/or/connection_or.c184
-rw-r--r--src/or/connection_or.h12
-rw-r--r--src/or/control.c1035
-rw-r--r--src/or/control.h62
-rw-r--r--src/or/dircollate.c260
-rw-r--r--src/or/dircollate.h50
-rw-r--r--src/or/directory.c44
-rw-r--r--src/or/dirserv.c79
-rw-r--r--src/or/dirvote.c97
-rw-r--r--src/or/dirvote.h9
-rw-r--r--src/or/dns.c2
-rw-r--r--src/or/entrynodes.c76
-rw-r--r--src/or/include.am80
-rw-r--r--src/or/keypin.c419
-rw-r--r--src/or/keypin.h46
-rw-r--r--src/or/main.c248
-rw-r--r--src/or/main.h1
-rw-r--r--src/or/microdesc.c1
-rw-r--r--src/or/or.h86
-rw-r--r--src/or/rendclient.c453
-rw-r--r--src/or/rendclient.h8
-rw-r--r--src/or/rendcommon.c221
-rw-r--r--src/or/rendcommon.h19
-rw-r--r--src/or/rendservice.c701
-rw-r--r--src/or/rendservice.h25
-rw-r--r--src/or/router.c317
-rw-r--r--src/or/router.h9
-rw-r--r--src/or/routerkeys.c658
-rw-r--r--src/or/routerkeys.h67
-rw-r--r--src/or/routerlist.c33
-rw-r--r--src/or/routerlist.h7
-rw-r--r--src/or/routerparse.c361
-rw-r--r--src/or/routerparse.h5
-rw-r--r--src/or/torcert.c285
-rw-r--r--src/or/torcert.h76
-rw-r--r--src/or/transports.c5
-rw-r--r--src/test/bench.c10
-rwxr-xr-xsrc/test/bt_test.py4
-rw-r--r--src/test/example_extrainfo.inc233
-rw-r--r--src/test/failing_routerdescs.inc901
-rw-r--r--src/test/include.am64
-rwxr-xr-xsrc/test/ntor_ref.py2
-rw-r--r--src/test/test-memwipe.c209
-rwxr-xr-xsrc/test/test-network.sh2
-rw-r--r--src/test/test.c8
-rw-r--r--src/test/test_address.c192
-rw-r--r--src/test/test_bt.sh.in9
-rw-r--r--src/test/test_bt_cl.c2
-rw-r--r--src/test/test_buffers.c54
-rw-r--r--src/test/test_channel.c2
-rwxr-xr-xsrc/test/test_cmdline_args.py311
-rw-r--r--src/test/test_config.c1771
-rw-r--r--src/test/test_containers.c38
-rw-r--r--src/test/test_controller.c163
-rw-r--r--src/test/test_controller_events.c99
-rw-r--r--src/test/test_crypto.c120
-rw-r--r--src/test/test_dir.c147
-rw-r--r--src/test/test_entrynodes.c3
-rw-r--r--src/test/test_hs.c263
-rw-r--r--src/test/test_keypin.c255
-rw-r--r--src/test/test_link_handshake.c928
-rw-r--r--src/test/test_microdesc.c94
-rw-r--r--src/test/test_ntor.sh.in9
-rw-r--r--src/test/test_routerkeys.c548
-rw-r--r--src/test/test_scheduler.c25
-rw-r--r--src/test/test_util.c50
-rw-r--r--src/test/test_util_slow.c2
-rw-r--r--src/test/test_workqueue.c6
-rw-r--r--src/test/test_zero_length_keys.sh.in10
-rw-r--r--src/test/testing_common.c4
-rwxr-xr-xsrc/test/zero_length_keys.sh20
-rw-r--r--src/tools/include.am28
-rw-r--r--src/tools/tor-checkkey.c2
-rw-r--r--src/tools/tor-gencert.c16
-rw-r--r--src/tools/tor-resolve.c4
-rw-r--r--src/trunnel/ed25519_cert.c887
-rw-r--r--src/trunnel/ed25519_cert.h288
-rw-r--r--src/trunnel/ed25519_cert.trunnel76
-rw-r--r--src/trunnel/include.am23
-rw-r--r--src/trunnel/link_handshake.c1885
-rw-r--r--src/trunnel/link_handshake.h654
-rw-r--r--src/trunnel/link_handshake.trunnel57
-rw-r--r--src/trunnel/pwbox.c2
-rw-r--r--src/trunnel/pwbox.h2
-rw-r--r--src/win32/orconfig.h2
126 files changed, 16528 insertions, 2404 deletions
diff --git a/src/common/address.c b/src/common/address.c
index 42a116a91e..6bd107889a 100644
--- a/src/common/address.c
+++ b/src/common/address.c
@@ -1504,47 +1504,22 @@ tor_addr_is_multicast(const tor_addr_t *a)
return 0;
}
-/** Set *<b>addr</b> to the IP address (if any) of whatever interface
- * connects to the Internet. This address should only be used in checking
- * whether our address has changed. Return 0 on success, -1 on failure.
+/** Attempt to retrieve IP address of current host by utilizing some
+ * UDP socket trickery. Only look for address of given <b>family</b>.
+ * Set result to *<b>addr</b>. Return 0 on success, -1 on failure.
*/
-MOCK_IMPL(int,
-get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
+STATIC int
+get_interface_address6_via_udp_socket_hack(int severity,
+ sa_family_t family,
+ tor_addr_t *addr)
{
- /* XXX really, this function should yield a smartlist of addresses. */
- smartlist_t *addrs;
- int sock=-1, r=-1;
struct sockaddr_storage my_addr, target_addr;
+ int sock=-1, r=-1;
socklen_t addr_len;
- tor_assert(addr);
-
- /* Try to do this the smart way if possible. */
- if ((addrs = get_interface_addresses_raw(severity))) {
- int rv = -1;
- SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
- if (family != AF_UNSPEC && family != tor_addr_family(a))
- continue;
- if (tor_addr_is_loopback(a) ||
- tor_addr_is_multicast(a))
- continue;
- tor_addr_copy(addr, a);
- rv = 0;
-
- /* If we found a non-internal address, declare success. Otherwise,
- * keep looking. */
- if (!tor_addr_is_internal(a, 0))
- break;
- } SMARTLIST_FOREACH_END(a);
-
- SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
- smartlist_free(addrs);
- return rv;
- }
-
- /* Okay, the smart way is out. */
memset(addr, 0, sizeof(tor_addr_t));
memset(&target_addr, 0, sizeof(target_addr));
+
/* Don't worry: no packets are sent. We just need to use a real address
* on the actual Internet. */
if (family == AF_INET6) {
@@ -1566,6 +1541,7 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
} else {
return -1;
}
+
if (sock < 0) {
int e = tor_socket_errno(-1);
log_fn(severity, LD_NET, "unable to create socket: %s",
@@ -1573,27 +1549,74 @@ get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
goto err;
}
- if (connect(sock,(struct sockaddr *)&target_addr, addr_len) < 0) {
+ if (tor_connect_socket(sock,(struct sockaddr *)&target_addr,
+ addr_len) < 0) {
int e = tor_socket_errno(sock);
log_fn(severity, LD_NET, "connect() failed: %s", tor_socket_strerror(e));
goto err;
}
- if (getsockname(sock,(struct sockaddr*)&my_addr, &addr_len)) {
+ if (tor_getsockname(sock,(struct sockaddr*)&my_addr, &addr_len)) {
int e = tor_socket_errno(sock);
log_fn(severity, LD_NET, "getsockname() to determine interface failed: %s",
tor_socket_strerror(e));
goto err;
}
- tor_addr_from_sockaddr(addr, (struct sockaddr*)&my_addr, NULL);
- r=0;
+ if (tor_addr_from_sockaddr(addr, (struct sockaddr*)&my_addr, NULL) == 0) {
+ if (tor_addr_is_loopback(addr) || tor_addr_is_multicast(addr)) {
+ log_fn(severity, LD_NET, "Address that we determined via UDP socket"
+ " magic is unsuitable for public comms.");
+ } else {
+ r=0;
+ }
+ }
+
err:
if (sock >= 0)
tor_close_socket(sock);
return r;
}
+/** Set *<b>addr</b> to the IP address (if any) of whatever interface
+ * connects to the Internet. This address should only be used in checking
+ * whether our address has changed. Return 0 on success, -1 on failure.
+ */
+MOCK_IMPL(int,
+get_interface_address6,(int severity, sa_family_t family, tor_addr_t *addr))
+{
+ /* XXX really, this function should yield a smartlist of addresses. */
+ smartlist_t *addrs;
+ tor_assert(addr);
+
+ /* Try to do this the smart way if possible. */
+ if ((addrs = get_interface_addresses_raw(severity))) {
+ int rv = -1;
+ SMARTLIST_FOREACH_BEGIN(addrs, tor_addr_t *, a) {
+ if (family != AF_UNSPEC && family != tor_addr_family(a))
+ continue;
+ if (tor_addr_is_loopback(a) ||
+ tor_addr_is_multicast(a))
+ continue;
+
+ tor_addr_copy(addr, a);
+ rv = 0;
+
+ /* If we found a non-internal address, declare success. Otherwise,
+ * keep looking. */
+ if (!tor_addr_is_internal(a, 0))
+ break;
+ } SMARTLIST_FOREACH_END(a);
+
+ SMARTLIST_FOREACH(addrs, tor_addr_t *, a, tor_free(a));
+ smartlist_free(addrs);
+ return rv;
+ }
+
+ /* Okay, the smart way is out. */
+ return get_interface_address6_via_udp_socket_hack(severity,family,addr);
+}
+
/* ======
* IPv4 helpers
* XXXX024 IPv6 deprecate some of these.
diff --git a/src/common/address.h b/src/common/address.h
index df835e917a..cd80615f93 100644
--- a/src/common/address.h
+++ b/src/common/address.h
@@ -274,6 +274,9 @@ tor_addr_port_t *tor_addr_port_new(const tor_addr_t *addr, uint16_t port);
#ifdef ADDRESS_PRIVATE
STATIC smartlist_t *get_interface_addresses_raw(int severity);
+STATIC int get_interface_address6_via_udp_socket_hack(int severity,
+ sa_family_t family,
+ tor_addr_t *addr);
#ifdef HAVE_IFADDRS_TO_SMARTLIST
STATIC smartlist_t *ifaddrs_to_smartlist(const struct ifaddrs *ifa);
diff --git a/src/common/aes.c b/src/common/aes.c
index 7651f1d93a..8b9d81fa10 100644
--- a/src/common/aes.c
+++ b/src/common/aes.c
@@ -25,18 +25,19 @@
#endif
#include <openssl/opensslv.h>
+#include "crypto.h"
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
+#error "We require OpenSSL >= 1.0.0"
+#endif
+
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
-#include "crypto.h"
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)
-/* See comments about which counter mode implementation to use below. */
#include <openssl/modes.h>
-#define CAN_USE_OPENSSL_CTR
-#endif
#include "compat.h"
#include "aes.h"
#include "util.h"
@@ -189,11 +190,9 @@ struct aes_cnt_cipher {
* we're testing it or because we have hardware acceleration configured */
static int should_use_EVP = 0;
-#ifdef CAN_USE_OPENSSL_CTR
/** True iff we have tested the counter-mode implementation and found that it
* doesn't have the counter-mode bug from OpenSSL 1.0.0. */
static int should_use_openssl_CTR = 0;
-#endif
/** Check whether we should use the EVP interface for AES. If <b>force_val</b>
* is nonnegative, we use use EVP iff it is true. Otherwise, we use EVP
@@ -235,7 +234,6 @@ evaluate_evp_for_aes(int force_val)
int
evaluate_ctr_for_aes(void)
{
-#ifdef CAN_USE_OPENSSL_CTR
/* Result of encrypting an all-zero block with an all-zero 128-bit AES key.
* This should be the same as encrypting an all-zero block with an all-zero
* 128-bit AES key in counter mode, starting at position 0 of the stream.
@@ -268,10 +266,6 @@ evaluate_ctr_for_aes(void)
"mode; using it.");
should_use_openssl_CTR = 1;
}
-#else
- log_info(LD_CRYPTO, "This version of OpenSSL has a slow implementation of "
- "counter mode; not using it.");
-#endif
return 0;
}
@@ -331,7 +325,7 @@ static void
aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits)
{
if (should_use_EVP) {
- const EVP_CIPHER *c;
+ const EVP_CIPHER *c = 0;
switch (key_bits) {
case 128: c = EVP_aes_128_ecb(); break;
case 192: c = EVP_aes_192_ecb(); break;
@@ -356,11 +350,9 @@ aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits)
cipher->pos = 0;
-#ifdef CAN_USE_OPENSSL_CTR
if (should_use_openssl_CTR)
memset(cipher->buf, 0, sizeof(cipher->buf));
else
-#endif
aes_fill_buf_(cipher);
}
@@ -386,7 +378,6 @@ aes_cipher_free(aes_cnt_cipher_t *cipher)
#define UPDATE_CTR_BUF(c, n)
#endif
-#ifdef CAN_USE_OPENSSL_CTR
/* Helper function to use EVP with openssl's counter-mode wrapper. */
static void
evp_block128_fn(const uint8_t in[16],
@@ -397,7 +388,6 @@ evp_block128_fn(const uint8_t in[16],
int inl=16, outl=16;
EVP_EncryptUpdate(ctx, out, &outl, in, inl);
}
-#endif
/** Encrypt <b>len</b> bytes from <b>input</b>, storing the result in
* <b>output</b>. Uses the key in <b>cipher</b>, and advances the counter
@@ -407,7 +397,6 @@ void
aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
char *output)
{
-#ifdef CAN_USE_OPENSSL_CTR
if (should_use_openssl_CTR) {
if (cipher->using_evp) {
/* In openssl 1.0.0, there's an if'd out EVP_aes_128_ctr in evp.h. If
@@ -431,9 +420,7 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
&cipher->pos);
}
return;
- } else
-#endif
- {
+ } else {
int c = cipher->pos;
if (PREDICT_UNLIKELY(!len)) return;
@@ -466,13 +453,10 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
void
aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len)
{
-#ifdef CAN_USE_OPENSSL_CTR
if (should_use_openssl_CTR) {
aes_crypt(cipher, data, len, data);
return;
- } else
-#endif
- {
+ } else {
int c = cipher->pos;
if (PREDICT_UNLIKELY(!len)) return;
@@ -512,9 +496,7 @@ aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv)
cipher->pos = 0;
memcpy(cipher->ctr_buf.buf, iv, 16);
-#ifdef CAN_USE_OPENSSL_CTR
if (!should_use_openssl_CTR)
-#endif
aes_fill_buf_(cipher);
}
diff --git a/src/common/compat.c b/src/common/compat.c
index 1788e32ee3..8da7ef3f69 100644
--- a/src/common/compat.c
+++ b/src/common/compat.c
@@ -1156,12 +1156,20 @@ mark_socket_open(tor_socket_t s)
/** @} */
/** As socket(), but counts the number of open sockets. */
-tor_socket_t
-tor_open_socket(int domain, int type, int protocol)
+MOCK_IMPL(tor_socket_t,
+tor_open_socket,(int domain, int type, int protocol))
{
return tor_open_socket_with_extensions(domain, type, protocol, 1, 0);
}
+/** Mockable wrapper for connect(). */
+MOCK_IMPL(tor_socket_t,
+tor_connect_socket,(tor_socket_t socket,const struct sockaddr *address,
+ socklen_t address_len))
+{
+ return connect(socket,address,address_len);
+}
+
/** As socket(), but creates a nonblocking socket and
* counts the number of open sockets. */
tor_socket_t
@@ -1308,6 +1316,14 @@ get_n_open_sockets(void)
return n;
}
+/** Mockable wrapper for getsockname(). */
+MOCK_IMPL(int,
+tor_getsockname,(tor_socket_t socket, struct sockaddr *address,
+ socklen_t *address_len))
+{
+ return getsockname(socket, address, address_len);
+}
+
/** Turn <b>socket</b> into a nonblocking socket. Return 0 on success, -1
* on failure.
*/
diff --git a/src/common/compat.h b/src/common/compat.h
index 11b41cded9..5189b7e056 100644
--- a/src/common/compat.h
+++ b/src/common/compat.h
@@ -463,7 +463,8 @@ int tor_close_socket(tor_socket_t s);
tor_socket_t tor_open_socket_with_extensions(
int domain, int type, int protocol,
int cloexec, int nonblock);
-tor_socket_t tor_open_socket(int domain, int type, int protocol);
+MOCK_DECL(tor_socket_t,
+tor_open_socket,(int domain, int type, int protocol));
tor_socket_t tor_open_socket_nonblocking(int domain, int type, int protocol);
tor_socket_t tor_accept_socket(tor_socket_t sockfd, struct sockaddr *addr,
socklen_t *len);
@@ -474,8 +475,15 @@ tor_socket_t tor_accept_socket_with_extensions(tor_socket_t sockfd,
struct sockaddr *addr,
socklen_t *len,
int cloexec, int nonblock);
+MOCK_DECL(tor_socket_t,
+tor_connect_socket,(tor_socket_t socket,const struct sockaddr *address,
+ socklen_t address_len));
int get_n_open_sockets(void);
+MOCK_DECL(int,
+tor_getsockname,(tor_socket_t socket, struct sockaddr *address,
+ socklen_t *address_len));
+
#define tor_socket_send(s, buf, len, flags) send(s, buf, len, flags)
#define tor_socket_recv(s, buf, len, flags) recv(s, buf, len, flags)
diff --git a/src/common/compat_libevent.c b/src/common/compat_libevent.c
index 15308dd4cb..a366b6c9c6 100644
--- a/src/common/compat_libevent.c
+++ b/src/common/compat_libevent.c
@@ -56,11 +56,6 @@ typedef uint32_t le_version_t;
* it is. */
#define LE_OTHER V(0,0,99)
-#if 0
-static le_version_t tor_get_libevent_version(const char **v_out);
-#endif
-
-#if defined(HAVE_EVENT_SET_LOG_CALLBACK) || defined(RUNNING_DOXYGEN)
/** A string which, if it appears in a libevent log, should be ignored. */
static const char *suppress_msg = NULL;
/** Callback function passed to event_set_log() so we can intercept
@@ -107,17 +102,6 @@ suppress_libevent_log_msg(const char *msg)
{
suppress_msg = msg;
}
-#else
-void
-configure_libevent_logging(void)
-{
-}
-void
-suppress_libevent_log_msg(const char *msg)
-{
- (void)msg;
-}
-#endif
#ifndef HAVE_EVENT2_EVENT_H
/** Work-alike replacement for event_new() on pre-Libevent-2.0 systems. */
@@ -275,19 +259,11 @@ tor_libevent_initialize(tor_libevent_cfg *torcfg)
exit(1);
}
-#if defined(HAVE_EVENT_GET_VERSION) && defined(HAVE_EVENT_GET_METHOD)
/* Making this a NOTICE for now so we can link bugs to a libevent versions
* or methods better. */
log_info(LD_GENERAL,
"Initialized libevent version %s using method %s. Good.",
event_get_version(), tor_libevent_get_method());
-#else
- log_notice(LD_GENERAL,
- "Initialized old libevent (version 1.0b or earlier).");
- log_warn(LD_GENERAL,
- "You have a *VERY* old version of libevent. It is likely to be buggy; "
- "please build Tor with a more recent version.");
-#endif
#ifdef USE_BUFFEREVENTS
tor_libevent_set_tick_timeout(torcfg->msec_per_tick);
@@ -301,27 +277,14 @@ tor_libevent_get_base, (void))
return the_event_base;
}
-#ifndef HAVE_EVENT_BASE_LOOPEXIT
-/** Replacement for event_base_loopexit on some very old versions of Libevent
- * that we are not yet brave enough to deprecate. */
-int
-tor_event_base_loopexit(struct event_base *base, struct timeval *tv)
-{
- tor_assert(base == the_event_base);
- return event_loopexit(tv);
-}
-#endif
-
/** Return the name of the Libevent backend we're using. */
const char *
tor_libevent_get_method(void)
{
#ifdef HAVE_EVENT2_EVENT_H
return event_base_get_method(the_event_base);
-#elif defined(HAVE_EVENT_GET_METHOD)
- return event_get_method();
#else
- return "<unknown>";
+ return event_get_method();
#endif
}
@@ -376,54 +339,12 @@ le_versions_compatibility(le_version_t v)
return 5;
}
-#if 0
-/** Return the version number of the currently running version of Libevent.
- * See le_version_t for info on the format.
- */
-static le_version_t
-tor_get_libevent_version(const char **v_out)
-{
- const char *v;
- le_version_t r;
-#if defined(HAVE_EVENT_GET_VERSION_NUMBER)
- v = event_get_version();
- r = event_get_version_number();
-#elif defined (HAVE_EVENT_GET_VERSION)
- v = event_get_version();
- r = tor_decode_libevent_version(v);
-#else
- v = "pre-1.0c";
- r = LE_OLD;
-#endif
- if (v_out)
- *v_out = v;
- return r;
-}
-#endif
-
/** Return a string representation of the version of the currently running
* version of Libevent. */
const char *
tor_libevent_get_version_str(void)
{
-#ifdef HAVE_EVENT_GET_VERSION
return event_get_version();
-#else
- return "pre-1.0c";
-#endif
-}
-
-/**
- * Compare the current Libevent method and version to a list of versions
- * which are known not to work. Warn the user as appropriate.
- */
-void
-tor_check_libevent_version(const char *m, int server,
- const char **badness_out)
-{
- (void) m;
- (void) server;
- *badness_out = NULL;
}
#if defined(LIBEVENT_VERSION)
@@ -452,7 +373,7 @@ tor_check_libevent_header_compatibility(void)
/* In libevent versions before 2.0, it's hard to keep binary compatibility
* between upgrades, and unpleasant to detect when the version we compiled
* against is unlike the version we have linked against. Here's how. */
-#if defined(HEADER_VERSION) && defined(HAVE_EVENT_GET_VERSION)
+#if defined(HEADER_VERSION)
/* We have a header-file version and a function-call version. Easy. */
if (strcmp(HEADER_VERSION, event_get_version())) {
le_version_t v1, v2;
@@ -474,7 +395,7 @@ tor_check_libevent_header_compatibility(void)
else
log_info(LD_GENERAL, "I think these versions are binary-compatible.");
}
-#elif defined(HAVE_EVENT_GET_VERSION)
+#else
/* event_get_version but no _EVENT_VERSION. We might be in 1.4.0-beta or
earlier, where that's normal. To see whether we were compiled with an
earlier version, let's see whether the struct event defines MIN_HEAP_IDX.
@@ -504,9 +425,6 @@ tor_check_libevent_header_compatibility(void)
}
#endif
-#elif defined(HEADER_VERSION)
-#warn "_EVENT_VERSION is defined but not get_event_version(): Libevent is odd."
-#else
/* Your libevent is ancient. */
#endif
}
diff --git a/src/common/compat_libevent.h b/src/common/compat_libevent.h
index 6bbfae0056..39181efb7b 100644
--- a/src/common/compat_libevent.h
+++ b/src/common/compat_libevent.h
@@ -52,12 +52,7 @@ periodic_timer_t *periodic_timer_new(struct event_base *base,
void *data);
void periodic_timer_free(periodic_timer_t *);
-#ifdef HAVE_EVENT_BASE_LOOPEXIT
#define tor_event_base_loopexit event_base_loopexit
-#else
-struct timeval;
-int tor_event_base_loopexit(struct event_base *base, struct timeval *tv);
-#endif
/** Defines a configuration for using libevent with Tor: passed as an argument
* to tor_libevent_initialize() to describe how we want to set up. */
@@ -74,8 +69,6 @@ typedef struct tor_libevent_cfg {
void tor_libevent_initialize(tor_libevent_cfg *cfg);
MOCK_DECL(struct event_base *, tor_libevent_get_base, (void));
const char *tor_libevent_get_method(void);
-void tor_check_libevent_version(const char *m, int server,
- const char **badness_out);
void tor_check_libevent_header_compatibility(void);
const char *tor_libevent_get_version_str(void);
const char *tor_libevent_get_header_version_str(void);
diff --git a/src/common/compat_pthreads.c b/src/common/compat_pthreads.c
index 246076b276..487f7e5851 100644
--- a/src/common/compat_pthreads.c
+++ b/src/common/compat_pthreads.c
@@ -50,7 +50,8 @@ static pthread_attr_t attr_detached;
static int threads_initialized = 0;
/** Minimalist interface to run a void function in the background. On
- * Unix calls fork, on win32 calls beginthread. Returns -1 on failure.
+ * Unix calls pthread_create, on win32 calls beginthread. Returns -1 on
+ * failure.
* func should not return, but rather should call spawn_exit.
*
* NOTE: if <b>data</b> is used, it should not be allocated on the stack,
@@ -91,10 +92,9 @@ static pthread_mutexattr_t attr_recursive;
void
tor_mutex_init(tor_mutex_t *mutex)
{
- int err;
if (PREDICT_UNLIKELY(!threads_initialized))
tor_threads_init();
- err = pthread_mutex_init(&mutex->mutex, &attr_recursive);
+ const int err = pthread_mutex_init(&mutex->mutex, &attr_recursive);
if (PREDICT_UNLIKELY(err)) {
log_err(LD_GENERAL, "Error %d creating a mutex.", err);
tor_fragile_assert();
@@ -278,12 +278,14 @@ tor_threads_init(void)
if (!threads_initialized) {
pthread_mutexattr_init(&attr_recursive);
pthread_mutexattr_settype(&attr_recursive, PTHREAD_MUTEX_RECURSIVE);
- tor_assert(0==pthread_attr_init(&attr_detached));
+ const int ret1 = pthread_attr_init(&attr_detached);
+ tor_assert(ret1 == 0);
#ifndef PTHREAD_CREATE_DETACHED
#define PTHREAD_CREATE_DETACHED 1
#endif
- tor_assert(0==pthread_attr_setdetachstate(&attr_detached,
- PTHREAD_CREATE_DETACHED));
+ const int ret2 =
+ pthread_attr_setdetachstate(&attr_detached, PTHREAD_CREATE_DETACHED);
+ tor_assert(ret2 == 0);
threads_initialized = 1;
set_main_thread();
}
diff --git a/src/common/container.c b/src/common/container.c
index 864fd8a552..082afb51ee 100644
--- a/src/common/container.c
+++ b/src/common/container.c
@@ -208,6 +208,19 @@ smartlist_string_pos(const smartlist_t *sl, const char *element)
return -1;
}
+/** If <b>element</b> is the same pointer as an element of <b>sl</b>, return
+ * that element's index. Otherwise, return -1. */
+int
+smartlist_pos(const smartlist_t *sl, const void *element)
+{
+ int i;
+ if (!sl) return -1;
+ for (i=0; i < sl->num_used; i++)
+ if (element == sl->list[i])
+ return i;
+ return -1;
+}
+
/** Return true iff <b>sl</b> has some element E such that
* !strcasecmp(E,<b>element</b>)
*/
diff --git a/src/common/container.h b/src/common/container.h
index 457b5e4ea0..125900c8ca 100644
--- a/src/common/container.h
+++ b/src/common/container.h
@@ -38,6 +38,7 @@ void smartlist_reverse(smartlist_t *sl);
void smartlist_string_remove(smartlist_t *sl, const char *element);
int smartlist_contains(const smartlist_t *sl, const void *element);
int smartlist_contains_string(const smartlist_t *sl, const char *element);
+int smartlist_pos(const smartlist_t *sl, const void *element);
int smartlist_string_pos(const smartlist_t *, const char *elt);
int smartlist_contains_string_case(const smartlist_t *sl, const char *element);
int smartlist_contains_int_as_string(const smartlist_t *sl, int num);
diff --git a/src/common/crypto.c b/src/common/crypto.c
index a68294a756..afaf93aed2 100644
--- a/src/common/crypto.c
+++ b/src/common/crypto.c
@@ -24,13 +24,21 @@
#undef OCSP_RESPONSE
#endif
+#include <openssl/opensslv.h>
+
+#define CRYPTO_PRIVATE
+#include "crypto.h"
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
+#error "We require OpenSSL >= 1.0.0"
+#endif
+
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/engine.h>
#include <openssl/rand.h>
-#include <openssl/opensslv.h>
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/conf.h>
@@ -49,19 +57,13 @@
#include <sys/fcntl.h>
#endif
-#define CRYPTO_PRIVATE
-#include "crypto.h"
-#include "../common/torlog.h"
+#include "torlog.h"
#include "aes.h"
-#include "../common/util.h"
+#include "util.h"
#include "container.h"
#include "compat.h"
#include "sandbox.h"
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8)
-#error "We require OpenSSL >= 0.9.8"
-#endif
-
#ifdef ANDROID
/* Android's OpenSSL seems to have removed all of its Engine support. */
#define DISABLE_ENGINES
@@ -300,16 +302,9 @@ crypto_early_init(void)
SSLeay(), SSLeay_version(SSLEAY_VERSION));
}
- if (SSLeay() < OPENSSL_V_SERIES(1,0,0)) {
- log_notice(LD_CRYPTO,
- "Your OpenSSL version seems to be %s. We recommend 1.0.0 "
- "or later.",
- crypto_openssl_get_version_str());
- }
-
crypto_force_rand_ssleay();
- if (crypto_seed_rng(1) < 0)
+ if (crypto_seed_rng() < 0)
return -1;
if (crypto_init_siphash_key() < 0)
return -1;
@@ -391,7 +386,7 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
}
if (crypto_force_rand_ssleay()) {
- if (crypto_seed_rng(1) < 0)
+ if (crypto_seed_rng() < 0)
return -1;
}
@@ -834,7 +829,7 @@ crypto_pk_public_exponent_ok(crypto_pk_t *env)
* Note that this may leak information about the keys through timing.
*/
int
-crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b)
+crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b)
{
int result;
char a_is_non_null = (a != NULL) && (a->key != NULL);
@@ -860,19 +855,19 @@ crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b)
* Note that this may leak information about the keys through timing.
*/
int
-crypto_pk_eq_keys(crypto_pk_t *a, crypto_pk_t *b)
+crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b)
{
return (crypto_pk_cmp_keys(a, b) == 0);
}
/** Return the size of the public key modulus in <b>env</b>, in bytes. */
size_t
-crypto_pk_keysize(crypto_pk_t *env)
+crypto_pk_keysize(const crypto_pk_t *env)
{
tor_assert(env);
tor_assert(env->key);
- return (size_t) RSA_size(env->key);
+ return (size_t) RSA_size((RSA*)env->key);
}
/** Return the size of the public key modulus of <b>env</b>, in bits. */
@@ -1001,7 +996,7 @@ crypto_pk_private_decrypt(crypto_pk_t *env, char *to,
* at least the length of the modulus of <b>env</b>.
*/
int
-crypto_pk_public_checksig(crypto_pk_t *env, char *to,
+crypto_pk_public_checksig(const crypto_pk_t *env, char *to,
size_t tolen,
const char *from, size_t fromlen)
{
@@ -1073,7 +1068,7 @@ crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data,
* at least the length of the modulus of <b>env</b>.
*/
int
-crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen,
+crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
const char *from, size_t fromlen)
{
int r;
@@ -1088,7 +1083,7 @@ crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen,
r = RSA_private_encrypt((int)fromlen,
(unsigned char*)from, (unsigned char*)to,
- env->key, RSA_PKCS1_PADDING);
+ (RSA*)env->key, RSA_PKCS1_PADDING);
if (r<0) {
crypto_log_errors(LOG_WARN, "generating RSA signature");
return -1;
@@ -1302,7 +1297,7 @@ crypto_pk_get_digest(const crypto_pk_t *pk, char *digest_out)
unsigned char *buf = NULL;
int len;
- len = i2d_RSAPublicKey(pk->key, &buf);
+ len = i2d_RSAPublicKey((RSA*)pk->key, &buf);
if (len < 0 || buf == NULL)
return -1;
if (crypto_digest(digest_out, (char*)buf, len) < 0) {
@@ -1401,6 +1396,78 @@ crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out)
return 0;
}
+/** Given a crypto_pk_t <b>pk</b>, allocate a new buffer containing the
+ * Base64 encoding of the DER representation of the private key as a NUL
+ * terminated string, and return it via <b>priv_out</b>. Return 0 on
+ * sucess, -1 on failure.
+ *
+ * It is the caller's responsibility to sanitize and free the resulting buffer.
+ */
+int
+crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out)
+{
+ unsigned char *der = NULL;
+ int der_len;
+ int ret = -1;
+
+ *priv_out = NULL;
+
+ der_len = i2d_RSAPrivateKey(pk->key, &der);
+ if (der_len < 0 || der == NULL)
+ return ret;
+
+ size_t priv_len = base64_encode_size(der_len, 0) + 1;
+ char *priv = tor_malloc_zero(priv_len);
+ if (base64_encode(priv, priv_len, (char *)der, der_len, 0) >= 0) {
+ *priv_out = priv;
+ ret = 0;
+ } else {
+ tor_free(priv);
+ }
+
+ memwipe(der, 0, der_len);
+ OPENSSL_free(der);
+ return ret;
+}
+
+/** Given a string containing the Base64 encoded DER representation of the
+ * private key <b>str</b>, decode and return the result on success, or NULL
+ * on failure.
+ */
+crypto_pk_t *
+crypto_pk_base64_decode(const char *str, size_t len)
+{
+ crypto_pk_t *pk = NULL;
+
+ char *der = tor_malloc_zero(len + 1);
+ int der_len = base64_decode(der, len, str, len);
+ if (der_len <= 0) {
+ log_warn(LD_CRYPTO, "Stored RSA private key seems corrupted (base64).");
+ goto out;
+ }
+
+ const unsigned char *dp = (unsigned char*)der; /* Shut the compiler up. */
+ RSA *rsa = d2i_RSAPrivateKey(NULL, &dp, der_len);
+ if (!rsa) {
+ crypto_log_errors(LOG_WARN, "decoding private key");
+ goto out;
+ }
+
+ pk = crypto_new_pk_from_rsa_(rsa);
+
+ /* Make sure it's valid. */
+ if (crypto_pk_check_key(pk) <= 0) {
+ crypto_pk_free(pk);
+ pk = NULL;
+ goto out;
+ }
+
+ out:
+ memwipe(der, 0, len + 1);
+ tor_free(der);
+ return pk;
+}
+
/* symmetric crypto */
/** Return a pointer to the key set for the cipher in <b>env</b>.
@@ -1728,7 +1795,24 @@ crypto_digest_assign(crypto_digest_t *into,
* <b>out_len</b> must be \<= DIGEST256_LEN. */
void
crypto_digest_smartlist(char *digest_out, size_t len_out,
- const smartlist_t *lst, const char *append,
+ const smartlist_t *lst,
+ const char *append,
+ digest_algorithm_t alg)
+{
+ crypto_digest_smartlist_prefix(digest_out, len_out, NULL, lst, append, alg);
+}
+
+/** Given a list of strings in <b>lst</b>, set the <b>len_out</b>-byte digest
+ * at <b>digest_out</b> to the hash of the concatenation of: the
+ * optional string <b>prepend</b>, those strings,
+ * and the optional string <b>append</b>, computed with the algorithm
+ * <b>alg</b>.
+ * <b>out_len</b> must be \<= DIGEST256_LEN. */
+void
+crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
+ const char *prepend,
+ const smartlist_t *lst,
+ const char *append,
digest_algorithm_t alg)
{
crypto_digest_t *d;
@@ -1736,6 +1820,8 @@ crypto_digest_smartlist(char *digest_out, size_t len_out,
d = crypto_digest_new();
else
d = crypto_digest256_new(alg);
+ if (prepend)
+ crypto_digest_add_bytes(d, prepend, strlen(prepend));
SMARTLIST_FOREACH(lst, const char *, cp,
crypto_digest_add_bytes(d, cp, strlen(cp)));
if (append)
@@ -1772,235 +1858,12 @@ static BIGNUM *dh_param_p_tls = NULL;
/** Shared G parameter for our DH key exchanges. */
static BIGNUM *dh_param_g = NULL;
-/** Generate and return a reasonable and safe DH parameter p. */
-static BIGNUM *
-crypto_generate_dynamic_dh_modulus(void)
-{
- BIGNUM *dynamic_dh_modulus;
- DH *dh_parameters;
- int r, dh_codes;
- char *s;
-
- dynamic_dh_modulus = BN_new();
- tor_assert(dynamic_dh_modulus);
-
- dh_parameters = DH_new();
- tor_assert(dh_parameters);
-
- r = DH_generate_parameters_ex(dh_parameters,
- DH_BYTES*8, DH_GENERATOR, NULL);
- tor_assert(r == 0);
-
- r = DH_check(dh_parameters, &dh_codes);
- tor_assert(r && !dh_codes);
-
- BN_copy(dynamic_dh_modulus, dh_parameters->p);
- tor_assert(dynamic_dh_modulus);
-
- DH_free(dh_parameters);
-
- { /* log the dynamic DH modulus: */
- s = BN_bn2hex(dynamic_dh_modulus);
- tor_assert(s);
- log_info(LD_OR, "Dynamic DH modulus generated: [%s]", s);
- OPENSSL_free(s);
- }
-
- return dynamic_dh_modulus;
-}
-
-/** Store our dynamic DH modulus (and its group parameters) to
- <b>fname</b> for future use. */
-static int
-crypto_store_dynamic_dh_modulus(const char *fname)
-{
- int len, new_len;
- DH *dh = NULL;
- unsigned char *dh_string_repr = NULL;
- char *base64_encoded_dh = NULL;
- char *file_string = NULL;
- int retval = -1;
- static const char file_header[] = "# This file contains stored Diffie-"
- "Hellman parameters for future use.\n# You *do not* need to edit this "
- "file.\n\n";
-
- tor_assert(fname);
-
- if (!dh_param_p_tls) {
- log_info(LD_CRYPTO, "Tried to store a DH modulus that does not exist.");
- goto done;
- }
-
- if (!(dh = DH_new()))
- goto done;
- if (!(dh->p = BN_dup(dh_param_p_tls)))
- goto done;
- if (!(dh->g = BN_new()))
- goto done;
- if (!BN_set_word(dh->g, DH_GENERATOR))
- goto done;
-
- len = i2d_DHparams(dh, &dh_string_repr);
- if ((len < 0) || (dh_string_repr == NULL)) {
- log_warn(LD_CRYPTO, "Error occured while DER encoding DH modulus (2).");
- goto done;
- }
-
- base64_encoded_dh = tor_calloc(len, 2); /* should be enough */
- new_len = base64_encode(base64_encoded_dh, len * 2,
- (char *)dh_string_repr, len);
- if (new_len < 0) {
- log_warn(LD_CRYPTO, "Error occured while base64-encoding DH modulus.");
- goto done;
- }
-
- /* concatenate file header and the dh parameters blob */
- new_len = tor_asprintf(&file_string, "%s%s", file_header, base64_encoded_dh);
-
- /* write to file */
- if (write_bytes_to_new_file(fname, file_string, new_len, 0) < 0) {
- log_info(LD_CRYPTO, "'%s' was already occupied.", fname);
- goto done;
- }
-
- retval = 0;
-
- done:
- if (dh)
- DH_free(dh);
- if (dh_string_repr)
- OPENSSL_free(dh_string_repr);
- tor_free(base64_encoded_dh);
- tor_free(file_string);
-
- return retval;
-}
-
-/** Return the dynamic DH modulus stored in <b>fname</b>. If there is no
- dynamic DH modulus stored in <b>fname</b>, return NULL. */
-static BIGNUM *
-crypto_get_stored_dynamic_dh_modulus(const char *fname)
-{
- int retval;
- char *contents = NULL;
- const char *contents_tmp = NULL;
- int dh_codes;
- DH *stored_dh = NULL;
- BIGNUM *dynamic_dh_modulus = NULL;
- int length = 0;
- unsigned char *base64_decoded_dh = NULL;
- const unsigned char *cp = NULL;
-
- tor_assert(fname);
-
- contents = read_file_to_str(fname, RFTS_IGNORE_MISSING, NULL);
- if (!contents) {
- log_info(LD_CRYPTO, "Could not open file '%s'", fname);
- goto done; /*usually means that ENOENT. don't try to move file to broken.*/
- }
-
- /* skip the file header */
- contents_tmp = eat_whitespace(contents);
- if (!*contents_tmp) {
- log_warn(LD_CRYPTO, "Stored dynamic DH modulus file "
- "seems corrupted (eat_whitespace).");
- goto err;
- }
-
- /* 'fname' contains the DH parameters stored in base64-ed DER
- * format. We are only interested in the DH modulus.
- * NOTE: We allocate more storage here than we need. Since we're already
- * doing that, we can also add 1 byte extra to appease Coverity's
- * scanner. */
-
- cp = base64_decoded_dh = tor_malloc_zero(strlen(contents_tmp) + 1);
- length = base64_decode((char *)base64_decoded_dh, strlen(contents_tmp),
- contents_tmp, strlen(contents_tmp));
- if (length < 0) {
- log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (base64).");
- goto err;
- }
-
- stored_dh = d2i_DHparams(NULL, &cp, length);
- if ((!stored_dh) || (cp - base64_decoded_dh != length)) {
- log_warn(LD_CRYPTO, "Stored dynamic DH modulus seems corrupted (d2i).");
- goto err;
- }
-
- { /* check the cryptographic qualities of the stored dynamic DH modulus: */
- retval = DH_check(stored_dh, &dh_codes);
- if (!retval || dh_codes) {
- log_warn(LD_CRYPTO, "Stored dynamic DH modulus is not a safe prime.");
- goto err;
- }
-
- retval = DH_size(stored_dh);
- if (retval < DH_BYTES) {
- log_warn(LD_CRYPTO, "Stored dynamic DH modulus is smaller "
- "than '%d' bits.", DH_BYTES*8);
- goto err;
- }
-
- if (!BN_is_word(stored_dh->g, 2)) {
- log_warn(LD_CRYPTO, "Stored dynamic DH parameters do not use '2' "
- "as the group generator.");
- goto err;
- }
- }
-
- { /* log the dynamic DH modulus: */
- char *s = BN_bn2hex(stored_dh->p);
- tor_assert(s);
- log_info(LD_OR, "Found stored dynamic DH modulus: [%s]", s);
- OPENSSL_free(s);
- }
-
- goto done;
-
- err:
-
- {
- /* move broken prime to $filename.broken */
- char *fname_new=NULL;
- tor_asprintf(&fname_new, "%s.broken", fname);
-
- log_warn(LD_CRYPTO, "Moving broken dynamic DH prime to '%s'.", fname_new);
-
- if (replace_file(fname, fname_new))
- log_notice(LD_CRYPTO, "Error while moving '%s' to '%s'.",
- fname, fname_new);
-
- tor_free(fname_new);
- }
-
- if (stored_dh) {
- DH_free(stored_dh);
- stored_dh = NULL;
- }
-
- done:
- tor_free(contents);
- tor_free(base64_decoded_dh);
-
- if (stored_dh) {
- dynamic_dh_modulus = BN_dup(stored_dh->p);
- DH_free(stored_dh);
- }
-
- return dynamic_dh_modulus;
-}
-
-/** Set the global TLS Diffie-Hellman modulus.
- * If <b>dynamic_dh_modulus_fname</b> is set, try to read a dynamic DH modulus
- * off it and use it as the DH modulus. If that's not possible,
- * generate a new dynamic DH modulus.
- * If <b>dynamic_dh_modulus_fname</b> is NULL, use the Apache mod_ssl DH
+/** Set the global TLS Diffie-Hellman modulus. Use the Apache mod_ssl DH
* modulus. */
void
-crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname)
+crypto_set_tls_dh_prime(void)
{
BIGNUM *tls_prime = NULL;
- int store_dh_prime_afterwards = 0;
int r;
/* If the space is occupied, free the previous TLS DH prime */
@@ -2009,44 +1872,24 @@ crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname)
dh_param_p_tls = NULL;
}
- if (dynamic_dh_modulus_fname) { /* use dynamic DH modulus: */
- log_info(LD_OR, "Using stored dynamic DH modulus.");
- tls_prime = crypto_get_stored_dynamic_dh_modulus(dynamic_dh_modulus_fname);
-
- if (!tls_prime) {
- log_notice(LD_OR, "Generating fresh dynamic DH modulus. "
- "This might take a while...");
- tls_prime = crypto_generate_dynamic_dh_modulus();
-
- store_dh_prime_afterwards++;
- }
- } else { /* use the static DH prime modulus used by Apache in mod_ssl: */
- tls_prime = BN_new();
- tor_assert(tls_prime);
+ tls_prime = BN_new();
+ tor_assert(tls_prime);
- /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see
- * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this
- * prime.
- */
- r =BN_hex2bn(&tls_prime,
- "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
- "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
- "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
- "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
- "B0E7393E0F24218EB3");
- tor_assert(r);
- }
+ /* This is the 1024-bit safe prime that Apache uses for its DH stuff; see
+ * modules/ssl/ssl_engine_dh.c; Apache also uses a generator of 2 with this
+ * prime.
+ */
+ r = BN_hex2bn(&tls_prime,
+ "D67DE440CBBBDC1936D693D34AFD0AD50C84D239A45F520BB88174CB98"
+ "BCE951849F912E639C72FB13B4B4D7177E16D55AC179BA420B2A29FE324A"
+ "467A635E81FF5901377BEDDCFD33168A461AAD3B72DAE8860078045B07A7"
+ "DBCA7874087D1510EA9FCC9DDD330507DD62DB88AEAA747DE0F4D6E2BD68"
+ "B0E7393E0F24218EB3");
+ tor_assert(r);
tor_assert(tls_prime);
dh_param_p_tls = tls_prime;
-
- if (store_dh_prime_afterwards)
- /* save the new dynamic DH modulus to disk. */
- if (crypto_store_dynamic_dh_modulus(dynamic_dh_modulus_fname)) {
- log_notice(LD_CRYPTO, "Failed while storing dynamic DH modulus. "
- "Make sure your data directory is sane.");
- }
}
/** Initialize dh_param_p and dh_param_g if they are not already
@@ -2083,10 +1926,8 @@ init_dh_param(void)
dh_param_p = circuit_dh_prime;
dh_param_g = generator;
- /* Ensure that we have TLS DH parameters set up, too, even if we're
- going to change them soon. */
if (!dh_param_p_tls) {
- crypto_set_tls_dh_prime(NULL);
+ crypto_set_tls_dh_prime();
}
}
@@ -2138,6 +1979,8 @@ crypto_dh_t *
crypto_dh_dup(const crypto_dh_t *dh)
{
crypto_dh_t *dh_new = tor_malloc_zero(sizeof(crypto_dh_t));
+ tor_assert(dh);
+ tor_assert(dh->dh);
dh_new->dh = dh->dh;
DH_up_ref(dh->dh);
return dh_new;
@@ -2423,15 +2266,6 @@ crypto_dh_free(crypto_dh_t *dh)
* work for us too. */
#define ADD_ENTROPY 32
-/** True iff it's safe to use RAND_poll after setup.
- *
- * Versions of OpenSSL prior to 0.9.7k and 0.9.8c had a bug where RAND_poll
- * would allocate an fd_set on the stack, open a new file, and try to FD_SET
- * that fd without checking whether it fit in the fd_set. Thus, if the
- * system has not just been started up, it is unsafe to call */
-#define RAND_POLL_IS_SAFE \
- (OPENSSL_VERSION_NUMBER >= OPENSSL_V(0,9,8,'c'))
-
/** Set the seed of the weak RNG to a random value. */
void
crypto_seed_weak_rng(tor_weak_rng_t *rng)
@@ -2501,7 +2335,7 @@ crypto_strongest_rand(uint8_t *out, size_t out_len)
* have not yet allocated a bunch of fds. Return 0 on success, -1 on failure.
*/
int
-crypto_seed_rng(int startup)
+crypto_seed_rng(void)
{
int rand_poll_ok = 0, load_entropy_ok = 0;
uint8_t buf[ADD_ENTROPY];
@@ -2509,11 +2343,9 @@ crypto_seed_rng(int startup)
/* OpenSSL has a RAND_poll function that knows about more kinds of
* entropy than we do. We'll try calling that, *and* calling our own entropy
* functions. If one succeeds, we'll accept the RNG as seeded. */
- if (startup || RAND_POLL_IS_SAFE) {
- rand_poll_ok = RAND_poll();
- if (rand_poll_ok == 0)
- log_warn(LD_CRYPTO, "RAND_poll() failed.");
- }
+ rand_poll_ok = RAND_poll();
+ if (rand_poll_ok == 0)
+ log_warn(LD_CRYPTO, "RAND_poll() failed.");
load_entropy_ok = !crypto_strongest_rand(buf, sizeof(buf));
if (load_entropy_ok) {
@@ -2566,8 +2398,40 @@ crypto_rand_int(unsigned int max)
}
}
+/** Return a pseudorandom integer, chosen uniformly from the values <i>i</i>
+ * such that <b>min</b> &lt;= <i>i</i> &lt <b>max</b>.
+ *
+ * <b>min</b> MUST be in range [0, <b>max</b>).
+ * <b>max</b> MUST be in range (min, INT_MAX].
+ */
+int
+crypto_rand_int_range(unsigned int min, unsigned int max)
+{
+ tor_assert(min < max);
+ tor_assert(max <= INT_MAX);
+
+ /* The overflow is avoided here because crypto_rand_int() returns a value
+ * between 0 and (max - min) inclusive. */
+ return min + crypto_rand_int(max - min);
+}
+
+/** As crypto_rand_int_range, but supports uint64_t. */
+uint64_t
+crypto_rand_uint64_range(uint64_t min, uint64_t max)
+{
+ tor_assert(min < max);
+ return min + crypto_rand_uint64(max - min);
+}
+
+/** As crypto_rand_int_range, but supports time_t. */
+time_t
+crypto_rand_time_range(time_t min, time_t max)
+{
+ return (time_t) crypto_rand_uint64_range(min, max);
+}
+
/** Return a pseudorandom 64-bit integer, chosen uniformly from the values
- * between 0 and <b>max</b>-1. */
+ * between 0 and <b>max</b>-1 inclusive. */
uint64_t
crypto_rand_uint64(uint64_t max)
{
@@ -2628,7 +2492,7 @@ crypto_random_hostname(int min_rand_len, int max_rand_len, const char *prefix,
if (min_rand_len > max_rand_len)
min_rand_len = max_rand_len;
- randlen = min_rand_len + crypto_rand_int(max_rand_len - min_rand_len + 1);
+ randlen = crypto_rand_int_range(min_rand_len, max_rand_len+1);
prefixlen = strlen(prefix);
resultlen = prefixlen + strlen(suffix) + randlen + 16;
@@ -2675,36 +2539,222 @@ smartlist_shuffle(smartlist_t *sl)
}
}
+#define BASE64_OPENSSL_LINELEN 64
+
+/** Return the Base64 encoded size of <b>srclen</b> bytes of data in
+ * bytes.
+ *
+ * If <b>flags</b>&amp;BASE64_ENCODE_MULTILINE is true, return the size
+ * of the encoded output as multiline output (64 character, `\n' terminated
+ * lines).
+ */
+size_t
+base64_encode_size(size_t srclen, int flags)
+{
+ size_t enclen;
+ tor_assert(srclen < INT_MAX);
+
+ if (srclen == 0)
+ return 0;
+
+ enclen = ((srclen - 1) / 3) * 4 + 4;
+ if (flags & BASE64_ENCODE_MULTILINE) {
+ size_t remainder = enclen % BASE64_OPENSSL_LINELEN;
+ enclen += enclen / BASE64_OPENSSL_LINELEN;
+ if (remainder)
+ enclen++;
+ }
+ tor_assert(enclen < INT_MAX && enclen > srclen);
+ return enclen;
+}
+
+/** Internal table mapping 6 bit values to the Base64 alphabet. */
+static const char base64_encode_table[64] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/'
+};
+
/** Base64 encode <b>srclen</b> bytes of data from <b>src</b>. Write
* the result into <b>dest</b>, if it will fit within <b>destlen</b>
- * bytes. Return the number of bytes written on success; -1 if
+ * bytes. Return the number of bytes written on success; -1 if
* destlen is too short, or other failure.
+ *
+ * If <b>flags</b>&amp;BASE64_ENCODE_MULTILINE is true, return encoded
+ * output in multiline format (64 character, `\n' terminated lines).
*/
int
-base64_encode(char *dest, size_t destlen, const char *src, size_t srclen)
-{
- /* FFFF we might want to rewrite this along the lines of base64_decode, if
- * it ever shows up in the profile. */
- EVP_ENCODE_CTX ctx;
- int len, ret;
- tor_assert(srclen < INT_MAX);
+base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
+ int flags)
+{
+ const unsigned char *usrc = (unsigned char *)src;
+ const unsigned char *eous = usrc + srclen;
+ char *d = dest;
+ uint32_t n = 0;
+ size_t linelen = 0;
+ size_t enclen;
+ int n_idx = 0;
+
+ if (!src || !dest)
+ return -1;
- /* 48 bytes of input -> 64 bytes of output plus newline.
- Plus one more byte, in case I'm wrong.
- */
- if (destlen < ((srclen/48)+1)*66)
+ /* Ensure that there is sufficient space, including the NUL. */
+ enclen = base64_encode_size(srclen, flags);
+ if (destlen < enclen + 1)
return -1;
if (destlen > SIZE_T_CEILING)
return -1;
+ if (enclen > INT_MAX)
+ return -1;
- EVP_EncodeInit(&ctx);
- EVP_EncodeUpdate(&ctx, (unsigned char*)dest, &len,
- (unsigned char*)src, (int)srclen);
- EVP_EncodeFinal(&ctx, (unsigned char*)(dest+len), &ret);
- ret += len;
- return ret;
+ memset(dest, 0, enclen);
+
+ /* XXX/Yawning: If this ends up being too slow, this can be sped up
+ * by separating the multiline format case and the normal case, and
+ * processing 48 bytes of input at a time when newlines are desired.
+ */
+#define ENCODE_CHAR(ch) \
+ STMT_BEGIN \
+ *d++ = ch; \
+ if (flags & BASE64_ENCODE_MULTILINE) { \
+ if (++linelen % BASE64_OPENSSL_LINELEN == 0) { \
+ linelen = 0; \
+ *d++ = '\n'; \
+ } \
+ } \
+ STMT_END
+
+#define ENCODE_N(idx) \
+ ENCODE_CHAR(base64_encode_table[(n >> ((3 - idx) * 6)) & 0x3f])
+
+#define ENCODE_PAD() ENCODE_CHAR('=')
+
+ /* Iterate over all the bytes in src. Each one will add 8 bits to the
+ * value we're encoding. Accumulate bits in <b>n</b>, and whenever we
+ * have 24 bits, batch them into 4 bytes and flush those bytes to dest.
+ */
+ for ( ; usrc < eous; ++usrc) {
+ n = (n << 8) | *usrc;
+ if ((++n_idx) == 3) {
+ ENCODE_N(0);
+ ENCODE_N(1);
+ ENCODE_N(2);
+ ENCODE_N(3);
+ n_idx = 0;
+ n = 0;
+ }
+ }
+ switch (n_idx) {
+ case 0:
+ /* 0 leftover bits, no pading to add. */
+ break;
+ case 1:
+ /* 8 leftover bits, pad to 12 bits, write the 2 6-bit values followed
+ * by 2 padding characters.
+ */
+ n <<= 4;
+ ENCODE_N(2);
+ ENCODE_N(3);
+ ENCODE_PAD();
+ ENCODE_PAD();
+ break;
+ case 2:
+ /* 16 leftover bits, pad to 18 bits, write the 3 6-bit values followed
+ * by 1 padding character.
+ */
+ n <<= 2;
+ ENCODE_N(1);
+ ENCODE_N(2);
+ ENCODE_N(3);
+ ENCODE_PAD();
+ break;
+ default:
+ /* Something went catastrophically wrong. */
+ tor_fragile_assert();
+ return -1;
+ }
+
+#undef ENCODE_N
+#undef ENCODE_PAD
+#undef ENCODE_CHAR
+
+ /* Multiline output always includes at least one newline. */
+ if (flags & BASE64_ENCODE_MULTILINE && linelen != 0)
+ *d++ = '\n';
+
+ tor_assert(d - dest == (ptrdiff_t)enclen);
+
+ *d++ = '\0'; /* NUL terminate the output. */
+
+ return (int) enclen;
+}
+
+/** As base64_encode, but do not add any internal spaces or external padding
+ * to the output stream. */
+int
+base64_encode_nopad(char *dest, size_t destlen,
+ const uint8_t *src, size_t srclen)
+{
+ int n = base64_encode(dest, destlen, (const char*) src, srclen, 0);
+ if (n <= 0)
+ return n;
+ tor_assert((size_t)n < destlen && dest[n] == 0);
+ char *in, *out;
+ in = out = dest;
+ while (*in) {
+ if (*in == '=' || *in == '\n') {
+ ++in;
+ } else {
+ *out++ = *in++;
+ }
+ }
+ *out = 0;
+
+ tor_assert(out - dest <= INT_MAX);
+
+ return (int)(out - dest);
}
+/** As base64_decode, but do not require any padding on the input */
+int
+base64_decode_nopad(uint8_t *dest, size_t destlen,
+ const char *src, size_t srclen)
+{
+ if (srclen > SIZE_T_CEILING - 4)
+ return -1;
+ char *buf = tor_malloc(srclen + 4);
+ memcpy(buf, src, srclen+1);
+ size_t buflen;
+ switch (srclen % 4)
+ {
+ case 0:
+ default:
+ buflen = srclen;
+ break;
+ case 1:
+ tor_free(buf);
+ return -1;
+ case 2:
+ memcpy(buf+srclen, "==", 3);
+ buflen = srclen + 2;
+ break;
+ case 3:
+ memcpy(buf+srclen, "=", 2);
+ buflen = srclen + 1;
+ break;
+ }
+ int n = base64_decode((char*)dest, destlen, buf, buflen);
+ tor_free(buf);
+ return n;
+}
+
+#undef BASE64_OPENSSL_LINELEN
+
/** @{ */
/** Special values used for the base64_decode_table */
#define X 255
@@ -2749,26 +2799,6 @@ static const uint8_t base64_decode_table[256] = {
int
base64_decode(char *dest, size_t destlen, const char *src, size_t srclen)
{
-#ifdef USE_OPENSSL_BASE64
- EVP_ENCODE_CTX ctx;
- int len, ret;
- /* 64 bytes of input -> *up to* 48 bytes of output.
- Plus one more byte, in case I'm wrong.
- */
- if (destlen < ((srclen/64)+1)*49)
- return -1;
- if (destlen > SIZE_T_CEILING)
- return -1;
-
- memset(dest, 0, destlen);
-
- EVP_DecodeInit(&ctx);
- EVP_DecodeUpdate(&ctx, (unsigned char*)dest, &len,
- (unsigned char*)src, srclen);
- EVP_DecodeFinal(&ctx, (unsigned char*)dest, &ret);
- ret += len;
- return ret;
-#else
const char *eos = src+srclen;
uint32_t n=0;
int n_idx=0;
@@ -2839,20 +2869,20 @@ base64_decode(char *dest, size_t destlen, const char *src, size_t srclen)
tor_assert((dest-dest_orig) <= INT_MAX);
return (int)(dest-dest_orig);
-#endif
}
#undef X
#undef SP
#undef PAD
/** Base64 encode DIGEST_LINE bytes from <b>digest</b>, remove the trailing =
- * and newline characters, and store the nul-terminated result in the first
+ * characters, and store the nul-terminated result in the first
* BASE64_DIGEST_LEN+1 bytes of <b>d64</b>. */
+/* XXXX unify with crypto_format.c code */
int
digest_to_base64(char *d64, const char *digest)
{
char buf[256];
- base64_encode(buf, sizeof(buf), digest, DIGEST_LEN);
+ base64_encode(buf, sizeof(buf), digest, DIGEST_LEN, 0);
buf[BASE64_DIGEST_LEN] = '\0';
memcpy(d64, buf, BASE64_DIGEST_LEN+1);
return 0;
@@ -2861,36 +2891,25 @@ digest_to_base64(char *d64, const char *digest)
/** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without
* trailing newline or = characters), decode it and store the result in the
* first DIGEST_LEN bytes at <b>digest</b>. */
+/* XXXX unify with crypto_format.c code */
int
digest_from_base64(char *digest, const char *d64)
{
-#ifdef USE_OPENSSL_BASE64
- char buf_in[BASE64_DIGEST_LEN+3];
- char buf[256];
- if (strlen(d64) != BASE64_DIGEST_LEN)
- return -1;
- memcpy(buf_in, d64, BASE64_DIGEST_LEN);
- memcpy(buf_in+BASE64_DIGEST_LEN, "=\n\0", 3);
- if (base64_decode(buf, sizeof(buf), buf_in, strlen(buf_in)) != DIGEST_LEN)
- return -1;
- memcpy(digest, buf, DIGEST_LEN);
- return 0;
-#else
if (base64_decode(digest, DIGEST_LEN, d64, strlen(d64)) == DIGEST_LEN)
return 0;
else
return -1;
-#endif
}
/** Base64 encode DIGEST256_LINE bytes from <b>digest</b>, remove the
- * trailing = and newline characters, and store the nul-terminated result in
- * the first BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>. */
+ * trailing = characters, and store the nul-terminated result in the first
+ * BASE64_DIGEST256_LEN+1 bytes of <b>d64</b>. */
+ /* XXXX unify with crypto_format.c code */
int
digest256_to_base64(char *d64, const char *digest)
{
char buf[256];
- base64_encode(buf, sizeof(buf), digest, DIGEST256_LEN);
+ base64_encode(buf, sizeof(buf), digest, DIGEST256_LEN, 0);
buf[BASE64_DIGEST256_LEN] = '\0';
memcpy(d64, buf, BASE64_DIGEST256_LEN+1);
return 0;
@@ -2899,26 +2918,14 @@ digest256_to_base64(char *d64, const char *digest)
/** Given a base64 encoded, nul-terminated digest in <b>d64</b> (without
* trailing newline or = characters), decode it and store the result in the
* first DIGEST256_LEN bytes at <b>digest</b>. */
+/* XXXX unify with crypto_format.c code */
int
digest256_from_base64(char *digest, const char *d64)
{
-#ifdef USE_OPENSSL_BASE64
- char buf_in[BASE64_DIGEST256_LEN+3];
- char buf[256];
- if (strlen(d64) != BASE64_DIGEST256_LEN)
- return -1;
- memcpy(buf_in, d64, BASE64_DIGEST256_LEN);
- memcpy(buf_in+BASE64_DIGEST256_LEN, "=\n\0", 3);
- if (base64_decode(buf, sizeof(buf), buf_in, strlen(buf_in)) != DIGEST256_LEN)
- return -1;
- memcpy(digest, buf, DIGEST256_LEN);
- return 0;
-#else
if (base64_decode(digest, DIGEST256_LEN, d64, strlen(d64)) == DIGEST256_LEN)
return 0;
else
return -1;
-#endif
}
/** Implements base32 encoding as in RFC 4648. Limitation: Requires
@@ -3123,13 +3130,11 @@ openssl_dynlock_destroy_cb_(struct CRYPTO_dynlock_value *v,
tor_free(v);
}
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)
static void
tor_set_openssl_thread_id(CRYPTO_THREADID *threadid)
{
CRYPTO_THREADID_set_numeric(threadid, tor_get_thread_id());
}
-#endif
/** @{ */
/** Helper: Construct mutexes, and set callbacks to help OpenSSL handle being
@@ -3144,11 +3149,7 @@ setup_openssl_threading(void)
for (i=0; i < n; ++i)
openssl_mutexes_[i] = tor_mutex_new();
CRYPTO_set_locking_callback(openssl_locking_cb_);
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
- CRYPTO_set_id_callback(tor_get_thread_id);
-#else
CRYPTO_THREADID_set_callback(tor_set_openssl_thread_id);
-#endif
CRYPTO_set_dynlock_create_callback(openssl_dynlock_create_cb_);
CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock_cb_);
CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy_cb_);
diff --git a/src/common/crypto.h b/src/common/crypto.h
index d305bc17a0..b953ab93e7 100644
--- a/src/common/crypto.h
+++ b/src/common/crypto.h
@@ -122,8 +122,7 @@ int crypto_global_cleanup(void);
crypto_pk_t *crypto_pk_new(void);
void crypto_pk_free(crypto_pk_t *env);
-void crypto_set_tls_dh_prime(const char *dynamic_dh_modulus_fname);
-
+void crypto_set_tls_dh_prime(void);
crypto_cipher_t *crypto_cipher_new(const char *key);
crypto_cipher_t *crypto_cipher_new_with_iv(const char *key, const char *iv);
void crypto_cipher_free(crypto_cipher_t *env);
@@ -147,9 +146,9 @@ int crypto_pk_write_private_key_to_filename(crypto_pk_t *env,
const char *fname);
int crypto_pk_check_key(crypto_pk_t *env);
-int crypto_pk_cmp_keys(crypto_pk_t *a, crypto_pk_t *b);
-int crypto_pk_eq_keys(crypto_pk_t *a, crypto_pk_t *b);
-size_t crypto_pk_keysize(crypto_pk_t *env);
+int crypto_pk_cmp_keys(const crypto_pk_t *a, const crypto_pk_t *b);
+int crypto_pk_eq_keys(const crypto_pk_t *a, const crypto_pk_t *b);
+size_t crypto_pk_keysize(const crypto_pk_t *env);
int crypto_pk_num_bits(crypto_pk_t *env);
crypto_pk_t *crypto_pk_dup_key(crypto_pk_t *orig);
crypto_pk_t *crypto_pk_copy_full(crypto_pk_t *orig);
@@ -161,11 +160,11 @@ int crypto_pk_public_encrypt(crypto_pk_t *env, char *to, size_t tolen,
int crypto_pk_private_decrypt(crypto_pk_t *env, char *to, size_t tolen,
const char *from, size_t fromlen,
int padding, int warnOnFailure);
-int crypto_pk_public_checksig(crypto_pk_t *env, char *to, size_t tolen,
+int crypto_pk_public_checksig(const crypto_pk_t *env, char *to, size_t tolen,
const char *from, size_t fromlen);
int crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data,
size_t datalen, const char *sig, size_t siglen);
-int crypto_pk_private_sign(crypto_pk_t *env, char *to, size_t tolen,
+int crypto_pk_private_sign(const crypto_pk_t *env, char *to, size_t tolen,
const char *from, size_t fromlen);
int crypto_pk_private_sign_digest(crypto_pk_t *env, char *to, size_t tolen,
const char *from, size_t fromlen);
@@ -185,6 +184,9 @@ int crypto_pk_get_all_digests(crypto_pk_t *pk, digests_t *digests_out);
int crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out,int add_space);
int crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out);
+int crypto_pk_base64_encode(const crypto_pk_t *pk, char **priv_out);
+crypto_pk_t *crypto_pk_base64_decode(const char *str, size_t len);
+
/* symmetric crypto */
const char *crypto_cipher_get_key(crypto_cipher_t *env);
@@ -207,6 +209,11 @@ int crypto_digest256(char *digest, const char *m, size_t len,
digest_algorithm_t algorithm);
int crypto_digest_all(digests_t *ds_out, const char *m, size_t len);
struct smartlist_t;
+void crypto_digest_smartlist_prefix(char *digest_out, size_t len_out,
+ const char *prepend,
+ const struct smartlist_t *lst,
+ const char *append,
+ digest_algorithm_t alg);
void crypto_digest_smartlist(char *digest_out, size_t len_out,
const struct smartlist_t *lst, const char *append,
digest_algorithm_t alg);
@@ -251,10 +258,13 @@ int crypto_expand_key_material_rfc5869_sha256(
uint8_t *key_out, size_t key_out_len);
/* random numbers */
-int crypto_seed_rng(int startup);
+int crypto_seed_rng(void);
MOCK_DECL(int,crypto_rand,(char *to, size_t n));
int crypto_strongest_rand(uint8_t *out, size_t out_len);
int crypto_rand_int(unsigned int max);
+int crypto_rand_int_range(unsigned int min, unsigned int max);
+uint64_t crypto_rand_uint64_range(uint64_t min, uint64_t max);
+time_t crypto_rand_time_range(time_t min, time_t max);
uint64_t crypto_rand_uint64(uint64_t max);
double crypto_rand_double(void);
struct tor_weak_rng_t;
@@ -268,8 +278,16 @@ struct smartlist_t;
void *smartlist_choose(const struct smartlist_t *sl);
void smartlist_shuffle(struct smartlist_t *sl);
-int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen);
+#define BASE64_ENCODE_MULTILINE 1
+size_t base64_encode_size(size_t srclen, int flags);
+int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen,
+ int flags);
int base64_decode(char *dest, size_t destlen, const char *src, size_t srclen);
+int base64_encode_nopad(char *dest, size_t destlen,
+ const uint8_t *src, size_t srclen);
+int base64_decode_nopad(uint8_t *dest, size_t destlen,
+ const char *src, size_t srclen);
+
/** Characters that can appear (case-insensitively) in a base32 encoding. */
#define BASE32_CHARS "abcdefghijklmnopqrstuvwxyz234567"
void base32_encode(char *dest, size_t destlen, const char *src, size_t srclen);
diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c
index f2e6945ac8..6b93751dda 100644
--- a/src/common/crypto_ed25519.c
+++ b/src/common/crypto_ed25519.c
@@ -351,3 +351,24 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out,
return 0;
}
+/** Release all storage held for <b>kp</b>. */
+void
+ed25519_keypair_free(ed25519_keypair_t *kp)
+{
+ if (! kp)
+ return;
+
+ memwipe(kp, 0, sizeof(*kp));
+ tor_free(kp);
+}
+
+/** Return true iff <b>key1</b> and <b>key2</b> are the same public key. */
+int
+ed25519_pubkey_eq(const ed25519_public_key_t *key1,
+ const ed25519_public_key_t *key2)
+{
+ tor_assert(key1);
+ tor_assert(key2);
+ return tor_memeq(key1->pubkey, key2->pubkey, ED25519_PUBKEY_LEN);
+}
+
diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h
index 7efa74bff5..4d20406d06 100644
--- a/src/common/crypto_ed25519.h
+++ b/src/common/crypto_ed25519.h
@@ -6,6 +6,7 @@
#include "testsupport.h"
#include "torint.h"
+#include "crypto_curve25519.h"
#define ED25519_PUBKEY_LEN 32
#define ED25519_SECKEY_LEN 64
@@ -60,7 +61,7 @@ int ed25519_checksig(const ed25519_signature_t *signature,
*/
typedef struct {
/** The public key that supposedly generated the signature. */
- ed25519_public_key_t *pubkey;
+ const ed25519_public_key_t *pubkey;
/** The signature to check. */
ed25519_signature_t signature;
/** The message that the signature is supposed to have been applied to. */
@@ -87,13 +88,21 @@ int ed25519_public_blind(ed25519_public_key_t *out,
const ed25519_public_key_t *inp,
const uint8_t *param);
+/* XXXX move these to crypto_format.h */
#define ED25519_BASE64_LEN 43
-
int ed25519_public_from_base64(ed25519_public_key_t *pkey,
const char *input);
int ed25519_public_to_base64(char *output,
const ed25519_public_key_t *pkey);
+/* XXXX move these to crypto_format.h */
+#define ED25519_SIG_BASE64_LEN 86
+
+int ed25519_signature_from_base64(ed25519_signature_t *sig,
+ const char *input);
+int ed25519_signature_to_base64(char *output,
+ const ed25519_signature_t *sig);
+
/* XXXX read encrypted, write encrypted. */
int ed25519_seckey_write_to_file(const ed25519_secret_key_t *seckey,
@@ -109,5 +118,10 @@ int ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out,
char **tag_out,
const char *filename);
+void ed25519_keypair_free(ed25519_keypair_t *kp);
+
+int ed25519_pubkey_eq(const ed25519_public_key_t *key1,
+ const ed25519_public_key_t *key2);
+
#endif
diff --git a/src/common/crypto_format.c b/src/common/crypto_format.c
index 00e0e9ea85..e825132cb9 100644
--- a/src/common/crypto_format.c
+++ b/src/common/crypto_format.c
@@ -19,7 +19,7 @@ curve25519_public_to_base64(char *output,
{
char buf[128];
base64_encode(buf, sizeof(buf),
- (const char*)pkey->public_key, CURVE25519_PUBKEY_LEN);
+ (const char*)pkey->public_key, CURVE25519_PUBKEY_LEN, 0);
buf[CURVE25519_BASE64_PADDED_LEN] = '\0';
memcpy(output, buf, CURVE25519_BASE64_PADDED_LEN+1);
return 0;
@@ -65,3 +65,42 @@ ed25519_public_to_base64(char *output,
return digest256_to_base64(output, (const char *)pkey->pubkey);
}
+/** Encode the signature <b>sig</b> into the buffer at <b>output</b>,
+ * which must have space for ED25519_SIG_BASE64_LEN bytes of encoded signature,
+ * plus one byte for a terminating NUL. Return 0 on success, -1 on failure.
+ */
+int
+ed25519_signature_to_base64(char *output,
+ const ed25519_signature_t *sig)
+{
+ char buf[256];
+ int n = base64_encode_nopad(buf, sizeof(buf), sig->sig, ED25519_SIG_LEN);
+ tor_assert(n == ED25519_SIG_BASE64_LEN);
+ memcpy(output, buf, ED25519_SIG_BASE64_LEN+1);
+ return 0;
+}
+
+/** Try to decode the string <b>input</b> into an ed25519 signature. On
+ * success, store the value in <b>sig</b> and return 0. Otherwise return
+ * -1. */
+int
+ed25519_signature_from_base64(ed25519_signature_t *sig,
+ const char *input)
+{
+
+ if (strlen(input) != ED25519_SIG_BASE64_LEN)
+ return -1;
+ char buf[ED25519_SIG_BASE64_LEN+3];
+ memcpy(buf, input, ED25519_SIG_BASE64_LEN);
+ buf[ED25519_SIG_BASE64_LEN+0] = '=';
+ buf[ED25519_SIG_BASE64_LEN+1] = '=';
+ buf[ED25519_SIG_BASE64_LEN+2] = 0;
+ char decoded[128];
+ int n = base64_decode(decoded, sizeof(decoded), buf, strlen(buf));
+ if (n < 0 || n != ED25519_SIG_LEN)
+ return -1;
+ memcpy(sig->sig, decoded, ED25519_SIG_LEN);
+
+ return 0;
+}
+
diff --git a/src/common/include.am b/src/common/include.am
index 5b63392541..b782310663 100644
--- a/src/common/include.am
+++ b/src/common/include.am
@@ -11,9 +11,7 @@ noinst_LIBRARIES += \
src/common/libor-event-testing.a
endif
-EXTRA_DIST+= \
- src/common/common_sha1.i \
- src/common/Makefile.nmake
+EXTRA_DIST += src/common/Makefile.nmake
#CFLAGS = -Wall -Wpointer-arith -O2
AM_CPPFLAGS += -I$(srcdir)/src/common -Isrc/common -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel
@@ -63,7 +61,6 @@ LIBOR_A_SOURCES = \
src/common/log.c \
src/common/memarea.c \
src/common/util.c \
- src/common/util_codedigest.c \
src/common/util_process.c \
src/common/sandbox.c \
src/common/workqueue.c \
@@ -72,6 +69,8 @@ LIBOR_A_SOURCES = \
$(libor_extra_source) \
$(threads_impl_source)
+src/common/log.o: micro-revision.i
+
LIBOR_CRYPTO_A_SOURCES = \
src/common/aes.c \
src/common/crypto.c \
@@ -96,9 +95,9 @@ src_common_libor_testing_a_SOURCES = $(LIBOR_A_SOURCES)
src_common_libor_crypto_testing_a_SOURCES = $(LIBOR_CRYPTO_A_SOURCES)
src_common_libor_event_testing_a_SOURCES = $(LIBOR_EVENT_A_SOURCES)
-src_common_libor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
-src_common_libor_crypto_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
-src_common_libor_event_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_common_libor_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_common_libor_crypto_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_common_libor_event_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_common_libor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_common_libor_crypto_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_common_libor_event_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
@@ -133,17 +132,3 @@ COMMONHEADERS = \
noinst_HEADERS+= $(COMMONHEADERS)
-DISTCLEANFILES+= src/common/common_sha1.i
-
-src/common/common_sha1.i: $(libor_SOURCES) $(libor_crypto_a_SOURCES) $(COMMONHEADERS)
- $(AM_V_GEN)if test "@SHA1SUM@" != none; then \
- (cd "$(srcdir)" && "@SHA1SUM@" $(src_common_libor_SOURCES) $(src_common_libor_crypto_a_SOURCES) $(COMMONHEADERS)) | "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > $@; \
- elif test "@OPENSSL@" != none; then \
- (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_common_libor_SOURCES) $(src_Common_libor_crypto_a_SOURCES) $(COMMONHEADERS)) | "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > $@; \
- else \
- rm $@; \
- touch $@; \
- fi
-
-src/common/util_codedigest.o: src/common/common_sha1.i
-
diff --git a/src/common/log.c b/src/common/log.c
index e8cc30c312..4ad0bc3697 100644
--- a/src/common/log.c
+++ b/src/common/log.c
@@ -140,6 +140,9 @@ static size_t pending_startup_messages_len;
* configured. */
static int queue_startup_messages = 1;
+/** True iff __PRETTY_FUNCTION__ includes parenthesized arguments. */
+static int pretty_fn_has_parens = 0;
+
/** Don't store more than this many bytes of messages while waiting for the
* logs to get configured. */
#define MAX_STARTUP_MSG_LEN (1<<16)
@@ -263,6 +266,13 @@ log_tor_version(logfile_t *lf, int reset)
return 0;
}
+const char bug_suffix[] = " (on Tor " VERSION
+#ifndef _MSC_VER
+ " "
+#include "micro-revision.i"
+#endif
+ ")";
+
/** Helper: Format a log message into a fixed-sized buffer. (This is
* factored out of <b>logv</b> so that we never format a message more
* than once.) Return a pointer to the first character of the message
@@ -306,7 +316,9 @@ format_msg(char *buf, size_t buf_len,
}
if (funcname && should_log_function_name(domain, severity)) {
- r = tor_snprintf(buf+n, buf_len-n, "%s(): ", funcname);
+ r = tor_snprintf(buf+n, buf_len-n,
+ pretty_fn_has_parens ? "%s: " : "%s(): ",
+ funcname);
if (r<0)
n = strlen(buf);
else
@@ -341,6 +353,13 @@ format_msg(char *buf, size_t buf_len,
}
}
}
+
+ if (domain == LD_BUG &&
+ buf_len - n > strlen(bug_suffix)+1) {
+ memcpy(buf+n, bug_suffix, strlen(bug_suffix));
+ n += strlen(bug_suffix);
+ }
+
buf[n]='\n';
buf[n+1]='\0';
*msg_len_out = n+1;
@@ -925,6 +944,11 @@ init_logging(int disable_startup_queue)
tor_mutex_init(&log_mutex);
log_mutex_initialized = 1;
}
+#ifdef __GNUC__
+ if (strchr(__PRETTY_FUNCTION__, '(')) {
+ pretty_fn_has_parens = 1;
+ }
+#endif
if (pending_cb_messages == NULL)
pending_cb_messages = smartlist_new();
if (disable_startup_queue)
diff --git a/src/common/sandbox.c b/src/common/sandbox.c
index 49316c6193..cdb4521c82 100644
--- a/src/common/sandbox.c
+++ b/src/common/sandbox.c
@@ -129,11 +129,13 @@ static int filter_nopar_gen[] = {
SCMP_SYS(clone),
SCMP_SYS(epoll_create),
SCMP_SYS(epoll_wait),
+ SCMP_SYS(eventfd2),
SCMP_SYS(fcntl),
SCMP_SYS(fstat),
#ifdef __NR_fstat64
SCMP_SYS(fstat64),
#endif
+ SCMP_SYS(futex),
SCMP_SYS(getdents64),
SCMP_SYS(getegid),
#ifdef __NR_getegid32
@@ -170,6 +172,7 @@ static int filter_nopar_gen[] = {
SCMP_SYS(read),
SCMP_SYS(rt_sigreturn),
SCMP_SYS(sched_getaffinity),
+ SCMP_SYS(sendmsg),
SCMP_SYS(set_robust_list),
#ifdef __NR_sigreturn
SCMP_SYS(sigreturn),
@@ -547,6 +550,15 @@ sb_socket(scmp_filter_ctx ctx, sandbox_cfg_t *filter)
SCMP_CMP(0, SCMP_CMP_EQ, PF_UNIX),
SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_STREAM),
SCMP_CMP(2, SCMP_CMP_EQ, 0));
+ if (rc)
+ return rc;
+
+ rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket),
+ SCMP_CMP(0, SCMP_CMP_EQ, PF_UNIX),
+ SCMP_CMP_MASKED(1, SOCK_CLOEXEC|SOCK_NONBLOCK, SOCK_DGRAM),
+ SCMP_CMP(2, SCMP_CMP_EQ, 0));
+ if (rc)
+ return rc;
rc = seccomp_rule_add_3(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket),
SCMP_CMP(0, SCMP_CMP_EQ, PF_NETLINK),
diff --git a/src/common/tortls.c b/src/common/tortls.c
index 1812e3f9d5..4cfe94e182 100644
--- a/src/common/tortls.c
+++ b/src/common/tortls.c
@@ -43,13 +43,22 @@
#pragma GCC diagnostic ignored "-Wredundant-decls"
#endif
+#include <openssl/opensslv.h>
+#include "crypto.h"
+
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,0,0)
+#error "We require OpenSSL >= 1.0.0"
+#endif
+#ifdef OPENSSL_NO_EC
+#error "We require OpenSSL with ECC support"
+#endif
+
#include <openssl/ssl.h>
#include <openssl/ssl3.h>
#include <openssl/err.h>
#include <openssl/tls1.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
-#include <openssl/opensslv.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
@@ -68,17 +77,12 @@
#include "compat_libevent.h"
#endif
-#include "crypto.h"
#include "tortls.h"
#include "util.h"
#include "torlog.h"
#include "container.h"
#include <string.h>
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(0,9,8)
-#error "We require OpenSSL >= 0.9.8"
-#endif
-
/* Enable the "v2" TLS handshake.
*/
#define V2_HANDSHAKE_SERVER
@@ -93,10 +97,8 @@
#define ADDR(tls) (((tls) && (tls)->address) ? tls->address : "peer")
-#if (OPENSSL_VERSION_NUMBER < OPENSSL_V(0,9,8,'s') || \
- (OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(0,9,9) && \
- OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f')))
-/* This is a version of OpenSSL before 0.9.8s/1.0.0f. It does not have
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V(1,0,0,'f')
+/* This is a version of OpenSSL before 1.0.0f. It does not have
* the CVE-2011-4576 fix, and as such it can't use RELEASE_BUFFERS and
* SSL3 safely at the same time.
*/
@@ -114,22 +116,8 @@
#define SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION 0x0010
#endif
-/** Does the run-time openssl version look like we need
- * SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION? */
-static int use_unsafe_renegotiation_op = 0;
-/** Does the run-time openssl version look like we need
- * SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION? */
-static int use_unsafe_renegotiation_flag = 0;
-#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
-/* If we have openssl 1.1, we just trust that the "mode" will work, and don't
- * use the "flag" at all. Nobody would forward-port that weird little glitch
- * from 0.9.8l to 1.1, would they?
- */
-#define SUPPORT_UNSAFE_RENEGOTIATION_FLAG
-#endif
-
/** Structure that we use for a single certificate. */
-struct tor_cert_t {
+struct tor_x509_cert_t {
X509 *cert;
uint8_t *encoded;
size_t encoded_len;
@@ -144,9 +132,9 @@ struct tor_cert_t {
typedef struct tor_tls_context_t {
int refcnt;
SSL_CTX *ctx;
- tor_cert_t *my_link_cert;
- tor_cert_t *my_id_cert;
- tor_cert_t *my_auth_cert;
+ tor_x509_cert_t *my_link_cert;
+ tor_x509_cert_t *my_id_cert;
+ tor_x509_cert_t *my_auth_cert;
crypto_pk_t *link_key;
crypto_pk_t *auth_key;
} tor_tls_context_t;
@@ -476,65 +464,13 @@ tor_tls_init(void)
check_no_tls_errors();
if (!tls_library_is_initialized) {
- long version;
SSL_library_init();
SSL_load_error_strings();
- version = SSLeay();
-
- /* OpenSSL 0.9.8l introduced SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
- * here, but without thinking too hard about it: it turns out that the
- * flag in question needed to be set at the last minute, and that it
- * conflicted with an existing flag number that had already been added
- * in the OpenSSL 1.0.0 betas. OpenSSL 0.9.8m thoughtfully replaced
- * the flag with an option and (it seems) broke anything that used
- * SSL3_FLAGS_* for the purpose. So we need to know how to do both,
- * and we mustn't use the SSL3_FLAGS option with anything besides
- * OpenSSL 0.9.8l.
- *
- * No, we can't just set flag 0x0010 everywhere. It breaks Tor with
- * OpenSSL 1.0.0beta3 and later. On the other hand, we might be able to
- * set option 0x00040000L everywhere.
- *
- * No, we can't simply detect whether the flag or the option is present
- * in the headers at build-time: some vendors (notably Apple) like to
- * leave their headers out of sync with their libraries.
- *
- * Yes, it _is_ almost as if the OpenSSL developers decided that no
- * program should be allowed to use renegotiation unless it first passed
- * a test of intelligence and determination.
- */
- if (version > OPENSSL_V(0,9,8,'k') && version <= OPENSSL_V(0,9,8,'l')) {
- log_info(LD_GENERAL, "OpenSSL %s looks like version 0.9.8l, but "
- "some vendors have backported renegotiation code from "
- "0.9.8m without updating the version number. "
- "I will try SSL3_FLAGS and SSL_OP to enable renegotation.",
- SSLeay_version(SSLEAY_VERSION));
- use_unsafe_renegotiation_flag = 1;
- use_unsafe_renegotiation_op = 1;
- } else if (version > OPENSSL_V(0,9,8,'l')) {
- log_info(LD_GENERAL, "OpenSSL %s looks like version 0.9.8m or later; "
- "I will try SSL_OP to enable renegotiation",
- SSLeay_version(SSLEAY_VERSION));
- use_unsafe_renegotiation_op = 1;
- } else if (version <= OPENSSL_V(0,9,8,'k')) {
- log_info(LD_GENERAL, "OpenSSL %s [%lx] looks like it's older than "
- "0.9.8l, but some vendors have backported 0.9.8l's "
- "renegotiation code to earlier versions, and some have "
- "backported the code from 0.9.8m or 0.9.8n. I'll set both "
- "SSL3_FLAGS and SSL_OP just to be safe.",
- SSLeay_version(SSLEAY_VERSION), version);
- use_unsafe_renegotiation_flag = 1;
- use_unsafe_renegotiation_op = 1;
- } else {
- /* this is dead code, yes? */
- log_info(LD_GENERAL, "OpenSSL %s has version %lx",
- SSLeay_version(SSLEAY_VERSION), version);
- }
-
#if (SIZEOF_VOID_P >= 8 && \
- !defined(OPENSSL_NO_EC) && \
OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,1))
+ long version = SSLeay();
+
if (version >= OPENSSL_V_SERIES(1,0,1)) {
/* Warn if we could *almost* be running with much faster ECDH.
If we're built for a 64-bit target, using OpenSSL 1.0.1, but we
@@ -650,7 +586,8 @@ tor_tls_create_certificate(crypto_pk_t *rsa,
* than having it start right now. Don't choose quite uniformly, since
* then we might pick a time where we're about to expire. Lastly, be
* sure to start on a day boundary. */
- start_time = time(NULL) - crypto_rand_int(cert_lifetime) + 2*24*3600;
+ time_t now = time(NULL);
+ start_time = crypto_rand_time_range(now - cert_lifetime, now) + 2*24*3600;
start_time -= start_time % (24*3600);
tor_assert(rsa);
@@ -779,7 +716,7 @@ const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
/** List of ciphers that clients should advertise, omitting items that
* our OpenSSL doesn't know about. */
static const char CLIENT_CIPHER_LIST[] =
-#include "./ciphers.inc"
+#include "ciphers.inc"
/* Tell it not to use SSLv2 ciphers, so that it can select an SSLv3 version
* of any cipher we say. */
"!SSLv2"
@@ -789,7 +726,7 @@ static const char CLIENT_CIPHER_LIST[] =
/** Free all storage held in <b>cert</b> */
void
-tor_cert_free(tor_cert_t *cert)
+tor_x509_cert_free(tor_x509_cert_t *cert)
{
if (! cert)
return;
@@ -801,14 +738,14 @@ tor_cert_free(tor_cert_t *cert)
}
/**
- * Allocate a new tor_cert_t to hold the certificate "x509_cert".
+ * Allocate a new tor_x509_cert_t to hold the certificate "x509_cert".
*
* Steals a reference to x509_cert.
*/
-static tor_cert_t *
-tor_cert_new(X509 *x509_cert)
+static tor_x509_cert_t *
+tor_x509_cert_new(X509 *x509_cert)
{
- tor_cert_t *cert;
+ tor_x509_cert_t *cert;
EVP_PKEY *pkey;
RSA *rsa;
int length;
@@ -818,7 +755,7 @@ tor_cert_new(X509 *x509_cert)
return NULL;
length = i2d_X509(x509_cert, &buf);
- cert = tor_malloc_zero(sizeof(tor_cert_t));
+ cert = tor_malloc_zero(sizeof(tor_x509_cert_t));
if (length <= 0 || buf == NULL) {
tor_free(cert);
log_err(LD_CRYPTO, "Couldn't get length of encoded x509 certificate");
@@ -848,14 +785,14 @@ tor_cert_new(X509 *x509_cert)
}
/** Read a DER-encoded X509 cert, of length exactly <b>certificate_len</b>,
- * from a <b>certificate</b>. Return a newly allocated tor_cert_t on success
- * and NULL on failure. */
-tor_cert_t *
-tor_cert_decode(const uint8_t *certificate, size_t certificate_len)
+ * from a <b>certificate</b>. Return a newly allocated tor_x509_cert_t on
+ * success and NULL on failure. */
+tor_x509_cert_t *
+tor_x509_cert_decode(const uint8_t *certificate, size_t certificate_len)
{
X509 *x509;
const unsigned char *cp = (const unsigned char *)certificate;
- tor_cert_t *newcert;
+ tor_x509_cert_t *newcert;
tor_assert(certificate);
check_no_tls_errors();
@@ -870,14 +807,14 @@ tor_cert_decode(const uint8_t *certificate, size_t certificate_len)
X509_free(x509);
goto err; /* Didn't use all the bytes */
}
- newcert = tor_cert_new(x509);
+ newcert = tor_x509_cert_new(x509);
if (!newcert) {
goto err;
}
if (newcert->encoded_len != certificate_len ||
fast_memneq(newcert->encoded, certificate, certificate_len)) {
/* Cert wasn't in DER */
- tor_cert_free(newcert);
+ tor_x509_cert_free(newcert);
goto err;
}
return newcert;
@@ -889,7 +826,7 @@ tor_cert_decode(const uint8_t *certificate, size_t certificate_len)
/** Set *<b>encoded_out</b> and *<b>size_out</b> to <b>cert</b>'s encoded DER
* representation and length, respectively. */
void
-tor_cert_get_der(const tor_cert_t *cert,
+tor_x509_cert_get_der(const tor_x509_cert_t *cert,
const uint8_t **encoded_out, size_t *size_out)
{
tor_assert(cert);
@@ -902,7 +839,7 @@ tor_cert_get_der(const tor_cert_t *cert,
/** Return a set of digests for the public key in <b>cert</b>, or NULL if this
* cert's public key is not one we know how to take the digest of. */
const digests_t *
-tor_cert_get_id_digests(const tor_cert_t *cert)
+tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert)
{
if (cert->pkey_digests_set)
return &cert->pkey_digests;
@@ -912,7 +849,7 @@ tor_cert_get_id_digests(const tor_cert_t *cert)
/** Return a set of digests for the public key in <b>cert</b>. */
const digests_t *
-tor_cert_get_cert_digests(const tor_cert_t *cert)
+tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert)
{
return &cert->cert_digests;
}
@@ -925,9 +862,9 @@ tor_tls_context_decref(tor_tls_context_t *ctx)
tor_assert(ctx);
if (--ctx->refcnt == 0) {
SSL_CTX_free(ctx->ctx);
- tor_cert_free(ctx->my_link_cert);
- tor_cert_free(ctx->my_id_cert);
- tor_cert_free(ctx->my_auth_cert);
+ tor_x509_cert_free(ctx->my_link_cert);
+ tor_x509_cert_free(ctx->my_id_cert);
+ tor_x509_cert_free(ctx->my_auth_cert);
crypto_pk_free(ctx->link_key);
crypto_pk_free(ctx->auth_key);
tor_free(ctx);
@@ -941,8 +878,8 @@ tor_tls_context_decref(tor_tls_context_t *ctx)
* client mode. */
int
tor_tls_get_my_certs(int server,
- const tor_cert_t **link_cert_out,
- const tor_cert_t **id_cert_out)
+ const tor_x509_cert_t **link_cert_out,
+ const tor_x509_cert_t **id_cert_out)
{
tor_tls_context_t *ctx = server ? server_tls_context : client_tls_context;
if (! ctx)
@@ -971,7 +908,7 @@ tor_tls_get_my_client_auth_key(void)
* certifies. Return NULL if the cert's key is not RSA.
*/
crypto_pk_t *
-tor_tls_cert_get_key(tor_cert_t *cert)
+tor_tls_cert_get_key(tor_x509_cert_t *cert)
{
crypto_pk_t *result = NULL;
EVP_PKEY *pkey = X509_get_pubkey(cert->cert);
@@ -991,8 +928,8 @@ tor_tls_cert_get_key(tor_cert_t *cert)
/** Return true iff the other side of <b>tls</b> has authenticated to us, and
* the key certified in <b>cert</b> is the same as the key they used to do it.
*/
-int
-tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert)
+MOCK_IMPL(int,
+tor_tls_cert_matches_key,(const tor_tls_t *tls, const tor_x509_cert_t *cert))
{
X509 *peercert = SSL_get_peer_certificate(tls->ssl);
EVP_PKEY *link_key = NULL, *cert_key = NULL;
@@ -1021,8 +958,8 @@ tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert)
* we couldn't check it. */
int
tor_tls_cert_is_valid(int severity,
- const tor_cert_t *cert,
- const tor_cert_t *signing_cert,
+ const tor_x509_cert_t *cert,
+ const tor_x509_cert_t *signing_cert,
int check_rsa_1024)
{
check_no_tls_errors();
@@ -1233,9 +1170,9 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
result = tor_malloc_zero(sizeof(tor_tls_context_t));
result->refcnt = 1;
if (!is_client) {
- result->my_link_cert = tor_cert_new(X509_dup(cert));
- result->my_id_cert = tor_cert_new(X509_dup(idcert));
- result->my_auth_cert = tor_cert_new(X509_dup(authcert));
+ result->my_link_cert = tor_x509_cert_new(X509_dup(cert));
+ result->my_id_cert = tor_x509_cert_new(X509_dup(idcert));
+ result->my_auth_cert = tor_x509_cert_new(X509_dup(authcert));
if (!result->my_link_cert || !result->my_id_cert || !result->my_auth_cert)
goto error;
result->link_key = crypto_pk_dup_key(rsa);
@@ -1299,24 +1236,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
}
#endif
- /* XXX This block is now obsolete. */
- if (
-#ifdef DISABLE_SSL3_HANDSHAKE
- 1 ||
-#endif
- SSLeay() < OPENSSL_V(0,9,8,'s') ||
- (SSLeay() >= OPENSSL_V_SERIES(0,9,9) &&
- SSLeay() < OPENSSL_V(1,0,0,'f'))) {
- /* And not SSL3 if it's subject to CVE-2011-4576. */
- log_info(LD_NET, "Disabling SSLv3 because this OpenSSL version "
- "might otherwise be vulnerable to CVE-2011-4576 "
- "(compile-time version %08lx (%s); "
- "runtime version %08lx (%s))",
- (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT,
- (unsigned long)SSLeay(), SSLeay_version(SSLEAY_VERSION));
- SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3);
- }
-
SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_DH_USE);
SSL_CTX_set_options(result->ctx, SSL_OP_SINGLE_ECDH_USE);
@@ -1327,7 +1246,7 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
/* Yes, we know what we are doing here. No, we do not treat a renegotiation
* as authenticating any earlier-received data.
*/
- if (use_unsafe_renegotiation_op) {
+ {
SSL_CTX_set_options(result->ctx,
SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
}
@@ -1376,8 +1295,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
SSL_CTX_set_tmp_dh(result->ctx, crypto_dh_get_dh_(dh));
crypto_dh_free(dh);
}
-#if (!defined(OPENSSL_NO_EC) && \
- OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0))
if (! is_client) {
int nid;
EC_KEY *ec_key;
@@ -1393,9 +1310,6 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
SSL_CTX_set_tmp_ecdh(result->ctx, ec_key);
EC_KEY_free(ec_key);
}
-#else
- (void)flags;
-#endif
SSL_CTX_set_verify(result->ctx, SSL_VERIFY_PEER,
always_accept_verify_cb);
/* let us realloc bufs that we're writing from */
@@ -1535,6 +1449,8 @@ find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
}
#endif
(void) ssl;
+ (void) m;
+ (void) cipher;
return 1; /* No way to search */
}
@@ -1655,7 +1571,7 @@ tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
{
STACK_OF(SSL_CIPHER) *ciphers;
#ifdef HAVE_SSL_GET_CLIENT_CIPHERS
- ciphers = SSL_get_client_ciphers(ssl);
+ ciphers = SSL_get_ciphers(ssl);
#else
SSL_SESSION *session;
if (!(session = SSL_get_session((SSL *)ssl))) {
@@ -1719,10 +1635,6 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
if (tls) {
tls->wasV2Handshake = 1;
-#if (defined(USE_BUFFEREVENTS) && defined(SUPPORT_UNSAFE_RENEGOTATION_FLAG))
- if (use_unsafe_renegotiation_flag)
- tls->ssl->s3->flags |= SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
-#endif
} else {
log_warn(LD_BUG, "Couldn't look up the tls for an SSL*. How odd!");
}
@@ -1730,7 +1642,6 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
}
#endif
-#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0)
/** Callback to get invoked on a server after we've read the list of ciphers
* the client supports, but before we pick our own ciphersuite.
*
@@ -1768,9 +1679,6 @@ tor_tls_setup_session_secret_cb(tor_tls_t *tls)
{
SSL_set_session_secret_cb(tls->ssl, tor_tls_session_secret_cb, NULL);
}
-#else
-#define tor_tls_setup_session_secret_cb(tls) STMT_NIL
-#endif
/** Create a new TLS object from a file descriptor, and a flag to
* determine whether it is functioning as a server.
@@ -1903,15 +1811,8 @@ tor_tls_unblock_renegotiation(tor_tls_t *tls)
{
/* Yes, we know what we are doing here. No, we do not treat a renegotiation
* as authenticating any earlier-received data. */
-#ifdef SUPPORT_UNSAFE_RENEGOTIATION_FLAG
- if (use_unsafe_renegotiation_flag) {
- tls->ssl->s3->flags |= SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
- }
-#endif
- if (use_unsafe_renegotiation_op) {
- SSL_set_options(tls->ssl,
- SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
- }
+ SSL_set_options(tls->ssl,
+ SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);
}
/** If this version of openssl supports it, turn off renegotiation on
@@ -1932,16 +1833,8 @@ tor_tls_block_renegotiation(tor_tls_t *tls)
void
tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
{
-#ifdef SUPPORT_UNSAFE_RENEGOTIATION_FLAG
- if (use_unsafe_renegotiation_flag) {
- tor_assert(0 != (tls->ssl->s3->flags &
- SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
- }
-#endif
- if (use_unsafe_renegotiation_op) {
- long options = SSL_get_options(tls->ssl);
- tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
- }
+ long options = SSL_get_options(tls->ssl);
+ tor_assert(0 != (options & SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
}
/** Return whether this tls initiated the connect (client) or
@@ -1984,8 +1877,8 @@ tor_tls_free(tor_tls_t *tls)
* number of characters read. On failure, returns TOR_TLS_ERROR,
* TOR_TLS_CLOSE, TOR_TLS_WANTREAD, or TOR_TLS_WANTWRITE.
*/
-int
-tor_tls_read(tor_tls_t *tls, char *cp, size_t len)
+MOCK_IMPL(int,
+tor_tls_read,(tor_tls_t *tls, char *cp, size_t len))
{
int r, err;
tor_assert(tls);
@@ -2101,14 +1994,6 @@ tor_tls_handshake(tor_tls_t *tls)
return r;
}
-/* SSL_clear_mode was introduced in 0.9.8m */
-#ifndef SSL_clear_mode
-static void SSL_clear_mode(SSL *s, unsigned long m)
-{
- s->mode &= ~m;
-}
-#endif
-
/** Perform the final part of the intial TLS handshake on <b>tls</b>. This
* should be called for the first handshake only: it determines whether the v1
* or the v2 handshake was used, and adjusts things for the renegotiation
@@ -2294,15 +2179,15 @@ tor_tls_peer_has_cert(tor_tls_t *tls)
}
/** Return the peer certificate, or NULL if there isn't one. */
-tor_cert_t *
-tor_tls_get_peer_cert(tor_tls_t *tls)
+MOCK_IMPL(tor_x509_cert_t *,
+tor_tls_get_peer_cert,(tor_tls_t *tls))
{
X509 *cert;
cert = SSL_get_peer_certificate(tls->ssl);
tls_log_errors(tls, LOG_WARN, LD_HANDSHAKE, "getting peer certificate");
if (!cert)
return NULL;
- return tor_cert_new(cert);
+ return tor_x509_cert_new(cert);
}
/** Warn that a certificate lifetime extends through a certain range. */
@@ -2760,8 +2645,8 @@ SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len)
* the v3 handshake to prove that the client knows the TLS secrets for the
* connection <b>tls</b>. Return 0 on success, -1 on failure.
*/
-int
-tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out)
+MOCK_IMPL(int,
+tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out))
{
#define TLSSECRET_MAGIC "Tor V3 handshake TLS cross-certification"
uint8_t buf[128];
@@ -2925,3 +2810,29 @@ tor_tls_init_bufferevent(tor_tls_t *tls, struct bufferevent *bufev_in,
}
#endif
+/** Check whether the ECC group requested is supported by the current OpenSSL
+ * library instance. Return 1 if the group is supported, and 0 if not.
+ */
+int
+evaluate_ecgroup_for_tls(const char *ecgroup)
+{
+ EC_KEY *ec_key;
+ int nid;
+ int ret;
+
+ if (!ecgroup)
+ nid = NID_tor_default_ecdhe_group;
+ else if (!strcasecmp(ecgroup, "P256"))
+ nid = NID_X9_62_prime256v1;
+ else if (!strcasecmp(ecgroup, "P224"))
+ nid = NID_secp224r1;
+ else
+ return 0;
+
+ ec_key = EC_KEY_new_by_curve_name(nid);
+ ret = (ec_key != NULL);
+ EC_KEY_free(ec_key);
+
+ return ret;
+}
+
diff --git a/src/common/tortls.h b/src/common/tortls.h
index 5e1606f42d..124b77160f 100644
--- a/src/common/tortls.h
+++ b/src/common/tortls.h
@@ -19,7 +19,7 @@
typedef struct tor_tls_t tor_tls_t;
/* Opaque structure to hold an X509 certificate. */
-typedef struct tor_cert_t tor_cert_t;
+typedef struct tor_x509_cert_t tor_x509_cert_t;
/* Possible return values for most tor_tls_* functions. */
#define MIN_TOR_TLS_ERROR_VAL_ -9
@@ -72,12 +72,12 @@ void tor_tls_set_renegotiate_callback(tor_tls_t *tls,
int tor_tls_is_server(tor_tls_t *tls);
void tor_tls_free(tor_tls_t *tls);
int tor_tls_peer_has_cert(tor_tls_t *tls);
-tor_cert_t *tor_tls_get_peer_cert(tor_tls_t *tls);
+MOCK_DECL(tor_x509_cert_t *,tor_tls_get_peer_cert,(tor_tls_t *tls));
int tor_tls_verify(int severity, tor_tls_t *tls, crypto_pk_t **identity);
int tor_tls_check_lifetime(int severity,
tor_tls_t *tls, int past_tolerance,
int future_tolerance);
-int tor_tls_read(tor_tls_t *tls, char *cp, size_t len);
+MOCK_DECL(int, tor_tls_read, (tor_tls_t *tls, char *cp, size_t len));
int tor_tls_write(tor_tls_t *tls, const char *cp, size_t n);
int tor_tls_handshake(tor_tls_t *tls);
int tor_tls_finish_handshake(tor_tls_t *tls);
@@ -102,7 +102,7 @@ int tor_tls_used_v1_handshake(tor_tls_t *tls);
int tor_tls_received_v3_certificate(tor_tls_t *tls);
int tor_tls_get_num_server_handshakes(tor_tls_t *tls);
int tor_tls_server_got_renegotiate(tor_tls_t *tls);
-int tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out);
+MOCK_DECL(int,tor_tls_get_tlssecrets,(tor_tls_t *tls, uint8_t *secrets_out));
/* Log and abort if there are unhandled TLS errors in OpenSSL's error stack.
*/
@@ -120,24 +120,27 @@ struct bufferevent *tor_tls_init_bufferevent(tor_tls_t *tls,
int filter);
#endif
-void tor_cert_free(tor_cert_t *cert);
-tor_cert_t *tor_cert_decode(const uint8_t *certificate,
+void tor_x509_cert_free(tor_x509_cert_t *cert);
+tor_x509_cert_t *tor_x509_cert_decode(const uint8_t *certificate,
size_t certificate_len);
-void tor_cert_get_der(const tor_cert_t *cert,
+void tor_x509_cert_get_der(const tor_x509_cert_t *cert,
const uint8_t **encoded_out, size_t *size_out);
-const digests_t *tor_cert_get_id_digests(const tor_cert_t *cert);
-const digests_t *tor_cert_get_cert_digests(const tor_cert_t *cert);
+const digests_t *tor_x509_cert_get_id_digests(const tor_x509_cert_t *cert);
+const digests_t *tor_x509_cert_get_cert_digests(const tor_x509_cert_t *cert);
int tor_tls_get_my_certs(int server,
- const tor_cert_t **link_cert_out,
- const tor_cert_t **id_cert_out);
+ const tor_x509_cert_t **link_cert_out,
+ const tor_x509_cert_t **id_cert_out);
crypto_pk_t *tor_tls_get_my_client_auth_key(void);
-crypto_pk_t *tor_tls_cert_get_key(tor_cert_t *cert);
-int tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_cert_t *cert);
+crypto_pk_t *tor_tls_cert_get_key(tor_x509_cert_t *cert);
+MOCK_DECL(int,tor_tls_cert_matches_key,(const tor_tls_t *tls,
+ const tor_x509_cert_t *cert));
int tor_tls_cert_is_valid(int severity,
- const tor_cert_t *cert,
- const tor_cert_t *signing_cert,
+ const tor_x509_cert_t *cert,
+ const tor_x509_cert_t *signing_cert,
int check_rsa_1024);
const char *tor_tls_get_ciphersuite_name(tor_tls_t *tls);
+int evaluate_ecgroup_for_tls(const char *ecgroup);
+
#endif
diff --git a/src/common/util.c b/src/common/util.c
index 442d57a2cf..942d0c290e 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -95,6 +95,9 @@
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
+#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
+#include <sys/prctl.h>
+#endif
#ifdef __clang_analyzer__
#undef MALLOC_ZERO_WORKS
@@ -771,16 +774,6 @@ fast_memcmpstart(const void *mem, size_t memlen,
return fast_memcmp(mem, prefix, plen);
}
-/** Given a nul-terminated string s, set every character before the nul
- * to zero. */
-void
-tor_strclear(char *s)
-{
- while (*s) {
- *s++ = '\0';
- }
-}
-
/** Return a pointer to the first char of s that is not whitespace and
* not a comment, or to the terminating NUL if no such character exists.
*/
@@ -3562,7 +3555,7 @@ finish_daemon(const char *cp)
/** Write the current process ID, followed by NL, into <b>filename</b>.
*/
void
-write_pidfile(char *filename)
+write_pidfile(const char *filename)
{
FILE *pidfile;
@@ -3949,9 +3942,11 @@ process_handle_new(void)
process_handle_t *out = tor_malloc_zero(sizeof(process_handle_t));
#ifdef _WIN32
+ out->stdin_pipe = INVALID_HANDLE_VALUE;
out->stdout_pipe = INVALID_HANDLE_VALUE;
out->stderr_pipe = INVALID_HANDLE_VALUE;
#else
+ out->stdin_pipe = -1;
out->stdout_pipe = -1;
out->stderr_pipe = -1;
#endif
@@ -3991,7 +3986,7 @@ process_handle_waitpid_cb(int status, void *arg)
#define CHILD_STATE_FORK 3
#define CHILD_STATE_DUPOUT 4
#define CHILD_STATE_DUPERR 5
-#define CHILD_STATE_REDIRECT 6
+#define CHILD_STATE_DUPIN 6
#define CHILD_STATE_CLOSEFD 7
#define CHILD_STATE_EXEC 8
#define CHILD_STATE_FAILEXEC 9
@@ -4025,6 +4020,8 @@ tor_spawn_background(const char *const filename, const char **argv,
HANDLE stdout_pipe_write = NULL;
HANDLE stderr_pipe_read = NULL;
HANDLE stderr_pipe_write = NULL;
+ HANDLE stdin_pipe_read = NULL;
+ HANDLE stdin_pipe_write = NULL;
process_handle_t *process_handle;
int status;
@@ -4070,6 +4067,20 @@ tor_spawn_background(const char *const filename, const char **argv,
return status;
}
+ /* Set up pipe for stdin */
+ if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, &saAttr, 0)) {
+ log_warn(LD_GENERAL,
+ "Failed to create pipe for stdin communication with child process: %s",
+ format_win32_error(GetLastError()));
+ return status;
+ }
+ if (!SetHandleInformation(stdin_pipe_write, HANDLE_FLAG_INHERIT, 0)) {
+ log_warn(LD_GENERAL,
+ "Failed to configure pipe for stdin communication with child "
+ "process: %s", format_win32_error(GetLastError()));
+ return status;
+ }
+
/* Create the child process */
/* Windows expects argv to be a whitespace delimited string, so join argv up
@@ -4084,7 +4095,7 @@ tor_spawn_background(const char *const filename, const char **argv,
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = stderr_pipe_write;
siStartInfo.hStdOutput = stdout_pipe_write;
- siStartInfo.hStdInput = NULL;
+ siStartInfo.hStdInput = stdin_pipe_read;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
/* Create the child process */
@@ -4114,6 +4125,7 @@ tor_spawn_background(const char *const filename, const char **argv,
/* TODO: Close hProcess and hThread in process_handle->pid? */
process_handle->stdout_pipe = stdout_pipe_read;
process_handle->stderr_pipe = stderr_pipe_read;
+ process_handle->stdin_pipe = stdin_pipe_write;
status = process_handle->status = PROCESS_STATUS_RUNNING;
}
@@ -4124,6 +4136,7 @@ tor_spawn_background(const char *const filename, const char **argv,
pid_t pid;
int stdout_pipe[2];
int stderr_pipe[2];
+ int stdin_pipe[2];
int fd, retval;
ssize_t nbytes;
process_handle_t *process_handle;
@@ -4148,7 +4161,7 @@ tor_spawn_background(const char *const filename, const char **argv,
child_state = CHILD_STATE_PIPE;
- /* Set up pipe for redirecting stdout and stderr of child */
+ /* Set up pipe for redirecting stdout, stderr, and stdin of child */
retval = pipe(stdout_pipe);
if (-1 == retval) {
log_warn(LD_GENERAL,
@@ -4169,6 +4182,20 @@ tor_spawn_background(const char *const filename, const char **argv,
return status;
}
+ retval = pipe(stdin_pipe);
+ if (-1 == retval) {
+ log_warn(LD_GENERAL,
+ "Failed to set up pipe for stdin communication with child process: %s",
+ strerror(errno));
+
+ close(stdout_pipe[0]);
+ close(stdout_pipe[1]);
+ close(stderr_pipe[0]);
+ close(stderr_pipe[1]);
+
+ return status;
+ }
+
child_state = CHILD_STATE_MAXFD;
#ifdef _SC_OPEN_MAX
@@ -4190,6 +4217,15 @@ tor_spawn_background(const char *const filename, const char **argv,
if (0 == pid) {
/* In child */
+#if defined(HAVE_SYS_PRCTL_H) && defined(__linux__)
+ /* Attempt to have the kernel issue a SIGTERM if the parent
+ * goes away. Certain attributes of the binary being execve()ed
+ * will clear this during the execve() call, but it's better
+ * than nothing.
+ */
+ prctl(PR_SET_PDEATHSIG, SIGTERM);
+#endif
+
child_state = CHILD_STATE_DUPOUT;
/* Link child stdout to the write end of the pipe */
@@ -4204,13 +4240,11 @@ tor_spawn_background(const char *const filename, const char **argv,
if (-1 == retval)
goto error;
- child_state = CHILD_STATE_REDIRECT;
+ child_state = CHILD_STATE_DUPIN;
- /* Link stdin to /dev/null */
- fd = open("/dev/null", O_RDONLY); /* NOT cloexec, obviously. */
- if (fd != -1)
- dup2(fd, STDIN_FILENO);
- else
+ /* Link child stdin to the read end of the pipe */
+ retval = dup2(stdin_pipe[0], STDIN_FILENO);
+ if (-1 == retval)
goto error;
child_state = CHILD_STATE_CLOSEFD;
@@ -4219,7 +4253,8 @@ tor_spawn_background(const char *const filename, const char **argv,
close(stderr_pipe[1]);
close(stdout_pipe[0]);
close(stdout_pipe[1]);
- close(fd);
+ close(stdin_pipe[0]);
+ close(stdin_pipe[1]);
/* Close all other fds, including the read end of the pipe */
/* XXX: We should now be doing enough FD_CLOEXEC setting to make
@@ -4235,8 +4270,10 @@ tor_spawn_background(const char *const filename, const char **argv,
does not modify the arguments */
if (env)
execve(filename, (char *const *) argv, env->unixoid_environment_block);
- else
- execvp(filename, (char *const *) argv);
+ else {
+ static char *new_env[] = { NULL };
+ execve(filename, (char *const *) argv, new_env);
+ }
/* If we got here, the exec or open(/dev/null) failed */
@@ -4269,6 +4306,8 @@ tor_spawn_background(const char *const filename, const char **argv,
if (-1 == pid) {
log_warn(LD_GENERAL, "Failed to fork child process: %s", strerror(errno));
+ close(stdin_pipe[0]);
+ close(stdin_pipe[1]);
close(stdout_pipe[0]);
close(stdout_pipe[1]);
close(stderr_pipe[0]);
@@ -4305,16 +4344,28 @@ tor_spawn_background(const char *const filename, const char **argv,
strerror(errno));
}
+ /* Return write end of the stdin pipe to caller, and close the read end */
+ process_handle->stdin_pipe = stdin_pipe[1];
+ retval = close(stdin_pipe[0]);
+
+ if (-1 == retval) {
+ log_warn(LD_GENERAL,
+ "Failed to close read end of stdin pipe in parent process: %s",
+ strerror(errno));
+ }
+
status = process_handle->status = PROCESS_STATUS_RUNNING;
- /* Set stdout/stderr pipes to be non-blocking */
+ /* Set stdin/stdout/stderr pipes to be non-blocking */
if (fcntl(process_handle->stdout_pipe, F_SETFL, O_NONBLOCK) < 0 ||
- fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK) < 0) {
- log_warn(LD_GENERAL, "Failed to set stderror/stdout pipes nonblocking "
- "in parent process: %s", strerror(errno));
+ fcntl(process_handle->stderr_pipe, F_SETFL, O_NONBLOCK) < 0 ||
+ fcntl(process_handle->stdin_pipe, F_SETFL, O_NONBLOCK) < 0) {
+ log_warn(LD_GENERAL, "Failed to set stderror/stdout/stdin pipes "
+ "nonblocking in parent process: %s", strerror(errno));
}
/* Open the buffered IO streams */
process_handle->stdout_handle = fdopen(process_handle->stdout_pipe, "r");
process_handle->stderr_handle = fdopen(process_handle->stderr_pipe, "r");
+ process_handle->stdin_handle = fdopen(process_handle->stdin_pipe, "r");
*process_handle_out = process_handle;
return process_handle->status;
@@ -4357,6 +4408,9 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
if (process_handle->stderr_pipe)
CloseHandle(process_handle->stderr_pipe);
+
+ if (process_handle->stdin_pipe)
+ CloseHandle(process_handle->stdin_pipe);
#else
if (process_handle->stdout_handle)
fclose(process_handle->stdout_handle);
@@ -4364,6 +4418,9 @@ tor_process_handle_destroy,(process_handle_t *process_handle,
if (process_handle->stderr_handle)
fclose(process_handle->stderr_handle);
+ if (process_handle->stdin_handle)
+ fclose(process_handle->stdin_handle);
+
clear_waitpid_callback(process_handle->waitpid_cb);
#endif
diff --git a/src/common/util.h b/src/common/util.h
index ea774bd9bd..ac4ebfc6f3 100644
--- a/src/common/util.h
+++ b/src/common/util.h
@@ -45,6 +45,13 @@
#error "Sorry; we don't support building with NDEBUG."
#endif
+/* Don't use assertions during coverage. It leads to tons of unreached
+ * branches which in reality are only assertions we didn't hit. */
+#ifdef TOR_COVERAGE
+#define tor_assert(a) STMT_BEGIN \
+ (void)(a); \
+ STMT_END
+#else
/** Like assert(3), but send assertion failures to the log as well as to
* stderr. */
#define tor_assert(expr) STMT_BEGIN \
@@ -52,6 +59,7 @@
tor_assertion_failed_(SHORT_FILE__, __LINE__, __func__, #expr); \
abort(); \
} STMT_END
+#endif
void tor_assertion_failed_(const char *fname, unsigned int line,
const char *func, const char *expr);
@@ -209,7 +217,6 @@ int strcasecmpstart(const char *s1, const char *s2) ATTR_NONNULL((1,2));
int strcmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2));
int strcasecmpend(const char *s1, const char *s2) ATTR_NONNULL((1,2));
int fast_memcmpstart(const void *mem, size_t memlen, const char *prefix);
-void tor_strclear(char *s);
void tor_strstrip(char *s, const char *strip) ATTR_NONNULL((1,2));
long tor_parse_long(const char *s, int base, long min,
@@ -411,7 +418,7 @@ int path_is_relative(const char *filename);
/* Process helpers */
void start_daemon(void);
void finish_daemon(const char *desired_cwd);
-void write_pidfile(char *filename);
+void write_pidfile(const char *filename);
/* Port forwarding */
void tor_check_port_forwarding(const char *filename,
@@ -467,12 +474,15 @@ struct process_handle_t {
/** One of the PROCESS_STATUS_* values */
int status;
#ifdef _WIN32
+ HANDLE stdin_pipe;
HANDLE stdout_pipe;
HANDLE stderr_pipe;
PROCESS_INFORMATION pid;
#else
+ int stdin_pipe;
int stdout_pipe;
int stderr_pipe;
+ FILE *stdin_handle;
FILE *stdout_handle;
FILE *stderr_handle;
pid_t pid;
@@ -563,8 +573,6 @@ STATIC int format_helper_exit_status(unsigned char child_state,
#endif
-const char *libor_get_digests(void);
-
#define ARRAY_LENGTH(x) ((sizeof(x)) / sizeof(x[0]))
#endif
diff --git a/src/common/util_codedigest.c b/src/common/util_codedigest.c
deleted file mode 100644
index 7384f7dc1a..0000000000
--- a/src/common/util_codedigest.c
+++ /dev/null
@@ -1,13 +0,0 @@
-
-#include "util.h"
-
-/** Return a string describing the digest of the source files in src/common/
- */
-const char *
-libor_get_digests(void)
-{
- return ""
-#include "common_sha1.i"
- ;
-}
-
diff --git a/src/common/workqueue.c b/src/common/workqueue.c
index c1bd6d4e8b..ed896d78d0 100644
--- a/src/common/workqueue.c
+++ b/src/common/workqueue.c
@@ -359,12 +359,17 @@ threadpool_queue_update(threadpool_t *pool,
return 0;
}
+/** Don't have more than this many threads per pool. */
+#define MAX_THREADS 1024
+
/** Launch threads until we have <b>n</b>. */
static int
threadpool_start_threads(threadpool_t *pool, int n)
{
if (n < 0)
return -1;
+ if (n > MAX_THREADS)
+ n = MAX_THREADS;
tor_mutex_acquire(&pool->lock);
diff --git a/src/ext/eventdns.c b/src/ext/eventdns.c
index 2b2988f1ec..a0c7ff29fa 100644
--- a/src/ext/eventdns.c
+++ b/src/ext/eventdns.c
@@ -37,7 +37,7 @@
*/
#include "eventdns_tor.h"
-#include "../common/util.h"
+#include "util.h"
#include <sys/types.h>
/* #define NDEBUG */
diff --git a/src/ext/trunnel/trunnel-impl.h b/src/ext/trunnel/trunnel-impl.h
index 8714fded9f..d98dc34fa2 100644
--- a/src/ext/trunnel/trunnel-impl.h
+++ b/src/ext/trunnel/trunnel-impl.h
@@ -1,4 +1,4 @@
-/* trunnel-impl.h -- copied from Trunnel v1.2
+/* trunnel-impl.h -- copied from Trunnel v1.4.1
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/ext/trunnel/trunnel.c b/src/ext/trunnel/trunnel.c
index 735323798f..ce8db4d248 100644
--- a/src/ext/trunnel/trunnel.c
+++ b/src/ext/trunnel/trunnel.c
@@ -1,4 +1,4 @@
-/* trunnel.c -- copied from Trunnel v1.4-pre
+/* trunnel.c -- copied from Trunnel v1.4.1
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/ext/trunnel/trunnel.h b/src/ext/trunnel/trunnel.h
index 22c1ed80c9..78da904f4f 100644
--- a/src/ext/trunnel/trunnel.h
+++ b/src/ext/trunnel/trunnel.h
@@ -1,4 +1,4 @@
-/* trunnel.h -- copied from Trunnel v1.2
+/* trunnel.h -- copied from Trunnel v1.4.1
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/or/buffers.c b/src/or/buffers.c
index be9974418d..2d7dd937d8 100644
--- a/src/or/buffers.c
+++ b/src/or/buffers.c
@@ -20,8 +20,8 @@
#include "control.h"
#include "reasons.h"
#include "ext_orport.h"
-#include "../common/util.h"
-#include "../common/torlog.h"
+#include "util.h"
+#include "torlog.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
@@ -615,7 +615,7 @@ read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf)
if (r < 0)
return r; /* Error */
tor_assert(total_read+r < INT_MAX);
- total_read += r;
+ total_read += r;
if ((size_t)r < readlen) /* eof, block, or no more to read. */
break;
}
diff --git a/src/or/channel.c b/src/or/channel.c
index bf0387f10e..af095026e4 100644
--- a/src/or/channel.c
+++ b/src/or/channel.c
@@ -4431,10 +4431,10 @@ channel_num_circuits(channel_t *chan)
* This is called when setting up a channel and replaces the old
* connection_or_set_circid_type()
*/
-void
-channel_set_circid_type(channel_t *chan,
- crypto_pk_t *identity_rcvd,
- int consider_identity)
+MOCK_IMPL(void,
+channel_set_circid_type,(channel_t *chan,
+ crypto_pk_t *identity_rcvd,
+ int consider_identity))
{
int started_here;
crypto_pk_t *our_identity;
diff --git a/src/or/channel.h b/src/or/channel.h
index ecc2a092e4..2b38ca7e19 100644
--- a/src/or/channel.h
+++ b/src/or/channel.h
@@ -562,8 +562,9 @@ int channel_matches_extend_info(channel_t *chan, extend_info_t *extend_info);
int channel_matches_target_addr_for_extend(channel_t *chan,
const tor_addr_t *target);
unsigned int channel_num_circuits(channel_t *chan);
-void channel_set_circid_type(channel_t *chan, crypto_pk_t *identity_rcvd,
- int consider_identity);
+MOCK_DECL(void,channel_set_circid_type,(channel_t *chan,
+ crypto_pk_t *identity_rcvd,
+ int consider_identity));
void channel_timestamp_client(channel_t *chan);
void channel_update_xmit_queue_size(channel_t *chan);
diff --git a/src/or/channeltls.c b/src/or/channeltls.c
index 1cf697ccc5..c90f569233 100644
--- a/src/or/channeltls.c
+++ b/src/or/channeltls.c
@@ -13,6 +13,8 @@
#define TOR_CHANNEL_INTERNAL_
+#define CHANNELTLS_PRIVATE
+
#include "or.h"
#include "channel.h"
#include "channeltls.h"
@@ -22,6 +24,7 @@
#include "connection.h"
#include "connection_or.h"
#include "control.h"
+#include "link_handshake.h"
#include "relay.h"
#include "rephist.h"
#include "router.h"
@@ -48,9 +51,6 @@ uint64_t stats_n_authorize_cells_processed = 0;
/** Active listener, if any */
channel_listener_t *channel_tls_listener = NULL;
-/* Utility function declarations */
-static void channel_tls_common_init(channel_tls_t *tlschan);
-
/* channel_tls_t method declarations */
static void channel_tls_close_method(channel_t *chan);
@@ -92,12 +92,6 @@ static void channel_tls_process_versions_cell(var_cell_t *cell,
channel_tls_t *tlschan);
static void channel_tls_process_netinfo_cell(cell_t *cell,
channel_tls_t *tlschan);
-static void channel_tls_process_certs_cell(var_cell_t *cell,
- channel_tls_t *tlschan);
-static void channel_tls_process_auth_challenge_cell(var_cell_t *cell,
- channel_tls_t *tlschan);
-static void channel_tls_process_authenticate_cell(var_cell_t *cell,
- channel_tls_t *tlschan);
static int command_allowed_before_handshake(uint8_t command);
static int enter_v3_handshake_with_cell(var_cell_t *cell,
channel_tls_t *tlschan);
@@ -107,7 +101,7 @@ static int enter_v3_handshake_with_cell(var_cell_t *cell,
* and channel_tls_handle_incoming().
*/
-static void
+STATIC void
channel_tls_common_init(channel_tls_t *tlschan)
{
channel_t *chan;
@@ -1747,16 +1741,17 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan)
* If it's the server side, wait for an AUTHENTICATE cell.
*/
-static void
+STATIC void
channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
{
- tor_cert_t *link_cert = NULL;
- tor_cert_t *id_cert = NULL;
- tor_cert_t *auth_cert = NULL;
- uint8_t *ptr;
+#define MAX_CERT_TYPE_WANTED OR_CERT_TYPE_AUTH_1024
+ tor_x509_cert_t *certs[MAX_CERT_TYPE_WANTED + 1];
int n_certs, i;
+ certs_cell_t *cc = NULL;
+
int send_netinfo = 0;
+ memset(certs, 0, sizeof(certs));
tor_assert(cell);
tor_assert(chan);
tor_assert(chan->conn);
@@ -1786,63 +1781,41 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
if (cell->circ_id)
ERR("It had a nonzero circuit ID");
- n_certs = cell->payload[0];
- ptr = cell->payload + 1;
+ if (certs_cell_parse(&cc, cell->payload, cell->payload_len) < 0)
+ ERR("It couldn't be parsed.");
+
+ n_certs = cc->n_certs;
+
for (i = 0; i < n_certs; ++i) {
- uint8_t cert_type;
- uint16_t cert_len;
- if (cell->payload_len < 3)
- goto truncated;
- if (ptr > cell->payload + cell->payload_len - 3) {
- goto truncated;
- }
- cert_type = *ptr;
- cert_len = ntohs(get_uint16(ptr+1));
- if (cell->payload_len < 3 + cert_len)
- goto truncated;
- if (ptr > cell->payload + cell->payload_len - cert_len - 3) {
- goto truncated;
- }
- if (cert_type == OR_CERT_TYPE_TLS_LINK ||
- cert_type == OR_CERT_TYPE_ID_1024 ||
- cert_type == OR_CERT_TYPE_AUTH_1024) {
- tor_cert_t *cert = tor_cert_decode(ptr + 3, cert_len);
- if (!cert) {
- log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
- "Received undecodable certificate in CERTS cell from %s:%d",
- safe_str(chan->conn->base_.address),
- chan->conn->base_.port);
+ certs_cell_cert_t *c = certs_cell_get_certs(cc, i);
+
+ uint16_t cert_type = c->cert_type;
+ uint16_t cert_len = c->cert_len;
+ uint8_t *cert_body = certs_cell_cert_getarray_body(c);
+
+ if (cert_type > MAX_CERT_TYPE_WANTED)
+ continue;
+
+ tor_x509_cert_t *cert = tor_x509_cert_decode(cert_body, cert_len);
+ if (!cert) {
+ log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
+ "Received undecodable certificate in CERTS cell from %s:%d",
+ safe_str(chan->conn->base_.address),
+ chan->conn->base_.port);
+ } else {
+ if (certs[cert_type]) {
+ tor_x509_cert_free(cert);
+ ERR("Duplicate x509 certificate");
} else {
- if (cert_type == OR_CERT_TYPE_TLS_LINK) {
- if (link_cert) {
- tor_cert_free(cert);
- ERR("Too many TLS_LINK certificates");
- }
- link_cert = cert;
- } else if (cert_type == OR_CERT_TYPE_ID_1024) {
- if (id_cert) {
- tor_cert_free(cert);
- ERR("Too many ID_1024 certificates");
- }
- id_cert = cert;
- } else if (cert_type == OR_CERT_TYPE_AUTH_1024) {
- if (auth_cert) {
- tor_cert_free(cert);
- ERR("Too many AUTH_1024 certificates");
- }
- auth_cert = cert;
- } else {
- tor_cert_free(cert);
- }
+ certs[cert_type] = cert;
}
}
- ptr += 3 + cert_len;
- continue;
-
- truncated:
- ERR("It ends in the middle of a certificate");
}
+ tor_x509_cert_t *id_cert = certs[OR_CERT_TYPE_ID_1024];
+ tor_x509_cert_t *auth_cert = certs[OR_CERT_TYPE_AUTH_1024];
+ tor_x509_cert_t *link_cert = certs[OR_CERT_TYPE_TLS_LINK];
+
if (chan->conn->handshake_state->started_here) {
int severity;
if (! (id_cert && link_cert))
@@ -1867,7 +1840,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
chan->conn->handshake_state->authenticated = 1;
{
- const digests_t *id_digests = tor_cert_get_id_digests(id_cert);
+ const digests_t *id_digests = tor_x509_cert_get_id_digests(id_cert);
crypto_pk_t *identity_rcvd;
if (!id_digests)
ERR("Couldn't compute digests for key in ID cert");
@@ -1891,7 +1864,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
safe_str(chan->conn->base_.address), chan->conn->base_.port);
chan->conn->handshake_state->id_cert = id_cert;
- id_cert = NULL;
+ certs[OR_CERT_TYPE_ID_1024] = NULL;
if (!public_server_mode(get_options())) {
/* If we initiated the connection and we are not a public server, we
@@ -1918,7 +1891,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
chan->conn->handshake_state->id_cert = id_cert;
chan->conn->handshake_state->auth_cert = auth_cert;
- id_cert = auth_cert = NULL;
+ certs[OR_CERT_TYPE_ID_1024] = certs[OR_CERT_TYPE_AUTH_1024] = NULL;
}
chan->conn->handshake_state->received_certs_cell = 1;
@@ -1932,9 +1905,10 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
}
err:
- tor_cert_free(id_cert);
- tor_cert_free(link_cert);
- tor_cert_free(auth_cert);
+ for (unsigned i = 0; i < ARRAY_LENGTH(certs); ++i) {
+ tor_x509_cert_free(certs[i]);
+ }
+ certs_cell_free(cc);
#undef ERR
}
@@ -1949,11 +1923,11 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan)
* want to authenticate, send an AUTHENTICATE cell and then a NETINFO cell.
*/
-static void
+STATIC void
channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
{
int n_types, i, use_type = -1;
- uint8_t *cp;
+ auth_challenge_cell_t *ac = NULL;
tor_assert(cell);
tor_assert(chan);
@@ -1966,7 +1940,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
safe_str(chan->conn->base_.address), \
chan->conn->base_.port, (s)); \
connection_or_close_for_error(chan->conn, 0); \
- return; \
+ goto done; \
} while (0)
if (chan->conn->base_.state != OR_CONN_STATE_OR_HANDSHAKING_V3)
@@ -1979,19 +1953,17 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
ERR("We already received one");
if (!(chan->conn->handshake_state->received_certs_cell))
ERR("We haven't gotten a CERTS cell yet");
- if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2)
- ERR("It was too short");
if (cell->circ_id)
ERR("It had a nonzero circuit ID");
- n_types = ntohs(get_uint16(cell->payload + OR_AUTH_CHALLENGE_LEN));
- if (cell->payload_len < OR_AUTH_CHALLENGE_LEN + 2 + 2*n_types)
- ERR("It looks truncated");
+ if (auth_challenge_cell_parse(&ac, cell->payload, cell->payload_len) < 0)
+ ERR("It was not well-formed.");
+
+ n_types = ac->n_methods;
/* Now see if there is an authentication type we can use */
- cp = cell->payload+OR_AUTH_CHALLENGE_LEN + 2;
- for (i = 0; i < n_types; ++i, cp += 2) {
- uint16_t authtype = ntohs(get_uint16(cp));
+ for (i = 0; i < n_types; ++i) {
+ uint16_t authtype = auth_challenge_cell_get_methods(ac, i);
if (authtype == AUTHTYPE_RSA_SHA256_TLSSECRET)
use_type = authtype;
}
@@ -2002,7 +1974,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
/* If we're not a public server then we don't want to authenticate on a
connection we originated, and we already sent a NETINFO cell when we
got the CERTS cell. We have nothing more to do. */
- return;
+ goto done;
}
if (use_type >= 0) {
@@ -2016,7 +1988,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
log_warn(LD_OR,
"Couldn't send authenticate cell");
connection_or_close_for_error(chan->conn, 0);
- return;
+ goto done;
}
} else {
log_info(LD_OR,
@@ -2029,9 +2001,12 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
if (connection_or_send_netinfo(chan->conn) < 0) {
log_warn(LD_OR, "Couldn't send netinfo cell");
connection_or_close_for_error(chan->conn, 0);
- return;
+ goto done;
}
+ done:
+ auth_challenge_cell_free(ac);
+
#undef ERR
}
@@ -2045,10 +2020,10 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan)
* the identity of the router on the other side of the connection.
*/
-static void
+STATIC void
channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
{
- uint8_t expected[V3_AUTH_FIXED_PART_LEN];
+ uint8_t expected[V3_AUTH_FIXED_PART_LEN+256];
const uint8_t *auth;
int authlen;
@@ -2104,11 +2079,13 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
if (authlen < V3_AUTH_BODY_LEN + 1)
ERR("Authenticator was too short");
- if (connection_or_compute_authenticate_cell_body(
- chan->conn, expected, sizeof(expected), NULL, 1) < 0)
+ ssize_t bodylen =
+ connection_or_compute_authenticate_cell_body(
+ chan->conn, expected, sizeof(expected), NULL, 1);
+ if (bodylen < 0 || bodylen != V3_AUTH_FIXED_PART_LEN)
ERR("Couldn't compute expected AUTHENTICATE cell body");
- if (tor_memneq(expected, auth, sizeof(expected)))
+ if (tor_memneq(expected, auth, bodylen))
ERR("Some field in the AUTHENTICATE cell body was not as expected");
{
@@ -2154,7 +2131,7 @@ channel_tls_process_authenticate_cell(var_cell_t *cell, channel_tls_t *chan)
crypto_pk_t *identity_rcvd =
tor_tls_cert_get_key(chan->conn->handshake_state->id_cert);
const digests_t *id_digests =
- tor_cert_get_id_digests(chan->conn->handshake_state->id_cert);
+ tor_x509_cert_get_id_digests(chan->conn->handshake_state->id_cert);
/* This must exist; we checked key type when reading the cert. */
tor_assert(id_digests);
diff --git a/src/or/channeltls.h b/src/or/channeltls.h
index 507429420b..a0df9faac2 100644
--- a/src/or/channeltls.h
+++ b/src/or/channeltls.h
@@ -52,5 +52,15 @@ void channel_tls_update_marks(or_connection_t *conn);
/* Cleanup at shutdown */
void channel_tls_free_all(void);
+#ifdef CHANNELTLS_PRIVATE
+STATIC void channel_tls_process_certs_cell(var_cell_t *cell,
+ channel_tls_t *tlschan);
+STATIC void channel_tls_process_auth_challenge_cell(var_cell_t *cell,
+ channel_tls_t *tlschan);
+STATIC void channel_tls_common_init(channel_tls_t *tlschan);
+STATIC void channel_tls_process_authenticate_cell(var_cell_t *cell,
+ channel_tls_t *tlschan);
+#endif
+
#endif
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 946c002735..0688398f6d 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -1053,6 +1053,10 @@ circuit_note_clock_jumped(int seconds_elapsed)
"CLOCK_JUMPED");
circuit_mark_all_unused_circs();
circuit_mark_all_dirty_circs_as_unusable();
+ if (seconds_elapsed < 0) {
+ /* Restart all the timers in case we jumped a long way into the past. */
+ reset_all_main_loop_timers();
+ }
}
/** Take the 'extend' <b>cell</b>, pull out addr/port plus the onion
@@ -1396,9 +1400,12 @@ onionskin_answer(or_circuit_t *circ,
log_debug(LD_CIRC,"Finished sending '%s' cell.",
circ->is_first_hop ? "created_fast" : "created");
- /* Ignore the local bit when testing - many test networks run on local
- * addresses */
- if ((!channel_is_local(circ->p_chan) || get_options()->TestingTorNetwork)
+ /* Ignore the local bit when ExtendAllowPrivateAddresses is set:
+ * it violates the assumption that private addresses are local.
+ * Also, many test networks run on local addresses, and
+ * TestingTorNetwork sets ExtendAllowPrivateAddresses. */
+ if ((!channel_is_local(circ->p_chan)
+ || get_options()->ExtendAllowPrivateAddresses)
&& !channel_is_outgoing(circ->p_chan)) {
/* record that we could process create cells from a non-local conn
* that we didn't initiate; presumably this means that create cells
diff --git a/src/or/circuitstats.c b/src/or/circuitstats.c
index 7b3ad56537..3ced5afad5 100644
--- a/src/or/circuitstats.c
+++ b/src/or/circuitstats.c
@@ -1232,6 +1232,9 @@ circuit_build_times_network_is_live(circuit_build_times_t *cbt)
}
cbt->liveness.network_last_live = now;
cbt->liveness.nonlive_timeouts = 0;
+
+ /* Tell control.c */
+ control_event_network_liveness_update(1);
}
/**
@@ -1316,6 +1319,9 @@ circuit_build_times_network_close(circuit_build_times_t *cbt,
"Tor has not observed any network activity for the past %d "
"seconds. Disabling circuit build timeout recording.",
(int)(now - cbt->liveness.network_last_live));
+
+ /* Tell control.c */
+ control_event_network_liveness_update(0);
} else {
log_info(LD_CIRC,
"Got non-live timeout. Current count is: %d",
diff --git a/src/or/circuituse.c b/src/or/circuituse.c
index d0d31ad9cf..a429a7d053 100644
--- a/src/or/circuituse.c
+++ b/src/or/circuituse.c
@@ -1189,17 +1189,28 @@ circuit_detach_stream(circuit_t *circ, edge_connection_t *conn)
if (CIRCUIT_IS_ORIGIN(circ)) {
origin_circuit_t *origin_circ = TO_ORIGIN_CIRCUIT(circ);
+ int removed = 0;
if (conn == origin_circ->p_streams) {
origin_circ->p_streams = conn->next_stream;
- return;
+ removed = 1;
+ } else {
+ for (prevconn = origin_circ->p_streams;
+ prevconn && prevconn->next_stream && prevconn->next_stream != conn;
+ prevconn = prevconn->next_stream)
+ ;
+ if (prevconn && prevconn->next_stream) {
+ prevconn->next_stream = conn->next_stream;
+ removed = 1;
+ }
}
-
- for (prevconn = origin_circ->p_streams;
- prevconn && prevconn->next_stream && prevconn->next_stream != conn;
- prevconn = prevconn->next_stream)
- ;
- if (prevconn && prevconn->next_stream) {
- prevconn->next_stream = conn->next_stream;
+ if (removed) {
+ /* If the stream was removed, and it was a rend stream, decrement the
+ * number of streams on the circuit associated with the rend service.
+ */
+ if (circ->purpose == CIRCUIT_PURPOSE_S_REND_JOINED) {
+ tor_assert(origin_circ->rend_data);
+ origin_circ->rend_data->nr_streams--;
+ }
return;
}
} else {
@@ -2149,7 +2160,7 @@ link_apconn_to_circ(entry_connection_t *apconn, origin_circuit_t *circ,
* that an attempt to connect to a hidden service just
* succeeded. Tell rendclient.c. */
rend_client_note_connection_attempt_ended(
- ENTRY_TO_EDGE_CONN(apconn)->rend_data->onion_address);
+ ENTRY_TO_EDGE_CONN(apconn)->rend_data);
}
if (cpath) { /* we were given one; use it */
diff --git a/src/or/config.c b/src/or/config.c
index fca350c203..ef249a653b 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -225,7 +225,7 @@ static config_var_t option_vars_[] = {
V(DisableDebuggerAttachment, BOOL, "1"),
V(DisableIOCP, BOOL, "1"),
OBSOLETE("DisableV2DirectoryInfo_"),
- V(DynamicDHGroups, BOOL, "0"),
+ OBSOLETE("DynamicDHGroups"),
VPORT(DNSPort, LINELIST, NULL),
V(DNSListenAddress, LINELIST, NULL),
V(DownloadExtraInfo, BOOL, "0"),
@@ -286,6 +286,8 @@ static config_var_t option_vars_[] = {
VAR("HiddenServiceVersion",LINELIST_S, RendConfigLines, NULL),
VAR("HiddenServiceAuthorizeClient",LINELIST_S,RendConfigLines, NULL),
VAR("HiddenServiceAllowUnknownPorts",LINELIST_S, RendConfigLines, NULL),
+ VAR("HiddenServiceMaxStreams",LINELIST_S, RendConfigLines, NULL),
+ VAR("HiddenServiceMaxStreamsCloseCircuit",LINELIST_S, RendConfigLines, NULL),
V(HiddenServiceStatistics, BOOL, "0"),
V(HidServAuth, LINELIST, NULL),
V(CloseHSClientCircuitsImmediatelyOnTimeout, BOOL, "0"),
@@ -298,6 +300,7 @@ static config_var_t option_vars_[] = {
VAR("ServerTransportPlugin", LINELIST, ServerTransportPlugin, NULL),
V(ServerTransportListenAddr, LINELIST, NULL),
V(ServerTransportOptions, LINELIST, NULL),
+ V(SigningKeyLifetime, INTERVAL, "30 days"),
V(Socks4Proxy, STRING, NULL),
V(Socks5Proxy, STRING, NULL),
V(Socks5ProxyUsername, STRING, NULL),
@@ -356,6 +359,13 @@ static config_var_t option_vars_[] = {
V(TestingTorNetwork, BOOL, "0"),
V(TestingMinExitFlagThreshold, MEMUNIT, "0"),
V(TestingMinFastFlagThreshold, MEMUNIT, "0"),
+
+ V(TestingLinkCertLifetime, INTERVAL, "2 days"),
+ V(TestingAuthKeyLifetime, INTERVAL, "2 days"),
+ V(TestingLinkKeySlop, INTERVAL, "3 hours"),
+ V(TestingAuthKeySlop, INTERVAL, "3 hours"),
+ V(TestingSigningKeySlop, INTERVAL, "1 day"),
+
V(OptimisticData, AUTOBOOL, "auto"),
V(PortForwarding, BOOL, "0"),
V(PortForwardingHelper, FILENAME, "tor-fw-helper"),
@@ -545,8 +555,6 @@ static char *get_bindaddr_from_transport_listen_line(const char *line,
static int parse_dir_authority_line(const char *line,
dirinfo_type_t required_type,
int validate_only);
-static int parse_dir_fallback_line(const char *line,
- int validate_only);
static void port_cfg_free(port_cfg_t *port);
static int parse_ports(or_options_t *options, int validate_only,
char **msg_out, int *n_ports_out);
@@ -841,6 +849,41 @@ escaped_safe_str(const char *address)
return escaped(address);
}
+/** List of default directory authorities */
+
+static const char *default_authorities[] = {
+ "moria1 orport=9101 "
+ "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
+ "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
+ "tor26 orport=443 "
+ "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
+ "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D",
+ "dizum orport=443 "
+ "v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 "
+ "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755",
+ "Tonga orport=443 bridge "
+ "82.94.251.203:80 4A0C CD2D DC79 9508 3D73 F5D6 6710 0C8A 5831 F16D",
+ "gabelmoo orport=443 "
+ "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 "
+ "131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281",
+ "dannenberg orport=443 "
+ "v3ident=585769C78764D58426B8B52B6651A5A71137189A "
+ "193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123",
+ "urras orport=80 "
+ "v3ident=80550987E1D626E3EBA5E5E75A458DE0626D088C "
+ "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417",
+ "maatuska orport=80 "
+ "v3ident=49015F787433103580E3B66A1707A00E60F2D15B "
+ "171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810",
+ "Faravahar orport=443 "
+ "v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 "
+ "154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC",
+ "longclaw orport=443 "
+ "v3ident=23D15D965BC35114467363C165C4F724B64B4F66 "
+ "199.254.238.52:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145",
+ NULL
+};
+
/** Add the default directory authorities directly into the trusted dir list,
* but only add them insofar as they share bits with <b>type</b>.
* Each authority's bits are restricted to the bits shared with <b>type</b>.
@@ -849,50 +892,18 @@ static void
add_default_trusted_dir_authorities(dirinfo_type_t type)
{
int i;
- const char *authorities[] = {
- "moria1 orport=9101 "
- "v3ident=D586D18309DED4CD6D57C18FDB97EFA96D330566 "
- "128.31.0.39:9131 9695 DFC3 5FFE B861 329B 9F1A B04C 4639 7020 CE31",
- "tor26 orport=443 "
- "v3ident=14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4 "
- "86.59.21.38:80 847B 1F85 0344 D787 6491 A548 92F9 0493 4E4E B85D",
- "dizum orport=443 "
- "v3ident=E8A9C45EDE6D711294FADF8E7951F4DE6CA56B58 "
- "194.109.206.212:80 7EA6 EAD6 FD83 083C 538F 4403 8BBF A077 587D D755",
- "Tonga orport=443 bridge "
- "82.94.251.203:80 4A0C CD2D DC79 9508 3D73 F5D6 6710 0C8A 5831 F16D",
- "gabelmoo orport=443 "
- "v3ident=ED03BB616EB2F60BEC80151114BB25CEF515B226 "
- "131.188.40.189:80 F204 4413 DAC2 E02E 3D6B CF47 35A1 9BCA 1DE9 7281",
- "dannenberg orport=443 "
- "v3ident=585769C78764D58426B8B52B6651A5A71137189A "
- "193.23.244.244:80 7BE6 83E6 5D48 1413 21C5 ED92 F075 C553 64AC 7123",
- "urras orport=80 "
- "v3ident=80550987E1D626E3EBA5E5E75A458DE0626D088C "
- "208.83.223.34:443 0AD3 FA88 4D18 F89E EA2D 89C0 1937 9E0E 7FD9 4417",
- "maatuska orport=80 "
- "v3ident=49015F787433103580E3B66A1707A00E60F2D15B "
- "171.25.193.9:443 BD6A 8292 55CB 08E6 6FBE 7D37 4836 3586 E46B 3810",
- "Faravahar orport=443 "
- "v3ident=EFCBE720AB3A82B99F9E953CD5BF50F7EEFC7B97 "
- "154.35.175.225:80 CF6D 0AAF B385 BE71 B8E1 11FC 5CFF 4B47 9237 33BC",
- "longclaw orport=443 "
- "v3ident=23D15D965BC35114467363C165C4F724B64B4F66 "
- "199.254.238.52:80 74A9 1064 6BCE EFBC D2E8 74FC 1DC9 9743 0F96 8145",
- NULL
- };
- for (i=0; authorities[i]; i++) {
- if (parse_dir_authority_line(authorities[i], type, 0)<0) {
+ for (i=0; default_authorities[i]; i++) {
+ if (parse_dir_authority_line(default_authorities[i], type, 0)<0) {
log_err(LD_BUG, "Couldn't parse internal DirAuthority line %s",
- authorities[i]);
+ default_authorities[i]);
}
}
}
/** Add the default fallback directory servers into the fallback directory
* server list. */
-static void
-add_default_fallback_dir_servers(void)
+MOCK_IMPL(void,
+add_default_fallback_dir_servers,(void))
{
int i;
const char *fallback[] = {
@@ -961,7 +972,7 @@ validate_dir_servers(or_options_t *options, or_options_t *old_options)
/** Look at all the config options and assign new dir authorities
* as appropriate.
*/
-static int
+int
consider_adding_dir_servers(const or_options_t *options,
const or_options_t *old_options)
{
@@ -979,23 +990,36 @@ consider_adding_dir_servers(const or_options_t *options,
if (!need_to_update)
return 0; /* all done */
+ /* "You cannot set both DirAuthority and Alternate*Authority."
+ * Checking that this restriction holds allows us to simplify
+ * the unit tests. */
+ tor_assert(!(options->DirAuthorities &&
+ (options->AlternateDirAuthority
+ || options->AlternateBridgeAuthority)));
+
/* Start from a clean slate. */
clear_dir_servers();
if (!options->DirAuthorities) {
/* then we may want some of the defaults */
dirinfo_type_t type = NO_DIRINFO;
- if (!options->AlternateBridgeAuthority)
+ if (!options->AlternateBridgeAuthority) {
type |= BRIDGE_DIRINFO;
- if (!options->AlternateDirAuthority)
+ }
+ if (!options->AlternateDirAuthority) {
type |= V3_DIRINFO | EXTRAINFO_DIRINFO | MICRODESC_DIRINFO;
+ /* Only add the default fallback directories when the DirAuthorities,
+ * AlternateDirAuthority, and FallbackDir directory config options
+ * are set to their defaults. */
+ if (!options->FallbackDir) {
+ add_default_fallback_dir_servers();
+ }
+ }
/* if type == NO_DIRINFO, we don't want to add any of the
* default authorities, because we've replaced them all */
if (type != NO_DIRINFO)
add_default_trusted_dir_authorities(type);
}
- if (!options->FallbackDir)
- add_default_fallback_dir_servers();
for (cl = options->DirAuthorities; cl; cl = cl->next)
if (parse_dir_authority_line(cl->value, NO_DIRINFO, 0)<0)
@@ -1318,10 +1342,6 @@ options_transition_requires_fresh_tls_context(const or_options_t *old_options,
if (!old_options)
return 0;
- if ((old_options->DynamicDHGroups != new_options->DynamicDHGroups)) {
- return 1;
- }
-
if (!opt_streq(old_options->TLSECGroup, new_options->TLSECGroup))
return 1;
@@ -1503,24 +1523,6 @@ options_act(const or_options_t *old_options)
finish_daemon(options->DataDirectory);
}
- /* If needed, generate a new TLS DH prime according to the current torrc. */
- if (server_mode(options) && options->DynamicDHGroups) {
- char *keydir = get_datadir_fname("keys");
- if (check_private_dir(keydir, CPD_CREATE, options->User)) {
- tor_free(keydir);
- return -1;
- }
- tor_free(keydir);
-
- if (!old_options || !old_options->DynamicDHGroups) {
- char *fname = get_datadir_fname2("keys", "dynamic_dh_params");
- crypto_set_tls_dh_prime(fname);
- tor_free(fname);
- }
- } else { /* clients don't need a dynamic DH prime. */
- crypto_set_tls_dh_prime(NULL);
- }
-
/* We want to reinit keys as needed before we do much of anything else:
keys are important, and other things can depend on them. */
if (transition_affects_workers ||
@@ -1758,6 +1760,7 @@ options_act(const or_options_t *old_options)
if (!public_server_mode(options)) {
options->CellStatistics = 0;
options->EntryStatistics = 0;
+ options->ConnDirectionStatistics = 0;
options->HiddenServiceStatistics = 0;
options->ExitPortStatistics = 0;
}
@@ -1890,28 +1893,33 @@ options_act(const or_options_t *old_options)
return 0;
}
+typedef enum {
+ TAKES_NO_ARGUMENT = 0,
+ ARGUMENT_NECESSARY = 1,
+ ARGUMENT_OPTIONAL = 2
+} takes_argument_t;
+
static const struct {
const char *name;
- int takes_argument;
+ takes_argument_t takes_argument;
} CMDLINE_ONLY_OPTIONS[] = {
- { "-f", 1 },
- { "--allow-missing-torrc", 0 },
- { "--defaults-torrc", 1 },
- { "--hash-password", 1 },
- { "--dump-config", 1 },
- { "--list-fingerprint", 0 },
- { "--verify-config", 0 },
- { "--ignore-missing-torrc", 0 },
- { "--quiet", 0 },
- { "--hush", 0 },
- { "--version", 0 },
- { "--library-versions", 0 },
- { "-h", 0 },
- { "--help", 0 },
- { "--list-torrc-options", 0 },
- { "--digests", 0 },
- { "--nt-service", 0 },
- { "-nt-service", 0 },
+ { "-f", ARGUMENT_NECESSARY },
+ { "--allow-missing-torrc", TAKES_NO_ARGUMENT },
+ { "--defaults-torrc", ARGUMENT_NECESSARY },
+ { "--hash-password", ARGUMENT_NECESSARY },
+ { "--dump-config", ARGUMENT_OPTIONAL },
+ { "--list-fingerprint", TAKES_NO_ARGUMENT },
+ { "--verify-config", TAKES_NO_ARGUMENT },
+ { "--ignore-missing-torrc", TAKES_NO_ARGUMENT },
+ { "--quiet", TAKES_NO_ARGUMENT },
+ { "--hush", TAKES_NO_ARGUMENT },
+ { "--version", TAKES_NO_ARGUMENT },
+ { "--library-versions", TAKES_NO_ARGUMENT },
+ { "-h", TAKES_NO_ARGUMENT },
+ { "--help", TAKES_NO_ARGUMENT },
+ { "--list-torrc-options", TAKES_NO_ARGUMENT },
+ { "--nt-service", TAKES_NO_ARGUMENT },
+ { "-nt-service", TAKES_NO_ARGUMENT },
{ NULL, 0 },
};
@@ -1938,7 +1946,7 @@ config_parse_commandline(int argc, char **argv, int ignore_errors,
while (i < argc) {
unsigned command = CONFIG_LINE_NORMAL;
- int want_arg = 1;
+ takes_argument_t want_arg = ARGUMENT_NECESSARY;
int is_cmdline = 0;
int j;
@@ -1968,7 +1976,9 @@ config_parse_commandline(int argc, char **argv, int ignore_errors,
want_arg = 0;
}
- if (want_arg && i == argc-1) {
+ const int is_last = (i == argc-1);
+
+ if (want_arg == ARGUMENT_NECESSARY && is_last) {
if (ignore_errors) {
arg = strdup("");
} else {
@@ -1978,8 +1988,11 @@ config_parse_commandline(int argc, char **argv, int ignore_errors,
config_free_lines(front_cmdline);
return -1;
}
+ } else if (want_arg == ARGUMENT_OPTIONAL && is_last) {
+ arg = tor_strdup("");
} else {
- arg = want_arg ? tor_strdup(argv[i+1]) : strdup("");
+ arg = (want_arg != TAKES_NO_ARGUMENT) ? tor_strdup(argv[i+1]) :
+ tor_strdup("");
}
param = tor_malloc_zero(sizeof(config_line_t));
@@ -2566,6 +2579,61 @@ options_validate_cb(void *old_options, void *options, void *default_options,
from_setconf, msg);
}
+#define REJECT(arg) \
+ STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
+#define COMPLAIN(args...) \
+ STMT_BEGIN log_warn(LD_CONFIG, args); STMT_END
+
+/** Log a warning message iff <b>filepath</b> is not absolute.
+ * Warning message must contain option name <b>option</b> and
+ * an absolute path that <b>filepath<b> will resolve to.
+ *
+ * In case <b>filepath</b> is absolute, do nothing.
+ */
+static void
+warn_if_option_path_is_relative(const char *option,
+ char *filepath)
+{
+ if (filepath && path_is_relative(filepath)) {
+ char *abs_path = make_path_absolute(filepath);
+ COMPLAIN("Path for %s (%s) is relative and will resolve to %s."
+ " Is this what you wanted?", option, filepath, abs_path);
+ tor_free(abs_path);
+ }
+}
+
+/** Scan <b>options</b> for occurances of relative file/directory
+ * path and log a warning whenever it is found.
+ */
+static void
+warn_about_relative_paths(or_options_t *options)
+{
+ tor_assert(options);
+
+ warn_if_option_path_is_relative("CookieAuthFile",
+ options->CookieAuthFile);
+ warn_if_option_path_is_relative("ExtORPortCookieAuthFile",
+ options->ExtORPortCookieAuthFile);
+ warn_if_option_path_is_relative("DirPortFrontPage",
+ options->DirPortFrontPage);
+ warn_if_option_path_is_relative("V3BandwidthsFile",
+ options->V3BandwidthsFile);
+ warn_if_option_path_is_relative("ControlPortWriteToFile",
+ options->ControlPortWriteToFile);
+ warn_if_option_path_is_relative("GeoIPFile",options->GeoIPFile);
+ warn_if_option_path_is_relative("GeoIPv6File",options->GeoIPv6File);
+ warn_if_option_path_is_relative("Log",options->DebugLogFile);
+ warn_if_option_path_is_relative("AccelDir",options->AccelDir);
+ warn_if_option_path_is_relative("DataDirectory",options->DataDirectory);
+ warn_if_option_path_is_relative("PidFile",options->PidFile);
+
+ for (config_line_t *hs_line = options->RendConfigLines; hs_line;
+ hs_line = hs_line->next) {
+ if (!strcasecmp(hs_line->key, "HiddenServiceDir"))
+ warn_if_option_path_is_relative("HiddenServiceDir",hs_line->value);
+ }
+}
+
/** Return 0 if every setting in <b>options</b> is reasonable, is a
* permissible transition from <b>old_options</b>, and none of the
* testing-only settings differ from <b>default_options</b> unless in
@@ -2587,13 +2655,12 @@ options_validate(or_options_t *old_options, or_options_t *options,
config_line_t *cl;
const char *uname = get_uname();
int n_ports=0;
-#define REJECT(arg) \
- STMT_BEGIN *msg = tor_strdup(arg); return -1; STMT_END
-#define COMPLAIN(arg) STMT_BEGIN log_warn(LD_CONFIG, arg); STMT_END
tor_assert(msg);
*msg = NULL;
+ warn_about_relative_paths(options);
+
if (server_mode(options) &&
(!strcmpstart(uname, "Windows 95") ||
!strcmpstart(uname, "Windows 98") ||
@@ -2747,6 +2814,9 @@ options_validate(or_options_t *old_options, or_options_t *options,
COMPLAIN("Unrecognized TLSECGroup: Falling back to the default.");
tor_free(options->TLSECGroup);
}
+ if (!evaluate_ecgroup_for_tls(options->TLSECGroup)) {
+ REJECT("Unsupported TLSECGroup.");
+ }
if (options->ExcludeNodes && options->StrictNodes) {
COMPLAIN("You have asked to exclude certain relays from all positions "
@@ -3626,8 +3696,20 @@ options_validate(or_options_t *old_options, or_options_t *options,
CHECK_DEFAULT(TestingDescriptorMaxDownloadTries);
CHECK_DEFAULT(TestingMicrodescMaxDownloadTries);
CHECK_DEFAULT(TestingCertMaxDownloadTries);
+ CHECK_DEFAULT(TestingAuthKeyLifetime);
+ CHECK_DEFAULT(TestingLinkCertLifetime);
+ CHECK_DEFAULT(TestingSigningKeySlop);
+ CHECK_DEFAULT(TestingAuthKeySlop);
+ CHECK_DEFAULT(TestingLinkKeySlop);
#undef CHECK_DEFAULT
+ if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2)
+ REJECT("SigningKeyLifetime is too short.");
+ if (options->TestingLinkCertLifetime < options->TestingAuthKeySlop*2)
+ REJECT("LinkCertLifetime is too short.");
+ if (options->TestingAuthKeyLifetime < options->TestingLinkKeySlop*2)
+ REJECT("TestingAuthKeyLifetime is too short.");
+
if (options->TestingV3AuthInitialVotingInterval
< MIN_VOTE_INTERVAL_TESTING_INITIAL) {
REJECT("TestingV3AuthInitialVotingInterval is insanely low.");
@@ -3755,9 +3837,10 @@ options_validate(or_options_t *old_options, or_options_t *options,
"combination.");
return 0;
+}
+
#undef REJECT
#undef COMPLAIN
-}
/* Given the value that the user has set for MaxMemInQueues, compute the
* actual maximum value. We clip this value if it's too low, and autodetect
@@ -4333,13 +4416,6 @@ options_init_from_torrc(int argc, char **argv)
exit(0);
}
- if (config_line_find(cmdline_only_options, "--digests")) {
- printf("Tor version %s.\n",get_version());
- printf("%s", libor_get_digests());
- printf("%s", tor_get_digests());
- exit(0);
- }
-
if (config_line_find(cmdline_only_options, "--library-versions")) {
printf("Tor version %s. \n", get_version());
printf("Library versions\tCompiled\t\tRuntime\n");
@@ -5458,7 +5534,7 @@ parse_dir_authority_line(const char *line, dirinfo_type_t required_type,
* <b>validate_only</b> is 0, and the line is well-formed, then add the
* dirserver described in the line as a fallback directory. Return 0 on
* success, or -1 if the line isn't well-formed or if we can't add it. */
-static int
+int
parse_dir_fallback_line(const char *line,
int validate_only)
{
@@ -5934,7 +6010,8 @@ parse_port_config(smartlist_t *out,
port = 1;
} else if (!strcmp(addrport, "auto")) {
port = CFG_AUTO_PORT;
- tor_addr_parse(&addr, defaultaddr);
+ int af = tor_addr_parse(&addr, defaultaddr);
+ tor_assert(af >= 0);
} else if (!strcasecmpend(addrport, ":auto")) {
char *addrtmp = tor_strndup(addrport, strlen(addrport)-5);
port = CFG_AUTO_PORT;
@@ -5949,7 +6026,8 @@ parse_port_config(smartlist_t *out,
"9050" might be a valid address. */
port = (int) tor_parse_long(addrport, 10, 0, 65535, &ok, NULL);
if (ok) {
- tor_addr_parse(&addr, defaultaddr);
+ int af = tor_addr_parse(&addr, defaultaddr);
+ tor_assert(af >= 0);
} else if (tor_addr_port_lookup(addrport, &addr, &ptmp) == 0) {
if (ptmp == 0) {
log_warn(LD_CONFIG, "%sPort line has address but no port", portname);
@@ -6695,7 +6773,6 @@ get_num_cpus(const or_options_t *options)
static void
init_libevent(const or_options_t *options)
{
- const char *badness=NULL;
tor_libevent_cfg cfg;
tor_assert(options);
@@ -6716,17 +6793,6 @@ init_libevent(const or_options_t *options)
tor_libevent_initialize(&cfg);
suppress_libevent_log_msg(NULL);
-
- tor_check_libevent_version(tor_libevent_get_method(),
- server_mode(get_options()),
- &badness);
- if (badness) {
- const char *v = tor_libevent_get_version_str();
- const char *m = tor_libevent_get_method();
- control_event_general_status(LOG_WARN,
- "BAD_LIBEVENT VERSION=%s METHOD=%s BADNESS=%s RECOVERED=NO",
- v, m, badness);
- }
}
/** Return a newly allocated string holding a filename relative to the data
@@ -6927,15 +6993,42 @@ getinfo_helper_config(control_connection_t *conn,
smartlist_free(sl);
} else if (!strcmp(question, "config/defaults")) {
smartlist_t *sl = smartlist_new();
- int i;
+ int i, dirauth_lines_seen = 0;
for (i = 0; option_vars_[i].name; ++i) {
const config_var_t *var = &option_vars_[i];
if (var->initvalue != NULL) {
- char *val = esc_for_log(var->initvalue);
- smartlist_add_asprintf(sl, "%s %s\n",var->name,val);
- tor_free(val);
+ if (strcmp(option_vars_[i].name, "DirAuthority") == 0) {
+ /*
+ * Count dirauth lines we have a default for; we'll use the
+ * count later to decide whether to add the defaults manually
+ */
+ ++dirauth_lines_seen;
+ }
+ char *val = esc_for_log(var->initvalue);
+ smartlist_add_asprintf(sl, "%s %s\n",var->name,val);
+ tor_free(val);
+ }
+ }
+
+ if (dirauth_lines_seen == 0) {
+ /*
+ * We didn't see any directory authorities with default values,
+ * so add the list of default authorities manually.
+ */
+ const char **i;
+
+ /*
+ * default_authorities is defined earlier in this file and
+ * is a const char ** NULL-terminated array of dirauth config
+ * lines.
+ */
+ for (i = default_authorities; *i != NULL; ++i) {
+ char *val = esc_for_log(*i);
+ smartlist_add_asprintf(sl, "DirAuthority %s\n", val);
+ tor_free(val);
}
}
+
*answer = smartlist_join_strings(sl, "", 0, NULL);
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
diff --git a/src/or/config.h b/src/or/config.h
index b064f05321..0ee1e1a3c4 100644
--- a/src/or/config.h
+++ b/src/or/config.h
@@ -61,6 +61,10 @@ char *options_get_datadir_fname2_suffix(const or_options_t *options,
* get_datadir_fname2_suffix. */
#define get_datadir_fname2(sub1,sub2) \
get_datadir_fname2_suffix((sub1), (sub2), NULL)
+/** Return a newly allocated string containing datadir/sub1/sub2 relative to
+ * opts. See get_datadir_fname2_suffix. */
+#define options_get_datadir_fname2(opts,sub1,sub2) \
+ options_get_datadir_fname2_suffix((opts),(sub1), (sub2), NULL)
/** Return a newly allocated string containing datadir/sub1suffix. See
* get_datadir_fname2_suffix. */
#define get_datadir_fname_suffix(sub1, suffix) \
@@ -91,7 +95,6 @@ int getinfo_helper_config(control_connection_t *conn,
const char *question, char **answer,
const char **errmsg);
-const char *tor_get_digests(void);
uint32_t get_effective_bwrate(const or_options_t *options);
uint32_t get_effective_bwburst(const or_options_t *options);
@@ -145,6 +148,12 @@ STATIC int options_validate(or_options_t *old_options,
STATIC int parse_transport_line(const or_options_t *options,
const char *line, int validate_only,
int server);
+STATIC int consider_adding_dir_servers(const or_options_t *options,
+ const or_options_t *old_options);
+MOCK_DECL(STATIC void, add_default_fallback_dir_servers, (void));
+STATIC int
+parse_dir_fallback_line(const char *line,
+ int validate_only);
#endif
#endif
diff --git a/src/or/config_codedigest.c b/src/or/config_codedigest.c
deleted file mode 100644
index 86d14bacef..0000000000
--- a/src/or/config_codedigest.c
+++ /dev/null
@@ -1,13 +0,0 @@
-
-const char *tor_get_digests(void);
-
-/** Return a string describing the digest of the source files in src/or/
- */
-const char *
-tor_get_digests(void)
-{
- return ""
-#include "or_sha1.i"
- ;
-}
-
diff --git a/src/or/connection.c b/src/or/connection.c
index 7db0238b3d..24d47ccd18 100644
--- a/src/or/connection.c
+++ b/src/or/connection.c
@@ -586,6 +586,13 @@ connection_free_(connection_t *conn)
control_connection_t *control_conn = TO_CONTROL_CONN(conn);
tor_free(control_conn->safecookie_client_hash);
tor_free(control_conn->incoming_cmd);
+ if (control_conn->ephemeral_onion_services) {
+ SMARTLIST_FOREACH(control_conn->ephemeral_onion_services, char *, cp, {
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+ });
+ smartlist_free(control_conn->ephemeral_onion_services);
+ }
}
/* Probably already freed by connection_free. */
@@ -1407,7 +1414,7 @@ static int
connection_handle_listener_read(connection_t *conn, int new_type)
{
tor_socket_t news; /* the new socket */
- connection_t *newconn;
+ connection_t *newconn = 0;
/* information about the remote peer when connecting to other routers */
struct sockaddr_storage addrbuf;
struct sockaddr *remote = (struct sockaddr*)&addrbuf;
@@ -3774,7 +3781,7 @@ connection_fetch_from_buf_line(connection_t *conn, char *data,
}
}
-/** As fetch_from_buf_http, but fetches from a conncetion's input buffer_t or
+/** As fetch_from_buf_http, but fetches from a connection's input buffer_t or
* its bufferevent as appropriate. */
int
connection_fetch_from_buf_http(connection_t *conn,
@@ -4440,25 +4447,12 @@ alloc_http_authenticator(const char *authenticator)
/* an authenticator in Basic authentication
* is just the string "username:password" */
const size_t authenticator_length = strlen(authenticator);
- /* The base64_encode function needs a minimum buffer length
- * of 66 bytes. */
- const size_t base64_authenticator_length = (authenticator_length/48+1)*66;
+ const size_t base64_authenticator_length =
+ base64_encode_size(authenticator_length, 0) + 1;
char *base64_authenticator = tor_malloc(base64_authenticator_length);
if (base64_encode(base64_authenticator, base64_authenticator_length,
- authenticator, authenticator_length) < 0) {
+ authenticator, authenticator_length, 0) < 0) {
tor_free(base64_authenticator); /* free and set to null */
- } else {
- int i = 0, j = 0;
- ssize_t len = strlen(base64_authenticator);
-
- /* remove all newline occurrences within the string */
- for (i=0; i < len; ++i) {
- if ('\n' != base64_authenticator[i]) {
- base64_authenticator[j] = base64_authenticator[i];
- ++j;
- }
- }
- base64_authenticator[j]='\0';
}
return base64_authenticator;
}
diff --git a/src/or/connection_edge.c b/src/or/connection_edge.c
index 2a1a2f0fd2..c63c350fd8 100644
--- a/src/or/connection_edge.c
+++ b/src/or/connection_edge.c
@@ -102,8 +102,7 @@ connection_mark_unattached_ap_,(entry_connection_t *conn, int endreason,
* but we should fix it someday anyway. */
if ((edge_conn->on_circuit != NULL || edge_conn->edge_has_sent_end) &&
connection_edge_is_rendezvous_stream(edge_conn)) {
- rend_client_note_connection_attempt_ended(
- edge_conn->rend_data->onion_address);
+ rend_client_note_connection_attempt_ended(edge_conn->rend_data);
}
if (base_conn->marked_for_close) {
@@ -1499,61 +1498,76 @@ connection_ap_handshake_rewrite_and_attach(entry_connection_t *conn,
return -1;
}
+ /* Look up if we have client authorization configured for this hidden
+ * service. If we do, associate it with the rend_data. */
+ rend_service_authorization_t *client_auth =
+ rend_client_lookup_service_authorization(socks->address);
+
+ const char *cookie = NULL;
+ rend_auth_type_t auth_type = REND_NO_AUTH;
+ if (client_auth) {
+ log_info(LD_REND, "Using previously configured client authorization "
+ "for hidden service request.");
+ auth_type = client_auth->auth_type;
+ cookie = client_auth->descriptor_cookie;
+ }
+
/* Fill in the rend_data field so we can start doing a connection to
* a hidden service. */
rend_data_t *rend_data = ENTRY_TO_EDGE_CONN(conn)->rend_data =
- tor_malloc_zero(sizeof(rend_data_t));
- strlcpy(rend_data->onion_address, socks->address,
- sizeof(rend_data->onion_address));
+ rend_data_client_create(socks->address, NULL, cookie, auth_type);
+ if (rend_data == NULL) {
+ return -1;
+ }
log_info(LD_REND,"Got a hidden service request for ID '%s'",
safe_str_client(rend_data->onion_address));
- /* see if we already have a hidden service descriptor cached for this
- * address. */
+ /* Lookup the given onion address. If invalid, stop right now else we
+ * might have it in the cache or not, it will be tested later on. */
+ unsigned int refetch_desc = 0;
rend_cache_entry_t *entry = NULL;
const int rend_cache_lookup_result =
rend_cache_lookup_entry(rend_data->onion_address, -1, &entry);
if (rend_cache_lookup_result < 0) {
- /* We should already have rejected this address! */
- log_warn(LD_BUG,"Invalid service name '%s'",
- safe_str_client(rend_data->onion_address));
- connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
- return -1;
+ switch (-rend_cache_lookup_result) {
+ case EINVAL:
+ /* We should already have rejected this address! */
+ log_warn(LD_BUG,"Invalid service name '%s'",
+ safe_str_client(rend_data->onion_address));
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_TORPROTOCOL);
+ return -1;
+ case ENOENT:
+ refetch_desc = 1;
+ break;
+ default:
+ log_warn(LD_BUG, "Unknown cache lookup error %d",
+ rend_cache_lookup_result);
+ return -1;
+ }
}
/* Help predict this next time. We're not sure if it will need
* a stable circuit yet, but we know we'll need *something*. */
rep_hist_note_used_internal(now, 0, 1);
- /* Look up if we have client authorization configured for this hidden
- * service. If we do, associate it with the rend_data. */
- rend_service_authorization_t *client_auth =
- rend_client_lookup_service_authorization(
- rend_data->onion_address);
- if (client_auth) {
- log_info(LD_REND, "Using previously configured client authorization "
- "for hidden service request.");
- memcpy(rend_data->descriptor_cookie,
- client_auth->descriptor_cookie, REND_DESC_COOKIE_LEN);
- rend_data->auth_type = client_auth->auth_type;
- }
-
- /* Now, we either launch an attempt to connect to the hidden service,
- * or we launch an attempt to look up its descriptor, depending on
- * whether we had the descriptor. */
- if (rend_cache_lookup_result == 0) {
+ /* Now we have a descriptor but is it usable or not? If not, refetch.
+ * Also, a fetch could have been requested if the onion address was not
+ * found in the cache previously. */
+ if (refetch_desc || !rend_client_any_intro_points_usable(entry)) {
base_conn->state = AP_CONN_STATE_RENDDESC_WAIT;
log_info(LD_REND, "Unknown descriptor %s. Fetching.",
- safe_str_client(rend_data->onion_address));
+ safe_str_client(rend_data->onion_address));
rend_client_refetch_v2_renddesc(rend_data);
- } else { /* rend_cache_lookup_result > 0 */
- base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
- log_info(LD_REND, "Descriptor is here. Great.");
- if (connection_ap_handshake_attach_circuit(conn) < 0) {
- if (!base_conn->marked_for_close)
- connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
- return -1;
- }
+ return 0;
+ }
+
+ /* We have the descriptor so launch a connection to the HS. */
+ base_conn->state = AP_CONN_STATE_CIRCUIT_WAIT;
+ log_info(LD_REND, "Descriptor is here. Great.");
+ if (connection_ap_handshake_attach_circuit(conn) < 0) {
+ if (!base_conn->marked_for_close)
+ connection_mark_unattached_ap(conn, END_STREAM_REASON_CANT_ATTACH);
+ return -1;
}
return 0;
}
@@ -2846,6 +2860,8 @@ connection_exit_begin_conn(cell_t *cell, circuit_t *circ)
origin_circ->p_streams = n_stream;
assert_circuit_ok(circ);
+ origin_circ->rend_data->nr_streams++;
+
connection_exit_connect(n_stream);
/* For path bias: This circuit was used successfully */
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index e0dff1c915..a967c93aca 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -30,6 +30,7 @@
#include "entrynodes.h"
#include "geoip.h"
#include "main.h"
+#include "link_handshake.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "reasons.h"
@@ -1318,8 +1319,8 @@ connection_or_close_normally(or_connection_t *orconn, int flush)
* the error state.
*/
-void
-connection_or_close_for_error(or_connection_t *orconn, int flush)
+MOCK_IMPL(void,
+connection_or_close_for_error,(or_connection_t *orconn, int flush))
{
channel_t *chan = NULL;
@@ -1879,8 +1880,8 @@ or_handshake_state_free(or_handshake_state_t *state)
return;
crypto_digest_free(state->digest_sent);
crypto_digest_free(state->digest_received);
- tor_cert_free(state->auth_cert);
- tor_cert_free(state->id_cert);
+ tor_x509_cert_free(state->auth_cert);
+ tor_x509_cert_free(state->id_cert);
memwipe(state, 0xBE, sizeof(or_handshake_state_t));
tor_free(state);
}
@@ -2013,9 +2014,9 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn)
* <b>conn</b>'s outbuf. Right now, this <em>DOES NOT</em> support cells that
* affect a circuit.
*/
-void
-connection_or_write_var_cell_to_buf(const var_cell_t *cell,
- or_connection_t *conn)
+MOCK_IMPL(void,
+connection_or_write_var_cell_to_buf,(const var_cell_t *cell,
+ or_connection_t *conn))
{
int n;
char hdr[VAR_CELL_MAX_HEADER_SIZE];
@@ -2158,8 +2159,8 @@ connection_or_send_versions(or_connection_t *conn, int v3_plus)
/** Send a NETINFO cell on <b>conn</b>, telling the other server what we know
* about their address, our address, and the current time. */
-int
-connection_or_send_netinfo(or_connection_t *conn)
+MOCK_IMPL(int,
+connection_or_send_netinfo,(or_connection_t *conn))
{
cell_t cell;
time_t now = time(NULL);
@@ -2228,7 +2229,7 @@ connection_or_send_netinfo(or_connection_t *conn)
int
connection_or_send_certs_cell(or_connection_t *conn)
{
- const tor_cert_t *link_cert = NULL, *id_cert = NULL;
+ const tor_x509_cert_t *link_cert = NULL, *id_cert = NULL;
const uint8_t *link_encoded = NULL, *id_encoded = NULL;
size_t link_len, id_len;
var_cell_t *cell;
@@ -2243,8 +2244,8 @@ connection_or_send_certs_cell(or_connection_t *conn)
server_mode = ! conn->handshake_state->started_here;
if (tor_tls_get_my_certs(server_mode, &link_cert, &id_cert) < 0)
return -1;
- tor_cert_get_der(link_cert, &link_encoded, &link_len);
- tor_cert_get_der(id_cert, &id_encoded, &id_len);
+ tor_x509_cert_get_der(link_cert, &link_encoded, &link_len);
+ tor_x509_cert_get_der(id_cert, &id_encoded, &id_len);
cell_len = 1 /* 1 byte: num certs in cell */ +
2 * ( 1 + 2 ) /* For each cert: 1 byte for type, 2 for length */ +
@@ -2280,28 +2281,37 @@ connection_or_send_certs_cell(or_connection_t *conn)
int
connection_or_send_auth_challenge_cell(or_connection_t *conn)
{
- var_cell_t *cell;
- uint8_t *cp;
- uint8_t challenge[OR_AUTH_CHALLENGE_LEN];
+ var_cell_t *cell = NULL;
+ int r = -1;
tor_assert(conn->base_.state == OR_CONN_STATE_OR_HANDSHAKING_V3);
if (! conn->handshake_state)
return -1;
- if (crypto_rand((char*)challenge, OR_AUTH_CHALLENGE_LEN) < 0)
- return -1;
- cell = var_cell_new(OR_AUTH_CHALLENGE_LEN + 4);
+ auth_challenge_cell_t *ac = auth_challenge_cell_new();
+
+ if (crypto_rand((char*)ac->challenge, sizeof(ac->challenge)) < 0)
+ goto done;
+
+ auth_challenge_cell_add_methods(ac, AUTHTYPE_RSA_SHA256_TLSSECRET);
+ auth_challenge_cell_set_n_methods(ac,
+ auth_challenge_cell_getlen_methods(ac));
+
+ cell = var_cell_new(auth_challenge_cell_encoded_len(ac));
+ ssize_t len = auth_challenge_cell_encode(cell->payload, cell->payload_len,
+ ac);
+ if (len != cell->payload_len)
+ goto done;
cell->command = CELL_AUTH_CHALLENGE;
- memcpy(cell->payload, challenge, OR_AUTH_CHALLENGE_LEN);
- cp = cell->payload + OR_AUTH_CHALLENGE_LEN;
- set_uint16(cp, htons(1)); /* We recognize one authentication type. */
- set_uint16(cp+2, htons(AUTHTYPE_RSA_SHA256_TLSSECRET));
connection_or_write_var_cell_to_buf(cell, conn);
+ r = 0;
+
+ done:
var_cell_free(cell);
- memwipe(challenge, 0, sizeof(challenge));
+ auth_challenge_cell_free(ac);
- return 0;
+ return r;
}
/** Compute the main body of an AUTHENTICATE cell that a client can use
@@ -2328,28 +2338,28 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
crypto_pk_t *signing_key,
int server)
{
- uint8_t *ptr;
+ auth1_t *auth = NULL;
+ auth_ctx_t *ctx = auth_ctx_new();
+ int result;
/* assert state is reasonable XXXX */
- if (outlen < V3_AUTH_FIXED_PART_LEN ||
- (!server && outlen < V3_AUTH_BODY_LEN))
- return -1;
+ ctx->is_ed = 0;
- ptr = out;
+ auth = auth1_new();
/* Type: 8 bytes. */
- memcpy(ptr, "AUTH0001", 8);
- ptr += 8;
+ memcpy(auth1_getarray_type(auth), "AUTH0001", 8);
{
- const tor_cert_t *id_cert=NULL, *link_cert=NULL;
+ const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL;
const digests_t *my_digests, *their_digests;
const uint8_t *my_id, *their_id, *client_id, *server_id;
if (tor_tls_get_my_certs(server, &link_cert, &id_cert))
- return -1;
- my_digests = tor_cert_get_id_digests(id_cert);
- their_digests = tor_cert_get_id_digests(conn->handshake_state->id_cert);
+ goto err;
+ my_digests = tor_x509_cert_get_id_digests(id_cert);
+ their_digests =
+ tor_x509_cert_get_id_digests(conn->handshake_state->id_cert);
tor_assert(my_digests);
tor_assert(their_digests);
my_id = (uint8_t*)my_digests->d[DIGEST_SHA256];
@@ -2359,12 +2369,10 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
server_id = server ? my_id : their_id;
/* Client ID digest: 32 octets. */
- memcpy(ptr, client_id, 32);
- ptr += 32;
+ memcpy(auth->cid, client_id, 32);
/* Server ID digest: 32 octets. */
- memcpy(ptr, server_id, 32);
- ptr += 32;
+ memcpy(auth->sid, server_id, 32);
}
{
@@ -2378,73 +2386,101 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn,
}
/* Server log digest : 32 octets */
- crypto_digest_get_digest(server_d, (char*)ptr, 32);
- ptr += 32;
+ crypto_digest_get_digest(server_d, (char*)auth->slog, 32);
/* Client log digest : 32 octets */
- crypto_digest_get_digest(client_d, (char*)ptr, 32);
- ptr += 32;
+ crypto_digest_get_digest(client_d, (char*)auth->clog, 32);
}
{
/* Digest of cert used on TLS link : 32 octets. */
- const tor_cert_t *cert = NULL;
- tor_cert_t *freecert = NULL;
+ const tor_x509_cert_t *cert = NULL;
+ tor_x509_cert_t *freecert = NULL;
if (server) {
tor_tls_get_my_certs(1, &cert, NULL);
} else {
freecert = tor_tls_get_peer_cert(conn->tls);
cert = freecert;
}
- if (!cert)
- return -1;
- memcpy(ptr, tor_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
+ if (!cert) {
+ log_warn(LD_OR, "Unable to find cert when making AUTH1 data.");
+ goto err;
+ }
+
+ memcpy(auth->scert,
+ tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32);
if (freecert)
- tor_cert_free(freecert);
- ptr += 32;
+ tor_x509_cert_free(freecert);
}
/* HMAC of clientrandom and serverrandom using master key : 32 octets */
- tor_tls_get_tlssecrets(conn->tls, ptr);
- ptr += 32;
-
- tor_assert(ptr - out == V3_AUTH_FIXED_PART_LEN);
-
- if (server)
- return V3_AUTH_FIXED_PART_LEN; // ptr-out
+ tor_tls_get_tlssecrets(conn->tls, auth->tlssecrets);
/* 8 octets were reserved for the current time, but we're trying to get out
* of the habit of sending time around willynilly. Fortunately, nothing
* checks it. That's followed by 16 bytes of nonce. */
- crypto_rand((char*)ptr, 24);
- ptr += 24;
+ crypto_rand((char*)auth->rand, 24);
- tor_assert(ptr - out == V3_AUTH_BODY_LEN);
+ ssize_t len;
+ if ((len = auth1_encode(out, outlen, auth, ctx)) < 0) {
+ log_warn(LD_OR, "Unable to encode signed part of AUTH1 data.");
+ goto err;
+ }
- if (!signing_key)
- return V3_AUTH_BODY_LEN; // ptr - out
+ if (server) {
+ auth1_t *tmp = NULL;
+ ssize_t len2 = auth1_parse(&tmp, out, len, ctx);
+ if (!tmp) {
+ log_warn(LD_OR, "Unable to parse signed part of AUTH1 data.");
+ goto err;
+ }
+ result = (int) (tmp->end_of_fixed_part - out);
+ auth1_free(tmp);
+ if (len2 != len) {
+ log_warn(LD_OR, "Mismatched length when re-parsing AUTH1 data.");
+ goto err;
+ }
+ goto done;
+ }
+
+ if (signing_key) {
+ auth1_setlen_sig(auth, crypto_pk_keysize(signing_key));
- {
- int siglen;
char d[32];
- crypto_digest256(d, (char*)out, ptr-out, DIGEST_SHA256);
- siglen = crypto_pk_private_sign(signing_key,
- (char*)ptr, outlen - (ptr-out),
+ crypto_digest256(d, (char*)out, len, DIGEST_SHA256);
+ int siglen = crypto_pk_private_sign(signing_key,
+ (char*)auth1_getarray_sig(auth),
+ auth1_getlen_sig(auth),
d, 32);
- if (siglen < 0)
- return -1;
+ if (siglen < 0) {
+ log_warn(LD_OR, "Unable to sign AUTH1 data.");
+ goto err;
+ }
+
+ auth1_setlen_sig(auth, siglen);
- ptr += siglen;
- tor_assert(ptr <= out+outlen);
- return (int)(ptr - out);
+ len = auth1_encode(out, outlen, auth, ctx);
+ if (len < 0) {
+ log_warn(LD_OR, "Unable to encode signed AUTH1 data.");
+ goto err;
+ }
}
+ result = (int) len;
+ goto done;
+
+ err:
+ result = -1;
+ done:
+ auth1_free(auth);
+ auth_ctx_free(ctx);
+ return result;
}
/** Send an AUTHENTICATE cell on the connection <b>conn</b>. Return 0 on
* success, -1 on failure */
-int
-connection_or_send_authenticate_cell(or_connection_t *conn, int authtype)
+MOCK_IMPL(int,
+connection_or_send_authenticate_cell,(or_connection_t *conn, int authtype))
{
var_cell_t *cell;
crypto_pk_t *pk = tor_tls_get_my_client_auth_key();
diff --git a/src/or/connection_or.h b/src/or/connection_or.h
index fc261c6bac..3877fd5a13 100644
--- a/src/or/connection_or.h
+++ b/src/or/connection_or.h
@@ -43,7 +43,8 @@ MOCK_DECL(or_connection_t *,
const char *id_digest, channel_tls_t *chan));
void connection_or_close_normally(or_connection_t *orconn, int flush);
-void connection_or_close_for_error(or_connection_t *orconn, int flush);
+MOCK_DECL(void,connection_or_close_for_error,
+ (or_connection_t *orconn, int flush));
void connection_or_report_broken_states(int severity, int domain);
@@ -77,17 +78,18 @@ void or_handshake_state_record_var_cell(or_connection_t *conn,
int connection_or_set_state_open(or_connection_t *conn);
void connection_or_write_cell_to_buf(const cell_t *cell,
or_connection_t *conn);
-void connection_or_write_var_cell_to_buf(const var_cell_t *cell,
- or_connection_t *conn);
+MOCK_DECL(void,connection_or_write_var_cell_to_buf,(const var_cell_t *cell,
+ or_connection_t *conn));
int connection_or_send_versions(or_connection_t *conn, int v3_plus);
-int connection_or_send_netinfo(or_connection_t *conn);
+MOCK_DECL(int,connection_or_send_netinfo,(or_connection_t *conn));
int connection_or_send_certs_cell(or_connection_t *conn);
int connection_or_send_auth_challenge_cell(or_connection_t *conn);
int connection_or_compute_authenticate_cell_body(or_connection_t *conn,
uint8_t *out, size_t outlen,
crypto_pk_t *signing_key,
int server);
-int connection_or_send_authenticate_cell(or_connection_t *conn, int type);
+MOCK_DECL(int,connection_or_send_authenticate_cell,
+ (or_connection_t *conn, int type));
int is_or_protocol_version_known(uint16_t version);
diff --git a/src/or/control.c b/src/or/control.c
index e25c3b2954..7a113f2c1c 100644
--- a/src/or/control.c
+++ b/src/or/control.c
@@ -37,6 +37,9 @@
#include "nodelist.h"
#include "policies.h"
#include "reasons.h"
+#include "rendclient.h"
+#include "rendcommon.h"
+#include "rendservice.h"
#include "rephist.h"
#include "router.h"
#include "routerlist.h"
@@ -72,7 +75,7 @@ static int disable_log_messages = 0;
/** Macro: true if any control connection is interested in events of type
* <b>e</b>. */
#define EVENT_IS_INTERESTING(e) \
- (!! (global_event_mask & (((uint64_t)1)<<(e))))
+ (!! (global_event_mask & EVENT_MASK_(e)))
/** If we're using cookie-type authentication, how long should our cookies be?
*/
@@ -92,6 +95,11 @@ static uint8_t *authentication_cookie = NULL;
"Tor safe cookie authentication controller-to-server hash"
#define SAFECOOKIE_SERVER_NONCE_LEN DIGEST256_LEN
+/** The list of onion services that have been added via ADD_ONION that do not
+ * belong to any particular control connection.
+ */
+static smartlist_t *detached_onion_services = NULL;
+
/** A sufficiently large size to record the last bootstrap phase string. */
#define BOOTSTRAP_MSG_LEN 1024
@@ -157,11 +165,22 @@ static int handle_control_resolve(control_connection_t *conn, uint32_t len,
static int handle_control_usefeature(control_connection_t *conn,
uint32_t len,
const char *body);
+static int handle_control_hsfetch(control_connection_t *conn, uint32_t len,
+ const char *body);
+static int handle_control_hspost(control_connection_t *conn, uint32_t len,
+ const char *body);
+static int handle_control_add_onion(control_connection_t *conn, uint32_t len,
+ const char *body);
+static int handle_control_del_onion(control_connection_t *conn, uint32_t len,
+ const char *body);
static int write_stream_target_to_buf(entry_connection_t *conn, char *buf,
size_t len);
static void orconn_target_get_name(char *buf, size_t len,
or_connection_t *conn);
+static int get_cached_network_liveness(void);
+static void set_cached_network_liveness(int liveness);
+
/** Given a control event code for a message event, return the corresponding
* log severity. */
static INLINE int
@@ -941,6 +960,8 @@ static const struct control_event_t control_event_table[] = {
{ EVENT_CIRC_BANDWIDTH_USED, "CIRC_BW" },
{ EVENT_TRANSPORT_LAUNCHED, "TRANSPORT_LAUNCHED" },
{ EVENT_HS_DESC, "HS_DESC" },
+ { EVENT_HS_DESC_CONTENT, "HS_DESC_CONTENT" },
+ { EVENT_NETWORK_LIVENESS, "NETWORK_LIVENESS" },
{ 0, NULL },
};
@@ -1713,6 +1734,22 @@ getinfo_helper_dir(control_connection_t *control_conn,
*answer = smartlist_join_strings(sl, "", 0, NULL);
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
+ } else if (!strcmpstart(question, "hs/client/desc/id/")) {
+ rend_cache_entry_t *e = NULL;
+
+ question += strlen("hs/client/desc/id/");
+ if (strlen(question) != REND_SERVICE_ID_LEN_BASE32) {
+ *errmsg = "Invalid address";
+ return -1;
+ }
+
+ if (!rend_cache_lookup_entry(question, -1, &e)) {
+ /* Descriptor found in cache */
+ *answer = tor_strdup(e->desc);
+ } else {
+ *errmsg = "Not found in cache";
+ return -1;
+ }
} else if (!strcmpstart(question, "md/id/")) {
const node_t *node = node_get_by_hex_id(question+strlen("md/id/"));
const microdesc_t *md = NULL;
@@ -2100,6 +2137,46 @@ getinfo_helper_events(control_connection_t *control_conn,
return -1;
}
*answer = bridge_stats;
+ } else if (!strcmp(question, "status/fresh-relay-descs")) {
+ if (!server_mode(get_options())) {
+ *errmsg = "Only relays have descriptors";
+ return -1;
+ }
+ routerinfo_t *r;
+ extrainfo_t *e;
+ if (router_build_fresh_descriptor(&r, &e) < 0) {
+ *errmsg = "Error generating descriptor";
+ return -1;
+ }
+ size_t size = r->cache_info.signed_descriptor_len + 1;
+ if (e) {
+ size += e->cache_info.signed_descriptor_len + 1;
+ }
+ tor_assert(r->cache_info.signed_descriptor_len);
+ char *descs = tor_malloc(size);
+ char *cp = descs;
+ memcpy(cp, signed_descriptor_get_body(&r->cache_info),
+ r->cache_info.signed_descriptor_len);
+ cp += r->cache_info.signed_descriptor_len - 1;
+ if (e) {
+ if (cp[0] == '\0') {
+ cp[0] = '\n';
+ } else if (cp[0] != '\n') {
+ cp[1] = '\n';
+ cp++;
+ }
+ memcpy(cp, signed_descriptor_get_body(&e->cache_info),
+ e->cache_info.signed_descriptor_len);
+ cp += e->cache_info.signed_descriptor_len - 1;
+ }
+ if (cp[0] == '\n') {
+ cp[0] = '\0';
+ } else if (cp[0] != '\0') {
+ cp[1] = '\0';
+ }
+ *answer = descs;
+ routerinfo_free(r);
+ extrainfo_free(e);
} else {
return 0;
}
@@ -2107,6 +2184,55 @@ getinfo_helper_events(control_connection_t *control_conn,
return 0;
}
+/** Implementation helper for GETINFO: knows how to enumerate hidden services
+ * created via the control port. */
+static int
+getinfo_helper_onions(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg)
+{
+ smartlist_t *onion_list = NULL;
+
+ if (control_conn && !strcmp(question, "onions/current")) {
+ onion_list = control_conn->ephemeral_onion_services;
+ } else if (!strcmp(question, "onions/detached")) {
+ onion_list = detached_onion_services;
+ } else {
+ return 0;
+ }
+ if (!onion_list || smartlist_len(onion_list) == 0) {
+ if (errmsg) {
+ *errmsg = "No onion services of the specified type.";
+ }
+ return -1;
+ }
+ if (answer) {
+ *answer = smartlist_join_strings(onion_list, "\r\n", 0, NULL);
+ }
+
+ return 0;
+}
+
+/** Implementation helper for GETINFO: answers queries about network
+ * liveness. */
+static int
+getinfo_helper_liveness(control_connection_t *control_conn,
+ const char *question, char **answer,
+ const char **errmsg)
+{
+ (void)control_conn;
+ (void)errmsg;
+ if (strcmp(question, "network-liveness") == 0) {
+ if (get_cached_network_liveness()) {
+ *answer = tor_strdup("up");
+ } else {
+ *answer = tor_strdup("down");
+ }
+ }
+
+ return 0;
+}
+
/** Callback function for GETINFO: on a given control connection, try to
* answer the question <b>q</b> and store the newly-allocated answer in
* *<b>a</b>. If an internal error occurs, return -1 and optionally set
@@ -2176,6 +2302,8 @@ static const getinfo_item_t getinfo_items[] = {
PREFIX("md/id/", dir, "Microdescriptors by ID"),
PREFIX("md/name/", dir, "Microdescriptors by name"),
PREFIX("extra-info/digest/", dir, "Extra-info documents by digest."),
+ PREFIX("hs/client/desc/id", dir,
+ "Hidden Service descriptor in client's cache by onion."),
PREFIX("net/listeners/", listeners, "Bound addresses by type"),
ITEM("ns/all", networkstatus,
"Brief summary of router status (v2 directory format)"),
@@ -2189,6 +2317,8 @@ static const getinfo_item_t getinfo_items[] = {
"Information about and from the ns consensus."),
ITEM("network-status", dir,
"Brief summary of router status (v1 directory format)"),
+ ITEM("network-liveness", liveness,
+ "Current opinion on whether the network is live"),
ITEM("circuit-status", events, "List of current circuits originating here."),
ITEM("stream-status", events,"List of current streams."),
ITEM("orconn-status", events, "A list of current OR connections."),
@@ -2210,6 +2340,8 @@ static const getinfo_item_t getinfo_items[] = {
"The last bootstrap phase status event that Tor sent."),
DOC("status/clients-seen",
"Breakdown of client countries seen by a bridge."),
+ DOC("status/fresh-relay-descs",
+ "A fresh relay/ei descriptor pair for Tor's current state. Not stored."),
DOC("status/version/recommended", "List of currently recommended versions."),
DOC("status/version/current", "Status of the current version."),
DOC("status/version/num-versioning", "Number of versioning authorities."),
@@ -2239,6 +2371,10 @@ static const getinfo_item_t getinfo_items[] = {
ITEM("exit-policy/ipv4", policies, "IPv4 parts of exit policy"),
ITEM("exit-policy/ipv6", policies, "IPv6 parts of exit policy"),
PREFIX("ip-to-country/", geoip, "Perform a GEOIP lookup"),
+ ITEM("onions/current", onions,
+ "Onion services owned by the current control connection."),
+ ITEM("onions/detached", onions,
+ "Onion services detached from the control connection."),
{ NULL, NULL, NULL, 0 }
};
@@ -2733,12 +2869,14 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
uint8_t purpose = ROUTER_PURPOSE_GENERAL;
int cache = 0; /* eventually, we may switch this to 1 */
- char *cp = memchr(body, '\n', len);
+ const char *cp = memchr(body, '\n', len);
smartlist_t *args = smartlist_new();
tor_assert(cp);
- *cp++ = '\0';
+ ++cp;
- smartlist_split_string(args, body, " ",
+ char *cmdline = tor_memdup_nulterm(body, cp-body);
+
+ smartlist_split_string(args, cmdline, " ",
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
SMARTLIST_FOREACH_BEGIN(args, char *, option) {
if (!strcasecmpstart(option, "purpose=")) {
@@ -2787,6 +2925,7 @@ handle_control_postdescriptor(control_connection_t *conn, uint32_t len,
done:
SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
smartlist_free(args);
+ tor_free(cmdline);
return 0;
}
@@ -3102,8 +3241,8 @@ handle_control_authchallenge(control_connection_t *conn, uint32_t len,
tor_free(client_nonce);
return -1;
}
-
- tor_assert(!crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN));
+ const int fail = crypto_rand(server_nonce, SAFECOOKIE_SERVER_NONCE_LEN);
+ tor_assert(!fail);
/* Now compute and send the server-to-controller response, and the
* server's nonce. */
@@ -3211,6 +3350,570 @@ handle_control_dropguards(control_connection_t *conn,
return 0;
}
+/** Implementation for the HSFETCH command. */
+static int
+handle_control_hsfetch(control_connection_t *conn, uint32_t len,
+ const char *body)
+{
+ int i;
+ char digest[DIGEST_LEN], *hsaddress = NULL, *arg1 = NULL, *desc_id = NULL;
+ smartlist_t *args = NULL, *hsdirs = NULL;
+ (void) len; /* body is nul-terminated; it's safe to ignore the length */
+ static const char *hsfetch_command = "HSFETCH";
+ static const char *v2_str = "v2-";
+ const size_t v2_str_len = strlen(v2_str);
+ rend_data_t *rend_query = NULL;
+
+ /* Make sure we have at least one argument, the HSAddress. */
+ args = getargs_helper(hsfetch_command, conn, body, 1, -1);
+ if (!args) {
+ goto exit;
+ }
+
+ /* Extract the first argument (either HSAddress or DescID). */
+ arg1 = smartlist_get(args, 0);
+ /* Test if it's an HS address without the .onion part. */
+ if (rend_valid_service_id(arg1)) {
+ hsaddress = arg1;
+ } else if (strcmpstart(arg1, v2_str) == 0 &&
+ rend_valid_descriptor_id(arg1 + v2_str_len) &&
+ base32_decode(digest, sizeof(digest), arg1 + v2_str_len,
+ REND_DESC_ID_V2_LEN_BASE32) == 0) {
+ /* We have a well formed version 2 descriptor ID. Keep the decoded value
+ * of the id. */
+ desc_id = digest;
+ } else {
+ connection_printf_to_buf(conn, "513 Unrecognized \"%s\"\r\n",
+ arg1);
+ goto done;
+ }
+
+ static const char *opt_server = "SERVER=";
+
+ /* Skip first argument because it's the HSAddress or DescID. */
+ for (i = 1; i < smartlist_len(args); ++i) {
+ const char *arg = smartlist_get(args, i);
+ const node_t *node;
+
+ if (!strcasecmpstart(arg, opt_server)) {
+ const char *server;
+
+ server = arg + strlen(opt_server);
+ node = node_get_by_hex_id(server);
+ if (!node) {
+ connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
+ server);
+ goto done;
+ }
+ if (!hsdirs) {
+ /* Stores routerstatus_t object for each specified server. */
+ hsdirs = smartlist_new();
+ }
+ /* Valid server, add it to our local list. */
+ smartlist_add(hsdirs, node->rs);
+ } else {
+ connection_printf_to_buf(conn, "513 Unexpected argument \"%s\"\r\n",
+ arg);
+ goto done;
+ }
+ }
+
+ rend_query = rend_data_client_create(hsaddress, desc_id, NULL,
+ REND_NO_AUTH);
+ if (rend_query == NULL) {
+ connection_printf_to_buf(conn, "551 Error creating the HS query\r\n");
+ goto done;
+ }
+
+ /* Using a descriptor ID, we force the user to provide at least one
+ * hsdir server using the SERVER= option. */
+ if (desc_id && (!hsdirs || !smartlist_len(hsdirs))) {
+ connection_printf_to_buf(conn, "512 %s option is required\r\n",
+ opt_server);
+ goto done;
+ }
+
+ /* We are about to trigger HSDir fetch so send the OK now because after
+ * that 650 event(s) are possible so better to have the 250 OK before them
+ * to avoid out of order replies. */
+ send_control_done(conn);
+
+ /* Trigger the fetch using the built rend query and possibly a list of HS
+ * directory to use. This function ignores the client cache thus this will
+ * always send a fetch command. */
+ rend_client_fetch_v2_desc(rend_query, hsdirs);
+
+ done:
+ SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+ smartlist_free(args);
+ /* Contains data pointer that we don't own thus no cleanup. */
+ smartlist_free(hsdirs);
+ rend_data_free(rend_query);
+ exit:
+ return 0;
+}
+
+/** Implementation for the HSPOST command. */
+static int
+handle_control_hspost(control_connection_t *conn,
+ uint32_t len,
+ const char *body)
+{
+ static const char *opt_server = "SERVER=";
+ smartlist_t *args = smartlist_new();
+ smartlist_t *hs_dirs = NULL;
+ const char *encoded_desc = body;
+ size_t encoded_desc_len = len;
+
+ char *cp = memchr(body, '\n', len);
+ char *argline = tor_strndup(body, cp-body);
+
+ /* If any SERVER= options were specified, try parse the options line */
+ if (!strcasecmpstart(argline, opt_server)) {
+ /* encoded_desc begins after a newline character */
+ cp = cp + 1;
+ encoded_desc = cp;
+ encoded_desc_len = len-(cp-body);
+
+ smartlist_split_string(args, argline, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ SMARTLIST_FOREACH_BEGIN(args, const char *, arg) {
+ if (!strcasecmpstart(arg, opt_server)) {
+ const char *server = arg + strlen(opt_server);
+ const node_t *node = node_get_by_hex_id(server);
+
+ if (!node || !node->rs) {
+ connection_printf_to_buf(conn, "552 Server \"%s\" not found\r\n",
+ server);
+ goto done;
+ }
+ if (!node->rs->is_hs_dir) {
+ connection_printf_to_buf(conn, "552 Server \"%s\" is not a HSDir"
+ "\r\n", server);
+ goto done;
+ }
+ /* Valid server, add it to our local list. */
+ if (!hs_dirs)
+ hs_dirs = smartlist_new();
+ smartlist_add(hs_dirs, node->rs);
+ } else {
+ connection_printf_to_buf(conn, "512 Unexpected argument \"%s\"\r\n",
+ arg);
+ goto done;
+ }
+ } SMARTLIST_FOREACH_END(arg);
+ }
+
+ /* Read the dot encoded descriptor, and parse it. */
+ rend_encoded_v2_service_descriptor_t *desc =
+ tor_malloc_zero(sizeof(rend_encoded_v2_service_descriptor_t));
+ read_escaped_data(encoded_desc, encoded_desc_len, &desc->desc_str);
+
+ rend_service_descriptor_t *parsed = NULL;
+ char *intro_content = NULL;
+ size_t intro_size;
+ size_t encoded_size;
+ const char *next_desc;
+ if (!rend_parse_v2_service_descriptor(&parsed, desc->desc_id, &intro_content,
+ &intro_size, &encoded_size,
+ &next_desc, desc->desc_str, 1)) {
+ /* Post the descriptor. */
+ char serviceid[REND_SERVICE_ID_LEN_BASE32+1];
+ if (!rend_get_service_id(parsed->pk, serviceid)) {
+ smartlist_t *descs = smartlist_new();
+ smartlist_add(descs, desc);
+
+ /* We are about to trigger HS descriptor upload so send the OK now
+ * because after that 650 event(s) are possible so better to have the
+ * 250 OK before them to avoid out of order replies. */
+ send_control_done(conn);
+
+ /* Trigger the descriptor upload */
+ directory_post_to_hs_dir(parsed, descs, hs_dirs, serviceid, 0);
+ smartlist_free(descs);
+ }
+
+ rend_service_descriptor_free(parsed);
+ } else {
+ connection_printf_to_buf(conn, "554 Invalid descriptor\r\n");
+ }
+
+ tor_free(intro_content);
+ rend_encoded_v2_service_descriptor_free(desc);
+ done:
+ tor_free(argline);
+ smartlist_free(hs_dirs); /* Contents belong to the rend service code. */
+ SMARTLIST_FOREACH(args, char *, arg, tor_free(arg));
+ smartlist_free(args);
+ return 0;
+}
+
+/** Called when we get a ADD_ONION command; parse the body, and set up
+ * the new ephemeral Onion Service. */
+static int
+handle_control_add_onion(control_connection_t *conn,
+ uint32_t len,
+ const char *body)
+{
+ smartlist_t *args;
+ size_t arg_len;
+ (void) len; /* body is nul-terminated; it's safe to ignore the length */
+ args = getargs_helper("ADD_ONION", conn, body, 2, -1);
+ if (!args)
+ return 0;
+ arg_len = smartlist_len(args);
+
+ /* Parse all of the arguments that do not involve handling cryptographic
+ * material first, since there's no reason to touch that at all if any of
+ * the other arguments are malformed.
+ */
+ smartlist_t *port_cfgs = smartlist_new();
+ int discard_pk = 0;
+ int detach = 0;
+ int max_streams = 0;
+ int max_streams_close_circuit = 0;
+ for (size_t i = 1; i < arg_len; i++) {
+ static const char *port_prefix = "Port=";
+ static const char *flags_prefix = "Flags=";
+ static const char *max_s_prefix = "MaxStreams=";
+
+ const char *arg = smartlist_get(args, i);
+ if (!strcasecmpstart(arg, port_prefix)) {
+ /* "Port=VIRTPORT[,TARGET]". */
+ const char *port_str = arg + strlen(port_prefix);
+
+ rend_service_port_config_t *cfg =
+ rend_service_parse_port_config(port_str, ",", NULL);
+ if (!cfg) {
+ connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n");
+ goto out;
+ }
+ smartlist_add(port_cfgs, cfg);
+ } else if (!strcasecmpstart(arg, max_s_prefix)) {
+ /* "MaxStreams=[0..65535]". */
+ const char *max_s_str = arg + strlen(max_s_prefix);
+ int ok = 0;
+ max_streams = (int)tor_parse_long(max_s_str, 10, 0, 65535, &ok, NULL);
+ if (!ok) {
+ connection_printf_to_buf(conn, "512 Invalid MaxStreams\r\n");
+ goto out;
+ }
+ } else if (!strcasecmpstart(arg, flags_prefix)) {
+ /* "Flags=Flag[,Flag]", where Flag can be:
+ * * 'DiscardPK' - If tor generates the keypair, do not include it in
+ * the response.
+ * * 'Detach' - Do not tie this onion service to any particular control
+ * connection.
+ * * 'MaxStreamsCloseCircuit' - Close the circuit if MaxStreams is
+ * exceeded.
+ */
+ static const char *discard_flag = "DiscardPK";
+ static const char *detach_flag = "Detach";
+ static const char *max_s_close_flag = "MaxStreamsCloseCircuit";
+
+ smartlist_t *flags = smartlist_new();
+ int bad = 0;
+
+ smartlist_split_string(flags, arg + strlen(flags_prefix), ",",
+ SPLIT_IGNORE_BLANK, 0);
+ if (smartlist_len(flags) < 1) {
+ connection_printf_to_buf(conn, "512 Invalid 'Flags' argument\r\n");
+ bad = 1;
+ }
+ SMARTLIST_FOREACH_BEGIN(flags, const char *, flag)
+ {
+ if (!strcasecmp(flag, discard_flag)) {
+ discard_pk = 1;
+ } else if (!strcasecmp(flag, detach_flag)) {
+ detach = 1;
+ } else if (!strcasecmp(flag, max_s_close_flag)) {
+ max_streams_close_circuit = 1;
+ } else {
+ connection_printf_to_buf(conn,
+ "512 Invalid 'Flags' argument: %s\r\n",
+ escaped(flag));
+ bad = 1;
+ break;
+ }
+ } SMARTLIST_FOREACH_END(flag);
+ SMARTLIST_FOREACH(flags, char *, cp, tor_free(cp));
+ smartlist_free(flags);
+ if (bad)
+ goto out;
+ } else {
+ connection_printf_to_buf(conn, "513 Invalid argument\r\n");
+ goto out;
+ }
+ }
+ if (smartlist_len(port_cfgs) == 0) {
+ connection_printf_to_buf(conn, "512 Missing 'Port' argument\r\n");
+ goto out;
+ }
+
+ /* Parse the "keytype:keyblob" argument. */
+ crypto_pk_t *pk = NULL;
+ const char *key_new_alg = NULL;
+ char *key_new_blob = NULL;
+ char *err_msg = NULL;
+
+ pk = add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk,
+ &key_new_alg, &key_new_blob,
+ &err_msg);
+ if (!pk) {
+ if (err_msg) {
+ connection_write_str_to_buf(err_msg, conn);
+ tor_free(err_msg);
+ }
+ goto out;
+ }
+ tor_assert(!err_msg);
+
+ /* Create the HS, using private key pk, and port config port_cfg.
+ * rend_service_add_ephemeral() will take ownership of pk and port_cfg,
+ * regardless of success/failure.
+ */
+ char *service_id = NULL;
+ int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams,
+ max_streams_close_circuit,
+ &service_id);
+ port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */
+ switch (ret) {
+ case RSAE_OKAY:
+ {
+ char *buf = NULL;
+ tor_assert(service_id);
+ if (key_new_alg) {
+ tor_assert(key_new_blob);
+ tor_asprintf(&buf,
+ "250-ServiceID=%s\r\n"
+ "250-PrivateKey=%s:%s\r\n"
+ "250 OK\r\n",
+ service_id,
+ key_new_alg,
+ key_new_blob);
+ } else {
+ tor_asprintf(&buf,
+ "250-ServiceID=%s\r\n"
+ "250 OK\r\n",
+ service_id);
+ }
+ if (detach) {
+ if (!detached_onion_services)
+ detached_onion_services = smartlist_new();
+ smartlist_add(detached_onion_services, service_id);
+ } else {
+ if (!conn->ephemeral_onion_services)
+ conn->ephemeral_onion_services = smartlist_new();
+ smartlist_add(conn->ephemeral_onion_services, service_id);
+ }
+
+ connection_write_str_to_buf(buf, conn);
+ memwipe(buf, 0, strlen(buf));
+ tor_free(buf);
+ break;
+ }
+ case RSAE_BADPRIVKEY:
+ connection_printf_to_buf(conn, "551 Failed to generate onion address\r\n");
+ break;
+ case RSAE_ADDREXISTS:
+ connection_printf_to_buf(conn, "550 Onion address collision\r\n");
+ break;
+ case RSAE_BADVIRTPORT:
+ connection_printf_to_buf(conn, "512 Invalid VIRTPORT/TARGET\r\n");
+ break;
+ case RSAE_INTERNAL: /* FALLSTHROUGH */
+ default:
+ connection_printf_to_buf(conn, "551 Failed to add Onion Service\r\n");
+ }
+ if (key_new_blob) {
+ memwipe(key_new_blob, 0, strlen(key_new_blob));
+ tor_free(key_new_blob);
+ }
+
+ out:
+ if (port_cfgs) {
+ SMARTLIST_FOREACH(port_cfgs, rend_service_port_config_t*, p,
+ rend_service_port_config_free(p));
+ smartlist_free(port_cfgs);
+ }
+
+ SMARTLIST_FOREACH(args, char *, cp, {
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+ });
+ smartlist_free(args);
+ return 0;
+}
+
+/** Helper function to handle parsing the KeyType:KeyBlob argument to the
+ * ADD_ONION command. Return a new crypto_pk_t and if a new key was generated
+ * and the private key not discarded, the algorithm and serialized private key,
+ * or NULL and an optional control protocol error message on failure. The
+ * caller is responsible for freeing the returned key_new_blob and err_msg.
+ *
+ * Note: The error messages returned are deliberately vague to avoid echoing
+ * key material.
+ */
+STATIC crypto_pk_t *
+add_onion_helper_keyarg(const char *arg, int discard_pk,
+ const char **key_new_alg_out, char **key_new_blob_out,
+ char **err_msg_out)
+{
+ smartlist_t *key_args = smartlist_new();
+ crypto_pk_t *pk = NULL;
+ const char *key_new_alg = NULL;
+ char *key_new_blob = NULL;
+ char *err_msg = NULL;
+ int ok = 0;
+
+ smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0);
+ if (smartlist_len(key_args) != 2) {
+ err_msg = tor_strdup("512 Invalid key type/blob\r\n");
+ goto err;
+ }
+
+ /* The format is "KeyType:KeyBlob". */
+ static const char *key_type_new = "NEW";
+ static const char *key_type_best = "BEST";
+ static const char *key_type_rsa1024 = "RSA1024";
+
+ const char *key_type = smartlist_get(key_args, 0);
+ const char *key_blob = smartlist_get(key_args, 1);
+
+ if (!strcasecmp(key_type_rsa1024, key_type)) {
+ /* "RSA:<Base64 Blob>" - Loading a pre-existing RSA1024 key. */
+ pk = crypto_pk_base64_decode(key_blob, strlen(key_blob));
+ if (!pk) {
+ err_msg = tor_strdup("512 Failed to decode RSA key\r\n");
+ goto err;
+ }
+ if (crypto_pk_num_bits(pk) != PK_BYTES*8) {
+ err_msg = tor_strdup("512 Invalid RSA key size\r\n");
+ goto err;
+ }
+ } else if (!strcasecmp(key_type_new, key_type)) {
+ /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */
+ if (!strcasecmp(key_type_rsa1024, key_blob) ||
+ !strcasecmp(key_type_best, key_blob)) {
+ /* "RSA1024", RSA 1024 bit, also currently "BEST" by default. */
+ pk = crypto_pk_new();
+ if (crypto_pk_generate_key(pk)) {
+ tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n",
+ key_type_rsa1024);
+ goto err;
+ }
+ if (!discard_pk) {
+ if (crypto_pk_base64_encode(pk, &key_new_blob)) {
+ tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n",
+ key_type_rsa1024);
+ goto err;
+ }
+ key_new_alg = key_type_rsa1024;
+ }
+ } else {
+ err_msg = tor_strdup("513 Invalid key type\r\n");
+ goto err;
+ }
+ } else {
+ err_msg = tor_strdup("513 Invalid key type\r\n");
+ goto err;
+ }
+
+ /* Succeded in loading or generating a private key. */
+ tor_assert(pk);
+ ok = 1;
+
+ err:
+ SMARTLIST_FOREACH(key_args, char *, cp, {
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+ });
+ smartlist_free(key_args);
+
+ if (!ok) {
+ crypto_pk_free(pk);
+ pk = NULL;
+ }
+ if (err_msg_out) {
+ *err_msg_out = err_msg;
+ } else {
+ tor_free(err_msg);
+ }
+ *key_new_alg_out = key_new_alg;
+ *key_new_blob_out = key_new_blob;
+
+ return pk;
+}
+
+/** Called when we get a DEL_ONION command; parse the body, and remove
+ * the existing ephemeral Onion Service. */
+static int
+handle_control_del_onion(control_connection_t *conn,
+ uint32_t len,
+ const char *body)
+{
+ smartlist_t *args;
+ (void) len; /* body is nul-terminated; it's safe to ignore the length */
+ args = getargs_helper("DEL_ONION", conn, body, 1, 1);
+ if (!args)
+ return 0;
+
+ const char *service_id = smartlist_get(args, 0);
+ if (!rend_valid_service_id(service_id)) {
+ connection_printf_to_buf(conn, "512 Malformed Onion Service id\r\n");
+ goto out;
+ }
+
+ /* Determine if the onion service belongs to this particular control
+ * connection, or if it is in the global list of detached services. If it
+ * is in neither, either the service ID is invalid in some way, or it
+ * explicitly belongs to a different control connection, and an error
+ * should be returned.
+ */
+ smartlist_t *services[2] = {
+ conn->ephemeral_onion_services,
+ detached_onion_services
+ };
+ smartlist_t *onion_services = NULL;
+ int idx = -1;
+ for (size_t i = 0; i < ARRAY_LENGTH(services); i++) {
+ idx = smartlist_string_pos(services[i], service_id);
+ if (idx != -1) {
+ onion_services = services[i];
+ break;
+ }
+ }
+ if (onion_services == NULL) {
+ connection_printf_to_buf(conn, "552 Unknown Onion Service id\r\n");
+ } else {
+ int ret = rend_service_del_ephemeral(service_id);
+ if (ret) {
+ /* This should *NEVER* fail, since the service is on either the
+ * per-control connection list, or the global one.
+ */
+ log_warn(LD_BUG, "Failed to remove Onion Service %s.",
+ escaped(service_id));
+ tor_fragile_assert();
+ }
+
+ /* Remove/scrub the service_id from the appropriate list. */
+ char *cp = smartlist_get(onion_services, idx);
+ smartlist_del(onion_services, idx);
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+
+ send_control_done(conn);
+ }
+
+ out:
+ SMARTLIST_FOREACH(args, char *, cp, {
+ memwipe(cp, 0, strlen(cp));
+ tor_free(cp);
+ });
+ smartlist_free(args);
+ return 0;
+}
+
/** Called when <b>conn</b> has no more bytes left on its outbuf. */
int
connection_control_finished_flushing(control_connection_t *conn)
@@ -3257,6 +3960,15 @@ connection_control_closed(control_connection_t *conn)
conn->event_mask = 0;
control_update_global_event_mask();
+ /* Close all ephemeral Onion Services if any.
+ * The list and it's contents are scrubbed/freed in connection_free_.
+ */
+ if (conn->ephemeral_onion_services) {
+ SMARTLIST_FOREACH(conn->ephemeral_onion_services, char *, cp, {
+ rend_service_del_ephemeral(cp);
+ });
+ }
+
if (conn->is_owning_control_connection) {
lost_owning_controller("connection", "closed");
}
@@ -3508,6 +4220,22 @@ connection_control_process_inbuf(control_connection_t *conn)
} else if (!strcasecmp(conn->incoming_cmd, "DROPGUARDS")) {
if (handle_control_dropguards(conn, cmd_data_len, args))
return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "HSFETCH")) {
+ if (handle_control_hsfetch(conn, cmd_data_len, args))
+ return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "+HSPOST")) {
+ if (handle_control_hspost(conn, cmd_data_len, args))
+ return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "ADD_ONION")) {
+ int ret = handle_control_add_onion(conn, cmd_data_len, args);
+ memwipe(args, 0, cmd_data_len); /* Scrub the private key. */
+ if (ret)
+ return -1;
+ } else if (!strcasecmp(conn->incoming_cmd, "DEL_ONION")) {
+ int ret = handle_control_del_onion(conn, cmd_data_len, args);
+ memwipe(args, 0, cmd_data_len); /* Scrub the service id/pk. */
+ if (ret)
+ return -1;
} else {
connection_printf_to_buf(conn, "510 Unrecognized command \"%s\"\r\n",
conn->incoming_cmd);
@@ -4415,6 +5143,52 @@ control_event_or_authdir_new_descriptor(const char *action,
return 0;
}
+/** Cached liveness for network liveness events and GETINFO
+ */
+
+static int network_is_live = 0;
+
+static int
+get_cached_network_liveness(void)
+{
+ return network_is_live;
+}
+
+static void
+set_cached_network_liveness(int liveness)
+{
+ network_is_live = liveness;
+}
+
+/** The network liveness has changed; this is called from circuitstats.c
+ * whenever we receive a cell, or when timeout expires and we assume the
+ * network is down. */
+int
+control_event_network_liveness_update(int liveness)
+{
+ if (liveness > 0) {
+ if (get_cached_network_liveness() <= 0) {
+ /* Update cached liveness */
+ set_cached_network_liveness(1);
+ log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS UP");
+ send_control_event_string(EVENT_NETWORK_LIVENESS, ALL_FORMATS,
+ "650 NETWORK_LIVENESS UP\r\n");
+ }
+ /* else was already live, no-op */
+ } else {
+ if (get_cached_network_liveness() > 0) {
+ /* Update cached liveness */
+ set_cached_network_liveness(0);
+ log_debug(LD_CONTROL, "Sending NETWORK_LIVENESS DOWN");
+ send_control_event_string(EVENT_NETWORK_LIVENESS, ALL_FORMATS,
+ "650 NETWORK_LIVENESS DOWN\r\n");
+ }
+ /* else was already dead, no-op */
+ }
+
+ return 0;
+}
+
/** Helper function for NS-style events. Constructs and sends an event
* of type <b>event</b> with string <b>event_string</b> out of the set of
* networkstatuses <b>statuses</b>. Currently it is used for NS events
@@ -5176,6 +5950,29 @@ node_describe_longname_by_id,(const char *id_digest))
return longname;
}
+/** Return either the onion address if the given pointer is a non empty
+ * string else the unknown string. */
+static const char *
+rend_hsaddress_str_or_unknown(const char *onion_address)
+{
+ static const char *str_unknown = "UNKNOWN";
+ const char *str_ret = str_unknown;
+
+ /* No valid pointer, unknown it is. */
+ if (!onion_address) {
+ goto end;
+ }
+ /* Empty onion address thus we don't know, unknown it is. */
+ if (onion_address[0] == '\0') {
+ goto end;
+ }
+ /* All checks are good so return the given onion address. */
+ str_ret = onion_address;
+
+ end:
+ return str_ret;
+}
+
/** send HS_DESC requested event.
*
* <b>rend_query</b> is used to fetch requested onion address and auth type.
@@ -5196,12 +5993,75 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query,
send_control_event(EVENT_HS_DESC, ALL_FORMATS,
"650 HS_DESC REQUESTED %s %s %s %s\r\n",
- rend_query->onion_address,
+ rend_hsaddress_str_or_unknown(rend_query->onion_address),
rend_auth_type_to_string(rend_query->auth_type),
node_describe_longname_by_id(id_digest),
desc_id_base32);
}
+/** For an HS descriptor query <b>rend_data</b>, using the
+ * <b>onion_address</b> and HSDir fingerprint <b>hsdir_fp</b>, find out
+ * which descriptor ID in the query is the right one.
+ *
+ * Return a pointer of the binary descriptor ID found in the query's object
+ * or NULL if not found. */
+static const char *
+get_desc_id_from_query(const rend_data_t *rend_data, const char *hsdir_fp)
+{
+ int replica;
+ const char *desc_id = NULL;
+
+ /* Possible if the fetch was done using a descriptor ID. This means that
+ * the HSFETCH command was used. */
+ if (!tor_digest_is_zero(rend_data->desc_id_fetch)) {
+ desc_id = rend_data->desc_id_fetch;
+ goto end;
+ }
+
+ /* OK, we have an onion address so now let's find which descriptor ID
+ * is the one associated with the HSDir fingerprint. */
+ for (replica = 0; replica < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
+ replica++) {
+ const char *digest = rend_data->descriptor_id[replica];
+
+ SMARTLIST_FOREACH_BEGIN(rend_data->hsdirs_fp, char *, fingerprint) {
+ if (tor_memcmp(fingerprint, hsdir_fp, DIGEST_LEN) == 0) {
+ /* Found it! This descriptor ID is the right one. */
+ desc_id = digest;
+ goto end;
+ }
+ } SMARTLIST_FOREACH_END(fingerprint);
+ }
+
+ end:
+ return desc_id;
+}
+
+/** send HS_DESC upload event.
+ *
+ * <b>service_id</b> is the descriptor onion address.
+ * <b>hs_dir</b> is the description of contacting hs directory.
+ * <b>desc_id_base32</b> is the ID of requested hs descriptor.
+ */
+void
+control_event_hs_descriptor_upload(const char *service_id,
+ const char *id_digest,
+ const char *desc_id_base32)
+{
+ if (!service_id || !id_digest || !desc_id_base32) {
+ log_warn(LD_BUG, "Called with service_digest==%p, "
+ "desc_id_base32==%p, id_digest==%p", service_id,
+ desc_id_base32, id_digest);
+ return;
+ }
+
+ send_control_event(EVENT_HS_DESC, ALL_FORMATS,
+ "650 HS_DESC UPLOAD %s UNKNOWN %s %s\r\n",
+ service_id,
+ node_describe_longname_by_id(id_digest),
+ desc_id_base32);
+}
+
/** send HS_DESC event after got response from hs directory.
*
* NOTE: this is an internal function used by following functions:
@@ -5212,27 +6072,77 @@ control_event_hs_descriptor_requested(const rend_data_t *rend_query,
*/
void
control_event_hs_descriptor_receive_end(const char *action,
- const rend_data_t *rend_query,
+ const char *onion_address,
+ const rend_data_t *rend_data,
const char *id_digest,
const char *reason)
{
+ char *desc_id_field = NULL;
char *reason_field = NULL;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ const char *desc_id = NULL;
- if (!action || !rend_query || !id_digest) {
- log_warn(LD_BUG, "Called with action==%p, rend_query==%p, "
- "id_digest==%p", action, rend_query, id_digest);
+ if (!action || !id_digest || !rend_data || !onion_address) {
+ log_warn(LD_BUG, "Called with action==%p, id_digest==%p, "
+ "rend_data==%p, onion_address==%p", action, id_digest,
+ rend_data, onion_address);
return;
}
+ desc_id = get_desc_id_from_query(rend_data, id_digest);
+ if (desc_id != NULL) {
+ /* Set the descriptor ID digest to base32 so we can send it. */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
+ /* Extra whitespace is needed before the value. */
+ tor_asprintf(&desc_id_field, " %s", desc_id_base32);
+ }
+
if (reason) {
tor_asprintf(&reason_field, " REASON=%s", reason);
}
send_control_event(EVENT_HS_DESC, ALL_FORMATS,
- "650 HS_DESC %s %s %s %s%s\r\n",
+ "650 HS_DESC %s %s %s %s%s%s\r\n",
+ action,
+ rend_hsaddress_str_or_unknown(onion_address),
+ rend_auth_type_to_string(rend_data->auth_type),
+ node_describe_longname_by_id(id_digest),
+ desc_id_field ? desc_id_field : "",
+ reason_field ? reason_field : "");
+
+ tor_free(desc_id_field);
+ tor_free(reason_field);
+}
+
+/** send HS_DESC event after got response from hs directory.
+ *
+ * NOTE: this is an internal function used by following functions:
+ * control_event_hs_descriptor_uploaded
+ * control_event_hs_descriptor_upload_failed
+ *
+ * So do not call this function directly.
+ */
+void
+control_event_hs_descriptor_upload_end(const char *action,
+ const char *id_digest,
+ const char *reason)
+{
+ char *reason_field = NULL;
+
+ if (!action || !id_digest) {
+ log_warn(LD_BUG, "Called with action==%p, id_digest==%p", action,
+ id_digest);
+ return;
+ }
+
+ if (reason) {
+ tor_asprintf(&reason_field, " REASON=%s", reason);
+ }
+
+ send_control_event(EVENT_HS_DESC, ALL_FORMATS,
+ "650 HS_DESC %s UNKNOWN UNKNOWN %s%s\r\n",
action,
- rend_query->onion_address,
- rend_auth_type_to_string(rend_query->auth_type),
node_describe_longname_by_id(id_digest),
reason_field ? reason_field : "");
@@ -5241,19 +6151,35 @@ control_event_hs_descriptor_receive_end(const char *action,
/** send HS_DESC RECEIVED event
*
- * called when a we successfully received a hidden service descriptor.
+ * called when we successfully received a hidden service descriptor.
*/
void
-control_event_hs_descriptor_received(const rend_data_t *rend_query,
+control_event_hs_descriptor_received(const char *onion_address,
+ const rend_data_t *rend_data,
const char *id_digest)
{
- if (!rend_query || !id_digest) {
- log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p",
- rend_query, id_digest);
+ if (!rend_data || !id_digest || !onion_address) {
+ log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p, "
+ "onion_address==%p", rend_data, id_digest, onion_address);
return;
}
- control_event_hs_descriptor_receive_end("RECEIVED", rend_query,
- id_digest, NULL);
+ control_event_hs_descriptor_receive_end("RECEIVED", onion_address,
+ rend_data, id_digest, NULL);
+}
+
+/** send HS_DESC UPLOADED event
+ *
+ * called when we successfully uploaded a hidden service descriptor.
+ */
+void
+control_event_hs_descriptor_uploaded(const char *id_digest)
+{
+ if (!id_digest) {
+ log_warn(LD_BUG, "Called with id_digest==%p",
+ id_digest);
+ return;
+ }
+ control_event_hs_descriptor_upload_end("UPLOADED", id_digest, NULL);
}
/** Send HS_DESC event to inform controller that query <b>rend_query</b>
@@ -5262,17 +6188,68 @@ control_event_hs_descriptor_received(const rend_data_t *rend_query,
* field.
*/
void
-control_event_hs_descriptor_failed(const rend_data_t *rend_query,
+control_event_hs_descriptor_failed(const rend_data_t *rend_data,
const char *id_digest,
const char *reason)
{
- if (!rend_query || !id_digest) {
- log_warn(LD_BUG, "Called with rend_query==%p, id_digest==%p",
- rend_query, id_digest);
+ if (!rend_data || !id_digest) {
+ log_warn(LD_BUG, "Called with rend_data==%p, id_digest==%p",
+ rend_data, id_digest);
return;
}
- control_event_hs_descriptor_receive_end("FAILED", rend_query,
- id_digest, reason);
+ control_event_hs_descriptor_receive_end("FAILED",
+ rend_data->onion_address,
+ rend_data, id_digest, reason);
+}
+
+/** send HS_DESC_CONTENT event after completion of a successful fetch from
+ * hs directory. */
+void
+control_event_hs_descriptor_content(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_id_digest,
+ const char *content)
+{
+ static const char *event_name = "HS_DESC_CONTENT";
+ char *esc_content = NULL;
+
+ if (!onion_address || !desc_id || !hsdir_id_digest) {
+ log_warn(LD_BUG, "Called with onion_address==%p, desc_id==%p, "
+ "hsdir_id_digest==%p", onion_address, desc_id, hsdir_id_digest);
+ return;
+ }
+
+ if (content == NULL) {
+ /* Point it to empty content so it can still be escaped. */
+ content = "";
+ }
+ write_escaped_data(content, strlen(content), &esc_content);
+
+ send_control_event(EVENT_HS_DESC_CONTENT, ALL_FORMATS,
+ "650+%s %s %s %s\r\n%s650 OK\r\n",
+ event_name,
+ rend_hsaddress_str_or_unknown(onion_address),
+ desc_id,
+ node_describe_longname_by_id(hsdir_id_digest),
+ esc_content);
+ tor_free(esc_content);
+}
+
+/** Send HS_DESC event to inform controller upload of hidden service
+ * descriptor identified by <b>id_digest</b> failed. If <b>reason</b>
+ * is not NULL, add it to REASON= field.
+ */
+void
+control_event_hs_descriptor_upload_failed(const char *id_digest,
+ const char *reason)
+{
+ if (!id_digest) {
+ log_warn(LD_BUG, "Called with id_digest==%p",
+ id_digest);
+ return;
+ }
+ control_event_hs_descriptor_upload_end("UPLOAD_FAILED",
+ id_digest, reason);
}
/** Free any leftover allocated memory of the control.c subsystem. */
@@ -5281,6 +6258,10 @@ control_free_all(void)
{
if (authentication_cookie) /* Free the auth cookie */
tor_free(authentication_cookie);
+ if (detached_onion_services) { /* Free the detached onion services */
+ SMARTLIST_FOREACH(detached_onion_services, char *, cp, tor_free(cp));
+ smartlist_free(detached_onion_services);
+ }
}
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/control.h b/src/or/control.h
index 47a601817a..2d02443834 100644
--- a/src/or/control.h
+++ b/src/or/control.h
@@ -67,6 +67,7 @@ int control_event_or_authdir_new_descriptor(const char *action,
size_t desclen,
const char *msg);
int control_event_my_descriptor_changed(void);
+int control_event_network_liveness_update(int liveness);
int control_event_networkstatus_changed(smartlist_t *statuses);
int control_event_newconsensus(const networkstatus_t *consensus);
@@ -106,15 +107,30 @@ MOCK_DECL(const char *, node_describe_longname_by_id,(const char *id_digest));
void control_event_hs_descriptor_requested(const rend_data_t *rend_query,
const char *desc_id_base32,
const char *hs_dir);
+void control_event_hs_descriptor_upload(const char *service_id,
+ const char *desc_id_base32,
+ const char *hs_dir);
void control_event_hs_descriptor_receive_end(const char *action,
- const rend_data_t *rend_query,
- const char *hs_dir,
- const char *reason);
-void control_event_hs_descriptor_received(const rend_data_t *rend_query,
- const char *hs_dir);
-void control_event_hs_descriptor_failed(const rend_data_t *rend_query,
- const char *hs_dir,
+ const char *onion_address,
+ const rend_data_t *rend_data,
+ const char *id_digest,
+ const char *reason);
+void control_event_hs_descriptor_upload_end(const char *action,
+ const char *hs_dir,
+ const char *reason);
+void control_event_hs_descriptor_received(const char *onion_address,
+ const rend_data_t *rend_data,
+ const char *id_digest);
+void control_event_hs_descriptor_uploaded(const char *hs_dir);
+void control_event_hs_descriptor_failed(const rend_data_t *rend_data,
+ const char *id_digest,
const char *reason);
+void control_event_hs_descriptor_upload_failed(const char *hs_dir,
+ const char *reason);
+void control_event_hs_descriptor_content(const char *onion_address,
+ const char *desc_id,
+ const char *hsdir_fp,
+ const char *content);
void control_free_all(void);
@@ -123,6 +139,7 @@ void control_free_all(void);
* because it is used both as a list of v0 event types, and as indices
* into the bitfield to determine which controllers want which events.
*/
+/* This bitfield has no event zero 0x0000 */
#define EVENT_MIN_ 0x0001
#define EVENT_CIRCUIT_STATUS 0x0001
#define EVENT_STREAM_STATUS 0x0002
@@ -157,10 +174,32 @@ void control_free_all(void);
#define EVENT_CIRC_BANDWIDTH_USED 0x001D
#define EVENT_TRANSPORT_LAUNCHED 0x0020
#define EVENT_HS_DESC 0x0021
-#define EVENT_MAX_ 0x0021
-/* If EVENT_MAX_ ever hits 0x003F, we need to make the mask into a
+#define EVENT_HS_DESC_CONTENT 0x0022
+#define EVENT_NETWORK_LIVENESS 0x0023
+#define EVENT_MAX_ 0x0023
+
+/* sizeof(control_connection_t.event_mask) in bits, currently a uint64_t */
+#define EVENT_CAPACITY_ 0x0040
+
+/* If EVENT_MAX_ ever hits 0x0040, we need to make the mask into a
* different structure, as it can only handle a maximum left shift of 1<<63. */
+#if EVENT_MAX_ >= EVENT_CAPACITY_
+#error control_connection_t.event_mask has an event greater than its capacity
+#endif
+
+#define EVENT_MASK_(e) (((uint64_t)1)<<(e))
+
+#define EVENT_MASK_NONE_ ((uint64_t)0x0)
+
+#define EVENT_MASK_ABOVE_MIN_ ((~((uint64_t)0x0)) << EVENT_MIN_)
+#define EVENT_MASK_BELOW_MAX_ ((~((uint64_t)0x0)) \
+ >> (EVENT_CAPACITY_ - EVENT_MAX_ \
+ - EVENT_MIN_))
+
+#define EVENT_MASK_ALL_ (EVENT_MASK_ABOVE_MIN_ \
+ & EVENT_MASK_BELOW_MAX_)
+
/* Used only by control.c and test.c */
STATIC size_t write_escaped_data(const char *data, size_t len, char **out);
STATIC size_t read_escaped_data(const char *data, size_t len, char **out);
@@ -204,6 +243,11 @@ void append_cell_stats_by_command(smartlist_t *event_parts,
void format_cell_stats(char **event_string, circuit_t *circ,
cell_stats_t *cell_stats);
STATIC char *get_bw_samples(void);
+
+STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk,
+ const char **key_new_alg_out,
+ char **key_new_blob_out,
+ char **err_msg_out);
#endif
#endif
diff --git a/src/or/dircollate.c b/src/or/dircollate.c
new file mode 100644
index 0000000000..331e0587b0
--- /dev/null
+++ b/src/or/dircollate.c
@@ -0,0 +1,260 @@
+/* Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dircollate.c
+ *
+ * \brief Collation code for figuring out which identities to vote for in
+ * the directory voting process.
+ */
+
+#define DIRCOLLATE_PRIVATE
+#include "dircollate.h"
+#include "dirvote.h"
+
+static void dircollator_collate_by_rsa(dircollator_t *dc);
+static void dircollator_collate_by_ed25519(dircollator_t *dc);
+
+typedef struct ddmap_entry_s {
+ HT_ENTRY(ddmap_entry_s) node;
+ uint8_t d[DIGEST_LEN + DIGEST256_LEN];
+ vote_routerstatus_t *vrs_lst[FLEXIBLE_ARRAY_MEMBER];
+} ddmap_entry_t;
+
+struct double_digest_map_s *by_both_ids;
+
+static void
+ddmap_entry_free(ddmap_entry_t *e)
+{
+ tor_free(e);
+}
+
+static ddmap_entry_t *
+ddmap_entry_new(int n_votes)
+{
+ return tor_malloc_zero(STRUCT_OFFSET(ddmap_entry_t, vrs_lst) +
+ sizeof(vote_routerstatus_t *) * n_votes);
+}
+
+static unsigned
+ddmap_entry_hash(const ddmap_entry_t *ent)
+{
+ return (unsigned) siphash24g(ent->d, sizeof(ent->d));
+}
+
+static unsigned
+ddmap_entry_eq(const ddmap_entry_t *a, const ddmap_entry_t *b)
+{
+ return fast_memeq(a->d, b->d, sizeof(a->d));
+}
+
+static void
+ddmap_entry_set_digests(ddmap_entry_t *ent,
+ const uint8_t *rsa_sha1,
+ const uint8_t *ed25519)
+{
+ memcpy(ent->d, rsa_sha1, DIGEST_LEN);
+ memcpy(ent->d + DIGEST_LEN, ed25519, DIGEST256_LEN);
+}
+
+HT_PROTOTYPE(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash,
+ ddmap_entry_eq);
+HT_GENERATE2(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash,
+ ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_);
+static void
+dircollator_add_routerstatus(dircollator_t *dc,
+ int vote_num,
+ networkstatus_t *vote,
+ vote_routerstatus_t *vrs)
+{
+ const char *id = vrs->status.identity_digest;
+
+ (void) vote;
+ vote_routerstatus_t **vrs_lst = digestmap_get(dc->by_rsa_sha1, id);
+ if (NULL == vrs_lst) {
+ vrs_lst = tor_calloc(sizeof(vote_routerstatus_t *), dc->n_votes);
+ digestmap_set(dc->by_rsa_sha1, id, vrs_lst);
+ }
+ tor_assert(vrs_lst[vote_num] == NULL);
+ vrs_lst[vote_num] = vrs;
+
+ const uint8_t *ed = vrs->ed25519_id;
+
+ if (tor_mem_is_zero((char*)ed, DIGEST256_LEN))
+ return;
+
+ ddmap_entry_t search, *found;
+ memset(&search, 0, sizeof(search));
+ ddmap_entry_set_digests(&search, (const uint8_t *)id, ed);
+ found = HT_FIND(double_digest_map, &dc->by_both_ids, &search);
+ if (NULL == found) {
+ found = ddmap_entry_new(dc->n_votes);
+ ddmap_entry_set_digests(found, (const uint8_t *)id, ed);
+ HT_INSERT(double_digest_map, &dc->by_both_ids, found);
+ }
+ vrs_lst = found->vrs_lst;
+ tor_assert(vrs_lst[vote_num] == NULL);
+ vrs_lst[vote_num] = vrs;
+}
+
+dircollator_t *
+dircollator_new(int n_votes, int n_authorities)
+{
+ dircollator_t *dc = tor_malloc_zero(sizeof(dircollator_t));
+
+ tor_assert(n_votes <= n_authorities);
+
+ dc->n_votes = n_votes;
+ dc->n_authorities = n_authorities;
+
+ dc->by_rsa_sha1 = digestmap_new();
+ HT_INIT(double_digest_map, &dc->by_both_ids);
+
+ return dc;
+}
+
+void
+dircollator_free(dircollator_t *dc)
+{
+ if (!dc)
+ return;
+
+ if (dc->by_collated_rsa_sha1 != dc->by_rsa_sha1)
+ digestmap_free(dc->by_collated_rsa_sha1, NULL);
+
+ digestmap_free(dc->by_rsa_sha1, tor_free_);
+ smartlist_free(dc->all_rsa_sha1_lst);
+
+ ddmap_entry_t **e, **next, *this;
+ for (e = HT_START(double_digest_map, &dc->by_both_ids);
+ e != NULL; e = next) {
+ this = *e;
+ next = HT_NEXT_RMV(double_digest_map, &dc->by_both_ids, e);
+ ddmap_entry_free(this);
+ }
+ HT_CLEAR(double_digest_map, &dc->by_both_ids);
+
+ tor_free(dc);
+}
+
+void
+dircollator_add_vote(dircollator_t *dc, networkstatus_t *v)
+{
+ tor_assert(v->type == NS_TYPE_VOTE);
+ tor_assert(dc->next_vote_num < dc->n_votes);
+ tor_assert(!dc->is_collated);
+
+ const int votenum = dc->next_vote_num++;
+
+ SMARTLIST_FOREACH_BEGIN(v->routerstatus_list, vote_routerstatus_t *, vrs) {
+ dircollator_add_routerstatus(dc, votenum, v, vrs);
+ } SMARTLIST_FOREACH_END(vrs);
+}
+
+void
+dircollator_collate(dircollator_t *dc, int consensus_method)
+{
+ tor_assert(!dc->is_collated);
+ dc->all_rsa_sha1_lst = smartlist_new();
+
+ if (consensus_method < MIN_METHOD_FOR_ED25519_ID_VOTING + 10/*XXX*/)
+ dircollator_collate_by_rsa(dc);
+ else
+ dircollator_collate_by_ed25519(dc);
+
+ smartlist_sort_digests(dc->all_rsa_sha1_lst);
+ dc->is_collated = 1;
+}
+
+static void
+dircollator_collate_by_rsa(dircollator_t *dc)
+{
+ const int total_authorities = dc->n_authorities;
+
+ DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) {
+ int n = 0, i;
+ for (i = 0; i < dc->n_votes; ++i) {
+ if (vrs_lst[i] != NULL)
+ ++n;
+ }
+
+ if (n <= total_authorities / 2)
+ continue;
+
+ smartlist_add(dc->all_rsa_sha1_lst, (char *)k);
+ } DIGESTMAP_FOREACH_END;
+
+ dc->by_collated_rsa_sha1 = dc->by_rsa_sha1;
+}
+
+static void
+dircollator_collate_by_ed25519(dircollator_t *dc)
+{
+ const int total_authorities = dc->n_authorities;
+ digestmap_t *rsa_digests = digestmap_new();
+
+ ddmap_entry_t **iter;
+
+ HT_FOREACH(iter, double_digest_map, &dc->by_both_ids) {
+ ddmap_entry_t *ent = *iter;
+ int n = 0, i;
+ for (i = 0; i < dc->n_votes; ++i) {
+ if (ent->vrs_lst[i] != NULL)
+ ++n;
+ }
+
+ if (n <= total_authorities / 2)
+ continue;
+
+ vote_routerstatus_t **vrs_lst2 = digestmap_get(dc->by_rsa_sha1,
+ (char*)ent->d);
+ tor_assert(vrs_lst2);
+
+ for (i = 0; i < dc->n_votes; ++i) {
+ if (ent->vrs_lst[i] != NULL) {
+ ent->vrs_lst[i]->ed25519_reflects_consensus = 1;
+ } else if (vrs_lst2[i] && ! vrs_lst2[i]->has_ed25519_listing) {
+ ent->vrs_lst[i] = vrs_lst2[i];
+ }
+ }
+
+ digestmap_set(rsa_digests, (char*)ent->d, ent->vrs_lst);
+ smartlist_add(dc->all_rsa_sha1_lst, ent->d);
+ }
+
+ DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) {
+ if (digestmap_get(rsa_digests, k) != NULL)
+ continue;
+
+ int n = 0, i;
+ for (i = 0; i < dc->n_votes; ++i) {
+ if (vrs_lst[i] != NULL)
+ ++n;
+ }
+
+ if (n <= total_authorities / 2)
+ continue;
+
+ digestmap_set(rsa_digests, k, vrs_lst);
+ smartlist_add(dc->all_rsa_sha1_lst, (char *)k);
+ } DIGESTMAP_FOREACH_END;
+
+ dc->by_collated_rsa_sha1 = rsa_digests;
+}
+
+int
+dircollator_n_routers(dircollator_t *dc)
+{
+ return smartlist_len(dc->all_rsa_sha1_lst);
+}
+
+vote_routerstatus_t **
+dircollator_get_votes_for_router(dircollator_t *dc, int idx)
+{
+ tor_assert(idx < smartlist_len(dc->all_rsa_sha1_lst));
+ return digestmap_get(dc->by_collated_rsa_sha1,
+ smartlist_get(dc->all_rsa_sha1_lst, idx));
+}
+
diff --git a/src/or/dircollate.h b/src/or/dircollate.h
new file mode 100644
index 0000000000..cd1e8ac96d
--- /dev/null
+++ b/src/or/dircollate.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file dirvote.h
+ * \brief Header file for dirvote.c.
+ **/
+
+#ifndef TOR_DIRCOLLATE_H
+#define TOR_DIRCOLLATE_H
+
+#include "testsupport.h"
+#include "or.h"
+
+typedef struct dircollator_s dircollator_t;
+
+dircollator_t *dircollator_new(int n_votes, int n_authorities);
+void dircollator_free(dircollator_t *obj);
+void dircollator_add_vote(dircollator_t *dc, networkstatus_t *v);
+
+void dircollator_collate(dircollator_t *dc, int consensus_method);
+
+int dircollator_n_routers(dircollator_t *dc);
+vote_routerstatus_t **dircollator_get_votes_for_router(dircollator_t *dc,
+ int idx);
+
+#ifdef DIRCOLLATE_PRIVATE
+struct ddmap_entry_s;
+typedef HT_HEAD(double_digest_map, ddmap_entry_s) double_digest_map_t;
+struct dircollator_s {
+ /**DOCDOC */
+ int is_collated;
+ int n_votes;
+ int n_authorities;
+
+ int next_vote_num;
+ digestmap_t *by_rsa_sha1;
+ struct double_digest_map by_both_ids;
+
+ digestmap_t *by_collated_rsa_sha1;
+
+ smartlist_t *all_rsa_sha1_lst;
+};
+#endif
+
+#endif
+
diff --git a/src/or/directory.c b/src/or/directory.c
index d2b6b86f6d..549d95a13c 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -2102,14 +2102,23 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
control_event_hs_descriptor_failed(conn->rend_data, \
conn->identity_digest, \
reason) )
+ #define SEND_HS_DESC_FAILED_CONTENT() ( \
+ control_event_hs_descriptor_content(conn->rend_data->onion_address, \
+ conn->requested_resource, \
+ conn->identity_digest, \
+ NULL) )
tor_assert(conn->rend_data);
log_info(LD_REND,"Received rendezvous descriptor (size %d, status %d "
"(%s))",
(int)body_len, status_code, escaped(reason));
switch (status_code) {
case 200:
+ {
+ rend_cache_entry_t *entry = NULL;
+
switch (rend_cache_store_v2_desc_as_client(body,
- conn->requested_resource, conn->rend_data)) {
+ conn->requested_resource, conn->rend_data,
+ &entry)) {
case RCS_BADDESC:
case RCS_NOTDIR: /* Impossible */
log_warn(LD_REND,"Fetching v2 rendezvous descriptor failed. "
@@ -2117,25 +2126,41 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
/* We'll retry when connection_about_to_close_connection()
* cleans this dir conn up. */
SEND_HS_DESC_FAILED_EVENT("BAD_DESC");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
case RCS_OKAY:
default:
+ {
+ char service_id[REND_SERVICE_ID_LEN_BASE32 + 1];
+ /* Should never be NULL here for an OKAY returned code. */
+ tor_assert(entry);
+ rend_get_service_id(entry->parsed->pk, service_id);
+
/* success. notify pending connections about this. */
log_info(LD_REND, "Successfully fetched v2 rendezvous "
"descriptor.");
- control_event_hs_descriptor_received(conn->rend_data,
+ control_event_hs_descriptor_received(service_id,
+ conn->rend_data,
conn->identity_digest);
+ control_event_hs_descriptor_content(service_id,
+ conn->requested_resource,
+ conn->identity_digest,
+ body);
conn->base_.purpose = DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2;
- rend_client_desc_trynow(conn->rend_data->onion_address);
+ rend_client_desc_trynow(service_id);
+ memwipe(service_id, 0, sizeof(service_id));
break;
+ }
}
break;
+ }
case 404:
/* Not there. We'll retry when
* connection_about_to_close_connection() cleans this conn up. */
log_info(LD_REND,"Fetching v2 rendezvous descriptor failed: "
"Retrying at another directory.");
SEND_HS_DESC_FAILED_EVENT("NOT_FOUND");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
case 400:
log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
@@ -2143,6 +2168,7 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
"v2 rendezvous query? Retrying at another directory.",
escaped(reason));
SEND_HS_DESC_FAILED_EVENT("QUERY_REJECTED");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
default:
log_warn(LD_REND, "Fetching v2 rendezvous descriptor failed: "
@@ -2152,11 +2178,15 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
status_code, escaped(reason), conn->base_.address,
conn->base_.port);
SEND_HS_DESC_FAILED_EVENT("UNEXPECTED");
+ SEND_HS_DESC_FAILED_CONTENT();
break;
}
}
if (conn->base_.purpose == DIR_PURPOSE_UPLOAD_RENDDESC_V2) {
+ #define SEND_HS_DESC_UPLOAD_FAILED_EVENT(reason) ( \
+ control_event_hs_descriptor_upload_failed(conn->identity_digest, \
+ reason) )
log_info(LD_REND,"Uploaded rendezvous descriptor (status %d "
"(%s))",
status_code, escaped(reason));
@@ -2165,17 +2195,20 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
log_info(LD_REND,
"Uploading rendezvous descriptor: finished with status "
"200 (%s)", escaped(reason));
+ control_event_hs_descriptor_uploaded(conn->identity_digest);
break;
case 400:
log_warn(LD_REND,"http status 400 (%s) response from dirserver "
"'%s:%d'. Malformed rendezvous descriptor?",
escaped(reason), conn->base_.address, conn->base_.port);
+ SEND_HS_DESC_UPLOAD_FAILED_EVENT("UPLOAD_REJECTED");
break;
default:
log_warn(LD_REND,"http status %d (%s) response unexpected (server "
"'%s:%d').",
status_code, escaped(reason), conn->base_.address,
conn->base_.port);
+ SEND_HS_DESC_UPLOAD_FAILED_EVENT("UNEXPECTED");
break;
}
}
@@ -3066,7 +3099,7 @@ directory_handle_command_get(dir_connection_t *conn, const char *headers,
/* Handle v2 rendezvous descriptor fetch request. */
const char *descp;
const char *query = url + strlen("/tor/rendezvous2/");
- if (strlen(query) == REND_DESC_ID_V2_LEN_BASE32) {
+ if (rend_valid_descriptor_id(query)) {
log_info(LD_REND, "Got a v2 rendezvous descriptor request for ID '%s'",
safe_str(escaped(query)));
switch (rend_cache_lookup_v2_desc_as_dir(query, &descp)) {
@@ -3446,6 +3479,9 @@ find_dl_schedule_and_len(download_status_t *dls, int server)
default:
tor_assert(0);
}
+
+ /* Impossible, but gcc will fail with -Werror without a `return`. */
+ return NULL;
}
/** Called when an attempt to download <b>dls</b> has failed with HTTP status
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index a024be8342..bee67cf749 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -18,6 +18,7 @@
#include "dirserv.h"
#include "dirvote.h"
#include "hibernate.h"
+#include "keypin.h"
#include "microdesc.h"
#include "networkstatus.h"
#include "nodelist.h"
@@ -27,6 +28,7 @@
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
+#include "torcert.h"
/**
* \file dirserv.c
@@ -225,6 +227,16 @@ dirserv_load_fingerprint_file(void)
return 0;
}
+/* If this is set, then we don't allow routers that have advertised an Ed25519
+ * identity to stop doing so. This is going to be essential for good identity
+ * security: otherwise anybody who can attack RSA-1024 but not Ed25519 could
+ * just sign fake descriptors missing the Ed25519 key. But we won't actually
+ * be able to prevent that kind of thing until we're confident that there
+ * isn't actually a legit reason to downgrade to 0.2.5. So for now, we have
+ * to leave this #undef.
+ */
+#undef DISABLE_DISABLING_ED25519
+
/** Check whether <b>router</b> has a nickname/identity key combination that
* we recognize from the fingerprint list, or an IP we automatically act on
* according to our configuration. Return the appropriate router status.
@@ -243,6 +255,36 @@ dirserv_router_get_status(const routerinfo_t *router, const char **msg)
return FP_REJECT;
}
+ if (router->signing_key_cert) {
+ /* This has an ed25519 identity key. */
+ if (KEYPIN_MISMATCH ==
+ keypin_check((const uint8_t*)router->cache_info.identity_digest,
+ router->signing_key_cert->signing_key.pubkey)) {
+ if (msg) {
+ *msg = "Ed25519 identity key or RSA identity key has changed.";
+ }
+ log_warn(LD_DIR, "Router %s uploaded a descriptor with a Ed25519 key "
+ "but the <rsa,ed25519> keys don't match what they were before.",
+ router_describe(router));
+ return FP_REJECT;
+ }
+ } else {
+ /* No ed25519 key */
+ if (KEYPIN_MISMATCH == keypin_check_lone_rsa(
+ (const uint8_t*)router->cache_info.identity_digest)) {
+ log_warn(LD_DIR, "Router %s uploaded a descriptor with no Ed25519 key, "
+ "when we previously knew an Ed25519 for it. Ignoring for now, "
+ "since Tor 0.2.6 is under development.",
+ router_describe(router));
+#ifdef DISABLE_DISABLING_ED25519
+ if (msg) {
+ *msg = "Ed25519 identity key has disappeared.";
+ }
+ return FP_REJECT;
+#endif
+ }
+ }
+
return dirserv_get_status_impl(d, router->nickname,
router->addr, router->or_port,
router->platform, msg, 1);
@@ -578,6 +620,28 @@ dirserv_add_descriptor(routerinfo_t *ri, const char **msg, const char *source)
return ROUTER_IS_ALREADY_KNOWN;
}
+ /* Do keypinning again ... this time, to add the pin if appropriate */
+ int keypin_status;
+ if (ri->signing_key_cert) {
+ keypin_status = keypin_check_and_add(
+ (const uint8_t*)ri->cache_info.identity_digest,
+ ri->signing_key_cert->signing_key.pubkey);
+ } else {
+ keypin_status = keypin_check_lone_rsa(
+ (const uint8_t*)ri->cache_info.identity_digest);
+#ifndef DISABLE_DISABLING_ED25519
+ if (keypin_status == KEYPIN_MISMATCH)
+ keypin_status = KEYPIN_NOT_FOUND;
+#endif
+ }
+ if (keypin_status == KEYPIN_MISMATCH) {
+ log_info(LD_DIRSERV, "Dropping descriptor from %s (source: %s) because "
+ "its key did not match an older RSA/Ed25519 keypair",
+ router_describe(ri), source);
+ *msg = "Looks like your keypair does not match its older value.";
+ return ROUTER_AUTHDIR_REJECTS;
+ }
+
/* Make a copy of desc, since router_add_to_routerlist might free
* ri and its associated signed_descriptor_t. */
desc = tor_strndup(ri->cache_info.signed_descriptor_body, desclen);
@@ -1929,6 +1993,16 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version,
smartlist_add_asprintf(chunks, "p %s\n", summary);
tor_free(summary);
}
+
+ if (format == NS_V3_VOTE && vrs) {
+ if (tor_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) {
+ smartlist_add(chunks, tor_strdup("id ed25519 none\n"));
+ } else {
+ char ed_b64[BASE64_DIGEST256_LEN+1];
+ digest256_to_base64(ed_b64, (const char*)vrs->ed25519_id);
+ smartlist_add_asprintf(chunks, "id ed25519 %s\n", ed_b64);
+ }
+ }
}
done:
@@ -2751,6 +2825,11 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key,
listbadexits,
vote_on_hsdirs);
+ if (ri->signing_key_cert) {
+ memcpy(vrs->ed25519_id, ri->signing_key_cert->signing_key.pubkey,
+ ED25519_PUBKEY_LEN);
+ }
+
if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest))
clear_status_flags_on_sybil(rs);
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 7a5154dae5..0f3b77fe28 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -6,6 +6,7 @@
#define DIRVOTE_PRIVATE
#include "or.h"
#include "config.h"
+#include "dircollate.h"
#include "directory.h"
#include "dirserv.h"
#include "dirvote.h"
@@ -17,6 +18,7 @@
#include "routerlist.h"
#include "routerparse.h"
#include "entrynodes.h" /* needed for guardfraction methods */
+#include "torcert.h"
/**
* \file dirvote.c
@@ -1138,8 +1140,10 @@ networkstatus_compute_consensus(smartlist_t *votes,
char *params = NULL;
char *packages = NULL;
int added_weights = 0;
+ dircollator_t *collator = NULL;
tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC);
tor_assert(total_authorities >= smartlist_len(votes));
+ tor_assert(total_authorities > 0);
flavor_name = networkstatus_get_flavor_name(flavor);
@@ -1493,12 +1497,24 @@ networkstatus_compute_consensus(smartlist_t *votes,
}
);
+ /* Populate the collator */
+ collator = dircollator_new(smartlist_len(votes), total_authorities);
+ SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
+ dircollator_add_vote(collator, v);
+ } SMARTLIST_FOREACH_END(v);
+
+ dircollator_collate(collator, consensus_method);
+
/* Now go through all the votes */
flag_counts = tor_calloc(smartlist_len(flags), sizeof(int));
- while (1) {
+ const int num_routers = dircollator_n_routers(collator);
+ for (i = 0; i < num_routers; ++i) {
+ vote_routerstatus_t **vrs_lst =
+ dircollator_get_votes_for_router(collator, i);
+
vote_routerstatus_t *rs;
routerstatus_t rs_out;
- const char *lowest_id = NULL;
+ const char *current_rsa_id = NULL;
const char *chosen_version;
const char *chosen_name = NULL;
int exitsummary_disagreement = 0;
@@ -1506,23 +1522,9 @@ networkstatus_compute_consensus(smartlist_t *votes,
int is_guard = 0, is_exit = 0, is_bad_exit = 0;
int naming_conflict = 0;
int n_listing = 0;
- int i;
char microdesc_digest[DIGEST256_LEN];
tor_addr_port_t alt_orport = {TOR_ADDR_NULL, 0};
- /* Of the next-to-be-considered digest in each voter, which is first? */
- SMARTLIST_FOREACH(votes, networkstatus_t *, v, {
- if (index[v_sl_idx] < size[v_sl_idx]) {
- rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]);
- if (!lowest_id ||
- fast_memcmp(rs->status.identity_digest,
- lowest_id, DIGEST_LEN) < 0)
- lowest_id = rs->status.identity_digest;
- }
- });
- if (!lowest_id) /* we're out of routers. */
- break;
-
memset(flag_counts, 0, sizeof(int)*smartlist_len(flags));
smartlist_clear(matching_descs);
smartlist_clear(chosen_flags);
@@ -1532,29 +1534,25 @@ networkstatus_compute_consensus(smartlist_t *votes,
num_guardfraction_inputs = 0;
/* Okay, go through all the entries for this digest. */
- SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
- if (index[v_sl_idx] >= size[v_sl_idx])
- continue; /* out of entries. */
- rs = smartlist_get(v->routerstatus_list, index[v_sl_idx]);
- if (fast_memcmp(rs->status.identity_digest, lowest_id, DIGEST_LEN))
- continue; /* doesn't include this router. */
- /* At this point, we know that we're looking at a routerstatus with
- * identity "lowest".
- */
- ++index[v_sl_idx];
+ for (int voter_idx = 0; voter_idx < smartlist_len(votes); ++voter_idx) {
+ if (vrs_lst[voter_idx] == NULL)
+ continue; /* This voter had nothing to say about this entry. */
+ rs = vrs_lst[voter_idx];
++n_listing;
+ current_rsa_id = rs->status.identity_digest;
+
smartlist_add(matching_descs, rs);
if (rs->version && rs->version[0])
smartlist_add(versions, rs->version);
/* Tally up all the flags. */
- for (i = 0; i < n_voter_flags[v_sl_idx]; ++i) {
- if (rs->flags & (U64_LITERAL(1) << i))
- ++flag_counts[flag_map[v_sl_idx][i]];
+ for (int flag = 0; flag < n_voter_flags[voter_idx]; ++flag) {
+ if (rs->flags & (U64_LITERAL(1) << flag))
+ ++flag_counts[flag_map[voter_idx][flag]];
}
- if (named_flag[v_sl_idx] >= 0 &&
- (rs->flags & (U64_LITERAL(1) << named_flag[v_sl_idx]))) {
+ if (named_flag[voter_idx] >= 0 &&
+ (rs->flags & (U64_LITERAL(1) << named_flag[voter_idx]))) {
if (chosen_name && strcmp(chosen_name, rs->status.nickname)) {
log_notice(LD_DIR, "Conflict on naming for router: %s vs %s",
chosen_name, rs->status.nickname);
@@ -1575,13 +1573,17 @@ networkstatus_compute_consensus(smartlist_t *votes,
if (rs->status.has_bandwidth)
bandwidths_kb[num_bandwidths++] = rs->status.bandwidth_kb;
- } SMARTLIST_FOREACH_END(v);
+ }
/* We don't include this router at all unless more than half of
* the authorities we believe in list it. */
if (n_listing <= total_authorities/2)
continue;
+ /* The clangalyzer can't figure out that this will never be NULL
+ * if n_listing is at least 1 */
+ tor_assert(current_rsa_id);
+
/* Figure out the most popular opinion of what the most recent
* routerinfo and its contents are. */
memset(microdesc_digest, 0, sizeof(microdesc_digest));
@@ -1589,8 +1591,9 @@ networkstatus_compute_consensus(smartlist_t *votes,
microdesc_digest, &alt_orport);
/* Copy bits of that into rs_out. */
memset(&rs_out, 0, sizeof(rs_out));
- tor_assert(fast_memeq(lowest_id, rs->status.identity_digest,DIGEST_LEN));
- memcpy(rs_out.identity_digest, lowest_id, DIGEST_LEN);
+ tor_assert(fast_memeq(current_rsa_id,
+ rs->status.identity_digest,DIGEST_LEN));
+ memcpy(rs_out.identity_digest, current_rsa_id, DIGEST_LEN);
memcpy(rs_out.descriptor_digest, rs->status.descriptor_digest,
DIGEST_LEN);
rs_out.addr = rs->status.addr;
@@ -1614,7 +1617,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
const char *d = strmap_get_lc(name_to_id_map, rs_out.nickname);
if (!d) {
is_named = is_unnamed = 0;
- } else if (fast_memeq(d, lowest_id, DIGEST_LEN)) {
+ } else if (fast_memeq(d, current_rsa_id, DIGEST_LEN)) {
is_named = 1; is_unnamed = 0;
} else {
is_named = 0; is_unnamed = 1;
@@ -1980,6 +1983,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
done:
+ dircollator_free(collator);
tor_free(client_versions);
tor_free(server_versions);
tor_free(packages);
@@ -2244,7 +2248,8 @@ networkstatus_format_signatures(networkstatus_t *consensus,
for_detached_signatures ? flavor_name : "",
digest_name, id, sk);
}
- base64_encode(buf, sizeof(buf), sig->signature, sig->signature_len);
+ base64_encode(buf, sizeof(buf), sig->signature, sig->signature_len,
+ BASE64_ENCODE_MULTILINE);
strlcat(buf, "-----END SIGNATURE-----\n", sizeof(buf));
smartlist_add(elements, tor_strdup(buf));
} SMARTLIST_FOREACH_END(sig);
@@ -3459,7 +3464,7 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
char kbuf[128];
base64_encode(kbuf, sizeof(kbuf),
(const char*)ri->onion_curve25519_pkey->public_key,
- CURVE25519_PUBKEY_LEN);
+ CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
}
@@ -3486,9 +3491,18 @@ dirvote_create_microdescriptor(const routerinfo_t *ri, int consensus_method)
}
if (consensus_method >= MIN_METHOD_FOR_ID_HASH_IN_MD) {
- char idbuf[BASE64_DIGEST_LEN+1];
- digest_to_base64(idbuf, ri->cache_info.identity_digest);
- smartlist_add_asprintf(chunks, "id rsa1024 %s\n", idbuf);
+ char idbuf[ED25519_BASE64_LEN+1];
+ const char *keytype;
+ if (consensus_method >= MIN_METHOD_FOR_ED25519_ID_IN_MD &&
+ ri->signing_key_cert &&
+ ri->signing_key_cert->signing_key_included) {
+ keytype = "ed25519";
+ ed25519_public_to_base64(idbuf, &ri->signing_key_cert->signing_key);
+ } else {
+ keytype = "rsa1024";
+ digest_to_base64(idbuf, ri->cache_info.identity_digest);
+ }
+ smartlist_add_asprintf(chunks, "id %s %s\n", keytype, idbuf);
}
output = smartlist_join_strings(chunks, "", 0, NULL);
@@ -3561,7 +3575,8 @@ static const struct consensus_method_range_t {
{MIN_METHOD_FOR_A_LINES, MIN_METHOD_FOR_P6_LINES - 1},
{MIN_METHOD_FOR_P6_LINES, MIN_METHOD_FOR_NTOR_KEY - 1},
{MIN_METHOD_FOR_NTOR_KEY, MIN_METHOD_FOR_ID_HASH_IN_MD - 1},
- {MIN_METHOD_FOR_ID_HASH_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD},
+ {MIN_METHOD_FOR_ID_HASH_IN_MD, MIN_METHOD_FOR_ED25519_ID_IN_MD - 1},
+ {MIN_METHOD_FOR_ED25519_ID_IN_MD, MAX_SUPPORTED_CONSENSUS_METHOD},
{-1, -1}
};
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index 542563b708..0fb2b2599b 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -55,7 +55,7 @@
#define MIN_SUPPORTED_CONSENSUS_METHOD 13
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 20
+#define MAX_SUPPORTED_CONSENSUS_METHOD 21
/** Lowest consensus method where microdesc consensuses omit any entry
* with no microdesc. */
@@ -86,6 +86,13 @@
* GuardFraction information in microdescriptors. */
#define MIN_METHOD_FOR_GUARDFRACTION 20
+/** Lowest consensus method where authorities may include an "id" line for
+ * ed25519 identities in microdescriptors. */
+#define MIN_METHOD_FOR_ED25519_ID_IN_MD 21
+/** Lowest consensus method where authorities vote on ed25519 ids and ensure
+ * ed25519 id consistency. */
+#define MIN_METHOD_FOR_ED25519_ID_VOTING MIN_METHOD_FOR_ED25519_ID_IN_MD
+
/** Default bandwidth to clip unmeasured bandwidths to using method >=
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
* get confused with the above macros.) */
diff --git a/src/or/dns.c b/src/or/dns.c
index cc4a169422..db77d20783 100644
--- a/src/or/dns.c
+++ b/src/or/dns.c
@@ -24,7 +24,7 @@
#include "relay.h"
#include "router.h"
#include "ht.h"
-#include "../common/sandbox.h"
+#include "sandbox.h"
#ifdef HAVE_EVENT2_DNS_H
#include <event2/event.h>
#include <event2/dns.h>
diff --git a/src/or/entrynodes.c b/src/or/entrynodes.c
index 30108b6041..ebf675166b 100644
--- a/src/or/entrynodes.c
+++ b/src/or/entrynodes.c
@@ -141,8 +141,7 @@ entry_guard_set_status(entry_guard_t *e, const node_t *node,
}
if (node) {
- int is_dir = node_is_dir(node) && node->rs &&
- node->rs->version_supports_microdesc_cache;
+ int is_dir = node_is_dir(node);
if (options->UseBridges && node_is_a_configured_bridge(node))
is_dir = 1;
if (e->is_dir_cache != is_dir) {
@@ -398,10 +397,10 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend,
entry->bad_since = 0;
entry->can_retry = 1;
}
- entry->is_dir_cache = node->rs &&
- node->rs->version_supports_microdesc_cache;
+ entry->is_dir_cache = node_is_dir(node);
if (get_options()->UseBridges && node_is_a_configured_bridge(node))
entry->is_dir_cache = 1;
+
return NULL;
}
} else if (!for_directory) {
@@ -432,8 +431,7 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend,
node_describe(node));
strlcpy(entry->nickname, node_get_nickname(node), sizeof(entry->nickname));
memcpy(entry->identity, node->identity, DIGEST_LEN);
- entry->is_dir_cache = node_is_dir(node) && node->rs &&
- node->rs->version_supports_microdesc_cache;
+ entry->is_dir_cache = node_is_dir(node);
if (get_options()->UseBridges && node_is_a_configured_bridge(node))
entry->is_dir_cache = 1;
@@ -442,7 +440,8 @@ add_an_entry_guard(const node_t *chosen, int reset_status, int prepend,
* don't all select them on the same day, and b) avoid leaving a
* precise timestamp in the state file about when we first picked
* this guard. For details, see the Jan 2010 or-dev thread. */
- entry->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30);
+ time_t now = time(NULL);
+ entry->chosen_on_date = crypto_rand_time_range(now - 3600*24*30, now);
entry->chosen_by_version = tor_strdup(VERSION);
/* Are we picking this guard because all of our current guards are
@@ -571,22 +570,6 @@ remove_obsolete_entry_guards(time_t now)
} else if (tor_version_parse(ver, &v)) {
msg = "does not seem to be from any recognized version of Tor";
version_is_bad = 1;
- } else {
- char *tor_ver = NULL;
- tor_asprintf(&tor_ver, "Tor %s", ver);
- if ((tor_version_as_new_as(tor_ver, "0.1.0.10-alpha") &&
- !tor_version_as_new_as(tor_ver, "0.1.2.16-dev")) ||
- (tor_version_as_new_as(tor_ver, "0.2.0.0-alpha") &&
- !tor_version_as_new_as(tor_ver, "0.2.0.6-alpha")) ||
- /* above are bug 440; below are bug 1217 */
- (tor_version_as_new_as(tor_ver, "0.2.1.3-alpha") &&
- !tor_version_as_new_as(tor_ver, "0.2.1.23")) ||
- (tor_version_as_new_as(tor_ver, "0.2.2.0-alpha") &&
- !tor_version_as_new_as(tor_ver, "0.2.2.7-alpha"))) {
- msg = "was selected without regard for guard bandwidth";
- version_is_bad = 1;
- }
- tor_free(tor_ver);
}
if (!version_is_bad && entry->chosen_on_date + guard_lifetime < now) {
/* It's been too long since the date listed in our state file. */
@@ -989,39 +972,6 @@ entry_list_is_constrained(const or_options_t *options)
return 0;
}
-/** Return true iff this node can answer directory questions about
- * microdescriptors. */
-static int
-node_understands_microdescriptors(const node_t *node)
-{
- tor_assert(node);
- if (node->rs && node->rs->version_supports_microdesc_cache)
- return 1;
- if (node->ri && tor_version_supports_microdescriptors(node->ri->platform))
- return 1;
- return 0;
-}
-
-/** Return true iff <b>node</b> is able to answer directory questions
- * of type <b>dirinfo</b>. Always returns true if <b>dirinfo</b> is
- * NO_DIRINFO (zero). */
-static int
-node_can_handle_dirinfo(const node_t *node, dirinfo_type_t dirinfo)
-{
- /* Checking dirinfo for any type other than microdescriptors isn't required
- yet, since we only choose directory guards that can support microdescs,
- routerinfos, and networkstatuses, AND we don't use directory guards if
- we're configured to do direct downloads of anything else. The only case
- where we might have a guard that doesn't know about a type of directory
- information is when we're retrieving directory information from a
- bridge. */
-
- if ((dirinfo & MICRODESC_DIRINFO) &&
- !node_understands_microdescriptors(node))
- return 0;
- return 1;
-}
-
/** Pick a live (up and listed) entry guard from entry_guards. If
* <b>state</b> is non-NULL, this is for a specific circuit --
* make sure not to pick this circuit's exit or any node in the
@@ -1077,6 +1027,8 @@ populate_live_entry_guards(smartlist_t *live_entry_guards,
int retval = 0;
entry_is_live_flags_t entry_flags = 0;
+ (void) dirinfo_type;
+
{ /* Set the flags we want our entry node to have */
if (need_uptime) {
entry_flags |= ENTRY_NEED_UPTIME;
@@ -1108,9 +1060,6 @@ populate_live_entry_guards(smartlist_t *live_entry_guards,
continue; /* don't pick the same node for entry and exit */
if (smartlist_contains(exit_family, node))
continue; /* avoid relays that are family members of our exit */
- if (dirinfo_type != NO_DIRINFO &&
- !node_can_handle_dirinfo(node, dirinfo_type))
- continue; /* this node won't be able to answer our dir questions */
smartlist_add(live_entry_guards, (void*)node);
if (!entry->made_contact) {
/* Always start with the first not-yet-contacted entry
@@ -1491,8 +1440,9 @@ entry_guards_parse_state(or_state_t *state, int set, char **msg)
}
} else {
if (state_version) {
+ time_t now = time(NULL);
+ e->chosen_on_date = crypto_rand_time_range(now - 3600*24*30, now);
e->chosen_by_version = tor_strdup(state_version);
- e->chosen_on_date = time(NULL) - crypto_rand_int(3600*24*30);
}
}
if (e->path_bias_disabled && !e->bad_since)
@@ -2484,11 +2434,9 @@ any_bridge_supports_microdescriptors(void)
SMARTLIST_FOREACH_BEGIN(entry_guards, entry_guard_t *, e) {
node = node_get_by_id(e->identity);
if (node && node->is_running &&
- node_is_bridge(node) && node_is_a_configured_bridge(node) &&
- node_understands_microdescriptors(node)) {
+ node_is_bridge(node) && node_is_a_configured_bridge(node)) {
/* This is one of our current bridges, and we know enough about
- * it to know that it will be able to answer our microdescriptor
- * questions. */
+ * it to know that it will be able to answer our questions. */
return 1;
}
} SMARTLIST_FOREACH_END(e);
diff --git a/src/or/include.am b/src/or/include.am
index b44e1099dc..6bbf78871c 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -15,7 +15,7 @@ else
tor_platform_source=
endif
-EXTRA_DIST+= src/or/ntmain.c src/or/or_sha1.i src/or/Makefile.nmake
+EXTRA_DIST+= src/or/ntmain.c src/or/Makefile.nmake
if USE_EXTERNAL_EVDNS
evdns_source=
@@ -43,6 +43,7 @@ LIBTOR_A_SOURCES = \
src/or/connection_or.c \
src/or/control.c \
src/or/cpuworker.c \
+ src/or/dircollate.c \
src/or/directory.c \
src/or/dirserv.c \
src/or/dirvote.c \
@@ -53,6 +54,7 @@ LIBTOR_A_SOURCES = \
src/or/entrynodes.c \
src/or/ext_orport.c \
src/or/hibernate.c \
+ src/or/keypin.c \
src/or/main.c \
src/or/microdesc.c \
src/or/networkstatus.c \
@@ -71,24 +73,21 @@ LIBTOR_A_SOURCES = \
src/or/rephist.c \
src/or/replaycache.c \
src/or/router.c \
+ src/or/routerkeys.c \
src/or/routerlist.c \
src/or/routerparse.c \
src/or/routerset.c \
src/or/scheduler.c \
src/or/statefile.c \
src/or/status.c \
+ src/or/torcert.c \
src/or/onion_ntor.c \
$(evdns_source) \
- $(tor_platform_source) \
- src/or/config_codedigest.c
+ $(tor_platform_source)
src_or_libtor_a_SOURCES = $(LIBTOR_A_SOURCES)
src_or_libtor_testing_a_SOURCES = $(LIBTOR_A_SOURCES)
-#libtor_a_LIBADD = ../common/libor.a ../common/libor-crypto.a \
-# ../common/libor-event.a
-
-
src_or_tor_SOURCES = src/or/tor_main.c
AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or
@@ -98,7 +97,7 @@ AM_CPPFLAGS += -DSHARE_DATADIR="\"$(datadir)\"" \
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
-DBINDIR="\"$(bindir)\""
-src_or_libtor_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_or_libtor_testing_a_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
# -L flags need to go in LDFLAGS. -l flags need to go in LDADD.
@@ -109,23 +108,23 @@ src_or_libtor_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_or_tor_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
src_or_tor_LDADD = src/or/libtor.a src/common/libor.a \
src/common/libor-crypto.a $(LIBDONNA) \
- src/common/libor-event.a \
+ src/common/libor-event.a src/trunnel/libor-trunnel.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@
if COVERAGE_ENABLED
src_or_tor_cov_SOURCES = src/or/tor_main.c
-src_or_tor_cov_CPPFLAGS = -DTOR_UNIT_TESTS $(AM_CPPFLAGS)
+src_or_tor_cov_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_or_tor_cov_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_or_tor_cov_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@
src_or_tor_cov_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
src/common/libor-crypto-testing.a $(LIBDONNA) \
- src/common/libor-event-testing.a \
+ src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ @TOR_SYSTEMD_LIBS@
-TESTING_TOR_BINARY = ./src/or/tor-cov
+TESTING_TOR_BINARY = $(top_builddir)/src/or/tor-cov
else
-TESTING_TOR_BINARY = ./src/or/tor
+TESTING_TOR_BINARY = $(top_builddir)/src/or/tor
endif
ORHEADERS = \
@@ -148,6 +147,7 @@ ORHEADERS = \
src/or/connection_or.h \
src/or/control.h \
src/or/cpuworker.h \
+ src/or/dircollate.h \
src/or/directory.h \
src/or/dirserv.h \
src/or/dirvote.h \
@@ -159,6 +159,7 @@ ORHEADERS = \
src/or/geoip.h \
src/or/entrynodes.h \
src/or/hibernate.h \
+ src/or/keypin.h \
src/or/main.h \
src/or/microdesc.h \
src/or/networkstatus.h \
@@ -180,45 +181,36 @@ ORHEADERS = \
src/or/rephist.h \
src/or/replaycache.h \
src/or/router.h \
+ src/or/routerkeys.h \
src/or/routerlist.h \
+ src/or/routerkeys.h \
src/or/routerset.h \
src/or/routerparse.h \
src/or/scheduler.h \
src/or/statefile.h \
- src/or/status.h
+ src/or/status.h \
+ src/or/torcert.h
noinst_HEADERS+= $(ORHEADERS) micro-revision.i
-src/or/config_codedigest.o: src/or/or_sha1.i
-
micro-revision.i: FORCE
- @rm -f micro-revision.tmp; \
- if test -d "$(top_srcdir)/.git" && \
- test -x "`which git 2>&1;true`"; then \
- HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \
- echo \"$$HASH\" > micro-revision.tmp; \
- fi; \
- if test ! -f micro-revision.tmp ; then \
- if test ! -f micro-revision.i ; then \
- echo '""' > micro-revision.i; \
- fi; \
- elif test ! -f micro-revision.i || \
- test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \
- mv micro-revision.tmp micro-revision.i; \
- fi; true
-
-src/or/or_sha1.i: $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS)
- $(AM_V_GEN)if test "@SHA1SUM@" != none; then \
- (cd "$(srcdir)" && "@SHA1SUM@" $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS) ) | \
- "@SED@" -n 's/^\(.*\)$$/"\1\\n"/p' > src/or/or_sha1.i; \
- elif test "@OPENSSL@" != none; then \
- (cd "$(srcdir)" && "@OPENSSL@" sha1 $(src_or_tor_SOURCES) $(src_or_libtor_a_SOURCES) $(ORHEADERS)) | \
- "@SED@" -n 's/SHA1(\(.*\))= \(.*\)/"\2 \1\\n"/p' > src/or/or_sha1.i; \
- else \
- rm src/or/or_sha1.i; \
- touch src/or/or_sha1.i; \
- fi
-
-CLEANFILES+= micro-revision.i src/or/micro-revision.i
+ $(AM_V_at)rm -f micro-revision.tmp; \
+ if test -d "$(top_srcdir)/.git" && \
+ test -x "`which git 2>&1;true`"; then \
+ HASH="`cd "$(top_srcdir)" && git rev-parse --short=16 HEAD`"; \
+ echo \"$$HASH\" > micro-revision.tmp; \
+ fi; \
+ if test ! -f micro-revision.tmp; then \
+ if test ! -f micro-revision.i; then \
+ echo '""' > micro-revision.i; \
+ fi; \
+ elif test ! -f micro-revision.i || \
+ test x"`cat micro-revision.tmp`" != x"`cat micro-revision.i`"; then \
+ mv micro-revision.tmp micro-revision.i; \
+ fi; \
+ rm -f micro-revision.tmp; \
+ true
+
+CLEANFILES+= micro-revision.i src/or/micro-revision.i micro-revision.tmp
FORCE:
diff --git a/src/or/keypin.c b/src/or/keypin.c
new file mode 100644
index 0000000000..a5b4cf75f9
--- /dev/null
+++ b/src/or/keypin.c
@@ -0,0 +1,419 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define KEYPIN_PRIVATE
+
+#include "orconfig.h"
+#include "compat.h"
+#include "crypto.h"
+#include "di_ops.h"
+#include "ht.h"
+#include "keypin.h"
+#include "siphash.h"
+#include "torint.h"
+#include "torlog.h"
+#include "util.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+
+/**
+ * @file keypin.c
+ * @brief Key-pinning for RSA and Ed25519 identity keys at directory
+ * authorities.
+ *
+ * This module implements a key-pinning mechanism to ensure that it's safe
+ * to use RSA keys as identitifers even as we migrate to Ed25519 keys. It
+ * remembers, for every Ed25519 key we've seen, what the associated Ed25519
+ * key is. This way, if we see a different Ed25519 key with that RSA key,
+ * we'll know that there's a mismatch.
+ *
+ * We persist these entries to disk using a simple format, where each line
+ * has a base64-encoded RSA SHA1 hash, then a base64-endoded Ed25519 key.
+ * Empty lines, misformed lines, and lines beginning with # are
+ * ignored. Lines beginning with @ are reserved for future extensions.
+ */
+
+static int keypin_journal_append_entry(const uint8_t *rsa_id_digest,
+ const uint8_t *ed25519_id_key);
+static int keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
+ const uint8_t *ed25519_id_key,
+ int do_not_add);
+
+static HT_HEAD(rsamap, keypin_ent_st) the_rsa_map = HT_INITIALIZER();
+static HT_HEAD(edmap, keypin_ent_st) the_ed_map = HT_INITIALIZER();
+
+/** Hashtable helper: compare two keypin table entries and return true iff
+ * they have the same RSA key IDs. */
+static INLINE int
+keypin_ents_eq_rsa(const keypin_ent_t *a, const keypin_ent_t *b)
+{
+ return tor_memeq(a->rsa_id, b->rsa_id, sizeof(a->rsa_id));
+}
+
+/** Hashtable helper: hash a keypin table entries based on its RSA key ID */
+static INLINE unsigned
+keypin_ent_hash_rsa(const keypin_ent_t *a)
+{
+return (unsigned) siphash24g(a->rsa_id, sizeof(a->rsa_id));
+}
+
+/** Hashtable helper: compare two keypin table entries and return true iff
+ * they have the same ed25519 keys */
+static INLINE int
+keypin_ents_eq_ed(const keypin_ent_t *a, const keypin_ent_t *b)
+{
+ return tor_memeq(a->ed25519_key, b->ed25519_key, sizeof(a->ed25519_key));
+}
+
+/** Hashtable helper: hash a keypin table entries based on its ed25519 key */
+static INLINE unsigned
+keypin_ent_hash_ed(const keypin_ent_t *a)
+{
+return (unsigned) siphash24g(a->ed25519_key, sizeof(a->ed25519_key));
+}
+
+HT_PROTOTYPE(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa,
+ keypin_ents_eq_rsa);
+HT_GENERATE2(rsamap, keypin_ent_st, rsamap_node, keypin_ent_hash_rsa,
+ keypin_ents_eq_rsa, 0.6, tor_reallocarray, tor_free_);
+
+HT_PROTOTYPE(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed,
+ keypin_ents_eq_ed);
+HT_GENERATE2(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed,
+ keypin_ents_eq_ed, 0.6, tor_reallocarray, tor_free_);
+
+/**
+ * Check whether we already have an entry in the key pinning table for a
+ * router with RSA ID digest <b>rsa_id_digest</b> or for ed25519 key
+ * <b>ed25519_id_key</b>. If we have an entry that matches both keys,
+ * return KEYPIN_FOUND. If we find an entry that matches one key but
+ * not the other, return KEYPIN_MISMATCH. If we have no entry for either
+ * key, add such an entry to the table and return KEYPIN_ADDED.
+ */
+int
+keypin_check_and_add(const uint8_t *rsa_id_digest,
+ const uint8_t *ed25519_id_key)
+{
+ return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 0);
+}
+
+/**
+ * As keypin_check_and_add, but do not add. Return KEYPIN_NOT_FOUND if
+ * we would add.
+ */
+int
+keypin_check(const uint8_t *rsa_id_digest,
+ const uint8_t *ed25519_id_key)
+{
+ return keypin_check_and_add_impl(rsa_id_digest, ed25519_id_key, 1);
+}
+
+/**
+ * Helper: implements keypin_check and keypin_check_and_add.
+ */
+static int
+keypin_check_and_add_impl(const uint8_t *rsa_id_digest,
+ const uint8_t *ed25519_id_key,
+ int do_not_add)
+{
+ keypin_ent_t search, *ent;
+ memset(&search, 0, sizeof(search));
+ memcpy(search.rsa_id, rsa_id_digest, sizeof(search.rsa_id));
+ memcpy(search.ed25519_key, ed25519_id_key, sizeof(search.ed25519_key));
+
+ /* Search by RSA key digest first */
+ ent = HT_FIND(rsamap, &the_rsa_map, &search);
+ if (ent) {
+ tor_assert(fast_memeq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id)));
+ if (tor_memeq(ent->ed25519_key, ed25519_id_key,sizeof(ent->ed25519_key))) {
+ return KEYPIN_FOUND; /* Match on both keys. Great. */
+ } else {
+ return KEYPIN_MISMATCH; /* Found RSA with different Ed key */
+ }
+ }
+
+ /* See if we know a different RSA key for this ed key */
+ ent = HT_FIND(edmap, &the_ed_map, &search);
+ if (ent) {
+ /* If we got here, then the ed key matches and the RSA doesn't */
+ tor_assert(fast_memeq(ent->ed25519_key, ed25519_id_key,
+ sizeof(ent->ed25519_key)));
+ tor_assert(fast_memneq(ent->rsa_id, rsa_id_digest, sizeof(ent->rsa_id)));
+ return KEYPIN_MISMATCH;
+ }
+
+ /* Okay, this one is new to us. */
+ if (do_not_add)
+ return KEYPIN_NOT_FOUND;
+
+ ent = tor_memdup(&search, sizeof(search));
+ keypin_add_entry_to_map(ent);
+ keypin_journal_append_entry(rsa_id_digest, ed25519_id_key);
+ return KEYPIN_ADDED;
+}
+
+/**
+ * Helper: add <b>ent</b> to the hash tables.
+ */
+MOCK_IMPL(STATIC void,
+keypin_add_entry_to_map, (keypin_ent_t *ent))
+{
+ HT_INSERT(rsamap, &the_rsa_map, ent);
+ HT_INSERT(edmap, &the_ed_map, ent);
+}
+
+/**
+ * Check whether we already have an entry in the key pinning table for a
+ * router with RSA ID digest <b>rsa_id_digest</b>. If we have no such entry,
+ * return KEYPIN_NOT_FOUND. If we find an entry that matches the RSA key but
+ * which has an ed25519 key, return KEYPIN_MISMATCH.
+ */
+int
+keypin_check_lone_rsa(const uint8_t *rsa_id_digest)
+{
+ keypin_ent_t search, *ent;
+ memset(&search, 0, sizeof(search));
+ memcpy(search.rsa_id, rsa_id_digest, sizeof(search.rsa_id));
+
+ /* Search by RSA key digest first */
+ ent = HT_FIND(rsamap, &the_rsa_map, &search);
+ if (ent) {
+ return KEYPIN_MISMATCH;
+ } else {
+ return KEYPIN_NOT_FOUND;
+ }
+}
+
+/** Open fd to the keypinning journal file. */
+static int keypin_journal_fd = -1;
+
+/** Open the key-pinning journal to append to <b>fname</b>. Return 0 on
+ * success, -1 on failure. */
+int
+keypin_open_journal(const char *fname)
+{
+ /* O_SYNC ??*/
+ int fd = tor_open_cloexec(fname, O_WRONLY|O_CREAT|O_BINARY, 0600);
+ if (fd < 0)
+ goto err;
+
+ if (tor_fd_seekend(fd) < 0)
+ goto err;
+
+ /* Add a newline in case the last line was only partially written */
+ if (write(fd, "\n", 1) < 1)
+ goto err;
+
+ /* Add something about when we opened this file. */
+ char buf[80];
+ char tbuf[ISO_TIME_LEN+1];
+ format_iso_time(tbuf, approx_time());
+ tor_snprintf(buf, sizeof(buf), "@opened-at %s\n", tbuf);
+ if (write_all(fd, buf, strlen(buf), 0) < 0)
+ goto err;
+
+ keypin_journal_fd = fd;
+ return 0;
+ err:
+ if (fd >= 0)
+ close(fd);
+ return -1;
+}
+
+/** Close the keypinning journal file. */
+int
+keypin_close_journal(void)
+{
+ if (keypin_journal_fd >= 0)
+ close(keypin_journal_fd);
+ keypin_journal_fd = -1;
+ return 0;
+}
+
+/** Length of a keypinning journal line, including terminating newline. */
+#define JOURNAL_LINE_LEN (BASE64_DIGEST_LEN + BASE64_DIGEST256_LEN + 2)
+
+/** Add an entry to the keypinning journal to map <b>rsa_id_digest</b> and
+ * <b>ed25519_id_key</b>. */
+static int
+keypin_journal_append_entry(const uint8_t *rsa_id_digest,
+ const uint8_t *ed25519_id_key)
+{
+ if (keypin_journal_fd == -1)
+ return -1;
+ char line[JOURNAL_LINE_LEN];
+ digest_to_base64(line, (const char*)rsa_id_digest);
+ line[BASE64_DIGEST_LEN] = ' ';
+ digest256_to_base64(line + BASE64_DIGEST_LEN + 1,
+ (const char*)ed25519_id_key);
+ line[BASE64_DIGEST_LEN+1+BASE64_DIGEST256_LEN] = '\n';
+
+ if (write_all(keypin_journal_fd, line, JOURNAL_LINE_LEN, 0)<0) {
+ log_warn(LD_DIRSERV, "Error while adding a line to the key-pinning "
+ "journal: %s", strerror(errno));
+ keypin_close_journal();
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Load a journal from the <b>size</b>-byte region at <b>data</b>. Return 0
+ * on success, -1 on failure. */
+STATIC int
+keypin_load_journal_impl(const char *data, size_t size)
+{
+ const char *start = data, *end = data + size, *next;
+
+ int n_corrupt_lines = 0;
+ int n_entries = 0;
+ int n_duplicates = 0;
+ int n_conflicts = 0;
+
+ for (const char *cp = start; cp < end; cp = next) {
+ const char *eol = memchr(cp, '\n', end-cp);
+ const char *eos = eol ? eol : end;
+ const size_t len = eos - cp;
+
+ next = eol ? eol + 1 : end;
+
+ if (len == 0) {
+ continue;
+ }
+
+ if (*cp == '@') {
+ /* Lines that start with @ are reserved. Ignore for now. */
+ continue;
+ }
+ if (*cp == '#') {
+ /* Lines that start with # are comments. */
+ continue;
+ }
+
+ /* Is it the right length? (The -1 here is for the newline.) */
+ if (len != JOURNAL_LINE_LEN - 1) {
+ /* Lines with a bad length are corrupt unless they are empty.
+ * Ignore them either way */
+ for (const char *s = cp; s < eos; ++s) {
+ if (! TOR_ISSPACE(*s)) {
+ ++n_corrupt_lines;
+ break;
+ }
+ }
+ continue;
+ }
+
+ keypin_ent_t *ent = keypin_parse_journal_line(cp);
+
+ if (ent == NULL) {
+ ++n_corrupt_lines;
+ continue;
+ }
+
+ const keypin_ent_t *ent2;
+ if ((ent2 = HT_FIND(rsamap, &the_rsa_map, ent))) {
+ if (fast_memeq(ent2->ed25519_key, ent->ed25519_key, DIGEST256_LEN)) {
+ ++n_duplicates;
+ } else {
+ ++n_conflicts;
+ }
+ tor_free(ent);
+ continue;
+ } else if (HT_FIND(edmap, &the_ed_map, ent)) {
+ tor_free(ent);
+ ++n_conflicts;
+ continue;
+ }
+
+ keypin_add_entry_to_map(ent);
+ ++n_entries;
+ }
+
+ int severity = (n_corrupt_lines || n_duplicates) ? LOG_WARN : LOG_INFO;
+ tor_log(severity, LD_DIRSERV,
+ "Loaded %d entries from keypin journal. "
+ "Found %d corrupt lines, %d duplicates, and %d conflicts.",
+ n_entries, n_corrupt_lines, n_duplicates, n_conflicts);
+
+ return 0;
+}
+
+/**
+ * Load a journal from the file called <b>fname</b>. Return 0 on success,
+ * -1 on failure.
+ */
+int
+keypin_load_journal(const char *fname)
+{
+ tor_mmap_t *map = tor_mmap_file(fname);
+ if (!map) {
+ if (errno == ENOENT)
+ return 0;
+ else
+ return -1;
+ }
+ int r = keypin_load_journal_impl(map->data, map->size);
+ tor_munmap_file(map);
+ return r;
+}
+
+/** Parse a single keypinning journal line entry from <b>cp</b>. The input
+ * does not need to be NUL-terminated, but it <em>does</em> need to have
+ * KEYPIN_JOURNAL_LINE_LEN -1 bytes available to read. Return a new entry
+ * on success, and NULL on failure.
+ */
+STATIC keypin_ent_t *
+keypin_parse_journal_line(const char *cp)
+{
+ /* XXXX assumes !USE_OPENSSL_BASE64 */
+ keypin_ent_t *ent = tor_malloc_zero(sizeof(keypin_ent_t));
+
+ if (base64_decode((char*)ent->rsa_id, sizeof(ent->rsa_id),
+ cp, BASE64_DIGEST_LEN) != DIGEST_LEN ||
+ cp[BASE64_DIGEST_LEN] != ' ' ||
+ base64_decode((char*)ent->ed25519_key, sizeof(ent->ed25519_key),
+ cp+BASE64_DIGEST_LEN+1, BASE64_DIGEST256_LEN) != DIGEST256_LEN) {
+ tor_free(ent);
+ return NULL;
+ } else {
+ return ent;
+ }
+}
+
+/** Remove all entries from the keypinning table.*/
+void
+keypin_clear(void)
+{
+ int bad_entries = 0;
+ {
+ keypin_ent_t **ent, **next, *this;
+ for (ent = HT_START(rsamap, &the_rsa_map); ent != NULL; ent = next) {
+ this = *ent;
+ next = HT_NEXT_RMV(rsamap, &the_rsa_map, ent);
+
+ keypin_ent_t *other_ent = HT_REMOVE(edmap, &the_ed_map, this);
+ bad_entries += (other_ent != this);
+
+ tor_free(this);
+ }
+ }
+ bad_entries += HT_SIZE(&the_ed_map);
+
+ HT_CLEAR(edmap,&the_ed_map);
+ HT_CLEAR(rsamap,&the_rsa_map);
+
+ if (bad_entries) {
+ log_warn(LD_BUG, "Found %d discrepencies in the the keypin database.",
+ bad_entries);
+ }
+}
+
diff --git a/src/or/keypin.h b/src/or/keypin.h
new file mode 100644
index 0000000000..2a5b3f1786
--- /dev/null
+++ b/src/or/keypin.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_KEYPIN_H
+#define TOR_KEYPIN_H
+
+#include "testsupport.h"
+
+int keypin_check_and_add(const uint8_t *rsa_id_digest,
+ const uint8_t *ed25519_id_key);
+int keypin_check(const uint8_t *rsa_id_digest,
+ const uint8_t *ed25519_id_key);
+
+int keypin_open_journal(const char *fname);
+int keypin_close_journal(void);
+int keypin_load_journal(const char *fname);
+void keypin_clear(void);
+int keypin_check_lone_rsa(const uint8_t *rsa_id_digest);
+
+#define KEYPIN_FOUND 0
+#define KEYPIN_ADDED 1
+#define KEYPIN_MISMATCH -1
+#define KEYPIN_NOT_FOUND -2
+
+#ifdef KEYPIN_PRIVATE
+
+/**
+ * In-memory representation of a key-pinning table entry.
+ */
+typedef struct keypin_ent_st {
+ HT_ENTRY(keypin_ent_st) rsamap_node;
+ HT_ENTRY(keypin_ent_st) edmap_node;
+ /** SHA1 hash of the RSA key */
+ uint8_t rsa_id[DIGEST_LEN];
+ /** Ed2219 key. */
+ uint8_t ed25519_key[DIGEST256_LEN];
+} keypin_ent_t;
+
+STATIC keypin_ent_t * keypin_parse_journal_line(const char *cp);
+STATIC int keypin_load_journal_impl(const char *data, size_t size);
+
+MOCK_DECL(STATIC void, keypin_add_entry_to_map, (keypin_ent_t *ent));
+#endif
+
+#endif
+
diff --git a/src/or/main.c b/src/or/main.c
index 651291b57b..5fa3a01500 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -37,6 +37,7 @@
#include "entrynodes.h"
#include "geoip.h"
#include "hibernate.h"
+#include "keypin.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
@@ -51,6 +52,7 @@
#include "rendservice.h"
#include "rephist.h"
#include "router.h"
+#include "routerkeys.h"
#include "routerlist.h"
#include "routerparse.h"
#include "scheduler.h"
@@ -63,7 +65,7 @@
#include <openssl/crypto.h>
#endif
#include "memarea.h"
-#include "../common/sandbox.h"
+#include "sandbox.h"
#ifdef HAVE_EVENT2_EVENT_H
#include <event2/event.h>
@@ -131,8 +133,6 @@ static uint64_t stats_n_bytes_written = 0;
time_t time_of_process_start = 0;
/** How many seconds have we been running? */
long stats_n_seconds_working = 0;
-/** When do we next launch DNS wildcarding checks? */
-static time_t time_to_check_for_correct_dns = 0;
/** How often will we honor SIGNEWNYM requests? */
#define MAX_SIGNEWNYM_RATE 10
@@ -1202,7 +1202,49 @@ get_signewnym_epoch(void)
return newnym_epoch;
}
-static time_t time_to_check_descriptor = 0;
+typedef struct {
+ time_t last_rotated_x509_certificate;
+ time_t check_v3_certificate;
+ time_t check_listeners;
+ time_t download_networkstatus;
+ time_t try_getting_descriptors;
+ time_t reset_descriptor_failures;
+ time_t add_entropy;
+ time_t write_bridge_status_file;
+ time_t downrate_stability;
+ time_t save_stability;
+ time_t clean_caches;
+ time_t recheck_bandwidth;
+ time_t check_for_expired_networkstatus;
+ time_t write_stats_files;
+ time_t write_bridge_stats;
+ time_t check_port_forwarding;
+ time_t launch_reachability_tests;
+ time_t retry_dns_init;
+ time_t next_heartbeat;
+ time_t check_descriptor;
+ /** When do we next launch DNS wildcarding checks? */
+ time_t check_for_correct_dns;
+ /** When do we next make sure our Ed25519 keys aren't about to expire? */
+ time_t check_ed_keys;
+
+} time_to_t;
+
+static time_to_t time_to = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/** Reset all the time_to's so we'll do all our actions again as if we
+ * just started up.
+ * Useful if our clock just moved back a long time from the future,
+ * so we don't wait until that future arrives again before acting.
+ */
+void
+reset_all_main_loop_timers(void)
+{
+ memset(&time_to, 0, sizeof(time_to));
+}
+
/**
* Update our schedule so that we'll check whether we need to update our
* descriptor immediately, rather than after up to CHECK_DESCRIPTOR_INTERVAL
@@ -1211,7 +1253,7 @@ static time_t time_to_check_descriptor = 0;
void
reschedule_descriptor_update_check(void)
{
- time_to_check_descriptor = 0;
+ time_to.check_descriptor = 0;
}
/** Perform regular maintenance tasks. This function gets run once per
@@ -1220,26 +1262,7 @@ reschedule_descriptor_update_check(void)
static void
run_scheduled_events(time_t now)
{
- static time_t last_rotated_x509_certificate = 0;
- static time_t time_to_check_v3_certificate = 0;
- static time_t time_to_check_listeners = 0;
- static time_t time_to_download_networkstatus = 0;
- static time_t time_to_try_getting_descriptors = 0;
- static time_t time_to_reset_descriptor_failures = 0;
- static time_t time_to_add_entropy = 0;
- static time_t time_to_write_bridge_status_file = 0;
- static time_t time_to_downrate_stability = 0;
- static time_t time_to_save_stability = 0;
- static time_t time_to_clean_caches = 0;
- static time_t time_to_recheck_bandwidth = 0;
- static time_t time_to_check_for_expired_networkstatus = 0;
- static time_t time_to_write_stats_files = 0;
- static time_t time_to_write_bridge_stats = 0;
- static time_t time_to_check_port_forwarding = 0;
- static time_t time_to_launch_reachability_tests = 0;
static int should_init_bridge_stats = 1;
- static time_t time_to_retry_dns_init = 0;
- static time_t time_to_next_heartbeat = 0;
const or_options_t *options = get_options();
int is_server = server_mode(options);
@@ -1279,19 +1302,31 @@ run_scheduled_events(time_t now)
router_upload_dir_desc_to_dirservers(0);
}
+ if (is_server && time_to.check_ed_keys < now) {
+ if (should_make_new_ed_keys(options, now)) {
+ if (load_ed_keys(options, now) < 0 ||
+ generate_ed_link_cert(options, now)) {
+ log_err(LD_OR, "Unable to update Ed25519 keys! Exiting.");
+ tor_cleanup();
+ exit(0);
+ }
+ }
+ time_to.check_ed_keys = now + 30;
+ }
+
if (!should_delay_dir_fetches(options, NULL) &&
- time_to_try_getting_descriptors < now) {
+ time_to.try_getting_descriptors < now) {
update_all_descriptor_downloads(now);
update_extrainfo_downloads(now);
if (router_have_minimum_dir_info())
- time_to_try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL;
+ time_to.try_getting_descriptors = now + LAZY_DESCRIPTOR_RETRY_INTERVAL;
else
- time_to_try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL;
+ time_to.try_getting_descriptors = now + GREEDY_DESCRIPTOR_RETRY_INTERVAL;
}
- if (time_to_reset_descriptor_failures < now) {
+ if (time_to.reset_descriptor_failures < now) {
router_reset_descriptor_download_failures();
- time_to_reset_descriptor_failures =
+ time_to.reset_descriptor_failures =
now + DESCRIPTOR_FAILURE_RESET_INTERVAL;
}
@@ -1300,28 +1335,29 @@ run_scheduled_events(time_t now)
/* 1b. Every MAX_SSL_KEY_LIFETIME_INTERNAL seconds, we change our
* TLS context. */
- if (!last_rotated_x509_certificate)
- last_rotated_x509_certificate = now;
- if (last_rotated_x509_certificate+MAX_SSL_KEY_LIFETIME_INTERNAL < now) {
+ if (!time_to.last_rotated_x509_certificate)
+ time_to.last_rotated_x509_certificate = now;
+ if (time_to.last_rotated_x509_certificate +
+ MAX_SSL_KEY_LIFETIME_INTERNAL < now) {
log_info(LD_GENERAL,"Rotating tls context.");
if (router_initialize_tls_context() < 0) {
log_warn(LD_BUG, "Error reinitializing TLS context");
/* XXX is it a bug here, that we just keep going? -RD */
}
- last_rotated_x509_certificate = now;
+ time_to.last_rotated_x509_certificate = now;
/* We also make sure to rotate the TLS connections themselves if they've
* been up for too long -- but that's done via is_bad_for_new_circs in
* connection_run_housekeeping() above. */
}
- if (time_to_add_entropy < now) {
- if (time_to_add_entropy) {
+ if (time_to.add_entropy < now) {
+ if (time_to.add_entropy) {
/* We already seeded once, so don't die on failure. */
- crypto_seed_rng(0);
+ crypto_seed_rng();
}
/** How often do we add more entropy to OpenSSL's RNG pool? */
#define ENTROPY_INTERVAL (60*60)
- time_to_add_entropy = now + ENTROPY_INTERVAL;
+ time_to.add_entropy = now + ENTROPY_INTERVAL;
}
/* 1c. If we have to change the accounting interval or record
@@ -1329,10 +1365,10 @@ run_scheduled_events(time_t now)
if (accounting_is_enabled(options))
accounting_run_housekeeping(now);
- if (time_to_launch_reachability_tests < now &&
+ if (time_to.launch_reachability_tests < now &&
(authdir_mode_tests_reachability(options)) &&
!net_is_disabled()) {
- time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL;
+ time_to.launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL;
/* try to determine reachability of the other Tor relays */
dirserv_test_reachability(now);
}
@@ -1340,29 +1376,29 @@ run_scheduled_events(time_t now)
/* 1d. Periodically, we discount older stability information so that new
* stability info counts more, and save the stability information to disk as
* appropriate. */
- if (time_to_downrate_stability < now)
- time_to_downrate_stability = rep_hist_downrate_old_runs(now);
+ if (time_to.downrate_stability < now)
+ time_to.downrate_stability = rep_hist_downrate_old_runs(now);
if (authdir_mode_tests_reachability(options)) {
- if (time_to_save_stability < now) {
- if (time_to_save_stability && rep_hist_record_mtbf_data(now, 1)<0) {
+ if (time_to.save_stability < now) {
+ if (time_to.save_stability && rep_hist_record_mtbf_data(now, 1)<0) {
log_warn(LD_GENERAL, "Couldn't store mtbf data.");
}
#define SAVE_STABILITY_INTERVAL (30*60)
- time_to_save_stability = now + SAVE_STABILITY_INTERVAL;
+ time_to.save_stability = now + SAVE_STABILITY_INTERVAL;
}
}
/* 1e. Periodically, if we're a v3 authority, we check whether our cert is
* close to expiring and warn the admin if it is. */
- if (time_to_check_v3_certificate < now) {
+ if (time_to.check_v3_certificate < now) {
v3_authority_check_key_expiry();
#define CHECK_V3_CERTIFICATE_INTERVAL (5*60)
- time_to_check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL;
+ time_to.check_v3_certificate = now + CHECK_V3_CERTIFICATE_INTERVAL;
}
/* 1f. Check whether our networkstatus has expired.
*/
- if (time_to_check_for_expired_networkstatus < now) {
+ if (time_to.check_for_expired_networkstatus < now) {
networkstatus_t *ns = networkstatus_get_latest_consensus();
/*XXXX RD: This value needs to be the same as REASONABLY_LIVE_TIME in
* networkstatus_get_reasonably_live_consensus(), but that value is way
@@ -1373,68 +1409,68 @@ run_scheduled_events(time_t now)
router_dir_info_changed();
}
#define CHECK_EXPIRED_NS_INTERVAL (2*60)
- time_to_check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL;
+ time_to.check_for_expired_networkstatus = now + CHECK_EXPIRED_NS_INTERVAL;
}
/* 1g. Check whether we should write statistics to disk.
*/
- if (time_to_write_stats_files < now) {
+ if (time_to.write_stats_files < now) {
#define CHECK_WRITE_STATS_INTERVAL (60*60)
- time_t next_time_to_write_stats_files = (time_to_write_stats_files > 0 ?
- time_to_write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL;
+ time_t next_time_to_write_stats_files = (time_to.write_stats_files > 0 ?
+ time_to.write_stats_files : now) + CHECK_WRITE_STATS_INTERVAL;
if (options->CellStatistics) {
time_t next_write =
- rep_hist_buffer_stats_write(time_to_write_stats_files);
+ rep_hist_buffer_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->DirReqStatistics) {
- time_t next_write = geoip_dirreq_stats_write(time_to_write_stats_files);
+ time_t next_write = geoip_dirreq_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->EntryStatistics) {
- time_t next_write = geoip_entry_stats_write(time_to_write_stats_files);
+ time_t next_write = geoip_entry_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->HiddenServiceStatistics) {
- time_t next_write = rep_hist_hs_stats_write(time_to_write_stats_files);
+ time_t next_write = rep_hist_hs_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->ExitPortStatistics) {
- time_t next_write = rep_hist_exit_stats_write(time_to_write_stats_files);
+ time_t next_write = rep_hist_exit_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->ConnDirectionStatistics) {
- time_t next_write = rep_hist_conn_stats_write(time_to_write_stats_files);
+ time_t next_write = rep_hist_conn_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
if (options->BridgeAuthoritativeDir) {
- time_t next_write = rep_hist_desc_stats_write(time_to_write_stats_files);
+ time_t next_write = rep_hist_desc_stats_write(time_to.write_stats_files);
if (next_write && next_write < next_time_to_write_stats_files)
next_time_to_write_stats_files = next_write;
}
- time_to_write_stats_files = next_time_to_write_stats_files;
+ time_to.write_stats_files = next_time_to_write_stats_files;
}
/* 1h. Check whether we should write bridge statistics to disk.
*/
if (should_record_bridge_info(options)) {
- if (time_to_write_bridge_stats < now) {
+ if (time_to.write_bridge_stats < now) {
if (should_init_bridge_stats) {
/* (Re-)initialize bridge statistics. */
geoip_bridge_stats_init(now);
- time_to_write_bridge_stats = now + WRITE_STATS_INTERVAL;
+ time_to.write_bridge_stats = now + WRITE_STATS_INTERVAL;
should_init_bridge_stats = 0;
} else {
/* Possibly write bridge statistics to disk and ask when to write
* them next time. */
- time_to_write_bridge_stats = geoip_bridge_stats_write(
- time_to_write_bridge_stats);
+ time_to.write_bridge_stats = geoip_bridge_stats_write(
+ time_to.write_bridge_stats);
}
}
} else if (!should_init_bridge_stats) {
@@ -1444,19 +1480,19 @@ run_scheduled_events(time_t now)
}
/* Remove old information from rephist and the rend cache. */
- if (time_to_clean_caches < now) {
+ if (time_to.clean_caches < now) {
rep_history_clean(now - options->RephistTrackTime);
rend_cache_clean(now);
rend_cache_clean_v2_descs_as_dir(now, 0);
microdesc_cache_rebuild(NULL, 0);
#define CLEAN_CACHES_INTERVAL (30*60)
- time_to_clean_caches = now + CLEAN_CACHES_INTERVAL;
+ time_to.clean_caches = now + CLEAN_CACHES_INTERVAL;
}
#define RETRY_DNS_INTERVAL (10*60)
/* If we're a server and initializing dns failed, retry periodically. */
- if (time_to_retry_dns_init < now) {
- time_to_retry_dns_init = now + RETRY_DNS_INTERVAL;
+ if (time_to.retry_dns_init < now) {
+ time_to.retry_dns_init = now + RETRY_DNS_INTERVAL;
if (is_server && has_dns_init_failed())
dns_init();
}
@@ -1471,9 +1507,9 @@ run_scheduled_events(time_t now)
/* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */
- if (time_to_check_descriptor < now && !options->DisableNetwork) {
+ if (time_to.check_descriptor < now && !options->DisableNetwork) {
static int dirport_reachability_count = 0;
- time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
+ time_to.check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
check_descriptor_bandwidth_changed(now);
check_descriptor_ipaddress_changed(now);
mark_my_descriptor_dirty_if_too_old(now);
@@ -1487,18 +1523,18 @@ run_scheduled_events(time_t now)
consider_testing_reachability(1, dirport_reachability_count==0);
if (++dirport_reachability_count > 5)
dirport_reachability_count = 0;
- } else if (time_to_recheck_bandwidth < now) {
+ } else if (time_to.recheck_bandwidth < now) {
/* If we haven't checked for 12 hours and our bandwidth estimate is
* low, do another bandwidth test. This is especially important for
* bridges, since they might go long periods without much use. */
const routerinfo_t *me = router_get_my_routerinfo();
- if (time_to_recheck_bandwidth && me &&
+ if (time_to.recheck_bandwidth && me &&
me->bandwidthcapacity < me->bandwidthrate &&
me->bandwidthcapacity < 51200) {
reset_bandwidth_test();
}
#define BANDWIDTH_RECHECK_INTERVAL (12*60*60)
- time_to_recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL;
+ time_to.recheck_bandwidth = now + BANDWIDTH_RECHECK_INTERVAL;
}
}
@@ -1516,8 +1552,8 @@ run_scheduled_events(time_t now)
#define networkstatus_dl_check_interval(o) ((o)->TestingTorNetwork ? 1 : 60)
if (!should_delay_dir_fetches(options, NULL) &&
- time_to_download_networkstatus < now) {
- time_to_download_networkstatus =
+ time_to.download_networkstatus < now) {
+ time_to.download_networkstatus =
now + networkstatus_dl_check_interval(options);
update_networkstatus_downloads(now);
}
@@ -1547,9 +1583,9 @@ run_scheduled_events(time_t now)
connection_expire_held_open();
/* 3d. And every 60 seconds, we relaunch listeners if any died. */
- if (!net_is_disabled() && time_to_check_listeners < now) {
+ if (!net_is_disabled() && time_to.check_listeners < now) {
retry_all_listeners(NULL, NULL, 0);
- time_to_check_listeners = now+60;
+ time_to.check_listeners = now+60;
}
/* 4. Every second, we try a new circuit if there are no valid
@@ -1601,28 +1637,29 @@ run_scheduled_events(time_t now)
* to us. */
if (!net_is_disabled() &&
public_server_mode(options) &&
- time_to_check_for_correct_dns < now &&
+ time_to.check_for_correct_dns < now &&
! router_my_exit_policy_is_reject_star()) {
- if (!time_to_check_for_correct_dns) {
- time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120);
+ if (!time_to.check_for_correct_dns) {
+ time_to.check_for_correct_dns =
+ crypto_rand_time_range(now + 60, now + 180);
} else {
dns_launch_correctness_checks();
- time_to_check_for_correct_dns = now + 12*3600 +
+ time_to.check_for_correct_dns = now + 12*3600 +
crypto_rand_int(12*3600);
}
}
/* 10. write bridge networkstatus file to disk */
if (options->BridgeAuthoritativeDir &&
- time_to_write_bridge_status_file < now) {
+ time_to.write_bridge_status_file < now) {
networkstatus_dump_bridge_status_to_file(now);
#define BRIDGE_STATUSFILE_INTERVAL (30*60)
- time_to_write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL;
+ time_to.write_bridge_status_file = now+BRIDGE_STATUSFILE_INTERVAL;
}
/* 11. check the port forwarding app */
if (!net_is_disabled() &&
- time_to_check_port_forwarding < now &&
+ time_to.check_port_forwarding < now &&
options->PortForwarding &&
is_server) {
#define PORT_FORWARDING_CHECK_INTERVAL 5
@@ -1635,7 +1672,7 @@ run_scheduled_events(time_t now)
SMARTLIST_FOREACH(ports_to_forward, char *, cp, tor_free(cp));
smartlist_free(ports_to_forward);
}
- time_to_check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
+ time_to.check_port_forwarding = now+PORT_FORWARDING_CHECK_INTERVAL;
}
/* 11b. check pending unconfigured managed proxies */
@@ -1644,10 +1681,10 @@ run_scheduled_events(time_t now)
/* 12. write the heartbeat message */
if (options->HeartbeatPeriod &&
- time_to_next_heartbeat <= now) {
- if (time_to_next_heartbeat) /* don't log the first heartbeat */
+ time_to.next_heartbeat <= now) {
+ if (time_to.next_heartbeat) /* don't log the first heartbeat */
log_heartbeat(now);
- time_to_next_heartbeat = now+options->HeartbeatPeriod;
+ time_to.next_heartbeat = now+options->HeartbeatPeriod;
}
}
@@ -1747,8 +1784,6 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
if (seconds_elapsed < -NUM_JUMPED_SECONDS_BEFORE_WARN ||
seconds_elapsed >= NUM_JUMPED_SECONDS_BEFORE_WARN) {
circuit_note_clock_jumped(seconds_elapsed);
- /* XXX if the time jumps *back* many months, do our events in
- * run_scheduled_events() recover? I don't think they do. -RD */
} else if (seconds_elapsed > 0)
stats_n_seconds_working += seconds_elapsed;
@@ -1872,7 +1907,7 @@ dns_servers_relaunch_checks(void)
{
if (server_mode(get_options())) {
dns_reset_correctness_checks();
- time_to_check_for_correct_dns = 0;
+ time_to.check_for_correct_dns = 0;
}
}
@@ -1997,6 +2032,23 @@ do_main_loop(void)
/* initialize the bootstrap status events to know we're starting up */
control_event_bootstrap(BOOTSTRAP_STATUS_STARTING, 0);
+ /* Initialize the keypinning log. */
+ if (authdir_mode_v3(get_options())) {
+ char *fname = get_datadir_fname("key-pinning-entries");
+ int r = 0;
+ if (keypin_load_journal(fname)<0) {
+ log_err(LD_DIR, "Error loading key-pinning journal: %s",strerror(errno));
+ r = -1;
+ }
+ if (keypin_open_journal(fname)<0) {
+ log_err(LD_DIR, "Error opening key-pinning journal: %s",strerror(errno));
+ r = -1;
+ }
+ tor_free(fname);
+ if (r)
+ return r;
+ }
+
if (trusted_dirs_reload_certs()) {
log_warn(LD_DIR,
"Couldn't load all cached v3 certificates. Starting anyway.");
@@ -2485,10 +2537,11 @@ tor_init(int argc, char *argv[])
if (!strcmp(cl->key, "--quiet") ||
!strcmp(cl->key, "--dump-config"))
quiet = 2;
- /* --version, --digests, and --help imply --hush */
+ /* The following options imply --hush */
if (!strcmp(cl->key, "--version") || !strcmp(cl->key, "--digests") ||
!strcmp(cl->key, "--list-torrc-options") ||
!strcmp(cl->key, "--library-versions") ||
+ !strcmp(cl->key, "--hash-password") ||
!strcmp(cl->key, "-h") || !strcmp(cl->key, "--help")) {
if (quiet < 1)
quiet = 1;
@@ -2676,6 +2729,7 @@ tor_free_all(int postfork)
config_free_all();
or_state_free_all();
router_free_all();
+ routerkeys_free_all();
policies_free_all();
}
if (!postfork) {
@@ -2733,6 +2787,7 @@ tor_cleanup(void)
or_state_save(now);
if (authdir_mode_tests_reachability(options))
rep_hist_record_mtbf_data(now, 0);
+ keypin_close_journal();
}
#ifdef USE_DMALLOC
dmalloc_log_stats();
@@ -2804,6 +2859,7 @@ do_dump_config(void)
const char *arg = options->command_arg;
int how;
char *opts;
+
if (!strcmp(arg, "short")) {
how = OPTIONS_DUMP_MINIMAL;
} else if (!strcmp(arg, "non-builtin")) {
@@ -2811,8 +2867,9 @@ do_dump_config(void)
} else if (!strcmp(arg, "full")) {
how = OPTIONS_DUMP_ALL;
} else {
- printf("%s is not a recognized argument to --dump-config. "
- "Please select 'short', 'non-builtin', or 'full'", arg);
+ fprintf(stderr, "No valid argument to --dump-config found!\n");
+ fprintf(stderr, "Please select 'short', 'non-builtin', or 'full'.\n");
+
return -1;
}
@@ -2985,7 +3042,7 @@ sandbox_init_filter(void)
// orport
if (server_mode(get_options())) {
- OPEN_DATADIR2_SUFFIX("keys", "secret_id_key", "tmp");
+ OPEN_DATADIR2_SUFFIX("keys", "secret_id_key", ".tmp");
OPEN_DATADIR2_SUFFIX("keys", "secret_onion_key", ".tmp");
OPEN_DATADIR2_SUFFIX("keys", "secret_onion_key_ntor", ".tmp");
OPEN_DATADIR2("keys", "secret_id_key.old");
@@ -3113,7 +3170,8 @@ tor_main(int argc, char *argv[])
result = 0;
break;
case CMD_VERIFY_CONFIG:
- printf("Configuration was valid\n");
+ if (quiet_level == 0)
+ printf("Configuration was valid\n");
result = 0;
break;
case CMD_DUMP_CONFIG:
diff --git a/src/or/main.h b/src/or/main.h
index f77b4711c5..542eab6565 100644
--- a/src/or/main.h
+++ b/src/or/main.h
@@ -52,6 +52,7 @@ void directory_info_has_arrived(time_t now, int from_cache);
void ip_address_changed(int at_interface);
void dns_servers_relaunch_checks(void);
+void reset_all_main_loop_timers(void);
void reschedule_descriptor_update_check(void);
MOCK_DECL(long,get_uptime,(void));
diff --git a/src/or/microdesc.c b/src/or/microdesc.c
index 0511e870d1..ee48f6a419 100644
--- a/src/or/microdesc.c
+++ b/src/or/microdesc.c
@@ -738,6 +738,7 @@ microdesc_free_(microdesc_t *md, const char *fname, int lineno)
if (md->onion_pkey)
crypto_pk_free(md->onion_pkey);
tor_free(md->onion_curve25519_pkey);
+ tor_free(md->ed25519_identity_pkey);
if (md->body && md->saved_location != SAVED_IN_CACHE)
tor_free(md->body);
diff --git a/src/or/or.h b/src/or/or.h
index 0d81b54d94..ec5f2774ba 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -88,7 +88,7 @@
#include "crypto.h"
#include "tortls.h"
-#include "../common/torlog.h"
+#include "torlog.h"
#include "container.h"
#include "torgzip.h"
#include "address.h"
@@ -96,6 +96,7 @@
#include "ht.h"
#include "replaycache.h"
#include "crypto_curve25519.h"
+#include "crypto_ed25519.h"
#include "tor_queue.h"
/* These signals are defined to help handle_control_signal work.
@@ -793,17 +794,34 @@ typedef struct rend_data_t {
/** Onion address (without the .onion part) that a client requests. */
char onion_address[REND_SERVICE_ID_LEN_BASE32+1];
+ /** Descriptor ID for each replicas computed from the onion address. If
+ * the onion address is empty, this array MUST be empty. We keep them so
+ * we know when to purge our entry in the last hsdir request table. */
+ char descriptor_id[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS][DIGEST_LEN];
+
/** (Optional) descriptor cookie that is used by a client. */
char descriptor_cookie[REND_DESC_COOKIE_LEN];
/** Authorization type for accessing a service used by a client. */
rend_auth_type_t auth_type;
+ /** Descriptor ID for a client request. The control port command HSFETCH
+ * uses this. It's set if the descriptor query should only use this
+ * descriptor ID. */
+ char desc_id_fetch[DIGEST_LEN];
+
/** Hash of the hidden service's PK used by a service. */
char rend_pk_digest[DIGEST_LEN];
/** Rendezvous cookie used by both, client and service. */
char rend_cookie[REND_COOKIE_LEN];
+
+ /** List of HSDir fingerprints on which this request has been sent to.
+ * This contains binary identity digest of the directory. */
+ smartlist_t *hsdirs_fp;
+
+ /** Number of streams associated with this rendezvous circuit. */
+ int nr_streams;
} rend_data_t;
/** Time interval for tracking replays of DH public keys received in
@@ -1336,6 +1354,8 @@ typedef struct listener_connection_t {
* in the v3 handshake. The subject key must be a 1024-bit RSA key; it
* must be signed by the identity key */
#define OR_CERT_TYPE_AUTH_1024 3
+/** DOCDOC */
+#define OR_CERT_TYPE_RSA_ED_CROSSCERT 7
/**@}*/
/** The one currently supported type of AUTHENTICATE cell. It contains
@@ -1411,9 +1431,9 @@ typedef struct or_handshake_state_t {
* @{
*/
/** The cert for the key that's supposed to sign the AUTHENTICATE cell */
- tor_cert_t *auth_cert;
+ tor_x509_cert_t *auth_cert;
/** A self-signed identity certificate */
- tor_cert_t *id_cert;
+ tor_x509_cert_t *id_cert;
/**@}*/
} or_handshake_state_t;
@@ -1731,6 +1751,9 @@ typedef struct control_connection_t {
* connection. */
unsigned int is_owning_control_connection:1;
+ /** List of ephemeral onion services belonging to this connection. */
+ smartlist_t *ephemeral_onion_services;
+
/** If we have sent an AUTHCHALLENGE reply on this connection and
* have not received a successful AUTHENTICATE command, points to
* the value which the client must send to authenticate itself;
@@ -2003,6 +2026,8 @@ typedef int16_t country_t;
/** Information about another onion router in the network. */
typedef struct {
signed_descriptor_t cache_info;
+ /** A SHA256-digest of the extrainfo (if any) */
+ char extra_info_digest256[DIGEST256_LEN];
char *nickname; /**< Human-readable OR name. */
uint32_t addr; /**< IPv4 address of OR, in host order. */
@@ -2020,6 +2045,11 @@ typedef struct {
crypto_pk_t *identity_pkey; /**< Public RSA key for signing. */
/** Public curve25519 key for onions */
curve25519_public_key_t *onion_curve25519_pkey;
+ /** Certificate for ed25519 signing key */
+ struct tor_cert_st *signing_key_cert;
+ /** What's the earliest expiration time on all the certs in this
+ * routerinfo? */
+ time_t cert_expiration_time;
char *platform; /**< What software/operating system is this OR using? */
@@ -2079,8 +2109,12 @@ typedef struct {
/** Information needed to keep and cache a signed extra-info document. */
typedef struct extrainfo_t {
signed_descriptor_t cache_info;
+ /** SHA256 digest of this document */
+ uint8_t digest256[DIGEST256_LEN];
/** The router's nickname. */
char nickname[MAX_NICKNAME_LEN+1];
+ /** Certificate for ed25519 signing key */
+ struct tor_cert_st *signing_key_cert;
/** True iff we found the right key for this extra-info, verified the
* signature, and found it to be bad. */
unsigned int bad_sig : 1;
@@ -2131,9 +2165,6 @@ typedef struct routerstatus_t {
* if the number of traits we care about ever becomes incredibly big. */
unsigned int version_known:1;
- /** True iff this router is a version that, if it caches directory info,
- * we can get microdescriptors from. */
- unsigned int version_supports_microdesc_cache:1;
/** True iff this router has a version that allows it to accept EXTEND2
* cells */
unsigned int version_supports_extend2_cells:1;
@@ -2228,6 +2259,8 @@ typedef struct microdesc_t {
crypto_pk_t *onion_pkey;
/** As routerinfo_t.onion_curve25519_pkey */
curve25519_public_key_t *onion_curve25519_pkey;
+ /** Ed25519 identity key, if included. */
+ ed25519_public_key_t *ed25519_identity_pkey;
/** As routerinfo_t.ipv6_add */
tor_addr_t ipv6_addr;
/** As routerinfo_t.ipv6_orport */
@@ -2342,9 +2375,13 @@ typedef struct vote_routerstatus_t {
char *version; /**< The version that the authority says this router is
* running. */
unsigned int has_measured_bw:1; /**< The vote had a measured bw */
+ unsigned int has_ed25519_listing:1; /** DOCDOC */
+ unsigned int ed25519_reflects_consensus:1; /** DOCDOC */
uint32_t measured_bw_kb; /**< Measured bandwidth (capacity) of the router */
/** The hash or hashes that the authority claims this microdesc has. */
vote_microdesc_hash_t *microdesc;
+ /** Ed25519 identity for this router, or zero if it has none. */
+ uint8_t ed25519_id[ED25519_PUBKEY_LEN];
} vote_routerstatus_t;
/** A signature of some document by an authority. */
@@ -3387,8 +3424,6 @@ typedef struct {
char *Address; /**< OR only: configured address for this onion router. */
char *PidFile; /**< Where to store PID of Tor process. */
- int DynamicDHGroups; /**< Dynamic generation of prime moduli for use in DH.*/
-
routerset_t *ExitNodes; /**< Structure containing nicknames, digests,
* country codes and IP address patterns of ORs to
* consider as exits. */
@@ -4246,6 +4281,20 @@ typedef struct {
* XXXX Eventually, the default will be 0. */
int ExitRelay;
+ /** For how long (seconds) do we declare our singning keys to be valid? */
+ int SigningKeyLifetime;
+ /** For how long (seconds) do we declare our link keys to be valid? */
+ int TestingLinkCertLifetime;
+ /** For how long (seconds) do we declare our auth keys to be valid? */
+ int TestingAuthKeyLifetime;
+
+ /** How long before signing keys expire will we try to make a new one? */
+ int TestingSigningKeySlop;
+ /** How long before link keys expire will we try to make a new one? */
+ int TestingLinkKeySlop;
+ /** How long before auth keys expire will we try to make a new one? */
+ int TestingAuthKeySlop;
+
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
@@ -4810,12 +4859,13 @@ typedef struct rend_encoded_v2_service_descriptor_t {
* introduction point. See also rend_intro_point_t.unreachable_count. */
#define MAX_INTRO_POINT_REACHABILITY_FAILURES 5
-/** The maximum number of distinct INTRODUCE2 cells which a hidden
- * service's introduction point will receive before it begins to
- * expire.
- *
- * XXX023 Is this number at all sane? */
-#define INTRO_POINT_LIFETIME_INTRODUCTIONS 16384
+/** The minimum and maximum number of distinct INTRODUCE2 cells which a
+ * hidden service's introduction point will receive before it begins to
+ * expire. */
+#define INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS 16384
+/* Double the minimum value so the interval is [min, min * 2]. */
+#define INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS \
+ (INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS * 2)
/** The minimum number of seconds that an introduction point will last
* before expiring due to old age. (If it receives
@@ -4869,6 +4919,12 @@ typedef struct rend_intro_point_t {
*/
int accepted_introduce2_count;
+ /** (Service side only) Number of maximum INTRODUCE2 cells that this IP
+ * will accept. This is a random value between
+ * INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS and
+ * INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS. */
+ int max_introductions;
+
/** (Service side only) The time at which this intro point was first
* published, or -1 if this intro point has not yet been
* published. */
@@ -5043,6 +5099,8 @@ typedef enum was_router_added_t {
/* Router descriptor was rejected because it was older than
* OLD_ROUTER_DESC_MAX_AGE. */
ROUTER_WAS_TOO_OLD = -7, /* note contrast with 'NOT_NEW' */
+ /* DOCDOC */
+ ROUTER_CERTS_EXPIRED = -8
} was_router_added_t;
/********************************* routerparse.c ************************/
diff --git a/src/or/rendclient.c b/src/or/rendclient.c
index 162e0ac53e..59e938e89c 100644
--- a/src/or/rendclient.c
+++ b/src/or/rendclient.c
@@ -141,7 +141,7 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
int r, v3_shift = 0;
char payload[RELAY_PAYLOAD_SIZE];
char tmp[RELAY_PAYLOAD_SIZE];
- rend_cache_entry_t *entry;
+ rend_cache_entry_t *entry = NULL;
crypt_path_t *cpath;
off_t dh_offset;
crypto_pk_t *intro_key = NULL;
@@ -158,8 +158,13 @@ rend_client_send_introduction(origin_circuit_t *introcirc,
tor_assert(!(rendcirc->build_state->onehop_tunnel));
#endif
- if (rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
- &entry) < 1) {
+ r = rend_cache_lookup_entry(introcirc->rend_data->onion_address, -1,
+ &entry);
+ /* An invalid onion address is not possible else we have a big issue. */
+ tor_assert(r != -EINVAL);
+ if (r < 0 || !rend_client_any_intro_points_usable(entry)) {
+ /* If the descriptor is not found or the intro points are not usable
+ * anymore, trigger a fetch. */
log_info(LD_REND,
"query %s didn't have valid rend desc in cache. "
"Refetching descriptor.",
@@ -469,9 +474,8 @@ rend_client_introduction_acked(origin_circuit_t *circ,
/** Contains the last request times to hidden service directories for
* certain queries; each key is a string consisting of the
- * concatenation of a base32-encoded HS directory identity digest, a
- * base32-encoded HS descriptor ID, and a hidden service address
- * (without the ".onion" part); each value is a pointer to a time_t
+ * concatenation of a base32-encoded HS directory identity digest and
+ * base32-encoded HS descriptor ID; each value is a pointer to a time_t
* holding the time of the last request for that descriptor ID to that
* HS directory. */
static strmap_t *last_hid_serv_requests_ = NULL;
@@ -487,19 +491,16 @@ get_last_hid_serv_requests(void)
}
#define LAST_HID_SERV_REQUEST_KEY_LEN (REND_DESC_ID_V2_LEN_BASE32 + \
- REND_DESC_ID_V2_LEN_BASE32 + \
- REND_SERVICE_ID_LEN_BASE32)
+ REND_DESC_ID_V2_LEN_BASE32)
/** Look up the last request time to hidden service directory <b>hs_dir</b>
- * for descriptor ID <b>desc_id_base32</b> for the service specified in
- * <b>rend_query</b>. If <b>set</b> is non-zero,
- * assign the current time <b>now</b> and return that. Otherwise, return
- * the most recent request time, or 0 if no such request has been sent
- * before. */
+ * for descriptor ID <b>desc_id_base32</b>. If <b>set</b> is non-zero,
+ * assign the current time <b>now</b> and return that. Otherwise, return the
+ * most recent request time, or 0 if no such request has been sent before.
+ */
static time_t
lookup_last_hid_serv_request(routerstatus_t *hs_dir,
const char *desc_id_base32,
- const rend_data_t *rend_query,
time_t now, int set)
{
char hsdir_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
@@ -508,10 +509,9 @@ lookup_last_hid_serv_request(routerstatus_t *hs_dir,
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
base32_encode(hsdir_id_base32, sizeof(hsdir_id_base32),
hs_dir->identity_digest, DIGEST_LEN);
- tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s%s",
+ tor_snprintf(hsdir_desc_comb_id, sizeof(hsdir_desc_comb_id), "%s%s",
hsdir_id_base32,
- desc_id_base32,
- rend_query->onion_address);
+ desc_id_base32);
/* XXX023 tor_assert(strlen(hsdir_desc_comb_id) ==
LAST_HID_SERV_REQUEST_KEY_LEN); */
if (set) {
@@ -552,20 +552,23 @@ directory_clean_last_hid_serv_requests(time_t now)
}
}
-/** Remove all requests related to the hidden service named
- * <b>onion_address</b> from the history of times of requests to
- * hidden service directories.
+/** Remove all requests related to the descriptor ID <b>desc_id</b> from the
+ * history of times of requests to hidden service directories.
+ * <b>desc_id</b> is an unencoded descriptor ID of size DIGEST_LEN.
*
* This is called from rend_client_note_connection_attempt_ended(), which
- * must be idempotent, so any future changes to this function must leave
- * it idempotent too.
- */
+ * must be idempotent, so any future changes to this function must leave it
+ * idempotent too. */
static void
-purge_hid_serv_from_last_hid_serv_requests(const char *onion_address)
+purge_hid_serv_from_last_hid_serv_requests(const char *desc_id)
{
strmap_iter_t *iter;
strmap_t *last_hid_serv_requests = get_last_hid_serv_requests();
- /* XXX023 tor_assert(strlen(onion_address) == REND_SERVICE_ID_LEN_BASE32); */
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+
+ /* Key is stored with the base32 encoded desc_id. */
+ base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_id,
+ DIGEST_LEN);
for (iter = strmap_iter_init(last_hid_serv_requests);
!strmap_iter_done(iter); ) {
const char *key;
@@ -573,9 +576,9 @@ purge_hid_serv_from_last_hid_serv_requests(const char *onion_address)
strmap_iter_get(iter, &key, &val);
/* XXX023 tor_assert(strlen(key) == LAST_HID_SERV_REQUEST_KEY_LEN); */
if (tor_memeq(key + LAST_HID_SERV_REQUEST_KEY_LEN -
- REND_SERVICE_ID_LEN_BASE32,
- onion_address,
- REND_SERVICE_ID_LEN_BASE32)) {
+ REND_DESC_ID_V2_LEN_BASE32,
+ desc_id_base32,
+ REND_DESC_ID_V2_LEN_BASE32)) {
iter = strmap_iter_next_rmv(last_hid_serv_requests, iter);
tor_free(val);
} else {
@@ -604,64 +607,53 @@ rend_client_purge_last_hid_serv_requests(void)
}
}
-/** Determine the responsible hidden service directories for <b>desc_id</b>
- * and fetch the descriptor with that ID from one of them. Only
- * send a request to a hidden service directory that we have not yet tried
- * during this attempt to connect to this hidden service; on success, return 1,
- * in the case that no hidden service directory is left to ask for the
- * descriptor, return 0, and in case of a failure -1. */
-static int
-directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
+/** This returns a good valid hs dir that should be used for the given
+ * descriptor id.
+ *
+ * Return NULL on error else the hsdir node pointer. */
+static routerstatus_t *
+pick_hsdir(const char *desc_id, const char *desc_id_base32)
{
smartlist_t *responsible_dirs = smartlist_new();
smartlist_t *usable_responsible_dirs = smartlist_new();
const or_options_t *options = get_options();
routerstatus_t *hs_dir;
- char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
time_t now = time(NULL);
- char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
-#ifdef ENABLE_TOR2WEB_MODE
- const int tor2web_mode = options->Tor2webMode;
- const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS;
-#else
- const int how_to_fetch = DIRIND_ANONYMOUS;
-#endif
int excluded_some;
- tor_assert(desc_id);
- tor_assert(rend_query);
- /* Determine responsible dirs. Even if we can't get all we want,
- * work with the ones we have. If it's empty, we'll notice below. */
- hid_serv_get_responsible_directories(responsible_dirs, desc_id);
- base32_encode(desc_id_base32, sizeof(desc_id_base32),
- desc_id, DIGEST_LEN);
+ tor_assert(desc_id);
+ tor_assert(desc_id_base32);
- /* Only select those hidden service directories to which we did not send
- * a request recently and for which we have a router descriptor here. */
+ /* Determine responsible dirs. Even if we can't get all we want, work with
+ * the ones we have. If it's empty, we'll notice below. */
+ hid_serv_get_responsible_directories(responsible_dirs, desc_id);
/* Clean request history first. */
directory_clean_last_hid_serv_requests(now);
- SMARTLIST_FOREACH(responsible_dirs, routerstatus_t *, dir, {
- time_t last = lookup_last_hid_serv_request(
- dir, desc_id_base32, rend_query, 0, 0);
- const node_t *node = node_get_by_id(dir->identity_digest);
- if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
- !node || !node_has_descriptor(node)) {
- SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
- continue;
- }
- if (! routerset_contains_node(options->ExcludeNodes, node)) {
- smartlist_add(usable_responsible_dirs, dir);
- }
- });
+ /* Only select those hidden service directories to which we did not send a
+ * request recently and for which we have a router descriptor here. */
+ SMARTLIST_FOREACH_BEGIN(responsible_dirs, routerstatus_t *, dir) {
+ time_t last = lookup_last_hid_serv_request(dir, desc_id_base32,
+ 0, 0);
+ const node_t *node = node_get_by_id(dir->identity_digest);
+ if (last + REND_HID_SERV_DIR_REQUERY_PERIOD >= now ||
+ !node || !node_has_descriptor(node)) {
+ SMARTLIST_DEL_CURRENT(responsible_dirs, dir);
+ continue;
+ }
+ if (!routerset_contains_node(options->ExcludeNodes, node)) {
+ smartlist_add(usable_responsible_dirs, dir);
+ }
+ } SMARTLIST_FOREACH_END(dir);
excluded_some =
smartlist_len(usable_responsible_dirs) < smartlist_len(responsible_dirs);
hs_dir = smartlist_choose(usable_responsible_dirs);
- if (! hs_dir && ! options->StrictNodes)
+ if (!hs_dir && !options->StrictNodes) {
hs_dir = smartlist_choose(responsible_dirs);
+ }
smartlist_free(responsible_dirs);
smartlist_free(usable_responsible_dirs);
@@ -674,23 +666,69 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
"requested hidden service: they are all either down or "
"excluded, and StrictNodes is set.");
}
- return 0;
+ } else {
+ /* Remember that we are requesting a descriptor from this hidden service
+ * directory now. */
+ lookup_last_hid_serv_request(hs_dir, desc_id_base32, now, 1);
+ }
+
+ return hs_dir;
+}
+
+/** Determine the responsible hidden service directories for <b>desc_id</b>
+ * and fetch the descriptor with that ID from one of them. Only
+ * send a request to a hidden service directory that we have not yet tried
+ * during this attempt to connect to this hidden service; on success, return 1,
+ * in the case that no hidden service directory is left to ask for the
+ * descriptor, return 0, and in case of a failure -1. */
+static int
+directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query,
+ routerstatus_t *rs_hsdir)
+{
+ routerstatus_t *hs_dir = rs_hsdir;
+ char *hsdir_fp;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
+ char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
+#ifdef ENABLE_TOR2WEB_MODE
+ const int tor2web_mode = get_options()->Tor2webMode;
+ const int how_to_fetch = tor2web_mode ? DIRIND_ONEHOP : DIRIND_ANONYMOUS;
+#else
+ const int how_to_fetch = DIRIND_ANONYMOUS;
+#endif
+
+ tor_assert(desc_id);
+
+ base32_encode(desc_id_base32, sizeof(desc_id_base32),
+ desc_id, DIGEST_LEN);
+
+ /* Automatically pick an hs dir if none given. */
+ if (!rs_hsdir) {
+ hs_dir = pick_hsdir(desc_id, desc_id_base32);
+ if (!hs_dir) {
+ /* No suitable hs dir can be found, stop right now. */
+ return 0;
+ }
}
- /* Remember that we are requesting a descriptor from this hidden service
- * directory now. */
- lookup_last_hid_serv_request(hs_dir, desc_id_base32, rend_query, now, 1);
+ /* Add a copy of the HSDir identity digest to the query so we can track it
+ * on the control port. */
+ hsdir_fp = tor_memdup(hs_dir->identity_digest,
+ sizeof(hs_dir->identity_digest));
+ smartlist_add(rend_query->hsdirs_fp, hsdir_fp);
- /* Encode descriptor cookie for logging purposes. */
+ /* Encode descriptor cookie for logging purposes. Also, if the cookie is
+ * malformed, no fetch is triggered thus this needs to be done before the
+ * fetch request. */
if (rend_query->auth_type != REND_NO_AUTH) {
if (base64_encode(descriptor_cookie_base64,
sizeof(descriptor_cookie_base64),
- rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN)<0) {
+ rend_query->descriptor_cookie, REND_DESC_COOKIE_LEN,
+ 0)<0) {
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
return 0;
}
- /* Remove == signs and newline. */
- descriptor_cookie_base64[strlen(descriptor_cookie_base64)-3] = '\0';
+ /* Remove == signs. */
+ descriptor_cookie_base64[strlen(descriptor_cookie_base64)-2] = '\0';
} else {
strlcpy(descriptor_cookie_base64, "(none)",
sizeof(descriptor_cookie_base64));
@@ -721,16 +759,144 @@ directory_get_from_hs_dir(const char *desc_id, const rend_data_t *rend_query)
return 1;
}
+/** Fetch a v2 descriptor using the given descriptor id. If any hsdir(s) are
+ * given, they will be used instead.
+ *
+ * On success, 1 is returned. If no hidden service is left to ask, return 0.
+ * On error, -1 is returned. */
+static int
+fetch_v2_desc_by_descid(const char *desc_id, const rend_data_t *rend_query,
+ smartlist_t *hsdirs)
+{
+ int ret;
+
+ tor_assert(rend_query);
+
+ if (!hsdirs) {
+ ret = directory_get_from_hs_dir(desc_id, rend_query, NULL);
+ goto end; /* either success or failure, but we're done */
+ }
+
+ /* Using the given hsdir list, trigger a fetch on each of them. */
+ SMARTLIST_FOREACH_BEGIN(hsdirs, routerstatus_t *, hs_dir) {
+ /* This should always be a success. */
+ ret = directory_get_from_hs_dir(desc_id, rend_query, hs_dir);
+ tor_assert(ret);
+ } SMARTLIST_FOREACH_END(hs_dir);
+
+ /* Everything went well. */
+ ret = 0;
+
+ end:
+ return ret;
+}
+
+/** Fetch a v2 descriptor using the onion address in the given query object.
+ * This will compute the descriptor id for each replicas and fetch it on the
+ * given hsdir(s) if any or the responsible ones that are choosen
+ * automatically.
+ *
+ * On success, 1 is returned. If no hidden service is left to ask, return 0.
+ * On error, -1 is returned. */
+static int
+fetch_v2_desc_by_addr(rend_data_t *query, smartlist_t *hsdirs)
+{
+ char descriptor_id[DIGEST_LEN];
+ int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
+ int i, tries_left, ret;
+
+ tor_assert(query);
+
+ /* Randomly iterate over the replicas until a descriptor can be fetched
+ * from one of the consecutive nodes, or no options are left. */
+ for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++) {
+ replicas_left_to_try[i] = i;
+ }
+
+ tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
+ while (tries_left > 0) {
+ int rand = crypto_rand_int(tries_left);
+ int chosen_replica = replicas_left_to_try[rand];
+ replicas_left_to_try[rand] = replicas_left_to_try[--tries_left];
+
+ ret = rend_compute_v2_desc_id(descriptor_id, query->onion_address,
+ query->auth_type == REND_STEALTH_AUTH ?
+ query->descriptor_cookie : NULL,
+ time(NULL), chosen_replica);
+ if (ret < 0) {
+ /* Normally, on failure the descriptor_id is untouched but let's be
+ * safe in general in case the function changes at some point. */
+ goto end;
+ }
+
+ if (tor_memcmp(descriptor_id, query->descriptor_id[chosen_replica],
+ sizeof(descriptor_id)) != 0) {
+ /* Not equal from what we currently have so purge the last hid serv
+ * request cache and update the descriptor ID with the new value. */
+ purge_hid_serv_from_last_hid_serv_requests(
+ query->descriptor_id[chosen_replica]);
+ memcpy(query->descriptor_id[chosen_replica], descriptor_id,
+ sizeof(query->descriptor_id[chosen_replica]));
+ }
+
+ /* Trigger the fetch with the computed descriptor ID. */
+ ret = fetch_v2_desc_by_descid(descriptor_id, query, hsdirs);
+ if (ret != 0) {
+ /* Either on success or failure, as long as we tried a fetch we are
+ * done here. */
+ goto end;
+ }
+ }
+
+ /* If we come here, there are no hidden service directories left. */
+ log_info(LD_REND, "Could not pick one of the responsible hidden "
+ "service directories to fetch descriptors, because "
+ "we already tried them all unsuccessfully.");
+ ret = 0;
+
+ end:
+ memwipe(descriptor_id, 0, sizeof(descriptor_id));
+ return ret;
+}
+
+/** Fetch a v2 descriptor using the given query. If any hsdir are specified,
+ * use them for the fetch.
+ *
+ * On success, 1 is returned. If no hidden service is left to ask, return 0.
+ * On error, -1 is returned. */
+int
+rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs)
+{
+ int ret;
+
+ tor_assert(query);
+
+ /* Depending on what's available in the rend data query object, we will
+ * trigger a fetch by HS address or using a descriptor ID. */
+
+ if (query->onion_address[0] != '\0') {
+ ret = fetch_v2_desc_by_addr(query, hsdirs);
+ } else if (!tor_digest_is_zero(query->desc_id_fetch)) {
+ ret = fetch_v2_desc_by_descid(query->desc_id_fetch, query, hsdirs);
+ } else {
+ /* Query data is invalid. */
+ ret = -1;
+ goto error;
+ }
+
+ error:
+ return ret;
+}
+
/** Unless we already have a descriptor for <b>rend_query</b> with at least
* one (possibly) working introduction point in it, start a connection to a
* hidden service directory to fetch a v2 rendezvous service descriptor. */
void
-rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
+rend_client_refetch_v2_renddesc(rend_data_t *rend_query)
{
- char descriptor_id[DIGEST_LEN];
- int replicas_left_to_try[REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS];
- int i, tries_left;
+ int ret;
rend_cache_entry_t *e = NULL;
+
tor_assert(rend_query);
/* Are we configured to fetch descriptors? */
if (!get_options()->FetchHidServDescriptors) {
@@ -739,7 +905,7 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
return;
}
/* Before fetching, check if we already have a usable descriptor here. */
- if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) > 0 &&
+ if (rend_cache_lookup_entry(rend_query->onion_address, -1, &e) == 0 &&
rend_client_any_intro_points_usable(e)) {
log_info(LD_REND, "We would fetch a v2 rendezvous descriptor, but we "
"already have a usable descriptor here. Not fetching.");
@@ -747,44 +913,12 @@ rend_client_refetch_v2_renddesc(const rend_data_t *rend_query)
}
log_debug(LD_REND, "Fetching v2 rendezvous descriptor for service %s",
safe_str_client(rend_query->onion_address));
- /* Randomly iterate over the replicas until a descriptor can be fetched
- * from one of the consecutive nodes, or no options are left. */
- tries_left = REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS;
- for (i = 0; i < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; i++)
- replicas_left_to_try[i] = i;
- while (tries_left > 0) {
- int rand = crypto_rand_int(tries_left);
- int chosen_replica = replicas_left_to_try[rand];
- replicas_left_to_try[rand] = replicas_left_to_try[--tries_left];
- if (rend_compute_v2_desc_id(descriptor_id, rend_query->onion_address,
- rend_query->auth_type == REND_STEALTH_AUTH ?
- rend_query->descriptor_cookie : NULL,
- time(NULL), chosen_replica) < 0) {
- log_warn(LD_REND, "Internal error: Computing v2 rendezvous "
- "descriptor ID did not succeed.");
- /*
- * Hmm, can this write anything to descriptor_id and still fail?
- * Let's clear it just to be safe.
- *
- * From here on, any returns should goto done which clears
- * descriptor_id so we don't leave key-derived material on the stack.
- */
- goto done;
- }
- if (directory_get_from_hs_dir(descriptor_id, rend_query) != 0)
- goto done; /* either success or failure, but we're done */
+ ret = rend_client_fetch_v2_desc(rend_query, NULL);
+ if (ret <= 0) {
+ /* Close pending connections on error or if no hsdir can be found. */
+ rend_client_desc_trynow(rend_query->onion_address);
}
- /* If we come here, there are no hidden service directories left. */
- log_info(LD_REND, "Could not pick one of the responsible hidden "
- "service directories to fetch descriptors, because "
- "we already tried them all unsuccessfully.");
- /* Close pending connections. */
- rend_client_desc_trynow(rend_query->onion_address);
-
- done:
- memwipe(descriptor_id, 0, sizeof(descriptor_id));
-
return;
}
@@ -845,7 +979,7 @@ rend_client_cancel_descriptor_fetches(void)
*/
int
rend_client_report_intro_point_failure(extend_info_t *failed_intro,
- const rend_data_t *rend_query,
+ rend_data_t *rend_query,
unsigned int failure_type)
{
int i, r;
@@ -853,17 +987,26 @@ rend_client_report_intro_point_failure(extend_info_t *failed_intro,
connection_t *conn;
r = rend_cache_lookup_entry(rend_query->onion_address, -1, &ent);
- if (r<0) {
- log_warn(LD_BUG, "Malformed service ID %s.",
- escaped_safe_str_client(rend_query->onion_address));
- return -1;
- }
- if (r==0) {
- log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.",
- escaped_safe_str_client(rend_query->onion_address));
- rend_client_refetch_v2_renddesc(rend_query);
- return 0;
+ if (r < 0) {
+ /* Either invalid onion address or cache entry not found. */
+ switch (-r) {
+ case EINVAL:
+ log_warn(LD_BUG, "Malformed service ID %s.",
+ escaped_safe_str_client(rend_query->onion_address));
+ return -1;
+ case ENOENT:
+ log_info(LD_REND, "Unknown service %s. Re-fetching descriptor.",
+ escaped_safe_str_client(rend_query->onion_address));
+ rend_client_refetch_v2_renddesc(rend_query);
+ return 0;
+ default:
+ log_warn(LD_BUG, "Unknown cache lookup returned code: %d", r);
+ return -1;
+ }
}
+ /* The intro points are not checked here if they are usable or not because
+ * this is called when an intro point circuit is closed thus there must be
+ * at least one intro point that is usable and is about to be flagged. */
for (i = 0; i < smartlist_len(ent->parsed->intro_nodes); i++) {
rend_intro_point_t *intro = smartlist_get(ent->parsed->intro_nodes, i);
@@ -1062,7 +1205,7 @@ rend_client_desc_trynow(const char *query)
continue;
assert_connection_ok(base_conn, now);
if (rend_cache_lookup_entry(rend_data->onion_address, -1,
- &entry) == 1 &&
+ &entry) == 0 &&
rend_client_any_intro_points_usable(entry)) {
/* either this fetch worked, or it failed but there was a
* valid entry from before which we should reuse */
@@ -1086,27 +1229,28 @@ rend_client_desc_trynow(const char *query)
"unavailable (try again later).",
safe_str_client(query));
connection_mark_unattached_ap(conn, END_STREAM_REASON_RESOLVEFAILED);
- rend_client_note_connection_attempt_ended(query);
+ rend_client_note_connection_attempt_ended(rend_data);
}
} SMARTLIST_FOREACH_END(base_conn);
}
-/** Clear temporary state used only during an attempt to connect to
- * the hidden service named <b>onion_address</b>. Called when a
- * connection attempt has ended; it is possible for this to be called
- * multiple times while handling an ended connection attempt, and
- * any future changes to this function must ensure it remains
- * idempotent.
- */
+/** Clear temporary state used only during an attempt to connect to the
+ * hidden service with <b>rend_data</b>. Called when a connection attempt
+ * has ended; it is possible for this to be called multiple times while
+ * handling an ended connection attempt, and any future changes to this
+ * function must ensure it remains idempotent. */
void
-rend_client_note_connection_attempt_ended(const char *onion_address)
+rend_client_note_connection_attempt_ended(const rend_data_t *rend_data)
{
+ unsigned int have_onion = 0;
rend_cache_entry_t *cache_entry = NULL;
- rend_cache_lookup_entry(onion_address, -1, &cache_entry);
- log_info(LD_REND, "Connection attempt for %s has ended; "
- "cleaning up temporary state.",
- safe_str_client(onion_address));
+ if (*rend_data->onion_address != '\0') {
+ /* Ignore return value; we find an entry, or we don't. */
+ (void) rend_cache_lookup_entry(rend_data->onion_address, -1,
+ &cache_entry);
+ have_onion = 1;
+ }
/* Clear the timed_out flag on all remaining intro points for this HS. */
if (cache_entry != NULL) {
@@ -1116,7 +1260,20 @@ rend_client_note_connection_attempt_ended(const char *onion_address)
}
/* Remove the HS's entries in last_hid_serv_requests. */
- purge_hid_serv_from_last_hid_serv_requests(onion_address);
+ if (have_onion) {
+ unsigned int replica;
+ for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id);
+ replica++) {
+ const char *desc_id = rend_data->descriptor_id[replica];
+ purge_hid_serv_from_last_hid_serv_requests(desc_id);
+ }
+ log_info(LD_REND, "Connection attempt for %s has ended; "
+ "cleaning up temporary state.",
+ safe_str_client(rend_data->onion_address));
+ } else {
+ /* We only have an ID for a fetch. Probably used by HSFETCH. */
+ purge_hid_serv_from_last_hid_serv_requests(rend_data->desc_id_fetch);
+ }
}
/** Return a newly allocated extend_info_t* for a randomly chosen introduction
@@ -1126,13 +1283,17 @@ rend_client_note_connection_attempt_ended(const char *onion_address)
extend_info_t *
rend_client_get_random_intro(const rend_data_t *rend_query)
{
+ int ret;
extend_info_t *result;
rend_cache_entry_t *entry;
- if (rend_cache_lookup_entry(rend_query->onion_address, -1, &entry) < 1) {
- log_warn(LD_REND,
- "Query '%s' didn't have valid rend desc in cache. Failing.",
- safe_str_client(rend_query->onion_address));
+ ret = rend_cache_lookup_entry(rend_query->onion_address, -1, &entry);
+ if (ret < 0 || !rend_client_any_intro_points_usable(entry)) {
+ log_warn(LD_REND,
+ "Query '%s' didn't have valid rend desc in cache. Failing.",
+ safe_str_client(rend_query->onion_address));
+ /* XXX: Should we refetch the descriptor here if the IPs are not usable
+ * anymore ?. */
return NULL;
}
diff --git a/src/or/rendclient.h b/src/or/rendclient.h
index 098c61d0a1..6118924e1d 100644
--- a/src/or/rendclient.h
+++ b/src/or/rendclient.h
@@ -19,7 +19,8 @@ void rend_client_rendcirc_has_opened(origin_circuit_t *circ);
int rend_client_introduction_acked(origin_circuit_t *circ,
const uint8_t *request,
size_t request_len);
-void rend_client_refetch_v2_renddesc(const rend_data_t *rend_query);
+void rend_client_refetch_v2_renddesc(rend_data_t *rend_query);
+int rend_client_fetch_v2_desc(rend_data_t *query, smartlist_t *hsdirs);
void rend_client_cancel_descriptor_fetches(void);
void rend_client_purge_last_hid_serv_requests(void);
@@ -28,7 +29,7 @@ void rend_client_purge_last_hid_serv_requests(void);
#define INTRO_POINT_FAILURE_UNREACHABLE 2
int rend_client_report_intro_point_failure(extend_info_t *failed_intro,
- const rend_data_t *rend_query,
+ rend_data_t *rend_query,
unsigned int failure_type);
int rend_client_rendezvous_acked(origin_circuit_t *circ,
@@ -39,7 +40,7 @@ int rend_client_receive_rendezvous(origin_circuit_t *circ,
size_t request_len);
void rend_client_desc_trynow(const char *query);
-void rend_client_note_connection_attempt_ended(const char *onion_address);
+void rend_client_note_connection_attempt_ended(const rend_data_t *rend_data);
extend_info_t *rend_client_get_random_intro(const rend_data_t *rend_query);
int rend_client_any_intro_points_usable(const rend_cache_entry_t *entry);
@@ -51,7 +52,6 @@ int rend_parse_service_authorization(const or_options_t *options,
rend_service_authorization_t *rend_client_lookup_service_authorization(
const char *onion_address);
void rend_service_authorization_free_all(void);
-rend_data_t *rend_data_dup(const rend_data_t *request);
#endif
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c
index 5fdd13efce..0acca58713 100644
--- a/src/or/rendcommon.c
+++ b/src/or/rendcommon.c
@@ -155,10 +155,10 @@ rend_compute_v2_desc_id(char *desc_id_out, const char *service_id,
}
/* Calculate current time-period. */
time_period = get_time_period(now, 0, service_id_binary);
- /* Calculate secret-id-part = h(time-period | replica). */
+ /* Calculate secret-id-part = h(time-period | desc-cookie | replica). */
get_secret_id_part_bytes(secret_id_part, time_period, descriptor_cookie,
replica);
- /* Calculate descriptor ID. */
+ /* Calculate descriptor ID: H(permanent-id | secret-id-part) */
rend_get_descriptor_id_bytes(desc_id_out, service_id_binary, secret_id_part);
return 0;
}
@@ -529,7 +529,8 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
}
/* Base64-encode introduction points. */
ipos_base64 = tor_calloc(ipos_len, 2);
- if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len)<0) {
+ if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len,
+ BASE64_ENCODE_MULTILINE)<0) {
log_warn(LD_REND, "Could not encode introduction point string to "
"base64. length=%d", (int)ipos_len);
tor_free(ipos_base64);
@@ -646,7 +647,6 @@ rend_encode_v2_descriptors(smartlist_t *descs_out,
rend_encoded_v2_service_descriptor_free(enc);
goto err;
}
- desc_str[written++] = '\n';
desc_str[written++] = 0;
/* Check if we can parse our own descriptor. */
if (!rend_desc_v2_is_parsable(enc)) {
@@ -920,36 +920,70 @@ rend_valid_service_id(const char *query)
return 1;
}
-/** If we have a cached rend_cache_entry_t for the service ID <b>query</b>
- * with <b>version</b>, set *<b>e</b> to that entry and return 1.
- * Else return 0. If <b>version</b> is nonnegative, only return an entry
- * in that descriptor format version. Otherwise (if <b>version</b> is
- * negative), return the most recent format we have.
- */
+/** Return true iff <b>query</b> is a syntactically valid descriptor ID.
+ * (as generated by rend_get_descriptor_id_bytes). */
+int
+rend_valid_descriptor_id(const char *query)
+{
+ if (strlen(query) != REND_DESC_ID_V2_LEN_BASE32) {
+ goto invalid;
+ }
+ if (strspn(query, BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) {
+ goto invalid;
+ }
+
+ return 1;
+
+ invalid:
+ return 0;
+}
+
+/** Lookup in the client cache the given service ID <b>query</b> for
+ * <b>version</b>.
+ *
+ * Return 0 if found and if <b>e</b> is non NULL, set it with the entry
+ * found. Else, a negative value is returned and <b>e</b> is untouched.
+ * -EINVAL means that <b>query</b> is not a valid service id.
+ * -ENOENT means that no entry in the cache was found. */
int
rend_cache_lookup_entry(const char *query, int version, rend_cache_entry_t **e)
{
- char key[REND_SERVICE_ID_LEN_BASE32+2]; /* <version><query>\0 */
+ int ret = 0;
+ char key[REND_SERVICE_ID_LEN_BASE32 + 2]; /* <version><query>\0 */
+ rend_cache_entry_t *entry = NULL;
+ static const int default_version = 2;
+
tor_assert(rend_cache);
- if (!rend_valid_service_id(query))
- return -1;
- *e = NULL;
- if (version != 0) {
- tor_snprintf(key, sizeof(key), "2%s", query);
- *e = strmap_get_lc(rend_cache, key);
+ tor_assert(query);
+
+ if (!rend_valid_service_id(query)) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ switch (version) {
+ case 0:
+ log_warn(LD_REND, "Cache lookup of a v0 renddesc is deprecated.");
+ break;
+ case 2:
+ /* Default is version 2. */
+ default:
+ tor_snprintf(key, sizeof(key), "%d%s", default_version, query);
+ entry = strmap_get_lc(rend_cache, key);
+ break;
}
- if (!*e && version != 2) {
- tor_snprintf(key, sizeof(key), "0%s", query);
- *e = strmap_get_lc(rend_cache, key);
+ if (!entry) {
+ ret = -ENOENT;
+ goto end;
}
- if (!*e)
- return 0;
- tor_assert((*e)->parsed && (*e)->parsed->intro_nodes);
- /* XXX023 hack for now, to return "not found" if there are no intro
- * points remaining. See bug 997. */
- if (! rend_client_any_intro_points_usable(*e))
- return 0;
- return 1;
+ tor_assert(entry->parsed && entry->parsed->intro_nodes);
+
+ if (e) {
+ *e = entry;
+ }
+
+ end:
+ return ret;
}
/** Lookup the v2 service descriptor with base32-encoded <b>desc_id</b> and
@@ -1121,12 +1155,14 @@ rend_cache_store_v2_desc_as_dir(const char *desc)
* If the descriptor's descriptor ID doesn't match <b>desc_id_base32</b>,
* reject it.
*
- * Return an appropriate rend_cache_store_status_t.
+ * Return an appropriate rend_cache_store_status_t. If entry is not NULL,
+ * set it with the cache entry pointer of the descriptor.
*/
rend_cache_store_status_t
rend_cache_store_v2_desc_as_client(const char *desc,
const char *desc_id_base32,
- const rend_data_t *rend_query)
+ const rend_data_t *rend_query,
+ rend_cache_entry_t **entry)
{
/*XXXX this seems to have a bit of duplicate code with
* rend_cache_store_v2_desc_as_dir(). Fix that. */
@@ -1159,6 +1195,9 @@ rend_cache_store_v2_desc_as_client(const char *desc,
tor_assert(desc);
tor_assert(desc_id_base32);
memset(want_desc_id, 0, sizeof(want_desc_id));
+ if (entry) {
+ *entry = NULL;
+ }
if (base32_decode(want_desc_id, sizeof(want_desc_id),
desc_id_base32, strlen(desc_id_base32)) != 0) {
log_warn(LD_BUG, "Couldn't decode base32 %s for descriptor id.",
@@ -1177,7 +1216,8 @@ rend_cache_store_v2_desc_as_client(const char *desc,
log_warn(LD_REND, "Couldn't compute service ID.");
goto err;
}
- if (strcmp(rend_query->onion_address, service_id)) {
+ if (rend_query->onion_address[0] != '\0' &&
+ strcmp(rend_query->onion_address, service_id)) {
log_warn(LD_REND, "Received service descriptor for service ID %s; "
"expected descriptor for service ID %s.",
service_id, safe_str(rend_query->onion_address));
@@ -1224,7 +1264,7 @@ rend_cache_store_v2_desc_as_client(const char *desc,
"service descriptor for %s. This is probably a (misguided) "
"attempt to improve reliability, but it could also be an "
"attempt to do a guard enumeration attack. Rejecting.",
- safe_str_client(rend_query->onion_address));
+ safe_str_client(service_id));
goto err;
}
@@ -1270,9 +1310,15 @@ rend_cache_store_v2_desc_as_client(const char *desc,
rend_cache_increment_allocation(rend_cache_entry_allocation(e));
log_debug(LD_REND,"Successfully stored rend desc '%s', len %d.",
safe_str_client(service_id), (int)encoded_size);
+ if (entry) {
+ *entry = e;
+ }
return RCS_OKAY;
okay:
+ if (entry) {
+ *entry = e;
+ }
retval = RCS_OKAY;
err:
@@ -1354,7 +1400,116 @@ rend_process_relay_cell(circuit_t *circ, const crypt_path_t *layer_hint,
rend_data_t *
rend_data_dup(const rend_data_t *data)
{
+ rend_data_t *data_dup;
tor_assert(data);
- return tor_memdup(data, sizeof(rend_data_t));
+ data_dup = tor_memdup(data, sizeof(rend_data_t));
+ data_dup->hsdirs_fp = smartlist_new();
+ SMARTLIST_FOREACH(data->hsdirs_fp, char *, fp,
+ smartlist_add(data_dup->hsdirs_fp,
+ tor_memdup(fp, DIGEST_LEN)));
+ return data_dup;
+}
+
+/** Compute descriptor ID for each replicas and save them. A valid onion
+ * address must be present in the <b>rend_data</b>.
+ *
+ * Return 0 on success else -1. */
+static int
+compute_desc_id(rend_data_t *rend_data)
+{
+ int ret = 0;
+ unsigned replica;
+ time_t now = time(NULL);
+
+ tor_assert(rend_data);
+
+ /* Compute descriptor ID for each replicas. */
+ for (replica = 0; replica < ARRAY_LENGTH(rend_data->descriptor_id);
+ replica++) {
+ ret = rend_compute_v2_desc_id(rend_data->descriptor_id[replica],
+ rend_data->onion_address,
+ rend_data->descriptor_cookie,
+ now, replica);
+ if (ret < 0) {
+ goto end;
+ }
+ }
+
+ end:
+ return ret;
+}
+
+/** Allocate and initialize a rend_data_t object for a service using the
+ * given arguments. Only the <b>onion_address</b> is not optional.
+ *
+ * Return a valid rend_data_t pointer. */
+rend_data_t *
+rend_data_service_create(const char *onion_address, const char *pk_digest,
+ const uint8_t *cookie, rend_auth_type_t auth_type)
+{
+ rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data));
+
+ /* We need at least one else the call is wrong. */
+ tor_assert(onion_address != NULL);
+
+ if (pk_digest) {
+ memcpy(rend_data->rend_pk_digest, pk_digest,
+ sizeof(rend_data->rend_pk_digest));
+ }
+ if (cookie) {
+ memcpy(rend_data->rend_cookie, cookie,
+ sizeof(rend_data->rend_cookie));
+ }
+
+ strlcpy(rend_data->onion_address, onion_address,
+ sizeof(rend_data->onion_address));
+ rend_data->auth_type = auth_type;
+ /* Won't be used but still need to initialize it for rend_data dup and
+ * free. */
+ rend_data->hsdirs_fp = smartlist_new();
+
+ return rend_data;
+}
+
+/** Allocate and initialize a rend_data_t object for a client request using
+ * the given arguments. Either an onion address or a descriptor ID is
+ * needed. Both can be given but only the onion address will be used to make
+ * the descriptor fetch.
+ *
+ * Return a valid rend_data_t pointer or NULL on error meaning the
+ * descriptor IDs couldn't be computed from the given data. */
+rend_data_t *
+rend_data_client_create(const char *onion_address, const char *desc_id,
+ const char *cookie, rend_auth_type_t auth_type)
+{
+ rend_data_t *rend_data = tor_malloc_zero(sizeof(*rend_data));
+
+ /* We need at least one else the call is wrong. */
+ tor_assert(onion_address != NULL || desc_id != NULL);
+
+ if (cookie) {
+ memcpy(rend_data->descriptor_cookie, cookie,
+ sizeof(rend_data->descriptor_cookie));
+ }
+ if (desc_id) {
+ memcpy(rend_data->desc_id_fetch, desc_id,
+ sizeof(rend_data->desc_id_fetch));
+ }
+ if (onion_address) {
+ strlcpy(rend_data->onion_address, onion_address,
+ sizeof(rend_data->onion_address));
+ if (compute_desc_id(rend_data) < 0) {
+ goto error;
+ }
+ }
+
+ rend_data->auth_type = auth_type;
+ rend_data->hsdirs_fp = smartlist_new();
+
+ return rend_data;
+
+ error:
+ rend_data_free(rend_data);
+ return NULL;
}
diff --git a/src/or/rendcommon.h b/src/or/rendcommon.h
index 8396cc3551..0ed7adc710 100644
--- a/src/or/rendcommon.h
+++ b/src/or/rendcommon.h
@@ -16,6 +16,12 @@
static INLINE void
rend_data_free(rend_data_t *data)
{
+ if (!data) {
+ return;
+ }
+ /* Cleanup the HSDir identity digest. */
+ SMARTLIST_FOREACH(data->hsdirs_fp, char *, d, tor_free(d));
+ smartlist_free(data->hsdirs_fp);
tor_free(data);
}
@@ -37,6 +43,7 @@ void rend_cache_clean_v2_descs_as_dir(time_t now, size_t min_to_remove);
void rend_cache_purge(void);
void rend_cache_free_all(void);
int rend_valid_service_id(const char *query);
+int rend_valid_descriptor_id(const char *query);
int rend_cache_lookup_entry(const char *query, int version,
rend_cache_entry_t **entry_out);
int rend_cache_lookup_v2_desc_as_dir(const char *query, const char **desc);
@@ -50,7 +57,8 @@ typedef enum {
rend_cache_store_status_t rend_cache_store_v2_desc_as_dir(const char *desc);
rend_cache_store_status_t rend_cache_store_v2_desc_as_client(const char *desc,
const char *desc_id_base32,
- const rend_data_t *rend_query);
+ const rend_data_t *rend_query,
+ rend_cache_entry_t **entry);
int rend_encode_v2_descriptors(smartlist_t *descs_out,
rend_service_descriptor_t *desc, time_t now,
uint8_t period, rend_auth_type_t auth_type,
@@ -65,5 +73,14 @@ void rend_get_descriptor_id_bytes(char *descriptor_id_out,
const char *secret_id_part);
size_t rend_cache_get_total_allocation(void);
+rend_data_t *rend_data_dup(const rend_data_t *data);
+rend_data_t *rend_data_client_create(const char *onion_address,
+ const char *desc_id,
+ const char *cookie,
+ rend_auth_type_t auth_type);
+rend_data_t *rend_data_service_create(const char *onion_address,
+ const char *pk_digest,
+ const uint8_t *cookie,
+ rend_auth_type_t auth_type);
#endif
diff --git a/src/or/rendservice.c b/src/or/rendservice.c
index 111b369b1c..c857d4cc87 100644
--- a/src/or/rendservice.c
+++ b/src/or/rendservice.c
@@ -15,6 +15,7 @@
#include "circuitlist.h"
#include "circuituse.h"
#include "config.h"
+#include "control.h"
#include "directory.h"
#include "main.h"
#include "networkstatus.h"
@@ -42,9 +43,15 @@ static int intro_point_accepted_intro_count(rend_intro_point_t *intro);
static int intro_point_should_expire_now(rend_intro_point_t *intro,
time_t now);
struct rend_service_t;
+static int rend_service_derive_key_digests(struct rend_service_t *s);
static int rend_service_load_keys(struct rend_service_t *s);
static int rend_service_load_auth_keys(struct rend_service_t *s,
const char *hfname);
+static struct rend_service_t *rend_service_get_by_pk_digest(
+ const char* digest);
+static struct rend_service_t *rend_service_get_by_service_id(const char *id);
+static const char *rend_service_escaped_dir(
+ const struct rend_service_t *s);
static ssize_t rend_service_parse_intro_for_v0_or_v1(
rend_intro_cell_t *intro,
@@ -65,7 +72,7 @@ static ssize_t rend_service_parse_intro_for_v3(
/** Represents the mapping from a virtual port of a rendezvous service to
* a real port on some IP.
*/
-typedef struct rend_service_port_config_t {
+struct rend_service_port_config_s {
/* The incoming HS virtual port we're mapping */
uint16_t virtual_port;
/* Is this an AF_UNIX port? */
@@ -76,7 +83,7 @@ typedef struct rend_service_port_config_t {
tor_addr_t real_addr;
/* The socket path to connect to, if is_unix_addr */
char unix_addr[FLEXIBLE_ARRAY_MEMBER];
-} rend_service_port_config_t;
+};
/** Try to maintain this many intro points per service by default. */
#define NUM_INTRO_POINTS_DEFAULT 3
@@ -102,7 +109,8 @@ typedef struct rend_service_port_config_t {
/** Represents a single hidden service running at this OP. */
typedef struct rend_service_t {
/* Fields specified in config file */
- char *directory; /**< where in the filesystem it stores it */
+ char *directory; /**< where in the filesystem it stores it. Will be NULL if
+ * this service is ephemeral. */
int dir_group_readable; /**< if 1, allow group read
permissions on directory */
smartlist_t *ports; /**< List of rend_service_port_config_t */
@@ -139,8 +147,23 @@ typedef struct rend_service_t {
/** If true, we don't close circuits for making requests to unsupported
* ports. */
int allow_unknown_ports;
+ /** The maximum number of simultanious streams-per-circuit that are allowed
+ * to be established, or 0 if no limit is set.
+ */
+ int max_streams_per_circuit;
+ /** If true, we close circuits that exceed the max_streams_per_circuit
+ * limit. */
+ int max_streams_close_circuit;
} rend_service_t;
+/** Returns a escaped string representation of the service, <b>s</b>.
+ */
+static const char *
+rend_service_escaped_dir(const struct rend_service_t *s)
+{
+ return (s->directory) ? escaped(s->directory) : "[EPHEMERAL]";
+}
+
/** A list of rend_service_t's for services run on this OP.
*/
static smartlist_t *rend_service_list = NULL;
@@ -173,7 +196,7 @@ rend_authorized_client_free(rend_authorized_client_t *client)
return;
if (client->client_key)
crypto_pk_free(client->client_key);
- tor_strclear(client->client_name);
+ memwipe(client->client_name, 0, strlen(client->client_name));
tor_free(client->client_name);
memwipe(client->descriptor_cookie, 0, sizeof(client->descriptor_cookie));
tor_free(client);
@@ -195,7 +218,8 @@ rend_service_free(rend_service_t *service)
return;
tor_free(service->directory);
- SMARTLIST_FOREACH(service->ports, void*, p, tor_free(p));
+ SMARTLIST_FOREACH(service->ports, rend_service_port_config_t*, p,
+ rend_service_port_config_free(p));
smartlist_free(service->ports);
if (service->private_key)
crypto_pk_free(service->private_key);
@@ -232,8 +256,9 @@ rend_service_free_all(void)
}
/** Validate <b>service</b> and add it to rend_service_list if possible.
+ * Return 0 on success. On failure, free <b>service</b> and return -1.
*/
-static void
+static int
rend_add_service(rend_service_t *service)
{
int i;
@@ -241,20 +266,38 @@ rend_add_service(rend_service_t *service)
service->intro_nodes = smartlist_new();
+ if (service->max_streams_per_circuit < 0) {
+ log_warn(LD_CONFIG, "Hidden service (%s) configured with negative max "
+ "streams per circuit; ignoring.",
+ rend_service_escaped_dir(service));
+ rend_service_free(service);
+ return -1;
+ }
+
+ if (service->max_streams_close_circuit < 0 ||
+ service->max_streams_close_circuit > 1) {
+ log_warn(LD_CONFIG, "Hidden service (%s) configured with invalid "
+ "max streams handling; ignoring.",
+ rend_service_escaped_dir(service));
+ rend_service_free(service);
+ return -1;
+ }
+
if (service->auth_type != REND_NO_AUTH &&
smartlist_len(service->clients) == 0) {
log_warn(LD_CONFIG, "Hidden service (%s) with client authorization but no "
"clients; ignoring.",
- escaped(service->directory));
+ rend_service_escaped_dir(service));
rend_service_free(service);
- return;
+ return -1;
}
if (!smartlist_len(service->ports)) {
log_warn(LD_CONFIG, "Hidden service (%s) with no ports configured; "
"ignoring.",
- escaped(service->directory));
+ rend_service_escaped_dir(service));
rend_service_free(service);
+ return -1;
} else {
int dupe = 0;
/* XXX This duplicate check has two problems:
@@ -272,14 +315,17 @@ rend_add_service(rend_service_t *service)
* lock file. But this is enough to detect a simple mistake that
* at least one person has actually made.
*/
- SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
- dupe = dupe ||
- !strcmp(ptr->directory, service->directory));
- if (dupe) {
- log_warn(LD_REND, "Another hidden service is already configured for "
- "directory %s, ignoring.", service->directory);
- rend_service_free(service);
- return;
+ if (service->directory != NULL) { /* Skip dupe for ephemeral services. */
+ SMARTLIST_FOREACH(rend_service_list, rend_service_t*, ptr,
+ dupe = dupe ||
+ !strcmp(ptr->directory, service->directory));
+ if (dupe) {
+ log_warn(LD_REND, "Another hidden service is already configured for "
+ "directory %s, ignoring.",
+ rend_service_escaped_dir(service));
+ rend_service_free(service);
+ return -1;
+ }
}
smartlist_add(rend_service_list, service);
log_debug(LD_REND,"Configuring service with directory \"%s\"",
@@ -305,7 +351,9 @@ rend_add_service(rend_service_t *service)
#endif /* defined(HAVE_SYS_UN_H) */
}
}
+ return 0;
}
+ /* NOTREACHED */
}
/** Return a new rend_service_port_config_t with its path set to
@@ -324,15 +372,17 @@ rend_service_port_config_new(const char *socket_path)
return conf;
}
-/** Parses a real-port to virtual-port mapping and returns a new
- * rend_service_port_config_t.
+/** Parses a real-port to virtual-port mapping separated by the provided
+ * separator and returns a new rend_service_port_config_t, or NULL and an
+ * optional error string on failure.
*
- * The format is: VirtualPort (IP|RealPort|IP:RealPort|'socket':path)?
+ * The format is: VirtualPort SEP (IP|RealPort|IP:RealPort|'socket':path)?
*
* IP defaults to 127.0.0.1; RealPort defaults to VirtualPort.
*/
-static rend_service_port_config_t *
-parse_port_config(const char *string)
+rend_service_port_config_t *
+rend_service_parse_port_config(const char *string, const char *sep,
+ char **err_msg_out)
{
smartlist_t *sl;
int virtport;
@@ -343,19 +393,24 @@ parse_port_config(const char *string)
rend_service_port_config_t *result = NULL;
unsigned int is_unix_addr = 0;
char *socket_path = NULL;
+ char *err_msg = NULL;
sl = smartlist_new();
- smartlist_split_string(sl, string, " ",
+ smartlist_split_string(sl, string, sep,
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
if (smartlist_len(sl) < 1 || smartlist_len(sl) > 2) {
- log_warn(LD_CONFIG, "Bad syntax in hidden service port configuration.");
+ if (err_msg_out)
+ err_msg = tor_strdup("Bad syntax in hidden service port configuration.");
+
goto err;
}
virtport = (int)tor_parse_long(smartlist_get(sl,0), 10, 1, 65535, NULL,NULL);
if (!virtport) {
- log_warn(LD_CONFIG, "Missing or invalid port %s in hidden service port "
- "configuration", escaped(smartlist_get(sl,0)));
+ if (err_msg_out)
+ tor_asprintf(&err_msg, "Missing or invalid port %s in hidden service "
+ "port configuration", escaped(smartlist_get(sl,0)));
+
goto err;
}
@@ -369,10 +424,11 @@ parse_port_config(const char *string)
addrport = smartlist_get(sl,1);
ret = config_parse_unix_port(addrport, &socket_path);
if (ret < 0 && ret != -ENOENT) {
- if (ret == -EINVAL) {
- log_warn(LD_CONFIG,
- "Empty socket path in hidden service port configuration.");
- }
+ if (ret == -EINVAL)
+ if (err_msg_out)
+ err_msg = tor_strdup("Empty socket path in hidden service port "
+ "configuration.");
+
goto err;
}
if (socket_path) {
@@ -380,8 +436,10 @@ parse_port_config(const char *string)
} else if (strchr(addrport, ':') || strchr(addrport, '.')) {
/* else try it as an IP:port pair if it has a : or . in it */
if (tor_addr_port_lookup(addrport, &addr, &p)<0) {
- log_warn(LD_CONFIG,"Unparseable address in hidden service port "
- "configuration.");
+ if (err_msg_out)
+ err_msg = tor_strdup("Unparseable address in hidden service port "
+ "configuration.");
+
goto err;
}
realport = p?p:virtport;
@@ -389,8 +447,11 @@ parse_port_config(const char *string)
/* No addr:port, no addr -- must be port. */
realport = (int)tor_parse_long(addrport, 10, 1, 65535, NULL, NULL);
if (!realport) {
- log_warn(LD_CONFIG,"Unparseable or out-of-range port %s in hidden "
- "service port configuration.", escaped(addrport));
+ if (err_msg_out)
+ tor_asprintf(&err_msg, "Unparseable or out-of-range port %s in "
+ "hidden service port configuration.",
+ escaped(addrport));
+
goto err;
}
tor_addr_from_ipv4h(&addr, 0x7F000001u); /* Default to 127.0.0.1 */
@@ -408,6 +469,7 @@ parse_port_config(const char *string)
}
err:
+ if (err_msg_out) *err_msg_out = err_msg;
SMARTLIST_FOREACH(sl, char *, c, tor_free(c));
smartlist_free(sl);
if (socket_path) tor_free(socket_path);
@@ -415,6 +477,13 @@ parse_port_config(const char *string)
return result;
}
+/** Release all storage held in a rend_service_port_config_t. */
+void
+rend_service_port_config_free(rend_service_port_config_t *p)
+{
+ tor_free(p);
+}
+
/** Set up rend_service_list, based on the values of HiddenServiceDir and
* HiddenServicePort in <b>options</b>. Return 0 on success and -1 on
* failure. (If <b>validate_only</b> is set, parse, warn and return as
@@ -439,113 +508,146 @@ rend_config_services(const or_options_t *options, int validate_only)
if (service) { /* register the one we just finished parsing */
if (validate_only)
rend_service_free(service);
- else
- rend_add_service(service);
- }
- service = tor_malloc_zero(sizeof(rend_service_t));
- service->directory = tor_strdup(line->value);
- service->ports = smartlist_new();
- service->intro_period_started = time(NULL);
- service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
- continue;
- }
- if (!service) {
- log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
- line->key);
- rend_service_free(service);
- return -1;
- }
- if (!strcasecmp(line->key, "HiddenServicePort")) {
- portcfg = parse_port_config(line->value);
- if (!portcfg) {
- rend_service_free(service);
- return -1;
- }
- smartlist_add(service->ports, portcfg);
- } else if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) {
- service->allow_unknown_ports = (int)tor_parse_long(line->value,
- 10, 0, 1, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s",
- line->value);
- rend_service_free(service);
- return -1;
- }
- log_info(LD_CONFIG,
- "HiddenServiceAllowUnknownPorts=%d for %s",
- (int)service->allow_unknown_ports, service->directory);
- } else if (!strcasecmp(line->key,
- "HiddenServiceDirGroupReadable")) {
- service->dir_group_readable = (int)tor_parse_long(line->value,
- 10, 0, 1, &ok, NULL);
- if (!ok) {
- log_warn(LD_CONFIG,
- "HiddenServiceDirGroupReadable should be 0 or 1, not %s",
- line->value);
- rend_service_free(service);
- return -1;
- }
- log_info(LD_CONFIG,
- "HiddenServiceDirGroupReadable=%d for %s",
- service->dir_group_readable, service->directory);
- } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
- /* Parse auth type and comma-separated list of client names and add a
- * rend_authorized_client_t for each client to the service's list
- * of authorized clients. */
- smartlist_t *type_names_split, *clients;
- const char *authname;
- int num_clients;
- if (service->auth_type != REND_NO_AUTH) {
- log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient "
- "lines for a single service.");
- rend_service_free(service);
- return -1;
- }
- type_names_split = smartlist_new();
- smartlist_split_string(type_names_split, line->value, " ", 0, 2);
- if (smartlist_len(type_names_split) < 1) {
- log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This "
- "should have been prevented when parsing the "
- "configuration.");
- smartlist_free(type_names_split);
- rend_service_free(service);
- return -1;
- }
- authname = smartlist_get(type_names_split, 0);
- if (!strcasecmp(authname, "basic")) {
- service->auth_type = REND_BASIC_AUTH;
- } else if (!strcasecmp(authname, "stealth")) {
- service->auth_type = REND_STEALTH_AUTH;
- } else {
- log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
- "unrecognized auth-type '%s'. Only 'basic' or 'stealth' "
- "are recognized.",
- (char *) smartlist_get(type_names_split, 0));
- SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
- smartlist_free(type_names_split);
- rend_service_free(service);
- return -1;
- }
- service->clients = smartlist_new();
- if (smartlist_len(type_names_split) < 2) {
- log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
- "auth-type '%s', but no client names.",
- service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
- SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
- smartlist_free(type_names_split);
- continue;
- }
- clients = smartlist_new();
- smartlist_split_string(clients, smartlist_get(type_names_split, 1),
- ",", SPLIT_SKIP_SPACE, 0);
- SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
- smartlist_free(type_names_split);
- /* Remove duplicate client names. */
- num_clients = smartlist_len(clients);
- smartlist_sort_strings(clients);
- smartlist_uniq_strings(clients);
- if (smartlist_len(clients) < num_clients) {
+ else
+ rend_add_service(service);
+ }
+ service = tor_malloc_zero(sizeof(rend_service_t));
+ service->directory = tor_strdup(line->value);
+ service->ports = smartlist_new();
+ service->intro_period_started = time(NULL);
+ service->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
+ continue;
+ }
+ if (!service) {
+ log_warn(LD_CONFIG, "%s with no preceding HiddenServiceDir directive",
+ line->key);
+ rend_service_free(service);
+ return -1;
+ }
+ if (!strcasecmp(line->key, "HiddenServicePort")) {
+ char *err_msg = NULL;
+ portcfg = rend_service_parse_port_config(line->value, " ", &err_msg);
+ if (!portcfg) {
+ if (err_msg)
+ log_warn(LD_CONFIG, "%s", err_msg);
+ tor_free(err_msg);
+ rend_service_free(service);
+ return -1;
+ }
+ tor_assert(!err_msg);
+ smartlist_add(service->ports, portcfg);
+ } else if (!strcasecmp(line->key, "HiddenServiceAllowUnknownPorts")) {
+ service->allow_unknown_ports = (int)tor_parse_long(line->value,
+ 10, 0, 1, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceAllowUnknownPorts should be 0 or 1, not %s",
+ line->value);
+ rend_service_free(service);
+ return -1;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceAllowUnknownPorts=%d for %s",
+ (int)service->allow_unknown_ports, service->directory);
+ } else if (!strcasecmp(line->key,
+ "HiddenServiceDirGroupReadable")) {
+ service->dir_group_readable = (int)tor_parse_long(line->value,
+ 10, 0, 1, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceDirGroupReadable should be 0 or 1, not %s",
+ line->value);
+ rend_service_free(service);
+ return -1;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceDirGroupReadable=%d for %s",
+ service->dir_group_readable, service->directory);
+ } else if (!strcasecmp(line->key, "HiddenServiceMaxStreams")) {
+ service->max_streams_per_circuit = (int)tor_parse_long(line->value,
+ 10, 0, 65535, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceMaxStreams should be between 0 and %d, not %s",
+ 65535, line->value);
+ rend_service_free(service);
+ return -1;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceMaxStreams=%d for %s",
+ service->max_streams_per_circuit, service->directory);
+ } else if (!strcasecmp(line->key, "HiddenServiceMaxStreamsCloseCircuit")) {
+ service->max_streams_close_circuit = (int)tor_parse_long(line->value,
+ 10, 0, 1, &ok, NULL);
+ if (!ok) {
+ log_warn(LD_CONFIG,
+ "HiddenServiceMaxStreamsCloseCircuit should be 0 or 1, "
+ "not %s",
+ line->value);
+ rend_service_free(service);
+ return -1;
+ }
+ log_info(LD_CONFIG,
+ "HiddenServiceMaxStreamsCloseCircuit=%d for %s",
+ (int)service->max_streams_close_circuit, service->directory);
+
+ } else if (!strcasecmp(line->key, "HiddenServiceAuthorizeClient")) {
+ /* Parse auth type and comma-separated list of client names and add a
+ * rend_authorized_client_t for each client to the service's list
+ * of authorized clients. */
+ smartlist_t *type_names_split, *clients;
+ const char *authname;
+ int num_clients;
+ if (service->auth_type != REND_NO_AUTH) {
+ log_warn(LD_CONFIG, "Got multiple HiddenServiceAuthorizeClient "
+ "lines for a single service.");
+ rend_service_free(service);
+ return -1;
+ }
+ type_names_split = smartlist_new();
+ smartlist_split_string(type_names_split, line->value, " ", 0, 2);
+ if (smartlist_len(type_names_split) < 1) {
+ log_warn(LD_BUG, "HiddenServiceAuthorizeClient has no value. This "
+ "should have been prevented when parsing the "
+ "configuration.");
+ smartlist_free(type_names_split);
+ rend_service_free(service);
+ return -1;
+ }
+ authname = smartlist_get(type_names_split, 0);
+ if (!strcasecmp(authname, "basic")) {
+ service->auth_type = REND_BASIC_AUTH;
+ } else if (!strcasecmp(authname, "stealth")) {
+ service->auth_type = REND_STEALTH_AUTH;
+ } else {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
+ "unrecognized auth-type '%s'. Only 'basic' or 'stealth' "
+ "are recognized.",
+ (char *) smartlist_get(type_names_split, 0));
+ SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
+ smartlist_free(type_names_split);
+ rend_service_free(service);
+ return -1;
+ }
+ service->clients = smartlist_new();
+ if (smartlist_len(type_names_split) < 2) {
+ log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains "
+ "auth-type '%s', but no client names.",
+ service->auth_type == REND_BASIC_AUTH ? "basic" : "stealth");
+ SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
+ smartlist_free(type_names_split);
+ continue;
+ }
+ clients = smartlist_new();
+ smartlist_split_string(clients, smartlist_get(type_names_split, 1),
+ ",", SPLIT_SKIP_SPACE, 0);
+ SMARTLIST_FOREACH(type_names_split, char *, cp, tor_free(cp));
+ smartlist_free(type_names_split);
+ /* Remove duplicate client names. */
+ num_clients = smartlist_len(clients);
+ smartlist_sort_strings(clients);
+ smartlist_uniq_strings(clients);
+ if (smartlist_len(clients) < num_clients) {
log_info(LD_CONFIG, "HiddenServiceAuthorizeClient contains %d "
"duplicate client name(s); removing.",
num_clients - smartlist_len(clients));
@@ -632,12 +734,35 @@ rend_config_services(const or_options_t *options, int validate_only)
if (old_service_list && !validate_only) {
smartlist_t *surviving_services = smartlist_new();
+ /* Preserve the existing ephemeral services.
+ *
+ * This is the ephemeral service equivalent of the "Copy introduction
+ * points to new services" block, except there's no copy required since
+ * the service structure isn't regenerated.
+ *
+ * After this is done, all ephemeral services will be:
+ * * Removed from old_service_list, so the equivalent non-ephemeral code
+ * will not attempt to preserve them.
+ * * Added to the new rend_service_list (that previously only had the
+ * services listed in the configuration).
+ * * Added to surviving_services, which is the list of services that
+ * will NOT have their intro point closed.
+ */
+ SMARTLIST_FOREACH(old_service_list, rend_service_t *, old, {
+ if (!old->directory) {
+ SMARTLIST_DEL_CURRENT(old_service_list, old);
+ smartlist_add(surviving_services, old);
+ smartlist_add(rend_service_list, old);
+ }
+ });
+
/* Copy introduction points to new services. */
/* XXXX This is O(n^2), but it's only called on reconfigure, so it's
* probably ok? */
SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, new) {
SMARTLIST_FOREACH_BEGIN(old_service_list, rend_service_t *, old) {
- if (!strcmp(old->directory, new->directory)) {
+ if (new->directory && old->directory &&
+ !strcmp(old->directory, new->directory)) {
smartlist_add_all(new->intro_nodes, old->intro_nodes);
smartlist_clear(old->intro_nodes);
smartlist_add(surviving_services, old);
@@ -685,6 +810,124 @@ rend_config_services(const or_options_t *options, int validate_only)
return 0;
}
+/** Add the ephemeral service <b>pk</b>/<b>ports</b> if possible, with
+ * <b>max_streams_per_circuit</b> streams allowed per rendezvous circuit,
+ * and circuit closure on max streams being exceeded set by
+ * <b>max_streams_close_circuit</b>.
+ *
+ * Regardless of sucess/failure, callers should not touch pk/ports after
+ * calling this routine, and may assume that correct cleanup has been done
+ * on failure.
+ *
+ * Return an appropriate rend_service_add_ephemeral_status_t.
+ */
+rend_service_add_ephemeral_status_t
+rend_service_add_ephemeral(crypto_pk_t *pk,
+ smartlist_t *ports,
+ int max_streams_per_circuit,
+ int max_streams_close_circuit,
+ char **service_id_out)
+{
+ *service_id_out = NULL;
+ /* Allocate the service structure, and initialize the key, and key derived
+ * parameters.
+ */
+ rend_service_t *s = tor_malloc_zero(sizeof(rend_service_t));
+ s->directory = NULL; /* This indicates the service is ephemeral. */
+ s->private_key = pk;
+ s->auth_type = REND_NO_AUTH;
+ s->ports = ports;
+ s->intro_period_started = time(NULL);
+ s->n_intro_points_wanted = NUM_INTRO_POINTS_DEFAULT;
+ s->max_streams_per_circuit = max_streams_per_circuit;
+ s->max_streams_close_circuit = max_streams_close_circuit;
+ if (rend_service_derive_key_digests(s) < 0) {
+ rend_service_free(s);
+ return RSAE_BADPRIVKEY;
+ }
+
+ if (!s->ports || smartlist_len(s->ports) == 0) {
+ log_warn(LD_CONFIG, "At least one VIRTPORT/TARGET must be specified.");
+ rend_service_free(s);
+ return RSAE_BADVIRTPORT;
+ }
+
+ /* Enforcing pk/id uniqueness should be done by rend_service_load_keys(), but
+ * it's not, see #14828.
+ */
+ if (rend_service_get_by_pk_digest(s->pk_digest)) {
+ log_warn(LD_CONFIG, "Onion Service private key collides with an "
+ "existing service.");
+ rend_service_free(s);
+ return RSAE_ADDREXISTS;
+ }
+ if (rend_service_get_by_service_id(s->service_id)) {
+ log_warn(LD_CONFIG, "Onion Service id collides with an existing service.");
+ rend_service_free(s);
+ return RSAE_ADDREXISTS;
+ }
+
+ /* Initialize the service. */
+ if (rend_add_service(s)) {
+ return RSAE_INTERNAL;
+ }
+ *service_id_out = tor_strdup(s->service_id);
+
+ log_debug(LD_CONFIG, "Added ephemeral Onion Service: %s", s->service_id);
+ return RSAE_OKAY;
+}
+
+/** Remove the ephemeral service <b>service_id</b> if possible. Returns 0 on
+ * success, and -1 on failure.
+ */
+int
+rend_service_del_ephemeral(const char *service_id)
+{
+ rend_service_t *s;
+ if (!rend_valid_service_id(service_id)) {
+ log_warn(LD_CONFIG, "Requested malformed Onion Service id for removal.");
+ return -1;
+ }
+ if ((s = rend_service_get_by_service_id(service_id)) == NULL) {
+ log_warn(LD_CONFIG, "Requested non-existent Onion Service id for "
+ "removal.");
+ return -1;
+ }
+ if (s->directory) {
+ log_warn(LD_CONFIG, "Requested non-ephemeral Onion Service for removal.");
+ return -1;
+ }
+
+ /* Kill the intro point circuit for the Onion Service, and remove it from
+ * the list. Closing existing connections is the application's problem.
+ *
+ * XXX: As with the comment in rend_config_services(), a nice abstraction
+ * would be ideal here, but for now just duplicate the code.
+ */
+ SMARTLIST_FOREACH_BEGIN(circuit_get_global_list(), circuit_t *, circ) {
+ if (!circ->marked_for_close &&
+ circ->state == CIRCUIT_STATE_OPEN &&
+ (circ->purpose == CIRCUIT_PURPOSE_S_ESTABLISH_INTRO ||
+ circ->purpose == CIRCUIT_PURPOSE_S_INTRO)) {
+ origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ);
+ tor_assert(oc->rend_data);
+ if (!tor_memeq(s->pk_digest, oc->rend_data->rend_pk_digest, DIGEST_LEN))
+ continue;
+ log_debug(LD_REND, "Closing intro point %s for service %s.",
+ safe_str_client(extend_info_describe(
+ oc->build_state->chosen_exit)),
+ oc->rend_data->onion_address);
+ circuit_mark_for_close(circ, END_CIRC_REASON_FINISHED);
+ }
+ } SMARTLIST_FOREACH_END(circ);
+ smartlist_remove(rend_service_list, s);
+ rend_service_free(s);
+
+ log_debug(LD_CONFIG, "Removed ephemeral Onion Service: %s", service_id);
+
+ return 0;
+}
+
/** Replace the old value of <b>service</b>-\>desc with one that reflects
* the other fields in service.
*/
@@ -769,6 +1012,7 @@ rend_service_add_filenames_to_list(smartlist_t *lst, const rend_service_t *s)
{
tor_assert(lst);
tor_assert(s);
+ tor_assert(s->directory);
smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"private_key",
s->directory);
smartlist_add_asprintf(lst, "%s"PATH_SEPARATOR"hostname",
@@ -787,11 +1031,31 @@ rend_services_add_filenames_to_lists(smartlist_t *open_lst,
if (!rend_service_list)
return;
SMARTLIST_FOREACH_BEGIN(rend_service_list, rend_service_t *, s) {
- rend_service_add_filenames_to_list(open_lst, s);
- smartlist_add(stat_lst, tor_strdup(s->directory));
+ if (s->directory) {
+ rend_service_add_filenames_to_list(open_lst, s);
+ smartlist_add(stat_lst, tor_strdup(s->directory));
+ }
} SMARTLIST_FOREACH_END(s);
}
+/** Derive all rend_service_t internal material based on the service's key.
+ * Returns 0 on sucess, -1 on failure.
+ */
+static int
+rend_service_derive_key_digests(struct rend_service_t *s)
+{
+ if (rend_get_service_id(s->private_key, s->service_id)<0) {
+ log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
+ return -1;
+ }
+ if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
+ log_warn(LD_BUG, "Couldn't compute hash of public key.");
+ return -1;
+ }
+
+ return 0;
+}
+
/** Load and/or generate private keys for the hidden service <b>s</b>,
* possibly including keys for client authorization. Return 0 on success, -1
* on failure. */
@@ -830,15 +1094,10 @@ rend_service_load_keys(rend_service_t *s)
if (!s->private_key)
return -1;
- /* Create service file */
- if (rend_get_service_id(s->private_key, s->service_id)<0) {
- log_warn(LD_BUG, "Internal error: couldn't encode service ID.");
- return -1;
- }
- if (crypto_pk_get_digest(s->private_key, s->pk_digest)<0) {
- log_warn(LD_BUG, "Couldn't compute hash of public key.");
+ if (rend_service_derive_key_digests(s) < 0)
return -1;
- }
+
+ /* Create service file */
if (strlcpy(fname,s->directory,sizeof(fname)) >= sizeof(fname) ||
strlcat(fname,PATH_SEPARATOR"hostname",sizeof(fname))
>= sizeof(fname)) {
@@ -941,7 +1200,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
}
if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
client->descriptor_cookie,
- REND_DESC_COOKIE_LEN) < 0) {
+ REND_DESC_COOKIE_LEN, 0) < 0) {
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
goto err;
}
@@ -968,7 +1227,6 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
client->client_key = prkey;
}
/* Add entry to client_keys file. */
- desc_cook_out[strlen(desc_cook_out)-1] = '\0'; /* Remove newline. */
written = tor_snprintf(buf, sizeof(buf),
"client-name %s\ndescriptor-cookie %s\n",
client->client_name, desc_cook_out);
@@ -1023,12 +1281,11 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
((int)s->auth_type - 1) << 4;
if (base64_encode(desc_cook_out, 3*REND_DESC_COOKIE_LEN_BASE64+1,
extended_desc_cookie,
- REND_DESC_COOKIE_LEN+1) < 0) {
+ REND_DESC_COOKIE_LEN+1, 0) < 0) {
log_warn(LD_BUG, "Could not base64-encode descriptor cookie.");
goto err;
}
- desc_cook_out[strlen(desc_cook_out)-3] = '\0'; /* Remove A= and
- newline. */
+ desc_cook_out[strlen(desc_cook_out)-2] = '\0'; /* Remove A=. */
tor_snprintf(buf, sizeof(buf),"%s.onion %s # client: %s\n",
service_id, desc_cook_out, client->client_name);
}
@@ -1052,7 +1309,7 @@ rend_service_load_auth_keys(rend_service_t *s, const char *hfname)
abort_writing_to_file(open_hfile);
done:
if (client_keys_str) {
- tor_strclear(client_keys_str);
+ memwipe(client_keys_str, 0, strlen(client_keys_str));
tor_free(client_keys_str);
}
strmap_free(parsed_clients, rend_authorized_client_strmap_item_free);
@@ -1080,6 +1337,20 @@ rend_service_get_by_pk_digest(const char* digest)
return NULL;
}
+/** Return the service whose service id is <b>id</b>, or NULL if no such
+ * service exists.
+ */
+static struct rend_service_t *
+rend_service_get_by_service_id(const char *id)
+{
+ tor_assert(strlen(id) == REND_SERVICE_ID_LEN_BASE32);
+ SMARTLIST_FOREACH(rend_service_list, rend_service_t*, s, {
+ if (tor_memeq(s->service_id, id, REND_SERVICE_ID_LEN_BASE32))
+ return s;
+ });
+ return NULL;
+}
+
/** Return 1 if any virtual port in <b>service</b> wants a circuit
* to have good uptime. Else return 0.
*/
@@ -1133,7 +1404,7 @@ rend_check_authorization(rend_service_t *service,
if (!auth_client) {
char descriptor_cookie_base64[3*REND_DESC_COOKIE_LEN_BASE64];
base64_encode(descriptor_cookie_base64, sizeof(descriptor_cookie_base64),
- descriptor_cookie, REND_DESC_COOKIE_LEN);
+ descriptor_cookie, REND_DESC_COOKIE_LEN, 0);
log_info(LD_REND, "No authorization found for descriptor cookie '%s'! "
"Dropping cell!",
descriptor_cookie_base64);
@@ -1167,16 +1438,17 @@ rend_service_note_removing_intro_point(rend_service_t *service,
/* This intro point was never used. Don't change
* n_intro_points_wanted. */
} else {
+
/* We want to increase the number of introduction points service
* operates if intro was heavily used, or decrease the number of
* intro points if intro was lightly used.
*
* We consider an intro point's target 'usage' to be
- * INTRO_POINT_LIFETIME_INTRODUCTIONS introductions in
+ * maximum of INTRODUCE2 cells divided by
* INTRO_POINT_LIFETIME_MIN_SECONDS seconds. To calculate intro's
- * fraction of target usage, we divide the fraction of
- * _LIFETIME_INTRODUCTIONS introductions that it has handled by
- * the fraction of _LIFETIME_MIN_SECONDS for which it existed.
+ * fraction of target usage, we divide the amount of INTRODUCE2 cells
+ * that it has handled by the fraction of _LIFETIME_MIN_SECONDS for
+ * which it existed.
*
* Then we multiply that fraction of desired usage by a fudge
* factor of 1.5, to decide how many new introduction points
@@ -1198,7 +1470,7 @@ rend_service_note_removing_intro_point(rend_service_t *service,
intro_point_accepted_intro_count(intro) /
(double)(now - intro->time_published);
const double intro_point_target_usage =
- INTRO_POINT_LIFETIME_INTRODUCTIONS /
+ intro->max_introductions /
(double)INTRO_POINT_LIFETIME_MIN_SECONDS;
const double fractional_n_intro_points_wanted_to_replace_this_one =
(1.5 * (intro_point_usage / intro_point_target_usage));
@@ -1533,13 +1805,11 @@ rend_service_introduce(origin_circuit_t *circuit, const uint8_t *request,
hexcookie, serviceid);
tor_assert(launched->build_state);
/* Fill in the circuit's state. */
- launched->rend_data = tor_malloc_zero(sizeof(rend_data_t));
- memcpy(launched->rend_data->rend_pk_digest,
- circuit->rend_data->rend_pk_digest,
- DIGEST_LEN);
- memcpy(launched->rend_data->rend_cookie, parsed_req->rc, REND_COOKIE_LEN);
- strlcpy(launched->rend_data->onion_address, service->service_id,
- sizeof(launched->rend_data->onion_address));
+
+ launched->rend_data =
+ rend_data_service_create(service->service_id,
+ circuit->rend_data->rend_pk_digest,
+ parsed_req->rc, service->auth_type);
launched->build_state->service_pending_final_cpath_ref =
tor_malloc_zero(sizeof(crypt_path_reference_t));
@@ -2511,10 +2781,9 @@ rend_service_launch_establish_intro(rend_service_t *service,
intro->extend_info = extend_info_dup(launched->build_state->chosen_exit);
}
- launched->rend_data = tor_malloc_zero(sizeof(rend_data_t));
- strlcpy(launched->rend_data->onion_address, service->service_id,
- sizeof(launched->rend_data->onion_address));
- memcpy(launched->rend_data->rend_pk_digest, service->pk_digest, DIGEST_LEN);
+ launched->rend_data = rend_data_service_create(service->service_id,
+ service->pk_digest, NULL,
+ service->auth_type);
launched->intro_key = crypto_pk_dup_key(intro->intro_key);
if (launched->base_.state == CIRCUIT_STATE_OPEN)
rend_service_intro_has_opened(launched);
@@ -2899,14 +3168,16 @@ find_intro_point(origin_circuit_t *circ)
return NULL;
}
-/** Determine the responsible hidden service directories for the
- * rend_encoded_v2_service_descriptor_t's in <b>descs</b> and upload them;
- * <b>service_id</b> and <b>seconds_valid</b> are only passed for logging
- * purposes. */
-static void
+/** Upload the rend_encoded_v2_service_descriptor_t's in <b>descs</b>
+ * associated with the rend_service_descriptor_t <b>renddesc</b> to
+ * the responsible hidden service directories OR the hidden service
+ * directories specified by <b>hs_dirs</b>; <b>service_id</b> and
+ * <b>seconds_valid</b> are only passed for logging purposes.
+ */
+void
directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
- smartlist_t *descs, const char *service_id,
- int seconds_valid)
+ smartlist_t *descs, smartlist_t *hs_dirs,
+ const char *service_id, int seconds_valid)
{
int i, j, failed_upload = 0;
smartlist_t *responsible_dirs = smartlist_new();
@@ -2914,14 +3185,21 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
routerstatus_t *hs_dir;
for (i = 0; i < smartlist_len(descs); i++) {
rend_encoded_v2_service_descriptor_t *desc = smartlist_get(descs, i);
- /* Determine responsible dirs. */
- if (hid_serv_get_responsible_directories(responsible_dirs,
- desc->desc_id) < 0) {
- log_warn(LD_REND, "Could not determine the responsible hidden service "
- "directories to post descriptors to.");
- smartlist_free(responsible_dirs);
- smartlist_free(successful_uploads);
- return;
+ /** If any HSDirs are specified, they should be used instead of
+ * the responsible directories */
+ if (hs_dirs && smartlist_len(hs_dirs) > 0) {
+ smartlist_add_all(responsible_dirs, hs_dirs);
+ } else {
+ /* Determine responsible dirs. */
+ if (hid_serv_get_responsible_directories(responsible_dirs,
+ desc->desc_id) < 0) {
+ log_warn(LD_REND, "Could not determine the responsible hidden service "
+ "directories to post descriptors to.");
+ control_event_hs_descriptor_upload(service_id,
+ "UNKNOWN",
+ "UNKNOWN");
+ goto done;
+ }
}
for (j = 0; j < smartlist_len(responsible_dirs); j++) {
char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
@@ -2961,6 +3239,9 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
hs_dir->nickname,
hs_dir_ip,
hs_dir->or_port);
+ control_event_hs_descriptor_upload(service_id,
+ hs_dir->identity_digest,
+ desc_id_base32);
tor_free(hs_dir_ip);
/* Remember successful upload to this router for next time. */
if (!smartlist_contains_digest(successful_uploads,
@@ -2988,6 +3269,7 @@ directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
}
});
}
+ done:
smartlist_free(responsible_dirs);
smartlist_free(successful_uploads);
}
@@ -3052,7 +3334,7 @@ upload_service_descriptor(rend_service_t *service)
rend_get_service_id(service->desc->pk, serviceid);
log_info(LD_REND, "Launching upload for hidden service %s",
serviceid);
- directory_post_to_hs_dir(service->desc, descs, serviceid,
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
seconds_valid);
/* Free memory for descriptors. */
for (i = 0; i < smartlist_len(descs); i++)
@@ -3081,7 +3363,7 @@ upload_service_descriptor(rend_service_t *service)
smartlist_free(client_cookies);
return;
}
- directory_post_to_hs_dir(service->desc, descs, serviceid,
+ directory_post_to_hs_dir(service->desc, descs, NULL, serviceid,
seconds_valid);
/* Free memory for descriptors. */
for (i = 0; i < smartlist_len(descs); i++)
@@ -3133,7 +3415,7 @@ intro_point_should_expire_now(rend_intro_point_t *intro,
}
if (intro_point_accepted_intro_count(intro) >=
- INTRO_POINT_LIFETIME_INTRODUCTIONS) {
+ intro->max_introductions) {
/* This intro point has been used too many times. Expire it now. */
return 1;
}
@@ -3142,9 +3424,8 @@ intro_point_should_expire_now(rend_intro_point_t *intro,
/* This intro point has been published, but we haven't picked an
* expiration time for it. Pick one now. */
int intro_point_lifetime_seconds =
- INTRO_POINT_LIFETIME_MIN_SECONDS +
- crypto_rand_int(INTRO_POINT_LIFETIME_MAX_SECONDS -
- INTRO_POINT_LIFETIME_MIN_SECONDS);
+ crypto_rand_int_range(INTRO_POINT_LIFETIME_MIN_SECONDS,
+ INTRO_POINT_LIFETIME_MAX_SECONDS);
/* Start the expiration timer now, rather than when the intro
* point was first published. There shouldn't be much of a time
@@ -3329,7 +3610,8 @@ rend_services_introduce(void)
log_warn(LD_REND,
"Could only establish %d introduction points for %s; "
"wanted %u.",
- smartlist_len(service->intro_nodes), service->service_id,
+ smartlist_len(service->intro_nodes),
+ safe_str_client(service->service_id),
n_intro_points_to_open);
break;
}
@@ -3340,10 +3622,14 @@ rend_services_introduce(void)
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
intro->extend_info = extend_info_from_node(node, 0);
intro->intro_key = crypto_pk_new();
- tor_assert(!crypto_pk_generate_key(intro->intro_key));
+ const int fail = crypto_pk_generate_key(intro->intro_key);
+ tor_assert(!fail);
intro->time_published = -1;
intro->time_to_expire = -1;
intro->time_expiring = -1;
+ intro->max_introductions =
+ crypto_rand_int_range(INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS,
+ INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS);
smartlist_add(service->intro_nodes, intro);
log_info(LD_REND, "Picked router %s as an intro point for %s.",
safe_str_client(node_describe(node)),
@@ -3567,6 +3853,25 @@ rend_service_set_connection_addr_port(edge_connection_t *conn,
serviceid, (unsigned)circ->base_.n_circ_id);
return -2;
}
+ if (service->max_streams_per_circuit > 0) {
+ /* Enforce the streams-per-circuit limit, and refuse to provide a
+ * mapping if this circuit will exceed the limit. */
+#define MAX_STREAM_WARN_INTERVAL 600
+ static struct ratelim_t stream_ratelim =
+ RATELIM_INIT(MAX_STREAM_WARN_INTERVAL);
+ if (circ->rend_data->nr_streams >= service->max_streams_per_circuit) {
+ log_fn_ratelim(&stream_ratelim, LOG_WARN, LD_REND,
+ "Maximum streams per circuit limit reached on rendezvous "
+ "circuit %u; %s. Circuit has %d out of %d streams.",
+ (unsigned)circ->base_.n_circ_id,
+ service->max_streams_close_circuit ?
+ "closing circuit" :
+ "ignoring open stream request",
+ circ->rend_data->nr_streams,
+ service->max_streams_per_circuit);
+ return service->max_streams_close_circuit ? -2 : -1;
+ }
+ }
matching_ports = smartlist_new();
SMARTLIST_FOREACH(service->ports, rend_service_port_config_t *, p,
{
diff --git a/src/or/rendservice.h b/src/or/rendservice.h
index 754f7c358c..b540d2c8ad 100644
--- a/src/or/rendservice.h
+++ b/src/or/rendservice.h
@@ -15,6 +15,7 @@
#include "or.h"
typedef struct rend_intro_cell_s rend_intro_cell_t;
+typedef struct rend_service_port_config_s rend_service_port_config_t;
#ifdef RENDSERVICE_PRIVATE
@@ -101,5 +102,29 @@ int rend_service_set_connection_addr_port(edge_connection_t *conn,
void rend_service_dump_stats(int severity);
void rend_service_free_all(void);
+rend_service_port_config_t *rend_service_parse_port_config(const char *string,
+ const char *sep,
+ char **err_msg_out);
+void rend_service_port_config_free(rend_service_port_config_t *p);
+
+/** Return value from rend_service_add_ephemeral. */
+typedef enum {
+ RSAE_BADVIRTPORT = -4, /**< Invalid VIRTPORT/TARGET(s) */
+ RSAE_ADDREXISTS = -3, /**< Onion address collision */
+ RSAE_BADPRIVKEY = -2, /**< Invalid public key */
+ RSAE_INTERNAL = -1, /**< Internal error */
+ RSAE_OKAY = 0 /**< Service added as expected */
+} rend_service_add_ephemeral_status_t;
+rend_service_add_ephemeral_status_t rend_service_add_ephemeral(crypto_pk_t *pk,
+ smartlist_t *ports,
+ int max_streams_per_circuit,
+ int max_streams_close_circuit,
+ char **service_id_out);
+int rend_service_del_ephemeral(const char *service_id);
+
+void directory_post_to_hs_dir(rend_service_descriptor_t *renddesc,
+ smartlist_t *descs, smartlist_t *hs_dirs,
+ const char *service_id, int seconds_valid);
+
#endif
diff --git a/src/or/router.c b/src/or/router.c
index 2ddaa895fc..0903eb2082 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -26,9 +26,11 @@
#include "relay.h"
#include "rephist.h"
#include "router.h"
+#include "routerkeys.h"
#include "routerlist.h"
#include "routerparse.h"
#include "statefile.h"
+#include "torcert.h"
#include "transports.h"
#include "routerset.h"
@@ -204,6 +206,8 @@ set_server_identity_key(crypto_pk_t *k)
static void
assert_identity_keys_ok(void)
{
+ if (1)
+ return;
tor_assert(client_identitykey);
if (public_server_mode(get_options())) {
/* assert that we have set the client and server keys to be equal */
@@ -683,7 +687,9 @@ router_initialize_tls_context(void)
if (!lifetime) { /* we should guess a good ssl cert lifetime */
/* choose between 5 and 365 days, and round to the day */
- lifetime = 5*24*3600 + crypto_rand_int(361*24*3600);
+ unsigned int five_days = 5*24*3600;
+ unsigned int one_year = 365*24*3600;
+ lifetime = crypto_rand_int_range(five_days, one_year);
lifetime -= lifetime % (24*3600);
if (crypto_rand_int(2)) {
@@ -861,6 +867,10 @@ init_keys(void)
set_client_identity_key(prkey);
}
+ /* 1d. Load all ed25519 keys */
+ if (load_ed_keys(options,now) < 0)
+ return -1;
+
/* 2. Read onion key. Make it if none is found. */
keydir = get_datadir_fname2("keys", "secret_onion_key");
log_info(LD_GENERAL,"Reading/making onion key \"%s\"...",keydir);
@@ -926,6 +936,13 @@ init_keys(void)
return -1;
}
+ /* 3b. Get an ed25519 link certificate. Note that we need to do this
+ * after we set up the TLS context */
+ if (generate_ed_link_cert(options, now) < 0) {
+ log_err(LD_GENERAL,"Couldn't make link cert");
+ return -1;
+ }
+
/* 4. Build our router descriptor. */
/* Must be called after keys are initialized. */
mydesc = router_get_my_descriptor();
@@ -1802,12 +1819,15 @@ router_pick_published_address(const or_options_t *options, uint32_t *addr)
return 0;
}
-/** If <b>force</b> is true, or our descriptor is out-of-date, rebuild a fresh
- * routerinfo, signed server descriptor, and extra-info document for this OR.
- * Return 0 on success, -1 on temporary error.
+/** Build a fresh routerinfo, signed server descriptor, and extra-info document
+ * for this OR. Set r to the generated routerinfo, e to the generated
+ * extra-info document. Return 0 on success, -1 on temporary error. Failure to
+ * generate an extra-info document is not an error and is indicated by setting
+ * e to NULL. Caller is responsible for freeing generated documents if 0 is
+ * returned.
*/
int
-router_rebuild_descriptor(int force)
+router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e)
{
routerinfo_t *ri;
extrainfo_t *ei;
@@ -1816,20 +1836,11 @@ router_rebuild_descriptor(int force)
int hibernating = we_are_hibernating();
const or_options_t *options = get_options();
- if (desc_clean_since && !force)
- return 0;
-
- if (router_pick_published_address(options, &addr) < 0 ||
- router_get_advertised_or_port(options) == 0) {
- /* Stop trying to rebuild our descriptor every second. We'll
- * learn that it's time to try again when ip_address_changed()
- * marks it dirty. */
- desc_clean_since = time(NULL);
+ if (router_pick_published_address(options, &addr) < 0) {
+ log_warn(LD_CONFIG, "Don't know my address while generating descriptor");
return -1;
}
- log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : "");
-
ri = tor_malloc_zero(sizeof(routerinfo_t));
ri->cache_info.routerlist_index = -1;
ri->nickname = tor_strdup(options->Nickname);
@@ -1876,6 +1887,8 @@ router_rebuild_descriptor(int force)
routerinfo_free(ri);
return -1;
}
+ ri->signing_key_cert = tor_cert_dup(get_master_signing_key_cert());
+
get_platform_str(platform, sizeof(platform));
ri->platform = tor_strdup(platform);
@@ -1966,10 +1979,12 @@ router_rebuild_descriptor(int force)
ei->cache_info.is_extrainfo = 1;
strlcpy(ei->nickname, get_options()->Nickname, sizeof(ei->nickname));
ei->cache_info.published_on = ri->cache_info.published_on;
+ ei->signing_key_cert = tor_cert_dup(get_master_signing_key_cert());
memcpy(ei->cache_info.identity_digest, ri->cache_info.identity_digest,
DIGEST_LEN);
if (extrainfo_dump_to_string(&ei->cache_info.signed_descriptor_body,
- ei, get_server_identity_key()) < 0) {
+ ei, get_server_identity_key(),
+ get_master_signing_keypair()) < 0) {
log_warn(LD_BUG, "Couldn't generate extra-info descriptor.");
extrainfo_free(ei);
ei = NULL;
@@ -1979,6 +1994,10 @@ router_rebuild_descriptor(int force)
router_get_extrainfo_hash(ei->cache_info.signed_descriptor_body,
ei->cache_info.signed_descriptor_len,
ei->cache_info.signed_descriptor_digest);
+ crypto_digest256((char*) ei->digest256,
+ ei->cache_info.signed_descriptor_body,
+ ei->cache_info.signed_descriptor_len,
+ DIGEST_SHA256);
}
/* Now finish the router descriptor. */
@@ -1986,12 +2005,18 @@ router_rebuild_descriptor(int force)
memcpy(ri->cache_info.extra_info_digest,
ei->cache_info.signed_descriptor_digest,
DIGEST_LEN);
+ memcpy(ri->extra_info_digest256,
+ ei->digest256,
+ DIGEST256_LEN);
} else {
/* ri was allocated with tor_malloc_zero, so there is no need to
* zero ri->cache_info.extra_info_digest here. */
}
- if (! (ri->cache_info.signed_descriptor_body = router_dump_router_to_string(
- ri, get_server_identity_key()))) {
+ if (! (ri->cache_info.signed_descriptor_body =
+ router_dump_router_to_string(ri, get_server_identity_key(),
+ get_onion_key(),
+ get_current_curve25519_keypair(),
+ get_master_signing_keypair())) ) {
log_warn(LD_BUG, "Couldn't generate router descriptor.");
routerinfo_free(ri);
extrainfo_free(ei);
@@ -2024,6 +2049,41 @@ router_rebuild_descriptor(int force)
tor_assert(! routerinfo_incompatible_with_extrainfo(ri, ei, NULL, NULL));
}
+ *r = ri;
+ *e = ei;
+ return 0;
+}
+
+/** If <b>force</b> is true, or our descriptor is out-of-date, rebuild a fresh
+ * routerinfo, signed server descriptor, and extra-info document for this OR.
+ * Return 0 on success, -1 on temporary error.
+ */
+int
+router_rebuild_descriptor(int force)
+{
+ routerinfo_t *ri;
+ extrainfo_t *ei;
+ uint32_t addr;
+ const or_options_t *options = get_options();
+
+ if (desc_clean_since && !force)
+ return 0;
+
+ if (router_pick_published_address(options, &addr) < 0 ||
+ router_get_advertised_or_port(options) == 0) {
+ /* Stop trying to rebuild our descriptor every second. We'll
+ * learn that it's time to try again when ip_address_changed()
+ * marks it dirty. */
+ desc_clean_since = time(NULL);
+ return -1;
+ }
+
+ log_info(LD_OR, "Rebuilding relay descriptor%s", force ? " (forced)" : "");
+
+ if (router_build_fresh_descriptor(&ri, &ei) < 0) {
+ return -1;
+ }
+
routerinfo_free(desc_routerinfo);
desc_routerinfo = ri;
extrainfo_free(desc_extrainfo);
@@ -2297,22 +2357,28 @@ get_platform_str(char *platform, size_t len)
*/
char *
router_dump_router_to_string(routerinfo_t *router,
- crypto_pk_t *ident_key)
+ const crypto_pk_t *ident_key,
+ const crypto_pk_t *tap_key,
+ const curve25519_keypair_t *ntor_keypair,
+ const ed25519_keypair_t *signing_keypair)
{
char *address = NULL;
char *onion_pkey = NULL; /* Onion key, PEM-encoded. */
char *identity_pkey = NULL; /* Identity key, PEM-encoded. */
- char digest[DIGEST_LEN];
+ char digest[DIGEST256_LEN];
char published[ISO_TIME_LEN+1];
char fingerprint[FINGERPRINT_LEN+1];
- int has_extra_info_digest;
- char extra_info_digest[HEX_DIGEST_LEN+1];
+ char *extra_info_line = NULL;
size_t onion_pkeylen, identity_pkeylen;
char *family_line = NULL;
char *extra_or_address = NULL;
const or_options_t *options = get_options();
smartlist_t *chunks = NULL;
char *output = NULL;
+ const int emit_ed_sigs = signing_keypair && router->signing_key_cert;
+ char *ed_cert_line = NULL;
+ char *rsa_tap_cc_line = NULL;
+ char *ntor_cc_line = NULL;
/* Make sure the identity key matches the one in the routerinfo. */
if (!crypto_pk_eq_keys(ident_key, router->identity_pkey)) {
@@ -2320,6 +2386,16 @@ router_dump_router_to_string(routerinfo_t *router,
"match router's public key!");
goto err;
}
+ if (emit_ed_sigs) {
+ if (!router->signing_key_cert->signing_key_included ||
+ !ed25519_pubkey_eq(&router->signing_key_cert->signed_key,
+ &signing_keypair->pubkey)) {
+ log_warn(LD_BUG, "Tried to sign a router descriptor with a mismatched "
+ "ed25519 key chain %d",
+ router->signing_key_cert->signing_key_included);
+ goto err;
+ }
+ }
/* record our fingerprint, so we can include it in the descriptor */
if (crypto_pk_get_fingerprint(router->identity_pkey, fingerprint, 1)<0) {
@@ -2327,6 +2403,30 @@ router_dump_router_to_string(routerinfo_t *router,
goto err;
}
+ if (emit_ed_sigs) {
+ /* Encode ed25519 signing cert */
+ char ed_cert_base64[256];
+ char ed_fp_base64[ED25519_BASE64_LEN+1];
+ if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64),
+ (const char*)router->signing_key_cert->encoded,
+ router->signing_key_cert->encoded_len,
+ BASE64_ENCODE_MULTILINE) < 0) {
+ log_err(LD_BUG,"Couldn't base64-encode signing key certificate!");
+ goto err;
+ }
+ if (ed25519_public_to_base64(ed_fp_base64,
+ &router->signing_key_cert->signing_key)<0) {
+ log_err(LD_BUG,"Couldn't base64-encode identity key\n");
+ goto err;
+ }
+ tor_asprintf(&ed_cert_line, "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "%s"
+ "-----END ED25519 CERT-----\n"
+ "master-key-ed25519 %s\n",
+ ed_cert_base64, ed_fp_base64);
+ }
+
/* PEM-encode the onion key */
if (crypto_pk_write_public_key_to_string(router->onion_pkey,
&onion_pkey,&onion_pkeylen)<0) {
@@ -2341,6 +2441,69 @@ router_dump_router_to_string(routerinfo_t *router,
goto err;
}
+ /* Cross-certify with RSA key */
+ if (tap_key && router->signing_key_cert &&
+ router->signing_key_cert->signing_key_included) {
+ char buf[256];
+ int tap_cc_len = 0;
+ uint8_t *tap_cc =
+ make_tap_onion_key_crosscert(tap_key,
+ &router->signing_key_cert->signing_key,
+ router->identity_pkey,
+ &tap_cc_len);
+ if (!tap_cc) {
+ log_warn(LD_BUG,"make_tap_onion_key_crosscert failed!");
+ goto err;
+ }
+
+ if (base64_encode(buf, sizeof(buf), (const char*)tap_cc, tap_cc_len,
+ BASE64_ENCODE_MULTILINE) < 0) {
+ log_warn(LD_BUG,"base64_encode(rsa_crosscert) failed!");
+ tor_free(tap_cc);
+ goto err;
+ }
+ tor_free(tap_cc);
+
+ tor_asprintf(&rsa_tap_cc_line,
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "%s"
+ "-----END CROSSCERT-----\n", buf);
+ }
+
+ /* Cross-certify with onion keys */
+ if (ntor_keypair && router->signing_key_cert &&
+ router->signing_key_cert->signing_key_included) {
+ int sign = 0;
+ char buf[256];
+ /* XXXX Base the expiration date on the actual onion key expiration time?*/
+ tor_cert_t *cert =
+ make_ntor_onion_key_crosscert(ntor_keypair,
+ &router->signing_key_cert->signing_key,
+ router->cache_info.published_on,
+ MIN_ONION_KEY_LIFETIME, &sign);
+ if (!cert) {
+ log_warn(LD_BUG,"make_ntor_onion_key_crosscert failed!");
+ goto err;
+ }
+ tor_assert(sign == 0 || sign == 1);
+
+ if (base64_encode(buf, sizeof(buf),
+ (const char*)cert->encoded, cert->encoded_len,
+ BASE64_ENCODE_MULTILINE)<0) {
+ log_warn(LD_BUG,"base64_encode(ntor_crosscert) failed!");
+ tor_cert_free(cert);
+ goto err;
+ }
+ tor_cert_free(cert);
+
+ tor_asprintf(&ntor_cc_line,
+ "ntor-onion-key-crosscert %d\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "%s"
+ "-----END ED25519 CERT-----\n", sign, buf);
+ }
+
/* Encode the publication time. */
format_iso_time(published, router->cache_info.published_on);
@@ -2353,12 +2516,19 @@ router_dump_router_to_string(routerinfo_t *router,
family_line = tor_strdup("");
}
- has_extra_info_digest =
- ! tor_digest_is_zero(router->cache_info.extra_info_digest);
-
- if (has_extra_info_digest) {
+ if (!tor_digest_is_zero(router->cache_info.extra_info_digest)) {
+ char extra_info_digest[HEX_DIGEST_LEN+1];
base16_encode(extra_info_digest, sizeof(extra_info_digest),
router->cache_info.extra_info_digest, DIGEST_LEN);
+ if (!tor_digest256_is_zero(router->extra_info_digest256)) {
+ char d256_64[BASE64_DIGEST256_LEN+1];
+ digest256_to_base64(d256_64, router->extra_info_digest256);
+ tor_asprintf(&extra_info_line, "extra-info-digest %s %s\n",
+ extra_info_digest, d256_64);
+ } else {
+ tor_asprintf(&extra_info_line, "extra-info-digest %s\n",
+ extra_info_digest);
+ }
}
if (router->ipv6_orport &&
@@ -2380,20 +2550,23 @@ router_dump_router_to_string(routerinfo_t *router,
smartlist_add_asprintf(chunks,
"router %s %s %d 0 %d\n"
"%s"
+ "%s"
"platform %s\n"
"protocols Link 1 2 Circuit 1\n"
"published %s\n"
"fingerprint %s\n"
"uptime %ld\n"
"bandwidth %d %d %d\n"
- "%s%s%s%s"
+ "%s%s"
"onion-key\n%s"
"signing-key\n%s"
+ "%s%s"
"%s%s%s%s",
router->nickname,
address,
router->or_port,
decide_to_advertise_dirport(options, router->dir_port),
+ ed_cert_line ? ed_cert_line : "",
extra_or_address ? extra_or_address : "",
router->platform,
published,
@@ -2402,12 +2575,12 @@ router_dump_router_to_string(routerinfo_t *router,
(int) router->bandwidthrate,
(int) router->bandwidthburst,
(int) router->bandwidthcapacity,
- has_extra_info_digest ? "extra-info-digest " : "",
- has_extra_info_digest ? extra_info_digest : "",
- has_extra_info_digest ? "\n" : "",
+ extra_info_line ? extra_info_line : "",
(options->DownloadExtraInfo || options->V3AuthoritativeDir) ?
"caches-extra-info\n" : "",
onion_pkey, identity_pkey,
+ rsa_tap_cc_line ? rsa_tap_cc_line : "",
+ ntor_cc_line ? ntor_cc_line : "",
family_line,
we_are_hibernating() ? "hibernating 1\n" : "",
options->HidServDirectoryV2 ? "hidden-service-dir\n" : "",
@@ -2424,7 +2597,7 @@ router_dump_router_to_string(routerinfo_t *router,
char kbuf[128];
base64_encode(kbuf, sizeof(kbuf),
(const char *)router->onion_curve25519_pkey->public_key,
- CURVE25519_PUBKEY_LEN);
+ CURVE25519_PUBKEY_LEN, BASE64_ENCODE_MULTILINE);
smartlist_add_asprintf(chunks, "ntor-onion-key %s", kbuf);
}
@@ -2450,7 +2623,24 @@ router_dump_router_to_string(routerinfo_t *router,
tor_free(p6);
}
- /* Sign the descriptor */
+ /* Sign the descriptor with Ed25519 */
+ if (emit_ed_sigs) {
+ smartlist_add(chunks, tor_strdup("router-sig-ed25519 "));
+ crypto_digest_smartlist_prefix(digest, DIGEST256_LEN,
+ ED_DESC_SIGNATURE_PREFIX,
+ chunks, "", DIGEST_SHA256);
+ ed25519_signature_t sig;
+ char buf[ED25519_SIG_BASE64_LEN+1];
+ if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN,
+ signing_keypair) < 0)
+ goto err;
+ if (ed25519_signature_to_base64(buf, &sig) < 0)
+ goto err;
+
+ smartlist_add_asprintf(chunks, "%s\n", buf);
+ }
+
+ /* Sign the descriptor with RSA */
smartlist_add(chunks, tor_strdup("router-signature\n"));
crypto_digest_smartlist(digest, DIGEST_LEN, chunks, "", DIGEST_SHA1);
@@ -2502,6 +2692,10 @@ router_dump_router_to_string(routerinfo_t *router,
tor_free(onion_pkey);
tor_free(identity_pkey);
tor_free(extra_or_address);
+ tor_free(ed_cert_line);
+ tor_free(rsa_tap_cc_line);
+ tor_free(ntor_cc_line);
+ tor_free(extra_info_line);
return output;
}
@@ -2645,7 +2839,8 @@ load_stats_file(const char *filename, const char *end_line, time_t now,
* success, negative on failure. */
int
extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
- crypto_pk_t *ident_key)
+ crypto_pk_t *ident_key,
+ const ed25519_keypair_t *signing_keypair)
{
const or_options_t *options = get_options();
char identity[HEX_DIGEST_LEN+1];
@@ -2655,20 +2850,46 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
int result;
static int write_stats_to_extrainfo = 1;
char sig[DIROBJ_MAX_SIG_LEN+1];
- char *s, *pre, *contents, *cp, *s_dup = NULL;
+ char *s = NULL, *pre, *contents, *cp, *s_dup = NULL;
time_t now = time(NULL);
smartlist_t *chunks = smartlist_new();
extrainfo_t *ei_tmp = NULL;
+ const int emit_ed_sigs = signing_keypair && extrainfo->signing_key_cert;
+ char *ed_cert_line = NULL;
base16_encode(identity, sizeof(identity),
extrainfo->cache_info.identity_digest, DIGEST_LEN);
format_iso_time(published, extrainfo->cache_info.published_on);
bandwidth_usage = rep_hist_get_bandwidth_lines();
+ if (emit_ed_sigs) {
+ if (!extrainfo->signing_key_cert->signing_key_included ||
+ !ed25519_pubkey_eq(&extrainfo->signing_key_cert->signed_key,
+ &signing_keypair->pubkey)) {
+ log_warn(LD_BUG, "Tried to sign a extrainfo descriptor with a "
+ "mismatched ed25519 key chain %d",
+ extrainfo->signing_key_cert->signing_key_included);
+ goto err;
+ }
+ char ed_cert_base64[256];
+ if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64),
+ (const char*)extrainfo->signing_key_cert->encoded,
+ extrainfo->signing_key_cert->encoded_len,
+ BASE64_ENCODE_MULTILINE) < 0) {
+ log_err(LD_BUG,"Couldn't base64-encode signing key certificate!");
+ goto err;
+ }
+ tor_asprintf(&ed_cert_line, "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "%s"
+ "-----END ED25519 CERT-----\n", ed_cert_base64);
+ } else {
+ ed_cert_line = tor_strdup("");
+ }
- tor_asprintf(&pre, "extra-info %s %s\npublished %s\n%s",
+ tor_asprintf(&pre, "extra-info %s %s\n%spublished %s\n%s",
extrainfo->nickname, identity,
+ ed_cert_line,
published, bandwidth_usage);
- tor_free(bandwidth_usage);
smartlist_add(chunks, pre);
if (geoip_is_loaded(AF_INET))
@@ -2726,6 +2947,23 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
}
}
+ if (emit_ed_sigs) {
+ char digest[DIGEST256_LEN];
+ smartlist_add(chunks, tor_strdup("router-sig-ed25519 "));
+ crypto_digest_smartlist_prefix(digest, DIGEST256_LEN,
+ ED_DESC_SIGNATURE_PREFIX,
+ chunks, "", DIGEST_SHA256);
+ ed25519_signature_t sig;
+ char buf[ED25519_SIG_BASE64_LEN+1];
+ if (ed25519_sign(&sig, (const uint8_t*)digest, DIGEST256_LEN,
+ signing_keypair) < 0)
+ goto err;
+ if (ed25519_signature_to_base64(buf, &sig) < 0)
+ goto err;
+
+ smartlist_add_asprintf(chunks, "%s\n", buf);
+ }
+
smartlist_add(chunks, tor_strdup("router-signature\n"));
s = smartlist_join_strings(chunks, "", 0, NULL);
@@ -2774,7 +3012,8 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
"adding statistics to this or any future "
"extra-info descriptors.");
write_stats_to_extrainfo = 0;
- result = extrainfo_dump_to_string(s_out, extrainfo, ident_key);
+ result = extrainfo_dump_to_string(s_out, extrainfo, ident_key,
+ signing_keypair);
goto done;
} else {
log_warn(LD_BUG, "We just generated an extrainfo descriptor we "
@@ -2796,7 +3035,9 @@ extrainfo_dump_to_string(char **s_out, extrainfo_t *extrainfo,
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
smartlist_free(chunks);
tor_free(s_dup);
+ tor_free(ed_cert_line);
extrainfo_free(ei_tmp);
+ tor_free(bandwidth_usage);
return result;
}
diff --git a/src/or/router.h b/src/or/router.h
index 8108ffb22f..61b35d6b5a 100644
--- a/src/or/router.h
+++ b/src/or/router.h
@@ -89,9 +89,13 @@ const uint8_t *router_get_my_id_digest(void);
int router_extrainfo_digest_is_me(const char *digest);
int router_is_me(const routerinfo_t *router);
int router_pick_published_address(const or_options_t *options, uint32_t *addr);
+int router_build_fresh_descriptor(routerinfo_t **r, extrainfo_t **e);
int router_rebuild_descriptor(int force);
char *router_dump_router_to_string(routerinfo_t *router,
- crypto_pk_t *ident_key);
+ const crypto_pk_t *ident_key,
+ const crypto_pk_t *tap_key,
+ const curve25519_keypair_t *ntor_keypair,
+ const ed25519_keypair_t *signing_keypair);
char *router_dump_exit_policy_to_string(const routerinfo_t *router,
int include_ipv4,
int include_ipv6);
@@ -106,7 +110,8 @@ int router_has_addr(const routerinfo_t *router, const tor_addr_t *addr);
int router_has_orport(const routerinfo_t *router,
const tor_addr_port_t *orport);
int extrainfo_dump_to_string(char **s, extrainfo_t *extrainfo,
- crypto_pk_t *ident_key);
+ crypto_pk_t *ident_key,
+ const ed25519_keypair_t *signing_keypair);
int is_legal_nickname(const char *s);
int is_legal_nickname_or_hexdigest(const char *s);
int is_legal_hexdigest(const char *s);
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
new file mode 100644
index 0000000000..e79204cf09
--- /dev/null
+++ b/src/or/routerkeys.c
@@ -0,0 +1,658 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "or.h"
+#include "config.h"
+#include "router.h"
+#include "routerkeys.h"
+#include "torcert.h"
+
+/**
+ * Read an ed25519 key and associated certificates from files beginning with
+ * <b>fname</b>, with certificate type <b>cert_type</b>. On failure, return
+ * NULL; on success return the keypair.
+ *
+ * If INIT_ED_KEY_CREATE is set in <b>flags</b>, then create the key (and
+ * certificate if requested) if it doesn't exist, and save it to disk.
+ *
+ * If INIT_ED_KEY_NEEDCERT is set in <b>flags</b>, load/create a certificate
+ * too and store it in *<b>cert_out</b>. Fail if the cert can't be
+ * found/created. To create a certificate, <b>signing_key</b> must be set to
+ * the key that should sign it; <b>now</b> to the current time, and
+ * <b>lifetime</b> to the lifetime of the key.
+ *
+ * If INIT_ED_KEY_REPLACE is set in <b>flags</b>, then create and save new key
+ * whether we can read the old one or not.
+ *
+ * If INIT_ED_KEY_EXTRA_STRONG is set in <b>flags</b>, set the extra_strong
+ * flag when creating the secret key.
+ *
+ * If INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT is set in <b>flags</b>, and
+ * we create a new certificate, create it with the signing key embedded.
+ *
+ * If INIT_ED_KEY_SPLIT is set in <b>flags</b>, and we create a new key,
+ * store the public key in a separate file from the secret key.
+ *
+ * If INIT_ED_KEY_MISSING_SECRET_OK is set in <b>flags</b>, and we find a
+ * public key file but no secret key file, return successfully anyway.
+ *
+ * If INIT_ED_KEY_OMIT_SECRET is set in <b>flags</b>, do not even try to
+ * load or return a secret key (but create and save on if needed).
+ */
+ed25519_keypair_t *
+ed_key_init_from_file(const char *fname, uint32_t flags,
+ int severity,
+ const ed25519_keypair_t *signing_key,
+ time_t now,
+ time_t lifetime,
+ uint8_t cert_type,
+ struct tor_cert_st **cert_out)
+{
+ char *secret_fname = NULL;
+ char *public_fname = NULL;
+ char *cert_fname = NULL;
+ int created_pk = 0, created_sk = 0, created_cert = 0;
+ const int try_to_load = ! (flags & INIT_ED_KEY_REPLACE);
+
+ char tag[8];
+ tor_snprintf(tag, sizeof(tag), "type%d", (int)cert_type);
+
+ tor_cert_t *cert = NULL;
+ char *got_tag = NULL;
+ ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t));
+
+ tor_asprintf(&secret_fname, "%s_secret_key", fname);
+ tor_asprintf(&public_fname, "%s_public_key", fname);
+ tor_asprintf(&cert_fname, "%s_cert", fname);
+
+ /* Try to read the secret key. */
+ const int have_secret = try_to_load &&
+ !(flags & INIT_ED_KEY_OMIT_SECRET) &&
+ ed25519_seckey_read_from_file(&keypair->seckey,
+ &got_tag, secret_fname) == 0;
+
+ if (have_secret) {
+ if (strcmp(got_tag, tag)) {
+ tor_log(severity, LD_OR, "%s has wrong tag", secret_fname);
+ goto err;
+ }
+ /* Derive the public key */
+ if (ed25519_public_key_generate(&keypair->pubkey, &keypair->seckey)<0) {
+ tor_log(severity, LD_OR, "%s can't produce a public key", secret_fname);
+ goto err;
+ }
+ }
+
+ /* If it's absent and that's okay, try to read the pubkey. */
+ int found_public = 0;
+ if (!have_secret && try_to_load) {
+ tor_free(got_tag);
+ found_public = ed25519_pubkey_read_from_file(&keypair->pubkey,
+ &got_tag, public_fname) == 0;
+ if (found_public && strcmp(got_tag, tag)) {
+ tor_log(severity, LD_OR, "%s has wrong tag", public_fname);
+ goto err;
+ }
+ }
+
+ /* If the secret key is absent and it's not allowed to be, fail. */
+ if (!have_secret && found_public && !(flags & INIT_ED_KEY_MISSING_SECRET_OK))
+ goto err;
+
+ /* If it's absent, and we're not supposed to make a new keypair, fail. */
+ if (!have_secret && !found_public && !(flags & INIT_ED_KEY_CREATE))
+ goto err;
+
+ /* if it's absent, make a new keypair and save it. */
+ if (!have_secret && !found_public) {
+ const int split = !! (flags & INIT_ED_KEY_SPLIT);
+ tor_free(keypair);
+ keypair = ed_key_new(signing_key, flags, now, lifetime,
+ cert_type, &cert);
+ if (!keypair) {
+ tor_log(severity, LD_OR, "Couldn't create keypair");
+ goto err;
+ }
+
+ created_pk = created_sk = created_cert = 1;
+ if (ed25519_seckey_write_to_file(&keypair->seckey, secret_fname, tag) < 0
+ ||
+ (split &&
+ ed25519_pubkey_write_to_file(&keypair->pubkey, public_fname, tag) < 0)
+ ||
+ (cert &&
+ crypto_write_tagged_contents_to_file(cert_fname, "ed25519v1-cert",
+ tag, cert->encoded, cert->encoded_len) < 0)) {
+ tor_log(severity, LD_OR, "Couldn't write keys or cert to file.");
+ goto err;
+ }
+ goto done;
+ }
+
+ /* If we're not supposed to get a cert, we're done. */
+ if (! (flags & INIT_ED_KEY_NEEDCERT))
+ goto done;
+
+ /* Read a cert. */
+ tor_free(got_tag);
+ uint8_t certbuf[256];
+ ssize_t cert_body_len = crypto_read_tagged_contents_from_file(
+ cert_fname, "ed25519v1-cert",
+ &got_tag, certbuf, sizeof(certbuf));
+ if (cert_body_len >= 0 && !strcmp(got_tag, tag))
+ cert = tor_cert_parse(certbuf, cert_body_len);
+
+ /* If we got it, check it to the extent we can. */
+ int bad_cert = 0;
+
+ if (! cert) {
+ tor_log(severity, LD_OR, "Cert was unparseable");
+ bad_cert = 1;
+ } else if (!tor_memeq(cert->signed_key.pubkey, keypair->pubkey.pubkey,
+ ED25519_PUBKEY_LEN)) {
+ tor_log(severity, LD_OR, "Cert was for wrong key");
+ bad_cert = 1;
+ } else if (signing_key &&
+ tor_cert_checksig(cert, &signing_key->pubkey, now) < 0 &&
+ (signing_key || cert->cert_expired)) {
+ tor_log(severity, LD_OR, "Can't check certificate");
+ bad_cert = 1;
+ }
+
+ if (bad_cert) {
+ tor_cert_free(cert);
+ cert = NULL;
+ }
+
+ /* If we got a cert, we're done. */
+ if (cert)
+ goto done;
+
+ /* If we didn't get a cert, and we're not supposed to make one, fail. */
+ if (!signing_key || !(flags & INIT_ED_KEY_CREATE))
+ goto err;
+
+ /* We have keys but not a certificate, so make one. */
+ uint32_t cert_flags = 0;
+ if (flags & INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT)
+ cert_flags |= CERT_FLAG_INCLUDE_SIGNING_KEY;
+ cert = tor_cert_create(signing_key, cert_type,
+ &keypair->pubkey,
+ now, lifetime,
+ cert_flags);
+
+ if (! cert)
+ goto err;
+
+ /* Write it to disk. */
+ created_cert = 1;
+ if (crypto_write_tagged_contents_to_file(cert_fname, "ed25519v1-cert",
+ tag, cert->encoded, cert->encoded_len) < 0) {
+ tor_log(severity, LD_OR, "Couldn't write cert to disk.");
+ goto err;
+ }
+
+ done:
+ if (cert_out)
+ *cert_out = cert;
+ else
+ tor_cert_free(cert);
+
+ goto cleanup;
+
+ err:
+ if (keypair)
+ memwipe(keypair, 0, sizeof(*keypair));
+ tor_free(keypair);
+ tor_cert_free(cert);
+ if (cert_out)
+ *cert_out = NULL;
+ if (created_sk)
+ unlink(secret_fname);
+ if (created_pk)
+ unlink(public_fname);
+ if (created_cert)
+ unlink(cert_fname);
+
+ cleanup:
+ tor_free(secret_fname);
+ tor_free(public_fname);
+ tor_free(cert_fname);
+ tor_free(got_tag);
+
+ return keypair;
+}
+
+/**
+ * Create a new signing key and (optionally) certficiate; do not read or write
+ * from disk. See ed_key_init_from_file() for more information.
+ */
+ed25519_keypair_t *
+ed_key_new(const ed25519_keypair_t *signing_key,
+ uint32_t flags,
+ time_t now,
+ time_t lifetime,
+ uint8_t cert_type,
+ struct tor_cert_st **cert_out)
+{
+ if (cert_out)
+ *cert_out = NULL;
+
+ const int extra_strong = !! (flags & INIT_ED_KEY_EXTRA_STRONG);
+ ed25519_keypair_t *keypair = tor_malloc_zero(sizeof(ed25519_keypair_t));
+ if (ed25519_keypair_generate(keypair, extra_strong) < 0)
+ goto err;
+
+ if (! (flags & INIT_ED_KEY_NEEDCERT))
+ return keypair;
+
+ tor_assert(signing_key);
+ tor_assert(cert_out);
+ uint32_t cert_flags = 0;
+ if (flags & INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT)
+ cert_flags |= CERT_FLAG_INCLUDE_SIGNING_KEY;
+ tor_cert_t *cert = tor_cert_create(signing_key, cert_type,
+ &keypair->pubkey,
+ now, lifetime,
+ cert_flags);
+ if (! cert)
+ goto err;
+
+ *cert_out = cert;
+ return keypair;
+
+ err:
+ tor_free(keypair);
+ return NULL;
+}
+
+static ed25519_keypair_t *master_identity_key = NULL;
+static ed25519_keypair_t *master_signing_key = NULL;
+static ed25519_keypair_t *current_auth_key = NULL;
+static tor_cert_t *signing_key_cert = NULL;
+static tor_cert_t *link_cert_cert = NULL;
+static tor_cert_t *auth_key_cert = NULL;
+
+static uint8_t *rsa_ed_crosscert = NULL;
+static size_t rsa_ed_crosscert_len = 0;
+
+/**
+ * Running as a server: load, reload, or refresh our ed25519 keys and
+ * certificates, creating and saving new ones as needed.
+ */
+int
+load_ed_keys(const or_options_t *options, time_t now)
+{
+ ed25519_keypair_t *id = NULL;
+ ed25519_keypair_t *sign = NULL;
+ ed25519_keypair_t *auth = NULL;
+ const ed25519_keypair_t *sign_signing_key_with_id = NULL;
+ const ed25519_keypair_t *use_signing = NULL;
+ const tor_cert_t *check_signing_cert = NULL;
+ tor_cert_t *sign_cert = NULL;
+ tor_cert_t *auth_cert = NULL;
+
+#define FAIL(msg) do { \
+ log_warn(LD_OR, (msg)); \
+ goto err; \
+ } while (0)
+#define SET_KEY(key, newval) do { \
+ ed25519_keypair_free(key); \
+ key = (newval); \
+ } while (0)
+#define SET_CERT(cert, newval) do { \
+ tor_cert_free(cert); \
+ cert = (newval); \
+ } while (0)
+#define EXPIRES_SOON(cert, interval) \
+ (!(cert) || (cert)->valid_until < now + (interval))
+
+ /* XXXX support encrypted identity keys fully */
+
+ /* First try to get the signing key to see how it is. */
+ if (master_signing_key) {
+ check_signing_cert = signing_key_cert;
+ use_signing = master_signing_key;
+ } else {
+ char *fname =
+ options_get_datadir_fname2(options, "keys", "ed25519_signing");
+ sign = ed_key_init_from_file(
+ fname,
+ INIT_ED_KEY_NEEDCERT|
+ INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT,
+ LOG_INFO,
+ NULL, 0, 0, CERT_TYPE_ID_SIGNING, &sign_cert);
+ tor_free(fname);
+ check_signing_cert = sign_cert;
+ use_signing = sign;
+ }
+
+ const int need_new_signing_key =
+ NULL == use_signing ||
+ EXPIRES_SOON(check_signing_cert, 0);
+ const int want_new_signing_key =
+ need_new_signing_key ||
+ EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop);
+
+ {
+ uint32_t flags =
+ (INIT_ED_KEY_CREATE|INIT_ED_KEY_SPLIT|
+ INIT_ED_KEY_EXTRA_STRONG);
+ if (! need_new_signing_key)
+ flags |= INIT_ED_KEY_MISSING_SECRET_OK;
+ if (! want_new_signing_key)
+ flags |= INIT_ED_KEY_OMIT_SECRET;
+
+ char *fname =
+ options_get_datadir_fname2(options, "keys", "ed25519_master_id");
+ id = ed_key_init_from_file(
+ fname,
+ flags,
+ LOG_WARN, NULL, 0, 0, 0, NULL);
+ tor_free(fname);
+ if (!id)
+ FAIL("Missing identity key");
+ if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey)))
+ sign_signing_key_with_id = NULL;
+ else
+ sign_signing_key_with_id = id;
+ }
+
+ if (need_new_signing_key && NULL == sign_signing_key_with_id)
+ FAIL("Can't load master key make a new signing key.");
+
+ if (want_new_signing_key && sign_signing_key_with_id) {
+ uint32_t flags = (INIT_ED_KEY_CREATE|
+ INIT_ED_KEY_REPLACE|
+ INIT_ED_KEY_EXTRA_STRONG|
+ INIT_ED_KEY_NEEDCERT|
+ INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT);
+ char *fname =
+ options_get_datadir_fname2(options, "keys", "ed25519_signing");
+ sign = ed_key_init_from_file(fname,
+ flags, LOG_WARN,
+ sign_signing_key_with_id, now,
+ options->SigningKeyLifetime,
+ CERT_TYPE_ID_SIGNING, &sign_cert);
+ tor_free(fname);
+ if (!sign)
+ FAIL("Missing signing key");
+ use_signing = sign;
+ } else if (want_new_signing_key) {
+ static ratelim_t missing_master = RATELIM_INIT(3600);
+ log_fn_ratelim(&missing_master, LOG_WARN, LD_OR,
+ "Signing key will expire soon, but I can't load the "
+ "master key to sign a new one!");
+ }
+
+ tor_assert(use_signing);
+
+ /* At this point we no longer need our secret identity key. So wipe
+ * it, if we loaded it in the first place. */
+ memwipe(id->seckey.seckey, 0, sizeof(id->seckey));
+
+ if (!rsa_ed_crosscert && server_mode(options)) {
+ uint8_t *crosscert;
+ ssize_t crosscert_len = tor_make_rsa_ed25519_crosscert(&id->pubkey,
+ get_server_identity_key(),
+ now+10*365*86400,/*XXXX*/
+ &crosscert);
+ rsa_ed_crosscert_len = crosscert_len;
+ rsa_ed_crosscert = crosscert;
+ }
+
+ if (!current_auth_key ||
+ EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop)) {
+ auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT,
+ now,
+ options->TestingAuthKeyLifetime,
+ CERT_TYPE_SIGNING_AUTH, &auth_cert);
+
+ if (!auth)
+ FAIL("Can't create auth key");
+ }
+
+ /* We've generated or loaded everything. Put them in memory. */
+
+ if (! master_identity_key) {
+ SET_KEY(master_identity_key, id);
+ } else {
+ tor_free(id);
+ }
+ if (sign) {
+ SET_KEY(master_signing_key, sign);
+ SET_CERT(signing_key_cert, sign_cert);
+ }
+ if (auth) {
+ SET_KEY(current_auth_key, auth);
+ SET_CERT(auth_key_cert, auth_cert);
+ }
+
+ return 0;
+ err:
+ ed25519_keypair_free(id);
+ ed25519_keypair_free(sign);
+ ed25519_keypair_free(auth);
+ tor_cert_free(sign_cert);
+ tor_cert_free(auth_cert);
+ return -1;
+}
+
+/**DOCDOC*/
+int
+generate_ed_link_cert(const or_options_t *options, time_t now)
+{
+ const tor_x509_cert_t *link = NULL, *id = NULL;
+ tor_cert_t *link_cert = NULL;
+
+ if (tor_tls_get_my_certs(1, &link, &id) < 0 || link == NULL) {
+ log_warn(LD_OR, "Can't get my x509 link cert.");
+ return -1;
+ }
+
+ const digests_t *digests = tor_x509_cert_get_cert_digests(link);
+
+ if (link_cert_cert &&
+ ! EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop) &&
+ fast_memeq(digests->d[DIGEST_SHA256], link_cert_cert->signed_key.pubkey,
+ DIGEST256_LEN)) {
+ return 0;
+ }
+
+ ed25519_public_key_t dummy_key;
+ memcpy(dummy_key.pubkey, digests->d[DIGEST_SHA256], DIGEST256_LEN);
+
+ link_cert = tor_cert_create(get_master_signing_keypair(),
+ CERT_TYPE_SIGNING_LINK,
+ &dummy_key,
+ now,
+ options->TestingLinkCertLifetime, 0);
+
+ if (link_cert) {
+ SET_CERT(link_cert_cert, link_cert);
+ }
+ return 0;
+}
+
+#undef FAIL
+#undef SET_KEY
+#undef SET_CERT
+
+int
+should_make_new_ed_keys(const or_options_t *options, const time_t now)
+{
+ if (!master_identity_key ||
+ !master_signing_key ||
+ !current_auth_key ||
+ !link_cert_cert ||
+ EXPIRES_SOON(signing_key_cert, options->TestingSigningKeySlop) ||
+ EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop) ||
+ EXPIRES_SOON(link_cert_cert, options->TestingLinkKeySlop))
+ return 1;
+
+ const tor_x509_cert_t *link = NULL, *id = NULL;
+
+ if (tor_tls_get_my_certs(1, &link, &id) < 0 || link == NULL)
+ return 1;
+
+ const digests_t *digests = tor_x509_cert_get_cert_digests(link);
+
+ if (!fast_memeq(digests->d[DIGEST_SHA256],
+ link_cert_cert->signed_key.pubkey,
+ DIGEST256_LEN)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+#undef EXPIRES_SOON
+
+const ed25519_public_key_t *
+get_master_identity_key(void)
+{
+ if (!master_identity_key)
+ return NULL;
+ return &master_identity_key->pubkey;
+}
+
+const ed25519_keypair_t *
+get_master_signing_keypair(void)
+{
+ return master_signing_key;
+}
+
+const struct tor_cert_st *
+get_master_signing_key_cert(void)
+{
+ return signing_key_cert;
+}
+
+const ed25519_keypair_t *
+get_current_auth_keypair(void)
+{
+ return current_auth_key;
+}
+
+const tor_cert_t *
+get_current_link_cert_cert(void)
+{
+ return link_cert_cert;
+}
+
+const tor_cert_t *
+get_current_auth_key_cert(void)
+{
+ return auth_key_cert;
+}
+
+void
+get_master_rsa_crosscert(const uint8_t **cert_out,
+ size_t *size_out)
+{
+ *cert_out = rsa_ed_crosscert;
+ *size_out = rsa_ed_crosscert_len;
+}
+
+/** Construct cross-certification for the master identity key with
+ * the ntor onion key. Store the sign of the corresponding ed25519 public key
+ * in *<b>sign_out</b>. */
+tor_cert_t *
+make_ntor_onion_key_crosscert(const curve25519_keypair_t *onion_key,
+ const ed25519_public_key_t *master_id_key, time_t now, time_t lifetime,
+ int *sign_out)
+{
+ tor_cert_t *cert = NULL;
+ ed25519_keypair_t ed_onion_key;
+
+ if (ed25519_keypair_from_curve25519_keypair(&ed_onion_key, sign_out,
+ onion_key) < 0)
+ goto end;
+
+ cert = tor_cert_create(&ed_onion_key, CERT_TYPE_ONION_ID, master_id_key,
+ now, lifetime, 0);
+
+ end:
+ memwipe(&ed_onion_key, 0, sizeof(ed_onion_key));
+ return cert;
+}
+
+/** Construct and return an RSA signature for the TAP onion key to
+ * cross-certify the RSA and Ed25519 identity keys. Set <b>len_out</b> to its
+ * length. */
+uint8_t *
+make_tap_onion_key_crosscert(const crypto_pk_t *onion_key,
+ const ed25519_public_key_t *master_id_key,
+ const crypto_pk_t *rsa_id_key,
+ int *len_out)
+{
+ uint8_t signature[PK_BYTES];
+ uint8_t signed_data[DIGEST_LEN + ED25519_PUBKEY_LEN];
+
+ *len_out = 0;
+ crypto_pk_get_digest(rsa_id_key, (char*)signed_data);
+ memcpy(signed_data + DIGEST_LEN, master_id_key->pubkey, ED25519_PUBKEY_LEN);
+
+ int r = crypto_pk_private_sign(onion_key,
+ (char*)signature, sizeof(signature),
+ (const char*)signed_data, sizeof(signed_data));
+ if (r < 0)
+ return NULL;
+
+ *len_out = r;
+
+ return tor_memdup(signature, r);
+}
+
+/** Check whether an RSA-TAP cross-certification is correct. Return 0 if it
+ * is, -1 if it isn't. */
+int
+check_tap_onion_key_crosscert(const uint8_t *crosscert,
+ int crosscert_len,
+ const crypto_pk_t *onion_pkey,
+ const ed25519_public_key_t *master_id_pkey,
+ const uint8_t *rsa_id_digest)
+{
+ uint8_t *cc = tor_malloc(crypto_pk_keysize(onion_pkey));
+ int cc_len =
+ crypto_pk_public_checksig(onion_pkey,
+ (char*)cc,
+ crypto_pk_keysize(onion_pkey),
+ (const char*)crosscert,
+ crosscert_len);
+ if (cc_len < 0) {
+ goto err;
+ }
+ if (cc_len < DIGEST_LEN + ED25519_PUBKEY_LEN) {
+ log_warn(LD_DIR, "Short signature on cross-certification with TAP key");
+ goto err;
+ }
+ if (tor_memneq(cc, rsa_id_digest, DIGEST_LEN) ||
+ tor_memneq(cc + DIGEST_LEN, master_id_pkey->pubkey,
+ ED25519_PUBKEY_LEN)) {
+ log_warn(LD_DIR, "Incorrect cross-certification with TAP key");
+ goto err;
+ }
+
+ tor_free(cc);
+ return 0;
+ err:
+ tor_free(cc);
+ return -1;
+}
+
+void
+routerkeys_free_all(void)
+{
+ ed25519_keypair_free(master_identity_key);
+ ed25519_keypair_free(master_signing_key);
+ ed25519_keypair_free(current_auth_key);
+ tor_cert_free(signing_key_cert);
+ tor_cert_free(link_cert_cert);
+ tor_cert_free(auth_key_cert);
+
+ master_identity_key = master_signing_key = NULL;
+ current_auth_key = NULL;
+ signing_key_cert = link_cert_cert = auth_key_cert = NULL;
+}
+
diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h
new file mode 100644
index 0000000000..b45a22ac12
--- /dev/null
+++ b/src/or/routerkeys.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TOR_ROUTERKEYS_H
+#define TOR_ROUTERKEYS_H
+
+#include "crypto_ed25519.h"
+
+#define INIT_ED_KEY_CREATE (1u<<0)
+#define INIT_ED_KEY_REPLACE (1u<<1)
+#define INIT_ED_KEY_SPLIT (1u<<2)
+#define INIT_ED_KEY_MISSING_SECRET_OK (1u<<3)
+#define INIT_ED_KEY_NEEDCERT (1u<<4)
+#define INIT_ED_KEY_EXTRA_STRONG (1u<<5)
+#define INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT (1u<<6)
+#define INIT_ED_KEY_OMIT_SECRET (1u<<7)
+
+struct tor_cert_st;
+ed25519_keypair_t *ed_key_init_from_file(const char *fname, uint32_t flags,
+ int severity,
+ const ed25519_keypair_t *signing_key,
+ time_t now,
+ time_t lifetime,
+ uint8_t cert_type,
+ struct tor_cert_st **cert_out);
+ed25519_keypair_t *ed_key_new(const ed25519_keypair_t *signing_key,
+ uint32_t flags,
+ time_t now,
+ time_t lifetime,
+ uint8_t cert_type,
+ struct tor_cert_st **cert_out);
+const ed25519_public_key_t *get_master_identity_key(void);
+const ed25519_keypair_t *get_master_signing_keypair(void);
+const struct tor_cert_st *get_master_signing_key_cert(void);
+
+const ed25519_keypair_t *get_current_auth_keypair(void);
+const struct tor_cert_st *get_current_link_cert_cert(void);
+const struct tor_cert_st *get_current_auth_key_cert(void);
+
+void get_master_rsa_crosscert(const uint8_t **cert_out,
+ size_t *size_out);
+
+struct tor_cert_st *make_ntor_onion_key_crosscert(
+ const curve25519_keypair_t *onion_key,
+ const ed25519_public_key_t *master_id_key,
+ time_t now, time_t lifetime,
+ int *sign_out);
+uint8_t *make_tap_onion_key_crosscert(const crypto_pk_t *onion_key,
+ const ed25519_public_key_t *master_id_key,
+ const crypto_pk_t *rsa_id_key,
+ int *len_out);
+
+int check_tap_onion_key_crosscert(const uint8_t *crosscert,
+ int crosscert_len,
+ const crypto_pk_t *onion_pkey,
+ const ed25519_public_key_t *master_id_pkey,
+ const uint8_t *rsa_id_digest);
+
+int load_ed_keys(const or_options_t *options, time_t now);
+int should_make_new_ed_keys(const or_options_t *options, const time_t now);
+
+int generate_ed_link_cert(const or_options_t *options, time_t now);
+
+void routerkeys_free_all(void);
+
+#endif
+
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index f4f6200bbc..35021964a2 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -13,6 +13,7 @@
#define ROUTERLIST_PRIVATE
#include "or.h"
+#include "crypto_ed25519.h"
#include "circuitstats.h"
#include "config.h"
#include "connection.h"
@@ -37,7 +38,9 @@
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
-#include "../common/sandbox.h"
+#include "sandbox.h"
+#include "torcert.h"
+
// #define DEBUG_ROUTERLIST
/****************************************************************************/
@@ -1498,9 +1501,6 @@ router_pick_directory_server_impl(dirinfo_type_t type, int flags,
if ((type & EXTRAINFO_DIRINFO) &&
!router_supports_extrainfo(node->identity, is_trusted_extrainfo))
continue;
- if ((type & MICRODESC_DIRINFO) && !is_trusted &&
- !node->rs->version_supports_microdesc_cache)
- continue;
if (for_guard && node->using_as_guard)
continue; /* Don't make the same node a guard twice. */
if (try_excluding &&
@@ -2663,6 +2663,7 @@ routerinfo_free(routerinfo_t *router)
tor_free(router->onion_curve25519_pkey);
if (router->identity_pkey)
crypto_pk_free(router->identity_pkey);
+ tor_cert_free(router->signing_key_cert);
if (router->declared_family) {
SMARTLIST_FOREACH(router->declared_family, char *, s, tor_free(s));
smartlist_free(router->declared_family);
@@ -2681,6 +2682,7 @@ extrainfo_free(extrainfo_t *extrainfo)
{
if (!extrainfo)
return;
+ tor_cert_free(extrainfo->signing_key_cert);
tor_free(extrainfo->cache_info.signed_descriptor_body);
tor_free(extrainfo->pending_sig);
@@ -3291,6 +3293,11 @@ router_add_to_routerlist(routerinfo_t *router, const char **msg,
old_router = router_get_mutable_by_digest(id_digest);
+ /* Make sure that it isn't expired. */
+ if (router->cert_expiration_time < approx_time()) {
+ return ROUTER_CERTS_EXPIRED;
+ }
+
/* Make sure that we haven't already got this exact descriptor. */
if (sdmap_get(routerlist->desc_digest_map,
router->cache_info.signed_descriptor_digest)) {
@@ -4897,7 +4904,7 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
signed_descriptor_t *sd,
const char **msg)
{
- int digest_matches, r=1;
+ int digest_matches, digest256_matches, r=1;
tor_assert(ri);
tor_assert(ei);
if (!sd)
@@ -4910,6 +4917,12 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
digest_matches = tor_memeq(ei->cache_info.signed_descriptor_digest,
sd->extra_info_digest, DIGEST_LEN);
+ /* Set digest256_matches to 1 if the digest is correct, or if no
+ * digest256 was in the ri. */
+ digest256_matches = tor_memeq(ei->digest256,
+ ri->extra_info_digest256, DIGEST256_LEN);
+ digest256_matches |=
+ tor_mem_is_zero(ri->extra_info_digest256, DIGEST256_LEN);
/* The identity must match exactly to have been generated at the same time
* by the same router. */
@@ -4920,6 +4933,11 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
goto err; /* different servers */
}
+ if (! tor_cert_opt_eq(ri->signing_key_cert, ei->signing_key_cert)) {
+ if (msg) *msg = "Extrainfo signing key cert didn't match routerinfo";
+ goto err; /* different servers */
+ }
+
if (ei->pending_sig) {
char signed_digest[128];
if (crypto_pk_public_checksig(ri->identity_pkey,
@@ -4946,6 +4964,11 @@ routerinfo_incompatible_with_extrainfo(const routerinfo_t *ri,
goto err;
}
+ if (!digest256_matches) {
+ if (msg) *msg = "Extrainfo digest did not match digest256 from routerdesc";
+ goto err; /* Digest doesn't match declared value. */
+ }
+
if (!digest_matches) {
if (msg) *msg = "Extrainfo digest did not match value from routerdesc";
goto err; /* Digest doesn't match declared value. */
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index 78c3fbb880..200533fe91 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -118,13 +118,15 @@ WRA_WAS_ADDED(was_router_added_t s) {
* - not in the consensus
* - neither in the consensus nor in any networkstatus document
* - it was outdated.
+ * - its certificates were expired.
*/
static INLINE int WRA_WAS_OUTDATED(was_router_added_t s)
{
return (s == ROUTER_WAS_TOO_OLD ||
s == ROUTER_IS_ALREADY_KNOWN ||
s == ROUTER_NOT_IN_CONSENSUS ||
- s == ROUTER_NOT_IN_CONSENSUS_OR_NETWORKSTATUS);
+ s == ROUTER_NOT_IN_CONSENSUS_OR_NETWORKSTATUS ||
+ s == ROUTER_CERTS_EXPIRED);
}
/** Return true iff the outcome code in <b>s</b> indicates that the descriptor
* was flat-out rejected. */
@@ -138,7 +140,8 @@ static INLINE int WRA_NEVER_DOWNLOADABLE(was_router_added_t s)
{
return (s == ROUTER_AUTHDIR_REJECTS ||
s == ROUTER_BAD_EI ||
- s == ROUTER_WAS_TOO_OLD);
+ s == ROUTER_WAS_TOO_OLD ||
+ s == ROUTER_CERTS_EXPIRED);
}
was_router_added_t router_add_to_routerlist(routerinfo_t *router,
const char **msg,
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 9c6651292c..ae50cda248 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -24,8 +24,11 @@
#include "microdesc.h"
#include "networkstatus.h"
#include "rephist.h"
+#include "routerkeys.h"
#include "routerparse.h"
#include "entrynodes.h"
+#include "torcert.h"
+
#undef log
#include <math.h>
@@ -69,6 +72,7 @@ typedef enum {
K_CLIENT_VERSIONS,
K_SERVER_VERSIONS,
K_OR_ADDRESS,
+ K_ID,
K_P,
K_P6,
K_R,
@@ -83,6 +87,11 @@ typedef enum {
K_HIDDEN_SERVICE_DIR,
K_ALLOW_SINGLE_HOP_EXITS,
K_IPV6_POLICY,
+ K_ROUTER_SIG_ED25519,
+ K_IDENTITY_ED25519,
+ K_MASTER_KEY_ED25519,
+ K_ONION_KEY_CROSSCERT,
+ K_NTOR_ONION_KEY_CROSSCERT,
K_DIRREQ_END,
K_DIRREQ_V2_IPS,
@@ -293,6 +302,13 @@ static token_rule_t routerdesc_token_table[] = {
T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ),
T01("extra-info-digest", K_EXTRA_INFO_DIGEST, GE(1), NO_OBJ ),
T01("hidden-service-dir", K_HIDDEN_SERVICE_DIR, NO_ARGS, NO_OBJ ),
+ T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ),
+ T01("master-key-ed25519", K_MASTER_KEY_ED25519, GE(1), NO_OBJ ),
+ T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ),
+ T01("onion-key-crosscert", K_ONION_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ),
+ T01("ntor-onion-key-crosscert", K_NTOR_ONION_KEY_CROSSCERT,
+ EQ(1), NEED_OBJ ),
+
T01("allow-single-hop-exits",K_ALLOW_SINGLE_HOP_EXITS, NO_ARGS, NO_OBJ ),
T01("family", K_FAMILY, ARGS, NO_OBJ ),
@@ -310,6 +326,8 @@ static token_rule_t routerdesc_token_table[] = {
static token_rule_t extrainfo_token_table[] = {
T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ),
T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
+ T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ),
+ T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ),
T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ),
@@ -353,6 +371,7 @@ static token_rule_t rtrstatus_token_table[] = {
T01("v", K_V, CONCAT_ARGS, NO_OBJ ),
T01("w", K_W, ARGS, NO_OBJ ),
T0N("m", K_M, CONCAT_ARGS, NO_OBJ ),
+ T0N("id", K_ID, GE(2), NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
END_OF_TABLE
};
@@ -490,6 +509,7 @@ static token_rule_t networkstatus_detached_signature_token_table[] = {
static token_rule_t microdesc_token_table[] = {
T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024),
T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ),
+ T0N("id", K_ID, GE(2), NO_OBJ ),
T0N("a", K_A, GE(1), NO_OBJ ),
T01("family", K_FAMILY, ARGS, NO_OBJ ),
T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
@@ -506,6 +526,10 @@ static addr_policy_t *router_parse_addr_policy(directory_token_t *tok,
unsigned fmt_flags);
static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok);
+static int router_get_hash_impl_helper(const char *s, size_t s_len,
+ const char *start_str,
+ const char *end_str, char end_c,
+ const char **start_out, const char **end_out);
static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
const char *start_str, const char *end_str,
char end_char,
@@ -637,7 +661,7 @@ router_get_extrainfo_hash(const char *s, size_t s_len, char *digest)
char *
router_get_dirobj_signature(const char *digest,
size_t digest_len,
- crypto_pk_t *private_key)
+ const crypto_pk_t *private_key)
{
char *signature;
size_t i, keysize;
@@ -664,7 +688,8 @@ router_get_dirobj_signature(const char *digest,
goto truncated;
i = strlen(buf);
- if (base64_encode(buf+i, buf_len-i, signature, siglen) < 0) {
+ if (base64_encode(buf+i, buf_len-i, signature, siglen,
+ BASE64_ENCODE_MULTILINE) < 0) {
log_warn(LD_BUG,"couldn't base64-encode signature");
goto err;
}
@@ -857,8 +882,8 @@ check_signature_token(const char *digest,
tor_free(signed_digest);
return -1;
}
-// log_debug(LD_DIR,"Signed %s hash starts %s", doctype,
-// hex_str(signed_digest,4));
+ // log_debug(LD_DIR,"Signed %s hash starts %s", doctype,
+ // hex_str(signed_digest,4));
if (tor_memneq(digest, signed_digest, digest_len)) {
log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype);
tor_free(signed_digest);
@@ -1105,6 +1130,7 @@ router_parse_entry_from_string(const char *s, const char *end,
size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0;
int ok = 1;
memarea_t *area = NULL;
+ tor_cert_t *ntor_cc_cert = NULL;
/* Do not set this to '1' until we have parsed everything that we intend to
* parse that's covered by the hash. */
int can_dl_again = 0;
@@ -1177,9 +1203,11 @@ router_parse_entry_from_string(const char *s, const char *end,
}
tok = find_by_keyword(tokens, K_ROUTER);
+ const int router_token_pos = smartlist_pos(tokens, tok);
tor_assert(tok->n_args >= 5);
router = tor_malloc_zero(sizeof(routerinfo_t));
+ router->cert_expiration_time = TIME_MAX;
router->cache_info.routerlist_index = -1;
router->cache_info.annotations_len = s-start_of_annotations + prepend_len;
router->cache_info.signed_descriptor_len = end-s;
@@ -1310,6 +1338,172 @@ router_parse_entry_from_string(const char *s, const char *end,
log_warn(LD_DIR, "Couldn't calculate key digest"); goto err;
}
+ {
+ directory_token_t *ed_sig_tok, *ed_cert_tok, *cc_tap_tok, *cc_ntor_tok,
+ *master_key_tok;
+ ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519);
+ ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519);
+ master_key_tok = find_opt_by_keyword(tokens, K_MASTER_KEY_ED25519);
+ cc_tap_tok = find_opt_by_keyword(tokens, K_ONION_KEY_CROSSCERT);
+ cc_ntor_tok = find_opt_by_keyword(tokens, K_NTOR_ONION_KEY_CROSSCERT);
+ int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok +
+ !!cc_tap_tok + !!cc_ntor_tok;
+ if ((n_ed_toks != 0 && n_ed_toks != 4) ||
+ (n_ed_toks == 4 && !router->onion_curve25519_pkey)) {
+ log_warn(LD_DIR, "Router descriptor with only partial ed25519/"
+ "cross-certification support");
+ goto err;
+ }
+ if (master_key_tok && !ed_sig_tok) {
+ log_warn(LD_DIR, "Router descriptor has ed25519 master key but no "
+ "certificate");
+ goto err;
+ }
+ if (ed_sig_tok) {
+ tor_assert(ed_cert_tok && cc_tap_tok && cc_ntor_tok);
+ const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok);
+ if (ed_cert_token_pos == -1 || router_token_pos == -1 ||
+ (ed_cert_token_pos != router_token_pos + 1 &&
+ ed_cert_token_pos != router_token_pos - 1)) {
+ log_warn(LD_DIR, "Ed25519 certificate in wrong position");
+ goto err;
+ }
+ if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) {
+ log_warn(LD_DIR, "Ed25519 signature in wrong position");
+ goto err;
+ }
+ if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) {
+ log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor");
+ goto err;
+ }
+ if (strcmp(cc_ntor_tok->object_type, "ED25519 CERT")) {
+ log_warn(LD_DIR, "Wrong object type on ntor-onion-key-crosscert "
+ "in decriptor");
+ goto err;
+ }
+ if (strcmp(cc_tap_tok->object_type, "CROSSCERT")) {
+ log_warn(LD_DIR, "Wrong object type on onion-key-crosscert "
+ "in decriptor");
+ goto err;
+ }
+ if (strcmp(cc_ntor_tok->args[0], "0") &&
+ strcmp(cc_ntor_tok->args[0], "1")) {
+ log_warn(LD_DIR, "Bad sign bit on ntor-onion-key-crosscert");
+ goto err;
+ }
+ int ntor_cc_sign_bit = !strcmp(cc_ntor_tok->args[0], "1");
+
+ uint8_t d256[DIGEST256_LEN];
+ const char *signed_start, *signed_end;
+ tor_cert_t *cert = tor_cert_parse(
+ (const uint8_t*)ed_cert_tok->object_body,
+ ed_cert_tok->object_size);
+ if (! cert) {
+ log_warn(LD_DIR, "Couldn't parse ed25519 cert");
+ goto err;
+ }
+ router->signing_key_cert = cert; /* makes sure it gets freed. */
+
+ if (cert->cert_type != CERT_TYPE_ID_SIGNING ||
+ ! cert->signing_key_included) {
+ log_warn(LD_DIR, "Invalid form for ed25519 cert");
+ goto err;
+ }
+
+ if (master_key_tok) {
+ /* This token is optional, but if it's present, it must match
+ * the signature in the signing cert, or supplant it. */
+ tor_assert(master_key_tok->n_args >= 1);
+ ed25519_public_key_t pkey;
+ if (ed25519_public_from_base64(&pkey, master_key_tok->args[0])<0) {
+ log_warn(LD_DIR, "Can't parse ed25519 master key");
+ goto err;
+ }
+
+ if (fast_memneq(&cert->signing_key.pubkey,
+ pkey.pubkey, ED25519_PUBKEY_LEN)) {
+ log_warn(LD_DIR, "Ed25519 master key does not match "
+ "key in certificate");
+ goto err;
+ }
+ }
+ ntor_cc_cert = tor_cert_parse((const uint8_t*)cc_ntor_tok->object_body,
+ cc_ntor_tok->object_size);
+ if (!ntor_cc_cert) {
+ log_warn(LD_DIR, "Couldn't parse ntor-onion-key-crosscert cert");
+ goto err;
+ }
+ if (ntor_cc_cert->cert_type != CERT_TYPE_ONION_ID ||
+ ! ed25519_pubkey_eq(&ntor_cc_cert->signed_key, &cert->signing_key)) {
+ log_warn(LD_DIR, "Invalid contents for ntor-onion-key-crosscert cert");
+ goto err;
+ }
+
+ ed25519_public_key_t ntor_cc_pk;
+ if (ed25519_public_key_from_curve25519_public_key(&ntor_cc_pk,
+ router->onion_curve25519_pkey,
+ ntor_cc_sign_bit)<0) {
+ log_warn(LD_DIR, "Error converting onion key to ed25519");
+ goto err;
+ }
+
+ if (router_get_hash_impl_helper(s, end-s, "router ",
+ "\nrouter-sig-ed25519",
+ ' ', &signed_start, &signed_end) < 0) {
+ log_warn(LD_DIR, "Can't find ed25519-signed portion of descriptor");
+ goto err;
+ }
+ crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256);
+ crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX,
+ strlen(ED_DESC_SIGNATURE_PREFIX));
+ crypto_digest_add_bytes(d, signed_start, signed_end-signed_start);
+ crypto_digest_get_digest(d, (char*)d256, sizeof(d256));
+ crypto_digest_free(d);
+
+ ed25519_checkable_t check[3];
+ int check_ok[3];
+ if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) {
+ log_err(LD_BUG, "Couldn't create 'checkable' for cert.");
+ goto err;
+ }
+ if (tor_cert_get_checkable_sig(&check[1],
+ ntor_cc_cert, &ntor_cc_pk) < 0) {
+ log_err(LD_BUG, "Couldn't create 'checkable' for ntor_cc_cert.");
+ goto err;
+ }
+
+ if (ed25519_signature_from_base64(&check[2].signature,
+ ed_sig_tok->args[0])<0) {
+ log_warn(LD_DIR, "Couldn't decode ed25519 signature");
+ goto err;
+ }
+ check[2].pubkey = &cert->signed_key;
+ check[2].msg = d256;
+ check[2].len = DIGEST256_LEN;
+
+ if (ed25519_checksig_batch(check_ok, check, 3) < 0) {
+ log_warn(LD_DIR, "Incorrect ed25519 signature(s)");
+ goto err;
+ }
+
+ if (check_tap_onion_key_crosscert(
+ (const uint8_t*)cc_tap_tok->object_body,
+ (int)cc_tap_tok->object_size,
+ router->onion_pkey,
+ &cert->signing_key,
+ (const uint8_t*)router->cache_info.identity_digest)<0) {
+ log_warn(LD_DIR, "Incorrect TAP cross-verification");
+ goto err;
+ }
+
+ /* We check this before adding it to the routerlist. */
+ if (cert->valid_until < ntor_cc_cert->valid_until)
+ router->cert_expiration_time = cert->valid_until;
+ else
+ router->cert_expiration_time = ntor_cc_cert->valid_until;
+ }
+ }
+
if ((tok = find_opt_by_keyword(tokens, K_FINGERPRINT))) {
/* If there's a fingerprint line, it must match the identity digest. */
char d[DIGEST_LEN];
@@ -1401,6 +1595,14 @@ router_parse_entry_from_string(const char *s, const char *end,
} else {
log_warn(LD_DIR, "Invalid extra info digest %s", escaped(tok->args[0]));
}
+
+ if (tok->n_args >= 2) {
+ if (digest256_from_base64(router->extra_info_digest256, tok->args[1])
+ < 0) {
+ log_warn(LD_DIR, "Invalid extra info digest256 %s",
+ escaped(tok->args[1]));
+ }
+ }
}
if (find_opt_by_keyword(tokens, K_HIDDEN_SERVICE_DIR)) {
@@ -1436,6 +1638,7 @@ router_parse_entry_from_string(const char *s, const char *end,
routerinfo_free(router);
router = NULL;
done:
+ tor_cert_free(ntor_cc_cert);
if (tokens) {
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
smartlist_free(tokens);
@@ -1502,6 +1705,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
goto err;
}
+ /* XXXX Accept this in position 1 too, and ed identity in position 0. */
tok = smartlist_get(tokens,0);
if (tok->tp != K_EXTRA_INFO) {
log_warn(LD_DIR,"Entry does not start with \"extra-info\"");
@@ -1514,6 +1718,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
extrainfo->cache_info.signed_descriptor_body = tor_memdup_nulterm(s,end-s);
extrainfo->cache_info.signed_descriptor_len = end-s;
memcpy(extrainfo->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
+ crypto_digest256((char*)extrainfo->digest256, s, end-s, DIGEST_SHA256);
tor_assert(tok->n_args >= 2);
if (!is_legal_nickname(tok->args[0])) {
@@ -1536,6 +1741,87 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
goto err;
}
+ {
+ directory_token_t *ed_sig_tok, *ed_cert_tok;
+ ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519);
+ ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519);
+ int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok;
+ if (n_ed_toks != 0 && n_ed_toks != 2) {
+ log_warn(LD_DIR, "Router descriptor with only partial ed25519/"
+ "cross-certification support");
+ goto err;
+ }
+ if (ed_sig_tok) {
+ tor_assert(ed_cert_tok);
+ const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok);
+ if (ed_cert_token_pos != 1) {
+ /* Accept this in position 0 XXXX */
+ log_warn(LD_DIR, "Ed25519 certificate in wrong position");
+ goto err;
+ }
+ if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) {
+ log_warn(LD_DIR, "Ed25519 signature in wrong position");
+ goto err;
+ }
+ if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) {
+ log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor");
+ goto err;
+ }
+
+ uint8_t d256[DIGEST256_LEN];
+ const char *signed_start, *signed_end;
+ tor_cert_t *cert = tor_cert_parse(
+ (const uint8_t*)ed_cert_tok->object_body,
+ ed_cert_tok->object_size);
+ if (! cert) {
+ log_warn(LD_DIR, "Couldn't parse ed25519 cert");
+ goto err;
+ }
+ extrainfo->signing_key_cert = cert; /* makes sure it gets freed. */
+ if (cert->cert_type != CERT_TYPE_ID_SIGNING ||
+ ! cert->signing_key_included) {
+ log_warn(LD_DIR, "Invalid form for ed25519 cert");
+ goto err;
+ }
+
+ if (router_get_hash_impl_helper(s, end-s, "extra-info ",
+ "\nrouter-sig-ed25519",
+ ' ', &signed_start, &signed_end) < 0) {
+ log_warn(LD_DIR, "Can't find ed25519-signed portion of extrainfo");
+ goto err;
+ }
+ crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256);
+ crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX,
+ strlen(ED_DESC_SIGNATURE_PREFIX));
+ crypto_digest_add_bytes(d, signed_start, signed_end-signed_start);
+ crypto_digest_get_digest(d, (char*)d256, sizeof(d256));
+ crypto_digest_free(d);
+
+ ed25519_checkable_t check[2];
+ int check_ok[2];
+ if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) {
+ log_err(LD_BUG, "Couldn't create 'checkable' for cert.");
+ goto err;
+ }
+
+ if (ed25519_signature_from_base64(&check[1].signature,
+ ed_sig_tok->args[0])<0) {
+ log_warn(LD_DIR, "Couldn't decode ed25519 signature");
+ goto err;
+ }
+ check[1].pubkey = &cert->signed_key;
+ check[1].msg = d256;
+ check[1].len = DIGEST256_LEN;
+
+ if (ed25519_checksig_batch(check_ok, check, 2) < 0) {
+ log_warn(LD_DIR, "Incorrect ed25519 signature(s)");
+ goto err;
+ }
+ /* We don't check the certificate expiration time: checking that it
+ * matches the cert in the router descriptor is adequate. */
+ }
+ }
+
/* We've checked everything that's covered by the hash. */
can_dl_again = 1;
@@ -2015,10 +2301,7 @@ routerstatus_parse_entry_from_string(memarea_t *area,
tor_assert(tok->n_args == 1);
rs->version_known = 1;
if (strcmpstart(tok->args[0], "Tor ")) {
- rs->version_supports_microdesc_cache = 1;
} else {
- rs->version_supports_microdesc_cache =
- tor_version_supports_microdescriptors(tok->args[0]);
rs->version_supports_extend2_cells =
tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha");
}
@@ -2091,6 +2374,18 @@ routerstatus_parse_entry_from_string(memarea_t *area,
line->microdesc_hash_line = tor_strdup(t->args[0]);
vote_rs->microdesc = line;
}
+ if (t->tp == K_ID) {
+ tor_assert(t->n_args >= 2);
+ if (!strcmp(t->args[0], "ed25519")) {
+ vote_rs->has_ed25519_listing = 1;
+ if (strcmp(t->args[1], "none") &&
+ digest256_from_base64((char*)vote_rs->ed25519_id,
+ t->args[1])<0) {
+ log_warn(LD_DIR, "Bogus ed25519 key in networkstatus vote");
+ goto err;
+ }
+ }
+ }
} SMARTLIST_FOREACH_END(t);
} else if (flav == FLAV_MICRODESC) {
tok = find_opt_by_keyword(tokens, K_M);
@@ -2915,6 +3210,23 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
goto err;
}
}
+ if (ns_type != NS_TYPE_CONSENSUS) {
+ digest256map_t *ed_id_map = digest256map_new();
+ SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, vote_routerstatus_t *,
+ vrs) {
+ if (! vrs->has_ed25519_listing ||
+ tor_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN))
+ continue;
+ if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) {
+ log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not "
+ "unique");
+ digest256map_free(ed_id_map, NULL);
+ goto err;
+ }
+ digest256map_set(ed_id_map, vrs->ed25519_id, (void*)1);
+ } SMARTLIST_FOREACH_END(vrs);
+ digest256map_free(ed_id_map, NULL);
+ }
/* Parse footer; check signature. */
footer_tokens = smartlist_new();
@@ -3363,7 +3675,9 @@ router_parse_addr_policy_item_from_string,(const char *s, int assume_action))
{
directory_token_t *tok = NULL;
const char *cp, *eos;
- /* Longest possible policy is "accept ffff:ffff:..255/ffff:...255:0-65535".
+ /* Longest possible policy is
+ * "accept6 ffff:ffff:..255/ffff:...255:10000-65535",
+ * which contains 2 max-length IPv6 addresses, plus 21 characters.
* But note that there can be an arbitrary amount of space between the
* accept and the address:mask/port element. */
char line[TOR_ADDR_BUF_LEN*2 + 32];
@@ -4210,6 +4524,26 @@ microdescs_parse_from_string(const char *s, const char *eos,
tor_memdup(&k, sizeof(curve25519_public_key_t));
}
+ smartlist_t *id_lines = find_all_by_keyword(tokens, K_ID);
+ if (id_lines) {
+ SMARTLIST_FOREACH_BEGIN(id_lines, directory_token_t *, t) {
+ tor_assert(t->n_args >= 2);
+ if (!strcmp(t->args[0], "ed25519")) {
+ if (md->ed25519_identity_pkey) {
+ log_warn(LD_DIR, "Extra ed25519 key in microdesc");
+ goto next;
+ }
+ ed25519_public_key_t k;
+ if (ed25519_public_from_base64(&k, t->args[1])<0) {
+ log_warn(LD_DIR, "Bogus ed25519 key in microdesc");
+ goto next;
+ }
+ md->ed25519_identity_pkey = tor_memdup(&k, sizeof(k));
+ }
+ } SMARTLIST_FOREACH_END(t);
+ smartlist_free(id_lines);
+ }
+
{
smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
if (a_lines) {
@@ -4263,14 +4597,6 @@ microdescs_parse_from_string(const char *s, const char *eos,
return result;
}
-/** Return true iff this Tor version can answer directory questions
- * about microdescriptors. */
-int
-tor_version_supports_microdescriptors(const char *platform)
-{
- return tor_version_as_new_as(platform, "0.2.3.1-alpha");
-}
-
/** Parse the Tor version of the platform string <b>platform</b>,
* and compare it to the version in <b>cutoff</b>. Return 1 if
* the router is at least as new as the cutoff, else return 0.
@@ -4583,8 +4909,7 @@ rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
tor_assert(tok == smartlist_get(tokens, 0));
tor_assert(tok->n_args == 1);
- if (strlen(tok->args[0]) != REND_DESC_ID_V2_LEN_BASE32 ||
- strspn(tok->args[0], BASE32_CHARS) != REND_DESC_ID_V2_LEN_BASE32) {
+ if (!rend_valid_descriptor_id(tok->args[0])) {
log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
goto err;
}
diff --git a/src/or/routerparse.h b/src/or/routerparse.h
index fc21cb1041..85e4b7d88e 100644
--- a/src/or/routerparse.h
+++ b/src/or/routerparse.h
@@ -19,7 +19,7 @@ int router_get_extrainfo_hash(const char *s, size_t s_len, char *digest);
#define DIROBJ_MAX_SIG_LEN 256
char *router_get_dirobj_signature(const char *digest,
size_t digest_len,
- crypto_pk_t *private_key);
+ const crypto_pk_t *private_key);
int router_append_dirobj_signature(char *buf, size_t buf_len,
const char *digest,
size_t digest_len,
@@ -44,7 +44,6 @@ MOCK_DECL(addr_policy_t *, router_parse_addr_policy_item_from_string,
(const char *s, int assume_action));
version_status_t tor_version_is_obsolete(const char *myversion,
const char *versionlist);
-int tor_version_supports_microdescriptors(const char *platform);
int tor_version_as_new_as(const char *platform, const char *cutoff);
int tor_version_parse(const char *s, tor_version_t *out);
int tor_version_compare(tor_version_t *a, tor_version_t *b);
@@ -92,5 +91,7 @@ STATIC int routerstatus_parse_guardfraction(const char *guardfraction_str,
routerstatus_t *rs);
#endif
+#define ED_DESC_SIGNATURE_PREFIX "Tor router descriptor signature v1"
+
#endif
diff --git a/src/or/torcert.c b/src/or/torcert.c
new file mode 100644
index 0000000000..f028910a70
--- /dev/null
+++ b/src/or/torcert.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "crypto.h"
+#include "torcert.h"
+#include "ed25519_cert.h"
+#include "torlog.h"
+#include "util.h"
+#include "compat.h"
+#include "link_handshake.h"
+
+/** Helper for tor_cert_create(): signs any 32 bytes, not just an ed25519
+ * key.
+ */
+static tor_cert_t *
+tor_cert_sign_impl(const ed25519_keypair_t *signing_key,
+ uint8_t cert_type,
+ uint8_t signed_key_type,
+ const uint8_t signed_key_info[32],
+ time_t now, time_t lifetime,
+ uint32_t flags)
+{
+ tor_cert_t *torcert = NULL;
+
+ ed25519_cert_t *cert = ed25519_cert_new();
+ cert->cert_type = cert_type;
+ cert->exp_field = (uint32_t) CEIL_DIV(now + lifetime, 3600);
+ cert->cert_key_type = signed_key_type;
+ memcpy(cert->certified_key, signed_key_info, 32);
+
+ if (flags & CERT_FLAG_INCLUDE_SIGNING_KEY) {
+ ed25519_cert_extension_t *ext = ed25519_cert_extension_new();
+ ext->ext_type = CERTEXT_SIGNED_WITH_KEY;
+ memcpy(ext->un_signing_key, signing_key->pubkey.pubkey, 32);
+ ed25519_cert_add_ext(cert, ext);
+ ++cert->n_extensions;
+ }
+
+ const ssize_t alloc_len = ed25519_cert_encoded_len(cert);
+ tor_assert(alloc_len > 0);
+ uint8_t *encoded = tor_malloc(alloc_len);
+ const ssize_t real_len = ed25519_cert_encode(encoded, alloc_len, cert);
+ if (real_len < 0)
+ goto err;
+ tor_assert(real_len == alloc_len);
+ tor_assert(real_len > ED25519_SIG_LEN);
+ uint8_t *sig = encoded + (real_len - ED25519_SIG_LEN);
+ tor_assert(tor_mem_is_zero((char*)sig, ED25519_SIG_LEN));
+
+ ed25519_signature_t signature;
+ if (ed25519_sign(&signature, encoded,
+ real_len-ED25519_SIG_LEN, signing_key)<0) {
+ log_warn(LD_BUG, "Can't sign certificate");
+ goto err;
+ }
+ memcpy(sig, signature.sig, ED25519_SIG_LEN);
+
+ torcert = tor_cert_parse(encoded, real_len);
+ if (! torcert) {
+ log_warn(LD_BUG, "Generated a certificate we cannot parse");
+ goto err;
+ }
+
+ if (tor_cert_checksig(torcert, &signing_key->pubkey, now) < 0) {
+ log_warn(LD_BUG, "Generated a certificate whose signature we can't check");
+ goto err;
+ }
+
+ tor_free(encoded);
+
+ goto done;
+
+ err:
+ tor_cert_free(torcert);
+ torcert = NULL;
+ done:
+ ed25519_cert_free(cert);
+ tor_free(encoded);
+ return torcert;
+}
+
+/**
+ * Create and return a new new certificate of type <b>cert_type</b> to
+ * authenticate <b>signed_key</b> using the key <b>signing_key</b>. The
+ * certificate should remain valid for at least <b>lifetime</b> seconds after
+ * <b>now</b>.
+ *
+ * If CERT_FLAG_INCLUDE_SIGNING_KEY is set in <b>flags</b>, embed
+ * the public part of <b>signing_key</b> in the certificate.
+ */
+tor_cert_t *
+tor_cert_create(const ed25519_keypair_t *signing_key,
+ uint8_t cert_type,
+ const ed25519_public_key_t *signed_key,
+ time_t now, time_t lifetime,
+ uint32_t flags)
+{
+ return tor_cert_sign_impl(signing_key, cert_type,
+ SIGNED_KEY_TYPE_ED25519, signed_key->pubkey,
+ now, lifetime, flags);
+}
+
+/** Release all storage held for <b>cert</>. */
+void
+tor_cert_free(tor_cert_t *cert)
+{
+ if (! cert)
+ return;
+
+ if (cert->encoded)
+ memwipe(cert->encoded, 0, cert->encoded_len);
+ tor_free(cert->encoded);
+
+ memwipe(cert, 0, sizeof(tor_cert_t));
+ tor_free(cert);
+}
+
+/** Parse a certificate encoded with <b>len</b> bytes in <b>encoded</b>. */
+tor_cert_t *
+tor_cert_parse(const uint8_t *encoded, const size_t len)
+{
+ tor_cert_t *cert = NULL;
+ ed25519_cert_t *parsed = NULL;
+ ssize_t got_len = ed25519_cert_parse(&parsed, encoded, len);
+ if (got_len < 0 || (size_t) got_len != len)
+ goto err;
+
+ cert = tor_malloc_zero(sizeof(tor_cert_t));
+ cert->encoded = tor_memdup(encoded, len);
+ cert->encoded_len = len;
+
+ memcpy(cert->signed_key.pubkey, parsed->certified_key, 32);
+ cert->valid_until = parsed->exp_field * 3600;
+ cert->cert_type = parsed->cert_type;
+
+ for (unsigned i = 0; i < ed25519_cert_getlen_ext(parsed); ++i) {
+ ed25519_cert_extension_t *ext = ed25519_cert_get_ext(parsed, i);
+ if (ext->ext_type == CERTEXT_SIGNED_WITH_KEY) {
+ if (cert->signing_key_included)
+ goto err;
+
+ cert->signing_key_included = 1;
+ memcpy(cert->signing_key.pubkey, ext->un_signing_key, 32);
+ } else if (ext->ext_flags & CERTEXT_FLAG_AFFECTS_VALIDATION) {
+ /* Unrecognized extension with affects_validation set */
+ goto err;
+ }
+ }
+
+ goto done;
+ err:
+ tor_cert_free(cert);
+ cert = NULL;
+ done:
+ ed25519_cert_free(parsed);
+ return cert;
+}
+
+/** Fill in <b>checkable_out</b> with the information needed to check
+ * the signature on <b>cert</b> with <b>pubkey</b>. */
+int
+tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out,
+ const tor_cert_t *cert,
+ const ed25519_public_key_t *pubkey)
+{
+ if (! pubkey) {
+ if (cert->signing_key_included)
+ pubkey = &cert->signing_key;
+ else
+ return -1;
+ }
+
+ checkable_out->msg = cert->encoded;
+ checkable_out->pubkey = pubkey;
+ tor_assert(cert->encoded_len > ED25519_SIG_LEN);
+ const size_t signed_len = cert->encoded_len - ED25519_SIG_LEN;
+ checkable_out->len = signed_len;
+ memcpy(checkable_out->signature.sig,
+ cert->encoded + signed_len, ED25519_SIG_LEN);
+
+ return 0;
+}
+
+/** Validates the signature on <b>cert</b> with <b>pubkey</b> relative to
+ * the current time <b>now</b>. Return 0 on success, -1 on failure.
+ * Sets flags in <b>cert</b> as appropriate.
+ */
+int
+tor_cert_checksig(tor_cert_t *cert,
+ const ed25519_public_key_t *pubkey, time_t now)
+{
+ ed25519_checkable_t checkable;
+ int okay;
+
+ if (now > cert->valid_until) {
+ cert->cert_expired = 1;
+ return -1;
+ }
+
+ if (tor_cert_get_checkable_sig(&checkable, cert, pubkey) < 0)
+ return -1;
+
+ if (ed25519_checksig_batch(&okay, &checkable, 1) < 0) {
+ cert->sig_bad = 1;
+ return -1;
+ } else {
+ cert->sig_ok = 1;
+ memcpy(cert->signing_key.pubkey, checkable.pubkey->pubkey, 32);
+ cert->cert_valid = 1;
+ return 0;
+ }
+}
+
+/** Return a new copy of <b>cert</b> */
+tor_cert_t *
+tor_cert_dup(const tor_cert_t *cert)
+{
+ tor_cert_t *newcert = tor_memdup(cert, sizeof(tor_cert_t));
+ if (cert->encoded)
+ newcert->encoded = tor_memdup(cert->encoded, cert->encoded_len);
+ return newcert;
+}
+
+/** Return true iff cert1 and cert2 are the same cert. */
+int
+tor_cert_eq(const tor_cert_t *cert1, const tor_cert_t *cert2)
+{
+ tor_assert(cert1);
+ tor_assert(cert2);
+ return cert1->encoded_len == cert2->encoded_len &&
+ tor_memeq(cert1->encoded, cert2->encoded, cert1->encoded_len);
+}
+
+/** Return true iff cert1 and cert2 are the same cert, or if they are both
+ * NULL. */
+int
+tor_cert_opt_eq(const tor_cert_t *cert1, const tor_cert_t *cert2)
+{
+ if (cert1 == NULL && cert2 == NULL)
+ return 1;
+ if (!cert1 || !cert2)
+ return 0;
+ return tor_cert_eq(cert1, cert2);
+}
+
+/** Create new cross-certification object to certify <b>ed_key</b> as the
+ * master ed25519 identity key for the RSA identity key <b>rsa_key</b>.
+ * Allocates and stores the encoded certificate in *<b>cert</b>, and returns
+ * the number of bytes stored. Returns negative on error.*/
+ssize_t
+tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key,
+ const crypto_pk_t *rsa_key,
+ time_t expires,
+ uint8_t **cert)
+{
+ uint8_t *res;
+
+ rsa_ed_crosscert_t *cc = rsa_ed_crosscert_new();
+ memcpy(cc->ed_key, ed_key->pubkey, ED25519_PUBKEY_LEN);
+ cc->expiration = (uint32_t) CEIL_DIV(expires, 3600);
+ cc->sig_len = crypto_pk_keysize(rsa_key);
+ rsa_ed_crosscert_setlen_sig(cc, crypto_pk_keysize(rsa_key));
+
+ ssize_t alloc_sz = rsa_ed_crosscert_encoded_len(cc);
+ tor_assert(alloc_sz > 0);
+ res = tor_malloc_zero(alloc_sz);
+ ssize_t sz = rsa_ed_crosscert_encode(res, alloc_sz, cc);
+ tor_assert(sz > 0 && sz <= alloc_sz);
+
+ const int signed_part_len = 32 + 4;
+ int siglen = crypto_pk_private_sign(rsa_key,
+ (char*)rsa_ed_crosscert_getarray_sig(cc),
+ rsa_ed_crosscert_getlen_sig(cc),
+ (char*)res, signed_part_len);
+ tor_assert(siglen > 0 && siglen <= (int)crypto_pk_keysize(rsa_key));
+ tor_assert(siglen <= UINT8_MAX);
+ cc->sig_len = siglen;
+ rsa_ed_crosscert_setlen_sig(cc, siglen);
+
+ sz = rsa_ed_crosscert_encode(res, alloc_sz, cc);
+ rsa_ed_crosscert_free(cc);
+ *cert = res;
+ return sz;
+}
+
diff --git a/src/or/torcert.h b/src/or/torcert.h
new file mode 100644
index 0000000000..b67dc525a2
--- /dev/null
+++ b/src/or/torcert.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#ifndef TORCERT_H_INCLUDED
+#define TORCERT_H_INCLUDED
+
+#include "crypto_ed25519.h"
+
+#define SIGNED_KEY_TYPE_ED25519 0x01
+
+#define CERT_TYPE_ID_SIGNING 0x04
+#define CERT_TYPE_SIGNING_LINK 0x05
+#define CERT_TYPE_SIGNING_AUTH 0x06
+#define CERT_TYPE_ONION_ID 0x0A
+
+#define CERT_FLAG_INCLUDE_SIGNING_KEY 0x1
+
+/** An ed25519-signed certificate as used throughout the Tor protocol.
+ **/
+typedef struct tor_cert_st {
+ /** The key authenticated by this certificate */
+ ed25519_public_key_t signed_key;
+ /** The key that signed this certificate. This value may be unset if the
+ * certificate has never been checked, and didn't include its own key. */
+ ed25519_public_key_t signing_key;
+ /** A time after which this certificate will no longer be valid. */
+ time_t valid_until;
+
+ /** The encoded representation of this certificate */
+ uint8_t *encoded;
+ /** The length of <b>encoded</b> */
+ size_t encoded_len;
+
+ /** One of CERT_TYPE_... */
+ uint8_t cert_type;
+ /** True iff we received a signing key embedded in this certificate */
+ unsigned signing_key_included : 1;
+ /** True iff we checked the signature and found it bad */
+ unsigned sig_bad : 1;
+ /** True iff we checked the signature and found it correct */
+ unsigned sig_ok : 1;
+ /** True iff we checked the signature and first found that the cert
+ * had expired */
+ unsigned cert_expired : 1;
+ /** True iff we checked the signature and found the whole cert valid */
+ unsigned cert_valid : 1;
+} tor_cert_t;
+
+tor_cert_t *tor_cert_create(const ed25519_keypair_t *signing_key,
+ uint8_t cert_type,
+ const ed25519_public_key_t *signed_key,
+ time_t now, time_t lifetime,
+ uint32_t flags);
+
+tor_cert_t *tor_cert_parse(const uint8_t *cert, size_t certlen);
+
+void tor_cert_free(tor_cert_t *cert);
+
+int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out,
+ const tor_cert_t *out,
+ const ed25519_public_key_t *pubkey);
+
+int tor_cert_checksig(tor_cert_t *cert,
+ const ed25519_public_key_t *pubkey, time_t now);
+
+tor_cert_t *tor_cert_dup(const tor_cert_t *cert);
+int tor_cert_eq(const tor_cert_t *cert1, const tor_cert_t *cert2);
+int tor_cert_opt_eq(const tor_cert_t *cert1, const tor_cert_t *cert2);
+
+ssize_t tor_make_rsa_ed25519_crosscert(const ed25519_public_key_t *ed_key,
+ const crypto_pk_t *rsa_key,
+ time_t expires,
+ uint8_t **cert);
+
+#endif
+
diff --git a/src/or/transports.c b/src/or/transports.c
index 6f07054ea8..ba2c784c2c 100644
--- a/src/or/transports.c
+++ b/src/or/transports.c
@@ -1388,6 +1388,11 @@ create_managed_proxy_environment(const managed_proxy_t *mp)
} else {
smartlist_add_asprintf(envs, "TOR_PT_EXTENDED_SERVER_PORT=");
}
+
+ /* All new versions of tor will keep stdin open, so PTs can use it
+ * as a reliable termination detection mechanism.
+ */
+ smartlist_add_asprintf(envs, "TOR_PT_EXIT_ON_STDIN_CLOSE=1");
} else {
/* If ClientTransportPlugin has a HTTPS/SOCKS proxy configured, set the
* TOR_PT_PROXY line.
diff --git a/src/test/bench.c b/src/test/bench.c
index 5cbc072700..bc2b1f04d8 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -19,11 +19,9 @@ const char tor_git_revision[] = "";
#include "relay.h"
#include <openssl/opensslv.h>
#include <openssl/evp.h>
-#ifndef OPENSSL_NO_EC
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/obj_mac.h>
-#endif
#include "config.h"
#include "crypto_curve25519.h"
@@ -502,9 +500,6 @@ bench_dh(void)
" %f millisec each.\n", NANOCOUNT(start, end, iters)/1e6);
}
-#if (!defined(OPENSSL_NO_EC) \
- && OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,0,0))
-#define HAVE_EC_BENCHMARKS
static void
bench_ecdh_impl(int nid, const char *name)
{
@@ -554,7 +549,6 @@ bench_ecdh_p224(void)
{
bench_ecdh_impl(NID_secp224r1, "P-224");
}
-#endif
typedef void (*bench_fn)(void);
@@ -577,10 +571,8 @@ static struct benchmark_t benchmarks[] = {
ENT(cell_aes),
ENT(cell_ops),
ENT(dh),
-#ifdef HAVE_EC_BENCHMARKS
ENT(ecdh_p256),
ENT(ecdh_p224),
-#endif
{NULL,NULL,0}
};
@@ -625,7 +617,7 @@ main(int argc, const char **argv)
reset_perftime();
- crypto_seed_rng(1);
+ crypto_seed_rng();
crypto_init_siphash_key();
options = options_new();
init_logging(1);
diff --git a/src/test/bt_test.py b/src/test/bt_test.py
index 0afe797a6d..e694361703 100755
--- a/src/test/bt_test.py
+++ b/src/test/bt_test.py
@@ -36,7 +36,7 @@ LINES = sys.stdin.readlines()
for I in range(len(LINES)):
if matches(LINES[I:], FUNCNAMES):
print("OK")
- break
+ sys.exit(0)
else:
print("BAD")
-
+ sys.exit(1)
diff --git a/src/test/example_extrainfo.inc b/src/test/example_extrainfo.inc
index 606279a765..e096afd6c4 100644
--- a/src/test/example_extrainfo.inc
+++ b/src/test/example_extrainfo.inc
@@ -190,3 +190,236 @@ static const char EX_EI_BAD_PUBLISHED_KEY[] =
"BvG6303md3INygg+KP49RvWEJR/cU4RZ9QfHpORxH2OocMyRedw2rLex2E7jNNSi\n"
"52yd1sHFYI8ZQ4aff+ZHUjJUGKRyqpbc8okVbq/Rl7vug0dd12eHAgMBAAE=\n"
"-----END RSA PUBLIC KEY-----\n";
+
+static const char EX_EI_GOOD_ED_EI[] =
+ "extra-info emma A692FE045C32B5E3A54B52882EF678A9DAC46A73\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AYgHn/OKR8GHBlscN5VkO73wA9jSci8QgTM30615ZT44AQAgBAC08woT\n"
+ "MBZpKzRcaoEJhEG7+RmuYtnB2+nODk9IRIs8ZoyYPTZ6dLzI+MLMmtzUuo/Wmvw0\n"
+ "PflTyCb2RlWitOEhAErWH3Z9UmYGnzM/COId0Fe3ScSriyvRoFnJY1+GVAQ=\n"
+ "-----END ED25519 CERT-----\n"
+ "published 2014-10-05 20:07:00\n"
+ "router-sig-ed25519 a7K8nwfg+HrdlSGQwr9rnLBq0qozkyZZs6d6aiLEiXGdhV1r9KJncmlQ5SNoY/zMQlyQm8EV5rCyBiVliKQ1Bw\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "GvmCmIGgbC1DeawRyRuChy62VmBOG0EviryG/a2qSZiFy0iPPwqSp5ZyZDQEIEId\n"
+ "kkk1zPzK1+S3fmgOAXyXGH0r4YFkoLGnhMk07BoEwi6HEXzjJsabmcNkOHfaOWgs\n"
+ "/5nvnLfcmxL4c6FstZ7t9VQpE06y3GU0zwBeIy1qjp0=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ "\n"
+ ;
+const char EX_EI_GOOD_ED_EI_FP[] = "A692FE045C32B5E3A54B52882EF678A9DAC46A73";
+static const char EX_EI_GOOD_ED_EI_KEY[] =
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAM3jdYwjwGxDWYj/vyFkQT7RgeCNIn89Ei6D2+L/fdtFnqrMXOreFFHL\n"
+ "C7CK2v2uN3v+uXxfb5lADz3NcalxJrCfGTGtaBk7PwMZraTSh2luFKOvSRBQCmB1\n"
+ "yD5N0QqnIhBJoGr6NITpbWyiTKWvYLjl9PZd9af8e8jQCAa5P1j1AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ ;
+
+static const char EX_EI_ED_MISSING_SIG[] =
+ "extra-info rachel 2A7521497B91A8437021515308A47491164EDBA1\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AT2/T71LFYHiI1ppwNiuaewIu2Hq+GWWQ85O8gpWcUxeAQAgBAC2dgYu\n"
+ "moxhtuip7GVlthT9iomZKba1IllVa7uE1u2uO9BUYZQWXciFt7OnNzMH5mlffwxB\n"
+ "1dWCl+G5nbOsV5jYLbfhrF5afZotf+EQTfob4cCH79AV223LPcySbTHTtQ4=\n"
+ "-----END ED25519 CERT-----\n"
+ "published 2014-10-05 20:07:00\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "oypRD2IZQ5EttOE8dvofrW80nnBfijSkvYzBrM6H4KVeayRYvWfmi96dYO6ybMqm\n"
+ "Yp7Gs3ngqeeNdfHtkRPuQVUXUGYZgBTvYItuagnFlFgRqaHy0knwUIVOL35eqWYx\n"
+ "xSbQKA7fglxEDMFs/RK7FRP4dWc731ZMt5wzzfJHZ8E=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ "\n"
+ ;
+const char EX_EI_ED_MISSING_SIG_FP[] = "2A7521497B91A8437021515308A47491164EDBA1";
+static const char EX_EI_ED_MISSING_SIG_KEY[] =
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAOOB8ccxbtk2dB5FuKFhGndDcO6STNjB6KiG0b9X2QwKrOZMfmXSigto\n"
+ "mtC1JfPTxECayRjLSiP/9UD8iTVvlcnc8mMWBGM12Pa/KoCZRn7McHI3JJ7n9lfn\n"
+ "qw9+iZ9b/rBimzOb3W6k3uxzg9r8secdq4jJwTnwSjTObgxZtC8/AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ ;
+
+static const char EX_EI_ED_MISSING_CERT[] =
+ "extra-info lynne E88E43E86015345A323D93D825C33E4AD1028F65\n"
+ "published 2014-10-05 20:07:00\n"
+ "router-sig-ed25519 H4gKIKm5K9Pfkriy7SlMUD6BdYVp6B5mXKzR/rTyYlpH0tEZ4Fx2hlHNfNNdWXJieXzKZQZo8e7SOVzvrAC3CQ\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "dIrbQjK5T9t5KM8CpsMF85hh2i060oPIxzYQMgE1q4j99dtb/n7SE8nhj1Sjij4D\n"
+ "7JvTjGdLHi3bFSxXaSmla0wxD9PUYFN7VsBQmwSaDrqrzJFb1SGwZuzW1IEZ7BBi\n"
+ "H0czsxEteg5hcNRwISj5WVthuWmau9v13MijtZGSK40=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ "\n"
+ "\n"
+ ;
+const char EX_EI_ED_MISSING_CERT_FP[] = "E88E43E86015345A323D93D825C33E4AD1028F65";
+static const char EX_EI_ED_MISSING_CERT_KEY[] =
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBALjA/geb0TR9rp/UPvLhABQpB0XUDYuZAnLkrv+i7AAV7FemTDveEGnc\n"
+ "XdXNSusO1mHOquvr0YYKPhwauInxD56S8QOzLYiWWajGq8XHARQ33b4/9K2TUrAx\n"
+ "W9HTHV1U1zrPlCJtrkbjxsYoHpUg5ljzM7FGYGY5xuvyHu18SQvzAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ ;
+static const char EX_EI_ED_BAD_CERT1[] =
+ "extra-info marcie F78D8A655607D32281D02144817A4F1D26AE520F\n"
+ "identity-ed25519\n"
+ "-----BEGIN PLAGICAL SPELL-----\n"
+ "aaaa\n"
+ "-----END PLAGICAL SPELL\n"
+ "published 2014-10-05 20:07:00\n"
+ "router-sig-ed25519 KQJ+2AH7EkkjrD0RtDtUAIr+Vc7wndwILYnoUxFLSJiTP+5fMi54eFF/f1OgkG8gYyTh8phMij9WOxK/dsOpBg\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "XWD+P25AH6moi79j20Si3hqKGcJDws+FORL1MTu+GeJLV1mp5CR9N83UH4ffulcL\n"
+ "CpSSBDL/j74HqapzW7QvBx3FilaNT55GvcobZDFK4TKkCEyEmcuWKpEceBS7JTTV\n"
+ "SvwZeOObTjWPafELbsc/gI9Rh5Idwu7mZt3ZVntCGaQ=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+const char EX_EI_ED_BAD_CERT1_FP[] = "F78D8A655607D32281D02144817A4F1D26AE520F";
+static const char EX_EI_ED_BAD_CERT1_KEY[] =
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMlR46JhxsCmWYtmIB/JjTV2TUYIhJLmHy+X7FfkK3ZVQvvl9/3GSXFL\n"
+ "3USfyf3j34XLh8An7pJBi9LAHkIXgnRbglCud7dXoexabmC+c2mSbw5RnuxDGEwz\n"
+ "krXUph/r2b+2UY1CgEt28nFigaHrIQbCmF4szFX/2GPYCLi5SrRNAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ ;
+static const char EX_EI_ED_BAD_CERT2[] =
+ "extra-info jaeger 7C2B42E783C4E0EB0CC3BDB37385D16737BACFBD\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55Acpw27GZBdwGCgawCj2F/DPadt8F/9DnEWywEew1Yi3qAOtLpCB8KXL7\n"
+ "4w5deFW2RBg8qTondNSUvAmwYLbLjNXMmgA3+nkoJOP3fcmQMHz1jm5xzgs2lCVP\n"
+ "t5txApaBIA4=\n"
+ "-----END ED25519 CERT-----\n"
+ "published 2014-10-05 20:07:00\n"
+ "router-sig-ed25519 DRQ4MLOGosBbW8M+17klNu8uWVkPxErmmEYoSo6OuH2Tzrcs6sUY+8Xi2qLoV1SbOugJ214Htl0I+6ceag+vBA\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "DfdA+DbuN9nVJNujuSY5wNCDLk7Hfzkrde/sK0hVmZRvivtpF/Fy/dVQHHGNFY5i\n"
+ "L1cESAgq9HLdbHU+hcc08XXxTIaGwvoklcJClcG3ENVBWkTXbJNT+ifr7chEagIi\n"
+ "cVrtU6RVmzldSbyir8V/Z4S/Cm67gYAgjM5gfoFUqDs=\n"
+ "-----END SIGNATURE-----\n"
+ ;
+const char EX_EI_ED_BAD_CERT2_FP[] = "7C2B42E783C4E0EB0CC3BDB37385D16737BACFBD";
+static const char EX_EI_ED_BAD_CERT2_KEY[] =
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBALAM1F/0XJEsbxIQqb3+ObX/yGVnq9of8Q9sLsmxffD6hwVpCqnV3lTg\n"
+ "iC6+xZ/bSlTGLPi0k8QLCaTmYxgKwmlMPpbQZ4kpZUrsb9flKdChMN7w8hd48pY9\n"
+ "lu8QiAEgErsl5rCCJIHHjrxxM/Cnd0TnedRnj/Z2YqpNx/ggsmsRAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ ;
+static const char EX_EI_ED_BAD_SIG1[] =
+ "extra-info vary 5AC3A538FEEFC6F9FCC5FA0CE64704396C30D62A\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AbPp++GrRb6WphSu+PkMaYsqY/beiLBmtiV3YP5i2JkKAQAgBABKXjg1\n"
+ "aiz2JfQpNOG308i2EojnUAZEk0C0x9g2BAAXGL63sv3eO/qrlytsG1x2hkcamxFn\n"
+ "LmfZBb/prqe1Vy4wABuhqWHAUtM29vXR6lpiCJeddt9Pa8XVy/tgWLX6TAw=\n"
+ "-----END ED25519 CERT-----\n"
+ "published 2014-10-05 20:07:00\n"
+ "router-sig-ed25519 a7K8nwfg+HrdlSGQwr9rnLBq0qozkyZZs6d6aiLEiXGdhV1r9KJncmlQ5SNoY/zMQlyQm8EV5rCyBiVliKQ1Bw\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "xhZX8Qmgft51NJ7eMd4vrESzf/VdxDrBz7hgn8K+5bLtZUksG0s6s7IyGRYWQtp4\n"
+ "/7oc9sYe3lcQiUN2K7DkeBDlL8Pcsl8aIlKuujWomCE3j0TIu+8XK6oJeo7eYic+\n"
+ "IA7EwVbdZsKsW5/eJVzbX2eO0a5zyJ5RIYotFNYNCSE=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+const char EX_EI_ED_BAD_SIG1_FP[] = "5AC3A538FEEFC6F9FCC5FA0CE64704396C30D62A";
+static const char EX_EI_ED_BAD_SIG1_KEY[] =
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMvb6SuoIkPfBkJgQuo5aQDepAs1kEETZ9VXotMlhB0JJikrqBrAAz+7\n"
+ "rjIJ4JsBaeQuN0Z5ksXk2ebxtef7oMIUs37NfekLQHbNR0VsXkFXPEGmOAqpZjW0\n"
+ "P524eHqybWYZTckvZtUvKI3xYGD6kEEkz4qmV6dcExU1OiAYO9jrAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ ;
+static const char EX_EI_ED_BAD_SIG2[] =
+ "extra-info coward 7F1D4DD477E340C6D6B389FAC26EDC746113082F\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf56AZkSDiFZ1QaiLJhcKdFDE5Kei/sPaPEIEoPMGP4BvOVXAQAgBAAlRLzx\n"
+ "U029tgIL9BRe47MVgcPJGy48db6ntzhjil7iOnWKT70z2LorUD5CZoLJs72TjB6r\n"
+ "8+HYNyFLEM6dvytWZf9NA5gLdhogbFcUk/R3gbNepmCF7XoZjbhPIp8zOwg=\n"
+ "-----END ED25519 CERT-----\n"
+ "published 2014-10-05 20:07:00\n"
+ "router-sig-ed25519 yfV+GySMIP1fw1oVa1C1de4XOWBqT4pUtEmSHq1h+WrLBNCh3/HZWvNC/denf2YVntuQrMLCJEv5ZaFKU+AIDQ\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "g+BWq69i9CP19va2cYMAXCQ6jK3IG0VmNYspjjUFgmFpJKGG6bHeOkuy1GXp47fG\n"
+ "LzZ3OPfJLptxU5AOQDUUYf25hu9uSl6gyknCzsszFs5n6ticuNejvcpzw6UfO1LP\n"
+ "5u+mGJlgpcMtmSraImDZrRipmZ3oRWvEULltlvzGQcQ=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+const char EX_EI_ED_BAD_SIG2_FP[] = "7F1D4DD477E340C6D6B389FAC26EDC746113082F";
+static const char EX_EI_ED_BAD_SIG2_KEY[] =
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBALzOyfCEUZnvCyhlyMctPkdXg/XRE3Cr6QgyzdKf5kQbUiu2n0FgSHOX\n"
+ "iP5gfq8sO9eVeTPZtjE7/+KiR8aQJECy+eoye+lpsfm3tXpLxnpOIgL4DlURxlo/\n"
+ "rfCyv30SYBN9j62qgU9m6U2ydI0tH7/9Ep8yIY/QL8me8VAjLbf/AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ ;
+
+static const char EX_EI_ED_MISPLACED_CERT[] =
+ "extra-info msselene 3B788BD0CE348BC5CED48313307C78175EB6D0F3\n"
+ "published 2014-10-05 20:07:00\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AWBcqjzLESDuLNGsqQ/tHn32XueXwj2fDlgEy/kQNVf/AQAgBAAFOegg\n"
+ "XY1LR82xE9ohAYJxYpwJJw0YfXsBhGHqfakEoBtSgFJ3cQAUXZQX4lX6G8IxAlQB\n"
+ "7Rj7dPQuQRUmqD1yyKb/ScBgCa8esxlhNlATz47kRNR38A3TcoJ4c1Zv6AE=\n"
+ "-----END ED25519 CERT-----\n"
+ "router-sig-ed25519 Q52JKH9/iMsr1jIPlWHHxakSBvyqjT1gzL944vad4OhzCZuNuAYGWyWSGzTb1DVmBqqbAUq73TiZKAz77YLNCQ\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "YplvAIwExGf5/L8AoroVQXtGm+26EffrxKBArMKn0zS1NOOie1p0oF/+qJg+rNWU\n"
+ "6cv3Anf188EXGlkUOddavgVH8CQbvve2nHSfIAPxjgEX9QNXbM5CiaMwgpCewXnF\n"
+ "UoNBVo5tydeLHVns15MBg/JNIxUQMd6svMoPp2WqmaE=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+const char EX_EI_ED_MISPLACED_CERT_FP[] = "3B788BD0CE348BC5CED48313307C78175EB6D0F3";
+static const char EX_EI_ED_MISPLACED_CERT_KEY[] =
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBALTwNqhTprg1oC6bEbDqwIYBoER6prqUXQFbwbFDn+ekXhZj8vltgGwp\n"
+ "aDGl9ceZWDKfi+reR6rZXjAJGctmv0VHkfe7maUX4FC/d2T8N8DvS+3IvJzFMpbT\n"
+ "O0fFrDTrCSnPikqFfQWnlP8yoF5vO7wo0jRRY432fLRXg9WqVzdrAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ ;
+static const char EX_EI_ED_MISPLACED_SIG[] =
+ "extra-info grazie 384E40A5DEED4AB1D8A74F1FCBDB18B7C24A8284\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AcGuIBoa6TBqD8Gg5atcwp/+r9ThxIBkULmPv9OSGhv+AQAgBACXH13y\n"
+ "mUvdpcN6oRN1nX6mnH40LyfYR5um8xogJZk3oINse5cRNrfMgVWiBpDlJZAwlDDa\n"
+ "lx99hzuZBong+CiOcnEvLMsBaVJmNTm5mpdetYclZpl0g8QEXznXXeRBMgM=\n"
+ "-----END ED25519 CERT-----\n"
+ "router-sig-ed25519 TxuO86dQ3pUaIY2raQ3hoDBmh4TTPC0OVgY98T5cf6Y+sHyiELCkkKQ3lqqXCjqnbTLr1/4riH980JoWPpR+Dw\n"
+ "published 2014-10-05 20:07:00\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "kV2CtArl1VF1nUSyHL00mO3nEdNxlQU5N7/hZNTd+45lej5Veb+6vb4ujelsFERJ\n"
+ "YoxwIs6SuKAR4orQytCL0e+GgZsrg8zGTveEtMX/+u//OcCwQBYEevR5duBZjVw/\n"
+ "yzpEHwdIdB2PPyDBLkf1VKnP7uDj059tXiQRWl7LXgE=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+const char EX_EI_ED_MISPLACED_SIG_FP[] = "384E40A5DEED4AB1D8A74F1FCBDB18B7C24A8284";
+static const char EX_EI_ED_MISPLACED_SIG_KEY[] =
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAK0HgOCG/6433VCrwz/vhk3cKmyOfenCp0GZ4DIUwPWt4DeyP4nTbN6T\n"
+ "1HJ1H8+hXC9bMuI4m43IWrzgLycQ9UaskUn372ZjHP9InPqHMJU6GQ7vZUe9Tgza\n"
+ "qnBdRPoxnrZzUOzlvatGrePt0hDiOZaMtDAkeEojFp9Wp2ZN7+tZAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ ;
+
diff --git a/src/test/failing_routerdescs.inc b/src/test/failing_routerdescs.inc
index b49d59fd8a..e2b72c58a0 100644
--- a/src/test/failing_routerdescs.inc
+++ b/src/test/failing_routerdescs.inc
@@ -666,3 +666,904 @@ static const char EX_RI_ZERO_ORPORT[] =
"wgFKhHI/49NHyWHX5IMQpeicg0T7Qa6qwnUvspH62p8=\n"
"-----END SIGNATURE-----\n"
;
+
+static const char EX_RI_MINIMAL_ED[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf5iAa+2yD5ryD5kXaWbpmzaTyuTjRfjMTFleDuFGkHe26wrAQAgBABFTAHm\n"
+ "hdZriC+6BRCCMYu48cYc9tUN1adfEROqSHZN3HHP4k/fYgncoxrS3OYDX1x8Ysm/\n"
+ "sqxAXBY4NhCMswWvuDYgtQpro9YaFohiorJkHjyLQXjUeZikCfDrlxyR8AM=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAOsjlHgM/lPQgjJyfrq0y+cR+iipcAeS2HAU8CK9SATETOTZYrxoL5vH\n"
+ "1BNteT+JxAxpjva+j7r7XZV41xPDx7alVr8G3zQsjqkAt5NnleTfUREUbg0+OSMV\n"
+ "10gU+DgcZJTMehfGYJnuJsF4eQHio/ZTdJLaZML7qwq0iWg3sZfBAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAK9NjRY7GtAZnlxrAZlImChXmGzml0uk2KlCugvju+eIsjSA/zW3LuqW\n"
+ "wqp7Kh488Ak5nUFSlCaV9GjAexT134pynst8P0m/ofrejwlzl5DHd6sFbR33Fkzl\n"
+ "H48zic0QDY+8tKXI732dA4GveEwZDlxxy8sPcvUDaVyTsuZLHR4zAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key 71DgscFrk4i58O5GuTerI9g3JL0kz+6QaCstAllz9xw=\n"
+ "ntor-onion-key-crosscert 1\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf5iAUVMAeaF1muIL7oFEIIxi7jxxhz21Q3Vp18RE6pIdk3cAH5ijeKqa+LM\n"
+ "T5Nb0I42Io4Z7BVjXG7sYVSxrospCOI4dqkl2ln3BKNuEFFT42xJwt+XGz3aMyK2\n"
+ "Cpp8w8I8nwU=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "lAZwD6YVic61NvJ0Iy62cSPuzJl5hJOFYNh9iSG/vn4/lVfnnCik+Gqi2v9pwItC\n"
+ "acwmutCSrMprmmFAW1dgzoU7GzUtdbxaGaOJdg8WwtO4JjFSzScTDB8R6sp0SCAI\n"
+ "PdbzAzJyiMqYcynyyCTiL77iwhUOBPzs2fXlivMtW2E=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 Oyo/eES+/wsgse1f+YSiJDGatBDaiB4fASf7vJ7GxFeD4OfLbB7OYa4hYNEo5NBssNt/PA55AQVSL8hvzBE3Cg\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "wdk26ZtS1H81IxcUThyirANLoszrnYYhOMP57YRAUDEzUr88X6yNDZ5S0tLl+FoT\n"
+ "9XlEVrpN7Z3k4N9WloWb0o/zVVidPMRVwt8YQakSgR8axzMQg6QhQ6zXTiYhiXa4\n"
+ "mawlwYFXsaVDSIIqYA2CudIyF3UBRZuTbw0CFZElMWc=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+
+static const char EX_RI_ED_MISSING_CROSSCERT[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf54AfsyyHhGluzfESzL4LP8AhFEm83+GkFoHbe1KnssVngHAQAgBABNzJRw\n"
+ "BLXT3QMlic0QZ4eG612wkfSRS4yzONIbATKLHIgyzgGiGl4gaSX0JTeHeGfIlu7P\n"
+ "5SKocZVNxm1mp55PG+tgBqHObDRJRSgbOyUbUgfOtcbQGUeVgUlFKWZ9FAY=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMqT7K8cEzWIaPNXbNgvoZ5ejavoszI2OjW9XXetPD/S2f+N7TfQXHBW\n"
+ "bnjpgj87gmk59w0OXTMCv+XofZ0xOy2YR/jG5l1VJIvqgJhhFJ8oSEGVzy+97Ekn\n"
+ "Lb1FEYuVfVxSxnU2jhHW6KPtee/gvuyRI/TvZuwmYWxLRpikVn4pAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAM4nITNe8UykgsIuo5czSSSl3Okr1K+UVWTzDGLznDg77MkLy7mydmk9\n"
+ "vf51OB+ogQhozYKIh9uHvecOzY4EhSIuKhui4hNyQklD9juGoW7RVTSpGdYT1ymp\n"
+ "dDYS30JBPwCZ7KjdMtXiU8ch2WgbzYBuI+JfjwOhfcsuNC9QPfbfAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key lx8o212IYw5Ly2KbH2ua1+fr4YvDq5nKd7LHMdPzTGo=\n"
+ "ntor-onion-key-crosscert 1\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf54AU3MlHAEtdPdAyWJzRBnh4brXbCR9JFLjLM40hsBMoscAJ8cHMIc71+p\n"
+ "Qa+lg5JiYb551mLgtPWLy12xdhog7SXiJl3NvnMgbMZXHDqkU2YZCidnVz+xqMdh\n"
+ "mjQFK4AtRwg=\n"
+ "-----END ED25519 CERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dg\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "cv1yL8HhQzQfjzkSosziu2kMecNUQGle4d103h6tVMoZS1ua1xiDpVKeuWPl9Z0+\n"
+ "wpFwRkOmK0HpNeOXCNHJwfJaWBGQXunB3WQ6Oi1BLilwLtWQixGTYG0hZ6xYLTnX\n"
+ "PdSQIbsohSgCzo9HLTAgTnkyBgklIO1PHJBJsaNOwfI=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+
+static const char EX_RI_ED_MISSING_CROSSCERT2[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf54AXXgm0CUWQr+rxvgdIslqaFdBiwosT+9PaC8zOxYGIsZAQAgBAA6yeH7\n"
+ "3AfGIGuDpVihVUUo0QwguWDPwk2dBJan7B0qgPWF5Y4YL5XDh2nMatskUrtUGCr1\n"
+ "abLYlJPozmYd6QBSv6eyBfITS/oNOMyZpjDiIjcLQD08tVQ2Jho+WmN64wc=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMdyTK/VPZloLUaLsvj1+NOFs33/E9HmA0VgvZ1nNUrR+PxSR71QF7Tw\n"
+ "DKz+/p2rJE+MPfQ/Na3dH0vH4CDZ+FH2m4A8SB9emF8aKxdc/7KCjQNDQCNlEQYn\n"
+ "O9WvZJhbNPHUmX0z4OotI+Sk3qBzVHu0BGDsPYC9gwszIumDUILxAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAL8o6CJiLfW4vdRFvJ2nFt/H/ei0ov83rilOuwSmNORmL9lvnHY++HrD\n"
+ "dmEEvBv74xqWJxGbJ6OQ3VOwRpf2X/cb4gAvsQDqDmNwpJsrPYRQVXp/KY/8z7bJ\n"
+ "dM4CjcsuJHHmj3yc3iCzgqt/Xr6vR24X4bee12/bP7R8IETvWoiHAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key qpNEGrLMVn28Odonk/nDtZq1ljy0fBshwgoAm4X1yzQ=\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "i4RKGIeaUrO6nzfdtb6j+ijYJh1Vgc9bsHMpW9cVCOjoJKFW9xljgl9xp6LytviN\n"
+ "ppKYCt9/JflbZUZjny34ESltPGrdquvHe8TtdQazjiZBWQok/kKnx2i+PioRF/xI\n"
+ "P8D0512kbJjXSuuq9tGl94RKPM/ySGjkTJPevN4TaJE=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 pMAOpepn5Q9MxcV9+Yiftu50oBzBsItQcBV9qdZCIt3lvSFqFY9+wJjaShvW3N9ICHkunrC0h/w5VEfx4SQdDA\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "Du5fJYDzvEeGqKTJwgaQsJJgz39K/J4qEM2TZ3Mh0XuDM1ZWDtjyzP03PaPQqbJ1\n"
+ "FsN5IStjOqN3O1IWuLzGaZGpGVuqcyYOxjs7REkGQn2LfqCjpzjaAdcsL0fI4ain\n"
+ "o/in8GQ6S/qhsx8enKlN0tffTmWmH9bmmVz0+yYmBSo=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+static const char EX_RI_ED_MISSING_CROSSCERT_SIGN[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf54AfoVFYuJnDNBWbjbTqfXACUtXWPipmqEYC++Ok/+4VoFAQAgBADH7JzI\n"
+ "fjSMV158AMiftgNY+KyHYIECuL9SnV3CSO+8+I7+r9n+A3DQQmGLULo/uZnkbteJ\n"
+ "+uy6uRG4kW0fnuBlKhseJQm9hjNGWzC8hmebp1M+bxwG41EGI7BZvnTrRgM=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBALEqlijoFIDX1y1i5zfei8DuDIsFtSw56PGgnMRGcybwD1PRQCheCUZM\n"
+ "erQgFCWjgLgvGJERBK/oILW1dFXp4MAR5RgnrPGTfWTinCj32obMLN1gIczpq6a9\n"
+ "P9uv6Cz0ApSxpA/AuvjyAZwQKbUXuMvIY4aTprAKSqqVohk6E+E1AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMZbbBjGV7xPri4XNmejq4add93p+XsWlsfbM930bcC2JZiwg4g4cq6W\n"
+ "idl8VDmCXeaWg5y3kb82Ch/Q9vPG0QYQbXxUA3JxQKKbcEK3QsEvqQh8Nb7krILK\n"
+ "YnSGAnLG2Nc3PnKb7Wpb8M3rAysC5O99Gq1mSfm8ntj3zlIM7NSHAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key CYcpfIF4T9PJcfROfVJTUYl0zNd4Ia5u0L9eng/EBSo=\n"
+ "ntor-onion-key-crosscert\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf54AcfsnMh+NIxXXnwAyJ+2A1j4rIdggQK4v1KdXcJI77z4AMRc2LxiKbyr\n"
+ "fqRVynHuB031C4TN/HAlNPBjVoRvQRgzpiyyoyCqMDxLZdM8KtzdLLeqZJOXtWod\n"
+ "UXbYG3L70go=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "BRwRAK2lWxWGS49k8gXFHLEQ/h4k8gOQxM0WgCaN4LjAOilLHFjsjXkmKgttVpHl\n"
+ "f0V9ebSf+HgkpQnDSD8ittnr/0QaohUbD4lzslW4e/tQYEiM46soSoFft85J6U3G\n"
+ "D3D63+GmaOfIaa4nv7CD0Rw/Jz0zTuyEuARsdJIr1IY=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 7XfV5r7FXbXPEvrxlecWmAJxat/6VT+/4tE5cHrQnvLM4zslysstWH6/AfIfcmUuDlQ0watmfg1MvVnjavcfDA\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "eigLL3S/oMGL2tJULt9bl3S0iY+YIxdKeGFCcKZci59zD786m+n+BpGM3yPpvrXr\n"
+ "bGvl4IBqCa1I+TqPP1rM9lIEcUWaBT7Zo5uMcL1o+zZl1ZWPWVVKP5hC5ehDueu8\n"
+ "/blzNhTEFAp23ftDK9PnFf+bXxqbgKkEoZsxnd3e9Ns=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+
+static const char EX_RI_ED_BAD_SIG1[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf54AR8QC+SNBpPOTVY198IQBANNwZjy+SBqQNxfzjEmo204AQAgBABjz4FP\n"
+ "zW/G+fu7YirvANvvqJeb7S1YYJnf6IrPaPsPRzDqJcO3/sTzFC5OSb9iJmzQAWnn\n"
+ "ADPOl+nOJC58XJnJ7CUJdPtyoVdMvUiUT/Jtg4RuCN1iDaDYaTh2VavImAY=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAKuLC0kzCBTV6+WPZcAOQPKjqbjvMIyaehIQS1o90dYM+Tosrhtk3bw8\n"
+ "QBLMaiWL3kfIWPZuWi2ai40dmqAXMrXH3yBgKRNZ6zZSbUUuJ1IknqmrQ2PKjC/p\n"
+ "sIW2awC6Tq+zrZ7vntDb02zY857vP59j8eolTDg1Vvn6l2ieL+WhAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMnBQPOJBQLZ3NAa70n6lGZGvS3DYZFNOZ2QnHVeVvOSFIFsuvHtnUdX\n"
+ "svDafznYAuRFRVqJS2xtKKGu0cmy6ulEbBF+4uAEMwQY7dGRPMgVF1Z33U0CSd08\n"
+ "ChCJGPTE7tGGuoeSIGN3mfC4z2v9SP3McBdAiLHisPzaUjfRTcwRAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key W8fUvBpKBoePmqb70rdJUcRT0NhELDWH7/BSXJtkXS0=\n"
+ "ntor-onion-key-crosscert 1\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf54AWPPgU/Nb8b5+7tiKu8A2++ol5vtLVhgmd/ois9o+w9HAAPwWqmL0HXa\n"
+ "bYKrKPWQYnpQHQ3Ty0MmCgj3ABF940JURnV161RlN8CRAOJaeQ0Z8wBRLFC1NqLT\n"
+ "+GVdtewGeQA=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "x0vT5Wv7Guc0/Vu2BqomWwenh8oda9+8K/7ILi5GQL/WC29Tj51i0EE7PVSnSMJ7\n"
+ "33I/V+N5neauqWnbg7TxYaLsPfr6SpPTpBL1Xt0OiwT1//PvPYZ1gCcF3ig3KcfI\n"
+ "mreQd5C5Vri6ukWkMtz/zNDaDpDanzaNXTdaUXmFHF4=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dg\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "Hci/Br1+NNymDZBmQy1QWMlCeLe8Z1vtZ2ZTj42jDhWg1OC/v72ptI072x4x5cmi\n"
+ "X3EONy8wQUvTNowkfG6/V/B768C7FYJYBId1GAFZZymXnON9zUYnE3z1J20eu6l6\n"
+ "QepmmdvRmteIHMQ7HLSrBuDuXZUDJD0yXm6g8bMT+Ek=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ "\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_SIG2[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf54AW8fyx54c7vQQA/AmShAitFP7XI1CLdifEVPSrFKwYq6AQAgBAChqjVA\n"
+ "/wKKJZ30BIQoXe5+QMiPR6meNxF1lBttQ2t5AhauZbH5XzRhZkdGo114wuyPNEM9\n"
+ "PrBwp5akTtari9doVy6gs3McqdoIbRdWevpaGj5g5oOEOtA9b5UNWQSwUAs=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBALp0Croi9zhpGxi9sUj54jr/flZdzxVVS+8VNldJG2c1soSx8kwlwotu\n"
+ "7mGGudJDAzDHGo5F5CCPEfQov2OmDehpefYUz/AaMLly6PrLRJlcUcpLogGf1+KU\n"
+ "1lLwE8kanXUkgvDhVQiFvNjy2Dxxuv3AHH4WdZZfbMbm8FJRGoHzAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMoI9vQT4g2sV2dViGOWOzxckk367T9sMjVwcYfJCmnixGxjWeKScQFB\n"
+ "K9v1uK73cfZR8AxiUGK4/iOX/9en14mJOGF7fftAqypFLAt1TBvb07IgXljOBoHc\n"
+ "Paw4oZoJQzEoazt0Oa181LyNnNIoaZpHVZd1+a1Gs1gKoM4xDBv1AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key KjyvXYkMcpke5ZsUYf2gZAUNeEoz8NAwYoQvvbcDGiw=\n"
+ "ntor-onion-key-crosscert 0\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf54AaGqNUD/AoolnfQEhChd7n5AyI9HqZ43EXWUG21Da3kCAI6MRHm7GpCF\n"
+ "/3zDGR/6jKe625uFZX9HpLt6FgAdGSJeMQ9W4Np9VkrFXAB3gvh7xxRzSgZ1rXgR\n"
+ "lUomgi7N1gc=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "xJXvCCpP4ExBuT3OTsdn2HJB0HidupmQq5zBh8fx/ox6+047ZBOM7+hVxxWapcMg\n"
+ "PMXbcLD4L/FCBpA/rjnFUE/9kztdq7FH/rOdi0nB6FZWhwDcsZuyfvbnDTxz5iHJ\n"
+ "87gd5nXA5PE649SRCxW5LX0OtSiPFPazu4KyyBgnTIM=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dgxx\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "tk4kBNYqB8utOmX30HrV8YfnwBXYODIiL3M/juRS6nPn0uvbW7pjoZ3ck/ahgW+6\n"
+ "FNQsgTJnEADCWS1r6v7PcvzQjtrOUUpNxGJxYw1r8yZkvmIxSQD6GMzuTxq7o1VA\n"
+ "/wZYDLonLhCWRdPjxnrl12+z92NdyISJCHMLRVqs2QY=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_SIG3[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf54AYYiKZrFWZ/Cj5mZbfK11MZHYbwchllsUl4qPqY9gfi6AQAgBAB4irxT\n"
+ "86FYA0NbZssSTmfyG6Edcf0ge61OwB4QD35kHCrvuZk2HnmL+63Tj4QoFqIVnwVC\n"
+ "3wRGJGcmS7y+vS64GUXbuyTgqgpl/KuoHo5Aqe6IxJlVWYtU6W0M6FV9tAM=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMUEvXTVTl5xkQ2MTEsB4sXQ3MQkz8sQrU63rlqglpi1yUv24fotjzvE\n"
+ "oJpeKJBwwg5WBW/fW0bUDJF2cOHRHkj/R4Is3m+2PR1Kn3UbYfxNkFkTE11l099V\n"
+ "H6xlsi0TJOJKlgrcbSuB7se2QctZVhwsdsJvFRptC9Qd+klAPb7tAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMooTeSUX7GPoyklSd1/6cF1u8e2LbjOLIpZrMon0Xt7c/aNwlrG9rVo\n"
+ "TSokHs3AQ2H2XIceySVRRWR4AdX9KApO4CX0gGTuVUmq6hFJWMnHdAs2mKL0kt1w\n"
+ "I+YWzjUqn4jIVa2nMbyHVQWzIysWwWiO4yduIjAYpBbWd9Biew4BAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key BN0I+pLmFkDQD5iRsdkcped4eZwGIuXnLiX2K0Zoi2I=\n"
+ "ntor-onion-key-crosscert 1\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf54AXiKvFPzoVgDQ1tmyxJOZ/IboR1x/SB7rU7AHhAPfmQcAOrIvaG/xJqe\n"
+ "adM6mai+FlV8Dbt6QrXTcNHJU1m+CUDthA9TPTAYz9D8W0mTEQ6KEAKGfQrNLy2r\n"
+ "G1B+9wWSpA4=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "BpLBsl6Yo64QzczJn0TjdcXC1Jv9IhUG2m/Re3v0voCELOP+t5vkZXXLoVL23oKv\n"
+ "JheSkWiuAIEPsatb4afXZ8wZxPcQjwy3zTOBM7p9CG5fA+KYpqKTxAi+dhVYlcDo\n"
+ "M7S5nMV63FclkZIT70FFTHwWed1sAKwEO3/Ny24eppc=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 abcdvEzGFYMcJ/Ea7sbessW1qRJmnNNo2Khkkl0rEEgtLX0b4L4MMhK/ktS52Y6jX3PRQWK5PZc6gjV7Jaldh+g0Aw\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "Vyj7g3eQ3K4+tm49fJkAtsAYnYHcEiMnlucYCEPeKojzYStNfZwQO2SG5gsoBIif\n"
+ "urgQZ/heaF4uiGFg64UFw08doXqQkd5SHO3B4astslITvmq0jyaqzSXhdB5uUzvp\n"
+ "QCR0fqGLVS1acUiqGbRr4PiZ9G7OJkm230N3rGdet+0=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_SIG4[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AaEnncX/t0cbLm1xrtlUpkXghaA8fVuV7g1VF3YNfCaIAQAgBAC7Ki3S\n"
+ "zzH9Aezz5X4fbwHeF+BQEDfVasfyTxTI4fhRi7t3RxHzBJd60uEMXy2FchD8VO5d\n"
+ "j4Dl7R4btrohPVSVBQZuemBQSW6g3ufNl0txpFWu0R7vBPTFH6oyXYfY9gQ=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBALGKwzhOui2/jJPjU1ngW5IZRPcoDk7RAfGDO4xaef4VfAFHCV9CQO1c\n"
+ "/wQ09CcRdggTvUcv9hJTGJhSObUUooCkxw4/35f/A6/NoW1Gi0JqF9EsQWHpuAfr\n"
+ "n/ATlJQ9oGdTCNDq/BXSPWXhoI6UhUe0wiD4P4x4QwaYHcZh+lE5AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAOKrizVm2h5/jE/HqqLCBLWJZVVoGspasCtDDqHhSqsPzyjpqa52iMKi\n"
+ "q/deJ92le3J2NJRGKxPmPQqWxwhIjnMS5kUMoW182iLpO/G9qyPZ0dh6jXB0NBLF\n"
+ "ySfW6V2s3h4G4D2P+fqnsnzQnAX7YufkvgDau/qTWi2CqD0CjavDAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key A9h8jY9dPbhHTDbIc/NYWXmRP65wwSMrkY1MN8dV3BM=\n"
+ "ntor-onion-key-crosscert 1\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55AbsqLdLPMf0B7PPlfh9vAd4X4FAQN9Vqx/JPFMjh+FGLAN8xr/w3KFVi\n"
+ "yXoP/az6hIbJh0HYCwH8D1rPoQLcdpe8XVwFSrHGarZesdslIwc9dZa/D1dx3OGO\n"
+ "UhJOrdv51QY=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "bLmdO7ME5vq+c9y/Hd8EyBviMBTeo85sHZF/z6Pehc3Wg3i1BJ8DHSd1cK24Pg48\n"
+ "4WUrGTfonewuzJBDd3MLkKe6epXmvUgvuQN5wQszq1+u9ap/mRf6b3nEG0MHxMlO\n"
+ "FLx5MBsScuo+Q+pwXZa8vPuKTtEjqbVZivdKExJuIX0=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ " router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dgxx\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "LqNGEa10zwSPeomBXTfgvBnnWAdWyiR7KYZq9T++jK4ctR6hUaWngH8qSteUrkMx\n"
+ "gyWb6UMmlxdfOG0sdcU463HsqV7zObaKya8/WwQ9elj3FfsToswUCeOaLR/Rg7wC\n"
+ "zcUjI5VsneQoXT2WVZbZBLsLB3+7QfezVHRMB377GAY=\n"
+ "-----END SIGNATURE-----\n"
+ ;
+
+static const char EX_RI_ED_BAD_CROSSCERT1[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AV1AfOvQWKlWsbzoBdJc5m72ShIJuA8eNV15basjhXYdAQAgBABy+KQK\n"
+ "3oLDGtqL5kwRmjAsls/+C6SAoAALll7U7wNSH7en5RVBal4RUzCf57ea/KG0c9V8\n"
+ "2DmZ3PdOt2aY/M2bWGmmH/tyyapOoV98dhDwFU7zcx/pMfRnJTDRSDwl8QE=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMP6xbqbj+x1mq5XImjeT0rUzqKZTgBd5zvK4Xcy9IifJuFC9+mMzrY4\n"
+ "WhYbdClxKUkDMkit9MVhek+P/w5TSHKl6AuqGaO09ID+hZpoUSdoBUYktynxfGsx\n"
+ "kIDu0XvgtAeSyJaVvoV1SKVChY0IBbzUqbHt4O2Q1BhzFCKEJTEzAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBANwWlBh7e/eSLlhto5YUdj1iGYOq+yAmlosDItVfYrSPJuUfM2ocMBAn\n"
+ "udbRbWiADoqsbKn/gwwHCC/f1HX2FkRXxxnOlJKLo+NEi8tGmOlcQXSQol1pCpvK\n"
+ "sA9TxtYr+Ft4LRpxNrexF+pIBxqzwetqQrZbKYr0CFJi8q1qlMynAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key cs1AP+xF5cXTLuKeOeItdoDAzfALTJkwk9lB4mtC4QI=\n"
+ "ntor-onion-key-crosscert 3\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55AXL4pAregsMa2ovmTBGaMCyWz/4LpICgAAuWXtTvA1IfAKo6ANUq+hi+\n"
+ "xb3J4aYafnszlj87oi/DR+SDf29wzwNw8gmaqGzJ5GbfISfABuTUCzlilZyVnLxi\n"
+ "BHcCH6PWiAQ=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "qC9Kph/kGtONR2DxZDoIFFgnDFC+/7H07EgCiYQdIFIROc+gGK9qBOgeFEptrkXF\n"
+ "XdE35xxox5xSASQvp7hjFwxUtJRGOtf2O98regqeeaz6O9VPXHkLf51uqX3bVgq8\n"
+ "KvFAsFFS66GxhtbrVjpyRgIwHAYvse1WVESfLuZZTn0=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 3uW8Q1aetIQLOsqSco128ZUaHlhqdYiBvrxV7x75BGNS5RzIMTEwYDNtEX1LNPFJ5N0YOV0HEEOLhrJUV9QCBA\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "WuD7S/saTYBxKvItITbHRi8n+e6g/oVbosicfbRbafYPzPp4Prb+RK03UTafzXrV\n"
+ "QEQIzDNhfePcIMH8qX+qrogLMXFqiXx6TVQ0GqNvqirokk8ar3AgtRtewhChAuAj\n"
+ "8pmQTj2JpZn/iB3PCE2l/93O9LHZfp44hc8QOWKs6BE=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ "\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_CROSSCERT4[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AW5TTGF9jCMl7aALZzqypD9Bj8WYnAPIrKCoIJdgMbY0AQAgBAB7eCn8\n"
+ "rukx7t/egZUdqU7+FYqsnO4wdmOkLZkp0+gpF3jjk6N1Q0037NNVNZBjONB0Nm2F\n"
+ "CpB3nWSJliSSKr5tOYsuBPFy5VVGYeKPakpOoxanQ1UcqevMBAQy0zf9hwA=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBALeS5YbeDuKQ5iiuUvh3REoyJ47/YU9lslWmTrVBf9b66pMnYJv/awPu\n"
+ "m2HredUAJ3VzwQ38VJA39w3fQXUhQDnQ0OPpKzeAmIiuG+6WdW/mBSK7uKcezC23\n"
+ "LA1d6Afyl79LjZz/n+ENXqNMlJk4QPcPHuRnAvwBl3t8YVRPJmxhAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAPprokY7utWuO/0252dBB5MCxmVD/dROaIBDyFtpdH+YVv04rkOlDzYD\n"
+ "W4mgHVBMxEm/cspTgQmJ4exRHJPpcSe1RYHt1ONZdLYr6D7OOWf0y1IUrVSzF6K4\n"
+ "lqlmNuH1H4+TKGbkvixYc5GU/2ZmAy6gFEuphYnBbsN2Ywc38mnfAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key Cgo6xniGfEiuYoLSPUdE4Vb2D4zj2NQzC1lRjysRRXs=\n"
+ "ntor-onion-key-crosscert 1\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf54AU3MlHAEtdPdAyWJzRBnh4brXbCR9JFLjLM40hsBMoscAJ8cHMIc71+p\n"
+ "Qa+lg5JiYb551mLgtPWLy12xdhog7SXiJl3NvnMgbMZXHDqkU2YZCidnVz+xqMdh\n"
+ "mjQFK4AtRwg=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "bi4M/AJLZF7/vSNmOj4uhrgKBQA/KfcZy5e58mhGL4owxd9vaWfl3aelvb9jf9zN\n"
+ "Q7FMv8f9aXzeVIoXIpRJxSKIJgBtG2wnMumIc80pqBvTyGInharszb6njfm0bg1u\n"
+ "PfJkbQYyf/dA5l5UwCrjFs06ImDmjFTAdsSWf6DfZ/k=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dgxx\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "io16v+e0pK3sbFzPGnkQrAjrRgIOJHrVZ1RXcxZ1+UNXagWM/MOLhQpkU/cw49Wd\n"
+ "4rQeZD3JQh16330eXbxc97AyDgp0b30He846SI0MfW/DnmGI8ZNeYfLbMv2bmbs9\n"
+ "QULzyIH8C+5mnMI1arcuiAua+Dpa34F79vgqPuvw5fU=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_CROSSCERT3[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AVB+j+B2yPgGywvp7nvejyhMh9ejKmw7LCwufV83Zl9eAQAgBAConA3B\n"
+ "jJ3X2tES40jd94rRUFS2/s/Yv7E4LEQ9z0+jz8horNivzK3O/t7IGxJggi+b41/9\n"
+ "Uaqt+wqtVuKj0xJ9jwBlCXFt28G2P9s4ZyXYgGZqo7MlJlboybnOMvmoTQA=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAPWuEWckT4aYAVNrZzLA8xVwfXp0wzfXeTWBztLS8VzssN6w/+cwXdeY\n"
+ "N1YNc2DiD3u8f+7kmuZIqL1EFQUwTvRwEzQXm2dqGM7qkm5ZGNMb5FKu+QwO2ImI\n"
+ "FLNiO5zO/LqP3cf/2L8/DuvruLenUrhRtecGFaHmhDYl+2brHIiPAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMtHTfk0gDvp9+PtIG8Ks7rgCiJZ2aihSvr6WaKHYuIprgspFuga98cg\n"
+ "D//J80CrgH5Dw68YnkG+gU40IxP7YzhQ4glFlJGu3s2y7Qazcv5ww1XtHur+GDoA\n"
+ "cY0zCLhltNQFxIsoVUepY97XA6Y2ejYJjyqNXQcAmoPNoVhnTdkhAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key ibZf57LptdOK3WpVFXkYMatEEqPhuVWxsnkwF6638V4=\n"
+ "ntor-onion-key-crosscert 0\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55AaicDcGMndfa0RLjSN33itFQVLb+z9i/sTgsRD3PT6PPAEbkxCdI/bH/\n"
+ "B06DAjRuoDiv1HKsGuW+UN1iGEiWu2ieFzf3m0Z7BL9p2u2zIbHYkP50b3T3sebD\n"
+ "1AksemmMdA0=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "BpLBsl6Yo64QzczJn0TjdcXC1Jv9IhUG2m/Re3v0voCELOP+t5vkZXXLoVL23oKv\n"
+ "JheSkWiuAIEPsatb4afXZ8wZxPcQjwy3zTOBM7p9CG5fA+KYpqKTxAi+dhVYlcDo\n"
+ "M7S5nMV63FclkZIT70FFTHwWed1sAKwEO3/Ny24eppc=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 XS4zVi46Xl3xKhuozPCDlW0QRFD4qUhJmkefonQNsRlMVsrPkALnP2tfnfdfTc69hbNa22pOjJNf6Gm505EnAw\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "Q+R3OpO8VhfvFbXuE5qolhVbgosBHy2A5QS91TMzCbsxa8pBA6Li4QdPR37wvdLq\n"
+ "KayfmmNCMKU5qiZMyXqJZm4fdpxiSi50Z0tYlXM3b2OVfza3+pSOEBl89fN6G4Qc\n"
+ "pAmM14eEo1UzXrqZw76tMS2CwOYF5vR2xFGCYC0b5hM=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ "\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_CROSSCERT5[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AaCfOaispi7dJhK0c8HXJHIwoBkMgRpmmHu+3Zce/soMAQAgBAB5bAIo\n"
+ "5i4TSY/bV2KQAyziRwvgJm+nEiECClflPbP9Um+zOzOgxtDmNnR5UFQj+VWNG4uf\n"
+ "5lnaryN+PfUXZMTcs8AARof3fFz9tVPINHDrsGvKt8gpzgZEHkVioAXOFwg=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAL3Fr/ovZ9SMGYrAM24taKBm/NpemZaXdD/JeBXFYm5Zs3szLwJC4Etm\n"
+ "zjNL6tVy+I21O1g3cs16TkflcidsjPXNx//PHAn7bqWMekjrt3SQdkHW2gDPgT2c\n"
+ "zYJ/hBR96JYG796jP3pkfJz6Iz5uT/ci3A/cdaVbzM1uZbMUgYGzAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMHB+1dWa8BBrKE94vTqfbkSEuysG5LyyZF/WrqHq/3W+ocDLz795k8O\n"
+ "2Zvgr9im/Ib4hD7IyrtRexcuBdwujdG7cBALdCcWiUTGAMkl96HNETSX+lUVIpJ9\n"
+ "pMsc9O7+yz+/0Cl2RpILZCdE/7I96qHpZl3tzlRKSu15WeIm5U77AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key GXi0a2VLcRHQMMYys85zu3IPqOn5ZTsOixYyQvTGnQs=\n"
+ "ntor-onion-key-crosscert 1\n"
+ "-----BEGIN BUTTERED CRUMPET-----\n"
+ "AQoABf54AU3MlHAEtdPdAyWJzRBnh4brXbCR9JFLjLM40hsBMoscAJ8cHMIc71+p\n"
+ "Qa+lg5JiYb551mLgtPWLy12xdhog7SXiJl3NvnMgbMZXHDqkU2YZCidnVz+xqMdh\n"
+ "mjQFK4AtRwg=\n"
+ "-----END BUTTERED CRUMPET-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "T9NHMBhuJo+TlfU3TztNgCc9fK1naNRwPOyoqr5R6lJvJ40jkHnIVOFuvuzvZ35O\n"
+ "QgPbyFcMjv6leV5xcW+/I9tWaBUFXiRGI27qjCFth4Gxq2B6B2dIcQliLXSvW9b+\n"
+ "CMTgDwVa4h2R2PMh18TRx1596ywE09YhCgBF3CwYsiM=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 sRpiP9kyW/DGOphp4V2VCtcKNA8i7zGuv2tnljNIPTB7r7KsTvdUk/Ha9ArRQEivO4nC2HHENtknDl3GtWIPCA\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "DtORw3+gO/yUUIp70xDaWSOgQZrJAAoZTNCB7q5WCoZOngeaCiC1Gtc+Fmdn7tER\n"
+ "uPqQC5H/Kh3Mi82PCj0JxvNivnNTNY1AZVaIX5YoioXVOkWF0B2pqMvFuDSdm2oJ\n"
+ "29PqSVcklquu19EjJRTopIHvYn3sFhQL4LarMsYY11c=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ "\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_CROSSCERT6[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55ARMMCtQ8pObC5bq02AUE9Lx2bqsZBBkeOsDZVaEq6JavAQAgBABtV0xF\n"
+ "CsWXL/uFIBnoEsnXBeU1MvYRFrj1vR7QHdWXnxywXvBYUAC8lu/uyc8qqLp+aQSJ\n"
+ "5JzpDYlg3hp1fl5k97iv5F9WrR6s554YpmgYy9agFaxZ4LmRgz7n0UJ8mwM=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAO5qd1TndKD2pEs1ZLWsHlvfO/E7cA0H7NKGLSioGpBf4P0rtkueX4ci\n"
+ "kJNa/4Fn/QsLECqEF2lUjkIc8YL+HMS6qteKvN8+nn16DfvnIhPDNZWTJjLl1bOI\n"
+ "sWSSiduhanoWQnhRtl3Rxg3opdNd9ApO0DLUNy4Qy18Ai6SgksfHAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAJkMYNpK7eJJyGwD/xG/iNg6gzzbIwrOSvmtoP7Rot42qtBiQ9A9kdsy\n"
+ "sazwkWkM93U1+1OaAADPYxeHoyHnuia95Cnc5y2lFSH3I7gnGGSPKSTwXtdyvDWZ\n"
+ "P1LbmQ4Bnh5leTCNZ/eFC4/GjNVzqHxjbb8a11dQhA8dOk8PrUq9AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key HdSQOqvLr4YnJE1XzzVIddgKgnjaHKJqnq0GqF4wXDg=\n"
+ "ntor-onion-key-crosscert 0\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55AW1XTEUKxZcv+4UgGegSydcF5TUy9hEWuPW9HtAd1ZefACVwif1deQry\n"
+ "K5GeemRa32sGzujVDDe75WRiPKFT3l/EtjTq3oeVq2xwbVJklnG3ASejKTr3YcHt\n"
+ "ov0jOl0jywc=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN NAUGHTY MARMOSET-----\n"
+ "BpLBsl6Yo64QzczJn0TjdcXC1Jv9IhUG2m/Re3v0voCELOP+t5vkZXXLoVL23oKv\n"
+ "JheSkWiuAIEPsatb4afXZ8wZxPcQjwy3zTOBM7p9CG5fA+KYpqKTxAi+dhVYlcDo\n"
+ "M7S5nMV63FclkZIT70FFTHwWed1sAKwEO3/Ny24eppc=\n"
+ "-----END NAUGHTY MARMOSET-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 lNY8TRX/FZdH5eFbsBkFHuRi8bPDsE5P+v7zExyD/IXnKS/ffYlP8qw1XIPdEDOIzGQ14+kyPX0SotaAqHRtBA\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "BHamS+epF77iozo5cBt+tbs22m9GhwY55DRXpEWAtvn67jsMnmn7qCOLONigK1RT\n"
+ "adZNezIydcCxXltgHTdKaZw4lcqv3s0KL8kI8frbBmm7PjXtWnrdXBYY+YK54MN/\n"
+ "t4N3162o9hzzKSwye0gPjgzpQ1xtEIkzWhBcmE9Vw5s=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_CROSSCERT7[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AfVmH2ReTyatl4VnS5YREtCM2dwikWuAPffq6M5bysZxAQAgBAAXoqE7\n"
+ "taqwLDXLZrZukpF1eBkCwYQK9uzctHTuMdqOHChguvkfX7V4H3O76Ayqvz+Z1ut1\n"
+ "KYRdgiArn3viRaBv3ZKT4Z75suMI3bjqGOSGLAKfOa0uLkOmKblHHhSUkwQ=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAOLNugzUezzzw+N1SuQWzILJYkUJyQDoVXSZjT0dzBplHCjlrv0WZCUP\n"
+ "/pbonE7SlCChIovHcdiASaLj7MVaGgYDq3M1Vtgt5vhgGl10/+evBAD1QEt8AVfr\n"
+ "5+PH/sbZvOWucAhNUhOlqFKAn4vdRY39VEEXC5/Jz5fsk1E/DBu5AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAKxzg1hsYMS+0zAIrgYxSGO0GbKRrL/VhdlMEGu7ACaoqlGnmGQS3B4B\n"
+ "gLk8xDdx9N//8+YTx0hUIxP38w08lubPl1WXMq8s7wAiFd06Nklf65mHs0sXVtS1\n"
+ "EG3f97PQqmBpEJOwYBATNcA9e6F62P8SXNkpSjOzNaE0h9wHNKk7AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key msdr3O4W4bm/xdmZLzj35363ZSFex8yQxLWsV3wRCAQ=\n"
+ "ntor-onion-key-crosscert 1\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "VQoABx54AU3MlHAEtgPdAyWJzRBnh4brXbCR9JFLjLM40hsBMoscAJ8cHMIc71+p\n"
+ "Qa+lg5JiYb551mLgtPWLy12xdhog7SXiJl3NvnMgbMZXHDqkU2YZCidnVz+xqMdh\n"
+ "mjQFK4AtRwg=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "RJJRiU0vjVtRi3bVZru3aTvV5l56X/WOOp/ii316yPAS3aAMpOm1+piFVR5MNqcB\n"
+ "ZGyrA2Kx0hawdL2buU47iZ12GOCi4f1Es4V4N0TQgJICsKX38DsRdct9c1qMcqpp\n"
+ "1aENSRuaw0szTIr9OgR7/8stqR5c3iF1H5fOhmTi6xM=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 4DSdPePrToNx3WQ+4GfFelB8IyHu5Z9vTbbLZ02vfYEsCF9QeaeHbYagY/yjdt+9e71jmfM+W5MfRQd8FJ1+Dgxx\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "F3ZqvsyL6RRhPEnNFFIZY4WJM7LK082rseWzRkGNXjwoEwOWUK8enQ4Wjit+wozW\n"
+ "4HVIY1F+vP7gm6IiOEAFgEpB4C8FGuyoFw2q0ONA2tqTcvBJDDnqbx08FO7v2Dij\n"
+ "d3ucfc5gf7YNaoFCMMuyAzC56eyNk4U+6cSKy6wnJds=\n"
+ "-----END SIGNATURE-----\n"
+ ;
+
+static const char EX_RI_ED_MISPLACED1[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAKT6OIN6TsDB+xcp1uLeE0K3aiHGqa7hdxMBGpvcD0UFSyzpVv1A/fJa\n"
+ "tClDCwTpfTGbyK2L7AO75Ci0c7jf6Pq+V7L6R7o12g6WBTMrgsceC4YqXSKpXNhi\n"
+ "oudJyPfVzBfKcJUSynv89FUQOyul/WRRqWTfv0xUsJ3yjuOESfCNAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AbBV9NVz0Hdl0Uiv87LiXaTAoeSXE+bheNG4Dju1GzQHAQAgBAD16h+T\n"
+ "ygzSgPN4Qat5ITthvm+lvMwMVGbVNWMxNy9i33NGhgp8kqMp2iPAY+LhX8It2b+X\n"
+ "8H9cBmYLO5G7AlMPj7GsuWdCdP/M/ldMvFfznlqeE3pCpRas6W48CFJ+9Ao=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBANMO/MepK3uCkKTLRCwIWc/8URVza2gEmDx6mDTJIB/Mw8U8VRDuu4iJ\n"
+ "v+LL3D8/HGLvT9a8OXbl5525Zszt8XueF3uePBF0Qp0fjGBL8GFqmrmFe6plurPJ\n"
+ "TfrS/m3q+KhXAUowmghciVGDY0kMiDG9X/t/zKLMKWVDYRZk+fupAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key I8yDO62Flx5O/QsFvgb2ArIRqwJLWetHMeZdxngRl2A=\n"
+ "ntor-onion-key-crosscert 1\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55AfXqH5PKDNKA83hBq3khO2G+b6W8zAxUZtU1YzE3L2LfAGC1uXxN2KwW\n"
+ "w4PqRidM1UPZ5jVOHceZYNQcTzzzArfBpr9OraOO2up4TGte8GVqjJNxrZc1gfjn\n"
+ "CwPW5WxpFg0=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "jLg3D3VO4i0sN8p2qtB6+5C3tai/K4M89mP7z2abQnUTbynOacPoNXIk4o64DjBJ\n"
+ "kaR42yfA7yQZ8Rj8abwgz0Zz6zbd+JjE+s/EklrEEtOl+jZAl3i+92FaHROJojXq\n"
+ "hw+ZEPOb9zgb1UQ7S1Fo+GoqA5bdGm/Wg1kSQielkNE=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 TRKvIl/wIIRD4Xcmd6HYmy7tD0KhVGgoStpWPtX0zmXGZ7+jugItrY0frDu9n82syiruuA45ZOs1Rfi4CbOSCg\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "NYpRfurB1YhFmDAdRc2Sd77S7By2V/0kgEHpJhtySb7efiQsyOA4ZBr1zEFPAXdp\n"
+ "TviKzyS9kN2fnz3hORoqFul33BDZbiLMNLtt5tzp62TYtmIg9IZdjjczbJUgbVLt\n"
+ "KCJL0vM7fdbXkZX61GIBbMYwzwIiHvVxG7F/AS5RbtE=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+static const char EX_RI_ED_MISPLACED2[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55AfJo9FIePrxeDNnWT6SWkoz0/L27018XjUNWEHfaR06MAQAgBAAMgolK\n"
+ "nLg3ZnVv0skzHCfmX+ZR9Ttwj7FNXfhXCsyr860S79OW5LD0/m1GcS9JflWhP+FO\n"
+ "ng5cRb+aqNc8Ul+/4sQudZRx8w4U3d5rOuMGCqhQXnktH9AFzQHFq0jpAAU=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAPeK/znKLRvSUmCIUiZOgfhiRFt7XGN//C2GFuey4xkKiIr9LWMuVe9m\n"
+ "Wx39Ea2UGEtNGCEVvZdJMDVRl7heFTfJTN4L1YeyWx6iNRWlpAmgQOKII7slHwlq\n"
+ "seEULOLOXc9AsU/v9ba9G54DFbHfe2k44ZOwEmaQZW5VF/I0YMMdAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAKFRzlrqPPxEW0nboAJ1qzKFb/vFtvRW0xNVb8RtbOY/NY5FV1hS8yfH\n"
+ "igtugkrOBmWah7cmJhiON2j+TKeBxEoXwJMZeyV+HLbr7nY/mFhad4BQ3Frkl8d6\n"
+ "1kQMhOJswMdwnnVHPNGUob4YAX0SpFA6MpBVj92zmMBeaihqUS9VAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key br8svioLcJCAQxoo3KvlT288p8rb4lQIZNLlplkIKkw=\n"
+ "ntor-onion-key-crosscert 0\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55AQyCiUqcuDdmdW/SyTMcJ+Zf5lH1O3CPsU1d+FcKzKvzAG9XqwmRm0uJ\n"
+ "E49NoHcWr9IzdIwSGo+PJSkVpk95a5p2s065BetCWxEEBJQniajQf2hZ36zmV9rq\n"
+ "a6puqkEAKAM=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "d6QGIVAJL5JjHUyV+aicLIdBYyxHwviKpPcp7uldRF8vfDGFpu0qFgJ5KT+3t36w\n"
+ "QY1r75bvUMG/ZzGKDg95dcK0X2AK6GFlcrYyCoQEVOsuPc1QEUeK9P2s7viNQE4V\n"
+ "tRwG/CvJhPfcnxErzVGfXIeYRL1r/hPNFDZSeSxPPM0=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "router-sig-ed25519 ts9pFk8PnDWtXgQad09XC/ZCbruSx1U1pNOMWF9fyoNG0CodxdDH9Vglg+BOS7Nd9fmsINfPWKCVdVuSSM7zCA\n"
+ "reject *:*\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "YMl6mpQm7UCsPQhZKMm0aZ7fzGevWzRbQO+de20HTn7fVqMWQf2hBDJe9QTN/uDK\n"
+ "/VKYT8SnIBexbrSMy1N5q8kNFKxxUtwA9GRtz620Vvc4m+lz/tnT9qucIKCDL5iJ\n"
+ "eRpnls0JoAMIHKl99zdUioYubmOZuqUaRAdT8ulWy+Y=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_CERT1[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55AYf+rX8a5rzdTBGPvLdQIP8XcElDDQnJIruGqfDTj+tjAP+3XOL2UTmn\n"
+ "Hu39PbLZV+m9DIj/DvG38M0hP4MmHUjP/iZG5PaCX6/aMe+nQSNuTl0IDGpIo1l8\n"
+ "dZToQTFSzAQ=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAM4o2DrTwn3wrvUMm41S/hFL5ZtRHGRDh26o8htn14AKMC65vpygKFY7\n"
+ "fUQVClAiJthAs5fD/8sE5XDtQrLnFv5OegQx8kSPuwyS/+5pI1bdxRJvKMOUl2Tc\n"
+ "fAUhzeNBmPvW3lMi9Fksw5sCSAKQ5VH/+DlYvBGZIO49pTnOAty1AgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAMzIsJeEWWjN3Lp6qrzaJGn8uhJPJyjy2Wt3sp7z7iD/yBWW6Q7Jku3e\n"
+ "C5QfKmSmNi2pNjS0SqPjqZZNsbcxpq/bEOcZdysZG1lqi/QgxUevk57RWjh3EFsG\n"
+ "TwK3ougKWB5Q6/3m32dNsnnnDqzVapgZo7Zd3V/aCo0BVtL5VXZbAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key W28nwT/5FJ818M78y/5sNOkxhQ7ENBhjVhGG2j6KvFY=\n"
+ "ntor-onion-key-crosscert 0\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55AYf+rX8a5rzdTBGPvLdQIP8XcElDDQnJIruGqfDTj+tjAP+3XOL2UTmn\n"
+ "Hu39PbLZV+m9DIj/DvG38M0hP4MmHUjP/iZG5PaCX6/aMe+nQSNuTl0IDGpIo1l8\n"
+ "dZToQTFSzAQ=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "FWnEjvFob0ObgqohMT7miwGsAuioCT7Urz6tyWaGWph/TP9hbFWj4MPK5mt998mn\n"
+ "xA8zHSF5n/edu7wVX+rtnPrYPBmg+qN8+Pq6XMg64CwtWu+sqigsi6vtz/TfAIDL\n"
+ "mypENmSY32sWPvy/CA8dAZ2ASh57EH9a+WcFModpXkM=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 88YqJdGJS4O6XiUCNrc9xbOHxujvcN/TkCoRuQQeKfZGHM+4IhI6AcXFlPIfDYq0SAavMhVmzsDDw0ROl7vyCQ\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "cU4WDO3w9ZfVRbNUgxOQMbwS2xWXvaL+cZmIV6AAjAZVWkLEpif4g6uYu+jJUZOS\n"
+ "NUT7lNOMwTu4tE4b1YJpnD9T8iW0DlOXxlvRBMQYmKwhQuYk898BDGTSk+0AY0HJ\n"
+ "vv8wRVewDajNhW7tFY907IdHvPXG0u83GANxkYrRyUg=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_CERT2[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN WOBBLY RUTABAGA-----\n"
+ "helo\n"
+ "-----END WOBBLY RUTABAGA-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBANZvqyqFeiekh8ApqIGK4ZtOqjaX87EzDestvAWwamVOXiPoUrzXgM3O\n"
+ "l8uuTnMA4TfnjLyyA2TnaMzJylOI1OMHuW/D9B/liWDstSxWNNIlKgLQ/Dh9xBS7\n"
+ "uQb2PYlI+iMkPKPyJQSTDdGHE7cdFPewUfhRtJU3F5ztm/3FLBFvAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBANZl8U/Z8KCPS7EBDzt8i9kNETXS7vnp9gnw3BQNXfjiDtDg9eO7ChxY\n"
+ "NBwuOTXmRxfX3W9kvZ0op9Hno6hixIhHzDql+vZ+hN7yPanVVDglSUXcr31yBm5K\n"
+ "kA+ZnRvH3oVQ97E4rRzpi09dtI13Pzu7JS5jRMtH+JF1kQBoNC0dAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key lUrEL+TVXpjjHQ2BIKk34vblyDmoyMro1a6/9hJ4VRc=\n"
+ "ntor-onion-key-crosscert 0\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55Abm5E7FBdd3F8N1xuz/vdv03zh2lABrmGjzPQ3AFJtntALNeQTgjv0JL\n"
+ "jON4+SPNi0B2Bva3yKaSsdxiHQ1rIwQqIUVkzXmmX4jmsvJK/9gERAdD7GafTKZQ\n"
+ "BaZbNXBvmQw=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "OxkqFsw1vHUQ9iPYcKC/MHUBtbLPK6JY2i81ccAai2eW118UXcTbeCRccrXyqSkl\n"
+ "RLcooZyli1D6wg9x7O8+2+HXIbUa6WcTOD1Qi7Z9wKZfk4sDUy7QHKENMRfAXwX3\n"
+ "U/gqd4BflMPp4+XrYfPzz+6yQPWp0t9wXbFv5hZ9F3k=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 fW6Bt4R3xVk5KMDyOcYg8n5ANP0OrQq2PQFK2cW0lTAdi+eX+oT/BeWnkrn0uSWOC/t4omCmH4Rdl8M9xtpfBA\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "DHxiQXuLxZR0ylqwUGGePgN4KF4ItlOV/DuGmmszCO/Ut0p+5s4FP2v6Mm9M92Wj\n"
+ "75rS9xF/Ts0Kf49dvgc+c5VTvhX5I5SwGQkRk0RNJtNoP0t+qXBHaFV8BlAeaWF6\n"
+ "Lg3O+GUK325fQv9uDPCe37mFQV9jafAzsZUrO/ggb1U=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+static const char EX_RI_ED_BAD_CERT3[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "BVVVnf55AW5TTGF9jCMl7aALZzqypD9Bj8WYnAPIrKCoIJdgMbY0AQAgBAB7eCn8\n"
+ "rukx7t/egZUdqU7+FYqsnO4wdmOkLZkp0+gpF3jjk6N1Q0037NNVNZBjONB0Nm2F\n"
+ "CpB3nWSJliSSKr5tOYsuBPFy5VVGYeKPakpOoxanQ1UcqevMBAQy0zf9hwA=\n"
+ "-----END ED25519 CERT-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAPgeQNbKwpnTU+qW/2djh66hptS9rcy1B4vdyWkDTdREao2ECuCv691Y\n"
+ "oIw3MpTWvpC1qHIKorunusR0FKgwXw3xQTikXbDq/1ptsekzoIA1R/hltQV3UuGH\n"
+ "zdzHuQXAMX7Fdll2gyya03c3Yq5s+xSDvGdkEeaIoctKjwxp4SdNAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAOzWuH4cPW9rIrfi8MrruMUg4IUVHz4BxfY4/szMIUvzeEAdHn4FYkWy\n"
+ "Vt7MDtUELZsmZeFNmkn72kLxnrdZ5XhxZBriq1Fzq11cSWRBF+SyE1MdcouY4GyG\n"
+ "drw6T8xb8ty19q0eO6C/gw27iqXPAp1clvkroLg6Nv9lGZvsedVDAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key /vYZ+9yLqG7yUnutoI57s96JBl36GTz0IDWE244rbzE=\n"
+ "ntor-onion-key-crosscert 0\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55AZ4zVBWP/fIYEgWmyj0WpO6CkXRJjtrWXtiT02k3IddiAMpYgMemGIpN\n"
+ "xj7TQRULsHHYvo4fLcKrSgndQbUUhfLTUuVhIzbnE2TBLMVOEkpxKU6mTuvTT/3h\n"
+ "MJugrwTWVg4=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "c/Vqu3wtsTsYMdnhTS9Tn1Pq6jDmH4uRD5WmbaCKKrkin2DjuYSMVpypndkdlZDE\n"
+ "He7uF7SUO3QG/UcRIXYOsg9MSLUmvn2kIwef8ykyqlRh95Csjo5DyattUhL2w4QF\n"
+ "tJkJBQAnXWaAVW1O8XimGCAvJ84cxbmZEcpN6WKjrXI=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 Ue7bkPpOoc8ca7cyQj/Vq3BP5X4vwLA5QmpLGw/WfRNVRPojJRxU3RVqWMi3JbsJFRTe6pH6ZHyXER33G5aAAA\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "ifKUtbxmqHVs8A0oT5n7Te0c6D/XqWQTc0RxX9OKGspzh6wNX26h0Xa2vpK1Q9Zu\n"
+ "sj61I7vbHuZN6rxiWs9IzJgb//XaNJasX1pd9tbGSXW+yYzc9G9kaa7vp3HcnhIP\n"
+ "XVWzzS8WmOiVNGcF65j6f7yGloTgN7cHMptgJG7pWes=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
+static const char EX_RI_BAD_EI_DIGEST2[] =
+ "router fred 127.0.0.1 9001 0 9002\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf55ATrK8IVBWLO2yXKCqXLXJOTu89W2b+hREPO+tCrxjVqWAQAgBACG/vVx\n"
+ "NK8wKVZvf34d75ZObSR0ge1N2RrAIKNslNXBq/tcllIrNE4S0ZNcMpA+hxXoVFeo\n"
+ "jbxifYX7nTs5N3GrGPmkiuo82v2X6ZwoIXJGFnvWMxCjsYsUVDDxoT6h/w8=\n"
+ "-----END ED25519 CERT-----\n"
+ "extra-info-digest E5FAC29E766D63F96AD175069640E803F2723765 99oo\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAK9wHSdRalxkuAybrSCA3dlEC1ZGc7oHOzXRGLg+z6batuiCdQtus1Rk\n"
+ "LP821eZJtEMAE56aewCIHDcTiCxVa6DMqmxRjm5pfW4G5H5QCPYT6Fu0RoYck3Ef\n"
+ "vkgits5/fNYGPPVC7k8AdGax5dKj5oFVGq+JWolYFRv6tyR9AThvAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAKxjxTQ/T/MHpFbk7/zwA7l5b3IW3yVcyVe6eIGFoYun8FI0fbYRmR4M\n"
+ "G5Asu07gP9Bbgt3AFPuEqrjg4u+lIkgqTcCgKWJbAgm7fslwaDTXQ36A7I1M95PD\n"
+ "GJ10Dk5v4dVbrqwoF7MSrQPFtMO91RP11nGPSvDqXZJ4XpwqwdxpAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key LuVmHxpj4F5mPXGNi4MtxbIbLMav6frJRBsRgAvpdzo=\n"
+ "ntor-onion-key-crosscert 0\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf55AYb+9XE0rzApVm9/fh3vlk5tJHSB7U3ZGsAgo2yU1cGrAKBcSzwi4lY/\n"
+ "salCELOLdeZzOjDNnBd6cKp2WJg7Yz5zFlbVbyNk0iwfGmucHk8vQZe5BS0Oq/Pz\n"
+ "B1u/BcJv8gk=\n"
+ "-----END ED25519 CERT-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "QsAQVdDVHtasDbhrZG4ZxImdTTMY7fz3vouAiGyZx6/jCCB5v0gHwTn4xo6pgLEW\n"
+ "LQfMhQZIr76Ky67c0hAN2hihuDlfvhfVe9c2c5UOH1BOhq3llE3Hc3xGyEy3rw7r\n"
+ "5y38YGi759CvsP2/L8JfXMuBg89OcgJYFa27Q6e6MdQ=\n"
+ "-----END CROSSCERT-----\n"
+ "published 2014-10-05 12:00:00\n"
+ "bandwidth 1000 1000 1000\n"
+ "reject *:*\n"
+ "router-sig-ed25519 5zoQ0dufeeOJ/tE/BgcWgM8JpfW1ELSXLz4dI+K8YRH/gUtaPmYJgU2QfeUHD0oy1iwv4Qvl8Ferga7aBk1+DA\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "D6KRMwkb6JmVEnpZ825SD3LMB84UmVy0i94xk44OwhoWNKLXhaSTWJgf6AqnPG5o\n"
+ "QrCypSb44bYLn+VaDN5LVUl36jeZqCT4xd+4ZwIRdPOUj7vcVmyUDg3lXcAIk97Q\n"
+ "E5PrQY1mQuLSIjjKInAR2NRBumNJtRw31Y/DTB7tODU=\n"
+ "-----END SIGNATURE-----\n"
+ "\n"
+ ;
diff --git a/src/test/include.am b/src/test/include.am
index d20d2f66b9..0ef556a9bf 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -1,10 +1,32 @@
-TESTS += src/test/test src/test/test-slow
+
+TESTSCRIPTS = src/test/test_zero_length_keys.sh
+
+if USEPYTHON
+TESTSCRIPTS += src/test/test_ntor.sh src/test/test_bt.sh
+endif
+
+TESTS += src/test/test src/test/test-slow src/test/test-memwipe \
+ $(TESTSCRIPTS)
+
+### This is a lovely feature, but it requires automake >= 1.12, and Tor
+### doesn't require that yet. Below is a kludge to work around.
+###
+# TEST_EXTENSIONS = .sh
+# SH_LOG_COMPILER = $(SHELL)
+
+check-am: set-test-permissions
+
+.PHONY: set-test-permissions
+set-test-permissions: $(TESTSCRIPTS)
+ $(AM_V_at)chmod u+x $(TESTSCRIPTS)
+
noinst_PROGRAMS+= src/test/bench
if UNITTESTS_ENABLED
noinst_PROGRAMS+= \
src/test/test \
src/test/test-slow \
+ src/test/test-memwipe \
src/test/test-child \
src/test/test_workqueue
endif
@@ -13,6 +35,8 @@ src_test_AM_CPPFLAGS = -DSHARE_DATADIR="\"$(datadir)\"" \
-DLOCALSTATEDIR="\"$(localstatedir)\"" \
-DBINDIR="\"$(bindir)\"" \
-I"$(top_srcdir)/src/or" -I"$(top_srcdir)/src/ext" \
+ -I"$(top_srcdir)/src/trunnel" \
+ -I"$(top_srcdir)/src/ext/trunnel" \
-DTOR_UNIT_TESTS
# -L flags need to go in LDFLAGS. -l flags need to go in LDADD.
@@ -34,6 +58,7 @@ src_test_test_SOURCES = \
src/test/test_circuitmux.c \
src/test/test_config.c \
src/test/test_containers.c \
+ src/test/test_controller.c \
src/test/test_controller_events.c \
src/test/test_crypto.c \
src/test/test_data.c \
@@ -44,6 +69,8 @@ src_test_test_SOURCES = \
src/test/test_extorport.c \
src/test/test_hs.c \
src/test/test_introduce.c \
+ src/test/test_keypin.c \
+ src/test/test_link_handshake.c \
src/test/test_logging.c \
src/test/test_microdesc.c \
src/test/test_nodelist.c \
@@ -73,10 +100,13 @@ src_test_test_slow_SOURCES = \
src/test/testing_common.c \
src/ext/tinytest.c
+src_test_test_memwipe_SOURCES = \
+ src/test/test-memwipe.c
+
src_test_test_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
-src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS)
+src_test_test_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_test_bench_SOURCES = \
src/test/bench.c
@@ -89,7 +119,7 @@ src_test_test_workqueue_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
src_test_test_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_test_LDADD = src/or/libtor-testing.a src/common/libor-testing.a \
- src/common/libor-crypto-testing.a $(LIBDONNA) \
+ src/common/libor-crypto-testing.a $(LIBDONNA) src/common/libor.a \
src/common/libor-event-testing.a src/trunnel/libor-trunnel-testing.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
@@ -100,11 +130,16 @@ src_test_test_slow_CFLAGS = $(src_test_test_CFLAGS)
src_test_test_slow_LDADD = $(src_test_test_LDADD)
src_test_test_slow_LDFLAGS = $(src_test_test_LDFLAGS)
+src_test_test_memwipe_CPPFLAGS = $(src_test_test_CPPFLAGS)
+src_test_test_memwipe_CFLAGS = $(src_test_test_CFLAGS)
+src_test_test_memwipe_LDADD = $(src_test_test_LDADD)
+src_test_test_memwipe_LDFLAGS = $(src_test_test_LDFLAGS)
+
src_test_bench_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ \
@TOR_LDFLAGS_libevent@
src_test_bench_LDADD = src/or/libtor.a src/common/libor.a \
src/common/libor-crypto.a $(LIBDONNA) \
- src/common/libor-event.a \
+ src/common/libor-event.a src/trunnel/libor-trunnel.a \
@TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ @TOR_LIBEVENT_LIBS@ \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \
@TOR_SYSTEMD_LIBS@
@@ -137,13 +172,6 @@ src_test_test_ntor_cl_LDADD = src/or/libtor.a src/common/libor.a \
@TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
src_test_test_ntor_cl_AM_CPPFLAGS = \
-I"$(top_srcdir)/src/or"
-NTOR_TEST_DEPS=src/test/test-ntor-cl
-
-if COVERAGE_ENABLED
-CMDLINE_TEST_TOR = ./src/or/tor-cov
-else
-CMDLINE_TEST_TOR = ./src/or/tor
-endif
noinst_PROGRAMS += src/test/test-bt-cl
src_test_test_bt_cl_SOURCES = src/test/test_bt_cl.c
@@ -151,22 +179,10 @@ src_test_test_bt_cl_LDADD = src/common/libor-testing.a \
@TOR_LIB_MATH@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@
src_test_test_bt_cl_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
-src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS)
-
-
-check-local: $(NTOR_TEST_DEPS) $(CMDLINE_TEST_TOR)
-if USEPYTHON
- $(PYTHON) $(top_srcdir)/src/test/test_cmdline_args.py $(CMDLINE_TEST_TOR) "${top_srcdir}"
- $(PYTHON) $(top_srcdir)/src/test/ntor_ref.py test-tor
- $(PYTHON) $(top_srcdir)/src/test/ntor_ref.py self-test
- ./src/test/test-bt-cl assert | $(PYTHON) $(top_srcdir)/src/test/bt_test.py
- ./src/test/test-bt-cl crash | $(PYTHON) $(top_srcdir)/src/test/bt_test.py
-endif
- $(top_srcdir)/src/test/zero_length_keys.sh
+src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS)
EXTRA_DIST += \
src/test/bt_test.py \
src/test/ntor_ref.py \
src/test/slownacl_curve25519.py \
- src/test/test_cmdline_args.py \
src/test/zero_length_keys.sh
diff --git a/src/test/ntor_ref.py b/src/test/ntor_ref.py
index e37637d92a..767da57a9c 100755
--- a/src/test/ntor_ref.py
+++ b/src/test/ntor_ref.py
@@ -283,7 +283,7 @@ def client_part2(seckey_x, msg, node_id, pubkey_B, keyBytes=72):
my_auth = H_mac(auth_input)
badness = my_auth != their_auth
- badness = bad_result(yx) + bad_result(bx)
+ badness |= bad_result(yx) + bad_result(bx)
if badness:
return None
diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c
new file mode 100644
index 0000000000..a39bad1540
--- /dev/null
+++ b/src/test/test-memwipe.c
@@ -0,0 +1,209 @@
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+
+#include "crypto.h"
+#include "compat.h"
+
+#undef MIN
+#define MIN(a,b) ( ((a)<(b)) ? (a) : (b) )
+
+static unsigned fill_a_buffer_memset(void) __attribute__((noinline));
+static unsigned fill_a_buffer_memwipe(void) __attribute__((noinline));
+static unsigned fill_a_buffer_nothing(void) __attribute__((noinline));
+static unsigned fill_heap_buffer_memset(void) __attribute__((noinline));
+static unsigned fill_heap_buffer_memwipe(void) __attribute__((noinline));
+static unsigned fill_heap_buffer_nothing(void) __attribute__((noinline));
+static unsigned check_a_buffer(void) __attribute__((noinline));
+
+const char *s = NULL;
+
+#define BUF_LEN 2048
+
+#define FILL_BUFFER_IMPL() \
+ unsigned int i; \
+ unsigned sum = 0; \
+ \
+ /* Fill up a 1k buffer with a recognizable pattern. */ \
+ for (i = 0; i < BUF_LEN; i += strlen(s)) { \
+ memcpy(buf+i, s, MIN(strlen(s), BUF_LEN-i)); \
+ } \
+ \
+ /* Use the buffer as input to a computation so the above can't get */ \
+ /* optimized away. */ \
+ for (i = 0; i < BUF_LEN; ++i) { \
+ sum += (unsigned char)buf[i]; \
+ }
+
+static unsigned
+fill_a_buffer_memset(void)
+{
+ char buf[BUF_LEN];
+ FILL_BUFFER_IMPL()
+ memset(buf, 0, sizeof(buf));
+ return sum;
+}
+
+static unsigned
+fill_a_buffer_memwipe(void)
+{
+ char buf[BUF_LEN];
+ FILL_BUFFER_IMPL()
+ memwipe(buf, 0, sizeof(buf));
+ return sum;
+}
+
+static unsigned
+fill_a_buffer_nothing(void)
+{
+ char buf[BUF_LEN];
+ FILL_BUFFER_IMPL()
+ return sum;
+}
+
+static INLINE int
+vmemeq(volatile char *a, const char *b, size_t n)
+{
+ while (n--) {
+ if (*a++ != *b++)
+ return 0;
+ }
+ return 1;
+}
+
+static unsigned
+check_a_buffer(void)
+{
+ unsigned int i;
+ volatile char buf[1024];
+ unsigned sum = 0;
+
+ /* See if this buffer has the string in it.
+
+ YES, THIS DOES INVOKE UNDEFINED BEHAVIOR BY READING FROM AN UNINITIALIZED
+ BUFFER.
+
+ If you know a better way to figure out whether the compiler eliminated
+ the memset/memwipe calls or not, please let me know.
+ */
+ for (i = 0; i < BUF_LEN - strlen(s); ++i) {
+ if (vmemeq(buf+i, s, strlen(s)))
+ ++sum;
+ }
+
+ return sum;
+}
+
+static char *heap_buf = NULL;
+
+static unsigned
+fill_heap_buffer_memset(void)
+{
+ char *buf = heap_buf = malloc(BUF_LEN);
+ FILL_BUFFER_IMPL()
+ memset(buf, 0, BUF_LEN);
+ free(buf);
+ return sum;
+}
+
+static unsigned
+fill_heap_buffer_memwipe(void)
+{
+ char *buf = heap_buf = malloc(BUF_LEN);
+ FILL_BUFFER_IMPL()
+ memwipe(buf, 0, BUF_LEN);
+ free(buf);
+ return sum;
+}
+
+static unsigned
+fill_heap_buffer_nothing(void)
+{
+ char *buf = heap_buf = malloc(BUF_LEN);
+ FILL_BUFFER_IMPL()
+ free(buf);
+ return sum;
+}
+
+static unsigned
+check_heap_buffer(void)
+{
+ unsigned int i;
+ unsigned sum = 0;
+ volatile char *buf = heap_buf;
+
+ /* See if this buffer has the string in it.
+
+ YES, THIS DOES INVOKE UNDEFINED BEHAVIOR BY READING FROM A FREED BUFFER.
+
+ If you know a better way to figure out whether the compiler eliminated
+ the memset/memwipe calls or not, please let me know.
+ */
+ for (i = 0; i < BUF_LEN - strlen(s); ++i) {
+ if (vmemeq(buf+i, s, strlen(s)))
+ ++sum;
+ }
+
+ return sum;
+}
+
+static struct testcase {
+ const char *name;
+ /* this spacing satisfies make check-spaces */
+ unsigned
+ (*fill_fn)(void);
+ unsigned
+ (*check_fn)(void);
+} testcases[] = {
+ { "nil", fill_a_buffer_nothing, check_a_buffer },
+ { "nil-heap", fill_heap_buffer_nothing, check_heap_buffer },
+ { "memset", fill_a_buffer_memset, check_a_buffer },
+ { "memset-heap", fill_heap_buffer_memset, check_heap_buffer },
+ { "memwipe", fill_a_buffer_memwipe, check_a_buffer },
+ { "memwipe-heap", fill_heap_buffer_memwipe, check_heap_buffer },
+ { NULL, NULL, NULL }
+};
+
+int
+main(int argc, char **argv)
+{
+ unsigned x, x2;
+ int i;
+ int working = 1;
+ unsigned found[6];
+ (void) argc; (void) argv;
+
+ s = "squamous haberdasher gallimaufry";
+
+ memset(found, 0, sizeof(found));
+
+ for (i = 0; testcases[i].name; ++i) {
+ x = testcases[i].fill_fn();
+ found[i] = testcases[i].check_fn();
+
+ x2 = fill_a_buffer_nothing();
+
+ if (x != x2) {
+ working = 0;
+ }
+ }
+
+ if (!working || !found[0] || !found[1]) {
+ printf("It appears that this test case may not give you reliable "
+ "information. Sorry.\n");
+ }
+
+ if (!found[2] && !found[3]) {
+ printf("It appears that memset is good enough on this platform. Good.\n");
+ }
+
+ if (found[4] || found[5]) {
+ printf("ERROR: memwipe does not wipe data!\n");
+ return 1;
+ } else {
+ printf("OKAY: memwipe seems to work.\n");
+ return 0;
+ }
+}
+
diff --git a/src/test/test-network.sh b/src/test/test-network.sh
index be57cafb7f..ccfb5df424 100755
--- a/src/test/test-network.sh
+++ b/src/test/test-network.sh
@@ -13,7 +13,7 @@ do
export TOR_DIR="$2"
shift
;;
- --flavo?r|--network-flavo?r)
+ --flavor|--flavour|--network-flavor|--network-flavour)
export NETWORK_FLAVOUR="$2"
shift
;;
diff --git a/src/test/test.c b/src/test/test.c
index 0524a6978f..7ad849f49e 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1127,6 +1127,7 @@ extern struct testcase_t circuitlist_tests[];
extern struct testcase_t circuitmux_tests[];
extern struct testcase_t config_tests[];
extern struct testcase_t container_tests[];
+extern struct testcase_t controller_tests[];
extern struct testcase_t controller_event_tests[];
extern struct testcase_t crypto_tests[];
extern struct testcase_t dir_tests[];
@@ -1136,6 +1137,8 @@ extern struct testcase_t guardfraction_tests[];
extern struct testcase_t extorport_tests[];
extern struct testcase_t hs_tests[];
extern struct testcase_t introduce_tests[];
+extern struct testcase_t keypin_tests[];
+extern struct testcase_t link_handshake_tests[];
extern struct testcase_t logging_tests[];
extern struct testcase_t microdesc_tests[];
extern struct testcase_t nodelist_tests[];
@@ -1171,7 +1174,8 @@ struct testgroup_t testgroups[] = {
{ "circuitmux/", circuitmux_tests },
{ "config/", config_tests },
{ "container/", container_tests },
- { "control/", controller_event_tests },
+ { "control/", controller_tests },
+ { "control/event/", controller_event_tests },
{ "crypto/", crypto_tests },
{ "dir/", dir_tests },
{ "dir/md/", microdesc_tests },
@@ -1181,6 +1185,8 @@ struct testgroup_t testgroups[] = {
{ "extorport/", extorport_tests },
{ "hs/", hs_tests },
{ "introduce/", introduce_tests },
+ { "keypin/", keypin_tests },
+ { "link-handshake/", link_handshake_tests },
{ "nodelist/", nodelist_tests },
{ "oom/", oom_tests },
{ "options/", options_tests },
diff --git a/src/test/test_address.c b/src/test/test_address.c
index 424a6352b0..d13d678f3d 100644
--- a/src/test/test_address.c
+++ b/src/test/test_address.c
@@ -130,8 +130,8 @@ test_address_ifaddrs_to_smartlist(void *arg)
ipv6_sockaddr = tor_malloc(sizeof(struct sockaddr_in6));
ipv6_sockaddr->sin6_family = AF_INET6;
ipv6_sockaddr->sin6_port = 0;
- inet_pton(AF_INET6, "2001:db8:8714:3a90::12",
- &(ipv6_sockaddr->sin6_addr));
+ tor_inet_pton(AF_INET6, "2001:db8:8714:3a90::12",
+ &(ipv6_sockaddr->sin6_addr));
ifa = tor_malloc(sizeof(struct ifaddrs));
ifa_ipv4 = tor_malloc(sizeof(struct ifaddrs));
@@ -222,7 +222,7 @@ test_address_get_if_addrs_ifaddrs(void *arg)
(void)arg;
- results = get_interface_addresses_ifaddrs(0);
+ results = get_interface_addresses_ifaddrs(LOG_ERR);
tt_int_op(smartlist_len(results),>=,1);
tt_assert(smartlist_contains_localhost_tor_addr(results));
@@ -245,7 +245,7 @@ test_address_get_if_addrs_win32(void *arg)
(void)arg;
- results = get_interface_addresses_win32(0);
+ results = get_interface_addresses_win32(LOG_ERR);
tt_int_op(smartlist_len(results),>=,1);
tt_assert(smartlist_contains_localhost_tor_addr(results));
@@ -452,10 +452,194 @@ test_address_get_if_addrs_ioctl(void *arg)
#endif
+#define FAKE_SOCKET_FD (42)
+
+static tor_socket_t
+fake_open_socket(int domain, int type, int protocol)
+{
+ (void)domain;
+ (void)type;
+ (void)protocol;
+
+ return FAKE_SOCKET_FD;
+}
+
+static int last_connected_socket_fd = 0;
+
+static int connect_retval = 0;
+
+static tor_socket_t
+pretend_to_connect(tor_socket_t socket, const struct sockaddr *address,
+ socklen_t address_len)
+{
+ (void)address;
+ (void)address_len;
+
+ last_connected_socket_fd = socket;
+
+ return connect_retval;
+}
+
+static struct sockaddr *mock_addr = NULL;
+
+static int
+fake_getsockname(tor_socket_t socket, struct sockaddr *address,
+ socklen_t *address_len)
+{
+ socklen_t bytes_to_copy = 0;
+ (void) socket;
+
+ if (!mock_addr)
+ return -1;
+
+ if (mock_addr->sa_family == AF_INET) {
+ bytes_to_copy = sizeof(struct sockaddr_in);
+ } else if (mock_addr->sa_family == AF_INET6) {
+ bytes_to_copy = sizeof(struct sockaddr_in6);
+ } else {
+ return -1;
+ }
+
+ if (*address_len < bytes_to_copy) {
+ return -1;
+ }
+
+ memcpy(address,mock_addr,bytes_to_copy);
+ *address_len = bytes_to_copy;
+
+ return 0;
+}
+
+static void
+test_address_udp_socket_trick_whitebox(void *arg)
+{
+ int hack_retval;
+ tor_addr_t *addr_from_hack = tor_malloc_zero(sizeof(tor_addr_t));
+ struct sockaddr_in6 *mock_addr6;
+ struct sockaddr_in6 *ipv6_to_check =
+ tor_malloc_zero(sizeof(struct sockaddr_in6));
+
+ (void)arg;
+
+ MOCK(tor_open_socket,fake_open_socket);
+ MOCK(tor_connect_socket,pretend_to_connect);
+ MOCK(tor_getsockname,fake_getsockname);
+
+ mock_addr = tor_malloc_zero(sizeof(struct sockaddr_storage));
+ sockaddr_in_from_string("23.32.246.118",(struct sockaddr_in *)mock_addr);
+
+ hack_retval =
+ get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
+ AF_INET, addr_from_hack);
+
+ tt_int_op(hack_retval,==,0);
+ tt_assert(tor_addr_eq_ipv4h(addr_from_hack, 0x1720f676));
+
+ /* Now, lets do an IPv6 case. */
+ memset(mock_addr,0,sizeof(struct sockaddr_storage));
+
+ mock_addr6 = (struct sockaddr_in6 *)mock_addr;
+ mock_addr6->sin6_family = AF_INET6;
+ mock_addr6->sin6_port = 0;
+ tor_inet_pton(AF_INET6,"2001:cdba::3257:9652",&(mock_addr6->sin6_addr));
+
+ hack_retval =
+ get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
+ AF_INET6, addr_from_hack);
+
+ tt_int_op(hack_retval,==,0);
+
+ tor_addr_to_sockaddr(addr_from_hack,0,(struct sockaddr *)ipv6_to_check,
+ sizeof(struct sockaddr_in6));
+
+ tt_assert(sockaddr_in6_are_equal(mock_addr6,ipv6_to_check));
+
+ UNMOCK(tor_open_socket);
+ UNMOCK(tor_connect_socket);
+ UNMOCK(tor_getsockname);
+
+ done:
+ tor_free(ipv6_to_check);
+ tor_free(mock_addr);
+ tor_free(addr_from_hack);
+ return;
+}
+
+static void
+test_address_udp_socket_trick_blackbox(void *arg)
+{
+ /* We want get_interface_address6_via_udp_socket_hack() to yield
+ * the same valid address that get_interface_address6() returns.
+ * If the latter is unable to find a valid address, we want
+ * _hack() to fail and return-1.
+ *
+ * Furthermore, we want _hack() never to crash, even if
+ * get_interface_addresses_raw() is returning NULL.
+ */
+
+ tor_addr_t addr4;
+ tor_addr_t addr4_to_check;
+ tor_addr_t addr6;
+ tor_addr_t addr6_to_check;
+ int retval, retval_reference;
+
+ (void)arg;
+
+#if 0
+ retval_reference = get_interface_address6(LOG_DEBUG,AF_INET,&addr4);
+ retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
+ AF_INET,
+ &addr4_to_check);
+
+ tt_int_op(retval,==,retval_reference);
+ tt_assert( (retval == -1 && retval_reference == -1) ||
+ (tor_addr_compare(&addr4,&addr4_to_check,CMP_EXACT) == 0) );
+
+ retval_reference = get_interface_address6(LOG_DEBUG,AF_INET6,&addr6);
+ retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
+ AF_INET6,
+ &addr6_to_check);
+
+ tt_int_op(retval,==,retval_reference);
+ tt_assert( (retval == -1 && retval_reference == -1) ||
+ (tor_addr_compare(&addr6,&addr6_to_check,CMP_EXACT) == 0) );
+
+#else
+ /* Both of the blackbox test cases fail horribly if:
+ * * The host has no external addreses.
+ * * There are multiple interfaces with either AF_INET or AF_INET6.
+ * * The last address isn't the one associated with the default route.
+ *
+ * The tests SHOULD be re-enabled when #12377 is fixed correctly, but till
+ * then this fails a lot, in situations we expect failures due to knowing
+ * about the code being broken.
+ */
+
+ (void)addr4_to_check;
+ (void)addr6_to_check;
+ (void)addr6;
+ (void) retval_reference;
+#endif
+
+ /* When family is neither AF_INET nor AF_INET6, we want _hack to
+ * fail and return -1.
+ */
+
+ retval = get_interface_address6_via_udp_socket_hack(LOG_DEBUG,
+ AF_INET+AF_INET6,&addr4);
+
+ tt_assert(retval == -1);
+
+ done:
+ return;
+}
+
#define ADDRESS_TEST(name, flags) \
{ #name, test_address_ ## name, flags, NULL, NULL }
struct testcase_t address_tests[] = {
+ ADDRESS_TEST(udp_socket_trick_whitebox, TT_FORK),
+ ADDRESS_TEST(udp_socket_trick_blackbox, TT_FORK),
#ifdef HAVE_IFADDRS_TO_SMARTLIST
ADDRESS_TEST(get_if_addrs_ifaddrs, TT_FORK),
ADDRESS_TEST(ifaddrs_to_smartlist, 0),
diff --git a/src/test/test_bt.sh.in b/src/test/test_bt.sh.in
new file mode 100644
index 0000000000..ca8be965d4
--- /dev/null
+++ b/src/test/test_bt.sh.in
@@ -0,0 +1,9 @@
+#!@SHELL@
+# Test backtrace functionality.
+
+exitcode=0
+
+@builddir@/src/test/test-bt-cl assert | @PYTHON@ @abs_top_srcdir@/src/test/bt_test.py || exitcode=1
+@builddir@/src/test/test-bt-cl crash | @PYTHON@ @abs_top_srcdir@/src/test/bt_test.py || exitcode=1
+
+exit ${exitcode}
diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c
index 0fa0cd5c0a..01c621eb0e 100644
--- a/src/test/test_bt_cl.c
+++ b/src/test/test_bt_cl.c
@@ -5,6 +5,8 @@
#include <stdio.h>
#include <stdlib.h>
+/* To prevent 'assert' from going away. */
+#undef TOR_COVERAGE
#include "or.h"
#include "util.h"
#include "backtrace.h"
diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c
index 0ede8081d8..e8fce12314 100644
--- a/src/test/test_buffers.c
+++ b/src/test/test_buffers.c
@@ -698,6 +698,58 @@ test_buffers_zlib_fin_at_chunk_end(void *arg)
tor_free(msg);
}
+const uint8_t *tls_read_ptr;
+int n_remaining;
+int next_reply_val[16];
+
+static int
+mock_tls_read(tor_tls_t *tls, char *cp, size_t len)
+{
+ (void)tls;
+ int rv = next_reply_val[0];
+ if (rv > 0) {
+ int max = rv > (int)len ? (int)len : rv;
+ if (max > n_remaining)
+ max = n_remaining;
+ memcpy(cp, tls_read_ptr, max);
+ rv = max;
+ n_remaining -= max;
+ tls_read_ptr += max;
+ }
+
+ memmove(next_reply_val, next_reply_val + 1, 15*sizeof(int));
+ return rv;
+}
+
+static void
+test_buffers_tls_read_mocked(void *arg)
+{
+ uint8_t *mem;
+ buf_t *buf;
+ (void)arg;
+
+ mem = tor_malloc(64*1024);
+ crypto_rand((char*)mem, 64*1024);
+ tls_read_ptr = mem;
+ n_remaining = 64*1024;
+
+ MOCK(tor_tls_read, mock_tls_read);
+
+ buf = buf_new();
+
+ next_reply_val[0] = 1024;
+ tt_int_op(128, ==, read_to_buf_tls(NULL, 128, buf));
+
+ next_reply_val[0] = 5000;
+ next_reply_val[1] = 5000;
+ tt_int_op(6000, ==, read_to_buf_tls(NULL, 6000, buf));
+
+ done:
+ UNMOCK(tor_tls_read);
+ tor_free(mem);
+ buf_free(buf);
+}
+
struct testcase_t buffer_tests[] = {
{ "basic", test_buffers_basic, TT_FORK, NULL, NULL },
{ "copy", test_buffer_copy, TT_FORK, NULL, NULL },
@@ -710,6 +762,8 @@ struct testcase_t buffer_tests[] = {
{ "zlib_fin_with_nil", test_buffers_zlib_fin_with_nil, TT_FORK, NULL, NULL },
{ "zlib_fin_at_chunk_end", test_buffers_zlib_fin_at_chunk_end, TT_FORK,
NULL, NULL},
+ { "tls_read_mocked", test_buffers_tls_read_mocked, 0,
+ NULL, NULL },
END_OF_TESTCASES
};
diff --git a/src/test/test_channel.c b/src/test/test_channel.c
index 6cf66493a8..e11ac3f3cc 100644
--- a/src/test/test_channel.c
+++ b/src/test/test_channel.c
@@ -420,6 +420,7 @@ new_fake_channel(void)
chan->close = chan_test_close;
chan->get_overhead_estimate = chan_test_get_overhead_estimate;
+ chan->get_remote_descr = chan_test_get_remote_descr;
chan->num_bytes_queued = chan_test_num_bytes_queued;
chan->num_cells_writeable = chan_test_num_cells_writeable;
chan->write_cell = chan_test_write_cell;
@@ -615,7 +616,6 @@ test_channel_dumpstats(void *arg)
/* Test channel_dump_statistics */
ch->describe_transport = chan_test_describe_transport;
ch->dumpstats = chan_test_dumpstats;
- ch->get_remote_descr = chan_test_get_remote_descr;
ch->is_canonical = chan_test_is_canonical;
old_count = test_dumpstats_calls;
channel_dump_statistics(ch, LOG_DEBUG);
diff --git a/src/test/test_cmdline_args.py b/src/test/test_cmdline_args.py
deleted file mode 100755
index 57641974db..0000000000
--- a/src/test/test_cmdline_args.py
+++ /dev/null
@@ -1,311 +0,0 @@
-#!/usr/bin/python
-
-import binascii
-import hashlib
-import os
-import re
-import shutil
-import subprocess
-import sys
-import tempfile
-import unittest
-
-TOR = "./src/or/tor"
-TOP_SRCDIR = "."
-
-if len(sys.argv) > 1:
- TOR = sys.argv[1]
- del sys.argv[1]
-
-if len(sys.argv) > 1:
- TOP_SRCDIR = sys.argv[1]
- del sys.argv[1]
-
-class UnexpectedSuccess(Exception):
- pass
-
-class UnexpectedFailure(Exception):
- pass
-
-if sys.version < '3':
- def b2s(b):
- return b
- def s2b(s):
- return s
- def NamedTemporaryFile():
- return tempfile.NamedTemporaryFile(delete=False)
-else:
- def b2s(b):
- return str(b, 'ascii')
- def s2b(s):
- return s.encode('ascii')
- def NamedTemporaryFile():
- return tempfile.NamedTemporaryFile(mode="w",delete=False,encoding="ascii")
-
-def contents(fn):
- f = open(fn)
- try:
- return f.read()
- finally:
- f.close()
-
-def run_tor(args, failure=False, stdin=None):
- kwargs = {}
- if stdin != None:
- kwargs['stdin'] = subprocess.PIPE
- p = subprocess.Popen([TOR] + args, stdout=subprocess.PIPE, **kwargs)
- output, _ = p.communicate(input=stdin)
- result = p.poll()
- if result and not failure:
- raise UnexpectedFailure()
- elif not result and failure:
- raise UnexpectedSuccess()
- return b2s(output.replace('\r\n','\n'))
-
-def spaceify_fp(fp):
- for i in range(0, len(fp), 4):
- yield fp[i:i+4]
-
-def lines(s):
- out = s.splitlines()
- if out and out[-1] == '':
- del out[-1]
- return out
-
-def strip_log_junk(line):
- m = re.match(r'([^\[]+\[[a-z]*\] *)(.*)', line)
- if not m:
- return ""+line
- return m.group(2).strip()
-
-def randstring(entropy_bytes):
- s = os.urandom(entropy_bytes)
- return b2s(binascii.b2a_hex(s))
-
-def findLineContaining(lines, s):
- for ln in lines:
- if s in ln:
- return True
- return False
-
-class CmdlineTests(unittest.TestCase):
-
- def test_version(self):
- out = run_tor(["--version"])
- self.assertTrue(out.startswith("Tor version "))
- self.assertEqual(len(lines(out)), 1)
-
- def test_quiet(self):
- out = run_tor(["--quiet", "--quumblebluffin", "1"], failure=True)
- self.assertEqual(out, "")
-
- def test_help(self):
- out = run_tor(["--help"], failure=False)
- out2 = run_tor(["-h"], failure=False)
- self.assertTrue(out.startswith("Copyright (c) 2001"))
- self.assertTrue(out.endswith(
- "tor -f <torrc> [args]\n"
- "See man page for options, or https://www.torproject.org/ for documentation.\n"))
- self.assertTrue(out == out2)
-
- def test_hush(self):
- torrc = NamedTemporaryFile()
- torrc.close()
- try:
- out = run_tor(["--hush", "-f", torrc.name,
- "--quumblebluffin", "1"], failure=True)
- finally:
- os.unlink(torrc.name)
- self.assertEqual(len(lines(out)), 2)
- ln = [ strip_log_junk(l) for l in lines(out) ]
- self.assertEqual(ln[0], "Failed to parse/validate config: Unknown option 'quumblebluffin'. Failing.")
- self.assertEqual(ln[1], "Reading config failed--see warnings above.")
-
- def test_missing_argument(self):
- out = run_tor(["--hush", "--hash-password"], failure=True)
- self.assertEqual(len(lines(out)), 2)
- ln = [ strip_log_junk(l) for l in lines(out) ]
- self.assertEqual(ln[0], "Command-line option '--hash-password' with no value. Failing.")
-
- def test_hash_password(self):
- out = run_tor(["--hash-password", "woodwose"])
- result = lines(out)[-1]
- self.assertEqual(result[:3], "16:")
- self.assertEqual(len(result), 61)
- r = binascii.a2b_hex(result[3:])
- self.assertEqual(len(r), 29)
-
- salt, how, hashed = r[:8], r[8], r[9:]
- self.assertEqual(len(hashed), 20)
- if type(how) == type("A"):
- how = ord(how)
-
- count = (16 + (how & 15)) << ((how >> 4) + 6)
- stuff = salt + s2b("woodwose")
- repetitions = count // len(stuff) + 1
- inp = stuff * repetitions
- inp = inp[:count]
-
- self.assertEqual(hashlib.sha1(inp).digest(), hashed)
-
- def test_digests(self):
- main_c = os.path.join(TOP_SRCDIR, "src", "or", "main.c")
-
- if os.stat(TOR).st_mtime < os.stat(main_c).st_mtime:
- self.skipTest(TOR+" not up to date")
- out = run_tor(["--digests"])
- main_line = [ l for l in lines(out) if l.endswith("/main.c") or l.endswith(" main.c") ]
- digest, name = main_line[0].split()
- f = open(main_c, 'rb')
- actual = hashlib.sha1(f.read()).hexdigest()
- f.close()
- self.assertEqual(digest, actual)
-
- def test_dump_options(self):
- default_torrc = NamedTemporaryFile()
- torrc = NamedTemporaryFile()
- torrc.write("SocksPort 9999")
- torrc.close()
- default_torrc.write("SafeLogging 0")
- default_torrc.close()
- out_sh = out_nb = out_fl = None
- opts = [ "-f", torrc.name,
- "--defaults-torrc", default_torrc.name ]
- try:
- out_sh = run_tor(["--dump-config", "short"]+opts)
- out_nb = run_tor(["--dump-config", "non-builtin"]+opts)
- out_fl = run_tor(["--dump-config", "full"]+opts)
- out_nr = run_tor(["--dump-config", "bliznert"]+opts,
- failure=True)
-
- out_verif = run_tor(["--verify-config"]+opts)
- finally:
- os.unlink(torrc.name)
- os.unlink(default_torrc.name)
-
- self.assertEqual(len(lines(out_sh)), 2)
- self.assertTrue(lines(out_sh)[0].startswith("DataDirectory "))
- self.assertEqual(lines(out_sh)[1:],
- [ "SocksPort 9999" ])
-
- self.assertEqual(len(lines(out_nb)), 2)
- self.assertEqual(lines(out_nb),
- [ "SafeLogging 0",
- "SocksPort 9999" ])
-
- out_fl = lines(out_fl)
- self.assertTrue(len(out_fl) > 100)
- self.assertTrue("SocksPort 9999" in out_fl)
- self.assertTrue("SafeLogging 0" in out_fl)
- self.assertTrue("ClientOnly 0" in out_fl)
-
- self.assertTrue(out_verif.endswith("Configuration was valid\n"))
-
- def test_list_fingerprint(self):
- tmpdir = tempfile.mkdtemp(prefix='ttca_')
- torrc = NamedTemporaryFile()
- torrc.write("ORPort 9999\n")
- torrc.write("DataDirectory %s\n"%tmpdir)
- torrc.write("Nickname tippi")
- torrc.close()
- opts = ["-f", torrc.name]
- try:
- out = run_tor(["--list-fingerprint"]+opts)
- fp = contents(os.path.join(tmpdir, "fingerprint"))
- finally:
- os.unlink(torrc.name)
- shutil.rmtree(tmpdir)
-
- out = lines(out)
- lastlog = strip_log_junk(out[-2])
- lastline = out[-1]
- fp = fp.strip()
- nn_fp = fp.split()[0]
- space_fp = " ".join(spaceify_fp(fp.split()[1]))
- self.assertEqual(lastlog,
- "Your Tor server's identity key fingerprint is '%s'"%fp)
- self.assertEqual(lastline, "tippi %s"%space_fp)
- self.assertEqual(nn_fp, "tippi")
-
- def test_list_options(self):
- out = lines(run_tor(["--list-torrc-options"]))
- self.assertTrue(len(out)>100)
- self.assertTrue(out[0] <= 'AccountingMax')
- self.assertTrue("UseBridges" in out)
- self.assertTrue("SocksPort" in out)
-
- def test_cmdline_args(self):
- default_torrc = NamedTemporaryFile()
- torrc = NamedTemporaryFile()
- contents = ("SocksPort 9999\n"
- "SocksPort 9998\n"
- "ORPort 9000\n"
- "ORPort 9001\n"
- "Nickname eleventeen\n"
- "ControlPort 9500\n")
- torrc.write(contents)
- default_torrc.write("")
- default_torrc.close()
- torrc.close()
- out_sh = out_nb = out_fl = None
-
- opts_stdin = [ "-f", "-",
- "--defaults-torrc", default_torrc.name,
- "--dump-config", "short" ]
- opts = [ "-f", torrc.name,
- "--defaults-torrc", default_torrc.name,
- "--dump-config", "short" ]
- try:
- out_0 = run_tor(opts_stdin,stdin=contents)
- out_1 = run_tor(opts)
- out_2 = run_tor(opts+["+ORPort", "9003",
- "SocksPort", "9090",
- "/ControlPort",
- "/TransPort",
- "+ExtORPort", "9005"])
- finally:
- os.unlink(torrc.name)
- os.unlink(default_torrc.name)
-
- out_0 = [ l for l in lines(out_0) if not l.startswith("DataDir") ]
- out_1 = [ l for l in lines(out_1) if not l.startswith("DataDir") ]
- out_2 = [ l for l in lines(out_2) if not l.startswith("DataDir") ]
-
- self.assertEqual(out_0,
- ["ControlPort 9500",
- "Nickname eleventeen",
- "ORPort 9000",
- "ORPort 9001",
- "SocksPort 9999",
- "SocksPort 9998"])
-
- self.assertEqual(out_1,
- ["ControlPort 9500",
- "Nickname eleventeen",
- "ORPort 9000",
- "ORPort 9001",
- "SocksPort 9999",
- "SocksPort 9998"])
-
- self.assertEqual(out_2,
- ["ExtORPort 9005",
- "Nickname eleventeen",
- "ORPort 9000",
- "ORPort 9001",
- "ORPort 9003",
- "SocksPort 9090"])
-
- def test_missing_torrc(self):
- fname = "nonexistent_file_"+randstring(8)
- out = run_tor(["-f", fname, "--verify-config"], failure=True)
- ln = [ strip_log_junk(l) for l in lines(out) ]
- self.assertTrue("Unable to open configuration file" in ln[-2])
- self.assertTrue("Reading config failed" in ln[-1])
-
- out = run_tor(["-f", fname, "--verify-config", "--ignore-missing-torrc"])
- ln = [ strip_log_junk(l) for l in lines(out) ]
- self.assertTrue(findLineContaining(ln, ", using reasonable defaults"))
- self.assertTrue("Configuration was valid" in ln[-1])
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/src/test/test_config.c b/src/test/test_config.c
index 0444062722..28e9fa0f32 100644
--- a/src/test/test_config.c
+++ b/src/test/test_config.c
@@ -17,6 +17,7 @@
#include "address.h"
#include "entrynodes.h"
#include "transports.h"
+#include "routerlist.h"
static void
test_config_addressmap(void *arg)
@@ -1036,7 +1037,7 @@ static int n_get_interface_address6_failure = 0;
/**
* This mock function is meant to replace get_interface_addres6().
- * It will pretent to fail by return -1.
+ * It will pretend to fail by return -1.
* <b>n_get_interface_address6_failure</b> is incremented by one
* every time this function is called and <b>last_address6_family</b>
* is assigned the value of <b>family</b> argument.
@@ -1405,7 +1406,7 @@ test_config_resolve_my_address(void *arg)
/* CASE 12:
* Suppose the following happens:
- * 1. options->Address is NULL AND options->DirAuthorities is 1.
+ * 1. options->Address is NULL AND options->DirAuthorities is non-NULL
* 2. tor_gethostname() succeeds in getting hostname of a machine ...
* 3. ... which is successfully parsed by tor_inet_aton() ...
* 4. into IPv4 address that tor_addr_is_inernal() considers to be
@@ -1443,10 +1444,1776 @@ test_config_resolve_my_address(void *arg)
UNMOCK(tor_gethostname);
}
+static int n_add_default_fallback_dir_servers_known_default = 0;
+
+/**
+ * This mock function is meant to replace add_default_fallback_dir_servers().
+ * It will parse and add one known default fallback dir server,
+ * which has a dir_port of 99.
+ * <b>n_add_default_fallback_dir_servers_known_default</b> is incremented by
+ * one every time this function is called.
+ */
+static void
+add_default_fallback_dir_servers_known_default(void)
+{
+ int i;
+ const char *fallback[] = {
+ "127.0.0.1:60099 orport=9009 "
+ "id=0923456789012345678901234567890123456789",
+ NULL
+ };
+ for (i=0; fallback[i]; i++) {
+ if (parse_dir_fallback_line(fallback[i], 0)<0) {
+ log_err(LD_BUG, "Couldn't parse internal FallbackDir line %s",
+ fallback[i]);
+ }
+ }
+ n_add_default_fallback_dir_servers_known_default++;
+}
+
+static void
+test_config_adding_dir_servers(void *arg)
+{
+ (void)arg;
+
+ /* allocate options */
+ or_options_t *options = tor_malloc(sizeof(or_options_t));
+
+ /* Allocate and populate configuration lines:
+ *
+ * Use the same format as the hard-coded directories in
+ * add_default_trusted_dir_authorities().
+ * Zeroing the structure has the same effect as initialising to:
+ * { NULL, NULL, NULL, CONFIG_LINE_NORMAL, 0};
+ */
+ config_line_t *test_dir_authority = tor_malloc(sizeof(config_line_t));
+ memset(test_dir_authority, 0, sizeof(config_line_t));
+ test_dir_authority->key = tor_strdup("DirAuthority");
+ test_dir_authority->value = tor_strdup(
+ "D0 orport=9000 "
+ "v3ident=0023456789012345678901234567890123456789 "
+ "127.0.0.1:60090 0123 4567 8901 2345 6789 0123 4567 8901 2345 6789"
+ );
+
+ config_line_t *test_alt_bridge_authority = tor_malloc(sizeof(config_line_t));
+ memset(test_alt_bridge_authority, 0, sizeof(config_line_t));
+ test_alt_bridge_authority->key = tor_strdup("AlternateBridgeAuthority");
+ test_alt_bridge_authority->value = tor_strdup(
+ "B1 orport=9001 bridge "
+ "127.0.0.1:60091 1123 4567 8901 2345 6789 0123 4567 8901 2345 6789"
+ );
+
+ config_line_t *test_alt_dir_authority = tor_malloc(sizeof(config_line_t));
+ memset(test_alt_dir_authority, 0, sizeof(config_line_t));
+ test_alt_dir_authority->key = tor_strdup("AlternateDirAuthority");
+ test_alt_dir_authority->value = tor_strdup(
+ "A2 orport=9002 "
+ "v3ident=0223456789012345678901234567890123456789 "
+ "127.0.0.1:60092 2123 4567 8901 2345 6789 0123 4567 8901 2345 6789"
+ );
+
+ /* Use the format specified in the manual page */
+ config_line_t *test_fallback_directory = tor_malloc(sizeof(config_line_t));
+ memset(test_fallback_directory, 0, sizeof(config_line_t));
+ test_fallback_directory->key = tor_strdup("FallbackDir");
+ test_fallback_directory->value = tor_strdup(
+ "127.0.0.1:60093 orport=9003 id=0323456789012345678901234567890123456789"
+ );
+
+ /* We need to know if add_default_fallback_dir_servers is called,
+ * so we use a version of add_default_fallback_dir_servers that adds
+ * one known default fallback directory.
+ * There doesn't appear to be any need to test it unmocked. */
+ MOCK(add_default_fallback_dir_servers,
+ add_default_fallback_dir_servers_known_default);
+
+ /* There are 16 different cases, covering each combination of set/NULL for:
+ * DirAuthorities, AlternateBridgeAuthority, AlternateDirAuthority &
+ * FallbackDir.
+ * But validate_dir_servers() ensures that:
+ * "You cannot set both DirAuthority and Alternate*Authority."
+ * This reduces the number of cases to 10.
+ *
+ * Let's count these cases using binary, with 1 meaning set & 0 meaning NULL
+ * So 1001 or case 9 is:
+ * DirAuthorities set,
+ * AlternateBridgeAuthority NULL,
+ * AlternateDirAuthority NULL
+ * FallbackDir set
+ * The valid cases are cases 0-9 counting using this method, as every case
+ * greater than or equal to 10 = 1010 is invalid.
+ *
+ * After #15642 - Disable default fallback dirs when any custom dirs set
+ *
+ * 1. Outcome: Use Set Directory Authorities
+ * - No Default Authorities
+ * - Use AlternateBridgeAuthority, AlternateDirAuthority, and FallbackDir
+ * if they are set
+ * Cases expected to yield this outcome:
+ * 8 & 9 (the 2 valid cases where DirAuthorities is set)
+ * 6 & 7 (the 2 cases where DirAuthorities is NULL, and
+ * AlternateBridgeAuthority and AlternateDirAuthority are both set)
+ *
+ * 2. Outcome: Use Set Bridge Authority
+ * - Use Default Non-Bridge Directory Authorities
+ * - Use FallbackDir if it is set, otherwise use default FallbackDir
+ * Cases expected to yield this outcome:
+ * 4 & 5 (the 2 cases where DirAuthorities is NULL,
+ * AlternateBridgeAuthority is set, and
+ * AlternateDirAuthority is NULL)
+ *
+ * 3. Outcome: Use Set Alternate Directory Authority
+ * - Use Default Bridge Authorities
+ * - Use FallbackDir if it is set, otherwise No Default Fallback Directories
+ * Cases expected to yield this outcome:
+ * 2 & 3 (the 2 cases where DirAuthorities and AlternateBridgeAuthority
+ * are both NULL, but AlternateDirAuthority is set)
+ *
+ * 4. Outcome: Use Set Custom Fallback Directory
+ * - Use Default Bridge & Directory Authorities
+ * Cases expected to yield this outcome:
+ * 1 (DirAuthorities, AlternateBridgeAuthority and AlternateDirAuthority
+ * are all NULL, but FallbackDir is set)
+ *
+ * 5. Outcome: Use All Defaults
+ * - Use Default Bridge & Directory Authorities, and
+ * Default Fallback Directories
+ * Cases expected to yield this outcome:
+ * 0 (DirAuthorities, AlternateBridgeAuthority, AlternateDirAuthority
+ * and FallbackDir are all NULL)
+ *
+ * Before #15642 but after #13163 - Stop using default authorities when both
+ * Alternate Dir and Bridge Authority are set
+ * (#13163 was committed in 0.2.6 as c1dd43d823c7)
+ *
+ * The behaviour is different in the following cases
+ * where FallbackDir is NULL:
+ * 2, 6, 8
+ *
+ * In these cases, the Default Fallback Directories are applied, even when
+ * DirAuthorities or AlternateDirAuthority are set.
+ *
+ * However, as the list of default fallback directories is currently empty,
+ * this change doesn't modify any user-visible behaviour.
+ */
+
+ /*
+ * Find out how many default Bridge, Non-Bridge and Fallback Directories
+ * are hard-coded into this build.
+ * This code makes some assumptions about the implementation.
+ * If they are wrong, one or more of cases 0-5 could fail.
+ */
+ int n_default_alt_bridge_authority = 0;
+ int n_default_alt_dir_authority = 0;
+ int n_default_fallback_dir = 0;
+#define n_default_authorities ((n_default_alt_bridge_authority) \
+ + (n_default_alt_dir_authority))
+
+ /* Pre-Count Number of Authorities of Each Type
+ * Use 0000: No Directory Authorities or Fallback Directories Set
+ */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 0000 */
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = NULL;
+ options->AlternateDirAuthority = NULL;
+ options->FallbackDir = NULL;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+
+ /* Count Bridge Authorities */
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if it's a bridge auth */
+ n_default_alt_bridge_authority +=
+ ((ds->is_authority && (ds->type & BRIDGE_DIRINFO)) ?
+ 1 : 0)
+ );
+ /* If we have no default bridge authority, something has gone wrong */
+ tt_assert(n_default_alt_bridge_authority >= 1);
+
+ /* Count v3 Authorities */
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment found counter if it's a v3 auth */
+ n_default_alt_dir_authority +=
+ ((ds->is_authority && (ds->type & V3_DIRINFO)) ?
+ 1 : 0)
+ );
+ /* If we have no default authorities, something has gone really wrong */
+ tt_assert(n_default_alt_dir_authority >= 1);
+
+ /* Calculate Fallback Directory Count */
+ n_default_fallback_dir = (smartlist_len(fallback_servers) -
+ n_default_alt_bridge_authority -
+ n_default_alt_dir_authority);
+ /* If we have a negative count, something has gone really wrong */
+ tt_assert(n_default_fallback_dir >= 0);
+ }
+ }
+
+ /*
+ * 1. Outcome: Use Set Directory Authorities
+ * - No Default Authorities
+ * - Use AlternateBridgeAuthority, AlternateDirAuthority, and FallbackDir
+ * if they are set
+ * Cases expected to yield this outcome:
+ * 8 & 9 (the 2 valid cases where DirAuthorities is set)
+ * 6 & 7 (the 2 cases where DirAuthorities is NULL, and
+ * AlternateBridgeAuthority and AlternateDirAuthority are both set)
+ */
+
+ /* Case 9: 1001 - DirAuthorities Set, AlternateBridgeAuthority Not Set,
+ AlternateDirAuthority Not Set, FallbackDir Set */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 1001 */
+ options->DirAuthorities = test_dir_authority;
+ options->AlternateBridgeAuthority = NULL;
+ options->AlternateDirAuthority = NULL;
+ options->FallbackDir = test_fallback_directory;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must not have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+
+ {
+ /* trusted_dir_servers */
+ const smartlist_t *dir_servers = router_get_trusted_dir_servers();
+ /* D0, (No B1), (No A2) */
+ tt_assert(smartlist_len(dir_servers) == 1);
+
+ /* DirAuthority - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 1);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+ }
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+ /* D0, (No B1), (No A2), Custom Fallback */
+ tt_assert(smartlist_len(fallback_servers) == 2);
+
+ /* DirAuthority - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 1);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+
+ /* Custom FallbackDir - No Nickname - dir_port: 60093 */
+ int found_non_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_non_default_fallback +=
+ (ds->dir_port == 60093 ?
+ 1 : 0)
+ );
+ tt_assert(found_non_default_fallback == 1);
+
+ /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
+ int found_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_default_fallback +=
+ (ds->dir_port == 60099 ?
+ 1 : 0)
+ );
+ tt_assert(found_default_fallback == 0);
+ }
+ }
+
+ /* Case 8: 1000 - DirAuthorities Set, Others Not Set */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 1000 */
+ options->DirAuthorities = test_dir_authority;
+ options->AlternateBridgeAuthority = NULL;
+ options->AlternateDirAuthority = NULL;
+ options->FallbackDir = NULL;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must not have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+
+ {
+ /* trusted_dir_servers */
+ const smartlist_t *dir_servers = router_get_trusted_dir_servers();
+ /* D0, (No B1), (No A2) */
+ tt_assert(smartlist_len(dir_servers) == 1);
+
+ /* DirAuthority - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 1);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+ }
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+ /* D0, (No B1), (No A2), (No Fallback) */
+ tt_assert(smartlist_len(fallback_servers) == 1);
+
+ /* DirAuthority - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 1);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+
+ /* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */
+ int found_non_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_non_default_fallback +=
+ (ds->dir_port == 60093 ?
+ 1 : 0)
+ );
+ tt_assert(found_non_default_fallback == 0);
+
+ /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
+ int found_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_default_fallback +=
+ (ds->dir_port == 60099 ?
+ 1 : 0)
+ );
+ tt_assert(found_default_fallback == 0);
+ }
+ }
+
+ /* Case 7: 0111 - DirAuthorities Not Set, Others Set */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 0111 */
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = test_alt_bridge_authority;
+ options->AlternateDirAuthority = test_alt_dir_authority;
+ options->FallbackDir = test_fallback_directory;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must not have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+
+ {
+ /* trusted_dir_servers */
+ const smartlist_t *dir_servers = router_get_trusted_dir_servers();
+ /* (No D0), B1, A2 */
+ tt_assert(smartlist_len(dir_servers) == 2);
+
+ /* (No DirAuthority) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* AlternateBridgeAuthority - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 1);
+
+ /* AlternateDirAuthority - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 1);
+ }
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+ /* (No D0), B1, A2, Custom Fallback */
+ tt_assert(smartlist_len(fallback_servers) == 3);
+
+ /* (No DirAuthority) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* AlternateBridgeAuthority - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 1);
+
+ /* AlternateDirAuthority - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 1);
+
+ /* Custom FallbackDir - No Nickname - dir_port: 60093 */
+ int found_non_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_non_default_fallback +=
+ (ds->dir_port == 60093 ?
+ 1 : 0)
+ );
+ tt_assert(found_non_default_fallback == 1);
+
+ /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
+ int found_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_default_fallback +=
+ (ds->dir_port == 60099 ?
+ 1 : 0)
+ );
+ tt_assert(found_default_fallback == 0);
+ }
+ }
+
+ /* Case 6: 0110 - DirAuthorities Not Set, AlternateBridgeAuthority &
+ AlternateDirAuthority Set, FallbackDir Not Set */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 0110 */
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = test_alt_bridge_authority;
+ options->AlternateDirAuthority = test_alt_dir_authority;
+ options->FallbackDir = NULL;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must not have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+
+ {
+ /* trusted_dir_servers */
+ const smartlist_t *dir_servers = router_get_trusted_dir_servers();
+ /* (No D0), B1, A2 */
+ tt_assert(smartlist_len(dir_servers) == 2);
+
+ /* (No DirAuthority) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* AlternateBridgeAuthority - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 1);
+
+ /* AlternateDirAuthority - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 1);
+ }
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+ /* (No D0), B1, A2, (No Fallback) */
+ tt_assert(smartlist_len(fallback_servers) == 2);
+
+ /* (No DirAuthority) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* AlternateBridgeAuthority - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 1);
+
+ /* AlternateDirAuthority - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 1);
+
+ /* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */
+ int found_non_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_non_default_fallback +=
+ (ds->dir_port == 60093 ?
+ 1 : 0)
+ );
+ tt_assert(found_non_default_fallback == 0);
+
+ /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
+ int found_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_default_fallback +=
+ (ds->dir_port == 60099 ?
+ 1 : 0)
+ );
+ tt_assert(found_default_fallback == 0);
+ }
+ }
+
+ /*
+ 2. Outcome: Use Set Bridge Authority
+ - Use Default Non-Bridge Directory Authorities
+ - Use FallbackDir if it is set, otherwise use default FallbackDir
+ Cases expected to yield this outcome:
+ 4 & 5 (the 2 cases where DirAuthorities is NULL,
+ AlternateBridgeAuthority is set, and
+ AlternateDirAuthority is NULL)
+ */
+
+ /* Case 5: 0101 - DirAuthorities Not Set, AlternateBridgeAuthority Set,
+ AlternateDirAuthority Not Set, FallbackDir Set */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 0101 */
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = test_alt_bridge_authority;
+ options->AlternateDirAuthority = NULL;
+ options->FallbackDir = test_fallback_directory;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must not have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+
+ {
+ /* trusted_dir_servers */
+ const smartlist_t *dir_servers = router_get_trusted_dir_servers();
+ /* (No D0), B1, (No A2), Default v3 Non-Bridge Authorities */
+ tt_assert(smartlist_len(dir_servers) == 1 + n_default_alt_dir_authority);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* AlternateBridgeAuthority - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 1);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+
+ /* There's no easy way of checking that we have included all the
+ * default v3 non-Bridge directory authorities, so let's assume that
+ * if the total count above is correct, we have the right ones.
+ */
+ }
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+ /* (No D0), B1, (No A2), Default v3 Non-Bridge Authorities,
+ * Custom Fallback */
+ tt_assert(smartlist_len(fallback_servers) ==
+ 2 + n_default_alt_dir_authority);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* AlternateBridgeAuthority - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 1);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+
+ /* Custom FallbackDir - No Nickname - dir_port: 60093 */
+ int found_non_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_non_default_fallback +=
+ (ds->dir_port == 60093 ?
+ 1 : 0)
+ );
+ tt_assert(found_non_default_fallback == 1);
+
+ /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
+ int found_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_default_fallback +=
+ (ds->dir_port == 60099 ?
+ 1 : 0)
+ );
+ tt_assert(found_default_fallback == 0);
+
+ /* There's no easy way of checking that we have included all the
+ * default v3 non-Bridge directory authorities, so let's assume that
+ * if the total count above is correct, we have the right ones.
+ */
+ }
+ }
+
+ /* Case 4: 0100 - DirAuthorities Not Set, AlternateBridgeAuthority Set,
+ AlternateDirAuthority & FallbackDir Not Set */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 0100 */
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = test_alt_bridge_authority;
+ options->AlternateDirAuthority = NULL;
+ options->FallbackDir = NULL;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+
+ {
+ /* trusted_dir_servers */
+ const smartlist_t *dir_servers = router_get_trusted_dir_servers();
+ /* (No D0), B1, (No A2), Default v3 Non-Bridge Authorities */
+ tt_assert(smartlist_len(dir_servers) == 1 + n_default_alt_dir_authority);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* AlternateBridgeAuthority - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 1);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+
+ /* There's no easy way of checking that we have included all the
+ * default v3 non-Bridge directory authorities, so let's assume that
+ * if the total count above is correct, we have the right ones.
+ */
+ }
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+ /* (No D0), B1, (No A2), Default v3 Non-Bridge Authorities,
+ * Default Fallback */
+ tt_assert(smartlist_len(fallback_servers) ==
+ 2 + n_default_alt_dir_authority);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* AlternateBridgeAuthority - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 1);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+
+ /* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */
+ int found_non_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_non_default_fallback +=
+ (ds->dir_port == 60093 ?
+ 1 : 0)
+ );
+ tt_assert(found_non_default_fallback == 0);
+
+ /* Default FallbackDir - No Nickname - dir_port: 60099 */
+ int found_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_default_fallback +=
+ (ds->dir_port == 60099 ?
+ 1 : 0)
+ );
+ tt_assert(found_default_fallback == 1);
+
+ /* There's no easy way of checking that we have included all the
+ * default v3 non-Bridge directory authorities, so let's assume that
+ * if the total count above is correct, we have the right ones.
+ */
+ }
+ }
+
+ /*
+ 3. Outcome: Use Set Alternate Directory Authority
+ - Use Default Bridge Authorities
+ - Use FallbackDir if it is set, otherwise No Default Fallback Directories
+ Cases expected to yield this outcome:
+ 2 & 3 (the 2 cases where DirAuthorities and AlternateBridgeAuthority
+ are both NULL, but AlternateDirAuthority is set)
+ */
+
+ /* Case 3: 0011 - DirAuthorities & AlternateBridgeAuthority Not Set,
+ AlternateDirAuthority & FallbackDir Set */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 0011 */
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = NULL;
+ options->AlternateDirAuthority = test_alt_dir_authority;
+ options->FallbackDir = test_fallback_directory;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must not have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+
+ {
+ /* trusted_dir_servers */
+ const smartlist_t *dir_servers = router_get_trusted_dir_servers();
+ /* (No D0), (No B1), Default Bridge Authorities, A2 */
+ tt_assert(smartlist_len(dir_servers) ==
+ 1 + n_default_alt_bridge_authority);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* AlternateDirAuthority - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 1);
+
+ /* There's no easy way of checking that we have included all the
+ * default Bridge authorities (except for hard-coding tonga's details),
+ * so let's assume that if the total count above is correct,
+ * we have the right ones.
+ */
+ }
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+ /* (No D0), (No B1), Default Bridge Authorities, A2,
+ * Custom Fallback Directory, (No Default Fallback Directories) */
+ tt_assert(smartlist_len(fallback_servers) ==
+ 2 + n_default_alt_bridge_authority);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* AlternateDirAuthority - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 1);
+
+ /* Custom FallbackDir - No Nickname - dir_port: 60093 */
+ int found_non_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_non_default_fallback +=
+ (ds->dir_port == 60093 ?
+ 1 : 0)
+ );
+ tt_assert(found_non_default_fallback == 1);
+
+ /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
+ int found_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_default_fallback +=
+ (ds->dir_port == 60099 ?
+ 1 : 0)
+ );
+ tt_assert(found_default_fallback == 0);
+
+ /* There's no easy way of checking that we have included all the
+ * default Bridge authorities (except for hard-coding tonga's details),
+ * so let's assume that if the total count above is correct,
+ * we have the right ones.
+ */
+ }
+ }
+
+ /* Case 2: 0010 - DirAuthorities & AlternateBridgeAuthority Not Set,
+ AlternateDirAuthority Set, FallbackDir Not Set */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 0010 */
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = NULL;
+ options->AlternateDirAuthority = test_alt_dir_authority;
+ options->FallbackDir = NULL;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must not have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+
+ {
+ /* trusted_dir_servers */
+ const smartlist_t *dir_servers = router_get_trusted_dir_servers();
+ /* (No D0), (No B1), Default Bridge Authorities, A2,
+ * No Default or Custom Fallback Directories */
+ tt_assert(smartlist_len(dir_servers) ==
+ 1 + n_default_alt_bridge_authority);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* AlternateDirAuthority - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 1);
+
+ /* There's no easy way of checking that we have included all the
+ * default Bridge authorities (except for hard-coding tonga's details),
+ * so let's assume that if the total count above is correct,
+ * we have the right ones.
+ */
+ }
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+ /* (No D0), (No B1), Default Bridge Authorities, A2,
+ * No Custom or Default Fallback Directories */
+ tt_assert(smartlist_len(fallback_servers) ==
+ 1 + n_default_alt_bridge_authority);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* AlternateDirAuthority - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 1);
+
+ /* (No Custom FallbackDir) - No Nickname - dir_port: 60093 */
+ int found_non_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_non_default_fallback +=
+ (ds->dir_port == 60093 ?
+ 1 : 0)
+ );
+ tt_assert(found_non_default_fallback == 0);
+
+ /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
+ int found_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_default_fallback +=
+ (ds->dir_port == 60099 ?
+ 1 : 0)
+ );
+ tt_assert(found_default_fallback == 0);
+
+ /* There's no easy way of checking that we have included all the
+ * default Bridge authorities (except for hard-coding tonga's details),
+ * so let's assume that if the total count above is correct,
+ * we have the right ones.
+ */
+ }
+ }
+
+ /*
+ 4. Outcome: Use Set Custom Fallback Directory
+ - Use Default Bridge & Directory Authorities
+ Cases expected to yield this outcome:
+ 1 (DirAuthorities, AlternateBridgeAuthority and AlternateDirAuthority
+ are all NULL, but FallbackDir is set)
+ */
+
+ /* Case 1: 0001 - DirAuthorities, AlternateBridgeAuthority
+ & AlternateDirAuthority Not Set, FallbackDir Set */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 0001 */
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = NULL;
+ options->AlternateDirAuthority = NULL;
+ options->FallbackDir = test_fallback_directory;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must not have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 0);
+
+ {
+ /* trusted_dir_servers */
+ const smartlist_t *dir_servers = router_get_trusted_dir_servers();
+ /* (No D0), (No B1), Default Bridge Authorities,
+ * (No A2), Default v3 Directory Authorities */
+ tt_assert(smartlist_len(dir_servers) == n_default_authorities);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+
+ /* There's no easy way of checking that we have included all the
+ * default Bridge & V3 Directory authorities, so let's assume that
+ * if the total count above is correct, we have the right ones.
+ */
+ }
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+ /* (No D0), (No B1), Default Bridge Authorities,
+ * (No A2), Default v3 Directory Authorities,
+ * Custom Fallback Directory, (No Default Fallback Directories) */
+ tt_assert(smartlist_len(fallback_servers) ==
+ 1 + n_default_authorities);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+
+ /* Custom FallbackDir - No Nickname - dir_port: 60093 */
+ int found_non_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_non_default_fallback +=
+ (ds->dir_port == 60093 ?
+ 1 : 0)
+ );
+ tt_assert(found_non_default_fallback == 1);
+
+ /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
+ int found_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_default_fallback +=
+ (ds->dir_port == 60099 ?
+ 1 : 0)
+ );
+ tt_assert(found_default_fallback == 0);
+
+ /* There's no easy way of checking that we have included all the
+ * default Bridge & V3 Directory authorities, so let's assume that
+ * if the total count above is correct, we have the right ones.
+ */
+ }
+ }
+
+ /*
+ 5. Outcome: Use All Defaults
+ - Use Default Bridge & Directory Authorities, Default Fallback Directories
+ Cases expected to yield this outcome:
+ 0 (DirAuthorities, AlternateBridgeAuthority, AlternateDirAuthority
+ and FallbackDir are all NULL)
+ */
+
+ /* Case 0: 0000 - All Not Set */
+ {
+ /* clear fallback dirs counter */
+ n_add_default_fallback_dir_servers_known_default = 0;
+
+ /* clear options*/
+ memset(options, 0, sizeof(or_options_t));
+
+ /* clear any previous dir servers:
+ consider_adding_dir_servers() should do this anyway */
+ clear_dir_servers();
+
+ /* assign options: 0001 */
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = NULL;
+ options->AlternateDirAuthority = NULL;
+ options->FallbackDir = NULL;
+
+ /* parse options - ensure we always update by passing NULL old_options */
+ consider_adding_dir_servers(options, NULL);
+
+ /* check outcome */
+
+ /* we must have added the default fallback dirs */
+ tt_assert(n_add_default_fallback_dir_servers_known_default == 1);
+
+ {
+ /* trusted_dir_servers */
+ const smartlist_t *dir_servers = router_get_trusted_dir_servers();
+ /* (No D0), (No B1), Default Bridge Authorities,
+ * (No A2), Default v3 Directory Authorities */
+ tt_assert(smartlist_len(dir_servers) == n_default_authorities);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(dir_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+
+ /* There's no easy way of checking that we have included all the
+ * default Bridge & V3 Directory authorities, so let's assume that
+ * if the total count above is correct, we have the right ones.
+ */
+ }
+
+ {
+ /* fallback_dir_servers */
+ const smartlist_t *fallback_servers = router_get_fallback_dir_servers();
+ /* (No D0), (No B1), Default Bridge Authorities,
+ * (No A2), Default v3 Directory Authorities,
+ * (No Custom Fallback Directory), Default Fallback Directories */
+ tt_assert(smartlist_len(fallback_servers) ==
+ n_default_authorities + n_default_fallback_dir);
+
+ /* (No DirAuthorities) - D0 - dir_port: 60090 */
+ int found_D0 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_D0 +=
+ (ds->dir_port == 60090 ?
+ 1 : 0)
+ );
+ tt_assert(found_D0 == 0);
+
+ /* (No AlternateBridgeAuthority) - B1 - dir_port: 60091 */
+ int found_B1 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_B1 +=
+ (ds->dir_port == 60091 ?
+ 1 : 0)
+ );
+ tt_assert(found_B1 == 0);
+
+ /* (No AlternateDirAuthority) - A2 - dir_port: 60092 */
+ int found_A2 = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_A2 +=
+ (ds->dir_port == 60092 ?
+ 1 : 0)
+ );
+ tt_assert(found_A2 == 0);
+
+ /* Custom FallbackDir - No Nickname - dir_port: 60093 */
+ int found_non_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_non_default_fallback +=
+ (ds->dir_port == 60093 ?
+ 1 : 0)
+ );
+ tt_assert(found_non_default_fallback == 0);
+
+ /* (No Default FallbackDir) - No Nickname - dir_port: 60099 */
+ int found_default_fallback = 0;
+ SMARTLIST_FOREACH(fallback_servers,
+ dir_server_t *,
+ ds,
+ /* increment the found counter if dir_port matches */
+ found_default_fallback +=
+ (ds->dir_port == 60099 ?
+ 1 : 0)
+ );
+ tt_assert(found_default_fallback == 1);
+
+ /* There's no easy way of checking that we have included all the
+ * default Bridge & V3 Directory authorities, and the default
+ * Fallback Directories, so let's assume that if the total count
+ * above is correct, we have the right ones.
+ */
+ }
+ }
+
+ done:
+ clear_dir_servers();
+
+ tor_free(test_dir_authority->key);
+ tor_free(test_dir_authority->value);
+ tor_free(test_dir_authority);
+
+ tor_free(test_alt_dir_authority->key);
+ tor_free(test_alt_dir_authority->value);
+ tor_free(test_alt_dir_authority);
+
+ tor_free(test_alt_bridge_authority->key);
+ tor_free(test_alt_bridge_authority->value);
+ tor_free(test_alt_bridge_authority);
+
+ tor_free(test_fallback_directory->key);
+ tor_free(test_fallback_directory->value);
+ tor_free(test_fallback_directory);
+
+ options->DirAuthorities = NULL;
+ options->AlternateBridgeAuthority = NULL;
+ options->AlternateDirAuthority = NULL;
+ options->FallbackDir = NULL;
+ or_options_free(options);
+
+ UNMOCK(add_default_fallback_dir_servers);
+}
+
#define CONFIG_TEST(name, flags) \
{ #name, test_config_ ## name, flags, NULL, NULL }
struct testcase_t config_tests[] = {
+ CONFIG_TEST(adding_dir_servers, TT_FORK),
CONFIG_TEST(resolve_my_address, TT_FORK),
CONFIG_TEST(addressmap, 0),
CONFIG_TEST(parse_bridge_line, 0),
diff --git a/src/test/test_containers.c b/src/test/test_containers.c
index 79085a748e..3d150f5abf 100644
--- a/src/test/test_containers.c
+++ b/src/test/test_containers.c
@@ -496,6 +496,43 @@ test_container_smartlist_join(void *arg)
}
static void
+test_container_smartlist_pos(void *arg)
+{
+ (void) arg;
+ smartlist_t *sl = smartlist_new();
+
+ smartlist_add(sl, tor_strdup("This"));
+ smartlist_add(sl, tor_strdup("is"));
+ smartlist_add(sl, tor_strdup("a"));
+ smartlist_add(sl, tor_strdup("test"));
+ smartlist_add(sl, tor_strdup("for"));
+ smartlist_add(sl, tor_strdup("a"));
+ smartlist_add(sl, tor_strdup("function"));
+
+ /* Test string_pos */
+ tt_int_op(smartlist_string_pos(NULL, "Fred"), ==, -1);
+ tt_int_op(smartlist_string_pos(sl, "Fred"), ==, -1);
+ tt_int_op(smartlist_string_pos(sl, "This"), ==, 0);
+ tt_int_op(smartlist_string_pos(sl, "a"), ==, 2);
+ tt_int_op(smartlist_string_pos(sl, "function"), ==, 6);
+
+ /* Test pos */
+ tt_int_op(smartlist_pos(NULL, "Fred"), ==, -1);
+ tt_int_op(smartlist_pos(sl, "Fred"), ==, -1);
+ tt_int_op(smartlist_pos(sl, "This"), ==, -1);
+ tt_int_op(smartlist_pos(sl, "a"), ==, -1);
+ tt_int_op(smartlist_pos(sl, "function"), ==, -1);
+ tt_int_op(smartlist_pos(sl, smartlist_get(sl,0)), ==, 0);
+ tt_int_op(smartlist_pos(sl, smartlist_get(sl,2)), ==, 2);
+ tt_int_op(smartlist_pos(sl, smartlist_get(sl,5)), ==, 5);
+ tt_int_op(smartlist_pos(sl, smartlist_get(sl,6)), ==, 6);
+
+ done:
+ SMARTLIST_FOREACH(sl, char *, cp, tor_free(cp));
+ smartlist_free(sl);
+}
+
+static void
test_container_smartlist_ints_eq(void *arg)
{
smartlist_t *sl1 = NULL, *sl2 = NULL;
@@ -1053,6 +1090,7 @@ struct testcase_t container_tests[] = {
CONTAINER_LEGACY(smartlist_overlap),
CONTAINER_LEGACY(smartlist_digests),
CONTAINER_LEGACY(smartlist_join),
+ CONTAINER_LEGACY(smartlist_pos),
CONTAINER(smartlist_ints_eq, 0),
CONTAINER_LEGACY(bitarray),
CONTAINER_LEGACY(digestset),
diff --git a/src/test/test_controller.c b/src/test/test_controller.c
new file mode 100644
index 0000000000..b40825bb5d
--- /dev/null
+++ b/src/test/test_controller.c
@@ -0,0 +1,163 @@
+/* Copyright (c) 2015, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#define CONTROL_PRIVATE
+#include "or.h"
+#include "control.h"
+#include "rendservice.h"
+#include "test.h"
+
+static void
+test_add_onion_helper_keyarg(void *arg)
+{
+ crypto_pk_t *pk = NULL;
+ crypto_pk_t *pk2 = NULL;
+ const char *key_new_alg = NULL;
+ char *key_new_blob = NULL;
+ char *err_msg = NULL;
+ char *encoded = NULL;
+ char *arg_str = NULL;
+
+ (void) arg;
+
+ /* Test explicit RSA1024 key generation. */
+ pk = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob,
+ &err_msg);
+ tt_assert(pk);
+ tt_str_op(key_new_alg, OP_EQ, "RSA1024");
+ tt_assert(key_new_blob);
+ tt_assert(!err_msg);
+
+ /* Test "BEST" key generation (Assumes BEST = RSA1024). */
+ crypto_pk_free(pk);
+ tor_free(key_new_blob);
+ pk = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob,
+ &err_msg);
+ tt_assert(pk);
+ tt_str_op(key_new_alg, OP_EQ, "RSA1024");
+ tt_assert(key_new_blob);
+ tt_assert(!err_msg);
+
+ /* Test discarding the private key. */
+ crypto_pk_free(pk);
+ tor_free(key_new_blob);
+ pk = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob,
+ &err_msg);
+ tt_assert(pk);
+ tt_assert(!key_new_alg);
+ tt_assert(!key_new_blob);
+ tt_assert(!err_msg);
+
+ /* Test generating a invalid key type. */
+ crypto_pk_free(pk);
+ pk = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob,
+ &err_msg);
+ tt_assert(!pk);
+ tt_assert(!key_new_alg);
+ tt_assert(!key_new_blob);
+ tt_assert(err_msg);
+
+ /* Test loading a RSA1024 key. */
+ tor_free(err_msg);
+ pk = pk_generate(0);
+ tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk, &encoded));
+ tor_asprintf(&arg_str, "RSA1024:%s", encoded);
+ pk2 = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
+ &err_msg);
+ tt_assert(pk2);
+ tt_assert(!key_new_alg);
+ tt_assert(!key_new_blob);
+ tt_assert(!err_msg);
+ tt_assert(crypto_pk_cmp_keys(pk, pk2) == 0);
+
+ /* Test loading a invalid key type. */
+ tor_free(arg_str);
+ crypto_pk_free(pk); pk = NULL;
+ tor_asprintf(&arg_str, "RSA512:%s", encoded);
+ pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
+ &err_msg);
+ tt_assert(!pk);
+ tt_assert(!key_new_alg);
+ tt_assert(!key_new_blob);
+ tt_assert(err_msg);
+
+ /* Test loading a invalid key. */
+ tor_free(arg_str);
+ crypto_pk_free(pk); pk = NULL;
+ tor_free(err_msg);
+ encoded[strlen(encoded)/2] = '\0';
+ tor_asprintf(&arg_str, "RSA1024:%s", encoded);
+ pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob,
+ &err_msg);
+ tt_assert(!pk);
+ tt_assert(!key_new_alg);
+ tt_assert(!key_new_blob);
+ tt_assert(err_msg);
+
+ done:
+ crypto_pk_free(pk);
+ crypto_pk_free(pk2);
+ tor_free(key_new_blob);
+ tor_free(err_msg);
+ tor_free(encoded);
+ tor_free(arg_str);
+}
+
+static void
+test_rend_service_parse_port_config(void *arg)
+{
+ const char *sep = ",";
+ rend_service_port_config_t *cfg = NULL;
+ char *err_msg = NULL;
+
+ (void)arg;
+
+ /* Test "VIRTPORT" only. */
+ cfg = rend_service_parse_port_config("80", sep, &err_msg);
+ tt_assert(cfg);
+ tt_assert(!err_msg);
+
+ /* Test "VIRTPORT,TARGET" (Target is port). */
+ rend_service_port_config_free(cfg);
+ cfg = rend_service_parse_port_config("80,8080", sep, &err_msg);
+ tt_assert(cfg);
+ tt_assert(!err_msg);
+
+ /* Test "VIRTPORT,TARGET" (Target is IPv4:port). */
+ rend_service_port_config_free(cfg);
+ cfg = rend_service_parse_port_config("80,192.0.2.1:8080", sep, &err_msg);
+ tt_assert(cfg);
+ tt_assert(!err_msg);
+
+ /* Test "VIRTPORT,TARGET" (Target is IPv6:port). */
+ rend_service_port_config_free(cfg);
+ cfg = rend_service_parse_port_config("80,[2001:db8::1]:8080", sep, &err_msg);
+ tt_assert(cfg);
+ tt_assert(!err_msg);
+
+ /* XXX: Someone should add tests for AF_UNIX targets if supported. */
+
+ /* Test empty config. */
+ rend_service_port_config_free(cfg);
+ cfg = rend_service_parse_port_config("", sep, &err_msg);
+ tt_assert(!cfg);
+ tt_assert(err_msg);
+
+ /* Test invalid port. */
+ tor_free(err_msg);
+ cfg = rend_service_parse_port_config("90001", sep, &err_msg);
+ tt_assert(!cfg);
+ tt_assert(err_msg);
+
+ done:
+ rend_service_port_config_free(cfg);
+ tor_free(err_msg);
+}
+
+struct testcase_t controller_tests[] = {
+ { "add_onion_helper_keyarg", test_add_onion_helper_keyarg, 0, NULL, NULL },
+ { "rend_service_parse_port_config", test_rend_service_parse_port_config, 0,
+ NULL, NULL },
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_controller_events.c b/src/test/test_controller_events.c
index e36314da45..bd91aecd94 100644
--- a/src/test/test_controller_events.c
+++ b/src/test/test_controller_events.c
@@ -293,6 +293,104 @@ test_cntev_format_cell_stats(void *arg)
tor_free(n_chan);
}
+static void
+test_cntev_event_mask(void *arg)
+{
+ unsigned int test_event, selected_event;
+ (void)arg;
+
+ /* Check that nothing is interesting when no events are set */
+ control_testing_set_global_event_mask(EVENT_MASK_NONE_);
+
+ /* Check that nothing is interesting between EVENT_MIN_ and EVENT_MAX_ */
+ for (test_event = EVENT_MIN_; test_event <= EVENT_MAX_; test_event++)
+ tt_assert(!control_event_is_interesting(test_event));
+
+ /* Check that nothing is interesting outside EVENT_MIN_ to EVENT_MAX_
+ * This will break if control_event_is_interesting() checks its arguments */
+ for (test_event = 0; test_event < EVENT_MIN_; test_event++)
+ tt_assert(!control_event_is_interesting(test_event));
+ for (test_event = EVENT_MAX_ + 1;
+ test_event < EVENT_CAPACITY_;
+ test_event++)
+ tt_assert(!control_event_is_interesting(test_event));
+
+ /* Check that all valid events are interesting when all events are set */
+ control_testing_set_global_event_mask(EVENT_MASK_ALL_);
+
+ /* Check that everything is interesting between EVENT_MIN_ and EVENT_MAX_ */
+ for (test_event = EVENT_MIN_; test_event <= EVENT_MAX_; test_event++)
+ tt_assert(control_event_is_interesting(test_event));
+
+ /* Check that nothing is interesting outside EVENT_MIN_ to EVENT_MAX_
+ * This will break if control_event_is_interesting() checks its arguments */
+ for (test_event = 0; test_event < EVENT_MIN_; test_event++)
+ tt_assert(!control_event_is_interesting(test_event));
+ for (test_event = EVENT_MAX_ + 1;
+ test_event < EVENT_CAPACITY_;
+ test_event++)
+ tt_assert(!control_event_is_interesting(test_event));
+
+ /* Check that only that event is interesting when a single event is set */
+ for (selected_event = EVENT_MIN_;
+ selected_event <= EVENT_MAX_;
+ selected_event++) {
+ control_testing_set_global_event_mask(EVENT_MASK_(selected_event));
+
+ /* Check that only this event is interesting
+ * between EVENT_MIN_ and EVENT_MAX_ */
+ for (test_event = EVENT_MIN_; test_event <= EVENT_MAX_; test_event++) {
+ if (test_event == selected_event) {
+ tt_assert(control_event_is_interesting(test_event));
+ } else {
+ tt_assert(!control_event_is_interesting(test_event));
+ }
+ }
+
+ /* Check that nothing is interesting outside EVENT_MIN_ to EVENT_MAX_
+ * This will break if control_event_is_interesting checks its arguments */
+ for (test_event = 0; test_event < EVENT_MIN_; test_event++)
+ tt_assert(!control_event_is_interesting(test_event));
+ for (test_event = EVENT_MAX_ + 1;
+ test_event < EVENT_CAPACITY_;
+ test_event++)
+ tt_assert(!control_event_is_interesting(test_event));
+ }
+
+ /* Check that only that event is not-interesting
+ * when a single event is un-set */
+ for (selected_event = EVENT_MIN_;
+ selected_event <= EVENT_MAX_;
+ selected_event++) {
+ control_testing_set_global_event_mask(
+ EVENT_MASK_ALL_
+ & ~(EVENT_MASK_(selected_event))
+ );
+
+ /* Check that only this event is not-interesting
+ * between EVENT_MIN_ and EVENT_MAX_ */
+ for (test_event = EVENT_MIN_; test_event <= EVENT_MAX_; test_event++) {
+ if (test_event == selected_event) {
+ tt_assert(!control_event_is_interesting(test_event));
+ } else {
+ tt_assert(control_event_is_interesting(test_event));
+ }
+ }
+
+ /* Check that nothing is interesting outside EVENT_MIN_ to EVENT_MAX_
+ * This will break if control_event_is_interesting checks its arguments */
+ for (test_event = 0; test_event < EVENT_MIN_; test_event++)
+ tt_assert(!control_event_is_interesting(test_event));
+ for (test_event = EVENT_MAX_ + 1;
+ test_event < EVENT_CAPACITY_;
+ test_event++)
+ tt_assert(!control_event_is_interesting(test_event));
+ }
+
+ done:
+ ;
+}
+
#define TEST(name, flags) \
{ #name, test_cntev_ ## name, flags, 0, NULL }
@@ -302,6 +400,7 @@ struct testcase_t controller_event_tests[] = {
TEST(sum_up_cell_stats, 0),
TEST(append_cell_stats, 0),
TEST(format_cell_stats, 0),
+ TEST(event_mask, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index e9fb8bf084..6cba850f30 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -14,6 +14,8 @@
#include "crypto_ed25519.h"
#include "ed25519_vectors.inc"
+#include <openssl/evp.h>
+
extern const char AUTHORITY_SIGNKEY_3[];
extern const char AUTHORITY_SIGNKEY_A_DIGEST[];
extern const char AUTHORITY_SIGNKEY_A_DIGEST256[];
@@ -72,7 +74,7 @@ test_crypto_rng(void *arg)
/* Try out RNG. */
(void)arg;
- tt_assert(! crypto_seed_rng(0));
+ tt_assert(! crypto_seed_rng());
crypto_rand(data1, 100);
crypto_rand(data2, 100);
tt_mem_op(data1,OP_NE, data2,100);
@@ -105,6 +107,30 @@ test_crypto_rng(void *arg)
;
}
+static void
+test_crypto_rng_range(void *arg)
+{
+ int got_smallest = 0, got_largest = 0;
+ int i;
+
+ (void)arg;
+ for (i = 0; i < 1000; ++i) {
+ int x = crypto_rand_int_range(5,9);
+ tt_int_op(x, OP_GE, 5);
+ tt_int_op(x, OP_LT, 9);
+ if (x == 5)
+ got_smallest = 1;
+ if (x == 8)
+ got_largest = 1;
+ }
+
+ /* These fail with probability 1/10^603. */
+ tt_assert(got_smallest);
+ tt_assert(got_largest);
+ done:
+ ;
+}
+
/** Run unit tests for our AES functionality */
static void
test_crypto_aes(void *arg)
@@ -571,6 +597,42 @@ test_crypto_pk_fingerprints(void *arg)
tor_free(mem_op_hex_tmp);
}
+static void
+test_crypto_pk_base64(void *arg)
+{
+ crypto_pk_t *pk1 = NULL;
+ crypto_pk_t *pk2 = NULL;
+ char *encoded = NULL;
+
+ (void)arg;
+
+ /* Test Base64 encoding a key. */
+ pk1 = pk_generate(0);
+ tt_assert(pk1);
+ tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk1, &encoded));
+ tt_assert(encoded);
+
+ /* Test decoding a valid key. */
+ pk2 = crypto_pk_base64_decode(encoded, strlen(encoded));
+ tt_assert(pk2);
+ tt_assert(crypto_pk_cmp_keys(pk1,pk2) == 0);
+ crypto_pk_free(pk2);
+
+ /* Test decoding a invalid key (not Base64). */
+ static const char *invalid_b64 = "The key is in another castle!";
+ pk2 = crypto_pk_base64_decode(invalid_b64, strlen(invalid_b64));
+ tt_assert(!pk2);
+
+ /* Test decoding a truncated Base64 blob. */
+ pk2 = crypto_pk_base64_decode(encoded, strlen(encoded)/2);
+ tt_assert(!pk2);
+
+ done:
+ crypto_pk_free(pk1);
+ crypto_pk_free(pk2);
+ tor_free(encoded);
+}
+
/** Sanity check for crypto pk digests */
static void
test_crypto_digests(void *arg)
@@ -601,6 +663,22 @@ test_crypto_digests(void *arg)
crypto_pk_free(k);
}
+/** Encode src into dest with OpenSSL's EVP Encode interface, returning the
+ * length of the encoded data in bytes.
+ */
+static int
+base64_encode_evp(char *dest, char *src, size_t srclen)
+{
+ const unsigned char *s = (unsigned char*)src;
+ EVP_ENCODE_CTX ctx;
+ int len, ret;
+
+ EVP_EncodeInit(&ctx);
+ EVP_EncodeUpdate(&ctx, (unsigned char *)dest, &len, s, (int)srclen);
+ EVP_EncodeFinal(&ctx, (unsigned char *)(dest + len), &ret);
+ return ret+ len;
+}
+
/** Run unit tests for misc crypto formatting functionality (base64, base32,
* fingerprints, etc) */
static void
@@ -618,17 +696,26 @@ test_crypto_formats(void *arg)
/* Base64 tests */
memset(data1, 6, 1024);
for (idx = 0; idx < 10; ++idx) {
- i = base64_encode(data2, 1024, data1, idx);
+ i = base64_encode(data2, 1024, data1, idx, 0);
tt_int_op(i, OP_GE, 0);
+ tt_int_op(i, OP_EQ, strlen(data2));
j = base64_decode(data3, 1024, data2, i);
tt_int_op(j,OP_EQ, idx);
tt_mem_op(data3,OP_EQ, data1, idx);
+
+ i = base64_encode_nopad(data2, 1024, (uint8_t*)data1, idx);
+ tt_int_op(i, OP_GE, 0);
+ tt_int_op(i, OP_EQ, strlen(data2));
+ tt_assert(! strchr(data2, '='));
+ j = base64_decode_nopad((uint8_t*)data3, 1024, data2, i);
+ tt_int_op(j, OP_EQ, idx);
+ tt_mem_op(data3,OP_EQ, data1, idx);
}
strlcpy(data1, "Test string that contains 35 chars.", 1024);
strlcat(data1, " 2nd string that contains 35 chars.", 1024);
- i = base64_encode(data2, 1024, data1, 71);
+ i = base64_encode(data2, 1024, data1, 71, 0);
tt_int_op(i, OP_GE, 0);
j = base64_decode(data3, 1024, data2, i);
tt_int_op(j,OP_EQ, 71);
@@ -647,6 +734,20 @@ test_crypto_formats(void *arg)
tt_assert(digest_from_base64(data3, "###") < 0);
+ for (i = 0; i < 256; i++) {
+ /* Test the multiline format Base64 encoder with 0 .. 256 bytes of
+ * output against OpenSSL.
+ */
+ const size_t enclen = base64_encode_size(i, BASE64_ENCODE_MULTILINE);
+ data1[i] = i;
+ j = base64_encode(data2, 1024, data1, i, BASE64_ENCODE_MULTILINE);
+ tt_int_op(j, OP_EQ, enclen);
+ j = base64_encode_evp(data3, data1, i);
+ tt_int_op(j, OP_EQ, enclen);
+ tt_mem_op(data2, OP_EQ, data3, enclen);
+ tt_int_op(j, OP_EQ, strlen(data2));
+ }
+
/* Encoding SHA256 */
crypto_rand(data2, DIGEST256_LEN);
memset(data2, 100, 1024);
@@ -1172,6 +1273,8 @@ test_crypto_ed25519_simple(void *arg)
tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pub2, &sec1));
tt_mem_op(pub1.pubkey, OP_EQ, pub2.pubkey, sizeof(pub1.pubkey));
+ tt_assert(ed25519_pubkey_eq(&pub1, &pub2));
+ tt_assert(ed25519_pubkey_eq(&pub1, &pub1));
memcpy(&kp1.pubkey, &pub1, sizeof(pub1));
memcpy(&kp1.seckey, &sec1, sizeof(sec1));
@@ -1191,6 +1294,7 @@ test_crypto_ed25519_simple(void *arg)
/* Wrong public key doesn't work. */
tt_int_op(0, OP_EQ, ed25519_public_key_generate(&pub2, &sec2));
tt_int_op(-1, OP_EQ, ed25519_checksig(&sig2, msg, msg_len, &pub2));
+ tt_assert(! ed25519_pubkey_eq(&pub1, &pub2));
/* Wrong message doesn't work. */
tt_int_op(0, OP_EQ, ed25519_checksig(&sig2, msg, msg_len, &pub1));
@@ -1329,9 +1433,10 @@ test_crypto_ed25519_test_vectors(void *arg)
static void
test_crypto_ed25519_encode(void *arg)
{
- char buf[ED25519_BASE64_LEN+1];
+ char buf[ED25519_SIG_BASE64_LEN+1];
ed25519_keypair_t kp;
ed25519_public_key_t pk;
+ ed25519_signature_t sig1, sig2;
char *mem_op_hex_tmp = NULL;
(void) arg;
@@ -1342,6 +1447,11 @@ test_crypto_ed25519_encode(void *arg)
tt_int_op(0, OP_EQ, ed25519_public_from_base64(&pk, buf));
tt_mem_op(kp.pubkey.pubkey, OP_EQ, pk.pubkey, ED25519_PUBKEY_LEN);
+ tt_int_op(0, OP_EQ, ed25519_sign(&sig1, (const uint8_t*)"ABC", 3, &kp));
+ tt_int_op(0, OP_EQ, ed25519_signature_to_base64(buf, &sig1));
+ tt_int_op(0, OP_EQ, ed25519_signature_from_base64(&sig2, buf));
+ tt_mem_op(sig1.sig, OP_EQ, sig2.sig, ED25519_SIG_LEN);
+
/* Test known value. */
tt_int_op(0, OP_EQ, ed25519_public_from_base64(&pk,
"lVIuIctLjbGZGU5wKMNXxXlSE3cW4kaqkqm04u6pxvM"));
@@ -1605,11 +1715,13 @@ test_crypto_siphash(void *arg)
struct testcase_t crypto_tests[] = {
CRYPTO_LEGACY(formats),
CRYPTO_LEGACY(rng),
+ { "rng_range", test_crypto_rng_range, 0, NULL, NULL },
{ "aes_AES", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"aes" },
{ "aes_EVP", test_crypto_aes, TT_FORK, &passthrough_setup, (void*)"evp" },
CRYPTO_LEGACY(sha),
CRYPTO_LEGACY(pk),
{ "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL },
+ { "pk_base64", test_crypto_pk_base64, TT_FORK, NULL, NULL },
CRYPTO_LEGACY(digests),
CRYPTO_LEGACY(dh),
{ "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup,
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index a949f5de73..35bd8ea166 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -14,15 +14,18 @@
#define NETWORKSTATUS_PRIVATE
#include "or.h"
#include "config.h"
+#include "crypto_ed25519.h"
#include "directory.h"
#include "dirserv.h"
#include "dirvote.h"
#include "hibernate.h"
#include "networkstatus.h"
#include "router.h"
+#include "routerkeys.h"
#include "routerlist.h"
#include "routerparse.h"
#include "test.h"
+#include "torcert.h"
static void
test_dir_nicknames(void *arg)
@@ -87,8 +90,10 @@ test_dir_formats(void *arg)
routerinfo_t *rp1 = NULL, *rp2 = NULL;
addr_policy_t *ex1, *ex2;
routerlist_t *dir1 = NULL, *dir2 = NULL;
+ uint8_t *rsa_cc = NULL;
or_options_t *options = get_options_mutable();
const addr_policy_t *p;
+ time_t now = time(NULL);
(void)arg;
pk1 = pk_generate(0);
@@ -127,14 +132,32 @@ test_dir_formats(void *arg)
ex2->prt_min = ex2->prt_max = 24;
r2 = tor_malloc_zero(sizeof(routerinfo_t));
r2->addr = 0x0a030201u; /* 10.3.2.1 */
+ ed25519_keypair_t kp1, kp2;
+ ed25519_secret_key_from_seed(&kp1.seckey,
+ (const uint8_t*)"YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY");
+ ed25519_public_key_generate(&kp1.pubkey, &kp1.seckey);
+ ed25519_secret_key_from_seed(&kp2.seckey,
+ (const uint8_t*)"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
+ ed25519_public_key_generate(&kp2.pubkey, &kp2.seckey);
+ r2->signing_key_cert = tor_cert_create(&kp1,
+ CERT_TYPE_ID_SIGNING,
+ &kp2.pubkey,
+ now, 86400,
+ CERT_FLAG_INCLUDE_SIGNING_KEY);
+ char cert_buf[256];
+ base64_encode(cert_buf, sizeof(cert_buf),
+ (const char*)r2->signing_key_cert->encoded,
+ r2->signing_key_cert->encoded_len,
+ BASE64_ENCODE_MULTILINE);
r2->platform = tor_strdup(platform);
r2->cache_info.published_on = 5;
r2->or_port = 9005;
r2->dir_port = 0;
r2->onion_pkey = crypto_pk_dup_key(pk2);
- r2->onion_curve25519_pkey = tor_malloc_zero(sizeof(curve25519_public_key_t));
- curve25519_public_from_base64(r2->onion_curve25519_pkey,
- "skyinAnvardNostarsNomoonNowindormistsorsnow");
+ curve25519_keypair_t r2_onion_keypair;
+ curve25519_keypair_generate(&r2_onion_keypair, 0);
+ r2->onion_curve25519_pkey = tor_memdup(&r2_onion_keypair.pubkey,
+ sizeof(curve25519_public_key_t));
r2->identity_pkey = crypto_pk_dup_key(pk1);
r2->bandwidthrate = r2->bandwidthburst = r2->bandwidthcapacity = 3000;
r2->exit_policy = smartlist_new();
@@ -150,7 +173,7 @@ test_dir_formats(void *arg)
/* XXXX025 router_dump_to_string should really take this from ri.*/
options->ContactInfo = tor_strdup("Magri White "
"<magri@elsewhere.example.com>");
- buf = router_dump_router_to_string(r1, pk2);
+ buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL);
tor_free(options->ContactInfo);
tt_assert(buf);
@@ -183,7 +206,7 @@ test_dir_formats(void *arg)
tt_str_op(buf,OP_EQ, buf2);
tor_free(buf);
- buf = router_dump_router_to_string(r1, pk2);
+ buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL);
tt_assert(buf);
cp = buf;
rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
@@ -201,7 +224,19 @@ test_dir_formats(void *arg)
strlcpy(buf2,
"router Fred 10.3.2.1 9005 0 0\n"
- "platform Tor "VERSION" on ", sizeof(buf2));
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n", sizeof(buf2));
+ strlcat(buf2, cert_buf, sizeof(buf2));
+ strlcat(buf2, "-----END ED25519 CERT-----\n", sizeof(buf2));
+ strlcat(buf2, "master-key-ed25519 ", sizeof(buf2));
+ {
+ char k[ED25519_BASE64_LEN+1];
+ tt_assert(ed25519_public_to_base64(k, &r2->signing_key_cert->signing_key)
+ >= 0);
+ strlcat(buf2, k, sizeof(buf2));
+ strlcat(buf2, "\n", sizeof(buf2));
+ }
+ strlcat(buf2, "platform Tor "VERSION" on ", sizeof(buf2));
strlcat(buf2, get_uname(), sizeof(buf2));
strlcat(buf2, "\n"
"protocols Link 1 2 Circuit 1\n"
@@ -215,19 +250,56 @@ test_dir_formats(void *arg)
strlcat(buf2, pk2_str, sizeof(buf2));
strlcat(buf2, "signing-key\n", sizeof(buf2));
strlcat(buf2, pk1_str, sizeof(buf2));
+ int rsa_cc_len;
+ rsa_cc = make_tap_onion_key_crosscert(pk2,
+ &kp1.pubkey,
+ pk1,
+ &rsa_cc_len);
+ tt_assert(rsa_cc);
+ base64_encode(cert_buf, sizeof(cert_buf), (char*)rsa_cc, rsa_cc_len,
+ BASE64_ENCODE_MULTILINE);
+ strlcat(buf2, "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n", sizeof(buf2));
+ strlcat(buf2, cert_buf, sizeof(buf2));
+ strlcat(buf2, "-----END CROSSCERT-----\n", sizeof(buf2));
+ int ntor_cc_sign;
+ {
+ tor_cert_t *ntor_cc = NULL;
+ ntor_cc = make_ntor_onion_key_crosscert(&r2_onion_keypair,
+ &kp1.pubkey,
+ r2->cache_info.published_on,
+ MIN_ONION_KEY_LIFETIME,
+ &ntor_cc_sign);
+ tt_assert(ntor_cc);
+ base64_encode(cert_buf, sizeof(cert_buf),
+ (char*)ntor_cc->encoded, ntor_cc->encoded_len,
+ BASE64_ENCODE_MULTILINE);
+ tor_cert_free(ntor_cc);
+ }
+ tor_snprintf(buf2+strlen(buf2), sizeof(buf2)-strlen(buf2),
+ "ntor-onion-key-crosscert %d\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "%s"
+ "-----END ED25519 CERT-----\n", ntor_cc_sign, cert_buf);
+
strlcat(buf2, "hidden-service-dir\n", sizeof(buf2));
- strlcat(buf2, "ntor-onion-key "
- "skyinAnvardNostarsNomoonNowindormistsorsnow=\n", sizeof(buf2));
+ strlcat(buf2, "ntor-onion-key ", sizeof(buf2));
+ base64_encode(cert_buf, sizeof(cert_buf),
+ (const char*)r2_onion_keypair.pubkey.public_key, 32,
+ BASE64_ENCODE_MULTILINE);
+ strlcat(buf2, cert_buf, sizeof(buf2));
strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2));
- strlcat(buf2, "router-signature\n", sizeof(buf2));
+ strlcat(buf2, "router-sig-ed25519 ", sizeof(buf2));
- buf = router_dump_router_to_string(r2, pk1);
+ buf = router_dump_router_to_string(r2, pk1, pk2, &r2_onion_keypair, &kp2);
+ tt_assert(buf);
buf[strlen(buf2)] = '\0'; /* Don't compare the sig; it's never the same
* twice */
- tt_str_op(buf,OP_EQ, buf2);
+
+ tt_str_op(buf, OP_EQ, buf2);
tor_free(buf);
- buf = router_dump_router_to_string(r2, pk1);
+ buf = router_dump_router_to_string(r2, pk1, NULL, NULL, NULL);
cp = buf;
rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL);
tt_assert(rp2);
@@ -280,6 +352,7 @@ test_dir_formats(void *arg)
if (rp2)
routerinfo_free(rp2);
+ tor_free(rsa_cc);
tor_free(buf);
tor_free(pk1_str);
tor_free(pk2_str);
@@ -293,7 +366,7 @@ test_dir_formats(void *arg)
#include "failing_routerdescs.inc"
static void
-test_dir_routerparse_bad(void *arg)
+test_dir_routerinfo_parsing(void *arg)
{
(void) arg;
@@ -318,6 +391,8 @@ test_dir_routerparse_bad(void *arg)
CHECK_OK(EX_RI_MINIMAL);
CHECK_OK(EX_RI_MAXIMAL);
+ CHECK_OK(EX_RI_MINIMAL_ED);
+
/* good annotations prepended */
routerinfo_free(ri);
ri = router_parse_entry_from_string(EX_RI_MINIMAL, NULL, 0, 0,
@@ -376,8 +451,28 @@ test_dir_routerparse_bad(void *arg)
CHECK_FAIL(EX_RI_BAD_FAMILY, 0);
CHECK_FAIL(EX_RI_ZERO_ORPORT, 0);
+ CHECK_FAIL(EX_RI_ED_MISSING_CROSSCERT, 0);
+ CHECK_FAIL(EX_RI_ED_MISSING_CROSSCERT2, 0);
+ CHECK_FAIL(EX_RI_ED_MISSING_CROSSCERT_SIGN, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_SIG1, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_SIG2, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_SIG3, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_SIG4, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT1, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT3, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT4, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT5, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT6, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_CROSSCERT7, 0);
+ CHECK_FAIL(EX_RI_ED_MISPLACED1, 0);
+ CHECK_FAIL(EX_RI_ED_MISPLACED2, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_CERT1, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_CERT2, 0);
+ CHECK_FAIL(EX_RI_ED_BAD_CERT3, 0);
+
/* This is allowed; we just ignore it. */
CHECK_OK(EX_RI_BAD_EI_DIGEST);
+ CHECK_OK(EX_RI_BAD_EI_DIGEST2);
#undef CHECK_FAIL
#undef CHECK_OK
@@ -433,20 +528,34 @@ test_dir_extrainfo_parsing(void *arg)
tt_assert(ei->pending_sig);
CHECK_OK(EX_EI_MAXIMAL);
tt_assert(ei->pending_sig);
+ CHECK_OK(EX_EI_GOOD_ED_EI);
+ tt_assert(ei->pending_sig);
map = (struct digest_ri_map_t *)digestmap_new();
ADD(EX_EI_MINIMAL);
ADD(EX_EI_MAXIMAL);
+ ADD(EX_EI_GOOD_ED_EI);
ADD(EX_EI_BAD_FP);
ADD(EX_EI_BAD_NICKNAME);
ADD(EX_EI_BAD_TOKENS);
ADD(EX_EI_BAD_START);
ADD(EX_EI_BAD_PUBLISHED);
+ ADD(EX_EI_ED_MISSING_SIG);
+ ADD(EX_EI_ED_MISSING_CERT);
+ ADD(EX_EI_ED_BAD_CERT1);
+ ADD(EX_EI_ED_BAD_CERT2);
+ ADD(EX_EI_ED_BAD_SIG1);
+ ADD(EX_EI_ED_BAD_SIG2);
+ ADD(EX_EI_ED_MISPLACED_CERT);
+ ADD(EX_EI_ED_MISPLACED_SIG);
+
CHECK_OK(EX_EI_MINIMAL);
tt_assert(!ei->pending_sig);
CHECK_OK(EX_EI_MAXIMAL);
tt_assert(!ei->pending_sig);
+ CHECK_OK(EX_EI_GOOD_ED_EI);
+ tt_assert(!ei->pending_sig);
CHECK_FAIL(EX_EI_BAD_SIG1,1);
CHECK_FAIL(EX_EI_BAD_SIG2,1);
@@ -457,6 +566,15 @@ test_dir_extrainfo_parsing(void *arg)
CHECK_FAIL(EX_EI_BAD_START,0);
CHECK_FAIL(EX_EI_BAD_PUBLISHED,0);
+ CHECK_FAIL(EX_EI_ED_MISSING_SIG,0);
+ CHECK_FAIL(EX_EI_ED_MISSING_CERT,0);
+ CHECK_FAIL(EX_EI_ED_BAD_CERT1,0);
+ CHECK_FAIL(EX_EI_ED_BAD_CERT2,0);
+ CHECK_FAIL(EX_EI_ED_BAD_SIG1,0);
+ CHECK_FAIL(EX_EI_ED_BAD_SIG2,0);
+ CHECK_FAIL(EX_EI_ED_MISPLACED_CERT,0);
+ CHECK_FAIL(EX_EI_ED_MISPLACED_SIG,0);
+
#undef CHECK_OK
#undef CHECK_FAIL
@@ -1394,6 +1512,7 @@ generate_ri_from_rs(const vote_routerstatus_t *vrs)
static time_t published = 0;
r = tor_malloc_zero(sizeof(routerinfo_t));
+ r->cert_expiration_time = TIME_MAX;
memcpy(r->cache_info.identity_digest, rs->identity_digest, DIGEST_LEN);
memcpy(r->cache_info.signed_descriptor_digest, rs->descriptor_digest,
DIGEST_LEN);
@@ -3108,7 +3227,7 @@ test_dir_packages(void *arg)
struct testcase_t dir_tests[] = {
DIR_LEGACY(nicknames),
DIR_LEGACY(formats),
- DIR(routerparse_bad, 0),
+ DIR(routerinfo_parsing, 0),
DIR(extrainfo_parsing, 0),
DIR(parse_router_list, TT_FORK),
DIR(load_routers, TT_FORK),
diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c
index 17cb9d9329..0011d3698a 100644
--- a/src/test/test_entrynodes.c
+++ b/src/test/test_entrynodes.c
@@ -162,9 +162,6 @@ populate_live_entry_guards_test_helper(int num_needed)
False, all guards should have made_contact enabled. */
tt_int_op(entry->made_contact, OP_EQ, 1);
- /* Since we don't have a routerstatus, all of the entry guards are
- not directory servers. */
- tt_int_op(entry->is_dir_cache, OP_EQ, 0);
} SMARTLIST_FOREACH_END(entry);
/* First, try to get some fast guards. This should fail. */
diff --git a/src/test/test_hs.c b/src/test/test_hs.c
index 67ef646aab..6d01798a63 100644
--- a/src/test/test_hs.c
+++ b/src/test/test_hs.c
@@ -13,6 +13,7 @@
#include "test.h"
#include "control.h"
#include "config.h"
+#include "rendcommon.h"
#include "routerset.h"
#include "circuitbuild.h"
#include "test_helpers.h"
@@ -28,6 +29,68 @@
#define STR_HSDIR_NONE_EXIST_LONGNAME \
"$BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+/* DuckDuckGo descriptor as an example. */
+static const char *hs_desc_content = "\
+rendezvous-service-descriptor g5ojobzupf275beh5ra72uyhb3dkpxwg\r\n\
+version 2\r\n\
+permanent-key\r\n\
+-----BEGIN RSA PUBLIC KEY-----\r\n\
+MIGJAoGBAJ/SzzgrXPxTlFrKVhXh3buCWv2QfcNgncUpDpKouLn3AtPH5Ocys0jE\r\n\
+aZSKdvaiQ62md2gOwj4x61cFNdi05tdQjS+2thHKEm/KsB9BGLSLBNJYY356bupg\r\n\
+I5gQozM65ENelfxYlysBjJ52xSDBd8C4f/p9umdzaaaCmzXG/nhzAgMBAAE=\r\n\
+-----END RSA PUBLIC KEY-----\r\n\
+secret-id-part anmjoxxwiupreyajjt5yasimfmwcnxlf\r\n\
+publication-time 2015-03-11 19:00:00\r\n\
+protocol-versions 2,3\r\n\
+introduction-points\r\n\
+-----BEGIN MESSAGE-----\r\n\
+aW50cm9kdWN0aW9uLXBvaW50IDd1bnd4cmg2dG5kNGh6eWt1Z3EzaGZzdHduc2ll\r\n\
+cmhyCmlwLWFkZHJlc3MgMTg4LjEzOC4xMjEuMTE4Cm9uaW9uLXBvcnQgOTAwMQpv\r\n\
+bmlvbi1rZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dC\r\n\
+QUxGRVVyeVpDbk9ROEhURmV5cDVjMTRObWVqL1BhekFLTTBxRENTNElKUWh0Y3g1\r\n\
+NXpRSFdOVWIKQ2hHZ0JqR1RjV3ZGRnA0N3FkdGF6WUZhVXE2c0lQKzVqeWZ5b0Q4\r\n\
+UmJ1bzBwQmFWclJjMmNhYUptWWM0RDh6Vgpuby9sZnhzOVVaQnZ1cWY4eHIrMDB2\r\n\
+S0JJNmFSMlA2OE1WeDhrMExqcUpUU2RKOE9idm9yQWdNQkFBRT0KLS0tLS1FTkQg\r\n\
+UlNBIFBVQkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQ\r\n\
+VUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTnJHb0ozeTlHNXQzN2F2ekI1cTlwN1hG\r\n\
+VUplRUVYMUNOaExnWmJXWGJhVk5OcXpoZFhyL0xTUQppM1Z6dW5OaUs3cndUVnE2\r\n\
+K2QyZ1lRckhMMmIvMXBBY3ZKWjJiNSs0bTRRc0NibFpjRENXTktRbHJnRWN5WXRJ\r\n\
+CkdscXJTbFFEaXA0ZnNrUFMvNDVkWTI0QmJsQ3NGU1k3RzVLVkxJck4zZFpGbmJr\r\n\
+NEZIS1hBZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJv\r\n\
+ZHVjdGlvbi1wb2ludCBiNGM3enlxNXNheGZzN2prNXFibG1wN3I1b3pwdHRvagpp\r\n\
+cC1hZGRyZXNzIDEwOS4xNjkuNDUuMjI2Cm9uaW9uLXBvcnQgOTAwMQpvbmlvbi1r\r\n\
+ZXkKLS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JR0pBb0dCQU8xSXpw\r\n\
+WFFUTUY3RXZUb1NEUXpzVnZiRVFRQUQrcGZ6NzczMVRXZzVaUEJZY1EyUkRaeVp4\r\n\
+OEQKNUVQSU1FeUE1RE83cGd0ak5LaXJvYXJGMC8yempjMkRXTUlSaXZyU29YUWVZ\r\n\
+ZXlMM1pzKzFIajJhMDlCdkYxZAp6MEswblRFdVhoNVR5V3lyMHdsbGI1SFBnTlI0\r\n\
+MS9oYkprZzkwZitPVCtIeGhKL1duUml2QWdNQkFBRT0KLS0tLS1FTkQgUlNBIFBV\r\n\
+QkxJQyBLRVktLS0tLQpzZXJ2aWNlLWtleQotLS0tLUJFR0lOIFJTQSBQVUJMSUMg\r\n\
+S0VZLS0tLS0KTUlHSkFvR0JBSzNWZEJ2ajFtQllLL3JrcHNwcm9Ub0llNUtHVmth\r\n\
+QkxvMW1tK1I2YUVJek1VZFE1SjkwNGtyRwpCd3k5NC8rV0lGNFpGYXh5Z2phejl1\r\n\
+N2pKY1k3ZGJhd1pFeG1hYXFCRlRwL2h2ZG9rcHQ4a1ByRVk4OTJPRHJ1CmJORUox\r\n\
+N1FPSmVMTVZZZk5Kcjl4TWZCQ3JQai8zOGh2RUdrbWVRNmRVWElvbVFNaUJGOVRB\r\n\
+Z01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0tLS0tCmludHJvZHVjdGlv\r\n\
+bi1wb2ludCBhdjVtcWl0Y2Q3cjJkandsYmN0c2Jlc2R3eGt0ZWtvegppcC1hZGRy\r\n\
+ZXNzIDE0NC43Ni44LjczCm9uaW9uLXBvcnQgNDQzCm9uaW9uLWtleQotLS0tLUJF\r\n\
+R0lOIFJTQSBQVUJMSUMgS0VZLS0tLS0KTUlHSkFvR0JBTzVweVZzQmpZQmNmMXBE\r\n\
+dklHUlpmWXUzQ05nNldka0ZLMGlvdTBXTGZtejZRVDN0NWhzd3cyVwpjejlHMXhx\r\n\
+MmN0Nkd6VWkrNnVkTDlITTRVOUdHTi9BbW8wRG9GV1hKWHpBQkFXd2YyMVdsd1lW\r\n\
+eFJQMHRydi9WCkN6UDkzcHc5OG5vSmdGUGRUZ05iMjdKYmVUZENLVFBrTEtscXFt\r\n\
+b3NveUN2RitRa25vUS9BZ01CQUFFPQotLS0tLUVORCBSU0EgUFVCTElDIEtFWS0t\r\n\
+LS0tCnNlcnZpY2Uta2V5Ci0tLS0tQkVHSU4gUlNBIFBVQkxJQyBLRVktLS0tLQpN\r\n\
+SUdKQW9HQkFMVjNKSmtWN3lTNU9jc1lHMHNFYzFQOTVRclFRR3ZzbGJ6Wi9zRGxl\r\n\
+RlpKYXFSOUYvYjRUVERNClNGcFMxcU1GbldkZDgxVmRGMEdYRmN2WVpLamRJdHU2\r\n\
+SndBaTRJeEhxeXZtdTRKdUxrcXNaTEFLaXRLVkx4eGsKeERlMjlDNzRWMmJrOTRJ\r\n\
+MEgybTNKS2tzTHVwc3VxWWRVUmhOVXN0SElKZmgyZmNIalF0bEFnTUJBQUU9Ci0t\r\n\
+LS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0KCg==\r\n\
+-----END MESSAGE-----\r\n\
+signature\r\n\
+-----BEGIN SIGNATURE-----\r\n\
+d4OuCE5OLAOnRB6cQN6WyMEmg/BHem144Vec+eYgeWoKwx3MxXFplUjFxgnMlmwN\r\n\
+PcftsZf2ztN0sbNCtPgDL3d0PqvxY3iHTQAI8EbaGq/IAJUZ8U4y963dD5+Bn6JQ\r\n\
+myE3ctmh0vy5+QxSiRjmQBkuEpCyks7LvWvHYrhnmcg=\r\n\
+-----END SIGNATURE-----";
+
/* Helper global variable for hidden service descriptor event test.
* It's used as a pointer to dynamically created message buffer in
* send_control_event_string_replacement function, which mocks
@@ -69,10 +132,13 @@ static void
test_hs_desc_event(void *arg)
{
#define STR_HS_ADDR "ajhb7kljbiru65qo"
- #define STR_HS_ID "b3oeducbhjmbqmgw2i3jtz4fekkrinwj"
+ #define STR_HS_CONTENT_DESC_ID "g5ojobzupf275beh5ra72uyhb3dkpxwg"
+ #define STR_DESC_ID_BASE32 "hba3gmcgpfivzfhx5rtfqkfdhv65yrj3"
+ int ret;
rend_data_t rend_query;
const char *expected_msg;
+ char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1];
(void) arg;
MOCK(send_control_event_string,
@@ -81,31 +147,48 @@ test_hs_desc_event(void *arg)
node_describe_longname_by_id_replacement);
/* setup rend_query struct */
+ memset(&rend_query, 0, sizeof(rend_query));
strncpy(rend_query.onion_address, STR_HS_ADDR,
REND_SERVICE_ID_LEN_BASE32+1);
- rend_query.auth_type = 0;
+ rend_query.auth_type = REND_NO_AUTH;
+ rend_query.hsdirs_fp = smartlist_new();
+ smartlist_add(rend_query.hsdirs_fp, tor_memdup(HSDIR_EXIST_ID,
+ DIGEST_LEN));
+
+ /* Compute descriptor ID for replica 0, should be STR_DESC_ID_BASE32. */
+ ret = rend_compute_v2_desc_id(rend_query.descriptor_id[0],
+ rend_query.onion_address,
+ NULL, 0, 0);
+ tt_int_op(ret, ==, 0);
+ base32_encode(desc_id_base32, sizeof(desc_id_base32),
+ rend_query.descriptor_id[0], DIGEST_LEN);
+ /* Make sure rend_compute_v2_desc_id works properly. */
+ tt_mem_op(desc_id_base32, OP_EQ, STR_DESC_ID_BASE32,
+ sizeof(desc_id_base32));
/* test request event */
control_event_hs_descriptor_requested(&rend_query, HSDIR_EXIST_ID,
- STR_HS_ID);
+ STR_DESC_ID_BASE32);
expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\
- STR_HSDIR_EXIST_LONGNAME" "STR_HS_ID"\r\n";
+ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n";
tt_assert(received_msg);
tt_str_op(received_msg,OP_EQ, expected_msg);
tor_free(received_msg);
/* test received event */
- rend_query.auth_type = 1;
- control_event_hs_descriptor_received(&rend_query, HSDIR_EXIST_ID);
+ rend_query.auth_type = REND_BASIC_AUTH;
+ control_event_hs_descriptor_received(rend_query.onion_address,
+ &rend_query, HSDIR_EXIST_ID);
expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\
- STR_HSDIR_EXIST_LONGNAME"\r\n";
+ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n";
tt_assert(received_msg);
tt_str_op(received_msg,OP_EQ, expected_msg);
tor_free(received_msg);
/* test failed event */
- rend_query.auth_type = 2;
- control_event_hs_descriptor_failed(&rend_query, HSDIR_NONE_EXIST_ID,
+ rend_query.auth_type = REND_STEALTH_AUTH;
+ control_event_hs_descriptor_failed(&rend_query,
+ HSDIR_NONE_EXIST_ID,
"QUERY_REJECTED");
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\
STR_HSDIR_NONE_EXIST_LONGNAME" REASON=QUERY_REJECTED\r\n";
@@ -115,14 +198,32 @@ test_hs_desc_event(void *arg)
/* test invalid auth type */
rend_query.auth_type = 999;
- control_event_hs_descriptor_failed(&rend_query, HSDIR_EXIST_ID,
+ control_event_hs_descriptor_failed(&rend_query,
+ HSDIR_EXIST_ID,
"QUERY_REJECTED");
expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\
- STR_HSDIR_EXIST_LONGNAME" REASON=QUERY_REJECTED\r\n";
+ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32\
+ " REASON=QUERY_REJECTED\r\n";
tt_assert(received_msg);
tt_str_op(received_msg,OP_EQ, expected_msg);
tor_free(received_msg);
+ /* test valid content. */
+ char *exp_msg;
+ control_event_hs_descriptor_content(rend_query.onion_address,
+ STR_HS_CONTENT_DESC_ID, HSDIR_EXIST_ID,
+ hs_desc_content);
+ tor_asprintf(&exp_msg, "650+HS_DESC_CONTENT " STR_HS_ADDR " "\
+ STR_HS_CONTENT_DESC_ID " " STR_HSDIR_EXIST_LONGNAME\
+ "\r\n%s\r\n.\r\n650 OK\r\n", hs_desc_content);
+
+ tt_assert(received_msg);
+ tt_str_op(received_msg, OP_EQ, exp_msg);
+ tor_free(received_msg);
+ tor_free(exp_msg);
+ SMARTLIST_FOREACH(rend_query.hsdirs_fp, char *, d, tor_free(d));
+ smartlist_free(rend_query.hsdirs_fp);
+
done:
UNMOCK(send_control_event_string);
UNMOCK(node_describe_longname_by_id);
@@ -198,7 +299,147 @@ test_pick_bad_tor2web_rendezvous_node(void *arg)
routerset_free(options->Tor2webRendezvousPoints);
}
+/* Make sure rend_data_t is valid at creation, destruction and when
+ * duplicated. */
+static void
+test_hs_rend_data(void *arg)
+{
+ int rep;
+ rend_data_t *client = NULL, *client_dup = NULL;
+ /* Binary format of a descriptor ID. */
+ char desc_id[DIGEST_LEN];
+ char client_cookie[REND_DESC_COOKIE_LEN];
+ time_t now = time(NULL);
+ rend_data_t *service_dup = NULL;
+ rend_data_t *service = NULL;
+
+ (void)arg;
+
+ base32_decode(desc_id, sizeof(desc_id), STR_DESC_ID_BASE32,
+ REND_DESC_ID_V2_LEN_BASE32);
+ memset(client_cookie, 'e', sizeof(client_cookie));
+
+ client = rend_data_client_create(STR_HS_ADDR, desc_id, client_cookie,
+ REND_NO_AUTH);
+ tt_assert(client);
+ tt_int_op(client->auth_type, ==, REND_NO_AUTH);
+ tt_str_op(client->onion_address, OP_EQ, STR_HS_ADDR);
+ tt_mem_op(client->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id));
+ tt_mem_op(client->descriptor_cookie, OP_EQ, client_cookie,
+ sizeof(client_cookie));
+ tt_assert(client->hsdirs_fp);
+ tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0);
+ for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
+ int ret = rend_compute_v2_desc_id(desc_id, client->onion_address,
+ client->descriptor_cookie, now, rep);
+ /* That shouldn't never fail. */
+ tt_int_op(ret, ==, 0);
+ tt_mem_op(client->descriptor_id[rep], OP_EQ, desc_id, sizeof(desc_id));
+ }
+ /* The rest should be zeroed because this is a client request. */
+ tt_int_op(tor_digest_is_zero(client->rend_pk_digest), ==, 1);
+ tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1);
+
+ /* Test dup(). */
+ client_dup = rend_data_dup(client);
+ tt_assert(client_dup);
+ tt_int_op(client_dup->auth_type, ==, client->auth_type);
+ tt_str_op(client_dup->onion_address, OP_EQ, client->onion_address);
+ tt_mem_op(client_dup->desc_id_fetch, OP_EQ, client->desc_id_fetch,
+ sizeof(client_dup->desc_id_fetch));
+ tt_mem_op(client_dup->descriptor_cookie, OP_EQ, client->descriptor_cookie,
+ sizeof(client_dup->descriptor_cookie));
+
+ tt_assert(client_dup->hsdirs_fp);
+ tt_int_op(smartlist_len(client_dup->hsdirs_fp), ==, 0);
+ for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
+ tt_mem_op(client_dup->descriptor_id[rep], OP_EQ,
+ client->descriptor_id[rep], DIGEST_LEN);
+ }
+ /* The rest should be zeroed because this is a client request. */
+ tt_int_op(tor_digest_is_zero(client_dup->rend_pk_digest), ==, 1);
+ tt_int_op(tor_digest_is_zero(client_dup->rend_cookie), ==, 1);
+ rend_data_free(client);
+ client = NULL;
+ rend_data_free(client_dup);
+ client_dup = NULL;
+
+ /* Reset state. */
+ base32_decode(desc_id, sizeof(desc_id), STR_DESC_ID_BASE32,
+ REND_DESC_ID_V2_LEN_BASE32);
+ memset(client_cookie, 'e', sizeof(client_cookie));
+
+ /* Try with different parameters here for which some content should be
+ * zeroed out. */
+ client = rend_data_client_create(NULL, desc_id, NULL, REND_BASIC_AUTH);
+ tt_assert(client);
+ tt_int_op(client->auth_type, ==, REND_BASIC_AUTH);
+ tt_int_op(strlen(client->onion_address), ==, 0);
+ tt_mem_op(client->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id));
+ tt_int_op(tor_mem_is_zero(client->descriptor_cookie,
+ sizeof(client->descriptor_cookie)), ==, 1);
+ tt_assert(client->hsdirs_fp);
+ tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0);
+ for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
+ tt_int_op(tor_digest_is_zero(client->descriptor_id[rep]), ==, 1);
+ }
+ /* The rest should be zeroed because this is a client request. */
+ tt_int_op(tor_digest_is_zero(client->rend_pk_digest), ==, 1);
+ tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1);
+ rend_data_free(client);
+ client = NULL;
+
+ /* Let's test the service object now. */
+ char rend_pk_digest[DIGEST_LEN];
+ uint8_t rend_cookie[DIGEST_LEN];
+ memset(rend_pk_digest, 'f', sizeof(rend_pk_digest));
+ memset(rend_cookie, 'g', sizeof(rend_cookie));
+
+ service = rend_data_service_create(STR_HS_ADDR, rend_pk_digest,
+ rend_cookie, REND_NO_AUTH);
+ tt_assert(service);
+ tt_int_op(service->auth_type, ==, REND_NO_AUTH);
+ tt_str_op(service->onion_address, OP_EQ, STR_HS_ADDR);
+ tt_mem_op(service->rend_pk_digest, OP_EQ, rend_pk_digest,
+ sizeof(rend_pk_digest));
+ tt_mem_op(service->rend_cookie, OP_EQ, rend_cookie, sizeof(rend_cookie));
+ tt_assert(service->hsdirs_fp);
+ tt_int_op(smartlist_len(service->hsdirs_fp), ==, 0);
+ for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
+ tt_int_op(tor_digest_is_zero(service->descriptor_id[rep]), ==, 1);
+ }
+ /* The rest should be zeroed because this is a service request. */
+ tt_int_op(tor_digest_is_zero(service->descriptor_cookie), ==, 1);
+ tt_int_op(tor_digest_is_zero(service->desc_id_fetch), ==, 1);
+
+ /* Test dup(). */
+ service_dup = rend_data_dup(service);
+ tt_assert(service_dup);
+ tt_int_op(service_dup->auth_type, ==, service->auth_type);
+ tt_str_op(service_dup->onion_address, OP_EQ, service->onion_address);
+ tt_mem_op(service_dup->rend_pk_digest, OP_EQ, service->rend_pk_digest,
+ sizeof(service_dup->rend_pk_digest));
+ tt_mem_op(service_dup->rend_cookie, OP_EQ, service->rend_cookie,
+ sizeof(service_dup->rend_cookie));
+ tt_assert(service_dup->hsdirs_fp);
+ tt_int_op(smartlist_len(service_dup->hsdirs_fp), ==, 0);
+ for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) {
+ tt_int_op(tor_digest_is_zero(service_dup->descriptor_id[rep]), ==, 1);
+ }
+ /* The rest should be zeroed because this is a service request. */
+ tt_int_op(tor_digest_is_zero(service_dup->descriptor_cookie), ==, 1);
+ tt_int_op(tor_digest_is_zero(service_dup->desc_id_fetch), ==, 1);
+
+ done:
+ rend_data_free(service);
+ rend_data_free(service_dup);
+ rend_data_free(client);
+ rend_data_free(client_dup);
+}
+
struct testcase_t hs_tests[] = {
+ { "hs_rend_data", test_hs_rend_data, TT_FORK,
+ NULL, NULL },
{ "hs_desc_event", test_hs_desc_event, TT_FORK,
NULL, NULL },
{ "pick_tor2web_rendezvous_node", test_pick_tor2web_rendezvous_node, TT_FORK,
diff --git a/src/test/test_keypin.c b/src/test/test_keypin.c
new file mode 100644
index 0000000000..afd4ca201d
--- /dev/null
+++ b/src/test/test_keypin.c
@@ -0,0 +1,255 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+#define KEYPIN_PRIVATE
+#include "or.h"
+#include "keypin.h"
+#include "util.h"
+
+#include "test.h"
+
+static void
+test_keypin_parse_line(void *arg)
+{
+ (void)arg;
+ keypin_ent_t *ent = NULL;
+
+ /* Good line */
+ ent = keypin_parse_journal_line(
+ "aGVyZSBpcyBhIGdvb2Qgc2hhMSE "
+ "VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4");
+ tt_assert(ent);
+ tt_mem_op(ent->rsa_id, ==, "here is a good sha1!", 20);
+ tt_mem_op(ent->ed25519_key, ==, "This ed25519 scoffs at the sha1.", 32);
+ tor_free(ent); ent = NULL;
+
+ /* Good line with extra stuff we will ignore. */
+ ent = keypin_parse_journal_line(
+ "aGVyZSBpcyBhIGdvb2Qgc2hhMSE "
+ "VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4helloworld");
+ tt_assert(ent);
+ tt_mem_op(ent->rsa_id, ==, "here is a good sha1!", 20);
+ tt_mem_op(ent->ed25519_key, ==, "This ed25519 scoffs at the sha1.", 32);
+ tor_free(ent); ent = NULL;
+
+ /* Bad line: no space in the middle. */
+ ent = keypin_parse_journal_line(
+ "aGVyZSBpcyBhIGdvb2Qgc2hhMSE?"
+ "VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4");
+ tt_assert(! ent);
+
+ /* Bad line: bad base64 in RSA ID */
+ ent = keypin_parse_journal_line(
+ "aGVyZSBpcyBhIGdv!2Qgc2hhMSE "
+ "VGhpcyBlZDI1NTE5IHNjb2ZmcyBhdCB0aGUgc2hhMS4");
+ tt_assert(! ent);
+
+ /* Bad line: bad base64 in Ed25519 */
+ ent = keypin_parse_journal_line(
+ "aGVyZSBpcyBhIGdvb2Qgc2hhMSE "
+ "VGhpcyBlZDI1NTE5IHNjb2ZmcyB!dCB0aGUgc2hhMS4");
+ tt_assert(! ent);
+
+ done:
+ tor_free(ent);
+}
+
+static smartlist_t *mock_addent_got = NULL;
+static void
+mock_addent(keypin_ent_t *ent)
+{
+ smartlist_add(mock_addent_got, ent);
+ keypin_add_entry_to_map__real(ent);
+}
+
+static void
+test_keypin_parse_file(void *arg)
+{
+ (void)arg;
+
+ mock_addent_got = smartlist_new();
+ MOCK(keypin_add_entry_to_map, mock_addent);
+
+ /* Simple, minimal, correct example. */
+ const char data1[] =
+"PT09PT09PT09PT09PT09PT09PT0 PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0\n"
+"TG9yYXggaXBzdW0gZ3J1dnZ1bHU cyB0aG5lZWQgYW1ldCwgc25lcmdlbGx5IG9uY2UtbGU\n"
+"ciBsZXJraW0sIHNlZCBkbyBiYXI YmFsb290IHRlbXBvciBnbHVwcGl0dXMgdXQgbGFib3I\n"
+"ZSBldCB0cnVmZnVsYSBtYWduYSA YWxpcXVhLiBVdCBlbmltIGFkIGdyaWNrbGUtZ3Jhc3M\n"
+"dmVuaWFtLCBxdWlzIG1pZmYtbXU ZmZlcmVkIGdhLXp1bXBjbyBsYWJvcmlzIG5pc2kgdXQ\n"
+"Y3J1ZmZ1bHVzIGV4IGVhIHNjaGw b3BwaXR5IGNvbnNlcXVhdC4gRHVpcyBhdXRlIHNuYXI\n"
+"Z2dsZSBpbiBzd29tZWVzd2FucyA aW4gdm9sdXB0YXRlIGF4ZS1oYWNrZXIgZXNzZSByaXA\n"
+"cHVsdXMgY3J1bW1paSBldSBtb28 ZiBudWxsYSBzbnV2di5QTFVHSFBMT1ZFUlhZWlpZLi4\n";
+
+ tt_int_op(0, ==, keypin_load_journal_impl(data1, strlen(data1)));
+ tt_int_op(8, ==, smartlist_len(mock_addent_got));
+ keypin_ent_t *ent = smartlist_get(mock_addent_got, 2);
+ tt_mem_op(ent->rsa_id, ==, "r lerkim, sed do bar", 20);
+ tt_mem_op(ent->ed25519_key, ==, "baloot tempor gluppitus ut labor", 32);
+
+ /* More complex example: weird lines, bogus lines,
+ duplicate/conflicting lines */
+ const char data2[] =
+ "PT09PT09PT09PT09PT09PT09PT0 PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0\n"
+ "# This is a comment.\n"
+ " \n"
+ "QXQgdGhlIGVuZCBvZiB0aGUgeWU YXIgS3VycmVta2FybWVycnVrIHNhaWQgdG8gaGltLCA\n"
+ "IllvdSBoYXZlIG1hZGUgYSBnb28 ZCBiZWdpbm5pbmcuIiBCdXQgbm8gbW9yZS4gV2l6YXI\n"
+ "\n"
+ "ZHMgc3BlYWsgdHJ1dGgsIGFuZCA aXQgd2FzIHRydWUgdGhhdCBhbGwgdGhlIG1hc3Rlcgo\n"
+ "@reserved for a future extension \n"
+ "eSBvZiBOYW1lcyB0aGF0IEdlZCA aGFkIHRvaWxlZCbyB3aW4gdGhhdCB5ZWFyIHdhcyA\n"
+ "eSBvZiBOYW1lcyB0aGF0IEdlZCA aGFkIHRvaWxlZCbyB3aW4gdGhhdCB5ZWFyIHdhcy"
+ "A line too long\n"
+ "dGhlIG1lcmUgc3RhcnQgb2Ygd2g YXQgaGUgbXVzdCBnbyBvb!BsZWFybmluZy4uLi4uLi4\n"
+ "ZHMgc3BlYWsgdaJ1dGgsIGFuZCA aXQgd2FzIHRydWUgdGhhdCBhbGwgdGhlIG1hc3Rlcgo\n"
+ "ZHMgc3BlYWsgdHJ1dGgsIGFuZCA aXQgd2FzIHRydaUgdGhhdCBhbGwgdGhlIG1hc3Rlcgo\n"
+ ;
+
+ tt_int_op(0, ==, keypin_load_journal_impl(data2, strlen(data2)));
+ tt_int_op(11, ==, smartlist_len(mock_addent_got));
+ ent = smartlist_get(mock_addent_got, 9);
+ tt_mem_op(ent->rsa_id, ==, "\"You have made a goo", 20);
+ tt_mem_op(ent->ed25519_key, ==, "d beginning.\" But no more. Wizar", 32);
+
+ ent = smartlist_get(mock_addent_got, 10);
+ tt_mem_op(ent->rsa_id, ==, "ds speak truth, and ", 20);
+ tt_mem_op(ent->ed25519_key, ==, "it was true that all the master\n", 32);
+
+ /* File truncated before NL */
+ const char data3[] =
+ "Tm8gZHJhZ29uIGNhbiByZXNpc3Q IHRoZSBmYXNjaW5hdGlvbiBvZiByaWRkbGluZyB0YWw";
+ tt_int_op(0, ==, keypin_load_journal_impl(data3, strlen(data3)));
+ tt_int_op(12, ==, smartlist_len(mock_addent_got));
+ ent = smartlist_get(mock_addent_got, 11);
+ tt_mem_op(ent->rsa_id, ==, "No dragon can resist", 20);
+ tt_mem_op(ent->ed25519_key, ==, " the fascination of riddling tal", 32);
+
+ done:
+ keypin_clear();
+ smartlist_free(mock_addent_got);
+}
+
+#define ADD(a,b) keypin_check_and_add((const uint8_t*)(a),(const uint8_t*)(b))
+#define LONE_RSA(a) keypin_check_lone_rsa((const uint8_t*)(a))
+
+static void
+test_keypin_add_entry(void *arg)
+{
+ (void)arg;
+ keypin_clear();
+
+ tt_int_op(KEYPIN_ADDED, ==, ADD("ambassadors-at-large",
+ "bread-and-butter thing-in-itself"));
+ tt_int_op(KEYPIN_ADDED, ==, ADD("gentleman-adventurer",
+ "cloak-and-dagger what's-his-face"));
+
+ tt_int_op(KEYPIN_FOUND, ==, ADD("ambassadors-at-large",
+ "bread-and-butter thing-in-itself"));
+ tt_int_op(KEYPIN_FOUND, ==, ADD("ambassadors-at-large",
+ "bread-and-butter thing-in-itself"));
+ tt_int_op(KEYPIN_FOUND, ==, ADD("gentleman-adventurer",
+ "cloak-and-dagger what's-his-face"));
+
+ tt_int_op(KEYPIN_ADDED, ==, ADD("Johnnies-come-lately",
+ "run-of-the-mill root-mean-square"));
+
+ tt_int_op(KEYPIN_MISMATCH, ==, ADD("gentleman-adventurer",
+ "hypersentimental closefistedness"));
+
+ tt_int_op(KEYPIN_MISMATCH, ==, ADD("disestablismentarian",
+ "cloak-and-dagger what's-his-face"));
+
+ tt_int_op(KEYPIN_FOUND, ==, ADD("gentleman-adventurer",
+ "cloak-and-dagger what's-his-face"));
+
+ tt_int_op(KEYPIN_NOT_FOUND, ==, LONE_RSA("Llanfairpwllgwyngyll"));
+ tt_int_op(KEYPIN_MISMATCH, ==, LONE_RSA("Johnnies-come-lately"));
+
+ done:
+ keypin_clear();
+}
+
+static void
+test_keypin_journal(void *arg)
+{
+ (void)arg;
+ char *contents = NULL;
+ const char *fname = get_fname("keypin-journal");
+
+ tt_int_op(0, ==, keypin_load_journal(fname)); /* ENOENT is okay */
+ update_approx_time(1217709000);
+ tt_int_op(0, ==, keypin_open_journal(fname));
+
+ tt_int_op(KEYPIN_ADDED, ==, ADD("king-of-the-herrings",
+ "good-for-nothing attorney-at-law"));
+ tt_int_op(KEYPIN_ADDED, ==, ADD("yellowish-red-yellow",
+ "salt-and-pepper high-muck-a-muck"));
+ tt_int_op(KEYPIN_FOUND, ==, ADD("yellowish-red-yellow",
+ "salt-and-pepper high-muck-a-muck"));
+ keypin_close_journal();
+ keypin_clear();
+
+ tt_int_op(0, ==, keypin_load_journal(fname));
+ update_approx_time(1231041600);
+ tt_int_op(0, ==, keypin_open_journal(fname));
+ tt_int_op(KEYPIN_FOUND, ==, ADD("yellowish-red-yellow",
+ "salt-and-pepper high-muck-a-muck"));
+ tt_int_op(KEYPIN_ADDED, ==, ADD("theatre-in-the-round",
+ "holier-than-thou jack-in-the-box"));
+ tt_int_op(KEYPIN_ADDED, ==, ADD("no-deposit-no-return",
+ "across-the-board will-o-the-wisp"));
+ tt_int_op(KEYPIN_MISMATCH, ==, ADD("intellectualizations",
+ "salt-and-pepper high-muck-a-muck"));
+ keypin_close_journal();
+ keypin_clear();
+
+ tt_int_op(0, ==, keypin_load_journal(fname));
+ update_approx_time(1412278354);
+ tt_int_op(0, ==, keypin_open_journal(fname));
+ tt_int_op(KEYPIN_FOUND, ==, ADD("yellowish-red-yellow",
+ "salt-and-pepper high-muck-a-muck"));
+ tt_int_op(KEYPIN_MISMATCH, ==, ADD("intellectualizations",
+ "salt-and-pepper high-muck-a-muck"));
+ tt_int_op(KEYPIN_FOUND, ==, ADD("theatre-in-the-round",
+ "holier-than-thou jack-in-the-box"));
+ tt_int_op(KEYPIN_MISMATCH, ==, ADD("counterrevolutionary",
+ "holier-than-thou jack-in-the-box"));
+ tt_int_op(KEYPIN_MISMATCH, ==, ADD("no-deposit-no-return",
+ "floccinaucinihilipilificationism"));
+ keypin_close_journal();
+
+ contents = read_file_to_str(fname, RFTS_BIN, NULL);
+ tt_assert(contents);
+ tt_str_op(contents,==,
+ "\n"
+ "@opened-at 2008-08-02 20:30:00\n"
+ "a2luZy1vZi10aGUtaGVycmluZ3M Z29vZC1mb3Itbm90aGluZyBhdHRvcm5leS1hdC1sYXc\n"
+ "eWVsbG93aXNoLXJlZC15ZWxsb3c c2FsdC1hbmQtcGVwcGVyIGhpZ2gtbXVjay1hLW11Y2s\n"
+ "\n"
+ "@opened-at 2009-01-04 04:00:00\n"
+ "dGhlYXRyZS1pbi10aGUtcm91bmQ aG9saWVyLXRoYW4tdGhvdSBqYWNrLWluLXRoZS1ib3g\n"
+ "bm8tZGVwb3NpdC1uby1yZXR1cm4 YWNyb3NzLXRoZS1ib2FyZCB3aWxsLW8tdGhlLXdpc3A\n"
+ "\n"
+ "@opened-at 2014-10-02 19:32:34\n");
+
+ done:
+ tor_free(contents);
+ keypin_clear();
+}
+
+#undef ADD
+#undef LONE_RSA
+
+#define TEST(name, flags) \
+ { #name , test_keypin_ ## name, (flags), NULL, NULL }
+
+struct testcase_t keypin_tests[] = {
+ TEST( parse_line, 0 ),
+ TEST( parse_file, TT_FORK ),
+ TEST( add_entry, TT_FORK ),
+ TEST( journal, TT_FORK ),
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c
new file mode 100644
index 0000000000..7ad2c30d0f
--- /dev/null
+++ b/src/test/test_link_handshake.c
@@ -0,0 +1,928 @@
+/* Copyright (c) 2014, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+#include "orconfig.h"
+
+#define CHANNELTLS_PRIVATE
+#define CONNECTION_PRIVATE
+#define TOR_CHANNEL_INTERNAL_
+#include "or.h"
+#include "config.h"
+#include "connection.h"
+#include "connection_or.h"
+#include "channeltls.h"
+#include "link_handshake.h"
+#include "scheduler.h"
+
+#include "test.h"
+
+var_cell_t *mock_got_var_cell = NULL;
+
+static void
+mock_write_var_cell(const var_cell_t *vc, or_connection_t *conn)
+{
+ (void)conn;
+
+ var_cell_t *newcell = var_cell_new(vc->payload_len);
+ memcpy(newcell, vc, sizeof(var_cell_t));
+ memcpy(newcell->payload, vc->payload, vc->payload_len);
+
+ mock_got_var_cell = newcell;
+}
+static int
+mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert)
+{
+ (void) tls;
+ (void) cert; // XXXX look at this.
+ return 1;
+}
+
+static int mock_send_netinfo_called = 0;
+static int
+mock_send_netinfo(or_connection_t *conn)
+{
+ (void) conn;
+ ++mock_send_netinfo_called;// XXX check_this
+ return 0;
+}
+
+static int mock_close_called = 0;
+static void
+mock_close_for_err(or_connection_t *orconn, int flush)
+{
+ (void)orconn;
+ (void)flush;
+ ++mock_close_called;
+}
+
+static int mock_send_authenticate_called = 0;
+static int
+mock_send_authenticate(or_connection_t *conn, int type)
+{
+ (void) conn;
+ (void) type;
+ ++mock_send_authenticate_called;// XXX check_this
+ return 0;
+}
+
+/* Test good certs cells */
+static void
+test_link_handshake_certs_ok(void *arg)
+{
+ (void) arg;
+
+ or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET);
+ or_connection_t *c2 = or_connection_new(CONN_TYPE_OR, AF_INET);
+ var_cell_t *cell1 = NULL, *cell2 = NULL;
+ certs_cell_t *cc1 = NULL, *cc2 = NULL;
+ channel_tls_t *chan1 = NULL, *chan2 = NULL;
+ crypto_pk_t *key1 = NULL, *key2 = NULL;
+
+ scheduler_init();
+
+ MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
+ MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell);
+ MOCK(connection_or_send_netinfo, mock_send_netinfo);
+
+ key1 = pk_generate(2);
+ key2 = pk_generate(3);
+
+ /* We need to make sure that our TLS certificates are set up before we can
+ * actually generate a CERTS cell.
+ */
+ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
+ key1, key2, 86400), ==, 0);
+
+ c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
+ c1->link_proto = 3;
+ tt_int_op(connection_init_or_handshake_state(c1, 1), ==, 0);
+
+ c2->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
+ c2->link_proto = 3;
+ tt_int_op(connection_init_or_handshake_state(c2, 0), ==, 0);
+
+ tt_int_op(0, ==, connection_or_send_certs_cell(c1));
+ tt_assert(mock_got_var_cell);
+ cell1 = mock_got_var_cell;
+
+ tt_int_op(0, ==, connection_or_send_certs_cell(c2));
+ tt_assert(mock_got_var_cell);
+ cell2 = mock_got_var_cell;
+
+ tt_int_op(cell1->command, ==, CELL_CERTS);
+ tt_int_op(cell1->payload_len, >, 1);
+
+ tt_int_op(cell2->command, ==, CELL_CERTS);
+ tt_int_op(cell2->payload_len, >, 1);
+
+ tt_int_op(cell1->payload_len, ==,
+ certs_cell_parse(&cc1, cell1->payload, cell1->payload_len));
+ tt_int_op(cell2->payload_len, ==,
+ certs_cell_parse(&cc2, cell2->payload, cell2->payload_len));
+
+ tt_int_op(2, ==, cc1->n_certs);
+ tt_int_op(2, ==, cc2->n_certs);
+
+ tt_int_op(certs_cell_get_certs(cc1, 0)->cert_type, ==,
+ CERTTYPE_RSA1024_ID_AUTH);
+ tt_int_op(certs_cell_get_certs(cc1, 1)->cert_type, ==,
+ CERTTYPE_RSA1024_ID_ID);
+
+ tt_int_op(certs_cell_get_certs(cc2, 0)->cert_type, ==,
+ CERTTYPE_RSA1024_ID_LINK);
+ tt_int_op(certs_cell_get_certs(cc2, 1)->cert_type, ==,
+ CERTTYPE_RSA1024_ID_ID);
+
+ chan1 = tor_malloc_zero(sizeof(*chan1));
+ channel_tls_common_init(chan1);
+ c1->chan = chan1;
+ chan1->conn = c1;
+ c1->base_.address = tor_strdup("C1");
+ c1->tls = tor_tls_new(-1, 0);
+ c1->link_proto = 4;
+ c1->base_.conn_array_index = -1;
+ crypto_pk_get_digest(key2, c1->identity_digest);
+
+ channel_tls_process_certs_cell(cell2, chan1);
+
+ tt_assert(c1->handshake_state->received_certs_cell);
+ tt_assert(c1->handshake_state->auth_cert == NULL);
+ tt_assert(c1->handshake_state->id_cert);
+ tt_assert(! tor_mem_is_zero(
+ (char*)c1->handshake_state->authenticated_peer_id, 20));
+
+ chan2 = tor_malloc_zero(sizeof(*chan2));
+ channel_tls_common_init(chan2);
+ c2->chan = chan2;
+ chan2->conn = c2;
+ c2->base_.address = tor_strdup("C2");
+ c2->tls = tor_tls_new(-1, 1);
+ c2->link_proto = 4;
+ c2->base_.conn_array_index = -1;
+ crypto_pk_get_digest(key1, c2->identity_digest);
+
+ channel_tls_process_certs_cell(cell1, chan2);
+
+ tt_assert(c2->handshake_state->received_certs_cell);
+ tt_assert(c2->handshake_state->auth_cert);
+ tt_assert(c2->handshake_state->id_cert);
+ tt_assert(tor_mem_is_zero(
+ (char*)c2->handshake_state->authenticated_peer_id, 20));
+
+ done:
+ UNMOCK(tor_tls_cert_matches_key);
+ UNMOCK(connection_or_write_var_cell_to_buf);
+ UNMOCK(connection_or_send_netinfo);
+ connection_free_(TO_CONN(c1));
+ connection_free_(TO_CONN(c2));
+ tor_free(cell1);
+ tor_free(cell2);
+ certs_cell_free(cc1);
+ certs_cell_free(cc2);
+ if (chan1)
+ circuitmux_free(chan1->base_.cmux);
+ tor_free(chan1);
+ if (chan2)
+ circuitmux_free(chan2->base_.cmux);
+ tor_free(chan2);
+ crypto_pk_free(key1);
+ crypto_pk_free(key2);
+}
+
+typedef struct certs_data_s {
+ or_connection_t *c;
+ channel_tls_t *chan;
+ certs_cell_t *ccell;
+ var_cell_t *cell;
+ crypto_pk_t *key1, *key2;
+} certs_data_t;
+
+static int
+recv_certs_cleanup(const struct testcase_t *test, void *obj)
+{
+ (void)test;
+ certs_data_t *d = obj;
+ UNMOCK(tor_tls_cert_matches_key);
+ UNMOCK(connection_or_send_netinfo);
+ UNMOCK(connection_or_close_for_error);
+
+ if (d) {
+ tor_free(d->cell);
+ certs_cell_free(d->ccell);
+ connection_free_(TO_CONN(d->c));
+ circuitmux_free(d->chan->base_.cmux);
+ tor_free(d->chan);
+ crypto_pk_free(d->key1);
+ crypto_pk_free(d->key2);
+ tor_free(d);
+ }
+ return 1;
+}
+
+static void *
+recv_certs_setup(const struct testcase_t *test)
+{
+ (void)test;
+ certs_data_t *d = tor_malloc_zero(sizeof(*d));
+ certs_cell_cert_t *ccc1 = NULL;
+ certs_cell_cert_t *ccc2 = NULL;
+ ssize_t n;
+
+ d->c = or_connection_new(CONN_TYPE_OR, AF_INET);
+ d->chan = tor_malloc_zero(sizeof(*d->chan));
+ d->c->chan = d->chan;
+ d->c->base_.address = tor_strdup("HaveAnAddress");
+ d->c->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
+ d->chan->conn = d->c;
+ tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0);
+ d->c->link_proto = 4;
+
+ d->key1 = pk_generate(2);
+ d->key2 = pk_generate(3);
+
+ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
+ d->key1, d->key2, 86400), ==, 0);
+ d->ccell = certs_cell_new();
+ ccc1 = certs_cell_cert_new();
+ certs_cell_add_certs(d->ccell, ccc1);
+ ccc2 = certs_cell_cert_new();
+ certs_cell_add_certs(d->ccell, ccc2);
+ d->ccell->n_certs = 2;
+ ccc1->cert_type = 1;
+ ccc2->cert_type = 2;
+
+ const tor_x509_cert_t *a,*b;
+ const uint8_t *enca, *encb;
+ size_t lena, lenb;
+ tor_tls_get_my_certs(1, &a, &b);
+ tor_x509_cert_get_der(a, &enca, &lena);
+ tor_x509_cert_get_der(b, &encb, &lenb);
+ certs_cell_cert_setlen_body(ccc1, lena);
+ ccc1->cert_len = lena;
+ certs_cell_cert_setlen_body(ccc2, lenb);
+ ccc2->cert_len = lenb;
+
+ memcpy(certs_cell_cert_getarray_body(ccc1), enca, lena);
+ memcpy(certs_cell_cert_getarray_body(ccc2), encb, lenb);
+
+ d->cell = var_cell_new(4096);
+ d->cell->command = CELL_CERTS;
+
+ n = certs_cell_encode(d->cell->payload, 4096, d->ccell);
+ tt_int_op(n, >, 0);
+ d->cell->payload_len = n;
+
+ MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key);
+ MOCK(connection_or_send_netinfo, mock_send_netinfo);
+ MOCK(connection_or_close_for_error, mock_close_for_err);
+
+ tt_int_op(0, ==, d->c->handshake_state->received_certs_cell);
+ tt_int_op(0, ==, mock_send_authenticate_called);
+ tt_int_op(0, ==, mock_send_netinfo_called);
+
+ return d;
+ done:
+ recv_certs_cleanup(test, d);
+ return NULL;
+}
+
+static struct testcase_setup_t setup_recv_certs = {
+ .setup_fn = recv_certs_setup,
+ .cleanup_fn = recv_certs_cleanup
+};
+
+static void
+test_link_handshake_recv_certs_ok(void *arg)
+{
+ certs_data_t *d = arg;
+ channel_tls_process_certs_cell(d->cell, d->chan);
+ tt_int_op(0, ==, mock_close_called);
+ tt_int_op(d->c->handshake_state->authenticated, ==, 1);
+ tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1);
+ tt_assert(d->c->handshake_state->id_cert != NULL);
+ tt_assert(d->c->handshake_state->auth_cert == NULL);
+
+ done:
+ ;
+}
+
+static void
+test_link_handshake_recv_certs_ok_server(void *arg)
+{
+ certs_data_t *d = arg;
+ d->c->handshake_state->started_here = 0;
+ certs_cell_get_certs(d->ccell, 0)->cert_type = 3;
+ certs_cell_get_certs(d->ccell, 1)->cert_type = 2;
+ ssize_t n = certs_cell_encode(d->cell->payload, 2048, d->ccell);
+ tt_int_op(n, >, 0);
+ d->cell->payload_len = n;
+ channel_tls_process_certs_cell(d->cell, d->chan);
+ tt_int_op(0, ==, mock_close_called);
+ tt_int_op(d->c->handshake_state->authenticated, ==, 0);
+ tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1);
+ tt_assert(d->c->handshake_state->id_cert != NULL);
+ tt_assert(d->c->handshake_state->auth_cert != NULL);
+
+ done:
+ ;
+}
+
+#define CERTS_FAIL(name, code) \
+ static void \
+ test_link_handshake_recv_certs_ ## name(void *arg) \
+ { \
+ certs_data_t *d = arg; \
+ { code ; } \
+ channel_tls_process_certs_cell(d->cell, d->chan); \
+ tt_int_op(1, ==, mock_close_called); \
+ tt_int_op(0, ==, mock_send_authenticate_called); \
+ tt_int_op(0, ==, mock_send_netinfo_called); \
+ done: \
+ ; \
+ }
+
+CERTS_FAIL(badstate, d->c->base_.state = OR_CONN_STATE_CONNECTING)
+CERTS_FAIL(badproto, d->c->link_proto = 2)
+CERTS_FAIL(duplicate, d->c->handshake_state->received_certs_cell = 1)
+CERTS_FAIL(already_authenticated,
+ d->c->handshake_state->authenticated = 1)
+CERTS_FAIL(empty, d->cell->payload_len = 0)
+CERTS_FAIL(bad_circid, d->cell->circ_id = 1)
+CERTS_FAIL(truncated_1, d->cell->payload[0] = 5)
+CERTS_FAIL(truncated_2,
+ {
+ d->cell->payload_len = 4;
+ memcpy(d->cell->payload, "\x01\x01\x00\x05", 4);
+ })
+CERTS_FAIL(truncated_3,
+ {
+ d->cell->payload_len = 7;
+ memcpy(d->cell->payload, "\x01\x01\x00\x05""abc", 7);
+ })
+#define REENCODE() do { \
+ ssize_t n = certs_cell_encode(d->cell->payload, 4096, d->ccell); \
+ tt_int_op(n, >, 0); \
+ d->cell->payload_len = n; \
+ } while (0)
+
+CERTS_FAIL(not_x509,
+ {
+ certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 0), 3);
+ certs_cell_get_certs(d->ccell, 0)->cert_len = 3;
+ REENCODE();
+ })
+CERTS_FAIL(both_link,
+ {
+ certs_cell_get_certs(d->ccell, 0)->cert_type = 1;
+ certs_cell_get_certs(d->ccell, 1)->cert_type = 1;
+ REENCODE();
+ })
+CERTS_FAIL(both_id_rsa,
+ {
+ certs_cell_get_certs(d->ccell, 0)->cert_type = 2;
+ certs_cell_get_certs(d->ccell, 1)->cert_type = 2;
+ REENCODE();
+ })
+CERTS_FAIL(both_auth,
+ {
+ certs_cell_get_certs(d->ccell, 0)->cert_type = 3;
+ certs_cell_get_certs(d->ccell, 1)->cert_type = 3;
+ REENCODE();
+ })
+CERTS_FAIL(wrong_labels_1,
+ {
+ certs_cell_get_certs(d->ccell, 0)->cert_type = 2;
+ certs_cell_get_certs(d->ccell, 1)->cert_type = 1;
+ REENCODE();
+ })
+CERTS_FAIL(wrong_labels_2,
+ {
+ const tor_x509_cert_t *a;
+ const tor_x509_cert_t *b;
+ const uint8_t *enca;
+ size_t lena;
+ tor_tls_get_my_certs(1, &a, &b);
+ tor_x509_cert_get_der(a, &enca, &lena);
+ certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 1), lena);
+ memcpy(certs_cell_cert_getarray_body(certs_cell_get_certs(d->ccell, 1)),
+ enca, lena);
+ certs_cell_get_certs(d->ccell, 1)->cert_len = lena;
+ REENCODE();
+ })
+CERTS_FAIL(wrong_labels_3,
+ {
+ certs_cell_get_certs(d->ccell, 0)->cert_type = 2;
+ certs_cell_get_certs(d->ccell, 1)->cert_type = 3;
+ REENCODE();
+ })
+CERTS_FAIL(server_missing_certs,
+ {
+ d->c->handshake_state->started_here = 0;
+ })
+CERTS_FAIL(server_wrong_labels_1,
+ {
+ d->c->handshake_state->started_here = 0;
+ certs_cell_get_certs(d->ccell, 0)->cert_type = 2;
+ certs_cell_get_certs(d->ccell, 1)->cert_type = 3;
+ REENCODE();
+ })
+
+static void
+test_link_handshake_send_authchallenge(void *arg)
+{
+ (void)arg;
+
+ or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET);
+ var_cell_t *cell1=NULL, *cell2=NULL;
+
+ MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell);
+
+ tt_int_op(connection_init_or_handshake_state(c1, 0), ==, 0);
+ c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
+ tt_assert(! mock_got_var_cell);
+ tt_int_op(0, ==, connection_or_send_auth_challenge_cell(c1));
+ cell1 = mock_got_var_cell;
+ tt_int_op(0, ==, connection_or_send_auth_challenge_cell(c1));
+ cell2 = mock_got_var_cell;
+ tt_int_op(36, ==, cell1->payload_len);
+ tt_int_op(36, ==, cell2->payload_len);
+ tt_int_op(0, ==, cell1->circ_id);
+ tt_int_op(0, ==, cell2->circ_id);
+ tt_int_op(CELL_AUTH_CHALLENGE, ==, cell1->command);
+ tt_int_op(CELL_AUTH_CHALLENGE, ==, cell2->command);
+
+ tt_mem_op("\x00\x01\x00\x01", ==, cell1->payload + 32, 4);
+ tt_mem_op("\x00\x01\x00\x01", ==, cell2->payload + 32, 4);
+ tt_mem_op(cell1->payload, !=, cell2->payload, 32);
+
+ done:
+ UNMOCK(connection_or_write_var_cell_to_buf);
+ connection_free_(TO_CONN(c1));
+ tor_free(cell1);
+ tor_free(cell2);
+}
+
+typedef struct authchallenge_data_s {
+ or_connection_t *c;
+ channel_tls_t *chan;
+ var_cell_t *cell;
+} authchallenge_data_t;
+
+static int
+recv_authchallenge_cleanup(const struct testcase_t *test, void *obj)
+{
+ (void)test;
+ authchallenge_data_t *d = obj;
+
+ UNMOCK(connection_or_send_netinfo);
+ UNMOCK(connection_or_close_for_error);
+ UNMOCK(connection_or_send_authenticate_cell);
+
+ if (d) {
+ tor_free(d->cell);
+ connection_free_(TO_CONN(d->c));
+ circuitmux_free(d->chan->base_.cmux);
+ tor_free(d->chan);
+ tor_free(d);
+ }
+ return 1;
+}
+
+static void *
+recv_authchallenge_setup(const struct testcase_t *test)
+{
+ (void)test;
+ authchallenge_data_t *d = tor_malloc_zero(sizeof(*d));
+ d->c = or_connection_new(CONN_TYPE_OR, AF_INET);
+ d->chan = tor_malloc_zero(sizeof(*d->chan));
+ d->c->chan = d->chan;
+ d->c->base_.address = tor_strdup("HaveAnAddress");
+ d->c->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
+ d->chan->conn = d->c;
+ tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0);
+ d->c->link_proto = 4;
+ d->c->handshake_state->received_certs_cell = 1;
+ d->cell = var_cell_new(128);
+ d->cell->payload_len = 38;
+ d->cell->payload[33] = 2;
+ d->cell->payload[35] = 7;
+ d->cell->payload[37] = 1;
+ d->cell->command = CELL_AUTH_CHALLENGE;
+
+ get_options_mutable()->ORPort_set = 1;
+
+ MOCK(connection_or_close_for_error, mock_close_for_err);
+ MOCK(connection_or_send_netinfo, mock_send_netinfo);
+ MOCK(connection_or_send_authenticate_cell, mock_send_authenticate);
+
+ tt_int_op(0, ==, d->c->handshake_state->received_auth_challenge);
+ tt_int_op(0, ==, mock_send_authenticate_called);
+ tt_int_op(0, ==, mock_send_netinfo_called);
+
+ return d;
+ done:
+ recv_authchallenge_cleanup(test, d);
+ return NULL;
+}
+
+static struct testcase_setup_t setup_recv_authchallenge = {
+ .setup_fn = recv_authchallenge_setup,
+ .cleanup_fn = recv_authchallenge_cleanup
+};
+
+static void
+test_link_handshake_recv_authchallenge_ok(void *arg)
+{
+ authchallenge_data_t *d = arg;
+
+ channel_tls_process_auth_challenge_cell(d->cell, d->chan);
+ tt_int_op(0, ==, mock_close_called);
+ tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge);
+ tt_int_op(1, ==, mock_send_authenticate_called);
+ tt_int_op(1, ==, mock_send_netinfo_called);
+ done:
+ ;
+}
+
+static void
+test_link_handshake_recv_authchallenge_ok_noserver(void *arg)
+{
+ authchallenge_data_t *d = arg;
+ get_options_mutable()->ORPort_set = 0;
+
+ channel_tls_process_auth_challenge_cell(d->cell, d->chan);
+ tt_int_op(0, ==, mock_close_called);
+ tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge);
+ tt_int_op(0, ==, mock_send_authenticate_called);
+ tt_int_op(0, ==, mock_send_netinfo_called);
+ done:
+ ;
+}
+
+static void
+test_link_handshake_recv_authchallenge_ok_unrecognized(void *arg)
+{
+ authchallenge_data_t *d = arg;
+ d->cell->payload[37] = 99;
+
+ channel_tls_process_auth_challenge_cell(d->cell, d->chan);
+ tt_int_op(0, ==, mock_close_called);
+ tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge);
+ tt_int_op(0, ==, mock_send_authenticate_called);
+ tt_int_op(1, ==, mock_send_netinfo_called);
+ done:
+ ;
+}
+
+#define AUTHCHALLENGE_FAIL(name, code) \
+ static void \
+ test_link_handshake_recv_authchallenge_ ## name(void *arg) \
+ { \
+ authchallenge_data_t *d = arg; \
+ { code ; } \
+ channel_tls_process_auth_challenge_cell(d->cell, d->chan); \
+ tt_int_op(1, ==, mock_close_called); \
+ tt_int_op(0, ==, mock_send_authenticate_called); \
+ tt_int_op(0, ==, mock_send_netinfo_called); \
+ done: \
+ ; \
+ }
+
+AUTHCHALLENGE_FAIL(badstate,
+ d->c->base_.state = OR_CONN_STATE_CONNECTING)
+AUTHCHALLENGE_FAIL(badproto,
+ d->c->link_proto = 2)
+AUTHCHALLENGE_FAIL(as_server,
+ d->c->handshake_state->started_here = 0;)
+AUTHCHALLENGE_FAIL(duplicate,
+ d->c->handshake_state->received_auth_challenge = 1)
+AUTHCHALLENGE_FAIL(nocerts,
+ d->c->handshake_state->received_certs_cell = 0)
+AUTHCHALLENGE_FAIL(tooshort,
+ d->cell->payload_len = 33)
+AUTHCHALLENGE_FAIL(truncated,
+ d->cell->payload_len = 34)
+AUTHCHALLENGE_FAIL(nonzero_circid,
+ d->cell->circ_id = 1337)
+
+static tor_x509_cert_t *mock_peer_cert = NULL;
+static tor_x509_cert_t *
+mock_get_peer_cert(tor_tls_t *tls)
+{
+ (void)tls;
+ return mock_peer_cert;
+}
+
+static int
+mock_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out)
+{
+ (void)tls;
+ memcpy(secrets_out, "int getRandomNumber(){return 4;}", 32);
+ return 0;
+}
+
+static void
+mock_set_circid_type(channel_t *chan,
+ crypto_pk_t *identity_rcvd,
+ int consider_identity)
+{
+ (void) chan;
+ (void) identity_rcvd;
+ (void) consider_identity;
+}
+
+typedef struct authenticate_data_s {
+ or_connection_t *c1, *c2;
+ channel_tls_t *chan2;
+ var_cell_t *cell;
+ crypto_pk_t *key1, *key2;
+} authenticate_data_t;
+
+static int
+authenticate_data_cleanup(const struct testcase_t *test, void *arg)
+{
+ (void) test;
+ UNMOCK(connection_or_write_var_cell_to_buf);
+ UNMOCK(tor_tls_get_peer_cert);
+ UNMOCK(tor_tls_get_tlssecrets);
+ UNMOCK(connection_or_close_for_error);
+ UNMOCK(channel_set_circid_type);
+ authenticate_data_t *d = arg;
+ if (d) {
+ tor_free(d->cell);
+ connection_free_(TO_CONN(d->c1));
+ connection_free_(TO_CONN(d->c2));
+ circuitmux_free(d->chan2->base_.cmux);
+ tor_free(d->chan2);
+ crypto_pk_free(d->key1);
+ crypto_pk_free(d->key2);
+ tor_free(d);
+ }
+ mock_peer_cert = NULL;
+
+ return 1;
+}
+
+static void *
+authenticate_data_setup(const struct testcase_t *test)
+{
+ authenticate_data_t *d = tor_malloc_zero(sizeof(*d));
+
+ scheduler_init();
+
+ MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell);
+ MOCK(tor_tls_get_peer_cert, mock_get_peer_cert);
+ MOCK(tor_tls_get_tlssecrets, mock_get_tlssecrets);
+ MOCK(connection_or_close_for_error, mock_close_for_err);
+ MOCK(channel_set_circid_type, mock_set_circid_type);
+ d->c1 = or_connection_new(CONN_TYPE_OR, AF_INET);
+ d->c2 = or_connection_new(CONN_TYPE_OR, AF_INET);
+
+ d->key1 = pk_generate(2);
+ d->key2 = pk_generate(3);
+ tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER,
+ d->key1, d->key2, 86400), ==, 0);
+
+ d->c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
+ d->c1->link_proto = 3;
+ tt_int_op(connection_init_or_handshake_state(d->c1, 1), ==, 0);
+
+ d->c2->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3;
+ d->c2->link_proto = 3;
+ tt_int_op(connection_init_or_handshake_state(d->c2, 0), ==, 0);
+ var_cell_t *cell = var_cell_new(16);
+ cell->command = CELL_CERTS;
+ or_handshake_state_record_var_cell(d->c1, d->c1->handshake_state, cell, 1);
+ or_handshake_state_record_var_cell(d->c2, d->c2->handshake_state, cell, 0);
+ memset(cell->payload, 0xf0, 16);
+ or_handshake_state_record_var_cell(d->c1, d->c1->handshake_state, cell, 0);
+ or_handshake_state_record_var_cell(d->c2, d->c2->handshake_state, cell, 1);
+ tor_free(cell);
+
+ d->chan2 = tor_malloc_zero(sizeof(*d->chan2));
+ channel_tls_common_init(d->chan2);
+ d->c2->chan = d->chan2;
+ d->chan2->conn = d->c2;
+ d->c2->base_.address = tor_strdup("C2");
+ d->c2->tls = tor_tls_new(-1, 1);
+ d->c2->handshake_state->received_certs_cell = 1;
+
+ const tor_x509_cert_t *id_cert=NULL, *link_cert=NULL, *auth_cert=NULL;
+ tt_assert(! tor_tls_get_my_certs(1, &link_cert, &id_cert));
+
+ const uint8_t *der;
+ size_t sz;
+ tor_x509_cert_get_der(id_cert, &der, &sz);
+ d->c1->handshake_state->id_cert = tor_x509_cert_decode(der, sz);
+ d->c2->handshake_state->id_cert = tor_x509_cert_decode(der, sz);
+
+ tor_x509_cert_get_der(link_cert, &der, &sz);
+ mock_peer_cert = tor_x509_cert_decode(der, sz);
+ tt_assert(mock_peer_cert);
+ tt_assert(! tor_tls_get_my_certs(0, &auth_cert, &id_cert));
+ tor_x509_cert_get_der(auth_cert, &der, &sz);
+ d->c2->handshake_state->auth_cert = tor_x509_cert_decode(der, sz);
+
+ /* Make an authenticate cell ... */
+ tt_int_op(0, ==, connection_or_send_authenticate_cell(d->c1,
+ AUTHTYPE_RSA_SHA256_TLSSECRET));
+ tt_assert(mock_got_var_cell);
+ d->cell = mock_got_var_cell;
+ mock_got_var_cell = NULL;
+
+ return d;
+ done:
+ authenticate_data_cleanup(test, d);
+ return NULL;
+}
+
+static struct testcase_setup_t setup_authenticate = {
+ .setup_fn = authenticate_data_setup,
+ .cleanup_fn = authenticate_data_cleanup
+};
+
+static void
+test_link_handshake_auth_cell(void *arg)
+{
+ authenticate_data_t *d = arg;
+ auth1_t *auth1 = NULL;
+ crypto_pk_t *auth_pubkey = NULL;
+
+ /* Is the cell well-formed on the outer layer? */
+ tt_int_op(d->cell->command, ==, CELL_AUTHENTICATE);
+ tt_int_op(d->cell->payload[0], ==, 0);
+ tt_int_op(d->cell->payload[1], ==, 1);
+ tt_int_op(ntohs(get_uint16(d->cell->payload + 2)), ==,
+ d->cell->payload_len - 4);
+
+ /* Check it out for plausibility... */
+ auth_ctx_t ctx;
+ ctx.is_ed = 0;
+ tt_int_op(d->cell->payload_len-4, ==, auth1_parse(&auth1,
+ d->cell->payload+4,
+ d->cell->payload_len - 4, &ctx));
+ tt_assert(auth1);
+
+ tt_mem_op(auth1->type, ==, "AUTH0001", 8);
+ tt_mem_op(auth1->tlssecrets, ==, "int getRandomNumber(){return 4;}", 32);
+ tt_int_op(auth1_getlen_sig(auth1), >, 120);
+
+ /* Is the signature okay? */
+ uint8_t sig[128];
+ uint8_t digest[32];
+
+ auth_pubkey = tor_tls_cert_get_key(d->c2->handshake_state->auth_cert);
+ int n = crypto_pk_public_checksig(
+ auth_pubkey,
+ (char*)sig, sizeof(sig), (char*)auth1_getarray_sig(auth1),
+ auth1_getlen_sig(auth1));
+ tt_int_op(n, ==, 32);
+ const uint8_t *start = d->cell->payload+4, *end = auth1->end_of_signed;
+ crypto_digest256((char*)digest,
+ (const char*)start, end-start, DIGEST_SHA256);
+ tt_mem_op(sig, ==, digest, 32);
+
+ /* Then feed it to c2. */
+ tt_int_op(d->c2->handshake_state->authenticated, ==, 0);
+ channel_tls_process_authenticate_cell(d->cell, d->chan2);
+ tt_int_op(mock_close_called, ==, 0);
+ tt_int_op(d->c2->handshake_state->authenticated, ==, 1);
+
+ done:
+ auth1_free(auth1);
+ crypto_pk_free(auth_pubkey);
+}
+
+#define AUTHENTICATE_FAIL(name, code) \
+ static void \
+ test_link_handshake_auth_ ## name(void *arg) \
+ { \
+ authenticate_data_t *d = arg; \
+ { code ; } \
+ tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \
+ channel_tls_process_authenticate_cell(d->cell, d->chan2); \
+ tt_int_op(mock_close_called, ==, 1); \
+ tt_int_op(d->c2->handshake_state->authenticated, ==, 0); \
+ done: \
+ ; \
+ }
+
+AUTHENTICATE_FAIL(badstate,
+ d->c2->base_.state = OR_CONN_STATE_CONNECTING)
+AUTHENTICATE_FAIL(badproto,
+ d->c2->link_proto = 2)
+AUTHENTICATE_FAIL(atclient,
+ d->c2->handshake_state->started_here = 1)
+AUTHENTICATE_FAIL(duplicate,
+ d->c2->handshake_state->received_authenticate = 1)
+static void
+test_link_handshake_auth_already_authenticated(void *arg)
+{
+ authenticate_data_t *d = arg;
+ d->c2->handshake_state->authenticated = 1;
+ channel_tls_process_authenticate_cell(d->cell, d->chan2);
+ tt_int_op(mock_close_called, ==, 1);
+ tt_int_op(d->c2->handshake_state->authenticated, ==, 1);
+ done:
+ ;
+}
+AUTHENTICATE_FAIL(nocerts,
+ d->c2->handshake_state->received_certs_cell = 0)
+AUTHENTICATE_FAIL(noidcert,
+ tor_x509_cert_free(d->c2->handshake_state->id_cert);
+ d->c2->handshake_state->id_cert = NULL)
+AUTHENTICATE_FAIL(noauthcert,
+ tor_x509_cert_free(d->c2->handshake_state->auth_cert);
+ d->c2->handshake_state->auth_cert = NULL)
+AUTHENTICATE_FAIL(tooshort,
+ d->cell->payload_len = 3)
+AUTHENTICATE_FAIL(badtype,
+ d->cell->payload[0] = 0xff)
+AUTHENTICATE_FAIL(truncated_1,
+ d->cell->payload[2]++)
+AUTHENTICATE_FAIL(truncated_2,
+ d->cell->payload[3]++)
+AUTHENTICATE_FAIL(tooshort_1,
+ tt_int_op(d->cell->payload_len, >=, 260);
+ d->cell->payload[2] -= 1;
+ d->cell->payload_len -= 256;)
+AUTHENTICATE_FAIL(badcontent,
+ d->cell->payload[10] ^= 0xff)
+AUTHENTICATE_FAIL(badsig_1,
+ d->cell->payload[d->cell->payload_len - 5] ^= 0xff)
+
+#define TEST(name, flags) \
+ { #name , test_link_handshake_ ## name, (flags), NULL, NULL }
+
+#define TEST_RCV_AUTHCHALLENGE(name) \
+ { "recv_authchallenge/" #name , \
+ test_link_handshake_recv_authchallenge_ ## name, TT_FORK, \
+ &setup_recv_authchallenge, NULL }
+
+#define TEST_RCV_CERTS(name) \
+ { "recv_certs/" #name , \
+ test_link_handshake_recv_certs_ ## name, TT_FORK, \
+ &setup_recv_certs, NULL }
+
+#define TEST_AUTHENTICATE(name) \
+ { "authenticate/" #name , test_link_handshake_auth_ ## name, TT_FORK, \
+ &setup_authenticate, NULL }
+
+struct testcase_t link_handshake_tests[] = {
+ TEST(certs_ok, TT_FORK),
+ //TEST(certs_bad, TT_FORK),
+ TEST_RCV_CERTS(ok),
+ TEST_RCV_CERTS(ok_server),
+ TEST_RCV_CERTS(badstate),
+ TEST_RCV_CERTS(badproto),
+ TEST_RCV_CERTS(duplicate),
+ TEST_RCV_CERTS(already_authenticated),
+ TEST_RCV_CERTS(empty),
+ TEST_RCV_CERTS(bad_circid),
+ TEST_RCV_CERTS(truncated_1),
+ TEST_RCV_CERTS(truncated_2),
+ TEST_RCV_CERTS(truncated_3),
+ TEST_RCV_CERTS(not_x509),
+ TEST_RCV_CERTS(both_link),
+ TEST_RCV_CERTS(both_id_rsa),
+ TEST_RCV_CERTS(both_auth),
+ TEST_RCV_CERTS(wrong_labels_1),
+ TEST_RCV_CERTS(wrong_labels_2),
+ TEST_RCV_CERTS(wrong_labels_3),
+ TEST_RCV_CERTS(server_missing_certs),
+ TEST_RCV_CERTS(server_wrong_labels_1),
+
+ TEST(send_authchallenge, TT_FORK),
+ TEST_RCV_AUTHCHALLENGE(ok),
+ TEST_RCV_AUTHCHALLENGE(ok_noserver),
+ TEST_RCV_AUTHCHALLENGE(ok_unrecognized),
+ TEST_RCV_AUTHCHALLENGE(badstate),
+ TEST_RCV_AUTHCHALLENGE(badproto),
+ TEST_RCV_AUTHCHALLENGE(as_server),
+ TEST_RCV_AUTHCHALLENGE(duplicate),
+ TEST_RCV_AUTHCHALLENGE(nocerts),
+ TEST_RCV_AUTHCHALLENGE(tooshort),
+ TEST_RCV_AUTHCHALLENGE(truncated),
+ TEST_RCV_AUTHCHALLENGE(nonzero_circid),
+
+ TEST_AUTHENTICATE(cell),
+ TEST_AUTHENTICATE(badstate),
+ TEST_AUTHENTICATE(badproto),
+ TEST_AUTHENTICATE(atclient),
+ TEST_AUTHENTICATE(duplicate),
+ TEST_AUTHENTICATE(already_authenticated),
+ TEST_AUTHENTICATE(nocerts),
+ TEST_AUTHENTICATE(noidcert),
+ TEST_AUTHENTICATE(noauthcert),
+ TEST_AUTHENTICATE(tooshort),
+ TEST_AUTHENTICATE(badtype),
+ TEST_AUTHENTICATE(truncated_1),
+ TEST_AUTHENTICATE(truncated_2),
+ TEST_AUTHENTICATE(tooshort_1),
+ TEST_AUTHENTICATE(badcontent),
+ TEST_AUTHENTICATE(badsig_1),
+ //TEST_AUTHENTICATE(),
+
+ END_OF_TESTCASES
+};
+
diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c
index fb3df77edc..5dc5b2b4fa 100644
--- a/src/test/test_microdesc.c
+++ b/src/test/test_microdesc.c
@@ -10,6 +10,7 @@
#include "networkstatus.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "torcert.h"
#include "test.h"
@@ -335,6 +336,59 @@ static const char test_ri[] =
"t0xkIE39ss/EwmQr7iIgkdVH4oRIMsjYnFFJBG26nYY=\n"
"-----END SIGNATURE-----\n";
+static const char test_ri2[] =
+ "router test001a 127.0.0.1 5001 0 7001\n"
+ "identity-ed25519\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQQABf/FAf5iDuKCZP2VxnAaQWdklilAh6kaEeFX4z8261Yx2T1/AQAgBADCp8vO\n"
+ "B8K1F9g2DzwuwvVCnPFLSK1qknVqPpNucHLH9DY7fuIYogBAdz4zHv1qC7RKaMNG\n"
+ "Jux/tMO2tzPcm62Ky5PjClMQplKUOnZNQ+RIpA3wYCIfUDy/cQnY7XWgNQ0=\n"
+ "-----END ED25519 CERT-----\n"
+ "platform Tor 0.2.6.0-alpha-dev on Darwin\n"
+ "protocols Link 1 2 Circuit 1\n"
+ "published 2014-10-08 12:58:04\n"
+ "fingerprint B7E2 7F10 4213 C36F 13E7 E982 9182 845E 4959 97A0\n"
+ "uptime 0\n"
+ "bandwidth 1073741824 1073741824 0\n"
+ "extra-info-digest 568F27331B6D8C73E7024F1EF5D097B90DFC7CDB\n"
+ "caches-extra-info\n"
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAL2R8EfubUcahxha4u02P4VAR0llQIMwFAmrHPjzcK7apcQgDOf2ovOA\n"
+ "+YQnJFxlpBmCoCZC6ssCi+9G0mqo650lFuTMP5I90BdtjotfzESfTykHLiChyvhd\n"
+ "l0dlqclb2SU/GKem/fLRXH16aNi72CdSUu/1slKs/70ILi34QixRAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "signing-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAN8+78KUVlgHXdMMkYJxcwh1Zv2y+Gb5eWUyltUaQRajhrT9ij2T5JZs\n"
+ "M0g85xTcuM3jNVVpV79+33hiTohdC6UZ+Bk4USQ7WBFzRbVFSXoVKLBJFkCOIexg\n"
+ "SMGNd5WEDtHWrXl58mizmPFu1eG6ZxHzt7RuLSol5cwBvawXPNkFAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "onion-key-crosscert\n"
+ "-----BEGIN CROSSCERT-----\n"
+ "ETFDzU49bvNfoZnKK1j6JeBP2gDirgj6bBCgWpUYs663OO9ypbZRO0JwWANssKl6\n"
+ "oaq9vKTsKGRsaNnqnz/JGMhehymakjjNtqg7crWwsahe8+7Pw9GKmW+YjFtcOkUf\n"
+ "KfOn2bmKBa1FoJb4yW3oXzHcdlLSRuCciKqPn+Hky5o=\n"
+ "-----END CROSSCERT-----\n"
+ "ntor-onion-key-crosscert 0\n"
+ "-----BEGIN ED25519 CERT-----\n"
+ "AQoABf2dAcKny84HwrUX2DYPPC7C9UKc8UtIrWqSdWo+k25wcsf0AFohutG+xI06\n"
+ "Ef21c5Zl1j8Hw6DzHDjYyJevXLFuOneaL3zcH2Ldn4sjrG3kc5UuVvRfTvV120UO\n"
+ "xk4f5s5LGwY=\n"
+ "-----END ED25519 CERT-----\n"
+ "hidden-service-dir\n"
+ "contact auth1@test.test\n"
+ "ntor-onion-key hbxdRnfVUJJY7+KcT4E3Rs7/zuClbN3hJrjSBiEGMgI=\n"
+ "reject *:*\n"
+ "router-sig-ed25519 5aQXyTif7PExIuL2di37UvktmJECKnils2OWz2vDi"
+ "hFxi+5TTAAPxYkS5clhc/Pjvw34itfjGmTKFic/8httAQ\n"
+ "router-signature\n"
+ "-----BEGIN SIGNATURE-----\n"
+ "BaUB+aFPQbb3BwtdzKsKqV3+6cRlSqJF5bI3UTmwRoJk+Z5Pz+W5NWokNI0xArHM\n"
+ "T4T5FZCCP9350jXsUCIvzyIyktU6aVRCGFt76rFlo1OETpN8GWkMnQU0w18cxvgS\n"
+ "cf34GXHv61XReJF3AlzNHFpbrPOYmowmhrTULKyMqow=\n"
+ "-----END SIGNATURE-----\n";
+
static const char test_md_8[] =
"onion-key\n"
"-----BEGIN RSA PUBLIC KEY-----\n"
@@ -365,6 +419,26 @@ static const char test_md_18[] =
"p reject 25,119,135-139,445,563,1214,4661-4666,6346-6429,6699,6881-6999\n"
"id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4\n";
+static const char test_md2_18[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAL2R8EfubUcahxha4u02P4VAR0llQIMwFAmrHPjzcK7apcQgDOf2ovOA\n"
+ "+YQnJFxlpBmCoCZC6ssCi+9G0mqo650lFuTMP5I90BdtjotfzESfTykHLiChyvhd\n"
+ "l0dlqclb2SU/GKem/fLRXH16aNi72CdSUu/1slKs/70ILi34QixRAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key hbxdRnfVUJJY7+KcT4E3Rs7/zuClbN3hJrjSBiEGMgI=\n"
+ "id rsa1024 t+J/EEITw28T5+mCkYKEXklZl6A\n";
+
+static const char test_md2_21[] =
+ "onion-key\n"
+ "-----BEGIN RSA PUBLIC KEY-----\n"
+ "MIGJAoGBAL2R8EfubUcahxha4u02P4VAR0llQIMwFAmrHPjzcK7apcQgDOf2ovOA\n"
+ "+YQnJFxlpBmCoCZC6ssCi+9G0mqo650lFuTMP5I90BdtjotfzESfTykHLiChyvhd\n"
+ "l0dlqclb2SU/GKem/fLRXH16aNi72CdSUu/1slKs/70ILi34QixRAgMBAAE=\n"
+ "-----END RSA PUBLIC KEY-----\n"
+ "ntor-onion-key hbxdRnfVUJJY7+KcT4E3Rs7/zuClbN3hJrjSBiEGMgI=\n"
+ "id ed25519 wqfLzgfCtRfYNg88LsL1QpzxS0itapJ1aj6TbnByx/Q\n";
+
static void
test_md_generate(void *arg)
{
@@ -391,6 +465,26 @@ test_md_generate(void *arg)
md = dirvote_create_microdescriptor(ri, 18);
tt_str_op(md->body, OP_EQ, test_md_18);
+ microdesc_free(md);
+ md = NULL;
+ md = dirvote_create_microdescriptor(ri, 21);
+ tt_str_op(md->body, ==, test_md_18);
+
+ routerinfo_free(ri);
+ ri = router_parse_entry_from_string(test_ri2, NULL, 0, 0, NULL, NULL);
+
+ microdesc_free(md);
+ md = NULL;
+ md = dirvote_create_microdescriptor(ri, 18);
+ tt_str_op(md->body, ==, test_md2_18);
+
+ microdesc_free(md);
+ md = NULL;
+ md = dirvote_create_microdescriptor(ri, 21);
+ tt_str_op(md->body, ==, test_md2_21);
+ tt_assert(ed25519_pubkey_eq(md->ed25519_identity_pkey,
+ &ri->signing_key_cert->signing_key));
+
done:
microdesc_free(md);
routerinfo_free(ri);
diff --git a/src/test/test_ntor.sh.in b/src/test/test_ntor.sh.in
new file mode 100644
index 0000000000..be35384ddf
--- /dev/null
+++ b/src/test/test_ntor.sh.in
@@ -0,0 +1,9 @@
+#!@SHELL@
+# Validate Tor's ntor implementation.
+
+exitcode=0
+
+@PYTHON@ @abs_top_srcdir@/src/test/ntor_ref.py test-tor || exitcode=1
+@PYTHON@ @abs_top_srcdir@/src/test/ntor_ref.py self-test || exitcode=1
+
+exit ${exitcode}
diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c
index 60b6bb5a72..a60cba746e 100644
--- a/src/test/test_routerkeys.c
+++ b/src/test/test_routerkeys.c
@@ -8,11 +8,17 @@
#include "or.h"
#include "config.h"
#include "router.h"
+#include "routerkeys.h"
#include "util.h"
#include "crypto.h"
-
+#include "torcert.h"
#include "test.h"
+#ifdef _WIN32
+/* For mkdir() */
+#include <direct.h>
+#endif
+
static void
test_routerkeys_write_fingerprint(void *arg)
{
@@ -75,11 +81,551 @@ test_routerkeys_write_fingerprint(void *arg)
tor_free(cp2);
}
+static void
+test_routerkeys_ed_certs(void *args)
+{
+ (void)args;
+ ed25519_keypair_t kp1, kp2;
+ tor_cert_t *cert[2] = {NULL, NULL}, *nocert = NULL;
+ tor_cert_t *parsed_cert[2] = {NULL, NULL};
+ time_t now = 1412094534;
+ uint8_t *junk = NULL;
+ char *base64 = NULL;
+
+ tt_int_op(0,==,ed25519_keypair_generate(&kp1, 0));
+ tt_int_op(0,==,ed25519_keypair_generate(&kp2, 0));
+
+ for (int i = 0; i <= 1; ++i) {
+ uint32_t flags = i ? CERT_FLAG_INCLUDE_SIGNING_KEY : 0;
+
+ cert[i] = tor_cert_create(&kp1, 5, &kp2.pubkey, now, 10000, flags);
+ tt_assert(cert[i]);
+
+ tt_assert(cert[i]->sig_bad == 0);
+ tt_assert(cert[i]->sig_ok == 1);
+ tt_assert(cert[i]->cert_expired == 0);
+ tt_assert(cert[i]->cert_valid == 1);
+ tt_int_op(cert[i]->cert_type, ==, 5);
+ tt_mem_op(cert[i]->signed_key.pubkey, ==, &kp2.pubkey.pubkey, 32);
+ tt_mem_op(cert[i]->signing_key.pubkey, ==, &kp1.pubkey.pubkey, 32);
+ tt_int_op(cert[i]->signing_key_included, ==, i);
+
+ tt_assert(cert[i]->encoded);
+ tt_int_op(cert[i]->encoded_len, ==, 104 + 36 * i);
+ tt_int_op(cert[i]->encoded[0], ==, 1);
+ tt_int_op(cert[i]->encoded[1], ==, 5);
+
+ parsed_cert[i] = tor_cert_parse(cert[i]->encoded, cert[i]->encoded_len);
+ tt_assert(parsed_cert[i]);
+ tt_int_op(cert[i]->encoded_len, ==, parsed_cert[i]->encoded_len);
+ tt_mem_op(cert[i]->encoded, ==, parsed_cert[i]->encoded,
+ cert[i]->encoded_len);
+ tt_assert(parsed_cert[i]->sig_bad == 0);
+ tt_assert(parsed_cert[i]->sig_ok == 0);
+ tt_assert(parsed_cert[i]->cert_expired == 0);
+ tt_assert(parsed_cert[i]->cert_valid == 0);
+
+ /* Expired */
+ tt_int_op(tor_cert_checksig(parsed_cert[i], &kp1.pubkey, now + 30000),
+ <, 0);
+ tt_assert(parsed_cert[i]->cert_expired == 1);
+ parsed_cert[i]->cert_expired = 0;
+
+ /* Wrong key */
+ tt_int_op(tor_cert_checksig(parsed_cert[i], &kp2.pubkey, now), <, 0);
+ tt_assert(parsed_cert[i]->sig_bad== 1);
+ parsed_cert[i]->sig_bad = 0;
+
+ /* Missing key */
+ int ok = tor_cert_checksig(parsed_cert[i], NULL, now);
+ tt_int_op(ok < 0, ==, i == 0);
+ tt_assert(parsed_cert[i]->sig_bad == 0);
+ tt_assert(parsed_cert[i]->sig_ok == (i != 0));
+ tt_assert(parsed_cert[i]->cert_valid == (i != 0));
+ parsed_cert[i]->sig_bad = 0;
+ parsed_cert[i]->sig_ok = 0;
+ parsed_cert[i]->cert_valid = 0;
+
+ /* Right key */
+ tt_int_op(tor_cert_checksig(parsed_cert[i], &kp1.pubkey, now), ==, 0);
+ tt_assert(parsed_cert[i]->sig_bad == 0);
+ tt_assert(parsed_cert[i]->sig_ok == 1);
+ tt_assert(parsed_cert[i]->cert_expired == 0);
+ tt_assert(parsed_cert[i]->cert_valid == 1);
+ }
+
+ /* Now try some junky certs. */
+ /* - Truncated */
+ nocert = tor_cert_parse(cert[0]->encoded, cert[0]->encoded_len-1);
+ tt_ptr_op(NULL, ==, nocert);
+
+ /* - First byte modified */
+ cert[0]->encoded[0] = 99;
+ nocert = tor_cert_parse(cert[0]->encoded, cert[0]->encoded_len);
+ tt_ptr_op(NULL, ==, nocert);
+ cert[0]->encoded[0] = 1;
+
+ /* - Extra byte at the end*/
+ junk = tor_malloc_zero(cert[0]->encoded_len + 1);
+ memcpy(junk, cert[0]->encoded, cert[0]->encoded_len);
+ nocert = tor_cert_parse(junk, cert[0]->encoded_len+1);
+ tt_ptr_op(NULL, ==, nocert);
+
+ /* - Multiple signing key instances */
+ tor_free(junk);
+ junk = tor_malloc_zero(104 + 36 * 2);
+ junk[0] = 1; /* version */
+ junk[1] = 5; /* cert type */
+ junk[6] = 1; /* key type */
+ junk[39] = 2; /* n_extensions */
+ junk[41] = 32; /* extlen */
+ junk[42] = 4; /* exttype */
+ junk[77] = 32; /* extlen */
+ junk[78] = 4; /* exttype */
+ nocert = tor_cert_parse(junk, 104 + 36 * 2);
+ tt_ptr_op(NULL, ==, nocert);
+
+ done:
+ tor_cert_free(cert[0]);
+ tor_cert_free(cert[1]);
+ tor_cert_free(parsed_cert[0]);
+ tor_cert_free(parsed_cert[1]);
+ tor_cert_free(nocert);
+ tor_free(junk);
+ tor_free(base64);
+}
+
+static void
+test_routerkeys_ed_key_create(void *arg)
+{
+ (void)arg;
+ tor_cert_t *cert = NULL;
+ ed25519_keypair_t *kp1 = NULL, *kp2 = NULL;
+ time_t now = time(NULL);
+
+ /* This is a simple alias for 'make a new keypair' */
+ kp1 = ed_key_new(NULL, 0, 0, 0, 0, &cert);
+ tt_assert(kp1);
+
+ /* Create a new certificate signed by kp1. */
+ kp2 = ed_key_new(kp1, INIT_ED_KEY_NEEDCERT, now, 3600, 4, &cert);
+ tt_assert(kp2);
+ tt_assert(cert);
+ tt_mem_op(&cert->signed_key, ==, &kp2->pubkey, sizeof(ed25519_public_key_t));
+ tt_assert(! cert->signing_key_included);
+
+ tt_int_op(cert->valid_until, >=, now);
+ tt_int_op(cert->valid_until, <=, now+7200);
+
+ /* Create a new key-including certificate signed by kp1 */
+ ed25519_keypair_free(kp2);
+ tor_cert_free(cert);
+ cert = NULL; kp2 = NULL;
+ kp2 = ed_key_new(kp1, (INIT_ED_KEY_NEEDCERT|
+ INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT),
+ now, 3600, 4, &cert);
+ tt_assert(kp2);
+ tt_assert(cert);
+ tt_assert(cert->signing_key_included);
+ tt_mem_op(&cert->signed_key, ==, &kp2->pubkey, sizeof(ed25519_public_key_t));
+ tt_mem_op(&cert->signing_key, ==, &kp1->pubkey,sizeof(ed25519_public_key_t));
+
+ done:
+ ed25519_keypair_free(kp1);
+ ed25519_keypair_free(kp2);
+ tor_cert_free(cert);
+}
+
+static void
+test_routerkeys_ed_key_init_basic(void *arg)
+{
+ (void) arg;
+
+ tor_cert_t *cert = NULL, *cert2 = NULL;
+ ed25519_keypair_t *kp1 = NULL, *kp2 = NULL, *kp3 = NULL;
+ time_t now = time(NULL);
+ char *fname1 = tor_strdup(get_fname("test_ed_key_1"));
+ char *fname2 = tor_strdup(get_fname("test_ed_key_2"));
+ struct stat st;
+
+ unlink(fname1);
+ unlink(fname2);
+
+ /* Fail to load a key that isn't there. */
+ kp1 = ed_key_init_from_file(fname1, 0, LOG_INFO, NULL, now, 0, 7, &cert);
+ tt_assert(kp1 == NULL);
+ tt_assert(cert == NULL);
+
+ /* Create the key if requested to do so. */
+ kp1 = ed_key_init_from_file(fname1, INIT_ED_KEY_CREATE, LOG_INFO,
+ NULL, now, 0, 7, &cert);
+ tt_assert(kp1 != NULL);
+ tt_assert(cert == NULL);
+ tt_int_op(stat(get_fname("test_ed_key_1_cert"), &st), <, 0);
+ tt_int_op(stat(get_fname("test_ed_key_1_secret_key"), &st), ==, 0);
+
+ /* Fail to load if we say we need a cert */
+ kp2 = ed_key_init_from_file(fname1, INIT_ED_KEY_NEEDCERT, LOG_INFO,
+ NULL, now, 0, 7, &cert);
+ tt_assert(kp2 == NULL);
+
+ /* Fail to load if we say the wrong key type */
+ kp2 = ed_key_init_from_file(fname1, 0, LOG_INFO,
+ NULL, now, 0, 6, &cert);
+ tt_assert(kp2 == NULL);
+
+ /* Load successfully if we're not picky, whether we say "create" or not. */
+ kp2 = ed_key_init_from_file(fname1, INIT_ED_KEY_CREATE, LOG_INFO,
+ NULL, now, 0, 7, &cert);
+ tt_assert(kp2 != NULL);
+ tt_assert(cert == NULL);
+ tt_mem_op(kp1, ==, kp2, sizeof(*kp1));
+ ed25519_keypair_free(kp2); kp2 = NULL;
+
+ kp2 = ed_key_init_from_file(fname1, 0, LOG_INFO,
+ NULL, now, 0, 7, &cert);
+ tt_assert(kp2 != NULL);
+ tt_assert(cert == NULL);
+ tt_mem_op(kp1, ==, kp2, sizeof(*kp1));
+ ed25519_keypair_free(kp2); kp2 = NULL;
+
+ /* Now create a key with a cert. */
+ kp2 = ed_key_init_from_file(fname2, (INIT_ED_KEY_CREATE|
+ INIT_ED_KEY_NEEDCERT),
+ LOG_INFO, kp1, now, 7200, 7, &cert);
+ tt_assert(kp2 != NULL);
+ tt_assert(cert != NULL);
+ tt_mem_op(kp1, !=, kp2, sizeof(*kp1));
+ tt_int_op(stat(get_fname("test_ed_key_2_cert"), &st), ==, 0);
+ tt_int_op(stat(get_fname("test_ed_key_2_secret_key"), &st), ==, 0);
+
+ tt_assert(cert->cert_valid == 1);
+ tt_mem_op(&cert->signed_key, ==, &kp2->pubkey, 32);
+
+ /* Now verify we can load the cert... */
+ kp3 = ed_key_init_from_file(fname2, (INIT_ED_KEY_CREATE|
+ INIT_ED_KEY_NEEDCERT),
+ LOG_INFO, kp1, now, 7200, 7, &cert2);
+ tt_mem_op(kp2, ==, kp3, sizeof(*kp2));
+ tt_mem_op(cert2->encoded, ==, cert->encoded, cert->encoded_len);
+ ed25519_keypair_free(kp3); kp3 = NULL;
+ tor_cert_free(cert2); cert2 = NULL;
+
+ /* ... even without create... */
+ kp3 = ed_key_init_from_file(fname2, INIT_ED_KEY_NEEDCERT,
+ LOG_INFO, kp1, now, 7200, 7, &cert2);
+ tt_mem_op(kp2, ==, kp3, sizeof(*kp2));
+ tt_mem_op(cert2->encoded, ==, cert->encoded, cert->encoded_len);
+ ed25519_keypair_free(kp3); kp3 = NULL;
+ tor_cert_free(cert2); cert2 = NULL;
+
+ /* ... but that we don't crash or anything if we say we don't want it. */
+ kp3 = ed_key_init_from_file(fname2, INIT_ED_KEY_NEEDCERT,
+ LOG_INFO, kp1, now, 7200, 7, NULL);
+ tt_mem_op(kp2, ==, kp3, sizeof(*kp2));
+ ed25519_keypair_free(kp3); kp3 = NULL;
+
+ /* Fail if we're told the wrong signing key */
+ kp3 = ed_key_init_from_file(fname2, INIT_ED_KEY_NEEDCERT,
+ LOG_INFO, kp2, now, 7200, 7, &cert2);
+ tt_assert(kp3 == NULL);
+ tt_assert(cert2 == NULL);
+
+ done:
+ ed25519_keypair_free(kp1);
+ ed25519_keypair_free(kp2);
+ ed25519_keypair_free(kp3);
+ tor_cert_free(cert);
+ tor_cert_free(cert2);
+ tor_free(fname1);
+ tor_free(fname2);
+}
+
+static void
+test_routerkeys_ed_key_init_split(void *arg)
+{
+ (void) arg;
+
+ tor_cert_t *cert = NULL;
+ ed25519_keypair_t *kp1 = NULL, *kp2 = NULL;
+ time_t now = time(NULL);
+ char *fname1 = tor_strdup(get_fname("test_ed_key_3"));
+ char *fname2 = tor_strdup(get_fname("test_ed_key_4"));
+ struct stat st;
+ const uint32_t flags = INIT_ED_KEY_SPLIT|INIT_ED_KEY_MISSING_SECRET_OK;
+
+ unlink(fname1);
+ unlink(fname2);
+
+ /* Can't load key that isn't there. */
+ kp1 = ed_key_init_from_file(fname1, flags, LOG_INFO, NULL, now, 0, 7, &cert);
+ tt_assert(kp1 == NULL);
+ tt_assert(cert == NULL);
+
+ /* Create a split key */
+ kp1 = ed_key_init_from_file(fname1, flags|INIT_ED_KEY_CREATE,
+ LOG_INFO, NULL, now, 0, 7, &cert);
+ tt_assert(kp1 != NULL);
+ tt_assert(cert == NULL);
+ tt_int_op(stat(get_fname("test_ed_key_3_cert"), &st), <, 0);
+ tt_int_op(stat(get_fname("test_ed_key_3_secret_key"), &st), ==, 0);
+ tt_int_op(stat(get_fname("test_ed_key_3_public_key"), &st), ==, 0);
+
+ /* Load it. */
+ kp2 = ed_key_init_from_file(fname1, flags|INIT_ED_KEY_CREATE,
+ LOG_INFO, NULL, now, 0, 7, &cert);
+ tt_assert(kp2 != NULL);
+ tt_assert(cert == NULL);
+ tt_mem_op(kp1, ==, kp2, sizeof(*kp2));
+ ed25519_keypair_free(kp2); kp2 = NULL;
+
+ /* Okay, try killing the secret key and loading it. */
+ unlink(get_fname("test_ed_key_3_secret_key"));
+ kp2 = ed_key_init_from_file(fname1, flags,
+ LOG_INFO, NULL, now, 0, 7, &cert);
+ tt_assert(kp2 != NULL);
+ tt_assert(cert == NULL);
+ tt_mem_op(&kp1->pubkey, ==, &kp2->pubkey, sizeof(kp2->pubkey));
+ tt_assert(tor_mem_is_zero((char*)kp2->seckey.seckey,
+ sizeof(kp2->seckey.seckey)));
+ ed25519_keypair_free(kp2); kp2 = NULL;
+
+ /* Even when we're told to "create", don't create if there's a public key */
+ kp2 = ed_key_init_from_file(fname1, flags|INIT_ED_KEY_CREATE,
+ LOG_INFO, NULL, now, 0, 7, &cert);
+ tt_assert(kp2 != NULL);
+ tt_assert(cert == NULL);
+ tt_mem_op(&kp1->pubkey, ==, &kp2->pubkey, sizeof(kp2->pubkey));
+ tt_assert(tor_mem_is_zero((char*)kp2->seckey.seckey,
+ sizeof(kp2->seckey.seckey)));
+ ed25519_keypair_free(kp2); kp2 = NULL;
+
+ /* Make sure we fail on a tag mismatch, though */
+ kp2 = ed_key_init_from_file(fname1, flags,
+ LOG_INFO, NULL, now, 0, 99, &cert);
+ tt_assert(kp2 == NULL);
+
+ done:
+ ed25519_keypair_free(kp1);
+ ed25519_keypair_free(kp2);
+ tor_cert_free(cert);
+ tor_free(fname1);
+ tor_free(fname2);
+}
+
+static void
+test_routerkeys_ed_keys_init_all(void *arg)
+{
+ (void)arg;
+ char *dir = tor_strdup(get_fname("test_ed_keys_init_all"));
+ or_options_t *options = tor_malloc_zero(sizeof(or_options_t));
+ time_t now = time(NULL);
+ ed25519_public_key_t id;
+ ed25519_keypair_t sign, auth;
+ tor_cert_t *link_cert = NULL;
+
+ get_options_mutable()->ORPort_set = 1;
+
+ crypto_pk_t *rsa = pk_generate(0);
+
+ set_server_identity_key(rsa);
+ set_client_identity_key(rsa);
+
+ router_initialize_tls_context();
+
+ options->SigningKeyLifetime = 30*86400;
+ options->TestingAuthKeyLifetime = 2*86400;
+ options->TestingLinkCertLifetime = 2*86400;
+ options->TestingSigningKeySlop = 2*86400;
+ options->TestingAuthKeySlop = 2*3600;
+ options->TestingLinkKeySlop = 2*3600;
+
+#ifdef _WIN32
+ mkdir(dir);
+ mkdir(get_fname("test_ed_keys_init_all/keys"));
+#else
+ mkdir(dir, 0700);
+ mkdir(get_fname("test_ed_keys_init_all/keys"), 0700);
+#endif
+
+ options->DataDirectory = dir;
+
+ tt_int_op(0, ==, load_ed_keys(options, now));
+ tt_int_op(0, ==, generate_ed_link_cert(options, now));
+ tt_assert(get_master_identity_key());
+ tt_assert(get_master_identity_key());
+ tt_assert(get_master_signing_keypair());
+ tt_assert(get_current_auth_keypair());
+ tt_assert(get_master_signing_key_cert());
+ tt_assert(get_current_link_cert_cert());
+ tt_assert(get_current_auth_key_cert());
+ memcpy(&id, get_master_identity_key(), sizeof(id));
+ memcpy(&sign, get_master_signing_keypair(), sizeof(sign));
+ memcpy(&auth, get_current_auth_keypair(), sizeof(auth));
+ link_cert = tor_cert_dup(get_current_link_cert_cert());
+
+ /* Call load_ed_keys again, but nothing has changed. */
+ tt_int_op(0, ==, load_ed_keys(options, now));
+ tt_int_op(0, ==, generate_ed_link_cert(options, now));
+ tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
+ tt_mem_op(&auth, ==, get_current_auth_keypair(), sizeof(auth));
+ tt_assert(tor_cert_eq(link_cert, get_current_link_cert_cert()));
+
+ /* Force a reload: we make new link/auth keys. */
+ routerkeys_free_all();
+ tt_int_op(0, ==, load_ed_keys(options, now));
+ tt_int_op(0, ==, generate_ed_link_cert(options, now));
+ tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
+ tt_assert(tor_cert_eq(link_cert, get_current_link_cert_cert()));
+ tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_assert(get_master_signing_key_cert());
+ tt_assert(get_current_link_cert_cert());
+ tt_assert(get_current_auth_key_cert());
+ tor_cert_free(link_cert);
+ link_cert = tor_cert_dup(get_current_link_cert_cert());
+ memcpy(&auth, get_current_auth_keypair(), sizeof(auth));
+
+ /* Force a link/auth-key regeneration by advancing time. */
+ tt_int_op(0, ==, load_ed_keys(options, now+3*86400));
+ tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400));
+ tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
+ tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert()));
+ tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_assert(get_master_signing_key_cert());
+ tt_assert(get_current_link_cert_cert());
+ tt_assert(get_current_auth_key_cert());
+ tor_cert_free(link_cert);
+ link_cert = tor_cert_dup(get_current_link_cert_cert());
+ memcpy(&auth, get_current_auth_keypair(), sizeof(auth));
+
+ /* Force a signing-key regeneration by advancing time. */
+ tt_int_op(0, ==, load_ed_keys(options, now+100*86400));
+ tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400));
+ tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, !=, get_master_signing_keypair(), sizeof(sign));
+ tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert()));
+ tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_assert(get_master_signing_key_cert());
+ tt_assert(get_current_link_cert_cert());
+ tt_assert(get_current_auth_key_cert());
+ memcpy(&sign, get_master_signing_keypair(), sizeof(sign));
+ tor_cert_free(link_cert);
+ link_cert = tor_cert_dup(get_current_link_cert_cert());
+ memcpy(&auth, get_current_auth_keypair(), sizeof(auth));
+
+ /* Demonstrate that we can start up with no secret identity key */
+ routerkeys_free_all();
+ unlink(get_fname("test_ed_keys_init_all/keys/"
+ "ed25519_master_id_secret_key"));
+ tt_int_op(0, ==, load_ed_keys(options, now));
+ tt_int_op(0, ==, generate_ed_link_cert(options, now));
+ tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
+ tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
+ tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert()));
+ tt_mem_op(&auth, !=, get_current_auth_keypair(), sizeof(auth));
+ tt_assert(get_master_signing_key_cert());
+ tt_assert(get_current_link_cert_cert());
+ tt_assert(get_current_auth_key_cert());
+
+ /* But we're in trouble if we have no id key and our signing key has
+ expired. */
+ log_global_min_severity_ = LOG_ERR; /* Suppress warnings.
+ * XXX (better way to do this)? */
+ routerkeys_free_all();
+ tt_int_op(-1, ==, load_ed_keys(options, now+200*86400));
+
+ done:
+ tor_free(dir);
+ tor_free(options);
+ tor_cert_free(link_cert);
+ routerkeys_free_all();
+}
+
+static void
+test_routerkeys_cross_certify_ntor(void *args)
+{
+ (void) args;
+
+ tor_cert_t *cert = NULL;
+ curve25519_keypair_t onion_keys;
+ ed25519_public_key_t master_key;
+ ed25519_public_key_t onion_check_key;
+ time_t now = time(NULL);
+ int sign;
+
+ tt_int_op(0, ==, ed25519_public_from_base64(&master_key,
+ "IamwritingthesetestsOnARainyAfternoonin2014"));
+ tt_int_op(0, ==, curve25519_keypair_generate(&onion_keys, 0));
+ cert = make_ntor_onion_key_crosscert(&onion_keys,
+ &master_key,
+ now, 10000,
+ &sign);
+ tt_assert(cert);
+ tt_assert(sign == 0 || sign == 1);
+ tt_int_op(cert->cert_type, ==, CERT_TYPE_ONION_ID);
+ tt_int_op(1, ==, ed25519_pubkey_eq(&cert->signed_key, &master_key));
+ tt_int_op(0, ==, ed25519_public_key_from_curve25519_public_key(
+ &onion_check_key, &onion_keys.pubkey, sign));
+ tt_int_op(0, ==, tor_cert_checksig(cert, &onion_check_key, now));
+
+ done:
+ tor_cert_free(cert);
+}
+
+static void
+test_routerkeys_cross_certify_tap(void *args)
+{
+ (void)args;
+ uint8_t *cc = NULL;
+ int cc_len;
+ ed25519_public_key_t master_key;
+ crypto_pk_t *onion_key = pk_generate(2), *id_key = pk_generate(1);
+ char digest[20];
+ char buf[128];
+ int n;
+
+ tt_int_op(0, ==, ed25519_public_from_base64(&master_key,
+ "IAlreadyWroteTestsForRouterdescsUsingTheseX"));
+
+ cc = make_tap_onion_key_crosscert(onion_key,
+ &master_key,
+ id_key, &cc_len);
+ tt_assert(cc);
+ tt_assert(cc_len);
+
+ n = crypto_pk_public_checksig(onion_key, buf, sizeof(buf),
+ (char*)cc, cc_len);
+ tt_int_op(n,>,0);
+ tt_int_op(n,==,52);
+
+ crypto_pk_get_digest(id_key, digest);
+ tt_mem_op(buf,==,digest,20);
+ tt_mem_op(buf+20,==,master_key.pubkey,32);
+
+ tt_int_op(0, ==, check_tap_onion_key_crosscert(cc, cc_len,
+ onion_key, &master_key, (uint8_t*)digest));
+
+ done:
+ tor_free(cc);
+ crypto_pk_free(id_key);
+ crypto_pk_free(onion_key);
+}
+
#define TEST(name, flags) \
{ #name , test_routerkeys_ ## name, (flags), NULL, NULL }
struct testcase_t routerkeys_tests[] = {
TEST(write_fingerprint, TT_FORK),
+ TEST(ed_certs, TT_FORK),
+ TEST(ed_key_create, TT_FORK),
+ TEST(ed_key_init_basic, TT_FORK),
+ TEST(ed_key_init_split, TT_FORK),
+ TEST(ed_keys_init_all, TT_FORK),
+ TEST(cross_certify_ntor, 0),
+ TEST(cross_certify_tap, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_scheduler.c b/src/test/test_scheduler.c
index 73a422088f..79a5534505 100644
--- a/src/test/test_scheduler.c
+++ b/src/test/test_scheduler.c
@@ -38,9 +38,9 @@ static circuitmux_t *mock_ccm_tgt_1 = NULL;
static circuitmux_t *mock_ccm_tgt_2 = NULL;
static circuitmux_t *mock_cgp_tgt_1 = NULL;
-static const circuitmux_policy_t *mock_cgp_val_1 = NULL;
+static circuitmux_policy_t *mock_cgp_val_1 = NULL;
static circuitmux_t *mock_cgp_tgt_2 = NULL;
-static const circuitmux_policy_t *mock_cgp_val_2 = NULL;
+static circuitmux_policy_t *mock_cgp_val_2 = NULL;
static int scheduler_compare_channels_mock_ctr = 0;
static int scheduler_run_mock_ctr = 0;
@@ -457,13 +457,19 @@ test_scheduler_compare_channels(void *arg)
/* Configure circuitmux_get_policy() mock */
mock_cgp_tgt_1 = cm1;
+ mock_cgp_tgt_2 = cm2;
+
/*
* This is to test the different-policies case, which uses the policy
* cast to an intptr_t as an arbitrary but definite thing to compare.
*/
- mock_cgp_val_1 = (const circuitmux_policy_t *)(1);
- mock_cgp_tgt_2 = cm2;
- mock_cgp_val_2 = (const circuitmux_policy_t *)(2);
+ mock_cgp_val_1 = tor_malloc_zero(16);
+ mock_cgp_val_2 = tor_malloc_zero(16);
+ if ( ((intptr_t) mock_cgp_val_1) > ((intptr_t) mock_cgp_val_2) ) {
+ void *tmp = mock_cgp_val_1;
+ mock_cgp_val_1 = mock_cgp_val_2;
+ mock_cgp_val_2 = tmp;
+ }
MOCK(circuitmux_get_policy, circuitmux_get_policy_mock);
@@ -483,6 +489,7 @@ test_scheduler_compare_channels(void *arg)
tt_int_op(result, ==, 1);
/* Distinct channels, same policy */
+ tor_free(mock_cgp_val_2);
mock_cgp_val_2 = mock_cgp_val_1;
result = scheduler_compare_channels(&c1, &c2);
tt_int_op(result, ==, -1);
@@ -497,13 +504,17 @@ test_scheduler_compare_channels(void *arg)
UNMOCK(circuitmux_get_policy);
mock_cgp_tgt_1 = NULL;
- mock_cgp_val_1 = NULL;
mock_cgp_tgt_2 = NULL;
- mock_cgp_val_2 = NULL;
tor_free(cm1);
tor_free(cm2);
+ if (mock_cgp_val_1 != mock_cgp_val_2)
+ tor_free(mock_cgp_val_1);
+ tor_free(mock_cgp_val_2);
+ mock_cgp_val_1 = NULL;
+ mock_cgp_val_2 = NULL;
+
return;
}
diff --git a/src/test/test_util.c b/src/test/test_util.c
index 51e9e761ab..b0366db37f 100644
--- a/src/test/test_util.c
+++ b/src/test/test_util.c
@@ -4111,26 +4111,6 @@ test_util_laplace(void *arg)
;
}
-static void
-test_util_strclear(void *arg)
-{
- static const char *vals[] = { "", "a", "abcdef", "abcdefgh", NULL };
- int i;
- char *v = NULL;
- (void)arg;
-
- for (i = 0; vals[i]; ++i) {
- size_t n;
- v = tor_strdup(vals[i]);
- n = strlen(v);
- tor_strclear(v);
- tt_assert(tor_mem_is_zero(v, n+1));
- tor_free(v);
- }
- done:
- tor_free(v);
-}
-
#define UTIL_LEGACY(name) \
{ #name, test_util_ ## name , 0, NULL, NULL }
@@ -4322,6 +4302,34 @@ test_util_ipv4_validation(void *arg)
return;
}
+static void
+test_util_writepid(void *arg)
+{
+ (void) arg;
+
+ char *contents = NULL;
+ const char *fname = get_fname("tmp_pid");
+ unsigned long pid;
+ char c;
+
+ write_pidfile(fname);
+
+ contents = read_file_to_str(fname, 0, NULL);
+ tt_assert(contents);
+
+ int n = sscanf(contents, "%lu\n%c", &pid, &c);
+ tt_int_op(n, OP_EQ, 1);
+
+#ifdef _WIN32
+ tt_uint_op(pid, OP_EQ, _getpid());
+#else
+ tt_uint_op(pid, OP_EQ, getpid());
+#endif
+
+ done:
+ tor_free(contents);
+}
+
struct testcase_t util_tests[] = {
UTIL_LEGACY(time),
UTIL_TEST(parse_http_time, 0),
@@ -4348,7 +4356,6 @@ struct testcase_t util_tests[] = {
UTIL_LEGACY(di_ops),
UTIL_TEST(round_to_next_multiple_of, 0),
UTIL_TEST(laplace, 0),
- UTIL_TEST(strclear, 0),
UTIL_TEST(find_str_at_start_of_line, 0),
UTIL_TEST(string_is_C_identifier, 0),
UTIL_TEST(asprintf, 0),
@@ -4389,6 +4396,7 @@ struct testcase_t util_tests[] = {
UTIL_TEST(max_mem, 0),
UTIL_TEST(hostname_validation, 0),
UTIL_TEST(ipv4_validation, 0),
+ UTIL_TEST(writepid, 0),
END_OF_TESTCASES
};
diff --git a/src/test/test_util_slow.c b/src/test/test_util_slow.c
index a597ef3cbc..dcd0c9af36 100644
--- a/src/test/test_util_slow.c
+++ b/src/test/test_util_slow.c
@@ -107,9 +107,11 @@ run_util_spawn_background(const char *argv[], const char *expected_out,
#ifdef _WIN32
tt_assert(process_handle->stdout_pipe != INVALID_HANDLE_VALUE);
tt_assert(process_handle->stderr_pipe != INVALID_HANDLE_VALUE);
+ tt_assert(process_handle->stdin_pipe != INVALID_HANDLE_VALUE);
#else
tt_assert(process_handle->stdout_pipe >= 0);
tt_assert(process_handle->stderr_pipe >= 0);
+ tt_assert(process_handle->stdin_pipe >= 0);
#endif
/* Check stdout */
diff --git a/src/test/test_workqueue.c b/src/test/test_workqueue.c
index aaff5069be..77053b0760 100644
--- a/src/test/test_workqueue.c
+++ b/src/test/test_workqueue.c
@@ -18,6 +18,8 @@
#include <event.h>
#endif
+#define MAX_INFLIGHT (1<<16)
+
static int opt_verbose = 0;
static int opt_n_threads = 8;
static int opt_n_items = 10000;
@@ -348,7 +350,7 @@ main(int argc, char **argv)
}
if (opt_n_threads < 1 ||
opt_n_items < 1 || opt_n_inflight < 1 || opt_n_lowwater < 0 ||
- opt_n_cancel > opt_n_inflight ||
+ opt_n_cancel > opt_n_inflight || opt_n_inflight > MAX_INFLIGHT ||
opt_ratio_rsa < 0) {
help();
return 1;
@@ -356,7 +358,7 @@ main(int argc, char **argv)
init_logging(1);
crypto_global_init(1, NULL, NULL);
- crypto_seed_rng(1);
+ crypto_seed_rng();
rq = replyqueue_new(as_flags);
tor_assert(rq);
diff --git a/src/test/test_zero_length_keys.sh.in b/src/test/test_zero_length_keys.sh.in
new file mode 100644
index 0000000000..d1492d5e20
--- /dev/null
+++ b/src/test/test_zero_length_keys.sh.in
@@ -0,0 +1,10 @@
+#!@SHELL@
+# Check that tor regenerates keys when key files are zero-length
+
+exitcode=0
+
+@SHELL@ @abs_top_srcdir@/src/test/zero_length_keys.sh "@builddir@/src/or/tor" -z || exitcode=1
+@SHELL@ @abs_top_srcdir@/src/test/zero_length_keys.sh "@builddir@/src/or/tor" -d || exitcode=1
+@SHELL@ @abs_top_srcdir@/src/test/zero_length_keys.sh "@builddir@/src/or/tor" -e || exitcode=1
+
+exit ${exitcode}
diff --git a/src/test/testing_common.c b/src/test/testing_common.c
index 403c83bdd2..7f387c0b3d 100644
--- a/src/test/testing_common.c
+++ b/src/test/testing_common.c
@@ -269,8 +269,8 @@ main(int c, const char **v)
printf("Can't initialize crypto subsystem; exiting.\n");
return 1;
}
- crypto_set_tls_dh_prime(NULL);
- crypto_seed_rng(1);
+ crypto_set_tls_dh_prime();
+ crypto_seed_rng();
rep_hist_init();
network_init();
setup_directory();
diff --git a/src/test/zero_length_keys.sh b/src/test/zero_length_keys.sh
index 2fd11d38bd..3c61f8d465 100755
--- a/src/test/zero_length_keys.sh
+++ b/src/test/zero_length_keys.sh
@@ -3,13 +3,13 @@
# Test for bug #13111 - Tor fails to start if onion keys are zero length
#
# Usage:
-# ./zero_length_keys.sh
+# ./zero_length_keys.sh PATH_TO_TOR
# Run all the tests below
-# ./zero_length_keys.sh -z
+# ./zero_length_keys.sh PATH_TO_TOR -z
# Check tor will launch and regenerate zero-length keys
-# ./zero_length_keys.sh -d
+# ./zero_length_keys.sh PATH_TO_TOR -d
# Check tor regenerates deleted keys (existing behaviour)
-# ./zero_length_keys.sh -e
+# ./zero_length_keys.sh PATH_TO_TOR -e
# Check tor does not overwrite existing keys (existing behaviour)
#
# Exit Statuses:
@@ -19,10 +19,16 @@
# 3: a command failed - the test could not be completed
#
-if [ $# -lt 1 ]; then
+if [ $# -eq 0 ] || [ ! -f ${1} ] || [ ! -x ${1} ]; then
+ echo "Usage: ${0} PATH_TO_TOR [-z|-d|-e]"
+ exit 1
+elif [ $# -eq 1 ]; then
echo "Testing that tor correctly handles zero-length keys"
- "$0" -z && "$0" -d && "$0" -e
+ "$0" "${1}" -z && "$0" "${1}" -d && "$0" "${1}" -e
exit $?
+else #[$# -gt 1 ]; then
+ TOR_BINARY="${1}"
+ shift
fi
DATA_DIR=`mktemp -d -t tor_zero_length_keys.XXXXXX`
@@ -40,7 +46,7 @@ touch "$DATA_DIR"/empty_torrc
# DisableNetwork means that the ORPort won't actually be opened.
# 'ExitRelay 0' suppresses a warning.
-TOR="./src/or/tor --hush --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f $DATA_DIR/empty_torrc"
+TOR="${TOR_BINARY} --hush --DisableNetwork 1 --ShutdownWaitLength 0 --ORPort 12345 --ExitRelay 0 -f $DATA_DIR/empty_torrc"
if [ -s "$DATA_DIR"/keys/secret_id_key ] && [ -s "$DATA_DIR"/keys/secret_onion_key ] &&
[ -s "$DATA_DIR"/keys/secret_onion_key_ntor ]; then
diff --git a/src/tools/include.am b/src/tools/include.am
index 54b150a80c..5d778c1143 100644
--- a/src/tools/include.am
+++ b/src/tools/include.am
@@ -1,21 +1,45 @@
bin_PROGRAMS+= src/tools/tor-resolve src/tools/tor-gencert
noinst_PROGRAMS+= src/tools/tor-checkkey
+if COVERAGE_ENABLED
+noinst_PROGRAMS+= src/tools/tor-cov-resolve src/tools/tor-cov-gencert
+endif
+
src_tools_tor_resolve_SOURCES = src/tools/tor-resolve.c
src_tools_tor_resolve_LDFLAGS =
src_tools_tor_resolve_LDADD = src/common/libor.a @TOR_LIB_MATH@ @TOR_LIB_WS32@
+if COVERAGE_ENABLED
+src_tools_tor_cov_resolve_SOURCES = src/tools/tor-resolve.c
+src_tools_tor_cov_resolve_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_tools_tor_cov_resolve_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_tools_tor_cov_resolve_LDADD = src/common/libor-testing.a \
+ @TOR_LIB_MATH@ @TOR_LIB_WS32@
+endif
+
src_tools_tor_gencert_SOURCES = src/tools/tor-gencert.c
src_tools_tor_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
src_tools_tor_gencert_LDADD = src/common/libor.a src/common/libor-crypto.a \
- $(LIBDONNA) \
+ $(LIBDONNA) \
+ @TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
+ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
+
+if COVERAGE_ENABLED
+src_tools_tor_cov_gencert_SOURCES = src/tools/tor-gencert.c
+src_tools_tor_cov_gencert_CPPFLAGS = $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
+src_tools_tor_cov_gencert_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
+src_tools_tor_cov_gencert_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
+src_tools_tor_cov_gencert_LDADD = src/common/libor-testing.a \
+ src/common/libor-crypto-testing.a \
+ $(LIBDONNA) \
@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
+endif
src_tools_tor_checkkey_SOURCES = src/tools/tor-checkkey.c
src_tools_tor_checkkey_LDFLAGS = @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@
src_tools_tor_checkkey_LDADD = src/common/libor.a src/common/libor-crypto.a \
- $(LIBDONNA) \
+ $(LIBDONNA) \
@TOR_LIB_MATH@ @TOR_ZLIB_LIBS@ @TOR_OPENSSL_LIBS@ \
@TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@
diff --git a/src/tools/tor-checkkey.c b/src/tools/tor-checkkey.c
index e404b682cf..ed68bdf52c 100644
--- a/src/tools/tor-checkkey.c
+++ b/src/tools/tor-checkkey.c
@@ -7,7 +7,7 @@
#include <stdlib.h>
#include "crypto.h"
#include "torlog.h"
-#include "../common/util.h"
+#include "util.h"
#include "compat.h"
#include <openssl/bn.h>
#include <openssl/rsa.h>
diff --git a/src/tools/tor-gencert.c b/src/tools/tor-gencert.c
index c599822e07..7660be6230 100644
--- a/src/tools/tor-gencert.c
+++ b/src/tools/tor-gencert.c
@@ -28,8 +28,8 @@
#endif
#include "compat.h"
-#include "../common/util.h"
-#include "../common/torlog.h"
+#include "util.h"
+#include "torlog.h"
#include "crypto.h"
#include "address.h"
@@ -186,8 +186,7 @@ parse_commandline(int argc, char **argv)
return 1;
in.s_addr = htonl(addr);
tor_inet_ntoa(&in, b, sizeof(b));
- address = tor_malloc(INET_NTOA_BUF_LEN+32);
- tor_snprintf(address, INET_NTOA_BUF_LEN+32, "%s:%d", b, (int)port);
+ tor_asprintf(&address, "%s:%d", b, (int)port);
} else if (!strcmp(argv[i], "--create-identity-key")) {
make_new_id = 1;
} else if (!strcmp(argv[i], "--passphrase-fd")) {
@@ -486,7 +485,8 @@ generate_certificate(void)
EVP_PKEY_get1_RSA(signing_key),
RSA_PKCS1_PADDING);
signed_len = strlen(buf);
- base64_encode(buf+signed_len, sizeof(buf)-signed_len, signature, r);
+ base64_encode(buf+signed_len, sizeof(buf)-signed_len, signature, r,
+ BASE64_ENCODE_MULTILINE);
strlcat(buf,
"-----END ID SIGNATURE-----\n"
@@ -501,7 +501,8 @@ generate_certificate(void)
RSA_PKCS1_PADDING);
strlcat(buf, "-----BEGIN SIGNATURE-----\n", sizeof(buf));
signed_len = strlen(buf);
- base64_encode(buf+signed_len, sizeof(buf)-signed_len, signature, r);
+ base64_encode(buf+signed_len, sizeof(buf)-signed_len, signature, r,
+ BASE64_ENCODE_MULTILINE);
strlcat(buf, "-----END SIGNATURE-----\n", sizeof(buf));
if (!(f = fopen(certificate_file, "w"))) {
@@ -532,7 +533,7 @@ main(int argc, char **argv)
fprintf(stderr, "Couldn't initialize crypto library.\n");
return 1;
}
- if (crypto_seed_rng(1)) {
+ if (crypto_seed_rng()) {
fprintf(stderr, "Couldn't seed RNG.\n");
goto done;
}
@@ -564,6 +565,7 @@ main(int argc, char **argv)
tor_free(identity_key_file);
tor_free(signing_key_file);
tor_free(certificate_file);
+ tor_free(address);
crypto_global_cleanup();
return r;
diff --git a/src/tools/tor-resolve.c b/src/tools/tor-resolve.c
index 04815a63f7..19e3a554fa 100644
--- a/src/tools/tor-resolve.c
+++ b/src/tools/tor-resolve.c
@@ -5,9 +5,9 @@
#include "orconfig.h"
#include "compat.h"
-#include "../common/util.h"
+#include "util.h"
#include "address.h"
-#include "../common/torlog.h"
+#include "torlog.h"
#include "sandbox.h"
#include <stdio.h>
diff --git a/src/trunnel/ed25519_cert.c b/src/trunnel/ed25519_cert.c
new file mode 100644
index 0000000000..6931eae722
--- /dev/null
+++ b/src/trunnel/ed25519_cert.c
@@ -0,0 +1,887 @@
+/* ed25519_cert.c -- generated by Trunnel v1.4.1.
+ * https://gitweb.torproject.org/trunnel.git
+ * You probably shouldn't edit this file.
+ */
+#include <stdlib.h>
+#include "trunnel-impl.h"
+
+#include "ed25519_cert.h"
+
+#define TRUNNEL_SET_ERROR_CODE(obj) \
+ do { \
+ (obj)->trunnel_error_code_ = 1; \
+ } while (0)
+
+#if defined(__COVERITY__) || defined(__clang_analyzer__)
+/* If we're runnning a static analysis tool, we don't want it to complain
+ * that some of our remaining-bytes checks are dead-code. */
+int edcert_deadcode_dummy__ = 0;
+#define OR_DEADCODE_DUMMY || edcert_deadcode_dummy__
+#else
+#define OR_DEADCODE_DUMMY
+#endif
+
+#define CHECK_REMAINING(nbytes, label) \
+ do { \
+ if (remaining < (nbytes) OR_DEADCODE_DUMMY) { \
+ goto label; \
+ } \
+ } while (0)
+
+ed25519_cert_extension_t *
+ed25519_cert_extension_new(void)
+{
+ ed25519_cert_extension_t *val = trunnel_calloc(1, sizeof(ed25519_cert_extension_t));
+ if (NULL == val)
+ return NULL;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+ed25519_cert_extension_clear(ed25519_cert_extension_t *obj)
+{
+ (void) obj;
+ TRUNNEL_DYNARRAY_WIPE(&obj->un_unparsed);
+ TRUNNEL_DYNARRAY_CLEAR(&obj->un_unparsed);
+}
+
+void
+ed25519_cert_extension_free(ed25519_cert_extension_t *obj)
+{
+ if (obj == NULL)
+ return;
+ ed25519_cert_extension_clear(obj);
+ trunnel_memwipe(obj, sizeof(ed25519_cert_extension_t));
+ trunnel_free_(obj);
+}
+
+uint16_t
+ed25519_cert_extension_get_ext_length(ed25519_cert_extension_t *inp)
+{
+ return inp->ext_length;
+}
+int
+ed25519_cert_extension_set_ext_length(ed25519_cert_extension_t *inp, uint16_t val)
+{
+ inp->ext_length = val;
+ return 0;
+}
+uint8_t
+ed25519_cert_extension_get_ext_type(ed25519_cert_extension_t *inp)
+{
+ return inp->ext_type;
+}
+int
+ed25519_cert_extension_set_ext_type(ed25519_cert_extension_t *inp, uint8_t val)
+{
+ inp->ext_type = val;
+ return 0;
+}
+uint8_t
+ed25519_cert_extension_get_ext_flags(ed25519_cert_extension_t *inp)
+{
+ return inp->ext_flags;
+}
+int
+ed25519_cert_extension_set_ext_flags(ed25519_cert_extension_t *inp, uint8_t val)
+{
+ inp->ext_flags = val;
+ return 0;
+}
+size_t
+ed25519_cert_extension_getlen_un_signing_key(const ed25519_cert_extension_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+ed25519_cert_extension_get_un_signing_key(const ed25519_cert_extension_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->un_signing_key[idx];
+}
+
+int
+ed25519_cert_extension_set_un_signing_key(ed25519_cert_extension_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->un_signing_key[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+ed25519_cert_extension_getarray_un_signing_key(ed25519_cert_extension_t *inp)
+{
+ return inp->un_signing_key;
+}
+size_t
+ed25519_cert_extension_getlen_un_unparsed(const ed25519_cert_extension_t *inp)
+{
+ return TRUNNEL_DYNARRAY_LEN(&inp->un_unparsed);
+}
+
+uint8_t
+ed25519_cert_extension_get_un_unparsed(ed25519_cert_extension_t *inp, size_t idx)
+{
+ return TRUNNEL_DYNARRAY_GET(&inp->un_unparsed, idx);
+}
+
+int
+ed25519_cert_extension_set_un_unparsed(ed25519_cert_extension_t *inp, size_t idx, uint8_t elt)
+{
+ TRUNNEL_DYNARRAY_SET(&inp->un_unparsed, idx, elt);
+ return 0;
+}
+int
+ed25519_cert_extension_add_un_unparsed(ed25519_cert_extension_t *inp, uint8_t elt)
+{
+ TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->un_unparsed, elt, {});
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+
+uint8_t *
+ed25519_cert_extension_getarray_un_unparsed(ed25519_cert_extension_t *inp)
+{
+ return inp->un_unparsed.elts_;
+}
+int
+ed25519_cert_extension_setlen_un_unparsed(ed25519_cert_extension_t *inp, size_t newlen)
+{
+ uint8_t *newptr;
+ newptr = trunnel_dynarray_setlen(&inp->un_unparsed.allocated_,
+ &inp->un_unparsed.n_, inp->un_unparsed.elts_, newlen,
+ sizeof(inp->un_unparsed.elts_[0]), (trunnel_free_fn_t) NULL,
+ &inp->trunnel_error_code_);
+ if (newptr == NULL)
+ goto trunnel_alloc_failed;
+ inp->un_unparsed.elts_ = newptr;
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+const char *
+ed25519_cert_extension_check(const ed25519_cert_extension_t *obj)
+{
+ if (obj == NULL)
+ return "Object was NULL";
+ if (obj->trunnel_error_code_)
+ return "A set function failed on this object";
+ switch (obj->ext_type) {
+
+ case CERTEXT_SIGNED_WITH_KEY:
+ break;
+
+ default:
+ break;
+ }
+ return NULL;
+}
+
+ssize_t
+ed25519_cert_extension_encoded_len(const ed25519_cert_extension_t *obj)
+{
+ ssize_t result = 0;
+
+ if (NULL != ed25519_cert_extension_check(obj))
+ return -1;
+
+
+ /* Length of u16 ext_length */
+ result += 2;
+
+ /* Length of u8 ext_type */
+ result += 1;
+
+ /* Length of u8 ext_flags */
+ result += 1;
+ switch (obj->ext_type) {
+
+ case CERTEXT_SIGNED_WITH_KEY:
+
+ /* Length of u8 un_signing_key[32] */
+ result += 32;
+ break;
+
+ default:
+
+ /* Length of u8 un_unparsed[] */
+ result += TRUNNEL_DYNARRAY_LEN(&obj->un_unparsed);
+ break;
+ }
+ return result;
+}
+int
+ed25519_cert_extension_clear_errors(ed25519_cert_extension_t *obj)
+{
+ int r = obj->trunnel_error_code_;
+ obj->trunnel_error_code_ = 0;
+ return r;
+}
+ssize_t
+ed25519_cert_extension_encode(uint8_t *output, const size_t avail, const ed25519_cert_extension_t *obj)
+{
+ ssize_t result = 0;
+ size_t written = 0;
+ uint8_t *ptr = output;
+ const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ const ssize_t encoded_len = ed25519_cert_extension_encoded_len(obj);
+#endif
+
+ uint8_t *backptr_ext_length = NULL;
+
+ if (NULL != (msg = ed25519_cert_extension_check(obj)))
+ goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ trunnel_assert(encoded_len >= 0);
+#endif
+
+ /* Encode u16 ext_length */
+ backptr_ext_length = ptr;
+ trunnel_assert(written <= avail);
+ if (avail - written < 2)
+ goto truncated;
+ trunnel_set_uint16(ptr, trunnel_htons(obj->ext_length));
+ written += 2; ptr += 2;
+
+ /* Encode u8 ext_type */
+ trunnel_assert(written <= avail);
+ if (avail - written < 1)
+ goto truncated;
+ trunnel_set_uint8(ptr, (obj->ext_type));
+ written += 1; ptr += 1;
+
+ /* Encode u8 ext_flags */
+ trunnel_assert(written <= avail);
+ if (avail - written < 1)
+ goto truncated;
+ trunnel_set_uint8(ptr, (obj->ext_flags));
+ written += 1; ptr += 1;
+ {
+ size_t written_before_union = written;
+
+ /* Encode union un[ext_type] */
+ trunnel_assert(written <= avail);
+ switch (obj->ext_type) {
+
+ case CERTEXT_SIGNED_WITH_KEY:
+
+ /* Encode u8 un_signing_key[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->un_signing_key, 32);
+ written += 32; ptr += 32;
+ break;
+
+ default:
+
+ /* Encode u8 un_unparsed[] */
+ {
+ size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->un_unparsed);
+ trunnel_assert(written <= avail);
+ if (avail - written < elt_len)
+ goto truncated;
+ memcpy(ptr, obj->un_unparsed.elts_, elt_len);
+ written += elt_len; ptr += elt_len;
+ }
+ break;
+ }
+ /* Write the length field back to ext_length */
+ trunnel_assert(written >= written_before_union);
+#if UINT16_MAX < SIZE_MAX
+ if (written - written_before_union > UINT16_MAX)
+ goto check_failed;
+#endif
+ trunnel_set_uint16(backptr_ext_length, trunnel_htons(written - written_before_union));
+ }
+
+
+ trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ {
+ trunnel_assert(encoded_len >= 0);
+ trunnel_assert((size_t)encoded_len == written);
+ }
+
+#endif
+
+ return written;
+
+ truncated:
+ result = -2;
+ goto fail;
+ check_failed:
+ (void)msg;
+ result = -1;
+ goto fail;
+ fail:
+ trunnel_assert(result < 0);
+ return result;
+}
+
+/** As ed25519_cert_extension_parse(), but do not allocate the output
+ * object.
+ */
+static ssize_t
+ed25519_cert_extension_parse_into(ed25519_cert_extension_t *obj, const uint8_t *input, const size_t len_in)
+{
+ const uint8_t *ptr = input;
+ size_t remaining = len_in;
+ ssize_t result = 0;
+ (void)result;
+
+ /* Parse u16 ext_length */
+ CHECK_REMAINING(2, truncated);
+ obj->ext_length = trunnel_ntohs(trunnel_get_uint16(ptr));
+ remaining -= 2; ptr += 2;
+
+ /* Parse u8 ext_type */
+ CHECK_REMAINING(1, truncated);
+ obj->ext_type = (trunnel_get_uint8(ptr));
+ remaining -= 1; ptr += 1;
+
+ /* Parse u8 ext_flags */
+ CHECK_REMAINING(1, truncated);
+ obj->ext_flags = (trunnel_get_uint8(ptr));
+ remaining -= 1; ptr += 1;
+ {
+ size_t remaining_after;
+ CHECK_REMAINING(obj->ext_length, truncated);
+ remaining_after = remaining - obj->ext_length;
+ remaining = obj->ext_length;
+
+ /* Parse union un[ext_type] */
+ switch (obj->ext_type) {
+
+ case CERTEXT_SIGNED_WITH_KEY:
+
+ /* Parse u8 un_signing_key[32] */
+ CHECK_REMAINING(32, fail);
+ memcpy(obj->un_signing_key, ptr, 32);
+ remaining -= 32; ptr += 32;
+ break;
+
+ default:
+
+ /* Parse u8 un_unparsed[] */
+ TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->un_unparsed, remaining, {});
+ obj->un_unparsed.n_ = remaining;
+ memcpy(obj->un_unparsed.elts_, ptr, remaining);
+ ptr += remaining; remaining -= remaining;
+ break;
+ }
+ if (remaining != 0)
+ goto fail;
+ remaining = remaining_after;
+ }
+ trunnel_assert(ptr + remaining == input + len_in);
+ return len_in - remaining;
+
+ truncated:
+ return -2;
+ trunnel_alloc_failed:
+ return -1;
+ fail:
+ result = -1;
+ return result;
+}
+
+ssize_t
+ed25519_cert_extension_parse(ed25519_cert_extension_t **output, const uint8_t *input, const size_t len_in)
+{
+ ssize_t result;
+ *output = ed25519_cert_extension_new();
+ if (NULL == *output)
+ return -1;
+ result = ed25519_cert_extension_parse_into(*output, input, len_in);
+ if (result < 0) {
+ ed25519_cert_extension_free(*output);
+ *output = NULL;
+ }
+ return result;
+}
+ed25519_cert_t *
+ed25519_cert_new(void)
+{
+ ed25519_cert_t *val = trunnel_calloc(1, sizeof(ed25519_cert_t));
+ if (NULL == val)
+ return NULL;
+ val->version = 1;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+ed25519_cert_clear(ed25519_cert_t *obj)
+{
+ (void) obj;
+ {
+
+ unsigned idx;
+ for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) {
+ ed25519_cert_extension_free(TRUNNEL_DYNARRAY_GET(&obj->ext, idx));
+ }
+ }
+ TRUNNEL_DYNARRAY_WIPE(&obj->ext);
+ TRUNNEL_DYNARRAY_CLEAR(&obj->ext);
+}
+
+void
+ed25519_cert_free(ed25519_cert_t *obj)
+{
+ if (obj == NULL)
+ return;
+ ed25519_cert_clear(obj);
+ trunnel_memwipe(obj, sizeof(ed25519_cert_t));
+ trunnel_free_(obj);
+}
+
+uint8_t
+ed25519_cert_get_version(ed25519_cert_t *inp)
+{
+ return inp->version;
+}
+int
+ed25519_cert_set_version(ed25519_cert_t *inp, uint8_t val)
+{
+ if (! ((val == 1))) {
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+ }
+ inp->version = val;
+ return 0;
+}
+uint8_t
+ed25519_cert_get_cert_type(ed25519_cert_t *inp)
+{
+ return inp->cert_type;
+}
+int
+ed25519_cert_set_cert_type(ed25519_cert_t *inp, uint8_t val)
+{
+ inp->cert_type = val;
+ return 0;
+}
+uint32_t
+ed25519_cert_get_exp_field(ed25519_cert_t *inp)
+{
+ return inp->exp_field;
+}
+int
+ed25519_cert_set_exp_field(ed25519_cert_t *inp, uint32_t val)
+{
+ inp->exp_field = val;
+ return 0;
+}
+uint8_t
+ed25519_cert_get_cert_key_type(ed25519_cert_t *inp)
+{
+ return inp->cert_key_type;
+}
+int
+ed25519_cert_set_cert_key_type(ed25519_cert_t *inp, uint8_t val)
+{
+ inp->cert_key_type = val;
+ return 0;
+}
+size_t
+ed25519_cert_getlen_certified_key(const ed25519_cert_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+ed25519_cert_get_certified_key(const ed25519_cert_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->certified_key[idx];
+}
+
+int
+ed25519_cert_set_certified_key(ed25519_cert_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->certified_key[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+ed25519_cert_getarray_certified_key(ed25519_cert_t *inp)
+{
+ return inp->certified_key;
+}
+uint8_t
+ed25519_cert_get_n_extensions(ed25519_cert_t *inp)
+{
+ return inp->n_extensions;
+}
+int
+ed25519_cert_set_n_extensions(ed25519_cert_t *inp, uint8_t val)
+{
+ inp->n_extensions = val;
+ return 0;
+}
+size_t
+ed25519_cert_getlen_ext(const ed25519_cert_t *inp)
+{
+ return TRUNNEL_DYNARRAY_LEN(&inp->ext);
+}
+
+struct ed25519_cert_extension_st *
+ed25519_cert_get_ext(ed25519_cert_t *inp, size_t idx)
+{
+ return TRUNNEL_DYNARRAY_GET(&inp->ext, idx);
+}
+
+int
+ed25519_cert_set_ext(ed25519_cert_t *inp, size_t idx, struct ed25519_cert_extension_st * elt)
+{
+ ed25519_cert_extension_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->ext, idx);
+ if (oldval && oldval != elt)
+ ed25519_cert_extension_free(oldval);
+ return ed25519_cert_set0_ext(inp, idx, elt);
+}
+int
+ed25519_cert_set0_ext(ed25519_cert_t *inp, size_t idx, struct ed25519_cert_extension_st * elt)
+{
+ TRUNNEL_DYNARRAY_SET(&inp->ext, idx, elt);
+ return 0;
+}
+int
+ed25519_cert_add_ext(ed25519_cert_t *inp, struct ed25519_cert_extension_st * elt)
+{
+#if SIZE_MAX >= UINT8_MAX
+ if (inp->ext.n_ == UINT8_MAX)
+ goto trunnel_alloc_failed;
+#endif
+ TRUNNEL_DYNARRAY_ADD(struct ed25519_cert_extension_st *, &inp->ext, elt, {});
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+
+struct ed25519_cert_extension_st * *
+ed25519_cert_getarray_ext(ed25519_cert_t *inp)
+{
+ return inp->ext.elts_;
+}
+int
+ed25519_cert_setlen_ext(ed25519_cert_t *inp, size_t newlen)
+{
+ struct ed25519_cert_extension_st * *newptr;
+#if UINT8_MAX < SIZE_MAX
+ if (newlen > UINT8_MAX)
+ goto trunnel_alloc_failed;
+#endif
+ newptr = trunnel_dynarray_setlen(&inp->ext.allocated_,
+ &inp->ext.n_, inp->ext.elts_, newlen,
+ sizeof(inp->ext.elts_[0]), (trunnel_free_fn_t) ed25519_cert_extension_free,
+ &inp->trunnel_error_code_);
+ if (newptr == NULL)
+ goto trunnel_alloc_failed;
+ inp->ext.elts_ = newptr;
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+size_t
+ed25519_cert_getlen_signature(const ed25519_cert_t *inp)
+{
+ (void)inp; return 64;
+}
+
+uint8_t
+ed25519_cert_get_signature(const ed25519_cert_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 64);
+ return inp->signature[idx];
+}
+
+int
+ed25519_cert_set_signature(ed25519_cert_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 64);
+ inp->signature[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+ed25519_cert_getarray_signature(ed25519_cert_t *inp)
+{
+ return inp->signature;
+}
+const char *
+ed25519_cert_check(const ed25519_cert_t *obj)
+{
+ if (obj == NULL)
+ return "Object was NULL";
+ if (obj->trunnel_error_code_)
+ return "A set function failed on this object";
+ if (! (obj->version == 1))
+ return "Integer out of bounds";
+ {
+ const char *msg;
+
+ unsigned idx;
+ for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) {
+ if (NULL != (msg = ed25519_cert_extension_check(TRUNNEL_DYNARRAY_GET(&obj->ext, idx))))
+ return msg;
+ }
+ }
+ if (TRUNNEL_DYNARRAY_LEN(&obj->ext) != obj->n_extensions)
+ return "Length mismatch for ext";
+ return NULL;
+}
+
+ssize_t
+ed25519_cert_encoded_len(const ed25519_cert_t *obj)
+{
+ ssize_t result = 0;
+
+ if (NULL != ed25519_cert_check(obj))
+ return -1;
+
+
+ /* Length of u8 version IN [1] */
+ result += 1;
+
+ /* Length of u8 cert_type */
+ result += 1;
+
+ /* Length of u32 exp_field */
+ result += 4;
+
+ /* Length of u8 cert_key_type */
+ result += 1;
+
+ /* Length of u8 certified_key[32] */
+ result += 32;
+
+ /* Length of u8 n_extensions */
+ result += 1;
+
+ /* Length of struct ed25519_cert_extension ext[n_extensions] */
+ {
+
+ unsigned idx;
+ for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) {
+ result += ed25519_cert_extension_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->ext, idx));
+ }
+ }
+
+ /* Length of u8 signature[64] */
+ result += 64;
+ return result;
+}
+int
+ed25519_cert_clear_errors(ed25519_cert_t *obj)
+{
+ int r = obj->trunnel_error_code_;
+ obj->trunnel_error_code_ = 0;
+ return r;
+}
+ssize_t
+ed25519_cert_encode(uint8_t *output, const size_t avail, const ed25519_cert_t *obj)
+{
+ ssize_t result = 0;
+ size_t written = 0;
+ uint8_t *ptr = output;
+ const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ const ssize_t encoded_len = ed25519_cert_encoded_len(obj);
+#endif
+
+ if (NULL != (msg = ed25519_cert_check(obj)))
+ goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ trunnel_assert(encoded_len >= 0);
+#endif
+
+ /* Encode u8 version IN [1] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 1)
+ goto truncated;
+ trunnel_set_uint8(ptr, (obj->version));
+ written += 1; ptr += 1;
+
+ /* Encode u8 cert_type */
+ trunnel_assert(written <= avail);
+ if (avail - written < 1)
+ goto truncated;
+ trunnel_set_uint8(ptr, (obj->cert_type));
+ written += 1; ptr += 1;
+
+ /* Encode u32 exp_field */
+ trunnel_assert(written <= avail);
+ if (avail - written < 4)
+ goto truncated;
+ trunnel_set_uint32(ptr, trunnel_htonl(obj->exp_field));
+ written += 4; ptr += 4;
+
+ /* Encode u8 cert_key_type */
+ trunnel_assert(written <= avail);
+ if (avail - written < 1)
+ goto truncated;
+ trunnel_set_uint8(ptr, (obj->cert_key_type));
+ written += 1; ptr += 1;
+
+ /* Encode u8 certified_key[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->certified_key, 32);
+ written += 32; ptr += 32;
+
+ /* Encode u8 n_extensions */
+ trunnel_assert(written <= avail);
+ if (avail - written < 1)
+ goto truncated;
+ trunnel_set_uint8(ptr, (obj->n_extensions));
+ written += 1; ptr += 1;
+
+ /* Encode struct ed25519_cert_extension ext[n_extensions] */
+ {
+
+ unsigned idx;
+ for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) {
+ trunnel_assert(written <= avail);
+ result = ed25519_cert_extension_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->ext, idx));
+ if (result < 0)
+ goto fail; /* XXXXXXX !*/
+ written += result; ptr += result;
+ }
+ }
+
+ /* Encode u8 signature[64] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 64)
+ goto truncated;
+ memcpy(ptr, obj->signature, 64);
+ written += 64; ptr += 64;
+
+
+ trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ {
+ trunnel_assert(encoded_len >= 0);
+ trunnel_assert((size_t)encoded_len == written);
+ }
+
+#endif
+
+ return written;
+
+ truncated:
+ result = -2;
+ goto fail;
+ check_failed:
+ (void)msg;
+ result = -1;
+ goto fail;
+ fail:
+ trunnel_assert(result < 0);
+ return result;
+}
+
+/** As ed25519_cert_parse(), but do not allocate the output object.
+ */
+static ssize_t
+ed25519_cert_parse_into(ed25519_cert_t *obj, const uint8_t *input, const size_t len_in)
+{
+ const uint8_t *ptr = input;
+ size_t remaining = len_in;
+ ssize_t result = 0;
+ (void)result;
+
+ /* Parse u8 version IN [1] */
+ CHECK_REMAINING(1, truncated);
+ obj->version = (trunnel_get_uint8(ptr));
+ remaining -= 1; ptr += 1;
+ if (! (obj->version == 1))
+ goto fail;
+
+ /* Parse u8 cert_type */
+ CHECK_REMAINING(1, truncated);
+ obj->cert_type = (trunnel_get_uint8(ptr));
+ remaining -= 1; ptr += 1;
+
+ /* Parse u32 exp_field */
+ CHECK_REMAINING(4, truncated);
+ obj->exp_field = trunnel_ntohl(trunnel_get_uint32(ptr));
+ remaining -= 4; ptr += 4;
+
+ /* Parse u8 cert_key_type */
+ CHECK_REMAINING(1, truncated);
+ obj->cert_key_type = (trunnel_get_uint8(ptr));
+ remaining -= 1; ptr += 1;
+
+ /* Parse u8 certified_key[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->certified_key, ptr, 32);
+ remaining -= 32; ptr += 32;
+
+ /* Parse u8 n_extensions */
+ CHECK_REMAINING(1, truncated);
+ obj->n_extensions = (trunnel_get_uint8(ptr));
+ remaining -= 1; ptr += 1;
+
+ /* Parse struct ed25519_cert_extension ext[n_extensions] */
+ TRUNNEL_DYNARRAY_EXPAND(ed25519_cert_extension_t *, &obj->ext, obj->n_extensions, {});
+ {
+ ed25519_cert_extension_t * elt;
+ unsigned idx;
+ for (idx = 0; idx < obj->n_extensions; ++idx) {
+ result = ed25519_cert_extension_parse(&elt, ptr, remaining);
+ if (result < 0)
+ goto relay_fail;
+ trunnel_assert((size_t)result <= remaining);
+ remaining -= result; ptr += result;
+ TRUNNEL_DYNARRAY_ADD(ed25519_cert_extension_t *, &obj->ext, elt, {ed25519_cert_extension_free(elt);});
+ }
+ }
+
+ /* Parse u8 signature[64] */
+ CHECK_REMAINING(64, truncated);
+ memcpy(obj->signature, ptr, 64);
+ remaining -= 64; ptr += 64;
+ trunnel_assert(ptr + remaining == input + len_in);
+ return len_in - remaining;
+
+ truncated:
+ return -2;
+ relay_fail:
+ trunnel_assert(result < 0);
+ return result;
+ trunnel_alloc_failed:
+ return -1;
+ fail:
+ result = -1;
+ return result;
+}
+
+ssize_t
+ed25519_cert_parse(ed25519_cert_t **output, const uint8_t *input, const size_t len_in)
+{
+ ssize_t result;
+ *output = ed25519_cert_new();
+ if (NULL == *output)
+ return -1;
+ result = ed25519_cert_parse_into(*output, input, len_in);
+ if (result < 0) {
+ ed25519_cert_free(*output);
+ *output = NULL;
+ }
+ return result;
+}
diff --git a/src/trunnel/ed25519_cert.h b/src/trunnel/ed25519_cert.h
new file mode 100644
index 0000000000..7839af4bee
--- /dev/null
+++ b/src/trunnel/ed25519_cert.h
@@ -0,0 +1,288 @@
+/* ed25519_cert.h -- generated by by Trunnel v1.4.1.
+ * https://gitweb.torproject.org/trunnel.git
+ * You probably shouldn't edit this file.
+ */
+#ifndef TRUNNEL_ED25519_CERT_H
+#define TRUNNEL_ED25519_CERT_H
+
+#include <stdint.h>
+#include "trunnel.h"
+
+#define CERTEXT_SIGNED_WITH_KEY 4
+#define CERTEXT_FLAG_AFFECTS_VALIDATION 1
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_ED25519_CERT_EXTENSION)
+struct ed25519_cert_extension_st {
+ uint16_t ext_length;
+ uint8_t ext_type;
+ uint8_t ext_flags;
+ uint8_t un_signing_key[32];
+ TRUNNEL_DYNARRAY_HEAD(, uint8_t) un_unparsed;
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct ed25519_cert_extension_st ed25519_cert_extension_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_ED25519_CERT)
+struct ed25519_cert_st {
+ uint8_t version;
+ uint8_t cert_type;
+ uint32_t exp_field;
+ uint8_t cert_key_type;
+ uint8_t certified_key[32];
+ uint8_t n_extensions;
+ TRUNNEL_DYNARRAY_HEAD(, struct ed25519_cert_extension_st *) ext;
+ uint8_t signature[64];
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct ed25519_cert_st ed25519_cert_t;
+/** Return a newly allocated ed25519_cert_extension with all elements
+ * set to zero.
+ */
+ed25519_cert_extension_t *ed25519_cert_extension_new(void);
+/** Release all storage held by the ed25519_cert_extension in
+ * 'victim'. (Do nothing if 'victim' is NULL.)
+ */
+void ed25519_cert_extension_free(ed25519_cert_extension_t *victim);
+/** Try to parse a ed25519_cert_extension from the buffer in 'input',
+ * using up to 'len_in' bytes from the input buffer. On success,
+ * return the number of bytes consumed and set *output to the newly
+ * allocated ed25519_cert_extension_t. On failure, return -2 if the
+ * input appears truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t ed25519_cert_extension_parse(ed25519_cert_extension_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * ed25519_cert_extension in 'obj'. On failure, return a negative
+ * value. Note that this value may be an overestimate, and can even be
+ * an underestimate for certain unencodeable objects.
+ */
+ssize_t ed25519_cert_extension_encoded_len(const ed25519_cert_extension_t *obj);
+/** Try to encode the ed25519_cert_extension from 'input' into the
+ * buffer at 'output', using up to 'avail' bytes of the output buffer.
+ * On success, return the number of bytes used. On failure, return -2
+ * if the buffer was not long enough, and -1 if the input was invalid.
+ */
+ssize_t ed25519_cert_extension_encode(uint8_t *output, const size_t avail, const ed25519_cert_extension_t *input);
+/** Check whether the internal state of the ed25519_cert_extension in
+ * 'obj' is consistent. Return NULL if it is, and a short message if
+ * it is not.
+ */
+const char *ed25519_cert_extension_check(const ed25519_cert_extension_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int ed25519_cert_extension_clear_errors(ed25519_cert_extension_t *obj);
+/** Return the value of the ext_length field of the
+ * ed25519_cert_extension_t in 'inp'
+ */
+uint16_t ed25519_cert_extension_get_ext_length(ed25519_cert_extension_t *inp);
+/** Set the value of the ext_length field of the
+ * ed25519_cert_extension_t in 'inp' to 'val'. Return 0 on success;
+ * return -1 and set the error code on 'inp' on failure.
+ */
+int ed25519_cert_extension_set_ext_length(ed25519_cert_extension_t *inp, uint16_t val);
+/** Return the value of the ext_type field of the
+ * ed25519_cert_extension_t in 'inp'
+ */
+uint8_t ed25519_cert_extension_get_ext_type(ed25519_cert_extension_t *inp);
+/** Set the value of the ext_type field of the
+ * ed25519_cert_extension_t in 'inp' to 'val'. Return 0 on success;
+ * return -1 and set the error code on 'inp' on failure.
+ */
+int ed25519_cert_extension_set_ext_type(ed25519_cert_extension_t *inp, uint8_t val);
+/** Return the value of the ext_flags field of the
+ * ed25519_cert_extension_t in 'inp'
+ */
+uint8_t ed25519_cert_extension_get_ext_flags(ed25519_cert_extension_t *inp);
+/** Set the value of the ext_flags field of the
+ * ed25519_cert_extension_t in 'inp' to 'val'. Return 0 on success;
+ * return -1 and set the error code on 'inp' on failure.
+ */
+int ed25519_cert_extension_set_ext_flags(ed25519_cert_extension_t *inp, uint8_t val);
+/** Return the (constant) length of the array holding the
+ * un_signing_key field of the ed25519_cert_extension_t in 'inp'.
+ */
+size_t ed25519_cert_extension_getlen_un_signing_key(const ed25519_cert_extension_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * un_signing_key of the ed25519_cert_extension_t in 'inp'.
+ */
+uint8_t ed25519_cert_extension_get_un_signing_key(const ed25519_cert_extension_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * un_signing_key of the ed25519_cert_extension_t in 'inp', so that it
+ * will hold the value 'elt'.
+ */
+int ed25519_cert_extension_set_un_signing_key(ed25519_cert_extension_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field un_signing_key of
+ * 'inp'.
+ */
+uint8_t * ed25519_cert_extension_getarray_un_signing_key(ed25519_cert_extension_t *inp);
+/** Return the length of the dynamic array holding the un_unparsed
+ * field of the ed25519_cert_extension_t in 'inp'.
+ */
+size_t ed25519_cert_extension_getlen_un_unparsed(const ed25519_cert_extension_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * un_unparsed of the ed25519_cert_extension_t in 'inp'.
+ */
+uint8_t ed25519_cert_extension_get_un_unparsed(ed25519_cert_extension_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * un_unparsed of the ed25519_cert_extension_t in 'inp', so that it
+ * will hold the value 'elt'.
+ */
+int ed25519_cert_extension_set_un_unparsed(ed25519_cert_extension_t *inp, size_t idx, uint8_t elt);
+/** Append a new element 'elt' to the dynamic array field un_unparsed
+ * of the ed25519_cert_extension_t in 'inp'.
+ */
+int ed25519_cert_extension_add_un_unparsed(ed25519_cert_extension_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field un_unparsed of
+ * 'inp'.
+ */
+uint8_t * ed25519_cert_extension_getarray_un_unparsed(ed25519_cert_extension_t *inp);
+/** Change the length of the variable-length array field un_unparsed
+ * of 'inp' to 'newlen'.Fill extra elements with 0. Return 0 on
+ * success; return -1 and set the error code on 'inp' on failure.
+ */
+int ed25519_cert_extension_setlen_un_unparsed(ed25519_cert_extension_t *inp, size_t newlen);
+/** Return a newly allocated ed25519_cert with all elements set to
+ * zero.
+ */
+ed25519_cert_t *ed25519_cert_new(void);
+/** Release all storage held by the ed25519_cert in 'victim'. (Do
+ * nothing if 'victim' is NULL.)
+ */
+void ed25519_cert_free(ed25519_cert_t *victim);
+/** Try to parse a ed25519_cert from the buffer in 'input', using up
+ * to 'len_in' bytes from the input buffer. On success, return the
+ * number of bytes consumed and set *output to the newly allocated
+ * ed25519_cert_t. On failure, return -2 if the input appears
+ * truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t ed25519_cert_parse(ed25519_cert_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * ed25519_cert in 'obj'. On failure, return a negative value. Note
+ * that this value may be an overestimate, and can even be an
+ * underestimate for certain unencodeable objects.
+ */
+ssize_t ed25519_cert_encoded_len(const ed25519_cert_t *obj);
+/** Try to encode the ed25519_cert from 'input' into the buffer at
+ * 'output', using up to 'avail' bytes of the output buffer. On
+ * success, return the number of bytes used. On failure, return -2 if
+ * the buffer was not long enough, and -1 if the input was invalid.
+ */
+ssize_t ed25519_cert_encode(uint8_t *output, const size_t avail, const ed25519_cert_t *input);
+/** Check whether the internal state of the ed25519_cert in 'obj' is
+ * consistent. Return NULL if it is, and a short message if it is not.
+ */
+const char *ed25519_cert_check(const ed25519_cert_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int ed25519_cert_clear_errors(ed25519_cert_t *obj);
+/** Return the value of the version field of the ed25519_cert_t in
+ * 'inp'
+ */
+uint8_t ed25519_cert_get_version(ed25519_cert_t *inp);
+/** Set the value of the version field of the ed25519_cert_t in 'inp'
+ * to 'val'. Return 0 on success; return -1 and set the error code on
+ * 'inp' on failure.
+ */
+int ed25519_cert_set_version(ed25519_cert_t *inp, uint8_t val);
+/** Return the value of the cert_type field of the ed25519_cert_t in
+ * 'inp'
+ */
+uint8_t ed25519_cert_get_cert_type(ed25519_cert_t *inp);
+/** Set the value of the cert_type field of the ed25519_cert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int ed25519_cert_set_cert_type(ed25519_cert_t *inp, uint8_t val);
+/** Return the value of the exp_field field of the ed25519_cert_t in
+ * 'inp'
+ */
+uint32_t ed25519_cert_get_exp_field(ed25519_cert_t *inp);
+/** Set the value of the exp_field field of the ed25519_cert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int ed25519_cert_set_exp_field(ed25519_cert_t *inp, uint32_t val);
+/** Return the value of the cert_key_type field of the ed25519_cert_t
+ * in 'inp'
+ */
+uint8_t ed25519_cert_get_cert_key_type(ed25519_cert_t *inp);
+/** Set the value of the cert_key_type field of the ed25519_cert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int ed25519_cert_set_cert_key_type(ed25519_cert_t *inp, uint8_t val);
+/** Return the (constant) length of the array holding the
+ * certified_key field of the ed25519_cert_t in 'inp'.
+ */
+size_t ed25519_cert_getlen_certified_key(const ed25519_cert_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * certified_key of the ed25519_cert_t in 'inp'.
+ */
+uint8_t ed25519_cert_get_certified_key(const ed25519_cert_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * certified_key of the ed25519_cert_t in 'inp', so that it will hold
+ * the value 'elt'.
+ */
+int ed25519_cert_set_certified_key(ed25519_cert_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field certified_key of
+ * 'inp'.
+ */
+uint8_t * ed25519_cert_getarray_certified_key(ed25519_cert_t *inp);
+/** Return the value of the n_extensions field of the ed25519_cert_t
+ * in 'inp'
+ */
+uint8_t ed25519_cert_get_n_extensions(ed25519_cert_t *inp);
+/** Set the value of the n_extensions field of the ed25519_cert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int ed25519_cert_set_n_extensions(ed25519_cert_t *inp, uint8_t val);
+/** Return the length of the dynamic array holding the ext field of
+ * the ed25519_cert_t in 'inp'.
+ */
+size_t ed25519_cert_getlen_ext(const ed25519_cert_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * ext of the ed25519_cert_t in 'inp'.
+ */
+struct ed25519_cert_extension_st * ed25519_cert_get_ext(ed25519_cert_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * ext of the ed25519_cert_t in 'inp', so that it will hold the value
+ * 'elt'. Free the previous value, if any.
+ */
+int ed25519_cert_set_ext(ed25519_cert_t *inp, size_t idx, struct ed25519_cert_extension_st * elt);
+/** As ed25519_cert_set_ext, but does not free the previous value.
+ */
+int ed25519_cert_set0_ext(ed25519_cert_t *inp, size_t idx, struct ed25519_cert_extension_st * elt);
+/** Append a new element 'elt' to the dynamic array field ext of the
+ * ed25519_cert_t in 'inp'.
+ */
+int ed25519_cert_add_ext(ed25519_cert_t *inp, struct ed25519_cert_extension_st * elt);
+/** Return a pointer to the variable-length array field ext of 'inp'.
+ */
+struct ed25519_cert_extension_st * * ed25519_cert_getarray_ext(ed25519_cert_t *inp);
+/** Change the length of the variable-length array field ext of 'inp'
+ * to 'newlen'.Fill extra elements with NULL; free removed elements.
+ * Return 0 on success; return -1 and set the error code on 'inp' on
+ * failure.
+ */
+int ed25519_cert_setlen_ext(ed25519_cert_t *inp, size_t newlen);
+/** Return the (constant) length of the array holding the signature
+ * field of the ed25519_cert_t in 'inp'.
+ */
+size_t ed25519_cert_getlen_signature(const ed25519_cert_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * signature of the ed25519_cert_t in 'inp'.
+ */
+uint8_t ed25519_cert_get_signature(const ed25519_cert_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * signature of the ed25519_cert_t in 'inp', so that it will hold the
+ * value 'elt'.
+ */
+int ed25519_cert_set_signature(ed25519_cert_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 64-element array field signature of 'inp'.
+ */
+uint8_t * ed25519_cert_getarray_signature(ed25519_cert_t *inp);
+
+
+#endif
diff --git a/src/trunnel/ed25519_cert.trunnel b/src/trunnel/ed25519_cert.trunnel
new file mode 100644
index 0000000000..c46f1b6c6b
--- /dev/null
+++ b/src/trunnel/ed25519_cert.trunnel
@@ -0,0 +1,76 @@
+
+struct ed25519_cert {
+ u8 version IN [1];
+ u8 cert_type;
+ u32 exp_field;
+ u8 cert_key_type;
+ u8 certified_key[32];
+ u8 n_extensions;
+ struct ed25519_cert_extension ext[n_extensions];
+ u8 signature[64];
+}
+
+const CERTEXT_SIGNED_WITH_KEY = 4;
+const CERTEXT_FLAG_AFFECTS_VALIDATION = 1;
+
+struct ed25519_cert_extension {
+ u16 ext_length;
+ u8 ext_type;
+ u8 ext_flags;
+ union un[ext_type] with length ext_length {
+ CERTEXT_SIGNED_WITH_KEY : u8 signing_key[32];
+ default: u8 unparsed[];
+ };
+}
+
+/*
+struct cert_revocation {
+ u8 prefix[8];
+ u8 version IN [1];
+ u8 keytype;
+ u8 identity_key[32];
+ u8 revoked_key[32];
+ u64 published;
+ u8 n_extensions;
+ struct cert_extension ext[n_extensions];
+ u8 signature[64];
+}
+
+struct crosscert_ed_rsa {
+ u8 ed_key[32];
+ u32 expiration_date;
+ u8 signature[128];
+}
+
+struct auth02_cell {
+ u8 type[8];
+ u8 cid[32];
+ u8 sid[32];
+ u8 cid_ed[32];
+ u8 sid_ed[32];
+ u8 slog[32];
+ u8 clog[32];
+ u8 scert[32];
+ u8 tlssecrets[32];
+ u8 rand[24];
+ u8 sig[64];
+}
+
+const LS_IPV4 = 0x00;
+const LS_IPV6 = 0x01;
+const LS_LEGACY_ID = 0x02;
+const LS_ED25519_ID = 0x03;
+
+// amended from tor.trunnel
+struct link_specifier {
+ u8 ls_type;
+ u8 ls_len;
+ union un[ls_type] with length ls_len {
+ LS_IPV4: u32 ipv4_addr; u16 ipv4_port;
+ LS_IPV6: u8 ipv6_addr[16]; u16 ipv6_port;
+ LS_LEGACY_ID: u8 legacy_id[20];
+ LS_ED25519_ID: u8 ed25519_id[32];
+ default: u8 unrecognized[];
+ };
+}
+*/ \ No newline at end of file
diff --git a/src/trunnel/include.am b/src/trunnel/include.am
index c7ac1679d0..9bf37fe58b 100644
--- a/src/trunnel/include.am
+++ b/src/trunnel/include.am
@@ -9,21 +9,30 @@ endif
AM_CPPFLAGS += -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel
+TRUNNELINPUTS = \
+ src/trunnel/ed25519_cert.trunnel \
+ src/trunnel/link_handshake.trunnel \
+ src/trunnel/pwbox.trunnel
+
TRUNNELSOURCES = \
- src/ext/trunnel/trunnel.c \
- src/trunnel/pwbox.c
+ src/ext/trunnel/trunnel.c \
+ src/trunnel/ed25519_cert.c \
+ src/trunnel/link_handshake.c \
+ src/trunnel/pwbox.c
TRUNNELHEADERS = \
- src/ext/trunnel/trunnel.h \
- src/ext/trunnel/trunnel-impl.h \
- src/trunnel/trunnel-local.h \
- src/trunnel/pwbox.h
+ src/ext/trunnel/trunnel.h \
+ src/ext/trunnel/trunnel-impl.h \
+ src/trunnel/trunnel-local.h \
+ src/trunnel/ed25519_cert.h \
+ src/trunnel/link_handshake.h \
+ src/trunnel/pwbox.h
src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES)
src_trunnel_libor_trunnel_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS)
src_trunnel_libor_trunnel_testing_a_SOURCES = $(TRUNNELSOURCES)
-src_trunnel_libor_trunnel_testing_a_CPPFLAGS = -DTOR_UNIT_TESTS -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS)
+src_trunnel_libor_trunnel_testing_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS) $(TEST_CPPFLAGS)
src_trunnel_libor_trunnel_testing_a_CFLAGS = $(AM_CFLAGS) $(TEST_CFLAGS)
noinst_HEADERS+= $(TRUNNELHEADERS)
diff --git a/src/trunnel/link_handshake.c b/src/trunnel/link_handshake.c
new file mode 100644
index 0000000000..f53161a3e5
--- /dev/null
+++ b/src/trunnel/link_handshake.c
@@ -0,0 +1,1885 @@
+/* link_handshake.c -- generated by Trunnel v1.4.1.
+ * https://gitweb.torproject.org/trunnel.git
+ * You probably shouldn't edit this file.
+ */
+#include <stdlib.h>
+#include "trunnel-impl.h"
+
+#include "link_handshake.h"
+
+#define TRUNNEL_SET_ERROR_CODE(obj) \
+ do { \
+ (obj)->trunnel_error_code_ = 1; \
+ } while (0)
+
+#if defined(__COVERITY__) || defined(__clang_analyzer__)
+/* If we're runnning a static analysis tool, we don't want it to complain
+ * that some of our remaining-bytes checks are dead-code. */
+int linkhandshake_deadcode_dummy__ = 0;
+#define OR_DEADCODE_DUMMY || linkhandshake_deadcode_dummy__
+#else
+#define OR_DEADCODE_DUMMY
+#endif
+
+#define CHECK_REMAINING(nbytes, label) \
+ do { \
+ if (remaining < (nbytes) OR_DEADCODE_DUMMY) { \
+ goto label; \
+ } \
+ } while (0)
+
+auth_challenge_cell_t *
+auth_challenge_cell_new(void)
+{
+ auth_challenge_cell_t *val = trunnel_calloc(1, sizeof(auth_challenge_cell_t));
+ if (NULL == val)
+ return NULL;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+auth_challenge_cell_clear(auth_challenge_cell_t *obj)
+{
+ (void) obj;
+ TRUNNEL_DYNARRAY_WIPE(&obj->methods);
+ TRUNNEL_DYNARRAY_CLEAR(&obj->methods);
+}
+
+void
+auth_challenge_cell_free(auth_challenge_cell_t *obj)
+{
+ if (obj == NULL)
+ return;
+ auth_challenge_cell_clear(obj);
+ trunnel_memwipe(obj, sizeof(auth_challenge_cell_t));
+ trunnel_free_(obj);
+}
+
+size_t
+auth_challenge_cell_getlen_challenge(const auth_challenge_cell_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+auth_challenge_cell_get_challenge(const auth_challenge_cell_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->challenge[idx];
+}
+
+int
+auth_challenge_cell_set_challenge(auth_challenge_cell_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->challenge[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth_challenge_cell_getarray_challenge(auth_challenge_cell_t *inp)
+{
+ return inp->challenge;
+}
+uint16_t
+auth_challenge_cell_get_n_methods(auth_challenge_cell_t *inp)
+{
+ return inp->n_methods;
+}
+int
+auth_challenge_cell_set_n_methods(auth_challenge_cell_t *inp, uint16_t val)
+{
+ inp->n_methods = val;
+ return 0;
+}
+size_t
+auth_challenge_cell_getlen_methods(const auth_challenge_cell_t *inp)
+{
+ return TRUNNEL_DYNARRAY_LEN(&inp->methods);
+}
+
+uint16_t
+auth_challenge_cell_get_methods(auth_challenge_cell_t *inp, size_t idx)
+{
+ return TRUNNEL_DYNARRAY_GET(&inp->methods, idx);
+}
+
+int
+auth_challenge_cell_set_methods(auth_challenge_cell_t *inp, size_t idx, uint16_t elt)
+{
+ TRUNNEL_DYNARRAY_SET(&inp->methods, idx, elt);
+ return 0;
+}
+int
+auth_challenge_cell_add_methods(auth_challenge_cell_t *inp, uint16_t elt)
+{
+#if SIZE_MAX >= UINT16_MAX
+ if (inp->methods.n_ == UINT16_MAX)
+ goto trunnel_alloc_failed;
+#endif
+ TRUNNEL_DYNARRAY_ADD(uint16_t, &inp->methods, elt, {});
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+
+uint16_t *
+auth_challenge_cell_getarray_methods(auth_challenge_cell_t *inp)
+{
+ return inp->methods.elts_;
+}
+int
+auth_challenge_cell_setlen_methods(auth_challenge_cell_t *inp, size_t newlen)
+{
+ uint16_t *newptr;
+#if UINT16_MAX < SIZE_MAX
+ if (newlen > UINT16_MAX)
+ goto trunnel_alloc_failed;
+#endif
+ newptr = trunnel_dynarray_setlen(&inp->methods.allocated_,
+ &inp->methods.n_, inp->methods.elts_, newlen,
+ sizeof(inp->methods.elts_[0]), (trunnel_free_fn_t) NULL,
+ &inp->trunnel_error_code_);
+ if (newptr == NULL)
+ goto trunnel_alloc_failed;
+ inp->methods.elts_ = newptr;
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+const char *
+auth_challenge_cell_check(const auth_challenge_cell_t *obj)
+{
+ if (obj == NULL)
+ return "Object was NULL";
+ if (obj->trunnel_error_code_)
+ return "A set function failed on this object";
+ if (TRUNNEL_DYNARRAY_LEN(&obj->methods) != obj->n_methods)
+ return "Length mismatch for methods";
+ return NULL;
+}
+
+ssize_t
+auth_challenge_cell_encoded_len(const auth_challenge_cell_t *obj)
+{
+ ssize_t result = 0;
+
+ if (NULL != auth_challenge_cell_check(obj))
+ return -1;
+
+
+ /* Length of u8 challenge[32] */
+ result += 32;
+
+ /* Length of u16 n_methods */
+ result += 2;
+
+ /* Length of u16 methods[n_methods] */
+ result += 2 * TRUNNEL_DYNARRAY_LEN(&obj->methods);
+ return result;
+}
+int
+auth_challenge_cell_clear_errors(auth_challenge_cell_t *obj)
+{
+ int r = obj->trunnel_error_code_;
+ obj->trunnel_error_code_ = 0;
+ return r;
+}
+ssize_t
+auth_challenge_cell_encode(uint8_t *output, const size_t avail, const auth_challenge_cell_t *obj)
+{
+ ssize_t result = 0;
+ size_t written = 0;
+ uint8_t *ptr = output;
+ const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ const ssize_t encoded_len = auth_challenge_cell_encoded_len(obj);
+#endif
+
+ if (NULL != (msg = auth_challenge_cell_check(obj)))
+ goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ trunnel_assert(encoded_len >= 0);
+#endif
+
+ /* Encode u8 challenge[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->challenge, 32);
+ written += 32; ptr += 32;
+
+ /* Encode u16 n_methods */
+ trunnel_assert(written <= avail);
+ if (avail - written < 2)
+ goto truncated;
+ trunnel_set_uint16(ptr, trunnel_htons(obj->n_methods));
+ written += 2; ptr += 2;
+
+ /* Encode u16 methods[n_methods] */
+ {
+
+ unsigned idx;
+ for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->methods); ++idx) {
+ trunnel_assert(written <= avail);
+ if (avail - written < 2)
+ goto truncated;
+ trunnel_set_uint16(ptr, trunnel_htons(TRUNNEL_DYNARRAY_GET(&obj->methods, idx)));
+ written += 2; ptr += 2;
+ }
+ }
+
+
+ trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ {
+ trunnel_assert(encoded_len >= 0);
+ trunnel_assert((size_t)encoded_len == written);
+ }
+
+#endif
+
+ return written;
+
+ truncated:
+ result = -2;
+ goto fail;
+ check_failed:
+ (void)msg;
+ result = -1;
+ goto fail;
+ fail:
+ trunnel_assert(result < 0);
+ return result;
+}
+
+/** As auth_challenge_cell_parse(), but do not allocate the output
+ * object.
+ */
+static ssize_t
+auth_challenge_cell_parse_into(auth_challenge_cell_t *obj, const uint8_t *input, const size_t len_in)
+{
+ const uint8_t *ptr = input;
+ size_t remaining = len_in;
+ ssize_t result = 0;
+ (void)result;
+
+ /* Parse u8 challenge[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->challenge, ptr, 32);
+ remaining -= 32; ptr += 32;
+
+ /* Parse u16 n_methods */
+ CHECK_REMAINING(2, truncated);
+ obj->n_methods = trunnel_ntohs(trunnel_get_uint16(ptr));
+ remaining -= 2; ptr += 2;
+
+ /* Parse u16 methods[n_methods] */
+ TRUNNEL_DYNARRAY_EXPAND(uint16_t, &obj->methods, obj->n_methods, {});
+ {
+ uint16_t elt;
+ unsigned idx;
+ for (idx = 0; idx < obj->n_methods; ++idx) {
+ CHECK_REMAINING(2, truncated);
+ elt = trunnel_ntohs(trunnel_get_uint16(ptr));
+ remaining -= 2; ptr += 2;
+ TRUNNEL_DYNARRAY_ADD(uint16_t, &obj->methods, elt, {});
+ }
+ }
+ trunnel_assert(ptr + remaining == input + len_in);
+ return len_in - remaining;
+
+ truncated:
+ return -2;
+ trunnel_alloc_failed:
+ return -1;
+}
+
+ssize_t
+auth_challenge_cell_parse(auth_challenge_cell_t **output, const uint8_t *input, const size_t len_in)
+{
+ ssize_t result;
+ *output = auth_challenge_cell_new();
+ if (NULL == *output)
+ return -1;
+ result = auth_challenge_cell_parse_into(*output, input, len_in);
+ if (result < 0) {
+ auth_challenge_cell_free(*output);
+ *output = NULL;
+ }
+ return result;
+}
+auth_ctx_t *
+auth_ctx_new(void)
+{
+ auth_ctx_t *val = trunnel_calloc(1, sizeof(auth_ctx_t));
+ if (NULL == val)
+ return NULL;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+auth_ctx_clear(auth_ctx_t *obj)
+{
+ (void) obj;
+}
+
+void
+auth_ctx_free(auth_ctx_t *obj)
+{
+ if (obj == NULL)
+ return;
+ auth_ctx_clear(obj);
+ trunnel_memwipe(obj, sizeof(auth_ctx_t));
+ trunnel_free_(obj);
+}
+
+uint8_t
+auth_ctx_get_is_ed(auth_ctx_t *inp)
+{
+ return inp->is_ed;
+}
+int
+auth_ctx_set_is_ed(auth_ctx_t *inp, uint8_t val)
+{
+ inp->is_ed = val;
+ return 0;
+}
+certs_cell_cert_t *
+certs_cell_cert_new(void)
+{
+ certs_cell_cert_t *val = trunnel_calloc(1, sizeof(certs_cell_cert_t));
+ if (NULL == val)
+ return NULL;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+certs_cell_cert_clear(certs_cell_cert_t *obj)
+{
+ (void) obj;
+ TRUNNEL_DYNARRAY_WIPE(&obj->body);
+ TRUNNEL_DYNARRAY_CLEAR(&obj->body);
+}
+
+void
+certs_cell_cert_free(certs_cell_cert_t *obj)
+{
+ if (obj == NULL)
+ return;
+ certs_cell_cert_clear(obj);
+ trunnel_memwipe(obj, sizeof(certs_cell_cert_t));
+ trunnel_free_(obj);
+}
+
+uint8_t
+certs_cell_cert_get_cert_type(certs_cell_cert_t *inp)
+{
+ return inp->cert_type;
+}
+int
+certs_cell_cert_set_cert_type(certs_cell_cert_t *inp, uint8_t val)
+{
+ inp->cert_type = val;
+ return 0;
+}
+uint16_t
+certs_cell_cert_get_cert_len(certs_cell_cert_t *inp)
+{
+ return inp->cert_len;
+}
+int
+certs_cell_cert_set_cert_len(certs_cell_cert_t *inp, uint16_t val)
+{
+ inp->cert_len = val;
+ return 0;
+}
+size_t
+certs_cell_cert_getlen_body(const certs_cell_cert_t *inp)
+{
+ return TRUNNEL_DYNARRAY_LEN(&inp->body);
+}
+
+uint8_t
+certs_cell_cert_get_body(certs_cell_cert_t *inp, size_t idx)
+{
+ return TRUNNEL_DYNARRAY_GET(&inp->body, idx);
+}
+
+int
+certs_cell_cert_set_body(certs_cell_cert_t *inp, size_t idx, uint8_t elt)
+{
+ TRUNNEL_DYNARRAY_SET(&inp->body, idx, elt);
+ return 0;
+}
+int
+certs_cell_cert_add_body(certs_cell_cert_t *inp, uint8_t elt)
+{
+#if SIZE_MAX >= UINT16_MAX
+ if (inp->body.n_ == UINT16_MAX)
+ goto trunnel_alloc_failed;
+#endif
+ TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->body, elt, {});
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+
+uint8_t *
+certs_cell_cert_getarray_body(certs_cell_cert_t *inp)
+{
+ return inp->body.elts_;
+}
+int
+certs_cell_cert_setlen_body(certs_cell_cert_t *inp, size_t newlen)
+{
+ uint8_t *newptr;
+#if UINT16_MAX < SIZE_MAX
+ if (newlen > UINT16_MAX)
+ goto trunnel_alloc_failed;
+#endif
+ newptr = trunnel_dynarray_setlen(&inp->body.allocated_,
+ &inp->body.n_, inp->body.elts_, newlen,
+ sizeof(inp->body.elts_[0]), (trunnel_free_fn_t) NULL,
+ &inp->trunnel_error_code_);
+ if (newptr == NULL)
+ goto trunnel_alloc_failed;
+ inp->body.elts_ = newptr;
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+const char *
+certs_cell_cert_check(const certs_cell_cert_t *obj)
+{
+ if (obj == NULL)
+ return "Object was NULL";
+ if (obj->trunnel_error_code_)
+ return "A set function failed on this object";
+ if (TRUNNEL_DYNARRAY_LEN(&obj->body) != obj->cert_len)
+ return "Length mismatch for body";
+ return NULL;
+}
+
+ssize_t
+certs_cell_cert_encoded_len(const certs_cell_cert_t *obj)
+{
+ ssize_t result = 0;
+
+ if (NULL != certs_cell_cert_check(obj))
+ return -1;
+
+
+ /* Length of u8 cert_type */
+ result += 1;
+
+ /* Length of u16 cert_len */
+ result += 2;
+
+ /* Length of u8 body[cert_len] */
+ result += TRUNNEL_DYNARRAY_LEN(&obj->body);
+ return result;
+}
+int
+certs_cell_cert_clear_errors(certs_cell_cert_t *obj)
+{
+ int r = obj->trunnel_error_code_;
+ obj->trunnel_error_code_ = 0;
+ return r;
+}
+ssize_t
+certs_cell_cert_encode(uint8_t *output, const size_t avail, const certs_cell_cert_t *obj)
+{
+ ssize_t result = 0;
+ size_t written = 0;
+ uint8_t *ptr = output;
+ const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ const ssize_t encoded_len = certs_cell_cert_encoded_len(obj);
+#endif
+
+ if (NULL != (msg = certs_cell_cert_check(obj)))
+ goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ trunnel_assert(encoded_len >= 0);
+#endif
+
+ /* Encode u8 cert_type */
+ trunnel_assert(written <= avail);
+ if (avail - written < 1)
+ goto truncated;
+ trunnel_set_uint8(ptr, (obj->cert_type));
+ written += 1; ptr += 1;
+
+ /* Encode u16 cert_len */
+ trunnel_assert(written <= avail);
+ if (avail - written < 2)
+ goto truncated;
+ trunnel_set_uint16(ptr, trunnel_htons(obj->cert_len));
+ written += 2; ptr += 2;
+
+ /* Encode u8 body[cert_len] */
+ {
+ size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->body);
+ trunnel_assert(obj->cert_len == elt_len);
+ trunnel_assert(written <= avail);
+ if (avail - written < elt_len)
+ goto truncated;
+ memcpy(ptr, obj->body.elts_, elt_len);
+ written += elt_len; ptr += elt_len;
+ }
+
+
+ trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ {
+ trunnel_assert(encoded_len >= 0);
+ trunnel_assert((size_t)encoded_len == written);
+ }
+
+#endif
+
+ return written;
+
+ truncated:
+ result = -2;
+ goto fail;
+ check_failed:
+ (void)msg;
+ result = -1;
+ goto fail;
+ fail:
+ trunnel_assert(result < 0);
+ return result;
+}
+
+/** As certs_cell_cert_parse(), but do not allocate the output object.
+ */
+static ssize_t
+certs_cell_cert_parse_into(certs_cell_cert_t *obj, const uint8_t *input, const size_t len_in)
+{
+ const uint8_t *ptr = input;
+ size_t remaining = len_in;
+ ssize_t result = 0;
+ (void)result;
+
+ /* Parse u8 cert_type */
+ CHECK_REMAINING(1, truncated);
+ obj->cert_type = (trunnel_get_uint8(ptr));
+ remaining -= 1; ptr += 1;
+
+ /* Parse u16 cert_len */
+ CHECK_REMAINING(2, truncated);
+ obj->cert_len = trunnel_ntohs(trunnel_get_uint16(ptr));
+ remaining -= 2; ptr += 2;
+
+ /* Parse u8 body[cert_len] */
+ CHECK_REMAINING(obj->cert_len, truncated);
+ TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->body, obj->cert_len, {});
+ obj->body.n_ = obj->cert_len;
+ memcpy(obj->body.elts_, ptr, obj->cert_len);
+ ptr += obj->cert_len; remaining -= obj->cert_len;
+ trunnel_assert(ptr + remaining == input + len_in);
+ return len_in - remaining;
+
+ truncated:
+ return -2;
+ trunnel_alloc_failed:
+ return -1;
+}
+
+ssize_t
+certs_cell_cert_parse(certs_cell_cert_t **output, const uint8_t *input, const size_t len_in)
+{
+ ssize_t result;
+ *output = certs_cell_cert_new();
+ if (NULL == *output)
+ return -1;
+ result = certs_cell_cert_parse_into(*output, input, len_in);
+ if (result < 0) {
+ certs_cell_cert_free(*output);
+ *output = NULL;
+ }
+ return result;
+}
+rsa_ed_crosscert_t *
+rsa_ed_crosscert_new(void)
+{
+ rsa_ed_crosscert_t *val = trunnel_calloc(1, sizeof(rsa_ed_crosscert_t));
+ if (NULL == val)
+ return NULL;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+rsa_ed_crosscert_clear(rsa_ed_crosscert_t *obj)
+{
+ (void) obj;
+ TRUNNEL_DYNARRAY_WIPE(&obj->sig);
+ TRUNNEL_DYNARRAY_CLEAR(&obj->sig);
+}
+
+void
+rsa_ed_crosscert_free(rsa_ed_crosscert_t *obj)
+{
+ if (obj == NULL)
+ return;
+ rsa_ed_crosscert_clear(obj);
+ trunnel_memwipe(obj, sizeof(rsa_ed_crosscert_t));
+ trunnel_free_(obj);
+}
+
+size_t
+rsa_ed_crosscert_getlen_ed_key(const rsa_ed_crosscert_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+rsa_ed_crosscert_get_ed_key(const rsa_ed_crosscert_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->ed_key[idx];
+}
+
+int
+rsa_ed_crosscert_set_ed_key(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->ed_key[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+rsa_ed_crosscert_getarray_ed_key(rsa_ed_crosscert_t *inp)
+{
+ return inp->ed_key;
+}
+uint32_t
+rsa_ed_crosscert_get_expiration(rsa_ed_crosscert_t *inp)
+{
+ return inp->expiration;
+}
+int
+rsa_ed_crosscert_set_expiration(rsa_ed_crosscert_t *inp, uint32_t val)
+{
+ inp->expiration = val;
+ return 0;
+}
+const uint8_t *
+rsa_ed_crosscert_get_end_of_signed(const rsa_ed_crosscert_t *inp)
+{
+ return inp->end_of_signed;
+}
+uint8_t
+rsa_ed_crosscert_get_sig_len(rsa_ed_crosscert_t *inp)
+{
+ return inp->sig_len;
+}
+int
+rsa_ed_crosscert_set_sig_len(rsa_ed_crosscert_t *inp, uint8_t val)
+{
+ inp->sig_len = val;
+ return 0;
+}
+size_t
+rsa_ed_crosscert_getlen_sig(const rsa_ed_crosscert_t *inp)
+{
+ return TRUNNEL_DYNARRAY_LEN(&inp->sig);
+}
+
+uint8_t
+rsa_ed_crosscert_get_sig(rsa_ed_crosscert_t *inp, size_t idx)
+{
+ return TRUNNEL_DYNARRAY_GET(&inp->sig, idx);
+}
+
+int
+rsa_ed_crosscert_set_sig(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt)
+{
+ TRUNNEL_DYNARRAY_SET(&inp->sig, idx, elt);
+ return 0;
+}
+int
+rsa_ed_crosscert_add_sig(rsa_ed_crosscert_t *inp, uint8_t elt)
+{
+#if SIZE_MAX >= UINT8_MAX
+ if (inp->sig.n_ == UINT8_MAX)
+ goto trunnel_alloc_failed;
+#endif
+ TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->sig, elt, {});
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+
+uint8_t *
+rsa_ed_crosscert_getarray_sig(rsa_ed_crosscert_t *inp)
+{
+ return inp->sig.elts_;
+}
+int
+rsa_ed_crosscert_setlen_sig(rsa_ed_crosscert_t *inp, size_t newlen)
+{
+ uint8_t *newptr;
+#if UINT8_MAX < SIZE_MAX
+ if (newlen > UINT8_MAX)
+ goto trunnel_alloc_failed;
+#endif
+ newptr = trunnel_dynarray_setlen(&inp->sig.allocated_,
+ &inp->sig.n_, inp->sig.elts_, newlen,
+ sizeof(inp->sig.elts_[0]), (trunnel_free_fn_t) NULL,
+ &inp->trunnel_error_code_);
+ if (newptr == NULL)
+ goto trunnel_alloc_failed;
+ inp->sig.elts_ = newptr;
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+const char *
+rsa_ed_crosscert_check(const rsa_ed_crosscert_t *obj)
+{
+ if (obj == NULL)
+ return "Object was NULL";
+ if (obj->trunnel_error_code_)
+ return "A set function failed on this object";
+ if (TRUNNEL_DYNARRAY_LEN(&obj->sig) != obj->sig_len)
+ return "Length mismatch for sig";
+ return NULL;
+}
+
+ssize_t
+rsa_ed_crosscert_encoded_len(const rsa_ed_crosscert_t *obj)
+{
+ ssize_t result = 0;
+
+ if (NULL != rsa_ed_crosscert_check(obj))
+ return -1;
+
+
+ /* Length of u8 ed_key[32] */
+ result += 32;
+
+ /* Length of u32 expiration */
+ result += 4;
+
+ /* Length of u8 sig_len */
+ result += 1;
+
+ /* Length of u8 sig[sig_len] */
+ result += TRUNNEL_DYNARRAY_LEN(&obj->sig);
+ return result;
+}
+int
+rsa_ed_crosscert_clear_errors(rsa_ed_crosscert_t *obj)
+{
+ int r = obj->trunnel_error_code_;
+ obj->trunnel_error_code_ = 0;
+ return r;
+}
+ssize_t
+rsa_ed_crosscert_encode(uint8_t *output, const size_t avail, const rsa_ed_crosscert_t *obj)
+{
+ ssize_t result = 0;
+ size_t written = 0;
+ uint8_t *ptr = output;
+ const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ const ssize_t encoded_len = rsa_ed_crosscert_encoded_len(obj);
+#endif
+
+ if (NULL != (msg = rsa_ed_crosscert_check(obj)))
+ goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ trunnel_assert(encoded_len >= 0);
+#endif
+
+ /* Encode u8 ed_key[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->ed_key, 32);
+ written += 32; ptr += 32;
+
+ /* Encode u32 expiration */
+ trunnel_assert(written <= avail);
+ if (avail - written < 4)
+ goto truncated;
+ trunnel_set_uint32(ptr, trunnel_htonl(obj->expiration));
+ written += 4; ptr += 4;
+
+ /* Encode u8 sig_len */
+ trunnel_assert(written <= avail);
+ if (avail - written < 1)
+ goto truncated;
+ trunnel_set_uint8(ptr, (obj->sig_len));
+ written += 1; ptr += 1;
+
+ /* Encode u8 sig[sig_len] */
+ {
+ size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->sig);
+ trunnel_assert(obj->sig_len == elt_len);
+ trunnel_assert(written <= avail);
+ if (avail - written < elt_len)
+ goto truncated;
+ memcpy(ptr, obj->sig.elts_, elt_len);
+ written += elt_len; ptr += elt_len;
+ }
+
+
+ trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ {
+ trunnel_assert(encoded_len >= 0);
+ trunnel_assert((size_t)encoded_len == written);
+ }
+
+#endif
+
+ return written;
+
+ truncated:
+ result = -2;
+ goto fail;
+ check_failed:
+ (void)msg;
+ result = -1;
+ goto fail;
+ fail:
+ trunnel_assert(result < 0);
+ return result;
+}
+
+/** As rsa_ed_crosscert_parse(), but do not allocate the output
+ * object.
+ */
+static ssize_t
+rsa_ed_crosscert_parse_into(rsa_ed_crosscert_t *obj, const uint8_t *input, const size_t len_in)
+{
+ const uint8_t *ptr = input;
+ size_t remaining = len_in;
+ ssize_t result = 0;
+ (void)result;
+
+ /* Parse u8 ed_key[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->ed_key, ptr, 32);
+ remaining -= 32; ptr += 32;
+
+ /* Parse u32 expiration */
+ CHECK_REMAINING(4, truncated);
+ obj->expiration = trunnel_ntohl(trunnel_get_uint32(ptr));
+ remaining -= 4; ptr += 4;
+ obj->end_of_signed = ptr;
+
+ /* Parse u8 sig_len */
+ CHECK_REMAINING(1, truncated);
+ obj->sig_len = (trunnel_get_uint8(ptr));
+ remaining -= 1; ptr += 1;
+
+ /* Parse u8 sig[sig_len] */
+ CHECK_REMAINING(obj->sig_len, truncated);
+ TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->sig, obj->sig_len, {});
+ obj->sig.n_ = obj->sig_len;
+ memcpy(obj->sig.elts_, ptr, obj->sig_len);
+ ptr += obj->sig_len; remaining -= obj->sig_len;
+ trunnel_assert(ptr + remaining == input + len_in);
+ return len_in - remaining;
+
+ truncated:
+ return -2;
+ trunnel_alloc_failed:
+ return -1;
+}
+
+ssize_t
+rsa_ed_crosscert_parse(rsa_ed_crosscert_t **output, const uint8_t *input, const size_t len_in)
+{
+ ssize_t result;
+ *output = rsa_ed_crosscert_new();
+ if (NULL == *output)
+ return -1;
+ result = rsa_ed_crosscert_parse_into(*output, input, len_in);
+ if (result < 0) {
+ rsa_ed_crosscert_free(*output);
+ *output = NULL;
+ }
+ return result;
+}
+auth1_t *
+auth1_new(void)
+{
+ auth1_t *val = trunnel_calloc(1, sizeof(auth1_t));
+ if (NULL == val)
+ return NULL;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+auth1_clear(auth1_t *obj)
+{
+ (void) obj;
+ TRUNNEL_DYNARRAY_WIPE(&obj->sig);
+ TRUNNEL_DYNARRAY_CLEAR(&obj->sig);
+}
+
+void
+auth1_free(auth1_t *obj)
+{
+ if (obj == NULL)
+ return;
+ auth1_clear(obj);
+ trunnel_memwipe(obj, sizeof(auth1_t));
+ trunnel_free_(obj);
+}
+
+size_t
+auth1_getlen_type(const auth1_t *inp)
+{
+ (void)inp; return 8;
+}
+
+uint8_t
+auth1_get_type(const auth1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 8);
+ return inp->type[idx];
+}
+
+int
+auth1_set_type(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 8);
+ inp->type[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth1_getarray_type(auth1_t *inp)
+{
+ return inp->type;
+}
+size_t
+auth1_getlen_cid(const auth1_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+auth1_get_cid(const auth1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->cid[idx];
+}
+
+int
+auth1_set_cid(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->cid[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth1_getarray_cid(auth1_t *inp)
+{
+ return inp->cid;
+}
+size_t
+auth1_getlen_sid(const auth1_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+auth1_get_sid(const auth1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->sid[idx];
+}
+
+int
+auth1_set_sid(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->sid[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth1_getarray_sid(auth1_t *inp)
+{
+ return inp->sid;
+}
+size_t
+auth1_getlen_u1_cid_ed(const auth1_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+auth1_get_u1_cid_ed(const auth1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->u1_cid_ed[idx];
+}
+
+int
+auth1_set_u1_cid_ed(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->u1_cid_ed[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth1_getarray_u1_cid_ed(auth1_t *inp)
+{
+ return inp->u1_cid_ed;
+}
+size_t
+auth1_getlen_u1_sid_ed(const auth1_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+auth1_get_u1_sid_ed(const auth1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->u1_sid_ed[idx];
+}
+
+int
+auth1_set_u1_sid_ed(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->u1_sid_ed[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth1_getarray_u1_sid_ed(auth1_t *inp)
+{
+ return inp->u1_sid_ed;
+}
+size_t
+auth1_getlen_slog(const auth1_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+auth1_get_slog(const auth1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->slog[idx];
+}
+
+int
+auth1_set_slog(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->slog[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth1_getarray_slog(auth1_t *inp)
+{
+ return inp->slog;
+}
+size_t
+auth1_getlen_clog(const auth1_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+auth1_get_clog(const auth1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->clog[idx];
+}
+
+int
+auth1_set_clog(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->clog[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth1_getarray_clog(auth1_t *inp)
+{
+ return inp->clog;
+}
+size_t
+auth1_getlen_scert(const auth1_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+auth1_get_scert(const auth1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->scert[idx];
+}
+
+int
+auth1_set_scert(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->scert[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth1_getarray_scert(auth1_t *inp)
+{
+ return inp->scert;
+}
+size_t
+auth1_getlen_tlssecrets(const auth1_t *inp)
+{
+ (void)inp; return 32;
+}
+
+uint8_t
+auth1_get_tlssecrets(const auth1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 32);
+ return inp->tlssecrets[idx];
+}
+
+int
+auth1_set_tlssecrets(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 32);
+ inp->tlssecrets[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth1_getarray_tlssecrets(auth1_t *inp)
+{
+ return inp->tlssecrets;
+}
+const uint8_t *
+auth1_get_end_of_fixed_part(const auth1_t *inp)
+{
+ return inp->end_of_fixed_part;
+}
+size_t
+auth1_getlen_rand(const auth1_t *inp)
+{
+ (void)inp; return 24;
+}
+
+uint8_t
+auth1_get_rand(const auth1_t *inp, size_t idx)
+{
+ trunnel_assert(idx < 24);
+ return inp->rand[idx];
+}
+
+int
+auth1_set_rand(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ trunnel_assert(idx < 24);
+ inp->rand[idx] = elt;
+ return 0;
+}
+
+uint8_t *
+auth1_getarray_rand(auth1_t *inp)
+{
+ return inp->rand;
+}
+const uint8_t *
+auth1_get_end_of_signed(const auth1_t *inp)
+{
+ return inp->end_of_signed;
+}
+size_t
+auth1_getlen_sig(const auth1_t *inp)
+{
+ return TRUNNEL_DYNARRAY_LEN(&inp->sig);
+}
+
+uint8_t
+auth1_get_sig(auth1_t *inp, size_t idx)
+{
+ return TRUNNEL_DYNARRAY_GET(&inp->sig, idx);
+}
+
+int
+auth1_set_sig(auth1_t *inp, size_t idx, uint8_t elt)
+{
+ TRUNNEL_DYNARRAY_SET(&inp->sig, idx, elt);
+ return 0;
+}
+int
+auth1_add_sig(auth1_t *inp, uint8_t elt)
+{
+ TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->sig, elt, {});
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+
+uint8_t *
+auth1_getarray_sig(auth1_t *inp)
+{
+ return inp->sig.elts_;
+}
+int
+auth1_setlen_sig(auth1_t *inp, size_t newlen)
+{
+ uint8_t *newptr;
+ newptr = trunnel_dynarray_setlen(&inp->sig.allocated_,
+ &inp->sig.n_, inp->sig.elts_, newlen,
+ sizeof(inp->sig.elts_[0]), (trunnel_free_fn_t) NULL,
+ &inp->trunnel_error_code_);
+ if (newptr == NULL)
+ goto trunnel_alloc_failed;
+ inp->sig.elts_ = newptr;
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+const char *
+auth1_check(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx)
+{
+ if (obj == NULL)
+ return "Object was NULL";
+ if (obj->trunnel_error_code_)
+ return "A set function failed on this object";
+ if (auth_ctx_ctx == NULL)
+ return "Context was NULL";
+ switch (auth_ctx_ctx->is_ed) {
+
+ case 0:
+ break;
+
+ case 1:
+ break;
+
+ default:
+ return "Bad tag for union";
+ break;
+ }
+ return NULL;
+}
+
+ssize_t
+auth1_encoded_len(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx)
+{
+ ssize_t result = 0;
+
+ if (NULL != auth1_check(obj, auth_ctx_ctx))
+ return -1;
+
+
+ /* Length of u8 type[8] */
+ result += 8;
+
+ /* Length of u8 cid[32] */
+ result += 32;
+
+ /* Length of u8 sid[32] */
+ result += 32;
+ switch (auth_ctx_ctx->is_ed) {
+
+ case 0:
+ break;
+
+ case 1:
+
+ /* Length of u8 u1_cid_ed[32] */
+ result += 32;
+
+ /* Length of u8 u1_sid_ed[32] */
+ result += 32;
+ break;
+
+ default:
+ trunnel_assert(0);
+ break;
+ }
+
+ /* Length of u8 slog[32] */
+ result += 32;
+
+ /* Length of u8 clog[32] */
+ result += 32;
+
+ /* Length of u8 scert[32] */
+ result += 32;
+
+ /* Length of u8 tlssecrets[32] */
+ result += 32;
+
+ /* Length of u8 rand[24] */
+ result += 24;
+
+ /* Length of u8 sig[] */
+ result += TRUNNEL_DYNARRAY_LEN(&obj->sig);
+ return result;
+}
+int
+auth1_clear_errors(auth1_t *obj)
+{
+ int r = obj->trunnel_error_code_;
+ obj->trunnel_error_code_ = 0;
+ return r;
+}
+ssize_t
+auth1_encode(uint8_t *output, const size_t avail, const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx)
+{
+ ssize_t result = 0;
+ size_t written = 0;
+ uint8_t *ptr = output;
+ const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ const ssize_t encoded_len = auth1_encoded_len(obj, auth_ctx_ctx);
+#endif
+
+ if (NULL != (msg = auth1_check(obj, auth_ctx_ctx)))
+ goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ trunnel_assert(encoded_len >= 0);
+#endif
+
+ /* Encode u8 type[8] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 8)
+ goto truncated;
+ memcpy(ptr, obj->type, 8);
+ written += 8; ptr += 8;
+
+ /* Encode u8 cid[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->cid, 32);
+ written += 32; ptr += 32;
+
+ /* Encode u8 sid[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->sid, 32);
+ written += 32; ptr += 32;
+
+ /* Encode union u1[auth_ctx.is_ed] */
+ trunnel_assert(written <= avail);
+ switch (auth_ctx_ctx->is_ed) {
+
+ case 0:
+ break;
+
+ case 1:
+
+ /* Encode u8 u1_cid_ed[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->u1_cid_ed, 32);
+ written += 32; ptr += 32;
+
+ /* Encode u8 u1_sid_ed[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->u1_sid_ed, 32);
+ written += 32; ptr += 32;
+ break;
+
+ default:
+ trunnel_assert(0);
+ break;
+ }
+
+ /* Encode u8 slog[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->slog, 32);
+ written += 32; ptr += 32;
+
+ /* Encode u8 clog[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->clog, 32);
+ written += 32; ptr += 32;
+
+ /* Encode u8 scert[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->scert, 32);
+ written += 32; ptr += 32;
+
+ /* Encode u8 tlssecrets[32] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 32)
+ goto truncated;
+ memcpy(ptr, obj->tlssecrets, 32);
+ written += 32; ptr += 32;
+
+ /* Encode u8 rand[24] */
+ trunnel_assert(written <= avail);
+ if (avail - written < 24)
+ goto truncated;
+ memcpy(ptr, obj->rand, 24);
+ written += 24; ptr += 24;
+
+ /* Encode u8 sig[] */
+ {
+ size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->sig);
+ trunnel_assert(written <= avail);
+ if (avail - written < elt_len)
+ goto truncated;
+ memcpy(ptr, obj->sig.elts_, elt_len);
+ written += elt_len; ptr += elt_len;
+ }
+
+
+ trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ {
+ trunnel_assert(encoded_len >= 0);
+ trunnel_assert((size_t)encoded_len == written);
+ }
+
+#endif
+
+ return written;
+
+ truncated:
+ result = -2;
+ goto fail;
+ check_failed:
+ (void)msg;
+ result = -1;
+ goto fail;
+ fail:
+ trunnel_assert(result < 0);
+ return result;
+}
+
+/** As auth1_parse(), but do not allocate the output object.
+ */
+static ssize_t
+auth1_parse_into(auth1_t *obj, const uint8_t *input, const size_t len_in, const auth_ctx_t *auth_ctx_ctx)
+{
+ const uint8_t *ptr = input;
+ size_t remaining = len_in;
+ ssize_t result = 0;
+ (void)result;
+ if (auth_ctx_ctx == NULL)
+ return -1;
+
+ /* Parse u8 type[8] */
+ CHECK_REMAINING(8, truncated);
+ memcpy(obj->type, ptr, 8);
+ remaining -= 8; ptr += 8;
+
+ /* Parse u8 cid[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->cid, ptr, 32);
+ remaining -= 32; ptr += 32;
+
+ /* Parse u8 sid[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->sid, ptr, 32);
+ remaining -= 32; ptr += 32;
+
+ /* Parse union u1[auth_ctx.is_ed] */
+ switch (auth_ctx_ctx->is_ed) {
+
+ case 0:
+ break;
+
+ case 1:
+
+ /* Parse u8 u1_cid_ed[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->u1_cid_ed, ptr, 32);
+ remaining -= 32; ptr += 32;
+
+ /* Parse u8 u1_sid_ed[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->u1_sid_ed, ptr, 32);
+ remaining -= 32; ptr += 32;
+ break;
+
+ default:
+ goto fail;
+ break;
+ }
+
+ /* Parse u8 slog[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->slog, ptr, 32);
+ remaining -= 32; ptr += 32;
+
+ /* Parse u8 clog[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->clog, ptr, 32);
+ remaining -= 32; ptr += 32;
+
+ /* Parse u8 scert[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->scert, ptr, 32);
+ remaining -= 32; ptr += 32;
+
+ /* Parse u8 tlssecrets[32] */
+ CHECK_REMAINING(32, truncated);
+ memcpy(obj->tlssecrets, ptr, 32);
+ remaining -= 32; ptr += 32;
+ obj->end_of_fixed_part = ptr;
+
+ /* Parse u8 rand[24] */
+ CHECK_REMAINING(24, truncated);
+ memcpy(obj->rand, ptr, 24);
+ remaining -= 24; ptr += 24;
+ obj->end_of_signed = ptr;
+
+ /* Parse u8 sig[] */
+ TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->sig, remaining, {});
+ obj->sig.n_ = remaining;
+ memcpy(obj->sig.elts_, ptr, remaining);
+ ptr += remaining; remaining -= remaining;
+ trunnel_assert(ptr + remaining == input + len_in);
+ return len_in - remaining;
+
+ truncated:
+ return -2;
+ trunnel_alloc_failed:
+ return -1;
+ fail:
+ result = -1;
+ return result;
+}
+
+ssize_t
+auth1_parse(auth1_t **output, const uint8_t *input, const size_t len_in, const auth_ctx_t *auth_ctx_ctx)
+{
+ ssize_t result;
+ *output = auth1_new();
+ if (NULL == *output)
+ return -1;
+ result = auth1_parse_into(*output, input, len_in, auth_ctx_ctx);
+ if (result < 0) {
+ auth1_free(*output);
+ *output = NULL;
+ }
+ return result;
+}
+certs_cell_t *
+certs_cell_new(void)
+{
+ certs_cell_t *val = trunnel_calloc(1, sizeof(certs_cell_t));
+ if (NULL == val)
+ return NULL;
+ return val;
+}
+
+/** Release all storage held inside 'obj', but do not free 'obj'.
+ */
+static void
+certs_cell_clear(certs_cell_t *obj)
+{
+ (void) obj;
+ {
+
+ unsigned idx;
+ for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) {
+ certs_cell_cert_free(TRUNNEL_DYNARRAY_GET(&obj->certs, idx));
+ }
+ }
+ TRUNNEL_DYNARRAY_WIPE(&obj->certs);
+ TRUNNEL_DYNARRAY_CLEAR(&obj->certs);
+}
+
+void
+certs_cell_free(certs_cell_t *obj)
+{
+ if (obj == NULL)
+ return;
+ certs_cell_clear(obj);
+ trunnel_memwipe(obj, sizeof(certs_cell_t));
+ trunnel_free_(obj);
+}
+
+uint8_t
+certs_cell_get_n_certs(certs_cell_t *inp)
+{
+ return inp->n_certs;
+}
+int
+certs_cell_set_n_certs(certs_cell_t *inp, uint8_t val)
+{
+ inp->n_certs = val;
+ return 0;
+}
+size_t
+certs_cell_getlen_certs(const certs_cell_t *inp)
+{
+ return TRUNNEL_DYNARRAY_LEN(&inp->certs);
+}
+
+struct certs_cell_cert_st *
+certs_cell_get_certs(certs_cell_t *inp, size_t idx)
+{
+ return TRUNNEL_DYNARRAY_GET(&inp->certs, idx);
+}
+
+int
+certs_cell_set_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt)
+{
+ certs_cell_cert_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->certs, idx);
+ if (oldval && oldval != elt)
+ certs_cell_cert_free(oldval);
+ return certs_cell_set0_certs(inp, idx, elt);
+}
+int
+certs_cell_set0_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt)
+{
+ TRUNNEL_DYNARRAY_SET(&inp->certs, idx, elt);
+ return 0;
+}
+int
+certs_cell_add_certs(certs_cell_t *inp, struct certs_cell_cert_st * elt)
+{
+#if SIZE_MAX >= UINT8_MAX
+ if (inp->certs.n_ == UINT8_MAX)
+ goto trunnel_alloc_failed;
+#endif
+ TRUNNEL_DYNARRAY_ADD(struct certs_cell_cert_st *, &inp->certs, elt, {});
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+
+struct certs_cell_cert_st * *
+certs_cell_getarray_certs(certs_cell_t *inp)
+{
+ return inp->certs.elts_;
+}
+int
+certs_cell_setlen_certs(certs_cell_t *inp, size_t newlen)
+{
+ struct certs_cell_cert_st * *newptr;
+#if UINT8_MAX < SIZE_MAX
+ if (newlen > UINT8_MAX)
+ goto trunnel_alloc_failed;
+#endif
+ newptr = trunnel_dynarray_setlen(&inp->certs.allocated_,
+ &inp->certs.n_, inp->certs.elts_, newlen,
+ sizeof(inp->certs.elts_[0]), (trunnel_free_fn_t) certs_cell_cert_free,
+ &inp->trunnel_error_code_);
+ if (newptr == NULL)
+ goto trunnel_alloc_failed;
+ inp->certs.elts_ = newptr;
+ return 0;
+ trunnel_alloc_failed:
+ TRUNNEL_SET_ERROR_CODE(inp);
+ return -1;
+}
+const char *
+certs_cell_check(const certs_cell_t *obj)
+{
+ if (obj == NULL)
+ return "Object was NULL";
+ if (obj->trunnel_error_code_)
+ return "A set function failed on this object";
+ {
+ const char *msg;
+
+ unsigned idx;
+ for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) {
+ if (NULL != (msg = certs_cell_cert_check(TRUNNEL_DYNARRAY_GET(&obj->certs, idx))))
+ return msg;
+ }
+ }
+ if (TRUNNEL_DYNARRAY_LEN(&obj->certs) != obj->n_certs)
+ return "Length mismatch for certs";
+ return NULL;
+}
+
+ssize_t
+certs_cell_encoded_len(const certs_cell_t *obj)
+{
+ ssize_t result = 0;
+
+ if (NULL != certs_cell_check(obj))
+ return -1;
+
+
+ /* Length of u8 n_certs */
+ result += 1;
+
+ /* Length of struct certs_cell_cert certs[n_certs] */
+ {
+
+ unsigned idx;
+ for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) {
+ result += certs_cell_cert_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->certs, idx));
+ }
+ }
+ return result;
+}
+int
+certs_cell_clear_errors(certs_cell_t *obj)
+{
+ int r = obj->trunnel_error_code_;
+ obj->trunnel_error_code_ = 0;
+ return r;
+}
+ssize_t
+certs_cell_encode(uint8_t *output, const size_t avail, const certs_cell_t *obj)
+{
+ ssize_t result = 0;
+ size_t written = 0;
+ uint8_t *ptr = output;
+ const char *msg;
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ const ssize_t encoded_len = certs_cell_encoded_len(obj);
+#endif
+
+ if (NULL != (msg = certs_cell_check(obj)))
+ goto check_failed;
+
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ trunnel_assert(encoded_len >= 0);
+#endif
+
+ /* Encode u8 n_certs */
+ trunnel_assert(written <= avail);
+ if (avail - written < 1)
+ goto truncated;
+ trunnel_set_uint8(ptr, (obj->n_certs));
+ written += 1; ptr += 1;
+
+ /* Encode struct certs_cell_cert certs[n_certs] */
+ {
+
+ unsigned idx;
+ for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->certs); ++idx) {
+ trunnel_assert(written <= avail);
+ result = certs_cell_cert_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->certs, idx));
+ if (result < 0)
+ goto fail; /* XXXXXXX !*/
+ written += result; ptr += result;
+ }
+ }
+
+
+ trunnel_assert(ptr == output + written);
+#ifdef TRUNNEL_CHECK_ENCODED_LEN
+ {
+ trunnel_assert(encoded_len >= 0);
+ trunnel_assert((size_t)encoded_len == written);
+ }
+
+#endif
+
+ return written;
+
+ truncated:
+ result = -2;
+ goto fail;
+ check_failed:
+ (void)msg;
+ result = -1;
+ goto fail;
+ fail:
+ trunnel_assert(result < 0);
+ return result;
+}
+
+/** As certs_cell_parse(), but do not allocate the output object.
+ */
+static ssize_t
+certs_cell_parse_into(certs_cell_t *obj, const uint8_t *input, const size_t len_in)
+{
+ const uint8_t *ptr = input;
+ size_t remaining = len_in;
+ ssize_t result = 0;
+ (void)result;
+
+ /* Parse u8 n_certs */
+ CHECK_REMAINING(1, truncated);
+ obj->n_certs = (trunnel_get_uint8(ptr));
+ remaining -= 1; ptr += 1;
+
+ /* Parse struct certs_cell_cert certs[n_certs] */
+ TRUNNEL_DYNARRAY_EXPAND(certs_cell_cert_t *, &obj->certs, obj->n_certs, {});
+ {
+ certs_cell_cert_t * elt;
+ unsigned idx;
+ for (idx = 0; idx < obj->n_certs; ++idx) {
+ result = certs_cell_cert_parse(&elt, ptr, remaining);
+ if (result < 0)
+ goto relay_fail;
+ trunnel_assert((size_t)result <= remaining);
+ remaining -= result; ptr += result;
+ TRUNNEL_DYNARRAY_ADD(certs_cell_cert_t *, &obj->certs, elt, {certs_cell_cert_free(elt);});
+ }
+ }
+ trunnel_assert(ptr + remaining == input + len_in);
+ return len_in - remaining;
+
+ truncated:
+ return -2;
+ relay_fail:
+ trunnel_assert(result < 0);
+ return result;
+ trunnel_alloc_failed:
+ return -1;
+}
+
+ssize_t
+certs_cell_parse(certs_cell_t **output, const uint8_t *input, const size_t len_in)
+{
+ ssize_t result;
+ *output = certs_cell_new();
+ if (NULL == *output)
+ return -1;
+ result = certs_cell_parse_into(*output, input, len_in);
+ if (result < 0) {
+ certs_cell_free(*output);
+ *output = NULL;
+ }
+ return result;
+}
diff --git a/src/trunnel/link_handshake.h b/src/trunnel/link_handshake.h
new file mode 100644
index 0000000000..6da6599e5a
--- /dev/null
+++ b/src/trunnel/link_handshake.h
@@ -0,0 +1,654 @@
+/* link_handshake.h -- generated by by Trunnel v1.4.1.
+ * https://gitweb.torproject.org/trunnel.git
+ * You probably shouldn't edit this file.
+ */
+#ifndef TRUNNEL_LINK_HANDSHAKE_H
+#define TRUNNEL_LINK_HANDSHAKE_H
+
+#include <stdint.h>
+#include "trunnel.h"
+
+#define CERTTYPE_RSA1024_ID_LINK 1
+#define CERTTYPE_RSA1024_ID_ID 2
+#define CERTTYPE_RSA1024_ID_AUTH 3
+#define CERTTYPE_ED_ID_SIGN 4
+#define CERTTYPE_ED_SIGN_LINK 5
+#define CERTTYPE_ED_SIGN_AUTH 6
+#define CERTTYPE_RSA1024_ID_EDID 7
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_AUTH_CHALLENGE_CELL)
+struct auth_challenge_cell_st {
+ uint8_t challenge[32];
+ uint16_t n_methods;
+ TRUNNEL_DYNARRAY_HEAD(, uint16_t) methods;
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct auth_challenge_cell_st auth_challenge_cell_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_AUTH_CTX)
+struct auth_ctx_st {
+ uint8_t is_ed;
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct auth_ctx_st auth_ctx_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_CERTS_CELL_CERT)
+struct certs_cell_cert_st {
+ uint8_t cert_type;
+ uint16_t cert_len;
+ TRUNNEL_DYNARRAY_HEAD(, uint8_t) body;
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct certs_cell_cert_st certs_cell_cert_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_RSA_ED_CROSSCERT)
+struct rsa_ed_crosscert_st {
+ uint8_t ed_key[32];
+ uint32_t expiration;
+ const uint8_t *end_of_signed;
+ uint8_t sig_len;
+ TRUNNEL_DYNARRAY_HEAD(, uint8_t) sig;
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct rsa_ed_crosscert_st rsa_ed_crosscert_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_AUTH1)
+struct auth1_st {
+ uint8_t type[8];
+ uint8_t cid[32];
+ uint8_t sid[32];
+ uint8_t u1_cid_ed[32];
+ uint8_t u1_sid_ed[32];
+ uint8_t slog[32];
+ uint8_t clog[32];
+ uint8_t scert[32];
+ uint8_t tlssecrets[32];
+ const uint8_t *end_of_fixed_part;
+ uint8_t rand[24];
+ const uint8_t *end_of_signed;
+ TRUNNEL_DYNARRAY_HEAD(, uint8_t) sig;
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct auth1_st auth1_t;
+#if !defined(TRUNNEL_OPAQUE) && !defined(TRUNNEL_OPAQUE_CERTS_CELL)
+struct certs_cell_st {
+ uint8_t n_certs;
+ TRUNNEL_DYNARRAY_HEAD(, struct certs_cell_cert_st *) certs;
+ uint8_t trunnel_error_code_;
+};
+#endif
+typedef struct certs_cell_st certs_cell_t;
+/** Return a newly allocated auth_challenge_cell with all elements set
+ * to zero.
+ */
+auth_challenge_cell_t *auth_challenge_cell_new(void);
+/** Release all storage held by the auth_challenge_cell in 'victim'.
+ * (Do nothing if 'victim' is NULL.)
+ */
+void auth_challenge_cell_free(auth_challenge_cell_t *victim);
+/** Try to parse a auth_challenge_cell from the buffer in 'input',
+ * using up to 'len_in' bytes from the input buffer. On success,
+ * return the number of bytes consumed and set *output to the newly
+ * allocated auth_challenge_cell_t. On failure, return -2 if the input
+ * appears truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t auth_challenge_cell_parse(auth_challenge_cell_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * auth_challenge_cell in 'obj'. On failure, return a negative value.
+ * Note that this value may be an overestimate, and can even be an
+ * underestimate for certain unencodeable objects.
+ */
+ssize_t auth_challenge_cell_encoded_len(const auth_challenge_cell_t *obj);
+/** Try to encode the auth_challenge_cell from 'input' into the buffer
+ * at 'output', using up to 'avail' bytes of the output buffer. On
+ * success, return the number of bytes used. On failure, return -2 if
+ * the buffer was not long enough, and -1 if the input was invalid.
+ */
+ssize_t auth_challenge_cell_encode(uint8_t *output, const size_t avail, const auth_challenge_cell_t *input);
+/** Check whether the internal state of the auth_challenge_cell in
+ * 'obj' is consistent. Return NULL if it is, and a short message if
+ * it is not.
+ */
+const char *auth_challenge_cell_check(const auth_challenge_cell_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int auth_challenge_cell_clear_errors(auth_challenge_cell_t *obj);
+/** Return the (constant) length of the array holding the challenge
+ * field of the auth_challenge_cell_t in 'inp'.
+ */
+size_t auth_challenge_cell_getlen_challenge(const auth_challenge_cell_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * challenge of the auth_challenge_cell_t in 'inp'.
+ */
+uint8_t auth_challenge_cell_get_challenge(const auth_challenge_cell_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * challenge of the auth_challenge_cell_t in 'inp', so that it will
+ * hold the value 'elt'.
+ */
+int auth_challenge_cell_set_challenge(auth_challenge_cell_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field challenge of 'inp'.
+ */
+uint8_t * auth_challenge_cell_getarray_challenge(auth_challenge_cell_t *inp);
+/** Return the value of the n_methods field of the
+ * auth_challenge_cell_t in 'inp'
+ */
+uint16_t auth_challenge_cell_get_n_methods(auth_challenge_cell_t *inp);
+/** Set the value of the n_methods field of the auth_challenge_cell_t
+ * in 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int auth_challenge_cell_set_n_methods(auth_challenge_cell_t *inp, uint16_t val);
+/** Return the length of the dynamic array holding the methods field
+ * of the auth_challenge_cell_t in 'inp'.
+ */
+size_t auth_challenge_cell_getlen_methods(const auth_challenge_cell_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * methods of the auth_challenge_cell_t in 'inp'.
+ */
+uint16_t auth_challenge_cell_get_methods(auth_challenge_cell_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * methods of the auth_challenge_cell_t in 'inp', so that it will hold
+ * the value 'elt'.
+ */
+int auth_challenge_cell_set_methods(auth_challenge_cell_t *inp, size_t idx, uint16_t elt);
+/** Append a new element 'elt' to the dynamic array field methods of
+ * the auth_challenge_cell_t in 'inp'.
+ */
+int auth_challenge_cell_add_methods(auth_challenge_cell_t *inp, uint16_t elt);
+/** Return a pointer to the variable-length array field methods of
+ * 'inp'.
+ */
+uint16_t * auth_challenge_cell_getarray_methods(auth_challenge_cell_t *inp);
+/** Change the length of the variable-length array field methods of
+ * 'inp' to 'newlen'.Fill extra elements with 0. Return 0 on success;
+ * return -1 and set the error code on 'inp' on failure.
+ */
+int auth_challenge_cell_setlen_methods(auth_challenge_cell_t *inp, size_t newlen);
+/** Return a newly allocated auth_ctx with all elements set to zero.
+ */
+auth_ctx_t *auth_ctx_new(void);
+/** Release all storage held by the auth_ctx in 'victim'. (Do nothing
+ * if 'victim' is NULL.)
+ */
+void auth_ctx_free(auth_ctx_t *victim);
+/** Return the value of the is_ed field of the auth_ctx_t in 'inp'
+ */
+uint8_t auth_ctx_get_is_ed(auth_ctx_t *inp);
+/** Set the value of the is_ed field of the auth_ctx_t in 'inp' to
+ * 'val'. Return 0 on success; return -1 and set the error code on
+ * 'inp' on failure.
+ */
+int auth_ctx_set_is_ed(auth_ctx_t *inp, uint8_t val);
+/** Return a newly allocated certs_cell_cert with all elements set to
+ * zero.
+ */
+certs_cell_cert_t *certs_cell_cert_new(void);
+/** Release all storage held by the certs_cell_cert in 'victim'. (Do
+ * nothing if 'victim' is NULL.)
+ */
+void certs_cell_cert_free(certs_cell_cert_t *victim);
+/** Try to parse a certs_cell_cert from the buffer in 'input', using
+ * up to 'len_in' bytes from the input buffer. On success, return the
+ * number of bytes consumed and set *output to the newly allocated
+ * certs_cell_cert_t. On failure, return -2 if the input appears
+ * truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t certs_cell_cert_parse(certs_cell_cert_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * certs_cell_cert in 'obj'. On failure, return a negative value. Note
+ * that this value may be an overestimate, and can even be an
+ * underestimate for certain unencodeable objects.
+ */
+ssize_t certs_cell_cert_encoded_len(const certs_cell_cert_t *obj);
+/** Try to encode the certs_cell_cert from 'input' into the buffer at
+ * 'output', using up to 'avail' bytes of the output buffer. On
+ * success, return the number of bytes used. On failure, return -2 if
+ * the buffer was not long enough, and -1 if the input was invalid.
+ */
+ssize_t certs_cell_cert_encode(uint8_t *output, const size_t avail, const certs_cell_cert_t *input);
+/** Check whether the internal state of the certs_cell_cert in 'obj'
+ * is consistent. Return NULL if it is, and a short message if it is
+ * not.
+ */
+const char *certs_cell_cert_check(const certs_cell_cert_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int certs_cell_cert_clear_errors(certs_cell_cert_t *obj);
+/** Return the value of the cert_type field of the certs_cell_cert_t
+ * in 'inp'
+ */
+uint8_t certs_cell_cert_get_cert_type(certs_cell_cert_t *inp);
+/** Set the value of the cert_type field of the certs_cell_cert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int certs_cell_cert_set_cert_type(certs_cell_cert_t *inp, uint8_t val);
+/** Return the value of the cert_len field of the certs_cell_cert_t in
+ * 'inp'
+ */
+uint16_t certs_cell_cert_get_cert_len(certs_cell_cert_t *inp);
+/** Set the value of the cert_len field of the certs_cell_cert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int certs_cell_cert_set_cert_len(certs_cell_cert_t *inp, uint16_t val);
+/** Return the length of the dynamic array holding the body field of
+ * the certs_cell_cert_t in 'inp'.
+ */
+size_t certs_cell_cert_getlen_body(const certs_cell_cert_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * body of the certs_cell_cert_t in 'inp'.
+ */
+uint8_t certs_cell_cert_get_body(certs_cell_cert_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * body of the certs_cell_cert_t in 'inp', so that it will hold the
+ * value 'elt'.
+ */
+int certs_cell_cert_set_body(certs_cell_cert_t *inp, size_t idx, uint8_t elt);
+/** Append a new element 'elt' to the dynamic array field body of the
+ * certs_cell_cert_t in 'inp'.
+ */
+int certs_cell_cert_add_body(certs_cell_cert_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field body of 'inp'.
+ */
+uint8_t * certs_cell_cert_getarray_body(certs_cell_cert_t *inp);
+/** Change the length of the variable-length array field body of 'inp'
+ * to 'newlen'.Fill extra elements with 0. Return 0 on success; return
+ * -1 and set the error code on 'inp' on failure.
+ */
+int certs_cell_cert_setlen_body(certs_cell_cert_t *inp, size_t newlen);
+/** Return a newly allocated rsa_ed_crosscert with all elements set to
+ * zero.
+ */
+rsa_ed_crosscert_t *rsa_ed_crosscert_new(void);
+/** Release all storage held by the rsa_ed_crosscert in 'victim'. (Do
+ * nothing if 'victim' is NULL.)
+ */
+void rsa_ed_crosscert_free(rsa_ed_crosscert_t *victim);
+/** Try to parse a rsa_ed_crosscert from the buffer in 'input', using
+ * up to 'len_in' bytes from the input buffer. On success, return the
+ * number of bytes consumed and set *output to the newly allocated
+ * rsa_ed_crosscert_t. On failure, return -2 if the input appears
+ * truncated, and -1 if the input is otherwise invalid.
+ */
+ssize_t rsa_ed_crosscert_parse(rsa_ed_crosscert_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * rsa_ed_crosscert in 'obj'. On failure, return a negative value.
+ * Note that this value may be an overestimate, and can even be an
+ * underestimate for certain unencodeable objects.
+ */
+ssize_t rsa_ed_crosscert_encoded_len(const rsa_ed_crosscert_t *obj);
+/** Try to encode the rsa_ed_crosscert from 'input' into the buffer at
+ * 'output', using up to 'avail' bytes of the output buffer. On
+ * success, return the number of bytes used. On failure, return -2 if
+ * the buffer was not long enough, and -1 if the input was invalid.
+ */
+ssize_t rsa_ed_crosscert_encode(uint8_t *output, const size_t avail, const rsa_ed_crosscert_t *input);
+/** Check whether the internal state of the rsa_ed_crosscert in 'obj'
+ * is consistent. Return NULL if it is, and a short message if it is
+ * not.
+ */
+const char *rsa_ed_crosscert_check(const rsa_ed_crosscert_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int rsa_ed_crosscert_clear_errors(rsa_ed_crosscert_t *obj);
+/** Return the (constant) length of the array holding the ed_key field
+ * of the rsa_ed_crosscert_t in 'inp'.
+ */
+size_t rsa_ed_crosscert_getlen_ed_key(const rsa_ed_crosscert_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * ed_key of the rsa_ed_crosscert_t in 'inp'.
+ */
+uint8_t rsa_ed_crosscert_get_ed_key(const rsa_ed_crosscert_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * ed_key of the rsa_ed_crosscert_t in 'inp', so that it will hold the
+ * value 'elt'.
+ */
+int rsa_ed_crosscert_set_ed_key(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field ed_key of 'inp'.
+ */
+uint8_t * rsa_ed_crosscert_getarray_ed_key(rsa_ed_crosscert_t *inp);
+/** Return the value of the expiration field of the rsa_ed_crosscert_t
+ * in 'inp'
+ */
+uint32_t rsa_ed_crosscert_get_expiration(rsa_ed_crosscert_t *inp);
+/** Set the value of the expiration field of the rsa_ed_crosscert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int rsa_ed_crosscert_set_expiration(rsa_ed_crosscert_t *inp, uint32_t val);
+/** Return the position for end_of_signed when we parsed this object
+ */
+const uint8_t * rsa_ed_crosscert_get_end_of_signed(const rsa_ed_crosscert_t *inp);
+/** Return the value of the sig_len field of the rsa_ed_crosscert_t in
+ * 'inp'
+ */
+uint8_t rsa_ed_crosscert_get_sig_len(rsa_ed_crosscert_t *inp);
+/** Set the value of the sig_len field of the rsa_ed_crosscert_t in
+ * 'inp' to 'val'. Return 0 on success; return -1 and set the error
+ * code on 'inp' on failure.
+ */
+int rsa_ed_crosscert_set_sig_len(rsa_ed_crosscert_t *inp, uint8_t val);
+/** Return the length of the dynamic array holding the sig field of
+ * the rsa_ed_crosscert_t in 'inp'.
+ */
+size_t rsa_ed_crosscert_getlen_sig(const rsa_ed_crosscert_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * sig of the rsa_ed_crosscert_t in 'inp'.
+ */
+uint8_t rsa_ed_crosscert_get_sig(rsa_ed_crosscert_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * sig of the rsa_ed_crosscert_t in 'inp', so that it will hold the
+ * value 'elt'.
+ */
+int rsa_ed_crosscert_set_sig(rsa_ed_crosscert_t *inp, size_t idx, uint8_t elt);
+/** Append a new element 'elt' to the dynamic array field sig of the
+ * rsa_ed_crosscert_t in 'inp'.
+ */
+int rsa_ed_crosscert_add_sig(rsa_ed_crosscert_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field sig of 'inp'.
+ */
+uint8_t * rsa_ed_crosscert_getarray_sig(rsa_ed_crosscert_t *inp);
+/** Change the length of the variable-length array field sig of 'inp'
+ * to 'newlen'.Fill extra elements with 0. Return 0 on success; return
+ * -1 and set the error code on 'inp' on failure.
+ */
+int rsa_ed_crosscert_setlen_sig(rsa_ed_crosscert_t *inp, size_t newlen);
+/** Return a newly allocated auth1 with all elements set to zero.
+ */
+auth1_t *auth1_new(void);
+/** Release all storage held by the auth1 in 'victim'. (Do nothing if
+ * 'victim' is NULL.)
+ */
+void auth1_free(auth1_t *victim);
+/** Try to parse a auth1 from the buffer in 'input', using up to
+ * 'len_in' bytes from the input buffer. On success, return the number
+ * of bytes consumed and set *output to the newly allocated auth1_t.
+ * On failure, return -2 if the input appears truncated, and -1 if the
+ * input is otherwise invalid.
+ */
+ssize_t auth1_parse(auth1_t **output, const uint8_t *input, const size_t len_in, const auth_ctx_t *auth_ctx_ctx);
+/** Return the number of bytes we expect to need to encode the auth1
+ * in 'obj'. On failure, return a negative value. Note that this value
+ * may be an overestimate, and can even be an underestimate for
+ * certain unencodeable objects.
+ */
+ssize_t auth1_encoded_len(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx);
+/** Try to encode the auth1 from 'input' into the buffer at 'output',
+ * using up to 'avail' bytes of the output buffer. On success, return
+ * the number of bytes used. On failure, return -2 if the buffer was
+ * not long enough, and -1 if the input was invalid.
+ */
+ssize_t auth1_encode(uint8_t *output, const size_t avail, const auth1_t *input, const auth_ctx_t *auth_ctx_ctx);
+/** Check whether the internal state of the auth1 in 'obj' is
+ * consistent. Return NULL if it is, and a short message if it is not.
+ */
+const char *auth1_check(const auth1_t *obj, const auth_ctx_t *auth_ctx_ctx);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int auth1_clear_errors(auth1_t *obj);
+/** Return the (constant) length of the array holding the type field
+ * of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_type(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field type
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_type(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field type
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_type(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 8-element array field type of 'inp'.
+ */
+uint8_t * auth1_getarray_type(auth1_t *inp);
+/** Return the (constant) length of the array holding the cid field of
+ * the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_cid(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field cid
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_cid(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field cid
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_cid(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field cid of 'inp'.
+ */
+uint8_t * auth1_getarray_cid(auth1_t *inp);
+/** Return the (constant) length of the array holding the sid field of
+ * the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_sid(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field sid
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_sid(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field sid
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_sid(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field sid of 'inp'.
+ */
+uint8_t * auth1_getarray_sid(auth1_t *inp);
+/** Return the (constant) length of the array holding the u1_cid_ed
+ * field of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_u1_cid_ed(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * u1_cid_ed of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_u1_cid_ed(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * u1_cid_ed of the auth1_t in 'inp', so that it will hold the value
+ * 'elt'.
+ */
+int auth1_set_u1_cid_ed(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field u1_cid_ed of 'inp'.
+ */
+uint8_t * auth1_getarray_u1_cid_ed(auth1_t *inp);
+/** Return the (constant) length of the array holding the u1_sid_ed
+ * field of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_u1_sid_ed(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * u1_sid_ed of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_u1_sid_ed(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * u1_sid_ed of the auth1_t in 'inp', so that it will hold the value
+ * 'elt'.
+ */
+int auth1_set_u1_sid_ed(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field u1_sid_ed of 'inp'.
+ */
+uint8_t * auth1_getarray_u1_sid_ed(auth1_t *inp);
+/** Return the (constant) length of the array holding the slog field
+ * of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_slog(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field slog
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_slog(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field slog
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_slog(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field slog of 'inp'.
+ */
+uint8_t * auth1_getarray_slog(auth1_t *inp);
+/** Return the (constant) length of the array holding the clog field
+ * of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_clog(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field clog
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_clog(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field clog
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_clog(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field clog of 'inp'.
+ */
+uint8_t * auth1_getarray_clog(auth1_t *inp);
+/** Return the (constant) length of the array holding the scert field
+ * of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_scert(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * scert of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_scert(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * scert of the auth1_t in 'inp', so that it will hold the value
+ * 'elt'.
+ */
+int auth1_set_scert(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field scert of 'inp'.
+ */
+uint8_t * auth1_getarray_scert(auth1_t *inp);
+/** Return the (constant) length of the array holding the tlssecrets
+ * field of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_tlssecrets(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field
+ * tlssecrets of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_tlssecrets(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field
+ * tlssecrets of the auth1_t in 'inp', so that it will hold the value
+ * 'elt'.
+ */
+int auth1_set_tlssecrets(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 32-element array field tlssecrets of
+ * 'inp'.
+ */
+uint8_t * auth1_getarray_tlssecrets(auth1_t *inp);
+/** Return the position for end_of_fixed_part when we parsed this
+ * object
+ */
+const uint8_t * auth1_get_end_of_fixed_part(const auth1_t *inp);
+/** Return the (constant) length of the array holding the rand field
+ * of the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_rand(const auth1_t *inp);
+/** Return the element at position 'idx' of the fixed array field rand
+ * of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_rand(const auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the fixed array field rand
+ * of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_rand(auth1_t *inp, size_t idx, uint8_t elt);
+/** Return a pointer to the 24-element array field rand of 'inp'.
+ */
+uint8_t * auth1_getarray_rand(auth1_t *inp);
+/** Return the position for end_of_signed when we parsed this object
+ */
+const uint8_t * auth1_get_end_of_signed(const auth1_t *inp);
+/** Return the length of the dynamic array holding the sig field of
+ * the auth1_t in 'inp'.
+ */
+size_t auth1_getlen_sig(const auth1_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * sig of the auth1_t in 'inp'.
+ */
+uint8_t auth1_get_sig(auth1_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * sig of the auth1_t in 'inp', so that it will hold the value 'elt'.
+ */
+int auth1_set_sig(auth1_t *inp, size_t idx, uint8_t elt);
+/** Append a new element 'elt' to the dynamic array field sig of the
+ * auth1_t in 'inp'.
+ */
+int auth1_add_sig(auth1_t *inp, uint8_t elt);
+/** Return a pointer to the variable-length array field sig of 'inp'.
+ */
+uint8_t * auth1_getarray_sig(auth1_t *inp);
+/** Change the length of the variable-length array field sig of 'inp'
+ * to 'newlen'.Fill extra elements with 0. Return 0 on success; return
+ * -1 and set the error code on 'inp' on failure.
+ */
+int auth1_setlen_sig(auth1_t *inp, size_t newlen);
+/** Return a newly allocated certs_cell with all elements set to zero.
+ */
+certs_cell_t *certs_cell_new(void);
+/** Release all storage held by the certs_cell in 'victim'. (Do
+ * nothing if 'victim' is NULL.)
+ */
+void certs_cell_free(certs_cell_t *victim);
+/** Try to parse a certs_cell from the buffer in 'input', using up to
+ * 'len_in' bytes from the input buffer. On success, return the number
+ * of bytes consumed and set *output to the newly allocated
+ * certs_cell_t. On failure, return -2 if the input appears truncated,
+ * and -1 if the input is otherwise invalid.
+ */
+ssize_t certs_cell_parse(certs_cell_t **output, const uint8_t *input, const size_t len_in);
+/** Return the number of bytes we expect to need to encode the
+ * certs_cell in 'obj'. On failure, return a negative value. Note that
+ * this value may be an overestimate, and can even be an underestimate
+ * for certain unencodeable objects.
+ */
+ssize_t certs_cell_encoded_len(const certs_cell_t *obj);
+/** Try to encode the certs_cell from 'input' into the buffer at
+ * 'output', using up to 'avail' bytes of the output buffer. On
+ * success, return the number of bytes used. On failure, return -2 if
+ * the buffer was not long enough, and -1 if the input was invalid.
+ */
+ssize_t certs_cell_encode(uint8_t *output, const size_t avail, const certs_cell_t *input);
+/** Check whether the internal state of the certs_cell in 'obj' is
+ * consistent. Return NULL if it is, and a short message if it is not.
+ */
+const char *certs_cell_check(const certs_cell_t *obj);
+/** Clear any errors that were set on the object 'obj' by its setter
+ * functions. Return true iff errors were cleared.
+ */
+int certs_cell_clear_errors(certs_cell_t *obj);
+/** Return the value of the n_certs field of the certs_cell_t in 'inp'
+ */
+uint8_t certs_cell_get_n_certs(certs_cell_t *inp);
+/** Set the value of the n_certs field of the certs_cell_t in 'inp' to
+ * 'val'. Return 0 on success; return -1 and set the error code on
+ * 'inp' on failure.
+ */
+int certs_cell_set_n_certs(certs_cell_t *inp, uint8_t val);
+/** Return the length of the dynamic array holding the certs field of
+ * the certs_cell_t in 'inp'.
+ */
+size_t certs_cell_getlen_certs(const certs_cell_t *inp);
+/** Return the element at position 'idx' of the dynamic array field
+ * certs of the certs_cell_t in 'inp'.
+ */
+struct certs_cell_cert_st * certs_cell_get_certs(certs_cell_t *inp, size_t idx);
+/** Change the element at position 'idx' of the dynamic array field
+ * certs of the certs_cell_t in 'inp', so that it will hold the value
+ * 'elt'. Free the previous value, if any.
+ */
+int certs_cell_set_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt);
+/** As certs_cell_set_certs, but does not free the previous value.
+ */
+int certs_cell_set0_certs(certs_cell_t *inp, size_t idx, struct certs_cell_cert_st * elt);
+/** Append a new element 'elt' to the dynamic array field certs of the
+ * certs_cell_t in 'inp'.
+ */
+int certs_cell_add_certs(certs_cell_t *inp, struct certs_cell_cert_st * elt);
+/** Return a pointer to the variable-length array field certs of
+ * 'inp'.
+ */
+struct certs_cell_cert_st * * certs_cell_getarray_certs(certs_cell_t *inp);
+/** Change the length of the variable-length array field certs of
+ * 'inp' to 'newlen'.Fill extra elements with NULL; free removed
+ * elements. Return 0 on success; return -1 and set the error code on
+ * 'inp' on failure.
+ */
+int certs_cell_setlen_certs(certs_cell_t *inp, size_t newlen);
+
+
+#endif
diff --git a/src/trunnel/link_handshake.trunnel b/src/trunnel/link_handshake.trunnel
new file mode 100644
index 0000000000..b858e17c60
--- /dev/null
+++ b/src/trunnel/link_handshake.trunnel
@@ -0,0 +1,57 @@
+
+struct certs_cell {
+ u8 n_certs;
+ struct certs_cell_cert certs[n_certs];
+}
+
+const CERTTYPE_RSA1024_ID_LINK = 1;
+const CERTTYPE_RSA1024_ID_ID = 2;
+const CERTTYPE_RSA1024_ID_AUTH = 3;
+const CERTTYPE_ED_ID_SIGN = 4;
+const CERTTYPE_ED_SIGN_LINK = 5;
+const CERTTYPE_ED_SIGN_AUTH = 6;
+const CERTTYPE_RSA1024_ID_EDID = 7;
+
+struct certs_cell_cert {
+ u8 cert_type;
+ u16 cert_len;
+ u8 body[cert_len];
+}
+
+struct rsa_ed_crosscert {
+ u8 ed_key[32];
+ u32 expiration;
+ @ptr end_of_signed;
+ u8 sig_len;
+ u8 sig[sig_len]; // mismatches spec.
+}
+
+struct auth_challenge_cell {
+ u8 challenge[32];
+ u16 n_methods;
+ u16 methods[n_methods];
+}
+
+context auth_ctx {
+ u8 is_ed;
+}
+
+struct auth1 with context auth_ctx {
+ u8 type[8];
+ u8 cid[32];
+ u8 sid[32];
+ union u1[auth_ctx.is_ed] {
+ 0 : ;
+ 1 : u8 cid_ed[32];
+ u8 sid_ed[32];
+ default: fail;
+ };
+ u8 slog[32];
+ u8 clog[32];
+ u8 scert[32];
+ u8 tlssecrets[32];
+ @ptr end_of_fixed_part;
+ u8 rand[24];
+ @ptr end_of_signed;
+ u8 sig[];
+}
diff --git a/src/trunnel/pwbox.c b/src/trunnel/pwbox.c
index bfea3ac671..28a4f74a7b 100644
--- a/src/trunnel/pwbox.c
+++ b/src/trunnel/pwbox.c
@@ -1,4 +1,4 @@
-/* pwbox.c -- generated by Trunnel v1.2.
+/* pwbox.c -- generated by Trunnel v1.4.1.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/trunnel/pwbox.h b/src/trunnel/pwbox.h
index 5b170eb45e..a6964b53b8 100644
--- a/src/trunnel/pwbox.h
+++ b/src/trunnel/pwbox.h
@@ -1,4 +1,4 @@
-/* pwbox.h -- generated by by Trunnel v1.2.
+/* pwbox.h -- generated by by Trunnel v1.4.1.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
diff --git a/src/win32/orconfig.h b/src/win32/orconfig.h
index b014a123f7..9c780e85d7 100644
--- a/src/win32/orconfig.h
+++ b/src/win32/orconfig.h
@@ -232,7 +232,7 @@
#define USING_TWOS_COMPLEMENT
/* Version number of package */
-#define VERSION "0.2.6.7-dev"
+#define VERSION "0.2.7.1-alpha-dev"