aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore14
-rw-r--r--ChangeLog597
-rw-r--r--Doxyfile.in6
-rw-r--r--Makefile.am69
-rw-r--r--ReleaseNotes1248
-rw-r--r--acinclude.m42
-rw-r--r--changes/.dummy37
-rw-r--r--changes/151883
-rw-r--r--changes/bug114475
-rw-r--r--changes/bug114546
-rw-r--r--changes/bug114575
-rw-r--r--changes/bug1249829
-rw-r--r--changes/bug14848_redux5
-rw-r--r--changes/bug149503
-rw-r--r--changes/bug149804
-rw-r--r--changes/bug149884
-rw-r--r--changes/bug149894
-rw-r--r--changes/bug150033
-rw-r--r--changes/bug150334
-rw-r--r--changes/bug150374
-rw-r--r--changes/bug150644
-rw-r--r--changes/bug1508310
-rw-r--r--changes/bug150884
-rw-r--r--changes/bug151513
-rw-r--r--changes/bug152055
-rw-r--r--changes/bug152406
-rw-r--r--changes/bug152455
-rw-r--r--changes/bug154364
-rw-r--r--changes/bug155154
-rw-r--r--changes/bug156005
-rw-r--r--changes/bug156014
-rw-r--r--changes/bug158234
-rw-r--r--changes/bug158504
-rw-r--r--changes/bug158813
-rw-r--r--changes/bug160604
-rw-r--r--changes/bug16115-NULL-getinfo-onions4
-rw-r--r--changes/bug16115-init-var4
-rw-r--r--changes/bug16115-signing-key-NULL-check6
-rw-r--r--changes/bug16115-spawn-comment6
-rw-r--r--changes/bug16115-undef-directive-in-macro6
-rw-r--r--changes/bug16115-unused-find-cipher7
-rw-r--r--changes/bug161523
-rw-r--r--changes/bug162284
-rw-r--r--changes/bug9495_redux4
-rw-r--r--changes/feature150064
-rw-r--r--changes/feature15817-clang-sanitizers7
-rw-r--r--changes/feature160525
-rw-r--r--changes/geoip-april20153
-rw-r--r--changes/geoip-march20153
-rw-r--r--changes/geoip6-april20152
-rw-r--r--changes/geoip6-march20153
-rw-r--r--changes/ticket144873
-rw-r--r--changes/ticket151763
-rw-r--r--changes/ticket152126
-rw-r--r--changes/ticket153583
-rw-r--r--changes/ticket160346
-rw-r--r--changes/ticket161406
-rw-r--r--configure.ac111
-rw-r--r--contrib/README7
-rw-r--r--contrib/clang/sanitize_blacklist.txt92
-rw-r--r--contrib/dist/tor.service.in6
-rw-r--r--contrib/win32build/tor-mingw.nsi.in2
-rw-r--r--doc/HACKING45
-rw-r--r--doc/WritingTests.txt167
-rw-r--r--doc/include.am4
-rw-r--r--doc/tor.1.txt102
-rw-r--r--scripts/codegen/makedesc.py355
-rwxr-xr-xscripts/codegen/run_trunnel.sh4
-rw-r--r--[-rwxr-xr-x]scripts/maint/checkOptionDocs.pl.in (renamed from scripts/maint/checkOptionDocs.pl)8
-rwxr-xr-xscripts/maint/format_changelog.py2
-rwxr-xr-xscripts/maint/lintChanges.py20
-rwxr-xr-xscripts/maint/sortChanges.py47
-rwxr-xr-xscripts/maint/updateVersions.pl.in (renamed from scripts/maint/updateVersions.pl)6
-rw-r--r--scripts/test/scan-build.sh8
-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.c771
-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/tortls.c594
-rw-r--r--src/common/tortls.h35
-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.c35
-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.c259
-rw-r--r--src/or/main.h1
-rw-r--r--src/or/microdesc.c1
-rw-r--r--src/or/networkstatus.c4
-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
-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
199 files changed, 19507 insertions, 2970 deletions
diff --git a/.gitignore b/.gitignore
index 0cdf2c4e61..613920167d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -110,6 +110,10 @@ cscope.*
/doc/spec/Makefile
/doc/spec/Makefile.in
+# /scripts
+/scripts/maint/checkOptionDocs.pl
+/scripts/maint/updateVersions.pl
+
# /src/
/src/Makefile
/src/Makefile.in
@@ -117,7 +121,6 @@ cscope.*
# /src/common/
/src/common/Makefile
/src/common/Makefile.in
-/src/common/common_sha1.i
/src/common/libor.a
/src/common/libor-testing.a
/src/common/libor.lib
@@ -145,7 +148,6 @@ cscope.*
# /src/or/
/src/or/Makefile
/src/or/Makefile.in
-/src/or/or_sha1.i
/src/or/tor
/src/or/tor.exe
/src/or/tor-cov
@@ -163,6 +165,7 @@ cscope.*
/src/test/test-slow
/src/test/test-bt-cl
/src/test/test-child
+/src/test/test-memwipe
/src/test/test-ntor-cl
/src/test/test_workqueue
/src/test/test.exe
@@ -171,14 +174,21 @@ cscope.*
/src/test/test-child.exe
/src/test/test-ntor-cl.exe
/src/test/test_workqueue.exe
+/src/test/test_zero_length_keys.sh
+/src/test/test_ntor.sh
+/src/test/test_bt.sh
# /src/tools/
/src/tools/tor-checkkey
/src/tools/tor-resolve
+/src/tools/tor-cov-resolve
/src/tools/tor-gencert
+/src/tools/tor-cov-gencert
/src/tools/tor-checkkey.exe
/src/tools/tor-resolve.exe
+/src/tools/tor-cov-resolve.exe
/src/tools/tor-gencert.exe
+/src/tools/tor-cov-gencert.exe
/src/tools/Makefile
/src/tools/Makefile.in
diff --git a/ChangeLog b/ChangeLog
index 4d93e1987e..a060eeaedf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,593 @@
-Changes in version 0.2.6.4-?? - 2015-0?-??
+Changes in version 0.2.7.2-alpha - 2015-0?-??
+Changes in version 0.2.6.9 - 2015-06-11
+ Tor 0.2.6.9 fixes a regression in the circuit isolation code, increases the
+ requirements for receiving an HSDir flag, and addresses some other small
+ bugs in the systemd and sandbox code. Clients using circuit isolation
+ should upgrade; all directory authorities should upgrade.
+
+ o Major bugfixes (client-side privacy):
+ - Properly separate out each SOCKSPort when applying stream
+ isolation. The error occurred because each port's session group was
+ being overwritten by a default value when the listener connection
+ was initialized. Fixes bug 16247; bugfix on 0.2.6.3-alpha. Patch
+ by "jojelino".
+
+ o Minor feature (directory authorities, security):
+ - The HSDir flag given by authorities now requires the Stable flag.
+ For the current network, this results in going from 2887 to 2806
+ HSDirs. Also, it makes it harder for an attacker to launch a sybil
+ attack by raising the effort for a relay to become Stable which
+ takes at the very least 7 days to do so and by keeping the 96
+ hours uptime requirement for HSDir. Implements ticket 8243.
+
+ o Minor bugfixes (compilation):
+ - Build with --enable-systemd correctly when libsystemd is
+ installed, but systemd is not. Fixes bug 16164; bugfix on
+ 0.2.6.3-alpha. Patch from Peter Palfrader.
+
+ o Minor bugfixes (Linux seccomp2 sandbox):
+ - Fix sandboxing to work when running as a relaymby renaming of
+ secret_id_key, and allowing the eventfd2 and futex syscalls. Fixes
+ bug 16244; bugfix on 0.2.6.1-alpha. Patch by Peter Palfrader.
+ - Allow systemd connections to work with the Linux seccomp2 sandbox
+ code. Fixes bug 16212; bugfix on 0.2.6.2-alpha. Patch by
+ Peter Palfrader.
+
+ o Minor bugfixes (tests):
+ - Fix a crash in the unit tests when built with MSVC2013. Fixes bug
+ 16030; bugfix on 0.2.6.2-alpha. Patch from "NewEraCracker".
+
+
+Changes in version 0.2.6.8 - 2015-05-21
+ Tor 0.2.6.8 fixes a bit of dodgy code in parsing INTRODUCE2 cells, and
+ fixes an authority-side bug in assigning the HSDir flag. All directory
+ authorities should upgrade.
+
+ o Major bugfixes (hidden services, backport from 0.2.7.1-alpha):
+ - Revert commit that made directory authorities assign the HSDir
+ flag to relay without a DirPort; this was bad because such relays
+ can't handle BEGIN_DIR cells. Fixes bug 15850; bugfix
+ on tor-0.2.6.3-alpha.
+
+ o Minor bugfixes (hidden service, backport from 0.2.7.1-alpha):
+ - Fix an out-of-bounds read when parsing invalid INTRODUCE2 cells on
+ a client authorized hidden service. Fixes bug 15823; bugfix
+ on 0.2.1.6-alpha.
+
+ o Minor features (geoip):
+ - Update geoip to the April 8 2015 Maxmind GeoLite2 Country database.
+ - Update geoip6 to the April 8 2015 Maxmind GeoLite2
+ Country database.
+
+
+Changes in version 0.2.7.1-alpha - 2015-05-12
+ Tor 0.2.7.1-alpha is the first alpha release in its series. It
+ includes numerous small features and bugfixes against previous Tor
+ versions, and numerous small infrastructure improvements. The most
+ notable features are several new ways for controllers to interact with
+ the hidden services subsystem.
+
+ o New system requirements:
+ - Tor no longer includes workarounds to support Libevent versions
+ before 1.3e. Libevent 2.0 or later is recommended. Closes
+ ticket 15248.
+
+ o Major features (controller):
+ - Add the ADD_ONION and DEL_ONION commands that allow the creation
+ and management of hidden services via the controller. Closes
+ ticket 6411.
+ - New "GETINFO onions/current" and "GETINFO onions/detached"
+ commands to get information about hidden services created via the
+ controller. Part of ticket 6411.
+ - New HSFETCH command to launch a request for a hidden service
+ descriptor. Closes ticket 14847.
+ - New HSPOST command to upload a hidden service descriptor. Closes
+ ticket 3523. Patch by "DonnchaC".
+
+ o Major bugfixes (hidden services):
+ - Revert commit that made directory authorities assign the HSDir
+ flag to relay without a DirPort; this was bad because such relays
+ can't handle BEGIN_DIR cells. Fixes bug 15850; bugfix
+ on tor-0.2.6.3-alpha.
+
+ o Minor features (clock-jump tolerance):
+ - Recover better when our clock jumps back many hours, like might
+ happen for Tails or Whonix users who start with a very wrong
+ hardware clock, use Tor to discover a more accurate time, and then
+ fix their clock. Resolves part of ticket 8766.
+
+ o Minor features (command-line interface):
+ - Make --hash-password imply --hush to prevent unnecessary noise.
+ Closes ticket 15542. Patch from "cypherpunks".
+ - Print a warning whenever we find a relative file path being used
+ as torrc option. Resolves issue 14018.
+
+ o Minor features (controller):
+ - Add DirAuthority lines for default directory authorities to the
+ output of the "GETINFO config/defaults" command if not already
+ present. Implements ticket 14840.
+ - Controllers can now use "GETINFO hs/client/desc/id/..." to
+ retrieve items from the client's hidden service descriptor cache.
+ Closes ticket 14845.
+ - Implement a new controller command "GETINFO status/fresh-relay-
+ descs" to fetch a descriptor/extrainfo pair that was generated on
+ demand just for the controller's use. Implements ticket 14784.
+
+ o Minor features (DoS-resistance):
+ - Make it harder for attackers to overload hidden services with
+ introductions, by blocking multiple introduction requests on the
+ same circuit. Resolves ticket 15515.
+
+ o Minor features (geoip):
+ - Update geoip to the April 8 2015 Maxmind GeoLite2 Country database.
+ - Update geoip6 to the April 8 2015 Maxmind GeoLite2
+ Country database.
+
+ o Minor features (HS popularity countermeasure):
+ - To avoid leaking HS popularity, don't cycle the introduction point
+ when we've handled a fixed number of INTRODUCE2 cells but instead
+ cycle it when a random number of introductions is reached, thus
+ making it more difficult for an attacker to find out the amount of
+ clients that have used the introduction point for a specific HS.
+ Closes ticket 15745.
+
+ o Minor features (logging):
+ - Include the Tor version in all LD_BUG log messages, since people
+ tend to cut and paste those into the bugtracker. Implements
+ ticket 15026.
+
+ o Minor features (pluggable transports):
+ - When launching managed pluggable transports on Linux systems,
+ attempt to have the kernel deliver a SIGTERM on tor exit if the
+ pluggable transport process is still running. Resolves
+ ticket 15471.
+ - When launching managed pluggable transports, setup a valid open
+ stdin in the child process that can be used to detect if tor has
+ terminated. The "TOR_PT_EXIT_ON_STDIN_CLOSE" environment variable
+ can be used by implementations to detect this new behavior.
+ Resolves ticket 15435.
+
+ o Minor features (testing):
+ - Add a test to verify that the compiler does not eliminate our
+ memwipe() implementation. Closes ticket 15377.
+ - Add make rule `check-changes` to verify the format of changes
+ files. Closes ticket 15180.
+ - Add unit tests for control_event_is_interesting(). Add a compile-
+ time check that the number of events doesn't exceed the capacity
+ of control_event_t.event_mask. Closes ticket 15431, checks for
+ bugs similar to 13085. Patch by "teor".
+ - Command-line argument tests moved to Stem. Resolves ticket 14806.
+ - Integrate the ntor, backtrace, and zero-length keys tests into the
+ automake test suite. Closes ticket 15344.
+ - Remove assertions during builds to determine Tor's test coverage.
+ We don't want to trigger these even in assertions, so including
+ them artificially makes our branch coverage look worse than it is.
+ This patch provides the new test-stem-full and coverage-html-full
+ configure options. Implements ticket 15400.
+
+ o Minor bugfixes (build):
+ - Improve out-of-tree builds by making non-standard rules work and
+ clean up additional files and directories. Fixes bug 15053; bugfix
+ on 0.2.7.0-alpha.
+
+ o Minor bugfixes (command-line interface):
+ - When "--quiet" is provided along with "--validate-config", do not
+ write anything to stdout on success. Fixes bug 14994; bugfix
+ on 0.2.3.3-alpha.
+ - When complaining about bad arguments to "--dump-config", use
+ stderr, not stdout.
+
+ o Minor bugfixes (configuration, unit tests):
+ - Only add the default fallback directories when the DirAuthorities,
+ AlternateDirAuthority, and FallbackDir directory config options
+ are set to their defaults. The default fallback directory list is
+ currently empty, this fix will only change tor's behavior when it
+ has default fallback directories. Includes unit tests for
+ consider_adding_dir_servers(). Fixes bug 15642; bugfix on
+ 90f6071d8dc0 in 0.2.4.7-alpha. Patch by "teor".
+
+ o Minor bugfixes (correctness):
+ - For correctness, avoid modifying a constant string in
+ handle_control_postdescriptor. Fixes bug 15546; bugfix
+ on 0.1.1.16-rc.
+ - Remove side-effects from tor_assert() calls. This was harmless,
+ because we never disable assertions, but it is bad style and
+ unnecessary. Fixes bug 15211; bugfix on 0.2.5.5, 0.2.2.36,
+ and 0.2.0.10.
+
+ o Minor bugfixes (hidden service):
+ - Fix an out-of-bounds read when parsing invalid INTRODUCE2 cells on
+ a client authorized hidden service. Fixes bug 15823; bugfix
+ on 0.2.1.6-alpha.
+ - Remove an extraneous newline character from the end of hidden
+ service descriptors. Fixes bug 15296; bugfix on 0.2.0.10-alpha.
+
+ o Minor bugfixes (interface):
+ - Print usage information for --dump-config when it is used without
+ an argument. Also, fix the error message to use different wording
+ and add newline at the end. Fixes bug 15541; bugfix
+ on 0.2.5.1-alpha.
+
+ o Minor bugfixes (logs):
+ - When building Tor under Clang, do not include an extra set of
+ parentheses in log messages that include function names. Fixes bug
+ 15269; bugfix on every released version of Tor when compiled with
+ recent enough Clang.
+
+ o Minor bugfixes (network):
+ - When attempting to use fallback technique for network interface
+ lookup, disregard loopback and multicast addresses since they are
+ unsuitable for public communications.
+
+ o Minor bugfixes (statistics):
+ - Disregard the ConnDirectionStatistics torrc options when Tor is
+ not a relay since in that mode of operation no sensible data is
+ being collected and because Tor might run into measurement hiccups
+ when running as a client for some time, then becoming a relay.
+ Fixes bug 15604; bugfix on 0.2.2.35.
+
+ o Minor bugfixes (test networks):
+ - When self-testing reachability, use ExtendAllowPrivateAddresses to
+ determine if local/private addresses imply reachability. The
+ previous fix used TestingTorNetwork, which implies
+ ExtendAllowPrivateAddresses, but this excluded rare configurations
+ where ExtendAllowPrivateAddresses is set but TestingTorNetwork is
+ not. Fixes bug 15771; bugfix on 0.2.6.1-alpha. Patch by "teor",
+ issue discovered by CJ Ess.
+
+ o Minor bugfixes (testing):
+ - Check for matching value in server response in ntor_ref.py. Fixes
+ bug 15591; bugfix on 0.2.4.8-alpha. Reported and fixed
+ by "joelanders".
+ - Set the severity correctly when testing
+ get_interface_addresses_ifaddrs() and
+ get_interface_addresses_win32(), so that the tests fail gracefully
+ instead of triggering an assertion. Fixes bug 15759; bugfix on
+ 0.2.6.3-alpha. Reported by Nicolas Derive.
+
+ o Code simplification and refactoring:
+ - Move the hacky fallback code out of get_interface_address6() into
+ separate function and get it covered with unit-tests. Resolves
+ ticket 14710.
+ - Refactor hidden service client-side cache lookup to intelligently
+ report its various failure cases, and disentangle failure cases
+ involving a lack of introduction points. Closes ticket 14391.
+ - Use our own Base64 encoder instead of OpenSSL's, to allow more
+ control over the output. Part of ticket 15652.
+
+ o Documentation:
+ - Improve the descriptions of statistics-related torrc options in
+ the manpage to describe rationale and possible uses cases. Fixes
+ issue 15550.
+ - Improve the layout and formatting of ./configure --help messages.
+ Closes ticket 15024. Patch from "cypherpunks".
+ - Standardize on the term "server descriptor" in the manual page.
+ Previously, we had used "router descriptor", "server descriptor",
+ and "relay descriptor" interchangeably. Part of ticket 14987.
+
+ o Removed code:
+ - Remove `USE_OPENSSL_BASE64` and the corresponding fallback code
+ and always use the internal Base64 decoder. The internal decoder
+ has been part of tor since tor-0.2.0.10-alpha, and no one should
+ be using the OpenSSL one. Part of ticket 15652.
+ - Remove the 'tor_strclear()' function; use memwipe() instead.
+ Closes ticket 14922.
+
+ o Removed features:
+ - Remove the (seldom-used) DynamicDHGroups feature. For anti-
+ fingerprinting we now recommend pluggable transports; for forward-
+ secrecy in TLS, we now use the P-256 group. Closes ticket 13736.
+ - Remove the undocumented "--digests" command-line option. It
+ complicated our build process, caused subtle build issues on
+ multiple platforms, and is now redundant since we started
+ including git version identifiers. Closes ticket 14742.
+ - Tor no longer contains checks for ancient directory cache versions
+ that didn't know about microdescriptors.
+ - Tor no longer contains workarounds for stat files generated by
+ super-old versions of Tor that didn't choose guards sensibly.
+
+
+Changes in version 0.2.4.27 - 2015-04-06
+ Tor 0.2.4.27 backports two fixes from 0.2.6.7 for security issues that
+ could be used by an attacker to crash hidden services, or crash clients
+ visiting hidden services. Hidden services should upgrade as soon as
+ possible; clients should upgrade whenever packages become available.
+
+ This release also backports a simple improvement to make hidden
+ services a bit less vulnerable to denial-of-service attacks.
+
+ o Major bugfixes (security, hidden service):
+ - Fix an issue that would allow a malicious client to trigger an
+ assertion failure and halt a hidden service. Fixes bug 15600;
+ bugfix on 0.2.1.6-alpha. Reported by "disgleirio".
+ - Fix a bug that could cause a client to crash with an assertion
+ failure when parsing a malformed hidden service descriptor. Fixes
+ bug 15601; bugfix on 0.2.1.5-alpha. Found by "DonnchaC".
+
+ o Minor features (DoS-resistance, hidden service):
+ - Introduction points no longer allow multiple INTRODUCE1 cells to
+ arrive on the same circuit. This should make it more expensive for
+ attackers to overwhelm hidden services with introductions.
+ Resolves ticket 15515.
+
+
+Changes in version 0.2.6.7 - 2015-04-06
+ Tor 0.2.6.7 fixes two security issues that could be used by an
+ attacker to crash hidden services, or crash clients visiting hidden
+ services. Hidden services should upgrade as soon as possible; clients
+ should upgrade whenever packages become available.
+
+ This release also contains two simple improvements to make hidden
+ services a bit less vulnerable to denial-of-service attacks.
+
+ o Major bugfixes (security, hidden service):
+ - Fix an issue that would allow a malicious client to trigger an
+ assertion failure and halt a hidden service. Fixes bug 15600;
+ bugfix on 0.2.1.6-alpha. Reported by "disgleirio".
+ - Fix a bug that could cause a client to crash with an assertion
+ failure when parsing a malformed hidden service descriptor. Fixes
+ bug 15601; bugfix on 0.2.1.5-alpha. Found by "DonnchaC".
+
+ o Minor features (DoS-resistance, hidden service):
+ - Introduction points no longer allow multiple INTRODUCE1 cells to
+ arrive on the same circuit. This should make it more expensive for
+ attackers to overwhelm hidden services with introductions.
+ Resolves ticket 15515.
+ - Decrease the amount of reattempts that a hidden service performs
+ when its rendezvous circuits fail. This reduces the computational
+ cost for running a hidden service under heavy load. Resolves
+ ticket 11447.
+
+
+Changes in version 0.2.6.6 - 2015-03-24
+ Tor 0.2.6.6 is the first stable release in the 0.2.6 series.
+
+ It adds numerous safety, security, correctness, and performance
+ improvements. Client programs can be configured to use more kinds of
+ sockets, AutomapHosts works better, the multithreading backend is
+ improved, cell transmission is refactored, test coverage is much
+ higher, more denial-of-service attacks are handled, guard selection is
+ improved to handle long-term guards better, pluggable transports
+ should work a bit better, and some annoying hidden service performance
+ bugs should be addressed.
+
+ o Minor bugfixes (portability):
+ - Use the correct datatype in the SipHash-2-4 function to prevent
+ compilers from assuming any sort of alignment. Fixes bug 15436;
+ bugfix on 0.2.5.3-alpha.
+
+Changes in version 0.2.6.5-rc - 2015-03-18
+ Tor 0.2.6.5-rc is the second and (hopefully) last release candidate in
+ the 0.2.6. It fixes a small number of bugs found in 0.2.6.4-rc.
+
+ o Major bugfixes (client):
+ - Avoid crashing when making certain configuration option changes on
+ clients. Fixes bug 15245; bugfix on 0.2.6.3-alpha. Reported
+ by "anonym".
+
+ o Major bugfixes (pluggable transports):
+ - Initialize the extended OR Port authentication cookie before
+ launching pluggable transports. This prevents a race condition
+ that occured when server-side pluggable transports would cache the
+ authentication cookie before it has been (re)generated. Fixes bug
+ 15240; bugfix on 0.2.5.1-alpha.
+
+ o Major bugfixes (portability):
+ - Do not crash on startup when running on Solaris. Fixes a bug
+ related to our fix for 9495; bugfix on 0.2.6.1-alpha. Reported
+ by "ruebezahl".
+
+ o Minor features (heartbeat):
+ - On relays, report how many connections we negotiated using each
+ version of the Tor link protocols. This information will let us
+ know if removing support for very old versions of the Tor
+ protocols is harming the network. Closes ticket 15212.
+
+ o Code simplification and refactoring:
+ - Refactor main loop to extract the 'loop' part. This makes it
+ easier to run Tor under Shadow. Closes ticket 15176.
+
+
+Changes in version 0.2.5.11 - 2015-03-17
+ Tor 0.2.5.11 is the second stable release in the 0.2.5 series.
+
+ It backports several bugfixes from the 0.2.6 branch, including a
+ couple of medium-level security fixes for relays and exit nodes.
+ It also updates the list of directory authorities.
+
+ o Directory authority changes:
+ - Remove turtles as a directory authority.
+ - Add longclaw as a new (v3) directory authority. This implements
+ ticket 13296. This keeps the directory authority count at 9.
+ - The directory authority Faravahar has a new IP address. This
+ closes ticket 14487.
+
+ o Major bugfixes (crash, OSX, security):
+ - Fix a remote denial-of-service opportunity caused by a bug in
+ OSX's _strlcat_chk() function. Fixes bug 15205; bug first appeared
+ in OSX 10.9.
+
+ o Major bugfixes (relay, stability, possible security):
+ - Fix a bug that could lead to a relay crashing with an assertion
+ failure if a buffer of exactly the wrong layout was passed to
+ buf_pullup() at exactly the wrong time. Fixes bug 15083; bugfix on
+ 0.2.0.10-alpha. Patch from 'cypherpunks'.
+ - Do not assert if the 'data' pointer on a buffer is advanced to the
+ very end of the buffer; log a BUG message instead. Only assert if
+ it is past that point. Fixes bug 15083; bugfix on 0.2.0.10-alpha.
+
+ o Major bugfixes (exit node stability):
+ - Fix an assertion failure that could occur under high DNS load.
+ Fixes bug 14129; bugfix on Tor 0.0.7rc1. Found by "jowr";
+ diagnosed and fixed by "cypherpunks".
+
+ o Major bugfixes (Linux seccomp2 sandbox):
+ - Upon receiving sighup with the seccomp2 sandbox enabled, do not
+ crash during attempts to call wait4. Fixes bug 15088; bugfix on
+ 0.2.5.1-alpha. Patch from "sanic".
+
+ o Minor features (controller):
+ - New "GETINFO bw-event-cache" to get information about recent
+ bandwidth events. Closes ticket 14128. Useful for controllers to
+ get recent bandwidth history after the fix for ticket 13988.
+
+ o Minor features (geoip):
+ - Update geoip to the March 3 2015 Maxmind GeoLite2 Country database.
+ - Update geoip6 to the March 3 2015 Maxmind GeoLite2
+ Country database.
+
+ o Minor bugfixes (client, automapping):
+ - Avoid crashing on torrc lines for VirtualAddrNetworkIPv[4|6] when
+ no value follows the option. Fixes bug 14142; bugfix on
+ 0.2.4.7-alpha. Patch by "teor".
+ - Fix a memory leak when using AutomapHostsOnResolve. Fixes bug
+ 14195; bugfix on 0.1.0.1-rc.
+
+ o Minor bugfixes (compilation):
+ - Build without warnings with the stock OpenSSL srtp.h header, which
+ has a duplicate declaration of SSL_get_selected_srtp_profile().
+ Fixes bug 14220; this is OpenSSL's bug, not ours.
+
+ o Minor bugfixes (directory authority):
+ - Allow directory authorities to fetch more data from one another if
+ they find themselves missing lots of votes. Previously, they had
+ been bumping against the 10 MB queued data limit. Fixes bug 14261;
+ bugfix on 0.1.2.5-alpha.
+ - Enlarge the buffer to read bwauth generated files to avoid an
+ issue when parsing the file in dirserv_read_measured_bandwidths().
+ Fixes bug 14125; bugfix on 0.2.2.1-alpha.
+
+ o Minor bugfixes (statistics):
+ - Increase period over which bandwidth observations are aggregated
+ from 15 minutes to 4 hours. Fixes bug 13988; bugfix on 0.0.8pre1.
+
+ o Minor bugfixes (preventative security, C safety):
+ - When reading a hexadecimal, base-32, or base-64 encoded value from
+ a string, always overwrite the whole output buffer. This prevents
+ some bugs where we would look at (but fortunately, not reveal)
+ uninitialized memory on the stack. Fixes bug 14013; bugfix on all
+ versions of Tor.
+
+
+Changes in version 0.2.4.26 - 2015-03-17
+ Tor 0.2.4.26 includes an updated list of directory authorities. It
+ also backports a couple of stability and security bugfixes from 0.2.5
+ and beyond.
+
+ o Directory authority changes:
+ - Remove turtles as a directory authority.
+ - Add longclaw as a new (v3) directory authority. This implements
+ ticket 13296. This keeps the directory authority count at 9.
+ - The directory authority Faravahar has a new IP address. This
+ closes ticket 14487.
+
+ o Major bugfixes (exit node stability, also in 0.2.6.3-alpha):
+ - Fix an assertion failure that could occur under high DNS load.
+ Fixes bug 14129; bugfix on Tor 0.0.7rc1. Found by "jowr";
+ diagnosed and fixed by "cypherpunks".
+
+ o Major bugfixes (relay, stability, possible security, also in 0.2.6.4-rc):
+ - Fix a bug that could lead to a relay crashing with an assertion
+ failure if a buffer of exactly the wrong layout was passed to
+ buf_pullup() at exactly the wrong time. Fixes bug 15083; bugfix on
+ 0.2.0.10-alpha. Patch from 'cypherpunks'.
+ - Do not assert if the 'data' pointer on a buffer is advanced to the
+ very end of the buffer; log a BUG message instead. Only assert if
+ it is past that point. Fixes bug 15083; bugfix on 0.2.0.10-alpha.
+
+ o Minor features (geoip):
+ - Update geoip to the March 3 2015 Maxmind GeoLite2 Country database.
+ - Update geoip6 to the March 3 2015 Maxmind GeoLite2
+ Country database.
+
+Changes in version 0.2.6.4-rc - 2015-03-09
+ Tor 0.2.6.4-alpha fixes an issue in the directory code that an
+ attacker might be able to use in order to crash certain Tor
+ directories. It also resolves some minor issues left over from, or
+ introduced in, Tor 0.2.6.3-alpha or earlier.
+
+ o Major bugfixes (crash, OSX, security):
+ - Fix a remote denial-of-service opportunity caused by a bug in
+ OSX's _strlcat_chk() function. Fixes bug 15205; bug first appeared
+ in OSX 10.9.
+
+ o Major bugfixes (relay, stability, possible security):
+ - Fix a bug that could lead to a relay crashing with an assertion
+ failure if a buffer of exactly the wrong layout is passed to
+ buf_pullup() at exactly the wrong time. Fixes bug 15083; bugfix on
+ 0.2.0.10-alpha. Patch from "cypherpunks".
+ - Do not assert if the 'data' pointer on a buffer is advanced to the
+ very end of the buffer; log a BUG message instead. Only assert if
+ it is past that point. Fixes bug 15083; bugfix on 0.2.0.10-alpha.
+
+ o Major bugfixes (FreeBSD IPFW transparent proxy):
+ - Fix address detection with FreeBSD transparent proxies, when
+ "TransProxyType ipfw" is in use. Fixes bug 15064; bugfix
+ on 0.2.5.4-alpha.
+
+ o Major bugfixes (Linux seccomp2 sandbox):
+ - Pass IPPROTO_TCP rather than 0 to socket(), so that the Linux
+ seccomp2 sandbox doesn't fail. Fixes bug 14989; bugfix
+ on 0.2.6.3-alpha.
+ - Allow AF_UNIX hidden services to be used with the seccomp2
+ sandbox. Fixes bug 15003; bugfix on 0.2.6.3-alpha.
+ - Upon receiving sighup with the seccomp2 sandbox enabled, do not
+ crash during attempts to call wait4. Fixes bug 15088; bugfix on
+ 0.2.5.1-alpha. Patch from "sanic".
+
+ o Minor features (controller):
+ - Messages about problems in the bootstrap process now include
+ information about the server we were trying to connect to when we
+ noticed the problem. Closes ticket 15006.
+
+ o Minor features (geoip):
+ - Update geoip to the March 3 2015 Maxmind GeoLite2 Country database.
+ - Update geoip6 to the March 3 2015 Maxmind GeoLite2
+ Country database.
+
+ o Minor features (logs):
+ - Quiet some log messages in the heartbeat and at startup. Closes
+ ticket 14950.
+
+ o Minor bugfixes (certificate handling):
+ - If an authority operator accidentally makes a signing certificate
+ with a future publication time, do not discard its real signing
+ certificates. Fixes bug 11457; bugfix on 0.2.0.3-alpha.
+ - Remove any old authority certificates that have been superseded
+ for at least two days. Previously, we would keep superseded
+ certificates until they expired, if they were published close in
+ time to the certificate that superseded them. Fixes bug 11454;
+ bugfix on 0.2.1.8-alpha.
+
+ o Minor bugfixes (compilation):
+ - Fix a compilation warning on s390. Fixes bug 14988; bugfix
+ on 0.2.5.2-alpha.
+ - Fix a compilation warning on FreeBSD. Fixes bug 15151; bugfix
+ on 0.2.6.2-alpha.
+
+ o Minor bugfixes (testing):
+ - Fix endianness issues in unit test for resolve_my_address() to
+ have it pass on big endian systems. Fixes bug 14980; bugfix on
+ Tor 0.2.6.3-alpha.
+ - Avoid a side-effect in a tor_assert() in the unit tests. Fixes bug
+ 15188; bugfix on 0.1.2.3-alpha. Patch from Tom van der Woerdt.
+ - When running the new 'make test-stem' target, use the configured
+ python binary. Fixes bug 15037; bugfix on 0.2.6.3-alpha. Patch
+ from "cypherpunks".
+ - When running the zero-length-keys tests, do not use the default
+ torrc file. Fixes bug 15033; bugfix on 0.2.6.3-alpha. Reported
+ by "reezer".
+
+ o Directory authority IP change:
+ - The directory authority Faravahar has a new IP address. This
+ closes ticket 14487.
+
+ o Removed code:
+ - Remove some lingering dead code that once supported mempools.
+ Mempools were disabled by default in 0.2.5, and removed entirely
+ in 0.2.6.3-alpha. Closes more of ticket 14848; patch
+ by "cypherpunks".
Changes in version 0.2.6.3-alpha - 2015-02-19
@@ -49,6 +637,13 @@ Changes in version 0.2.6.3-alpha - 2015-02-19
notified of updates and their correct digests. Implements proposal
227. Closes ticket 10395.
+ o Major features (guards):
+ - Introduce the Guardfraction feature to improves load balancing on
+ guard nodes. Specifically, it aims to reduce the traffic gap that
+ guard nodes experience when they first get the Guard flag. This is
+ a required step if we want to increase the guard lifetime to 9
+ months or greater. Closes ticket 9321.
+
o Major features (performance):
- Make the CPU worker implementation more efficient by avoiding the
kernel and lengthening pipelines. The original implementation used
diff --git a/Doxyfile.in b/Doxyfile.in
index 344ee27149..a39348f2cb 100644
--- a/Doxyfile.in
+++ b/Doxyfile.in
@@ -38,7 +38,7 @@ PROJECT_NUMBER = @VERSION@
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
-OUTPUT_DIRECTORY = ./doc/doxygen
+OUTPUT_DIRECTORY = @top_builddir@/doc/doxygen
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
# 4096 sub-directories (in 2 levels) under the output directory of each output
@@ -534,8 +534,8 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = src/common \
- src/or
+INPUT = @top_srcdir@/src/common \
+ @top_srcdir@/src/or
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/Makefile.am b/Makefile.am
index b1f92f5b34..316086c1e1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,6 +20,7 @@ DISTCLEANFILES=
bin_SCRIPTS=
AM_CPPFLAGS=
AM_CFLAGS = @TOR_SYSTEMD_CFLAGS@
+SHELL = @SHELL@
include src/include.am
include doc/include.am
include contrib/include.am
@@ -34,8 +35,10 @@ EXTRA_DIST+= \
if COVERAGE_ENABLED
TEST_CFLAGS=-fno-inline -fprofile-arcs -ftest-coverage
+TEST_CPPFLAGS=-DTOR_UNIT_TESTS -DTOR_COVERAGE
else
TEST_CFLAGS=
+TEST_CPPFLAGS=-DTOR_UNIT_TESTS
endif
#install-data-local:
@@ -64,12 +67,12 @@ doxygen:
doxygen && cd doc/doxygen/latex && make
test: all
- ./src/test/test
+ $(top_builddir)/src/test/test
# Note that test-network requires a copy of Chutney in $CHUTNEY_PATH.
# Chutney can be cloned from https://git.torproject.org/chutney.git .
test-network: all
- ./src/test/test-network.sh
+ $(top_srcdir)/src/test/test-network.sh
test-stem: $(TESTING_TOR_BINARY)
@if test -d "$$STEM_SOURCE_DIR"; then \
@@ -79,17 +82,41 @@ test-stem: $(TESTING_TOR_BINARY)
echo "To run these tests, git clone https://git.torproject.org/stem.git/ ; export STEM_SOURCE_DIR=\`pwd\`/stem"; \
fi
+test-stem-full: $(TESTING_TOR_BINARY)
+ @if test -d "$$STEM_SOURCE_DIR"; then \
+ "$$STEM_SOURCE_DIR"/run_tests.py --tor $(TESTING_TOR_BINARY) --all --log notice --target RUN_ALL,ONLINE -v; \
+ else \
+ echo '$$STEM_SOURCE_DIR was not set.'; echo; \
+ echo "To run these tests, git clone https://git.torproject.org/stem.git/ ; export STEM_SOURCE_DIR=\`pwd\`/stem"; \
+ fi
reset-gcov:
- rm -f src/*/*.gcda src/*/*/*.gcda
+ rm -f $(top_builddir)/src/*/*.gcda $(top_builddir)/src/*/*/*.gcda
-HTML_COVER_DIR=./coverage_html
+HTML_COVER_DIR=$(top_builddir)/coverage_html
coverage-html: all
+if COVERAGE_ENABLED
+ test -e "`which lcov`" || (echo "lcov must be installed. See <http://ltp.sourceforge.net/coverage/lcov.php>." && false)
+ test -d "$(HTML_COVER_DIR)" || $(MKDIR_P) "$(HTML_COVER_DIR)"
+ lcov --rc lcov_branch_coverage=1 --directory $(top_builddir)/src --zerocounters
+ $(MAKE) reset-gcov
+ $(MAKE) check
+ lcov --capture --rc lcov_branch_coverage=1 --no-external --directory $(top_builddir) --base-directory $(top_srcdir) --output-file "$(HTML_COVER_DIR)/lcov.tmp"
+ lcov --remove "$(HTML_COVER_DIR)/lcov.tmp" --rc lcov_branch_coverage=1 'test/*' 'ext/tinytest*' '/usr/*' --output-file "$(HTML_COVER_DIR)/lcov.info"
+ genhtml --branch-coverage -o "$(HTML_COVER_DIR)" "$(HTML_COVER_DIR)/lcov.info"
+else
+ @printf "Not configured with --enable-coverage, run ./configure --enable-coverage\n"
+endif
+
+coverage-html-full: all
test -e "`which lcov`" || (echo "lcov must be installed. See <http://ltp.sourceforge.net/coverage/lcov.php>." && false)
test -d "$(HTML_COVER_DIR)" || mkdir -p "$(HTML_COVER_DIR)"
lcov --rc lcov_branch_coverage=1 --directory ./src --zerocounters
$(MAKE) reset-gcov
$(MAKE) check
+ $(MAKE) test-stem-full
+ CHUTNEY_TOR=tor-cov CHUTNEY_TOR_GENCERT=tor-cov-gencert $(top_srcdir)/src/test/test-network.sh
+ CHUTNEY_TOR=tor-cov CHUTNEY_TOR_GENCERT=tor-cov-gencert $(top_srcdir)/src/test/test-network.sh --flavor hs
lcov --capture --rc lcov_branch_coverage=1 --no-external --directory . --output-file "$(HTML_COVER_DIR)/lcov.tmp"
lcov --remove "$(HTML_COVER_DIR)/lcov.tmp" --rc lcov_branch_coverage=1 'test/*' 'ext/tinytest*' '/usr/*' --output-file "$(HTML_COVER_DIR)/lcov.info"
genhtml --branch-coverage -o "$(HTML_COVER_DIR)" "$(HTML_COVER_DIR)/lcov.info"
@@ -97,19 +124,29 @@ coverage-html: all
# Avoid strlcpy.c, strlcat.c, aes.c, OpenBSD_malloc_Linux.c, sha256.c,
# eventdns.[hc], tinytest*.[ch]
check-spaces:
- ./scripts/maint/checkSpace.pl -C \
- src/common/*.[ch] \
- src/or/*.[ch] \
- src/test/*.[ch] \
- src/tools/*.[ch] \
- src/tools/tor-fw-helper/*.[ch]
+ $(top_srcdir)/scripts/maint/checkSpace.pl -C \
+ $(top_srcdir)/src/common/*.[ch] \
+ $(top_srcdir)/src/or/*.[ch] \
+ $(top_srcdir)/src/test/*.[ch] \
+ $(top_srcdir)/src/tools/*.[ch] \
+ $(top_srcdir)/src/tools/tor-fw-helper/*.[ch]
-check-docs:
- ./scripts/maint/checkOptionDocs.pl
+check-docs: all
+ $(PERL) $(top_builddir)/scripts/maint/checkOptionDocs.pl
check-logs:
- ./scripts/maint/checkLogs.pl \
- src/*/*.[ch] | sort -n
+ $(top_srcdir)/scripts/maint/checkLogs.pl \
+ $(top_srcdir)/src/*/*.[ch] | sort -n
+
+.PHONY: check-changes
+check-changes:
+ @if test -d "$(top_srcdir)/changes"; then \
+ $(PYTHON) $(top_srcdir)/scripts/maint/lintChanges.py $(top_srcdir)/changes/*; \
+ fi
+
+.PHONY: update-versions
+update-versions:
+ $(PERL) $(top_builddir)/scripts/maint/updateVersions.pl
version:
@echo "Tor @VERSION@"
@@ -119,4 +156,6 @@ version:
fi
mostlyclean-local:
- rm -f src/*/*.gc{da,no} src/*/*/*.gc{da,no}
+ rm -f $(top_builddir)/src/*/*.gc{da,no} $(top_builddir)/src/*/*/*.gc{da,no}
+ rm -rf $(HTML_COVER_DIR)
+ rm -rf $(top_builddir)/doc/doxygen
diff --git a/ReleaseNotes b/ReleaseNotes
index a9c8ceefdd..d9ddac07f8 100644
--- a/ReleaseNotes
+++ b/ReleaseNotes
@@ -3,6 +3,1254 @@ This document summarizes new features and bugfixes in each stable release
of Tor. If you want to see more detailed descriptions of the changes in
each development snapshot, see the ChangeLog file.
+Changes in version 0.2.6.9 - 2015-06-11
+ Tor 0.2.6.9 fixes a regression in the circuit isolation code, increases the
+ requirements for receiving an HSDir flag, and addresses some other small
+ bugs in the systemd and sandbox code. Clients using circuit isolation
+ should upgrade; all directory authorities should upgrade.
+
+ o Major bugfixes (client-side privacy):
+ - Properly separate out each SOCKSPort when applying stream
+ isolation. The error occurred because each port's session group was
+ being overwritten by a default value when the listener connection
+ was initialized. Fixes bug 16247; bugfix on 0.2.6.3-alpha. Patch
+ by "jojelino".
+
+ o Minor feature (directory authorities, security):
+ - The HSDir flag given by authorities now requires the Stable flag.
+ For the current network, this results in going from 2887 to 2806
+ HSDirs. Also, it makes it harder for an attacker to launch a sybil
+ attack by raising the effort for a relay to become Stable which
+ takes at the very least 7 days to do so and by keeping the 96
+ hours uptime requirement for HSDir. Implements ticket 8243.
+
+ o Minor bugfixes (compilation):
+ - Build with --enable-systemd correctly when libsystemd is
+ installed, but systemd is not. Fixes bug 16164; bugfix on
+ 0.2.6.3-alpha. Patch from Peter Palfrader.
+
+ o Minor bugfixes (Linux seccomp2 sandbox):
+ - Fix sandboxing to work when running as a relaymby renaming of
+ secret_id_key, and allowing the eventfd2 and futex syscalls. Fixes
+ bug 16244; bugfix on 0.2.6.1-alpha. Patch by Peter Palfrader.
+ - Allow systemd connections to work with the Linux seccomp2 sandbox
+ code. Fixes bug 16212; bugfix on 0.2.6.2-alpha. Patch by
+ Peter Palfrader.
+
+ o Minor bugfixes (tests):
+ - Fix a crash in the unit tests when built with MSVC2013. Fixes bug
+ 16030; bugfix on 0.2.6.2-alpha. Patch from "NewEraCracker".
+
+
+Changes in version 0.2.6.8 - 2015-05-21
+ Tor 0.2.6.8 fixes a bit of dodgy code in parsing INTRODUCE2 cells, and
+ fixes an authority-side bug in assigning the HSDir flag. All directory
+ authorities should upgrade.
+
+ o Major bugfixes (hidden services, backport from 0.2.7.1-alpha):
+ - Revert commit that made directory authorities assign the HSDir
+ flag to relay without a DirPort; this was bad because such relays
+ can't handle BEGIN_DIR cells. Fixes bug 15850; bugfix
+ on tor-0.2.6.3-alpha.
+
+ o Minor bugfixes (hidden service, backport from 0.2.7.1-alpha):
+ - Fix an out-of-bounds read when parsing invalid INTRODUCE2 cells on
+ a client authorized hidden service. Fixes bug 15823; bugfix
+ on 0.2.1.6-alpha.
+
+ o Minor features (geoip):
+ - Update geoip to the April 8 2015 Maxmind GeoLite2 Country database.
+ - Update geoip6 to the April 8 2015 Maxmind GeoLite2
+ Country database.
+
+
+Changes in version 0.2.6.7 - 2015-04-06
+ Tor 0.2.6.7 fixes two security issues that could be used by an
+ attacker to crash hidden services, or crash clients visiting hidden
+ services. Hidden services should upgrade as soon as possible; clients
+ should upgrade whenever packages become available.
+
+ This release also contains two simple improvements to make hidden
+ services a bit less vulnerable to denial-of-service attacks.
+
+ o Major bugfixes (security, hidden service):
+ - Fix an issue that would allow a malicious client to trigger an
+ assertion failure and halt a hidden service. Fixes bug 15600;
+ bugfix on 0.2.1.6-alpha. Reported by "disgleirio".
+ - Fix a bug that could cause a client to crash with an assertion
+ failure when parsing a malformed hidden service descriptor. Fixes
+ bug 15601; bugfix on 0.2.1.5-alpha. Found by "DonnchaC".
+
+ o Minor features (DoS-resistance, hidden service):
+ - Introduction points no longer allow multiple INTRODUCE1 cells to
+ arrive on the same circuit. This should make it more expensive for
+ attackers to overwhelm hidden services with introductions.
+ Resolves ticket 15515.
+ - Decrease the amount of reattempts that a hidden service performs
+ when its rendezvous circuits fail. This reduces the computational
+ cost for running a hidden service under heavy load. Resolves
+ ticket 11447.
+
+
+Changes in version 0.2.5.12 - 2015-04-06
+ Tor 0.2.5.12 backports two fixes from 0.2.6.7 for security issues that
+ could be used by an attacker to crash hidden services, or crash clients
+ visiting hidden services. Hidden services should upgrade as soon as
+ possible; clients should upgrade whenever packages become available.
+
+ This release also backports a simple improvement to make hidden
+ services a bit less vulnerable to denial-of-service attacks.
+
+ o Major bugfixes (security, hidden service):
+ - Fix an issue that would allow a malicious client to trigger an
+ assertion failure and halt a hidden service. Fixes bug 15600;
+ bugfix on 0.2.1.6-alpha. Reported by "disgleirio".
+ - Fix a bug that could cause a client to crash with an assertion
+ failure when parsing a malformed hidden service descriptor. Fixes
+ bug 15601; bugfix on 0.2.1.5-alpha. Found by "DonnchaC".
+
+ o Minor features (DoS-resistance, hidden service):
+ - Introduction points no longer allow multiple INTRODUCE1 cells to
+ arrive on the same circuit. This should make it more expensive for
+ attackers to overwhelm hidden services with introductions.
+ Resolves ticket 15515.
+
+
+Changes in version 0.2.4.27 - 2015-04-06
+ Tor 0.2.4.27 backports two fixes from 0.2.6.7 for security issues that
+ could be used by an attacker to crash hidden services, or crash clients
+ visiting hidden services. Hidden services should upgrade as soon as
+ possible; clients should upgrade whenever packages become available.
+
+ This release also backports a simple improvement to make hidden
+ services a bit less vulnerable to denial-of-service attacks.
+
+ o Major bugfixes (security, hidden service):
+ - Fix an issue that would allow a malicious client to trigger an
+ assertion failure and halt a hidden service. Fixes bug 15600;
+ bugfix on 0.2.1.6-alpha. Reported by "disgleirio".
+ - Fix a bug that could cause a client to crash with an assertion
+ failure when parsing a malformed hidden service descriptor. Fixes
+ bug 15601; bugfix on 0.2.1.5-alpha. Found by "DonnchaC".
+
+ o Minor features (DoS-resistance, hidden service):
+ - Introduction points no longer allow multiple INTRODUCE1 cells to
+ arrive on the same circuit. This should make it more expensive for
+ attackers to overwhelm hidden services with introductions.
+ Resolves ticket 15515.
+
+
+Changes in version 0.2.6.6 - 2015-03-24
+ Tor 0.2.6.6 is the first stable release in the 0.2.6 series.
+
+ It adds numerous safety, security, correctness, and performance
+ improvements. Client programs can be configured to use more kinds of
+ sockets, AutomapHosts works better, the multithreading backend is
+ improved, cell transmission is refactored, test coverage is much
+ higher, more denial-of-service attacks are handled, guard selection is
+ improved to handle long-term guards better, pluggable transports
+ should work a bit better, and some annoying hidden service performance
+ bugs should be addressed.
+
+ o New compiler and system requirements:
+ - Tor 0.2.6.x requires that your compiler support more of the C99
+ language standard than before. The 'configure' script now detects
+ whether your compiler supports C99 mid-block declarations and
+ designated initializers. If it does not, Tor will not compile.
+
+ We may revisit this requirement if it turns out that a significant
+ number of people need to build Tor with compilers that don't
+ bother implementing a 15-year-old standard. Closes ticket 13233.
+ - Tor no longer supports systems without threading support. When we
+ began working on Tor, there were several systems that didn't have
+ threads, or where the thread support wasn't able to run the
+ threads of a single process on multiple CPUs. That no longer
+ holds: every system where Tor needs to run well now has threading
+ support. Resolves ticket 12439.
+
+ o Deprecated versions and removed support:
+ - Tor relays older than 0.2.4.18-rc are no longer allowed to
+ advertise themselves on the network. Closes ticket 13555.
+ - Tor clients no longer support connecting to hidden services
+ running on Tor 0.2.2.x and earlier; the Support022HiddenServices
+ option has been removed. (There shouldn't be any hidden services
+ running these versions on the network.) Closes ticket 7803.
+
+ o Directory authority changes:
+ - The directory authority Faravahar has a new IP address. This
+ closes ticket 14487.
+ - Remove turtles as a directory authority.
+ - Add longclaw as a new (v3) directory authority. This implements
+ ticket 13296. This keeps the directory authority count at 9.
+
+ o Major features (bridges):
+ - Expose the outgoing upstream HTTP/SOCKS proxy to pluggable
+ transports if they are configured via the "TOR_PT_PROXY"
+ environment variable. Implements proposal 232. Resolves
+ ticket 8402.
+
+ o Major features (changed defaults):
+ - Prevent relay operators from unintentionally running exits: When a
+ relay is configured as an exit node, we now warn the user unless
+ the "ExitRelay" option is set to 1. We warn even more loudly if
+ the relay is configured with the default exit policy, since this
+ can indicate accidental misconfiguration. Setting "ExitRelay 0"
+ stops Tor from running as an exit relay. Closes ticket 10067.
+
+ o Major features (client performance, hidden services):
+ - Allow clients to use optimistic data when connecting to a hidden
+ service, which should remove a round-trip from hidden service
+ initialization. See proposal 181 for details. Implements
+ ticket 13211.
+
+ o Major features (directory system):
+ - Upon receiving an unparseable directory object, if its digest
+ matches what we expected, then don't try to download it again.
+ Previously, when we got a descriptor we didn't like, we would keep
+ trying to download it over and over. Closes ticket 11243.
+ - When downloading server- or microdescriptors from a directory
+ server, we no longer launch multiple simultaneous requests to the
+ same server. This reduces load on the directory servers,
+ especially when directory guards are in use. Closes ticket 9969.
+ - When downloading server- or microdescriptors over a tunneled
+ connection, do not limit the length of our requests to what the
+ Squid proxy is willing to handle. Part of ticket 9969.
+ - Authorities can now vote on the correct digests and latest
+ versions for different software packages. This allows packages
+ that include Tor to use the Tor authority system as a way to get
+ notified of updates and their correct digests. Implements proposal
+ 227. Closes ticket 10395.
+
+ o Major features (guards):
+ - Introduce the Guardfraction feature to improves load balancing on
+ guard nodes. Specifically, it aims to reduce the traffic gap that
+ guard nodes experience when they first get the Guard flag. This is
+ a required step if we want to increase the guard lifetime to 9
+ months or greater. Closes ticket 9321.
+
+ o Major features (hidden services):
+ - Make HS port scanning more difficult by immediately closing the
+ circuit when a user attempts to connect to a nonexistent port.
+ Closes ticket 13667.
+ - Add a HiddenServiceStatistics option that allows Tor relays to
+ gather and publish statistics about the overall size and volume of
+ hidden service usage. Specifically, when this option is turned on,
+ an HSDir will publish an approximate number of hidden services
+ that have published descriptors to it the past 24 hours. Also, if
+ a relay has acted as a hidden service rendezvous point, it will
+ publish the approximate amount of rendezvous cells it has relayed
+ the past 24 hours. The statistics themselves are obfuscated so
+ that the exact values cannot be derived. For more details see
+ proposal 238, "Better hidden service stats from Tor relays". This
+ feature is currently disabled by default. Implements feature 13192.
+
+ o Major features (performance):
+ - Make the CPU worker implementation more efficient by avoiding the
+ kernel and lengthening pipelines. The original implementation used
+ sockets to transfer data from the main thread to the workers, and
+ didn't allow any thread to be assigned more than a single piece of
+ work at once. The new implementation avoids communications
+ overhead by making requests in shared memory, avoiding kernel IO
+ where possible, and keeping more requests in flight at once.
+ Implements ticket 9682.
+
+ o Major features (relay):
+ - Raise the minimum acceptable configured bandwidth rate for bridges
+ to 50 KiB/sec and for relays to 75 KiB/sec. (The old values were
+ 20 KiB/sec.) Closes ticket 13822.
+ - Complete revision of the code that relays use to decide which cell
+ to send next. Formerly, we selected the best circuit to write on
+ each channel, but we didn't select among channels in any
+ sophisticated way. Now, we choose the best circuits globally from
+ among those whose channels are ready to deliver traffic.
+
+ This patch implements a new inter-cmux comparison API, a global
+ high/low watermark mechanism and a global scheduler loop for
+ transmission prioritization across all channels as well as among
+ circuits on one channel. This schedule is currently tuned to
+ (tolerantly) avoid making changes in network performance, but it
+ should form the basis for major circuit performance increases in
+ the future. Code by Andrea; tuning by Rob Jansen; implements
+ ticket 9262.
+
+ o Major features (sample torrc):
+ - Add a new, infrequently-changed "torrc.minimal". This file is
+ similar to torrc.sample, but it will change as infrequently as
+ possible, for the benefit of users whose systems prompt them for
+ intervention whenever a default configuration file is changed.
+ Making this change allows us to update torrc.sample to be a more
+ generally useful "sample torrc".
+
+ o Major features (security, unix domain sockets):
+ - Allow SocksPort to be an AF_UNIX Unix Domain Socket. Now high risk
+ applications can reach Tor without having to create AF_INET or
+ AF_INET6 sockets, meaning they can completely disable their
+ ability to make non-Tor network connections. To create a socket of
+ this type, use "SocksPort unix:/path/to/socket". Implements
+ ticket 12585.
+ - Support mapping hidden service virtual ports to AF_UNIX sockets.
+ The syntax is "HiddenServicePort 80 unix:/path/to/socket".
+ Implements ticket 11485.
+
+ o Major bugfixes (client, automap):
+ - Repair automapping with IPv6 addresses. This automapping should
+ have worked previously, but one piece of debugging code that we
+ inserted to detect a regression actually caused the regression to
+ manifest itself again. Fixes bug 13811 and bug 12831; bugfix on
+ 0.2.4.7-alpha. Diagnosed and fixed by Francisco Blas
+ Izquierdo Riera.
+
+ o Major bugfixes (crash, OSX, security):
+ - Fix a remote denial-of-service opportunity caused by a bug in
+ OSX's _strlcat_chk() function. Fixes bug 15205; bug first appeared
+ in OSX 10.9.
+
+ o Major bugfixes (directory authorities):
+ - Do not assign the HSDir flag to relays if they are not Valid, or
+ currently hibernating. Fixes 12573; bugfix on 0.2.0.10-alpha.
+
+ o Major bugfixes (directory bandwidth performance):
+ - Don't flush the zlib buffer aggressively when compressing
+ directory information for clients. This should save about 7% of
+ the bandwidth currently used for compressed descriptors and
+ microdescriptors. Fixes bug 11787; bugfix on 0.1.1.23.
+
+ o Major bugfixes (exit node stability):
+ - Fix an assertion failure that could occur under high DNS load.
+ Fixes bug 14129; bugfix on Tor 0.0.7rc1. Found by "jowr";
+ diagnosed and fixed by "cypherpunks".
+
+ o Major bugfixes (FreeBSD IPFW transparent proxy):
+ - Fix address detection with FreeBSD transparent proxies, when
+ "TransProxyType ipfw" is in use. Fixes bug 15064; bugfix
+ on 0.2.5.4-alpha.
+
+ o Major bugfixes (hidden services):
+ - When closing an introduction circuit that was opened in parallel
+ with others, don't mark the introduction point as unreachable.
+ Previously, the first successful connection to an introduction
+ point would make the other introduction points get marked as
+ having timed out. Fixes bug 13698; bugfix on 0.0.6rc2.
+
+ o Major bugfixes (Linux seccomp2 sandbox):
+ - Upon receiving sighup with the seccomp2 sandbox enabled, do not
+ crash during attempts to call wait4. Fixes bug 15088; bugfix on
+ 0.2.5.1-alpha. Patch from "sanic".
+
+ o Major bugfixes (mixed relay-client operation):
+ - When running as a relay and client at the same time (not
+ recommended), if we decide not to use a new guard because we want
+ to retry older guards, only close the locally-originating circuits
+ passing through that guard. Previously we would close all the
+ circuits through that guard. Fixes bug 9819; bugfix on
+ 0.2.1.1-alpha. Reported by "skruffy".
+
+ o Major bugfixes (pluggable transports):
+ - Initialize the extended OR Port authentication cookie before
+ launching pluggable transports. This prevents a race condition
+ that occured when server-side pluggable transports would cache the
+ authentication cookie before it has been (re)generated. Fixes bug
+ 15240; bugfix on 0.2.5.1-alpha.
+
+ o Major bugfixes (relay, stability, possible security):
+ - Fix a bug that could lead to a relay crashing with an assertion
+ failure if a buffer of exactly the wrong layout is passed to
+ buf_pullup() at exactly the wrong time. Fixes bug 15083; bugfix on
+ 0.2.0.10-alpha. Patch from "cypherpunks".
+ - Do not assert if the 'data' pointer on a buffer is advanced to the
+ very end of the buffer; log a BUG message instead. Only assert if
+ it is past that point. Fixes bug 15083; bugfix on 0.2.0.10-alpha.
+
+ o Minor features (build):
+ - New --disable-system-torrc compile-time option to prevent Tor from
+ looking for the system-wide torrc or torrc-defaults files.
+ Resolves ticket 13037.
+
+ o Minor features (client):
+ - Clients are now willing to send optimistic data (before they
+ receive a 'connected' cell) to relays of any version. (Relays
+ without support for optimistic data are no longer supported on the
+ Tor network.) Resolves ticket 13153.
+
+ o Minor features (client):
+ - Validate hostnames in SOCKS5 requests more strictly. If SafeSocks
+ is enabled, reject requests with IP addresses as hostnames.
+ Resolves ticket 13315.
+
+ o Minor features (controller):
+ - Add a "SIGNAL HEARTBEAT" controller command that tells Tor to
+ write an unscheduled heartbeat message to the log. Implements
+ feature 9503.
+ - Include SOCKS_USERNAME and SOCKS_PASSWORD values in controller
+ events so controllers can observe circuit isolation inputs. Closes
+ ticket 8405.
+ - ControlPort now supports the unix:/path/to/socket syntax as an
+ alternative to the ControlSocket option, for consistency with
+ SocksPort and HiddenServicePort. Closes ticket 14451.
+ - New "GETINFO bw-event-cache" to get information about recent
+ bandwidth events. Closes ticket 14128. Useful for controllers to
+ get recent bandwidth history after the fix for ticket 13988.
+ - Messages about problems in the bootstrap process now include
+ information about the server we were trying to connect to when we
+ noticed the problem. Closes ticket 15006.
+
+ o Minor features (Denial of service resistance):
+ - Count the total number of bytes used storing hidden service
+ descriptors against the value of MaxMemInQueues. If we're low on
+ memory, and more than 20% of our memory is used holding hidden
+ service descriptors, free them until no more than 10% of our
+ memory holds hidden service descriptors. Free the least recently
+ fetched descriptors first. Resolves ticket 13806.
+ - When we have recently been under memory pressure (over 3/4 of
+ MaxMemInQueues is allocated), then allocate smaller zlib objects
+ for small requests. Closes ticket 11791.
+
+ o Minor features (directory authorities):
+ - Don't list relays with a bandwidth estimate of 0 in the consensus.
+ Implements a feature proposed during discussion of bug 13000.
+ - In tor-gencert, report an error if the user provides the same
+ argument more than once.
+ - If a directory authority can't find a best consensus method in the
+ votes that it holds, it now falls back to its favorite consensus
+ method. Previously, it fell back to method 1. Neither of these is
+ likely to get enough signatures, but "fall back to favorite"
+ doesn't require us to maintain support an obsolete consensus
+ method. Implements part of proposal 215.
+
+ o Minor features (geoip):
+ - Update geoip to the March 3 2015 Maxmind GeoLite2 Country database.
+ - Update geoip6 to the March 3 2015 Maxmind GeoLite2
+ Country database.
+
+ o Minor features (guard nodes):
+ - Reduce the time delay before saving guard status to disk from 10
+ minutes to 30 seconds (or from one hour to 10 minutes if
+ AvoidDiskWrites is set). Closes ticket 12485.
+
+ o Minor features (heartbeat):
+ - On relays, report how many connections we negotiated using each
+ version of the Tor link protocols. This information will let us
+ know if removing support for very old versions of the Tor
+ protocols is harming the network. Closes ticket 15212.
+
+ o Minor features (hidden service):
+ - Make Sybil attacks against hidden services harder by changing the
+ minimum time required to get the HSDir flag from 25 hours up to 96
+ hours. Addresses ticket 14149.
+ - New option "HiddenServiceAllowUnknownPorts" to allow hidden
+ services to disable the anti-scanning feature introduced in
+ 0.2.6.2-alpha. With this option not set, a connection to an
+ unlisted port closes the circuit. With this option set, only a
+ RELAY_DONE cell is sent. Closes ticket 14084.
+ - When re-enabling the network, don't try to build introduction
+ circuits until we have successfully built a circuit. This makes
+ hidden services come up faster when the network is re-enabled.
+ Patch from "akwizgran". Closes ticket 13447.
+ - When we fail to retrieve a hidden service descriptor, send the
+ controller an "HS_DESC FAILED" controller event. Implements
+ feature 13212.
+ - New HiddenServiceDirGroupReadable option to cause hidden service
+ directories and hostname files to be created group-readable. Patch
+ from "anon", David Stainton, and "meejah". Closes ticket 11291.
+
+ o Minor features (interface):
+ - Implement "-f -" command-line option to read torrc configuration
+ from standard input, if you don't want to store the torrc file in
+ the file system. Implements feature 13865.
+
+ o Minor features (logging):
+ - Add a count of unique clients to the bridge heartbeat message.
+ Resolves ticket 6852.
+ - Suppress "router info incompatible with extra info" message when
+ reading extrainfo documents from cache. (This message got loud
+ around when we closed bug 9812 in 0.2.6.2-alpha.) Closes
+ ticket 13762.
+ - Elevate hidden service authorized-client message from DEBUG to
+ INFO. Closes ticket 14015.
+ - On Unix-like systems, you can now use named pipes as the target of
+ the Log option, and other options that try to append to files.
+ Closes ticket 12061. Patch from "carlo von lynX".
+ - When opening a log file at startup, send it every log message that
+ we generated between startup and opening it. Previously, log
+ messages that were generated before opening the log file were only
+ logged to stdout. Closes ticket 6938.
+ - Add a TruncateLogFile option to overwrite logs instead of
+ appending to them. Closes ticket 5583.
+ - Quiet some log messages in the heartbeat and at startup. Closes
+ ticket 14950.
+
+ o Minor features (portability, Solaris):
+ - Threads are no longer disabled by default on Solaris; we believe
+ that the versions of Solaris with broken threading support are all
+ obsolete by now. Resolves ticket 9495.
+
+ o Minor features (relay):
+ - Re-check our address after we detect a changed IP address from
+ getsockname(). This ensures that the controller command "GETINFO
+ address" will report the correct value. Resolves ticket 11582.
+ Patch from "ra".
+ - A new AccountingRule option lets Relays set whether they'd like
+ AccountingMax to be applied separately to inbound and outbound
+ traffic, or applied to the sum of inbound and outbound traffic.
+ Resolves ticket 961. Patch by "chobe".
+ - When identity keypair is generated for first time, log a
+ congratulatory message that links to the new relay lifecycle
+ document. Implements feature 10427.
+
+ o Minor features (security, memory wiping):
+ - Ensure we securely wipe keys from memory after
+ crypto_digest_get_digest and init_curve25519_keypair_from_file
+ have finished using them. Resolves ticket 13477.
+
+ o Minor features (security, out-of-memory handling):
+ - When handling an out-of-memory condition, allocate less memory for
+ temporary data structures. Fixes issue 10115.
+ - When handling an out-of-memory condition, consider more types of
+ buffers, including those on directory connections, and zlib
+ buffers. Resolves ticket 11792.
+
+ o Minor features (stability):
+ - Add assertions in our hash-table iteration code to check for
+ corrupted values that could cause infinite loops. Closes
+ ticket 11737.
+
+ o Minor features (systemd):
+ - Various improvements and modernizations in systemd hardening
+ support. Closes ticket 13805. Patch from Craig Andrews.
+ - Where supported, when running with systemd, report successful
+ startup to systemd. Part of ticket 11016. Patch by Michael Scherer.
+ - When running with systemd, support systemd watchdog messages. Part
+ of ticket 11016. Patch by Michael Scherer.
+
+ o Minor features (testing networks):
+ - Add the TestingDirAuthVoteExit option, which lists nodes to assign
+ the "Exit" flag regardless of their uptime, bandwidth, or exit
+ policy. TestingTorNetwork must be set for this option to have any
+ effect. Previously, authorities would take up to 35 minutes to
+ give nodes the Exit flag in a test network. Partially implements
+ ticket 13161.
+ - Drop the minimum RendPostPeriod on a testing network to 5 seconds,
+ and the default on a testing network to 2 minutes. Drop the
+ MIN_REND_INITIAL_POST_DELAY on a testing network to 5 seconds, but
+ keep the default on a testing network at 30 seconds. This reduces
+ HS bootstrap time to around 25 seconds. Also, change the default
+ time in test-network.sh to match. Closes ticket 13401. Patch
+ by "teor".
+ - Create TestingDirAuthVoteHSDir to correspond to
+ TestingDirAuthVoteExit/Guard. Ensures that authorities vote the
+ HSDir flag for the listed relays regardless of uptime or ORPort
+ connectivity. Respects the value of VoteOnHidServDirectoriesV2.
+ Partial implementation for ticket 14067. Patch by "teor".
+
+ o Minor features (tor2web mode):
+ - Introduce the config option Tor2webRendezvousPoints, which allows
+ clients in Tor2webMode to select a specific Rendezvous Point to be
+ used in HS circuits. This might allow better performance for
+ Tor2Web nodes. Implements ticket 12844.
+
+ o Minor features (transparent proxy):
+ - Update the transparent proxy option checks to allow for both ipfw
+ and pf on OS X. Closes ticket 14002.
+ - Use the correct option when using IPv6 with transparent proxy
+ support on Linux. Resolves 13808. Patch by Francisco Blas
+ Izquierdo Riera.
+
+ o Minor features (validation):
+ - Check all date/time values passed to tor_timegm and
+ parse_rfc1123_time for validity, taking leap years into account.
+ Improves HTTP header validation. Implemented with bug 13476.
+ - In correct_tm(), limit the range of values returned by system
+ localtime(_r) and gmtime(_r) to be between the years 1 and 8099.
+ This means we don't have to deal with negative or too large dates,
+ even if a clock is wrong. Otherwise we might fail to read a file
+ written by us which includes such a date. Fixes bug 13476.
+ - Stop allowing invalid address patterns like "*/24" that contain
+ both a wildcard address and a bit prefix length. This affects all
+ our address-range parsing code. Fixes bug 7484; bugfix
+ on 0.0.2pre14.
+
+ o Minor bugfixes (bridge clients):
+ - When configured to use a bridge without an identity digest (not
+ recommended), avoid launching an extra channel to it when
+ bootstrapping. Fixes bug 7733; bugfix on 0.2.4.4-alpha.
+
+ o Minor bugfixes (bridges):
+ - When DisableNetwork is set, do not launch pluggable transport
+ plugins, and if any are running, terminate them. Fixes bug 13213;
+ bugfix on 0.2.3.6-alpha.
+
+ o Minor bugfixes (C correctness):
+ - Fix several instances of possible integer overflow/underflow/NaN.
+ Fixes bug 13104; bugfix on 0.2.3.1-alpha and later. Patches
+ from "teor".
+ - In circuit_build_times_calculate_timeout() in circuitstats.c,
+ avoid dividing by zero in the pareto calculations. This traps
+ under clang's "undefined-trap" sanitizer. Fixes bug 13290; bugfix
+ on 0.2.2.2-alpha.
+ - Fix an integer overflow in format_time_interval(). Fixes bug
+ 13393; bugfix on 0.2.0.10-alpha.
+ - Set the correct day of year value when the system's localtime(_r)
+ or gmtime(_r) functions fail to set struct tm. Not externally
+ visible. Fixes bug 13476; bugfix on 0.0.2pre14.
+ - Avoid unlikely signed integer overflow in tor_timegm on systems
+ with 32-bit time_t. Fixes bug 13476; bugfix on 0.0.2pre14.
+
+ o Minor bugfixes (certificate handling):
+ - If an authority operator accidentally makes a signing certificate
+ with a future publication time, do not discard its real signing
+ certificates. Fixes bug 11457; bugfix on 0.2.0.3-alpha.
+ - Remove any old authority certificates that have been superseded
+ for at least two days. Previously, we would keep superseded
+ certificates until they expired, if they were published close in
+ time to the certificate that superseded them. Fixes bug 11454;
+ bugfix on 0.2.1.8-alpha.
+
+ o Minor bugfixes (client):
+ - Fix smartlist_choose_node_by_bandwidth() so that relays with the
+ BadExit flag are not considered worthy candidates. Fixes bug
+ 13066; bugfix on 0.1.2.3-alpha.
+ - Use the consensus schedule for downloading consensuses, and not
+ the generic schedule. Fixes bug 11679; bugfix on 0.2.2.6-alpha.
+ - Handle unsupported or malformed SOCKS5 requests properly by
+ responding with the appropriate error message before closing the
+ connection. Fixes bugs 12971 and 13314; bugfix on 0.0.2pre13.
+
+ o Minor bugfixes (client, automapping):
+ - Avoid crashing on torrc lines for VirtualAddrNetworkIPv[4|6] when
+ no value follows the option. Fixes bug 14142; bugfix on
+ 0.2.4.7-alpha. Patch by "teor".
+ - Fix a memory leak when using AutomapHostsOnResolve. Fixes bug
+ 14195; bugfix on 0.1.0.1-rc.
+ - Prevent changes to other options from removing the wildcard value
+ "." from "AutomapHostsSuffixes". Fixes bug 12509; bugfix
+ on 0.2.0.1-alpha.
+ - Allow MapAddress and AutomapHostsOnResolve to work together when
+ an address is mapped into another address type (like .onion) that
+ must be automapped at resolve time. Fixes bug 7555; bugfix
+ on 0.2.0.1-alpha.
+
+ o Minor bugfixes (client, bridges):
+ - When we are using bridges and we had a network connectivity
+ problem, only retry connecting to our currently configured
+ bridges, not all bridges we know about and remember using. Fixes
+ bug 14216; bugfix on 0.2.2.17-alpha.
+
+ o Minor bugfixes (client, DNS):
+ - Report the correct cached DNS expiration times on SOCKS port or in
+ DNS replies. Previously, we would report everything as "never
+ expires." Fixes bug 14193; bugfix on 0.2.3.17-beta.
+ - Avoid a small memory leak when we find a cached answer for a
+ reverse DNS lookup in a client-side DNS cache. (Remember, client-
+ side DNS caching is off by default, and is not recommended.) Fixes
+ bug 14259; bugfix on 0.2.0.1-alpha.
+
+ o Minor bugfixes (client, IPv6):
+ - Reject socks requests to literal IPv6 addresses when IPv6Traffic
+ flag is not set; and not because the NoIPv4Traffic flag was set.
+ Previously we'd looked at the NoIPv4Traffic flag for both types of
+ literal addresses. Fixes bug 14280; bugfix on 0.2.4.7-alpha.
+
+ o Minor bugfixes (client, microdescriptors):
+ - Use a full 256 bits of the SHA256 digest of a microdescriptor when
+ computing which microdescriptors to download. This keeps us from
+ erroneous download behavior if two microdescriptor digests ever
+ have the same first 160 bits. Fixes part of bug 13399; bugfix
+ on 0.2.3.1-alpha.
+ - Reset a router's status if its microdescriptor digest changes,
+ even if the first 160 bits remain the same. Fixes part of bug
+ 13399; bugfix on 0.2.3.1-alpha.
+
+ o Minor bugfixes (client, torrc):
+ - Stop modifying the value of our DirReqStatistics torrc option just
+ because we're not a bridge or relay. This bug was causing Tor
+ Browser users to write "DirReqStatistics 0" in their torrc files
+ as if they had chosen to change the config. Fixes bug 4244; bugfix
+ on 0.2.3.1-alpha.
+ - When GeoIPExcludeUnknown is enabled, do not incorrectly decide
+ that our options have changed every time we SIGHUP. Fixes bug
+ 9801; bugfix on 0.2.4.10-alpha. Patch from "qwerty1".
+
+ o Minor bugfixes (compilation):
+ - Fix a compilation warning on s390. Fixes bug 14988; bugfix
+ on 0.2.5.2-alpha.
+ - Silence clang warnings under --enable-expensive-hardening,
+ including implicit truncation of 64 bit values to 32 bit, const
+ char assignment to self, tautological compare, and additional
+ parentheses around equality tests. Fixes bug 13577; bugfix
+ on 0.2.5.4-alpha.
+ - Fix a clang warning about checking whether an address in the
+ middle of a structure is NULL. Fixes bug 14001; bugfix
+ on 0.2.1.2-alpha.
+ - The address of an array in the middle of a structure will always
+ be non-NULL. clang recognises this and complains. Disable the
+ tautologous and redundant check to silence this warning. Fixes bug
+ 14001; bugfix on 0.2.1.2-alpha.
+ - Compile correctly with (unreleased) OpenSSL 1.1.0 headers.
+ Addresses ticket 14188.
+ - Build without warnings with the stock OpenSSL srtp.h header, which
+ has a duplicate declaration of SSL_get_selected_srtp_profile().
+ Fixes bug 14220; this is OpenSSL's bug, not ours.
+ - Do not compile any code related to Tor2Web mode when Tor2Web mode
+ is not enabled at compile time. Previously, this code was included
+ in a disabled state. See discussion on ticket 12844.
+ - Allow our configure script to build correctly with autoconf 2.62
+ again. Fixes bug 12693; bugfix on 0.2.5.2-alpha.
+ - Improve the error message from ./configure to make it clear that
+ when asciidoc has not been found, the user will have to either add
+ --disable-asciidoc argument or install asciidoc. Resolves
+ ticket 13228.
+
+ o Minor bugfixes (controller):
+ - Report "down" in response to the "GETINFO entry-guards" command
+ when relays are down with an unreachable_since value. Previously,
+ we would report "up". Fixes bug 14184; bugfix on 0.1.2.2-alpha.
+ - Avoid crashing on a malformed EXTENDCIRCUIT command. Fixes bug
+ 14116; bugfix on 0.2.2.9-alpha.
+
+ o Minor bugfixes (controller):
+ - Return an error when the second or later arguments of the
+ "setevents" controller command are invalid events. Previously we
+ would return success while silently skipping invalid events. Fixes
+ bug 13205; bugfix on 0.2.3.2-alpha. Reported by "fpxnns".
+
+ o Minor bugfixes (directory authority):
+ - Allow directory authorities to fetch more data from one another if
+ they find themselves missing lots of votes. Previously, they had
+ been bumping against the 10 MB queued data limit. Fixes bug 14261;
+ bugfix on 0.1.2.5-alpha.
+ - Do not attempt to download extrainfo documents which we will be
+ unable to validate with a matching server descriptor. Fixes bug
+ 13762; bugfix on 0.2.0.1-alpha.
+ - Fix a bug that was truncating AUTHDIR_NEWDESC events sent to the
+ control port. Fixes bug 14953; bugfix on 0.2.0.1-alpha.
+ - Enlarge the buffer to read bwauth generated files to avoid an
+ issue when parsing the file in dirserv_read_measured_bandwidths().
+ Fixes bug 14125; bugfix on 0.2.2.1-alpha.
+ - When running as a v3 directory authority, advertise that you serve
+ extra-info documents so that clients who want them can find them
+ from you too. Fixes part of bug 11683; bugfix on 0.2.0.1-alpha.
+
+ o Minor bugfixes (directory system):
+ - Always believe that v3 directory authorities serve extra-info
+ documents, whether they advertise "caches-extra-info" or not.
+ Fixes part of bug 11683; bugfix on 0.2.0.1-alpha.
+ - Check the BRIDGE_DIRINFO flag bitwise rather than using equality.
+ Previously, directories offering BRIDGE_DIRINFO and some other
+ flag (i.e. microdescriptors or extrainfo) would be ignored when
+ looking for bridges. Partially fixes bug 13163; bugfix
+ on 0.2.0.7-alpha.
+
+ o Minor bugfixes (file handling):
+ - Stop failing when key files are zero-length. Instead, generate new
+ keys, and overwrite the empty key files. Fixes bug 13111; bugfix
+ on all versions of Tor. Patch by "teor".
+ - Stop generating a fresh .old RSA onion key file when the .old file
+ is missing. Fixes part of 13111; bugfix on 0.0.6rc1.
+ - Avoid overwriting .old key files with empty key files.
+ - Skip loading zero-length extrainfo store, router store, stats,
+ state, and key files.
+ - Avoid crashing when trying to reload a torrc specified as a
+ relative path with RunAsDaemon turned on. Fixes bug 13397; bugfix
+ on 0.2.3.11-alpha.
+
+ o Minor bugfixes (hidden services):
+ - Close the introduction circuit when we have no more usable intro
+ points, instead of waiting for it to time out. This also ensures
+ that no follow-up HS descriptor fetch is triggered when the
+ circuit eventually times out. Fixes bug 14224; bugfix on 0.0.6.
+ - When fetching a hidden service descriptor for a down service that
+ was recently up, do not keep refetching until we try the same
+ replica twice in a row. Fixes bug 14219; bugfix on 0.2.0.10-alpha.
+ - Correctly send a controller event when we find that a rendezvous
+ circuit has finished. Fixes bug 13936; bugfix on 0.1.1.5-alpha.
+ - Pre-check directory permissions for new hidden-services to avoid
+ at least one case of "Bug: Acting on config options left us in a
+ broken state. Dying." Fixes bug 13942; bugfix on 0.0.6pre1.
+ - When fetching hidden service descriptors, we now check not only
+ for whether we got the hidden service we had in mind, but also
+ whether we got the particular descriptors we wanted. This prevents
+ a class of inefficient but annoying DoS attacks by hidden service
+ directories. Fixes bug 13214; bugfix on 0.2.1.6-alpha. Reported
+ by "special".
+
+ o Minor bugfixes (Linux seccomp2 sandbox):
+ - Make transparent proxy support work along with the seccomp2
+ sandbox. Fixes part of bug 13808; bugfix on 0.2.5.1-alpha. Patch
+ by Francisco Blas Izquierdo Riera.
+ - Fix a memory leak in tor-resolve when running with the sandbox
+ enabled. Fixes bug 14050; bugfix on 0.2.5.9-rc.
+ - Allow glibc fatal errors to be sent to stderr before Tor exits.
+ Previously, glibc would try to write them to /dev/tty, and the
+ sandbox would trap the call and make Tor exit prematurely. Fixes
+ bug 14759; bugfix on 0.2.5.1-alpha.
+
+ o Minor bugfixes (logging):
+ - Avoid crashing when there are more log domains than entries in
+ domain_list. Bugfix on 0.2.3.1-alpha.
+ - Downgrade warnings about RSA signature failures to info log level.
+ Emit a warning when an extra info document is found incompatible
+ with a corresponding router descriptor. Fixes bug 9812; bugfix
+ on 0.0.6rc3.
+ - Make connection_ap_handshake_attach_circuit() log the circuit ID
+ correctly. Fixes bug 13701; bugfix on 0.0.6.
+
+ o Minor bugfixes (networking):
+ - Check for orconns and use connection_or_close_for_error() rather
+ than connection_mark_for_close() directly in the getsockopt()
+ failure case of connection_handle_write_impl(). Fixes bug 11302;
+ bugfix on 0.2.4.4-alpha.
+
+ o Minor bugfixes (parsing):
+ - Stop accepting milliseconds (or other junk) at the end of
+ descriptor publication times. Fixes bug 9286; bugfix on 0.0.2pre25.
+ - Support two-number and three-number version numbers correctly, in
+ case we change the Tor versioning system in the future. Fixes bug
+ 13661; bugfix on 0.0.8pre1.
+
+ o Minor bugfixes (portability):
+ - Fix the ioctl()-based network interface lookup code so that it
+ will work on systems that have variable-length struct ifreq, for
+ example Mac OS X.
+ - Use the correct datatype in the SipHash-2-4 function to prevent
+ compilers from assuming any sort of alignment. Fixes bug 15436;
+ bugfix on 0.2.5.3-alpha.
+
+ o Minor bugfixes (preventative security, C safety):
+ - When reading a hexadecimal, base-32, or base-64 encoded value from
+ a string, always overwrite the whole output buffer. This prevents
+ some bugs where we would look at (but fortunately, not reveal)
+ uninitialized memory on the stack. Fixes bug 14013; bugfix on all
+ versions of Tor.
+ - Clear all memory targetted by tor_addr_{to,from}_sockaddr(), not
+ just the part that's used. This makes it harder for data leak bugs
+ to occur in the event of other programming failures. Resolves
+ ticket 14041.
+
+ o Minor bugfixes (relay):
+ - When generating our family list, remove spaces from around the
+ entries. Fixes bug 12728; bugfix on 0.2.1.7-alpha.
+ - If our previous bandwidth estimate was 0 bytes, allow publishing a
+ new relay descriptor immediately. Fixes bug 13000; bugfix
+ on 0.1.1.6-alpha.
+
+ o Minor bugfixes (shutdown):
+ - When shutting down, always call event_del() on lingering read or
+ write events before freeing them. Otherwise, we risk double-frees
+ or read-after-frees in event_base_free(). Fixes bug 12985; bugfix
+ on 0.1.0.2-rc.
+
+ o Minor bugfixes (small memory leaks):
+ - Avoid leaking memory when using IPv6 virtual address mappings.
+ Fixes bug 14123; bugfix on 0.2.4.7-alpha. Patch by Tom van
+ der Woerdt.
+
+ o Minor bugfixes (statistics):
+ - Increase period over which bandwidth observations are aggregated
+ from 15 minutes to 4 hours. Fixes bug 13988; bugfix on 0.0.8pre1.
+
+ o Minor bugfixes (systemd support):
+ - Run correctly under systemd with the RunAsDaemon option set. Fixes
+ part of bug 14141; bugfix on 0.2.5.7-rc. Patch from Tomasz Torcz.
+ - Inform the systemd supervisor about more changes in the Tor
+ process status. Implements part of ticket 14141. Patch from
+ Tomasz Torcz.
+
+ o Minor bugfixes (testing networks):
+ - Fix TestingDirAuthVoteGuard to properly give out Guard flags in a
+ testing network. Fixes bug 13064; bugfix on 0.2.5.2-alpha.
+ - Stop using the default authorities in networks which provide both
+ AlternateDirAuthority and AlternateBridgeAuthority. Partially
+ fixes bug 13163; bugfix on 0.2.0.13-alpha.
+
+ o Minor bugfixes (testing networks, fast startup):
+ - Allow Tor to build circuits using a consensus with no exits. If
+ the consensus has no exits (typical of a bootstrapping test
+ network), allow Tor to build circuits once enough descriptors have
+ been downloaded. This assists in bootstrapping a testing Tor
+ network. Fixes bug 13718; bugfix on 0.2.4.10-alpha. Patch
+ by "teor".
+ - When V3AuthVotingInterval is low, give a lower If-Modified-Since
+ header to directory servers. This allows us to obtain consensuses
+ promptly when the consensus interval is very short. This assists
+ in bootstrapping a testing Tor network. Fixes parts of bugs 13718
+ and 13963; bugfix on 0.2.0.3-alpha. Patch by "teor".
+ - Stop assuming that private addresses are local when checking
+ reachability in a TestingTorNetwork. Instead, when testing, assume
+ all OR connections are remote. (This is necessary due to many test
+ scenarios running all relays on localhost.) This assists in
+ bootstrapping a testing Tor network. Fixes bug 13924; bugfix on
+ 0.1.0.1-rc. Patch by "teor".
+ - Avoid building exit circuits from a consensus with no exits. Now
+ thanks to our fix for 13718, we accept a no-exit network as not
+ wholly lost, but we need to remember not to try to build exit
+ circuits on it. Closes ticket 13814; patch by "teor".
+ - Stop requiring exits to have non-zero bandwithcapacity in a
+ TestingTorNetwork. Instead, when TestingMinExitFlagThreshold is 0,
+ ignore exit bandwidthcapacity. This assists in bootstrapping a
+ testing Tor network. Fixes parts of bugs 13718 and 13839; bugfix
+ on 0.2.0.3-alpha. Patch by "teor".
+ - Add "internal" to some bootstrap statuses when no exits are
+ available. If the consensus does not contain Exits, Tor will only
+ build internal circuits. In this case, relevant statuses will
+ contain the word "internal" as indicated in the Tor control-
+ spec.txt. When bootstrap completes, Tor will be ready to build
+ internal circuits. If a future consensus contains Exits, exit
+ circuits may become available. Fixes part of bug 13718; bugfix on
+ 0.2.4.10-alpha. Patch by "teor".
+ - Decrease minimum consensus interval to 10 seconds when
+ TestingTorNetwork is set, or 5 seconds for the first consensus.
+ Fix assumptions throughout the code that assume larger intervals.
+ Fixes bugs 13718 and 13823; bugfix on 0.2.0.3-alpha. Patch
+ by "teor".
+ - Avoid excluding guards from path building in minimal test
+ networks, when we're in a test network and excluding guards would
+ exclude all relays. This typically occurs in incredibly small tor
+ networks, and those using "TestingAuthVoteGuard *". Fixes part of
+ bug 13718; bugfix on 0.1.1.11-alpha. Patch by "teor".
+
+ o Minor bugfixes (testing):
+ - Avoid a side-effect in a tor_assert() in the unit tests. Fixes bug
+ 15188; bugfix on 0.1.2.3-alpha. Patch from Tom van der Woerdt.
+ - Stop spawn test failures due to a race condition between the
+ SIGCHLD handler updating the process status, and the test reading
+ it. Fixes bug 13291; bugfix on 0.2.3.3-alpha.
+ - Avoid passing an extra backslash when creating a temporary
+ directory for running the unit tests on Windows. Fixes bug 12392;
+ bugfix on 0.2.2.25-alpha. Patch from Gisle Vanem.
+
+ o Minor bugfixes (TLS):
+ - Check more thoroughly throughout the TLS code for possible
+ unlogged TLS errors. Possible diagnostic or fix for bug 13319.
+
+ o Minor bugfixes (transparent proxy):
+ - Use getsockname, not getsockopt, to retrieve the address for a
+ TPROXY-redirected connection. Fixes bug 13796; bugfix
+ on 0.2.5.2-alpha.
+
+ o Minor bugfixes (windows):
+ - Remove code to special-case handling of NTE_BAD_KEYSET when
+ acquiring windows CryptoAPI context. This error can't actually
+ occur for the parameters we're providing. Fixes bug 10816; bugfix
+ on 0.0.2pre26.
+
+ o Minor bugfixes (zlib):
+ - Avoid truncating a zlib stream when trying to finalize it with an
+ empty output buffer. Fixes bug 11824; bugfix on 0.1.1.23.
+
+ o Code simplification and refactoring:
+ - Change the entry_is_live() function to take named bitfield
+ elements instead of an unnamed list of booleans. Closes
+ ticket 12202.
+ - Refactor and unit-test entry_is_time_to_retry() in entrynodes.c.
+ Resolves ticket 12205.
+ - Use calloc and reallocarray functions instead of multiply-
+ then-malloc. This makes it less likely for us to fall victim to an
+ integer overflow attack when allocating. Resolves ticket 12855.
+ - Use the standard macro name SIZE_MAX, instead of our
+ own SIZE_T_MAX.
+ - Document usage of the NO_DIRINFO and ALL_DIRINFO flags clearly in
+ functions which take them as arguments. Replace 0 with NO_DIRINFO
+ in a function call for clarity. Seeks to prevent future issues
+ like 13163.
+ - Avoid 4 null pointer errors under clang static analysis by using
+ tor_assert() to prove that the pointers aren't null. Fixes
+ bug 13284.
+ - Rework the API of policies_parse_exit_policy() to use a bitmask to
+ represent parsing options, instead of a confusing mess of
+ booleans. Resolves ticket 8197.
+ - Introduce a helper function to parse ExitPolicy in
+ or_options_t structure.
+ - Move fields related to isolating and configuring client ports into
+ a shared structure. Previously, they were duplicated across
+ port_cfg_t, listener_connection_t, and edge_connection_t. Failure
+ to copy them correctly had been the cause of at least one bug in
+ the past. Closes ticket 8546.
+ - Refactor the get_interface_addresses_raw() doom-function into
+ multiple smaller and simpler subfunctions. Cover the resulting
+ subfunctions with unit-tests. Fixes a significant portion of
+ issue 12376.
+ - Remove workaround in dirserv_thinks_router_is_hs_dir() that was
+ only for version <= 0.2.2.24 which is now deprecated. Closes
+ ticket 14202.
+ - Remove a test for a long-defunct broken version-one
+ directory server.
+ - Refactor main loop to extract the 'loop' part. This makes it
+ easier to run Tor under Shadow. Closes ticket 15176.
+ - Stop using can_complete_circuits as a global variable; access it
+ with a function instead.
+ - Avoid using operators directly as macro arguments: this lets us
+ apply coccinelle transformations to our codebase more directly.
+ Closes ticket 13172.
+ - Combine the functions used to parse ClientTransportPlugin and
+ ServerTransportPlugin into a single function. Closes ticket 6456.
+ - Add inline functions and convenience macros for inspecting channel
+ state. Refactor the code to use convenience macros instead of
+ checking channel state directly. Fixes issue 7356.
+ - Document all members of was_router_added_t and rename
+ ROUTER_WAS_NOT_NEW to ROUTER_IS_ALREADY_KNOWN to make it less
+ confusable with ROUTER_WAS_TOO_OLD. Fixes issue 13644.
+ - In connection_exit_begin_conn(), use END_CIRC_REASON_TORPROTOCOL
+ constant instead of hardcoded value. Fixes issue 13840.
+ - Refactor our generic strmap and digestmap types into a single
+ implementation, so that we can add a new digest256map
+ type trivially.
+
+ o Documentation:
+ - Add a doc/TUNING document with tips for handling large numbers of
+ TCP connections when running busy Tor relay. Update the warning
+ message to point to this file when running out of sockets
+ operating system is allowing to use simultaneously. Resolves
+ ticket 9708.
+ - Adding section on OpenBSD to our TUNING document. Thanks to mmcc
+ for writing the OpenBSD-specific tips. Resolves ticket 13702.
+ - Make the tor-resolve documentation match its help string and its
+ options. Resolves part of ticket 14325.
+ - Log a more useful error message from tor-resolve when failing to
+ look up a hidden service address. Resolves part of ticket 14325.
+ - Document the bridge-authority-only 'networkstatus-bridges' file.
+ Closes ticket 13713; patch from "tom".
+ - Fix typo in PredictedPortsRelevanceTime option description in
+ manpage. Resolves issue 13707.
+ - Stop suggesting that users specify relays by nickname: it isn't a
+ good idea. Also, properly cross-reference how to specify relays in
+ all parts of manual documenting options that take a list of
+ relays. Closes ticket 13381.
+ - Clarify the HiddenServiceDir option description in manpage to make
+ it clear that relative paths are taken with respect to the current
+ working directory. Also clarify that this behavior is not
+ guaranteed to remain indefinitely. Fixes issue 13913.
+
+ o Distribution (systemd):
+ - systemd unit file: only allow tor to write to /var/lib/tor and
+ /var/log/tor. The rest of the filesystem is accessible for reading
+ only. Patch by intrigeri; resolves ticket 12751.
+ - systemd unit file: ensure that the process and all its children
+ can never gain new privileges. Patch by intrigeri; resolves
+ ticket 12939.
+ - systemd unit file: set up /var/run/tor as writable for the Tor
+ service. Patch by intrigeri; resolves ticket 13196.
+
+ o Downgraded warnings:
+ - Don't warn when we've attempted to contact a relay using the wrong
+ ntor onion key. Closes ticket 9635.
+
+ o Removed code:
+ - Remove some lingering dead code that once supported mempools.
+ Mempools were disabled by default in 0.2.5, and removed entirely
+ in 0.2.6.3-alpha. Closes more of ticket 14848; patch
+ by "cypherpunks".
+
+ o Removed features (directory authorities):
+ - Remove code that prevented authorities from listing Tor relays
+ affected by CVE-2011-2769 as guards. These relays are already
+ rejected altogether due to the minimum version requirement of
+ 0.2.3.16-alpha. Closes ticket 13152.
+ - The "AuthDirRejectUnlisted" option no longer has any effect, as
+ the fingerprints file (approved-routers) has been deprecated.
+ - Directory authorities do not support being Naming dirauths anymore.
+ The "NamingAuthoritativeDir" config option is now obsolete.
+ - Directory authorities do not support giving out the BadDirectory
+ flag anymore.
+ - Directory authorities no longer advertise or support consensus
+ methods 1 through 12 inclusive. These consensus methods were
+ obsolete and/or insecure: maintaining the ability to support them
+ served no good purpose. Implements part of proposal 215; closes
+ ticket 10163.
+
+ o Removed features:
+ - To avoid confusion with the "ExitRelay" option, "ExitNode" is no
+ longer silently accepted as an alias for "ExitNodes".
+ - The --enable-mempool and --enable-buf-freelists options, which
+ were originally created to work around bad malloc implementations,
+ no longer exist. They were off-by-default in 0.2.5. Closes
+ ticket 14848.
+ - We no longer remind the user about configuration options that have
+ been obsolete since 0.2.3.x or earlier. Patch by Adrien Bak.
+ - Remove our old, non-weighted bandwidth-based node selection code.
+ Previously, we used it as a fallback when we couldn't perform
+ weighted bandwidth-based node selection. But that would only
+ happen in the cases where we had no consensus, or when we had a
+ consensus generated by buggy or ancient directory authorities. In
+ either case, it's better to use the more modern, better maintained
+ algorithm, with reasonable defaults for the weights. Closes
+ ticket 13126.
+ - Remove the --disable-curve25519 configure option. Relays and
+ clients now are required to support curve25519 and the
+ ntor handshake.
+ - The old "StrictEntryNodes" and "StrictExitNodes" options, which
+ used to be deprecated synonyms for "StrictNodes", are now marked
+ obsolete. Resolves ticket 12226.
+ - Clients don't understand the BadDirectory flag in the consensus
+ anymore, and ignore it.
+
+ o Removed platform support:
+ - We no longer include special code to build on Windows CE; as far
+ as we know, nobody has used Tor on Windows CE in a very long time.
+ Closes ticket 11446.
+
+ o Testing (test-network.sh):
+ - Stop using "echo -n", as some shells' built-in echo doesn't
+ support "-n". Instead, use "/bin/echo -n". Partially fixes
+ bug 13161.
+ - Stop an apparent test-network hang when used with make -j2. Fixes
+ bug 13331.
+ - Add a --delay option to test-network.sh, which configures the
+ delay before the chutney network tests for data transmission.
+ Partially implements ticket 13161.
+
+ o Testing:
+ - Test that tor does not fail when key files are zero-length. Check
+ that tor generates new keys, and overwrites the empty key files.
+ - Test that tor generates new keys when keys are missing
+ (existing behavior).
+ - Test that tor does not overwrite key files that already contain
+ data (existing behavior). Tests bug 13111. Patch by "teor".
+ - New "make test-stem" target to run stem integration tests.
+ Requires that the "STEM_SOURCE_DIR" environment variable be set.
+ Closes ticket 14107.
+ - Make the test_cmdline_args.py script work correctly on Windows.
+ Patch from Gisle Vanem.
+ - Move the slower unit tests into a new "./src/test/test-slow"
+ binary that can be run independently of the other tests. Closes
+ ticket 13243.
+ - New tests for many parts of channel, relay, and circuitmux
+ functionality. Code by Andrea; part of 9262.
+ - New tests for parse_transport_line(). Part of ticket 6456.
+ - In the unit tests, use chgrp() to change the group of the unit
+ test temporary directory to the current user, so that the sticky
+ bit doesn't interfere with tests that check directory groups.
+ Closes 13678.
+ - Add unit tests for resolve_my_addr(). Part of ticket 12376; patch
+ by 'rl1987'.
+ - Refactor the function that chooses guard nodes so that it can more
+ easily be tested; write some tests for it.
+ - Fix and re-enable the fgets_eagain unit test. Fixes bug 12503;
+ bugfix on 0.2.3.1-alpha. Patch from "cypherpunks."
+ - Create unit tests for format_time_interval(). With bug 13393.
+ - Add unit tests for tor_timegm signed overflow, tor_timegm and
+ parse_rfc1123_time validity checks, correct_tm year clamping. Unit
+ tests (visible) fixes in bug 13476.
+ - Add a "coverage-html" make target to generate HTML-visualized
+ coverage results when building with --enable-coverage. (Requires
+ lcov.) Patch from Kevin Murray.
+ - Enable the backtrace handler (where supported) when running the
+ unit tests.
+ - Revise all unit tests that used the legacy test_* macros to
+ instead use the recommended tt_* macros. This patch was generated
+ with coccinelle, to avoid manual errors. Closes ticket 13119.
+
+Changes in version 0.2.5.11 - 2015-03-17
+ Tor 0.2.5.11 is the second stable release in the 0.2.5 series.
+
+ It backports several bugfixes from the 0.2.6 branch, including a
+ couple of medium-level security fixes for relays and exit nodes.
+ It also updates the list of directory authorities.
+
+ o Directory authority changes:
+ - Remove turtles as a directory authority.
+ - Add longclaw as a new (v3) directory authority. This implements
+ ticket 13296. This keeps the directory authority count at 9.
+ - The directory authority Faravahar has a new IP address. This
+ closes ticket 14487.
+
+ o Major bugfixes (crash, OSX, security):
+ - Fix a remote denial-of-service opportunity caused by a bug in
+ OSX's _strlcat_chk() function. Fixes bug 15205; bug first appeared
+ in OSX 10.9.
+
+ o Major bugfixes (relay, stability, possible security):
+ - Fix a bug that could lead to a relay crashing with an assertion
+ failure if a buffer of exactly the wrong layout was passed to
+ buf_pullup() at exactly the wrong time. Fixes bug 15083; bugfix on
+ 0.2.0.10-alpha. Patch from 'cypherpunks'.
+ - Do not assert if the 'data' pointer on a buffer is advanced to the
+ very end of the buffer; log a BUG message instead. Only assert if
+ it is past that point. Fixes bug 15083; bugfix on 0.2.0.10-alpha.
+
+ o Major bugfixes (exit node stability):
+ - Fix an assertion failure that could occur under high DNS load.
+ Fixes bug 14129; bugfix on Tor 0.0.7rc1. Found by "jowr";
+ diagnosed and fixed by "cypherpunks".
+
+ o Major bugfixes (Linux seccomp2 sandbox):
+ - Upon receiving sighup with the seccomp2 sandbox enabled, do not
+ crash during attempts to call wait4. Fixes bug 15088; bugfix on
+ 0.2.5.1-alpha. Patch from "sanic".
+
+ o Minor features (controller):
+ - New "GETINFO bw-event-cache" to get information about recent
+ bandwidth events. Closes ticket 14128. Useful for controllers to
+ get recent bandwidth history after the fix for ticket 13988.
+
+ o Minor features (geoip):
+ - Update geoip to the March 3 2015 Maxmind GeoLite2 Country database.
+ - Update geoip6 to the March 3 2015 Maxmind GeoLite2
+ Country database.
+
+ o Minor bugfixes (client, automapping):
+ - Avoid crashing on torrc lines for VirtualAddrNetworkIPv[4|6] when
+ no value follows the option. Fixes bug 14142; bugfix on
+ 0.2.4.7-alpha. Patch by "teor".
+ - Fix a memory leak when using AutomapHostsOnResolve. Fixes bug
+ 14195; bugfix on 0.1.0.1-rc.
+
+ o Minor bugfixes (compilation):
+ - Build without warnings with the stock OpenSSL srtp.h header, which
+ has a duplicate declaration of SSL_get_selected_srtp_profile().
+ Fixes bug 14220; this is OpenSSL's bug, not ours.
+
+ o Minor bugfixes (directory authority):
+ - Allow directory authorities to fetch more data from one another if
+ they find themselves missing lots of votes. Previously, they had
+ been bumping against the 10 MB queued data limit. Fixes bug 14261;
+ bugfix on 0.1.2.5-alpha.
+ - Enlarge the buffer to read bwauth generated files to avoid an
+ issue when parsing the file in dirserv_read_measured_bandwidths().
+ Fixes bug 14125; bugfix on 0.2.2.1-alpha.
+
+ o Minor bugfixes (statistics):
+ - Increase period over which bandwidth observations are aggregated
+ from 15 minutes to 4 hours. Fixes bug 13988; bugfix on 0.0.8pre1.
+
+ o Minor bugfixes (preventative security, C safety):
+ - When reading a hexadecimal, base-32, or base-64 encoded value from
+ a string, always overwrite the whole output buffer. This prevents
+ some bugs where we would look at (but fortunately, not reveal)
+ uninitialized memory on the stack. Fixes bug 14013; bugfix on all
+ versions of Tor.
+
+
+Changes in version 0.2.4.26 - 2015-03-17
+ Tor 0.2.4.26 includes an updated list of directory authorities. It
+ also backports a couple of stability and security bugfixes from 0.2.5
+ and beyond.
+
+ o Directory authority changes:
+ - Remove turtles as a directory authority.
+ - Add longclaw as a new (v3) directory authority. This implements
+ ticket 13296. This keeps the directory authority count at 9.
+ - The directory authority Faravahar has a new IP address. This
+ closes ticket 14487.
+
+ o Major bugfixes (exit node stability, also in 0.2.6.3-alpha):
+ - Fix an assertion failure that could occur under high DNS load.
+ Fixes bug 14129; bugfix on Tor 0.0.7rc1. Found by "jowr";
+ diagnosed and fixed by "cypherpunks".
+
+ o Major bugfixes (relay, stability, possible security, also in 0.2.6.4-rc):
+ - Fix a bug that could lead to a relay crashing with an assertion
+ failure if a buffer of exactly the wrong layout was passed to
+ buf_pullup() at exactly the wrong time. Fixes bug 15083; bugfix on
+ 0.2.0.10-alpha. Patch from 'cypherpunks'.
+ - Do not assert if the 'data' pointer on a buffer is advanced to the
+ very end of the buffer; log a BUG message instead. Only assert if
+ it is past that point. Fixes bug 15083; bugfix on 0.2.0.10-alpha.
+
+ o Minor features (geoip):
+ - Update geoip to the March 3 2015 Maxmind GeoLite2 Country database.
+ - Update geoip6 to the March 3 2015 Maxmind GeoLite2
+ Country database.
+
Changes in version 0.2.5.10 - 2014-10-24
Tor 0.2.5.10 is the first stable release in the 0.2.5 series.
diff --git a/acinclude.m4 b/acinclude.m4
index 8782a3eeaa..72593881ed 100644
--- a/acinclude.m4
+++ b/acinclude.m4
@@ -137,7 +137,7 @@ dnl
AC_DEFUN([TOR_SEARCH_LIBRARY], [
try$1dir=""
AC_ARG_WITH($1-dir,
- [ --with-$1-dir=PATH Specify path to $1 installation ],
+ AS_HELP_STRING(--with-$1-dir=PATH, [specify path to $1 installation]),
[
if test x$withval != xno ; then
try$1dir="$withval"
diff --git a/changes/.dummy b/changes/.dummy
new file mode 100644
index 0000000000..dd9738feb2
--- /dev/null
+++ b/changes/.dummy
@@ -0,0 +1,37 @@
+This file is here to keep git from removing the changes directory when
+all the changes files have been merged.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+"I'm Nobody! Who are you?
+ Are you--Nobody--too?
+ Then there's a pair of us!
+ Don’t tell! they'd advertise--you know!
+
+ How dreary--to be--Somebody!
+ How public--like a Frog--
+ To tell one's name--the livelong June--
+ To an admiring Bog!"
+ -- Emily Dickinson
+
diff --git a/changes/15188 b/changes/15188
deleted file mode 100644
index 2065b3974c..0000000000
--- a/changes/15188
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (testing):
- - Avoid a side-effect in a tor_assert() in the unit tests. Fixes bug
- 15188; bugfix on 0.1.2.3-alpha. Patch from Tom van der Woerdt.
diff --git a/changes/bug11447 b/changes/bug11447
deleted file mode 100644
index 8cd4f5b467..0000000000
--- a/changes/bug11447
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor features (DoS-resistance):
- - Decrease the amount of reattempts that a hidden service is
- willing to perform when its rendezvous circuits fail. This
- reduces the computational cost for hidden service under heavy
- load. Resolves ticket #11447. \ No newline at end of file
diff --git a/changes/bug11454 b/changes/bug11454
deleted file mode 100644
index b37a7d9728..0000000000
--- a/changes/bug11454
+++ /dev/null
@@ -1,6 +0,0 @@
- o Minor bugfixes (certificate handling):
- - Remove any old authority certificates that have been superseded
- for at least two days. Previously, we would keep superseded
- certificates until they expired, if they were published close
- in time to the certificate that superseded them.
- Fixes bug 11454; bugfix on 0.2.1.8-alpha.
diff --git a/changes/bug11457 b/changes/bug11457
deleted file mode 100644
index cf64c1d10d..0000000000
--- a/changes/bug11457
+++ /dev/null
@@ -1,5 +0,0 @@
- o Minor bugfixes (certificate handling):
- - If an authority operator accidentally makes a signing certificate with
- a future publication time, do not discard its real signing
- certificates. Fixes bug 11457; bugfix on 0.2.0.3-alpha.
-
diff --git a/changes/bug12498 b/changes/bug12498
new file mode 100644
index 0000000000..9f0147cc83
--- /dev/null
+++ b/changes/bug12498
@@ -0,0 +1,29 @@
+ o Major features (Ed25519 identity keys: #12498, Prop220):
+ - All relays now maintain a stronger identity key, using the
+ Ed25519 elliptic curve signature format. This master key is
+ designed so that it can be kept offline. Relays also generate
+ an online signing key, and a set of other Ed25519 keys and certificates.
+ These are all automatically regenerated and rotated as needed.
+ - Directory authorities track which Ed25519 identity keys have been
+ used with which RSA1024 identity keys, and do not allow them to vary
+ freely.
+ - Directory authorities now vote on Ed25519 identity keys along with
+ RSA1024 keys.
+ - Microdescriptors now include ed25519 identity keys.
+
+ o Major features (onion key cross-certification):
+ - Relay descriptors now include signatures of the identity keys using
+ the TAP and ntor onion keys. This allows relays to prove ownership of
+ their own onion keys. Because of this change, microdescriptors no longer
+ need to include RSA identity keys. Implements proposal 228;
+ closes ticket 12499.
+
+ o Code simplification and refactoring:
+ - The link authentication code has been refactored for better testability
+ and reliability. It now uses code generated with the "trunnel"
+ binary encoding generator, to reduce the risk of bugs due to
+ programmer error. Done as part of ticket 12498.
+
+ o Testing:
+ - The link authentication protocol code now has extensive tests.
+ - The relay descriptor signature testing code now has extensive tests.
diff --git a/changes/bug14848_redux b/changes/bug14848_redux
deleted file mode 100644
index c10320fb23..0000000000
--- a/changes/bug14848_redux
+++ /dev/null
@@ -1,5 +0,0 @@
- o Removed code:
- - Remove some lingering dead code that once supported mempools. Mempools
- were disabled by default in 0.2.5, and removed entirely in
- 0.2.6.3-alpha. Closes more of ticket 14848; patch by "cypherpunks".
-
diff --git a/changes/bug14950 b/changes/bug14950
deleted file mode 100644
index 33cea9cb58..0000000000
--- a/changes/bug14950
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor features (logs):
- - Quiet some log messages in the heartbeat and at startup. Closes
- ticket 14950. \ No newline at end of file
diff --git a/changes/bug14980 b/changes/bug14980
deleted file mode 100644
index b873bb009c..0000000000
--- a/changes/bug14980
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (testing):
- - Fix endianness issues in unit test for resolve_my_address() to
- have it pass on big endian systems. Fixes bug 14980; bugfix on
- Tor 0.2.6.3-alpha.
diff --git a/changes/bug14988 b/changes/bug14988
deleted file mode 100644
index 67dc96e443..0000000000
--- a/changes/bug14988
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (compilation):
- - Fix a compilation warning on s390. Fixes bug 14988; bugfix on
- 0.2.5.2-alpha.
-
diff --git a/changes/bug14989 b/changes/bug14989
deleted file mode 100644
index f4432d468b..0000000000
--- a/changes/bug14989
+++ /dev/null
@@ -1,4 +0,0 @@
- o Major bugfixes (Linux seccomp2 sandbox):
- - Pass IPPROTO_TCP rather than 0 to socket(), so that the
- Linux seccomp2 sandbox doesn't fail. Fixes bug 14989;
- bugfix on 0.2.6.3-alpha.
diff --git a/changes/bug15003 b/changes/bug15003
deleted file mode 100644
index 2dcce74dfe..0000000000
--- a/changes/bug15003
+++ /dev/null
@@ -1,3 +0,0 @@
- o Major bugfixes (linux seccomp2 sandbox):
- - Allow AF_UNIX hidden services to be used with the seccomp2 sandbox.
- Fixes bug 15003; bugfix on 0.2.6.3-alpha.
diff --git a/changes/bug15033 b/changes/bug15033
deleted file mode 100644
index 953e6c3d59..0000000000
--- a/changes/bug15033
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (tests):
- - When running the zero-length-keys check, do not use the default
- torrc file. Fixes bug 15033; bugfix on 0.2.6.3-alpha. Reported
- by "reezer".
diff --git a/changes/bug15037 b/changes/bug15037
deleted file mode 100644
index 587d63186e..0000000000
--- a/changes/bug15037
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (testing):
- - When running the new 'make test-stem' target, use the configured
- python binary. Fixes bug 15037; bugfix on 0.2.6.3-alpha. Patch
- from "cypherpunks".
diff --git a/changes/bug15064 b/changes/bug15064
deleted file mode 100644
index e6bd747b1f..0000000000
--- a/changes/bug15064
+++ /dev/null
@@ -1,4 +0,0 @@
- o Major bugfixes (FreeBSD IPFW transparent proxy):
- - Fix address detection with FreeBSD transparent proxies,
- when "TransProxyType ipfw" is in use.
- Fixes bug 15064; bugfix on 0.2.5.4-alpha.
diff --git a/changes/bug15083 b/changes/bug15083
deleted file mode 100644
index 5cc79b5ba1..0000000000
--- a/changes/bug15083
+++ /dev/null
@@ -1,10 +0,0 @@
- o Major bugfixes (relay, stability, possible security):
- - Fix a bug that could lead to a relay crashing with an assertion
- failure if a buffer of exactly the wrong layout was passed
- to buf_pullup() at exactly the wrong time. Fixes bug 15083;
- bugfix on 0.2.0.10-alpha. Patch from 'cypherpunks'.
-
- - Do not assert if the 'data' pointer on a buffer is advanced to the very
- end of the buffer; log a BUG message instead. Only assert if it is
- past that point. Fixes bug 15083; bugfix on 0.2.0.10-alpha.
-
diff --git a/changes/bug15088 b/changes/bug15088
deleted file mode 100644
index 95878bdb39..0000000000
--- a/changes/bug15088
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (Linux seccomp2 sandbox):
- - Upon receiving sighup, do not crash during attempts to call
- wait4. Fixes bug 15088; bugfix on 0.2.5.1-alpha. Patch from
- "sanic".
diff --git a/changes/bug15151 b/changes/bug15151
deleted file mode 100644
index b9c3061554..0000000000
--- a/changes/bug15151
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor bugfixes (compilation):
- - Fix a compilation warning on FreeBSD. Fixes bug 15151; bugfix on
- 0.2.6.2-alpha.
diff --git a/changes/bug15205 b/changes/bug15205
deleted file mode 100644
index 0cb9f3f4bc..0000000000
--- a/changes/bug15205
+++ /dev/null
@@ -1,5 +0,0 @@
- o Major bugfixes (crash, OSX, security):
- - Fix a remote denial-of-service opportunity caused by a bug
- in OSX's _strlcat_chk() function. Fixes bug 15205; bug first
- appeared in OSX 10.9.
-
diff --git a/changes/bug15240 b/changes/bug15240
deleted file mode 100644
index e11f804a12..0000000000
--- a/changes/bug15240
+++ /dev/null
@@ -1,6 +0,0 @@
- o Minor bugfixes (pluggable transports):
- - Initialize the extended OR Port authentication cookie before launching
- pluggable transports. This prevents a race condition that occured when
- server-side pluggable transports would cache the authentication cookie
- before it has been (re)generated. Fixes bug 15240; bugfix on
- 0.2.5.1-alpha.
diff --git a/changes/bug15245 b/changes/bug15245
deleted file mode 100644
index 520a370eeb..0000000000
--- a/changes/bug15245
+++ /dev/null
@@ -1,5 +0,0 @@
- o Major bugfixes:
- - Avoid crashing when making certain configuration option changes
- on clients. Fixes bug 15245; bugfix on 0.2.6.3-alpha. Reported
- by "anonym".
-
diff --git a/changes/bug15436 b/changes/bug15436
deleted file mode 100644
index 4fa44d1e16..0000000000
--- a/changes/bug15436
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (portability):
- - Use the correct datatype in the SipHash-2-4 function to prevent compilers
- from assuming any sort of alignment. Fixes bug 15436; bugfix on
- 0.2.5.3-alpha.
diff --git a/changes/bug15515 b/changes/bug15515
deleted file mode 100644
index dda7c2fcd8..0000000000
--- a/changes/bug15515
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features (DoS-resistance):
- - Make it harder for attackers to overwhelm hidden services with
- introductions, by blocking multiple introduction requests on the
- same circuit. Resolves ticket #15515.
diff --git a/changes/bug15600 b/changes/bug15600
deleted file mode 100644
index ee1d6cfe19..0000000000
--- a/changes/bug15600
+++ /dev/null
@@ -1,5 +0,0 @@
- o Major bugfixes (security, hidden service):
- - Fix an issue that would allow a malicious client to trigger
- an assertion failure and halt a hidden service. Fixes
- bug 15600; bugfix on 0.2.1.6-alpha. Reported by "skruffy".
-
diff --git a/changes/bug15601 b/changes/bug15601
deleted file mode 100644
index 2cc880af7f..0000000000
--- a/changes/bug15601
+++ /dev/null
@@ -1,4 +0,0 @@
- o Major bugfixes (security, hidden service):
- - Fix a bug that could cause a client to crash with an assertion
- failure when parsing a malformed hidden service descriptor.
- Fixes bug 15601; bugfix on 0.2.1.5-alpha. Found by "DonnCha".
diff --git a/changes/bug15823 b/changes/bug15823
deleted file mode 100644
index 987de5d9ac..0000000000
--- a/changes/bug15823
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor bugfixes (hidden service):
- - Fix an out-of-bounds read when parsing invalid INTRODUCE2 cells
- on a client authorized hidden service. Fixes bug 15823; bugfix
- on 0.2.1.6-alpha.
diff --git a/changes/bug15850 b/changes/bug15850
deleted file mode 100644
index 48a7c7bdd7..0000000000
--- a/changes/bug15850
+++ /dev/null
@@ -1,4 +0,0 @@
- o Major bugfix
- - Revert commit that made directory authority assign the HSDir flag to
- relay without a DirPort which is bad because relay can't handle
- BEGIN_DIR cells. Fixes #15850. Bugfix on tor-0.2.6.3-alpha;
diff --git a/changes/bug15881 b/changes/bug15881
new file mode 100644
index 0000000000..5cf561e07f
--- /dev/null
+++ b/changes/bug15881
@@ -0,0 +1,3 @@
+ o Controller fixes:
+ - Add the descriptor ID in each HS_DESC control event. It was missing
+ but specified in control-spec.txt. Fixes ticket 15881.
diff --git a/changes/bug16060 b/changes/bug16060
new file mode 100644
index 0000000000..eb0b50f7ed
--- /dev/null
+++ b/changes/bug16060
@@ -0,0 +1,4 @@
+ o Minor bugfixes (hidden service):
+ - Fix a crash when reloading configuration while at least one
+ configured and one ephemeral hidden service exists. Fixes bug 16060;
+ bugfix on 0.2.7.1-alpha.
diff --git a/changes/bug16115-NULL-getinfo-onions b/changes/bug16115-NULL-getinfo-onions
new file mode 100644
index 0000000000..ec1661e18d
--- /dev/null
+++ b/changes/bug16115-NULL-getinfo-onions
@@ -0,0 +1,4 @@
+ o Minor fixes (threads, comments):
+ - Check for NULL values in getinfo_helper_onions
+ Patch by "teor".
+ Fix on 915c7438a77e in Tor 0.2.7.1-alpha.
diff --git a/changes/bug16115-init-var b/changes/bug16115-init-var
new file mode 100644
index 0000000000..e3e924a3e3
--- /dev/null
+++ b/changes/bug16115-init-var
@@ -0,0 +1,4 @@
+ o Minor fixes (threads, comments):
+ - Always initialise return value in compute_desc_id in rendcommon.c
+ Patch by "teor".
+ Fix on e6a581f126ba, released in 0.2.7.1-alpha.
diff --git a/changes/bug16115-signing-key-NULL-check b/changes/bug16115-signing-key-NULL-check
new file mode 100644
index 0000000000..3d4f05bc28
--- /dev/null
+++ b/changes/bug16115-signing-key-NULL-check
@@ -0,0 +1,6 @@
+ o Minor fixes (threads, comments):
+ - Ensure signing_key is non-NULL before accessing one of its members
+ signing_key can be NULL in ed_key_init_from_file in routerkeys.c.
+ Discovered by clang 3.7 address sanitizer.
+ Patch by "teor".
+ Fix on c03694938ed0, not in any released version of Tor.
diff --git a/changes/bug16115-spawn-comment b/changes/bug16115-spawn-comment
new file mode 100644
index 0000000000..7792564688
--- /dev/null
+++ b/changes/bug16115-spawn-comment
@@ -0,0 +1,6 @@
+ o Minor fixes (threads, comments):
+ - Fix an incorrect comment on spawn_func in compat_pthreads.c.
+ spawn_func calls pthread_create on unix, not fork
+ Patch by "teor".
+ Bugfix on unknown tor version (existing code split out of
+ compat.c into compat_pthreads.c in c2f0d52b7fb9 on 22 Sep 2013).
diff --git a/changes/bug16115-undef-directive-in-macro b/changes/bug16115-undef-directive-in-macro
new file mode 100644
index 0000000000..8031267cdf
--- /dev/null
+++ b/changes/bug16115-undef-directive-in-macro
@@ -0,0 +1,6 @@
+ o Minor fixes (threads, comments):
+ - Remove undefined directive-in-macro in test_util_writepid
+ clang 3.7 complains that using a preprocessor directive inside
+ a macro invocation in test_util_writepid in test_util.c is undefined.
+ Patch by "teor".
+ Fix on 79e85313aa61 on 0.2.7.1-alpha.
diff --git a/changes/bug16115-unused-find-cipher b/changes/bug16115-unused-find-cipher
new file mode 100644
index 0000000000..0f04d6795b
--- /dev/null
+++ b/changes/bug16115-unused-find-cipher
@@ -0,0 +1,7 @@
+ o Minor fixes (threads, comments):
+ - Silence unused variable warnings in find_cipher_by_id
+ Unused variable warnings were still generated under some versions
+ of OpenSSL. Instead, make sure all variables are used under all
+ versions of OpenSSL.
+ Patch by "teor".
+ Fix on 496df21c89d1, not in any released version of tor.
diff --git a/changes/bug16152 b/changes/bug16152
new file mode 100644
index 0000000000..8b93a60715
--- /dev/null
+++ b/changes/bug16152
@@ -0,0 +1,3 @@
+ o Minor bugfixes (systemd):
+ - Fix an accidental formatting error that broke the systemd
+ configuration file. Fixes bug 16152; bugfix on 0.2.7.1-alpha.
diff --git a/changes/bug16228 b/changes/bug16228
new file mode 100644
index 0000000000..bf36cf82ea
--- /dev/null
+++ b/changes/bug16228
@@ -0,0 +1,4 @@
+ o Minor bugfixes (hidden services):
+ - Avoid crashing with a double-free bug when we create an
+ ephemeral hidden service but adding it fails for some reason.
+ Fixes bug 16228; bugfix on 0.2.7.1-alpha.
diff --git a/changes/bug9495_redux b/changes/bug9495_redux
deleted file mode 100644
index 74b0cdf2a8..0000000000
--- a/changes/bug9495_redux
+++ /dev/null
@@ -1,4 +0,0 @@
- o Major bugfixes (portability):
- - Do not crash on startup when running on Solaris. Fixes a bug
- related to our fix for 9495; bugfix on 0.2.6.1-alpha. Reported
- by "ruebezahl".
diff --git a/changes/feature15006 b/changes/feature15006
deleted file mode 100644
index 168a440ba0..0000000000
--- a/changes/feature15006
+++ /dev/null
@@ -1,4 +0,0 @@
- o Minor features (controller):
- - Messages about problems in the bootstrap process now include
- information about the server we were trying to connect to when we
- noticed the problem. Closes ticket 15006.
diff --git a/changes/feature15817-clang-sanitizers b/changes/feature15817-clang-sanitizers
new file mode 100644
index 0000000000..8bdf061c3a
--- /dev/null
+++ b/changes/feature15817-clang-sanitizers
@@ -0,0 +1,7 @@
+ o Minor enhancements (correctness, testing):
+ - Document use of coverity, clang static analyzer, and clang dynamic
+ undefined behavior and address sanitizers in doc/HACKING.
+ Add clang dynamic sanitizer blacklist in
+ contrib/clang/sanitizer_blacklist.txt to exempt known undefined
+ behavior. Include detailed usage instructions in the blacklist.
+ Patch by "teor".
diff --git a/changes/feature16052 b/changes/feature16052
new file mode 100644
index 0000000000..cd09b58867
--- /dev/null
+++ b/changes/feature16052
@@ -0,0 +1,5 @@
+ o Minor features (hidden service):
+ - Add the new options "HiddenServiceMaxStreams" and
+ "HiddenServiceMaxStreamsCloseCircuit" to allow hidden services to limit
+ the maximum number of simultaneous streams per circuit, and optionally
+ tear down the circuit when the limit is exceeded. Part of ticket 16052.
diff --git a/changes/geoip-april2015 b/changes/geoip-april2015
deleted file mode 100644
index 7db38ed797..0000000000
--- a/changes/geoip-april2015
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor features:
- - Update geoip to the April 8 2015 Maxmind GeoLite2 Country database.
-
diff --git a/changes/geoip-march2015 b/changes/geoip-march2015
deleted file mode 100644
index 565781280a..0000000000
--- a/changes/geoip-march2015
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor features:
- - Update geoip to the March 3 2015 Maxmind GeoLite2 Country database.
-
diff --git a/changes/geoip6-april2015 b/changes/geoip6-april2015
deleted file mode 100644
index 241c9119b6..0000000000
--- a/changes/geoip6-april2015
+++ /dev/null
@@ -1,2 +0,0 @@
- o Minor features:
- - Update geoip6 to the April 8 2015 Maxmind GeoLite2 Country database.
diff --git a/changes/geoip6-march2015 b/changes/geoip6-march2015
deleted file mode 100644
index 9a38c65e62..0000000000
--- a/changes/geoip6-march2015
+++ /dev/null
@@ -1,3 +0,0 @@
- o Minor features:
- - Update geoip6 to the March 3 2015 Maxmind GeoLite2 Country database.
-
diff --git a/changes/ticket14487 b/changes/ticket14487
deleted file mode 100644
index 577337ff24..0000000000
--- a/changes/ticket14487
+++ /dev/null
@@ -1,3 +0,0 @@
- o Directory authority IP change:
- - The directory authority Faravahar has a new IP address. Closes
- ticket 14487.
diff --git a/changes/ticket15176 b/changes/ticket15176
deleted file mode 100644
index 6d12723728..0000000000
--- a/changes/ticket15176
+++ /dev/null
@@ -1,3 +0,0 @@
- o Code simplification and refactoring:
- - Refactor main loop to extract the 'loop' part. This makes it easier
- to run Tor under Shadow. Closes ticket 15176.
diff --git a/changes/ticket15212 b/changes/ticket15212
deleted file mode 100644
index 2c41e3865c..0000000000
--- a/changes/ticket15212
+++ /dev/null
@@ -1,6 +0,0 @@
- o Minor features (heartbeat):
-
- - On relays, report how many connections we negotiated using each
- version of the Tor link protocols. This information will let us
- know if removing support for very old versions of the Tor
- protocols is harming the network. Closes ticket 15212.
diff --git a/changes/ticket15358 b/changes/ticket15358
new file mode 100644
index 0000000000..8c85d51007
--- /dev/null
+++ b/changes/ticket15358
@@ -0,0 +1,3 @@
+ o Features (control protocl):
+ - Support network-liveness GETINFO key and NETWORK_LIVENESS events in the
+ control protocol. Resolves ticket #15358.
diff --git a/changes/ticket16034 b/changes/ticket16034
new file mode 100644
index 0000000000..b909946cd4
--- /dev/null
+++ b/changes/ticket16034
@@ -0,0 +1,6 @@
+ o Removed features:
+
+ - Tor no longer supports versions of OpenSSL before 1.0. (If you
+ are on an operating system that has not upgraded to OpenSSL 1.0
+ or later, and you compile Tor from source, you will need to
+ install a more recent OpenSSL to link Tor against.)
diff --git a/changes/ticket16140 b/changes/ticket16140
new file mode 100644
index 0000000000..00c19f1778
--- /dev/null
+++ b/changes/ticket16140
@@ -0,0 +1,6 @@
+ o Removed features:
+
+ - Tor no longer supports copies of OpenSSL that are missing support for
+ Elliptic Curve Cryptography. In particular support for at least one of
+ P256 or P224 is now required, with manual configuration needed if only
+ P224 is available.
diff --git a/configure.ac b/configure.ac
index 1496b0544b..9f21cfedd8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,7 +3,7 @@ dnl Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson
dnl Copyright (c) 2007-2015, The Tor Project, Inc.
dnl See LICENSE for licensing information
-AC_INIT([tor],[0.2.6.9])
+AC_INIT([tor],[0.2.7.1-alpha-dev])
AC_CONFIG_SRCDIR([src/or/main.c])
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE
@@ -26,23 +26,23 @@ CPPFLAGS="$CPPFLAGS -I\${top_srcdir}/src/common"
#XXXX020 We should make these enabled or not, before 0.2.0.x-final
AC_ARG_ENABLE(openbsd-malloc,
- AS_HELP_STRING(--enable-openbsd-malloc, Use malloc code from openbsd. Linux only))
+ AS_HELP_STRING(--enable-openbsd-malloc, [use malloc code from OpenBSD. Linux only]))
AC_ARG_ENABLE(instrument-downloads,
- AS_HELP_STRING(--enable-instrument-downloads, Instrument downloads of directory resources etc.))
+ AS_HELP_STRING(--enable-instrument-downloads, [instrument downloads of directory resources etc.]))
AC_ARG_ENABLE(static-openssl,
- AS_HELP_STRING(--enable-static-openssl, Link against a static openssl library. Requires --with-openssl-dir))
+ AS_HELP_STRING(--enable-static-openssl, [link against a static openssl library. Requires --with-openssl-dir]))
AC_ARG_ENABLE(static-libevent,
- AS_HELP_STRING(--enable-static-libevent, Link against a static libevent library. Requires --with-libevent-dir))
+ AS_HELP_STRING(--enable-static-libevent, [link against a static libevent library. Requires --with-libevent-dir]))
AC_ARG_ENABLE(static-zlib,
- AS_HELP_STRING(--enable-static-zlib, Link against a static zlib library. Requires --with-zlib-dir))
+ AS_HELP_STRING(--enable-static-zlib, [link against a static zlib library. Requires --with-zlib-dir]))
AC_ARG_ENABLE(static-tor,
- AS_HELP_STRING(--enable-static-tor, Create an entirely static Tor binary. Requires --with-openssl-dir and --with-libevent-dir and --with-zlib-dir))
+ AS_HELP_STRING(--enable-static-tor, [create an entirely static Tor binary. Requires --with-openssl-dir and --with-libevent-dir and --with-zlib-dir]))
AC_ARG_ENABLE(unittests,
- AS_HELP_STRING(--disable-unittests, [Don't build unit tests for Tor. Risky!]))
+ AS_HELP_STRING(--disable-unittests, [don't build unit tests for Tor. Risky!]))
AC_ARG_ENABLE(coverage,
- AS_HELP_STRING(--enable-coverage, [Enable coverage support in the unit-test build]))
+ AS_HELP_STRING(--enable-coverage, [enable coverage support in the unit-test build]))
AC_ARG_ENABLE(system-torrc,
- AS_HELP_STRING(--disable-system-torrc, [Don't look for a system-wide torrc file]))
+ AS_HELP_STRING(--disable-system-torrc, [don't look for a system-wide torrc file]))
AM_CONDITIONAL(UNITTESTS_ENABLED, test x$enable_unittests != xno)
AM_CONDITIONAL(COVERAGE_ENABLED, test x$enable_coverage = xyes)
@@ -66,7 +66,7 @@ if test x$enable_instrument_downloads = xyes; then
fi
AC_ARG_ENABLE(transparent,
- AS_HELP_STRING(--disable-transparent, disable transparent proxy support),
+ AS_HELP_STRING(--disable-transparent, [disable transparent proxy support]),
[case "${enableval}" in
yes) transparent=true ;;
no) transparent=false ;;
@@ -74,7 +74,7 @@ AC_ARG_ENABLE(transparent,
esac], [transparent=true])
AC_ARG_ENABLE(asciidoc,
- AS_HELP_STRING(--disable-asciidoc, don't use asciidoc (disables building of manpages)),
+ AS_HELP_STRING(--disable-asciidoc, [don't use asciidoc (disables building of manpages)]),
[case "${enableval}" in
yes) asciidoc=true ;;
no) asciidoc=false ;;
@@ -83,7 +83,7 @@ AC_ARG_ENABLE(asciidoc,
# By default, we're not ready to ship a NAT-PMP aware Tor
AC_ARG_ENABLE(nat-pmp,
- AS_HELP_STRING(--enable-nat-pmp, enable NAT-PMP support),
+ AS_HELP_STRING(--enable-nat-pmp, [enable NAT-PMP support]),
[case "${enableval}" in
yes) natpmp=true ;;
no) natpmp=false ;;
@@ -92,7 +92,7 @@ AC_ARG_ENABLE(nat-pmp,
# By default, we're not ready to ship a UPnP aware Tor
AC_ARG_ENABLE(upnp,
- AS_HELP_STRING(--enable-upnp, enable UPnP support),
+ AS_HELP_STRING(--enable-upnp, [enable UPnP support]),
[case "${enableval}" in
yes) upnp=true ;;
no) upnp=false ;;
@@ -101,7 +101,7 @@ AC_ARG_ENABLE(upnp,
# systemd notify support
AC_ARG_ENABLE(systemd,
- AS_HELP_STRING(--enable-systemd, enable systemd notification support),
+ AS_HELP_STRING(--enable-systemd, [enable systemd notification support]),
[case "${enableval}" in
yes) systemd=true ;;
no) systemd=false ;;
@@ -148,24 +148,24 @@ case $host in
esac
AC_ARG_ENABLE(gcc-warnings,
- AS_HELP_STRING(--enable-gcc-warnings, enable verbose warnings))
+ AS_HELP_STRING(--enable-gcc-warnings, [enable verbose warnings]))
AC_ARG_ENABLE(gcc-warnings-advisory,
AS_HELP_STRING(--enable-gcc-warnings-advisory, [enable verbose warnings, excluding -Werror]))
dnl Others suggest '/gs /safeseh /nxcompat /dynamicbase' for non-gcc on Windows
AC_ARG_ENABLE(gcc-hardening,
- AS_HELP_STRING(--disable-gcc-hardening, disable compiler security checks))
+ AS_HELP_STRING(--disable-gcc-hardening, [disable compiler security checks]))
AC_ARG_ENABLE(expensive-hardening,
- AS_HELP_STRING(--enable-expensive-hardening, enable more expensive compiler hardening; makes Tor slower))
+ AS_HELP_STRING(--enable-expensive-hardening, [enable more expensive compiler hardening; makes Tor slower]))
dnl Linker hardening options
dnl Currently these options are ELF specific - you can't use this with MacOSX
AC_ARG_ENABLE(linker-hardening,
- AS_HELP_STRING(--disable-linker-hardening, disable linker security fixups))
+ AS_HELP_STRING(--disable-linker-hardening, [disable linker security fixups]))
AC_ARG_ENABLE(local-appdata,
- AS_HELP_STRING(--enable-local-appdata, default to host local application data paths on Windows))
+ AS_HELP_STRING(--enable-local-appdata, [default to host local application data paths on Windows]))
if test "$enable_local_appdata" = "yes"; then
AC_DEFINE(ENABLE_LOCAL_APPDATA, 1,
[Defined if we default to host local appdata paths on Windows])
@@ -173,22 +173,22 @@ fi
# Tor2web mode flag
AC_ARG_ENABLE(tor2web-mode,
- AS_HELP_STRING(--enable-tor2web-mode, support tor2web non-anonymous mode),
+ AS_HELP_STRING(--enable-tor2web-mode, [support tor2web non-anonymous mode]),
[if test x$enableval = xyes; then
CFLAGS="$CFLAGS -D ENABLE_TOR2WEB_MODE=1"
fi])
AC_ARG_ENABLE(bufferevents,
- AS_HELP_STRING(--enable-bufferevents, use Libevent's buffered IO.))
+ AS_HELP_STRING(--enable-bufferevents, [use Libevent's buffered IO]))
AC_ARG_ENABLE(tool-name-check,
- AS_HELP_STRING(--disable-tool-name-check, check for sanely named toolchain when cross-compiling))
+ AS_HELP_STRING(--disable-tool-name-check, [check for sanely named toolchain when cross-compiling]))
AC_ARG_ENABLE(seccomp,
- AS_HELP_STRING(--disable-seccomp, do not attempt to use libseccomp))
+ AS_HELP_STRING(--disable-seccomp, [do not attempt to use libseccomp]))
AC_ARG_ENABLE(libscrypt,
- AS_HELP_STRING(--disable-libscrypt, do not attempt to use libscrypt))
+ AS_HELP_STRING(--disable-libscrypt, [do not attempt to use libscrypt]))
dnl check for the correct "ar" when cross-compiling
AN_MAKEVAR([AR], [AC_PROG_AR])
@@ -215,6 +215,8 @@ AC_PROG_CPP
AC_PROG_MAKE_SET
AC_PROG_RANLIB
+AC_PATH_PROG([PERL], [perl])
+
dnl autoconf 2.59 appears not to support AC_PROG_SED
AC_CHECK_PROG([SED],[sed],[sed],[/bin/false])
@@ -281,12 +283,9 @@ if test "$tor_cv_c_c99_designated_init" != "yes"; then
AC_MSG_ERROR([Your compiler doesn't support c99 designated initializers. This is required as of Tor 0.2.6.x])
fi
-AC_PATH_PROG([SHA1SUM], [sha1sum], none)
-AC_PATH_PROG([OPENSSL], [openssl], none)
-
TORUSER=_tor
AC_ARG_WITH(tor-user,
- [ --with-tor-user=NAME Specify username for tor daemon ],
+ AS_HELP_STRING(--with-tor-user=NAME, [specify username for tor daemon]),
[
TORUSER=$withval
]
@@ -295,7 +294,7 @@ AC_SUBST(TORUSER)
TORGROUP=_tor
AC_ARG_WITH(tor-group,
- [ --with-tor-group=NAME Specify group name for tor daemon ],
+ AS_HELP_STRING(--with-tor-group=NAME, [specify group name for tor daemon]),
[
TORGROUP=$withval
]
@@ -439,15 +438,6 @@ AC_SUBST(TOR_LIB_WS32)
AC_SUBST(TOR_LIB_GDI)
AC_SUBST(TOR_LIB_IPHLPAPI)
-dnl We need to do this before we try our disgusting hack below.
-AC_CHECK_HEADERS([sys/types.h])
-
-dnl This is a disgusting hack so we safely include older libevent headers.
-AC_CHECK_TYPE(u_int64_t, unsigned long long)
-AC_CHECK_TYPE(u_int32_t, unsigned long)
-AC_CHECK_TYPE(u_int16_t, unsigned short)
-AC_CHECK_TYPE(u_int8_t, unsigned char)
-
tor_libevent_pkg_redhat="libevent"
tor_libevent_pkg_debian="libevent-dev"
tor_libevent_devpkg_redhat="libevent-devel"
@@ -488,13 +478,10 @@ save_CPPFLAGS="$CPPFLAGS"
LIBS="-levent $STATIC_LIBEVENT_FLAGS $TOR_LIB_WS32 $LIBS"
LDFLAGS="$TOR_LDFLAGS_libevent $LDFLAGS"
CPPFLAGS="$TOR_CPPFLAGS_libevent $CPPFLAGS"
-AC_CHECK_FUNCS([event_get_version \
- event_get_version_number \
- event_get_method \
- event_set_log_callback \
+AC_CHECK_FUNCS([event_get_version_number \
evutil_secure_rng_set_urandom_device_file \
evutil_secure_rng_init \
- event_base_loopexit])
+ ])
AC_CHECK_MEMBERS([struct event.min_heap_idx], , ,
[#include <event.h>
])
@@ -597,7 +584,7 @@ tor_openssl_devpkg_debian="libssl-dev"
ALT_openssl_WITHVAL=""
AC_ARG_WITH(ssl-dir,
- [ --with-ssl-dir=PATH Obsolete alias for --with-openssl-dir ],
+ AS_HELP_STRING(--with-ssl-dir=PATH, [obsolete alias for --with-openssl-dir]),
[
if test "x$withval" != xno && test "x$withval" != "x" ; then
ALT_openssl_WITHVAL="$withval"
@@ -623,10 +610,29 @@ else
fi
AC_SUBST(TOR_OPENSSL_LIBS)
+dnl Now check for particular openssl functions.
+save_LIBS="$LIBS"
+save_LDFLAGS="$LDFLAGS"
+save_CPPFLAGS="$CPPFLAGS"
+LIBS="$TOR_OPENSSL_LIBS $LIBS"
+LDFLAGS="$TOR_LDFLAGS_openssl $LDFLAGS"
+CPPFLAGS="$TOR_CPPFLAGS_openssl $CPPFLAGS"
AC_CHECK_MEMBERS([struct ssl_method_st.get_cipher_by_char], , ,
[#include <openssl/ssl.h>
])
+AC_CHECK_FUNCS([ \
+ SSL_SESSION_get_master_key \
+ SSL_get_server_random \
+ SSL_get_client_ciphers \
+ SSL_get_client_random \
+ SSL_CIPHER_find \
+ TLS_method
+ ])
+LIBS="$save_LIBS"
+LDFLAGS="$save_LDFLAGS"
+CPPFLAGS="$save_CPPFLAGS"
+
dnl ------------------------------------------------------
dnl Where do you live, zlib? And how do we call you?
@@ -1315,7 +1321,7 @@ fi
# Whether we should use the dmalloc memory allocation debugging library.
AC_MSG_CHECKING(whether to use dmalloc (debug memory allocation library))
AC_ARG_WITH(dmalloc,
-[ --with-dmalloc Use debug memory allocation library. ],
+AS_HELP_STRING(--with-dmalloc, [use debug memory allocation library]),
[if [[ "$withval" = "yes" ]]; then
dmalloc=1
AC_MSG_RESULT(yes)
@@ -1333,7 +1339,7 @@ if [[ $dmalloc -eq 1 ]]; then
fi
AC_ARG_WITH(tcmalloc,
-[ --with-tcmalloc Use tcmalloc memory allocation library. ],
+AS_HELP_STRING(--with-tcmalloc, [use tcmalloc memory allocation library]),
[ tcmalloc=yes ], [ tcmalloc=no ])
if test x$tcmalloc = xyes ; then
@@ -1363,7 +1369,7 @@ AC_CHECK_DECLS([mlockall], , , [
# Allow user to specify an alternate syslog facility
AC_ARG_WITH(syslog-facility,
-[ --with-syslog-facility=LOG syslog facility to use (default=LOG_DAEMON)],
+AS_HELP_STRING(--with-syslog-facility=LOG, [syslog facility to use (default=LOG_DAEMON)]),
syslog_facility="$withval", syslog_facility="LOG_DAEMON")
AC_DEFINE_UNQUOTED(LOGFACILITY,$syslog_facility,[name of the syslog facility])
AC_SUBST(LOGFACILITY)
@@ -1661,6 +1667,11 @@ AC_CONFIG_FILES([
contrib/dist/tor.service
src/config/torrc.sample
src/config/torrc.minimal
+ scripts/maint/checkOptionDocs.pl
+ scripts/maint/updateVersions.pl
+ src/test/test_zero_length_keys.sh
+ src/test/test_ntor.sh
+ src/test/test_bt.sh
])
if test x$asciidoc = xtrue && test "$ASCIIDOC" = "none" ; then
@@ -1682,7 +1693,3 @@ if test x$asciidoc = xtrue && test "$ASCIIDOC" = "none" ; then
fi
AC_OUTPUT
-
-if test -x /usr/bin/perl && test -x ./scripts/maint/updateVersions.pl ; then
- ./scripts/maint/updateVersions.pl
-fi
diff --git a/contrib/README b/contrib/README
index 07c6f777d5..3a94bb5016 100644
--- a/contrib/README
+++ b/contrib/README
@@ -11,6 +11,13 @@ add-tor is an old script to manipulate the approved-routers file.
nagios-check-tor-authority-cert is a nagios script to check when Tor
authority certificates are expired or nearly expired.
+clang/ -- Files for use with the clang compiler
+-----------------------------------------------
+
+sanitize_blacklist.txt is used to build Tor with clang's dynamic
+AddressSanitizer and UndefinedBehaviorSanitizer. It contains detailed
+instructions on configuration, build, and testing with clang's sanitizers.
+
client-tools/ -- Tools for use with Tor clients
-----------------------------------------------
diff --git a/contrib/clang/sanitize_blacklist.txt b/contrib/clang/sanitize_blacklist.txt
new file mode 100644
index 0000000000..03d1e70f31
--- /dev/null
+++ b/contrib/clang/sanitize_blacklist.txt
@@ -0,0 +1,92 @@
+# clang sanitizer special case list
+# syntax specified in http://clang.llvm.org/docs/SanitizerSpecialCaseList.html
+# for more info see http://clang.llvm.org/docs/AddressSanitizer.html
+
+# usage:
+# 1. configure tor build:
+# ./configure \
+# CC=clang \
+# CFLAGS="-fsanitize-blacklist=contrib/clang/sanitize_blacklist.txt -fsanitize=undefined -fsanitize=address -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-inline" \
+# LDFLAGS="-fsanitize=address" \
+# --disable-gcc-hardening
+# and any other flags required to build tor on your OS.
+#
+# 2. build tor:
+# make
+#
+# 3. test tor:
+# ASAN_OPTIONS=allow_user_segv_handler=1 make test
+# ASAN_OPTIONS=allow_user_segv_handler=1 make check
+# make test-network # requires chutney
+#
+# 4. the tor binary is now instrumented with clang sanitizers,
+# and can be run just like a standard tor binary
+
+# Compatibility:
+# This blacklist has been tested with clang 3.7's UndefinedBehaviorSanitizer
+# and AddressSanitizer on OS X 10.10 Yosemite, with all tests passing
+# on both x86_64 and i386 (using CC="clang -arch i386")
+# It has not been tested with ThreadSanitizer or MemorySanitizer
+# Success report and patches for other sanitizers or OSs are welcome
+
+# ccache and make don't account for the sanitizer blacklist as a dependency
+# you might need to set CCACHE_DISABLE=1 and/or use make clean to workaround
+
+# Configuration Flags:
+# -fno-sanitize-recover=all
+# causes clang to crash on undefined behavior, rather than printing
+# a warning and continuing (the AddressSanitizer always crashes)
+# -fno-omit-frame-pointer -fno-optimize-sibling-calls -fno-inline
+# make clang backtraces easier to read
+# --disable-gcc-hardening
+# disables warnings about the redefinition of _FORTIFY_SOURCE
+# (it conflicts with the sanitizers)
+
+# Turning the sanitizers off for particular functions:
+# (Unfortunately, exempting functions doesn't work for the blacklisted
+# functions below, and we can't turn the code off because it's essential)
+#
+# #if defined(__has_feature)
+# #if __has_feature(address_sanitizer)
+# /* tell clang AddressSanitizer not to instrument this function */
+# #define NOASAN __attribute__((no_sanitize_address))
+# #define _CLANG_ASAN_
+# #else
+# #define NOASAN
+# #endif
+# #else
+# #define NOASAN
+# #endif
+#
+# /* Telling AddressSanitizer to not instrument a function */
+# void func(void) NOASAN;
+#
+# /* Including or excluding sections of code */
+# #ifdef _CLANG_ASAN_
+# /* code that only runs under address sanitizer */
+# #else
+# /* code that doesn't run under address sanitizer */
+# #endif
+
+# Blacklist Entries:
+
+# test-memwipe.c checks if a freed buffer was properly wiped
+fun:vmemeq
+fun:check_a_buffer
+
+# we need to allow the tor bt handler to catch SIGSEGV
+# otherwise address sanitizer munges the expected output and the test fails
+# we can do this by setting an environmental variable
+# See https://code.google.com/p/address-sanitizer/wiki/Flags
+# ASAN_OPTIONS=allow_user_segv_handler=1
+
+# test_bt_cl.c stores to a NULL pointer to trigger a crash
+fun:crash
+
+# curve25519-donna.c left-shifts 1 bits into and past the sign bit of signed
+# integers. Until #13538 is resolved, we exempt functions that do left shifts.
+# Note that x86_64 uses curve25519-donna-c64.c instead of curve25519-donna.c
+fun:freduce_coefficients
+fun:freduce_degree
+fun:s32_eq
+fun:fcontract
diff --git a/contrib/dist/tor.service.in b/contrib/dist/tor.service.in
index c251158d9a..ae339ff844 100644
--- a/contrib/dist/tor.service.in
+++ b/contrib/dist/tor.service.in
@@ -1,3 +1,9 @@
+# tor.service -- this systemd configuration file for Tor sets up a
+# relatively conservative, hardened Tor service. You may need to
+# edit it if you are making changes to your Tor configuration that it
+# does not allow. Package maintainers: this should be a starting point
+# for your tor.service; it is not the last point.
+
[Unit]
Description = Anonymizing overlay network for TCP
After = syslog.target network.target nss-lookup.target
diff --git a/contrib/win32build/tor-mingw.nsi.in b/contrib/win32build/tor-mingw.nsi.in
index 5c5c1bb70d..928aced871 100644
--- a/contrib/win32build/tor-mingw.nsi.in
+++ b/contrib/win32build/tor-mingw.nsi.in
@@ -8,7 +8,7 @@
!include "LogicLib.nsh"
!include "FileFunc.nsh"
!insertmacro GetParameters
-!define VERSION "0.2.6.9"
+!define VERSION "0.2.7.1-alpha-dev"
!define INSTALLER "tor-${VERSION}-win32.exe"
!define WEBSITE "https://www.torproject.org/"
!define LICENSE "LICENSE"
diff --git a/doc/HACKING b/doc/HACKING
index 5c71b74bd1..d94e0a90d7 100644
--- a/doc/HACKING
+++ b/doc/HACKING
@@ -61,9 +61,10 @@ it's a bugfix, mention what bug it fixes and when the bug was
introduced. To find out which Git tag the change was introduced in,
you can use "git describe --contains <sha1 of commit>".
-If at all possible, try to create this file in the same commit where
-you are making the change. Please give it a distinctive name that no
-other branch will use for the lifetime of your change.
+If at all possible, try to create this file in the same commit where you are
+making the change. Please give it a distinctive name that no other branch will
+use for the lifetime of your change. To verify the format of the changes file,
+you can use "make check-changes".
When we go to make a release, we will concatenate all the entries
in changes to make a draft changelog, and clear the directory. We'll
@@ -114,6 +115,32 @@ valgrind --leak-check=yes --error-limit=no --show-reachable=yes src/or/tor
pass --undef-value-errors=no to valgrind, or rebuild your openssl
with -DPURIFY.)
+Coverity
+~~~~~~~~
+
+Nick regularly runs the coverity static analyzer on the Tor codebase.
+
+The preprocessor define __COVERITY__ is used to work around instances
+where coverity picks up behavior that we wish to permit.
+
+clang Static Analyzer
+~~~~~~~~~~~~~~~~~~~~~
+
+The clang static analyzer can be run on the Tor codebase using Xcode (WIP)
+or a command-line build.
+
+The preprocessor define __clang_analyzer__ is used to work around instances
+where clang picks up behavior that we wish to permit.
+
+clang Runtime Sanitizers
+~~~~~~~~~~~~~~~~
+
+To build the Tor codebase with the clang Address and Undefined Behavior
+sanitizers, see the file contrib/clang/sanitize_blacklist.txt.
+
+Preprocessor workarounds for instances where clang picks up behavior that
+we wish to permit are also documented in the blacklist file.
+
Running lcov for unit test coverage
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -373,7 +400,7 @@ do your own profiling to determine otherwise.
Log conventions
~~~~~~~~~~~~~~~
-https://trac.torproject.org/projects/tor/wiki/doc/TorFAQ#loglevel
+https://www.torproject.org/docs/faq#LogLevel
No error or warning messages should be expected during normal OR or OP
operation.
@@ -541,7 +568,9 @@ a stable release, add it to the ReleaseNotes file too. If we're adding
to a release-0.2.x branch, manually commit the changelogs to the later
git branches too.
-4) Bump the version number in configure.ac and rebuild.
+4) In maint-0.2.x, bump the version number in configure.ac and run
+ scripts/maint/updateVersions.pl to update version numbers in other
+ places, and commit. Then merge maint-0.2.x into release-0.2.x.
5) Make dist, put the tarball up somewhere, and tell #tor about it. Wait
a while to see if anybody has problems building it. Try to get Sebastian
@@ -563,6 +592,12 @@ on dist-master.
8b) Edit "include/versions.wmi" and "Makefile" to note the new version.
9) Email the packagers (cc'ing tor-assistants) that a new tarball is up.
+ The current list of packagers is:
+ {weasel,gk,mikeperry} at torproject dot org
+ {blueness} at gentoo dot org
+ {paul} at invizbox dot io
+ {ondrej.mikle} at gmail dot com
+ {lfleischer} at archlinux dot org
10) Add the version number to Trac. To do this, go to Trac, log in,
select "Admin" near the top of the screen, then select "Versions" from
diff --git a/doc/WritingTests.txt b/doc/WritingTests.txt
new file mode 100644
index 0000000000..b0f8722163
--- /dev/null
+++ b/doc/WritingTests.txt
@@ -0,0 +1,167 @@
+
+Writing tests for Tor: an incomplete guide
+==========================================
+
+Tor uses a variety of testing frameworks and methodologies to try to
+keep from introducing bugs. The major ones are:
+
+ 1. Unit tests written in C and shipped with the Tor distribution.
+
+ 2. Integration tests written in Python and shipped with the Tor
+ distribution.
+
+ 3. Integration tests written in Python and shipped with the Stem
+ library. Some of these use the Tor controller protocol.
+
+ 4. System tests written in Python and SH, and shipped with the
+ Chutney package. These work by running many instances of Tor
+ locally, and sending traffic through them.
+
+ 5. The Shadow network simulator.
+
+How to run these tests
+----------------------
+
+=== The easy version
+
+To run all the tests that come bundled with Tor, run "make check"
+
+To run the Stem tests as well, fetch stem from the git repository,
+set STEM_SOURCE_DIR to the checkout, and run "make test-stem".
+
+To run the Chutney tests as well, fetch chutney from the git repository,
+set CHUTNEY_PATH to the checkout, and run "make test-network".
+
+=== Running particular subtests
+
+XXXX WRITEME
+
+=== Finding test coverage
+
+When you configure Tor with the --enable-coverage option, it should
+build with support for coverage in the unit tests, and in a special
+"tor-cov" binary. If you launch
+
+XXXX "make test-network" doesn't know about "tor-cov"; you don't get
+XXXX coverage from that yet, unless you do "cp src/or/tor-cov
+XXXX src/or/tor" before you run it.
+
+What kinds of test should I write?
+----------------------------------
+
+XXXX writeme.
+
+
+Unit and regression tests: Does this function do what it's supposed to?
+-----------------------------------------------------------------------
+
+Most of Tor's unit tests are made using the "tinytest" testing framework.
+You can see a guide to using it in the tinytest manual at
+
+ https://github.com/nmathewson/tinytest/blob/master/tinytest-manual.md
+
+To add a new test of this kind, either edit an existing C file in src/test/,
+or create a new C file there. Each test is a single function that must
+be indexed in the table at the end of the file. We use the label "done:" as
+a cleanup point for all test functions.
+
+(Make sure you read tinytest-manual.md before proceeding.)
+
+I use the term "unit test" and "regression tests" very sloppily here.
+
+=== A simple example
+
+Here's an example of a test function for a simple function in util.c:
+
+ 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);
+ tt_int_op(pid, OP_EQ, getpid());
+
+ done:
+ tor_free(contents);
+ }
+
+This should look pretty familiar to you if you've read the tinytest
+manual. One thing to note here is that we use the testing-specific
+function "get_fname" to generate a file with respect to a temporary
+directory that the tests use. You don't need to delete the file;
+it will get removed when the tests are done.
+
+Also note our use of OP_EQ instead of == in the tt_int_op() calls.
+We define OP_* macros to use instead of the binary comparison
+operators so that analysis tools can more easily parse our code.
+(Coccinelle really hates to see == used as a macro argument.)
+
+Finally, remember that by convention, all *_free() functions that
+Tor defines are defined to accept NULL harmlessly. Thus, you don't
+need to say "if (contents)" in the cleanup block.
+
+=== Exposing static functions for testing
+
+Sometimes you need to test a function, but you don't want to expose
+it outside its usual module.
+
+To support this, Tor's build system compiles a testing version of
+teach module, with extra identifiers exposed. If you want to
+declare a function as static but available for testing, use the
+macro "STATIC" instead of "static". Then, make sure there's a
+macro-protected declaration of the function in the module's header.
+
+For example, crypto_curve25519.h contains:
+
+#ifdef CRYPTO_CURVE25519_PRIVATE
+STATIC int curve25519_impl(uint8_t *output, const uint8_t *secret,
+ const uint8_t *basepoint);
+#endif
+
+The crypto_curve25519.c file and the test_crypto.c file both define
+CRYPTO_CURVE25519_PRIVATE, so they can see this declaration.
+
+=== Mock functions for testing in isolation
+
+Often we want to test that a function works right, but the function depends
+on other functions whose behavior is hard to observe, or whose
+
+XXXX WRITEME
+
+=== Advanced techniques: Namespaces
+
+
+XXXX write this. danah boyd made us some really awesome stuff here.
+
+
+Integration tests: Calling Tor from the outside
+-----------------------------------------------
+
+XXXX WRITEME
+
+Writing integration tests with Stem
+-----------------------------------
+
+XXXX WRITEME
+
+System testing with Chutney
+---------------------------
+
+XXXX WRITEME
+
+Who knows what evil lurks in the timings of networks? The Shadow knows!
+-----------------------------------------------------------------------
+
+XXXX WRITEME
+
diff --git a/doc/include.am b/doc/include.am
index 30d3e20d83..783aa95c4e 100644
--- a/doc/include.am
+++ b/doc/include.am
@@ -64,14 +64,14 @@ doc/tor-gencert.html.in: doc/tor-gencert.1.txt
doc/tor-resolve.html.in: doc/tor-resolve.1.txt
doc/tor-fw-helper.html.in: doc/tor-fw-helper.1.txt
-# use ../config.status to swap all machine-specific magic strings
+# use config.status to swap all machine-specific magic strings
# in the asciidoc with their replacements.
$(asciidoc_product) :
$(AM_V_GEN)$(MKDIR_P) $(@D)
$(AM_V_at)if test -e $(top_srcdir)/$@.in && ! test -e $@.in ; then \
cp $(top_srcdir)/$@.in $@; \
fi
- $(AM_V_at)./config.status -q --file=$@;
+ $(AM_V_at)$(top_builddir)/config.status -q --file=$@;
doc/tor.html: doc/tor.html.in
doc/tor-gencert.html: doc/tor-gencert.html.in
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index e136bd0f7e..e7c08f5046 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -371,12 +371,6 @@ GENERAL OPTIONS
chosen with their regular weights, multiplied by this number, which
should be 1.0 or less. (Default: 1.0)
-[[DynamicDHGroups]] **DynamicDHGroups** **0**|**1**::
- If this option is set to 1, when running as a server, generate our
- own Diffie-Hellman group instead of using the one from Apache's mod_ssl.
- This option may help circumvent censorship based on static
- Diffie-Hellman parameters. (Default: 0)
-
[[AlternateDirAuthority]] **AlternateDirAuthority** [__nickname__] [**flags**] __address__:__port__ __fingerprint__ +
[[AlternateBridgeAuthority]] **AlternateBridgeAuthority** [__nickname__] [**flags**] __address__:__port__ __ fingerprint__::
@@ -1311,7 +1305,7 @@ The following options are useful only for clients (that is, if
[[DownloadExtraInfo]] **DownloadExtraInfo** **0**|**1**::
If true, Tor downloads and caches "extra-info" documents. These documents
contain information about servers other than the information in their
- regular router descriptors. Tor does not use this information for anything
+ regular server descriptors. Tor does not use this information for anything
itself; to save bandwidth, leave this option turned off. (Default: 0)
[[WarnPlaintextPorts]] **WarnPlaintextPorts** __port__,__port__,__...__::
@@ -1490,8 +1484,8 @@ is non-zero):
[[BridgeRelay]] **BridgeRelay** **0**|**1**::
Sets the relay to act as a "bridge" with respect to relaying connections
from bridge users to the Tor network. It mainly causes Tor to publish a
- server descriptor to the bridge database, rather than publishing a relay
- descriptor to the public directory authorities.
+ server descriptor to the bridge database, rather than
+ to the public directory authorities.
[[ContactInfo]] **ContactInfo** __email_address__::
Administrative contact information for this relay or bridge. This line
@@ -1790,27 +1784,53 @@ is non-zero):
(Default: P256)
[[CellStatistics]] **CellStatistics** **0**|**1**::
- When this option is enabled, Tor writes statistics on the mean time that
- cells spend in circuit queues to disk every 24 hours. (Default: 0)
+ Relays only.
+ When this option is enabled, Tor collects statistics about cell
+ processing (i.e. mean time a cell is spending in a queue, mean
+ number of cells in a queue and mean number of processed cells per
+ circuit) and writes them into disk every 24 hours. Onion router
+ operators may use the statistics for performance monitoring.
+ If ExtraInfoStatistics is enabled, it will published as part of
+ extra-info document. (Default: 0)
[[DirReqStatistics]] **DirReqStatistics** **0**|**1**::
+ Relays and bridges only.
When this option is enabled, a Tor directory writes statistics on the
number and response time of network status requests to disk every 24
- hours. (Default: 1)
+ hours. Enables relay and bridge operators to monitor how much their
+ server is being used by clients to learn about Tor network.
+ If ExtraInfoStatistics is enabled, it will published as part of
+ extra-info document. (Default: 1)
[[EntryStatistics]] **EntryStatistics** **0**|**1**::
+ Relays only.
When this option is enabled, Tor writes statistics on the number of
- directly connecting clients to disk every 24 hours. (Default: 0)
+ directly connecting clients to disk every 24 hours. Enables relay
+ operators to monitor how much inbound traffic that originates from
+ Tor clients passes through their server to go further down the
+ Tor network. If ExtraInfoStatistics is enabled, it will be published
+ as part of extra-info document. (Default: 0)
[[ExitPortStatistics]] **ExitPortStatistics** **0**|**1**::
- When this option is enabled, Tor writes statistics on the number of relayed
- bytes and opened stream per exit port to disk every 24 hours. (Default: 0)
+ Exit relays only.
+ When this option is enabled, Tor writes statistics on the number of
+ relayed bytes and opened stream per exit port to disk every 24 hours.
+ Enables exit relay operators to measure and monitor amounts of traffic
+ that leaves Tor network through their exit node. If ExtraInfoStatistics
+ is enabled, it will be published as part of extra-info document.
+ (Default: 0)
[[ConnDirectionStatistics]] **ConnDirectionStatistics** **0**|**1**::
- When this option is enabled, Tor writes statistics on the bidirectional use
- of connections to disk every 24 hours. (Default: 0)
+ Relays only.
+ When this option is enabled, Tor writes statistics on the amounts of
+ traffic it passes between itself and other relays to disk every 24
+ hours. Enables relay operators to monitor how much their relay is
+ being used as middle node in the circuit. If ExtraInfoStatistics is
+ enabled, it will be published as part of extra-info document.
+ (Default: 0)
[[HiddenServiceStatistics]] **HiddenServiceStatistics** **0**|**1**::
+ Relays only.
When this option is enabled, a Tor relay writes obfuscated
statistics on its role as hidden-service directory, introduction
point, or rendezvous point to disk every 24 hours. If
@@ -1837,6 +1857,13 @@ is non-zero):
this. If this option is set to 0, Tor will try to pick a reasonable
default based on your system's physical memory. (Default: 0)
+[[SigningKeyLifetime]] **SigningKeyLifetime** __N__ **days**|**weeks**|**months**::
+ For how long should each Ed25519 signing key be valid? Tor uses a
+ permanent master identity key that can be kept offline, and periodically
+ generates new "signing" keys that it uses online. This option
+ configures their lifetime.
+ (Default: 30 days)
+
DIRECTORY SERVER OPTIONS
------------------------
@@ -1929,7 +1956,7 @@ on the public Tor network.
[[BridgeAuthoritativeDir]] **BridgeAuthoritativeDir** **0**|**1**::
When this option is set in addition to **AuthoritativeDirectory**, Tor
- accepts and serves router descriptors, but it caches and serves the main
+ accepts and serves server descriptors, but it caches and serves the main
networkstatus documents rather than generating its own. (Default: 0)
[[MinUptimeHidServDirectoryV2]] **MinUptimeHidServDirectoryV2** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**::
@@ -1948,9 +1975,9 @@ on the public Tor network.
in the "params" line of its networkstatus vote.
[[DirAllowPrivateAddresses]] **DirAllowPrivateAddresses** **0**|**1**::
- If set to 1, Tor will accept router descriptors with arbitrary "Address"
+ If set to 1, Tor will accept server descriptors with arbitrary "Address"
elements. Otherwise, if the address is not an IP address or is a private IP
- address, it will reject the router descriptor. (Default: 0)
+ address, it will reject the server descriptor. (Default: 0)
[[AuthDirBadExit]] **AuthDirBadExit** __AddressPattern...__::
Authoritative directories only. A set of address patterns for servers that
@@ -2129,6 +2156,16 @@ The following options are used to configure a hidden service.
not an authorization mechanism; it is instead meant to be a mild
inconvenience to port-scanners.) (Default: 0)
+[[HiddenServiceMaxStreams]] **HiddenServiceMaxStreams** __N__::
+ The maximum number of simultaneous streams (connections) per rendezvous
+ circuit. (Setting this to 0 will allow an unlimited number of simultanous
+ streams.) (Default: 0)
+
+[[HiddenServiceMaxStreamsCloseCircuit]] **HiddenServiceMaxStreamsCloseCircuit** **0**|**1**::
+ If set to 1, then exceeding **HiddenServiceMaxStreams** will cause the
+ offending rendezvous circuit to be torn down, as opposed to stream creation
+ requests that exceed the limit being silently ignored. (Default: 0)
+
[[RendPostPeriod]] **RendPostPeriod** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**::
Every time the specified period elapses, Tor uploads any rendezvous
service descriptors to the directory servers. This information is also
@@ -2212,7 +2249,7 @@ The following options are used for running a testing Tor network.
that **TestingTorNetwork** is set. (Default: 30 minutes)
[[TestingEstimatedDescriptorPropagationTime]] **TestingEstimatedDescriptorPropagationTime** __N__ **minutes**|**hours**::
- Clients try downloading router descriptors from directory caches after this
+ Clients try downloading server descriptors from directory caches after this
time. Changing this requires that **TestingTorNetwork** is set. (Default:
10 minutes)
@@ -2260,7 +2297,7 @@ The following options are used for running a testing Tor network.
this requires that **TestingTorNetwork** is set. (Default: 8)
[[TestingDescriptorMaxDownloadTries]] **TestingDescriptorMaxDownloadTries** __NUM__::
- Try this often to download a router descriptor before giving up.
+ Try this often to download a server descriptor before giving up.
Changing this requires that **TestingTorNetwork** is set. (Default: 8)
[[TestingMicrodescMaxDownloadTries]] **TestingMicrodescMaxDownloadTries** __NUM__::
@@ -2319,6 +2356,23 @@ The following options are used for running a testing Tor network.
authority on a testing network. Overrides the usual default lower bound
of 4 KB. (Default: 0)
+[[TestingLinkCertLifetime]] **TestingLinkCertifetime** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**|**months**::
+ Overrides the default lifetime for the certificates used to authenticate
+ our X509 link cert with our ed25519 signing key.
+ (Default: 2 days)
+
+[[TestingAuthKeyLifetime]] **TestingAuthKeyLifetime** __N__ **seconds**|**minutes**|**hours**|**days**|**weeks**|**months**::
+ Overrides the default lifetime for a signing Ed25519 TLS Link authentication
+ key.
+ (Default: 2 days)
+
+[[TestingLinkKeySlop]] **TestingLinkKeySlop** __N__ **seconds**|**minutes**|**hours**::
+[[TestingAuthKeySlop]] **TestingAuthKeySlop** __N__ **seconds**|**minutes**|**hours**::
+[[TestingSigningKeySlop]] **TestingSigningKeySlop** __N__ **seconds**|**minutes**|**hours**::
+ How early before the official expiration of a an Ed25519 signing key do
+ we replace it and issue a new key?
+ (Default: 3 hours for link and auth; 1 day for signing.)
+
SIGNALS
-------
@@ -2402,7 +2456,7 @@ __DataDirectory__**/state**::
below).
- When the file was last written
- What version of Tor generated the state file
- - A short history of bandwidth usage, as produced in the router
+ - A short history of bandwidth usage, as produced in the server
descriptors.
__DataDirectory__**/bw_accounting**::
@@ -2447,7 +2501,7 @@ __DataDirectory__**/unverified-microdesc-consensus**::
to check yet.
__DataDirectory__**/unparseable-desc**::
- Onion router descriptors that Tor was unable to parse are dumped to this
+ Onion server descriptors that Tor was unable to parse are dumped to this
file. Only used for debugging.
__DataDirectory__**/router-stability**::
diff --git a/scripts/codegen/makedesc.py b/scripts/codegen/makedesc.py
index 833951945b..d4ba21efae 100644
--- a/scripts/codegen/makedesc.py
+++ b/scripts/codegen/makedesc.py
@@ -14,6 +14,18 @@ import binascii
import ctypes
import ctypes.util
import hashlib
+import optparse
+import os
+import re
+import struct
+import time
+import UserDict
+
+import slow_ed25519
+import slownacl_curve25519
+import ed25519_exts_ref
+
+# Pull in the openssl stuff we need.
crypt = ctypes.CDLL(ctypes.util.find_library('crypto'))
BIO_s_mem = crypt.BIO_s_mem
@@ -24,6 +36,15 @@ BIO_new = crypt.BIO_new
BIO_new.argtypes = [ctypes.c_void_p]
BIO_new.restype = ctypes.c_void_p
+crypt.BIO_free.argtypes = [ctypes.c_void_p]
+crypt.BIO_free.restype = ctypes.c_int
+
+crypt.BIO_ctrl.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_long, ctypes.c_void_p ]
+crypt.BIO_ctrl.restype = ctypes.c_long
+
+crypt.PEM_write_bio_RSAPublicKey.argtypes = [ ctypes.c_void_p, ctypes.c_void_p ]
+crypt.PEM_write_bio_RSAPublicKey.restype = ctypes.c_int
+
RSA_generate_key = crypt.RSA_generate_key
RSA_generate_key.argtypes = [ctypes.c_int, ctypes.c_ulong, ctypes.c_void_p, ctypes.c_void_p]
RSA_generate_key.restype = ctypes.c_void_p
@@ -39,6 +60,14 @@ i2d_RSAPublicKey.argtypes = [
]
i2d_RSAPublicKey.restype = ctypes.c_int
+
+def rsa_sign(msg, rsa):
+ buf = ctypes.create_string_buffer(1024)
+ n = RSA_private_encrypt(len(msg), msg, buf, rsa, 1)
+ if n <= 0:
+ raise Exception()
+ return buf.raw[:n]
+
def b64(x):
x = base64.b64encode(x)
res = []
@@ -51,29 +80,188 @@ def bio_extract(bio):
length = crypt.BIO_ctrl(bio, 3, 0, ctypes.byref(buf))
return ctypes.string_at(buf, length)
-def make_key(e=65537):
+def make_rsa_key(e=65537):
rsa = crypt.RSA_generate_key(1024, e, None, None)
bio = BIO_new(BIO_s_mem())
crypt.PEM_write_bio_RSAPublicKey(bio, rsa)
pem = bio_extract(bio).rstrip()
crypt.BIO_free(bio)
-
buf = ctypes.create_string_buffer(1024)
pBuf = ctypes.c_char_p(ctypes.addressof(buf))
n = crypt.i2d_RSAPublicKey(rsa, ctypes.byref(pBuf))
s = buf.raw[:n]
digest = hashlib.sha1(s).digest()
-
return (rsa,pem,digest)
+def makeEdSigningKeyCert(sk_master, pk_master, pk_signing, date,
+ includeSigning=False, certType=1):
+ assert len(pk_signing) == len(pk_master) == 32
+ expiration = struct.pack("!L", date//3600)
+ if includeSigning:
+ extensions = "\x01\x00\x20\x04\x00%s"%(pk_master)
+ else:
+ extensions = "\x00"
+ signed = "\x01%s%s\x01%s%s" % (
+ chr(certType), expiration, pk_signing, extensions)
+ signature = ed25519_exts_ref.signatureWithESK(signed, sk_master, pk_master)
+ assert len(signature) == 64
+ return signed+signature
+
+def objwrap(identifier, body):
+ return ("-----BEGIN {0}-----\n"
+ "{1}"
+ "-----END {0}-----").format(identifier, body)
+
+MAGIC1 = "<<<<<<MAGIC>>>>>>"
+MAGIC2 = "<<<<<!#!#!#XYZZY#!#!#!>>>>>"
+
+class OnDemandKeys(object):
+ def __init__(self, certDate=None):
+ if certDate is None:
+ certDate = time.time() + 86400
+ self.certDate = certDate
+ self.rsa_id = None
+ self.rsa_onion_key = None
+ self.ed_id_sk = None
+ self.ntor_sk = None
+ self.ntor_crosscert = None
+ self.rsa_crosscert_ed = None
+ self.rsa_crosscert_noed = None
+
+ @property
+ def RSA_IDENTITY(self):
+ if self.rsa_id is None:
+ self.rsa_id, self.rsa_ident_pem, self.rsa_id_digest = make_rsa_key()
+
+ return self.rsa_ident_pem
+
+ @property
+ def RSA_ID_DIGEST(self):
+ self.RSA_IDENTITY
+ return self.rsa_id_digest
+
+ @property
+ def RSA_FINGERPRINT_NOSPACE(self):
+ return binascii.b2a_hex(self.RSA_ID_DIGEST).upper()
+
+ @property
+ def RSA_ONION_KEY(self):
+ if self.rsa_onion_key is None:
+ self.rsa_onion_key, self.rsa_onion_pem, _ = make_rsa_key()
+
+ return self.rsa_onion_pem
+
+ @property
+ def RSA_FINGERPRINT(self):
+ hexdigest = self.RSA_FINGERPRINT_NOSPACEK
+ return " ".join(hexdigest[i:i+4] for i in range(0,len(hexdigest),4))
+
+ @property
+ def RSA_SIGNATURE(self):
+ return MAGIC1
+
+ @property
+ def ED_SIGNATURE(self):
+ return MAGIC2
+
+ @property
+ def NTOR_ONION_KEY(self):
+ if self.ntor_sk is None:
+ self.ntor_sk = slownacl_curve25519.Private()
+ self.ntor_pk = self.ntor_sk.get_public()
+ return base64.b64encode(self.ntor_pk.serialize())
+
+ @property
+ def ED_CERT(self):
+ if self.ed_id_sk is None:
+ self.ed_id_sk = ed25519_exts_ref.expandSK(os.urandom(32))
+ self.ed_signing_sk = ed25519_exts_ref.expandSK(os.urandom(32))
+ self.ed_id_pk = ed25519_exts_ref.publickeyFromESK(self.ed_id_sk)
+ self.ed_signing_pk = ed25519_exts_ref.publickeyFromESK(self.ed_signing_sk)
+ self.ed_cert = makeEdSigningKeyCert(self.ed_id_sk, self.ed_id_pk, self.ed_signing_pk, self.certDate, includeSigning=True, certType=4)
+
+ return objwrap('ED25519 CERT', b64(self.ed_cert))
+
+ @property
+ def NTOR_CROSSCERT(self):
+ if self.ntor_crosscert is None:
+ self.ED_CERT
+ self.NTOR_ONION_KEY
+
+ ed_privkey = self.ntor_sk.serialize() + os.urandom(32)
+ ed_pub0 = ed25519_exts_ref.publickeyFromESK(ed_privkey)
+ sign = (ord(ed_pub0[31]) & 255) >> 7
+
+ self.ntor_crosscert = makeEdSigningKeyCert(self.ntor_sk.serialize() + os.urandom(32), ed_pub0, self.ed_id_pk, self.certDate, certType=10)
+ self.ntor_crosscert_sign = sign
+
+ return objwrap('ED25519 CERT', b64(self.ntor_crosscert))
+
+ @property
+ def NTOR_CROSSCERT_SIGN(self):
+ self.NTOR_CROSSCERT
+ return self.ntor_crosscert_sign
+
+ @property
+ def RSA_CROSSCERT_NOED(self):
+ if self.rsa_crosscert_noed is None:
+ self.RSA_ONION_KEY
+ signed = self.RSA_ID_DIGEST
+ self.rsa_crosscert_noed = rsa_sign(signed, self.rsa_onion_key)
+ return objwrap("CROSSCERT",b64(self.rsa_crosscert_noed))
+
+ @property
+ def RSA_CROSSCERT_ED(self):
+ if self.rsa_crosscert_ed is None:
+ self.RSA_ONION_KEY
+ self.ED_CERT
+ signed = self.RSA_ID_DIGEST + self.ed_id_pk
+ self.rsa_crosscert_ed = rsa_sign(signed, self.rsa_onion_key)
+ return objwrap("CROSSCERT",b64(self.rsa_crosscert_ed))
+
+ def sign_desc(self, body):
+ idx = body.rfind("\nrouter-sig-ed25519 ")
+ if idx >= 0:
+ self.ED_CERT
+ signed_part = body[:idx+len("\nrouter-sig-ed25519 ")]
+ signed_part = "Tor router descriptor signature v1" + signed_part
+ digest = hashlib.sha256(signed_part).digest()
+ ed_sig = ed25519_exts_ref.signatureWithESK(digest,
+ self.ed_signing_sk, self.ed_signing_pk)
+
+ body = body.replace(MAGIC2, base64.b64encode(ed_sig).replace("=",""))
+
+ idx = body.rindex("\nrouter-signature")
+ end_of_sig = body.index("\n", idx+1)
+
+ signed_part = body[:end_of_sig+1]
+
+ digest = hashlib.sha1(signed_part).digest()
+ assert len(digest) == 20
+
+ rsasig = rsa_sign(digest, self.rsa_id)
+
+ body = body.replace(MAGIC1, objwrap("SIGNATURE", b64(rsasig)))
+
+ return body
+
+
def signdesc(body, args_out=None):
rsa, ident_pem, id_digest = make_key()
_, onion_pem, _ = make_key()
+ need_ed = '{ED25519-CERT}' in body or '{ED25519-SIGNATURE}' in body
+ if need_ed:
+ sk_master = os.urandom(32)
+ sk_signing = os.urandom(32)
+ pk_master = slow_ed25519.pubkey(sk_master)
+ pk_signing = slow_ed25519.pubkey(sk_signing)
+
hexdigest = binascii.b2a_hex(id_digest).upper()
fingerprint = " ".join(hexdigest[i:i+4] for i in range(0,len(hexdigest),4))
MAGIC = "<<<<<<MAGIC>>>>>>"
+ MORE_MAGIC = "<<<<<!#!#!#XYZZY#!#!#!>>>>>"
args = {
"RSA-IDENTITY" : ident_pem,
"ONION-KEY" : onion_pem,
@@ -81,6 +269,11 @@ def signdesc(body, args_out=None):
"FINGERPRINT-NOSPACE" : hexdigest,
"RSA-SIGNATURE" : MAGIC
}
+ if need_ed:
+ args['ED25519-CERT'] = makeEdSigningKeyCert(
+ sk_master, pk_master, pk_signing)
+ args['ED25519-SIGNATURE'] = MORE_MAGIC
+
if args_out:
args_out.update(args)
body = body.format(**args)
@@ -104,115 +297,55 @@ def signdesc(body, args_out=None):
return body.rstrip()
-def emit_ri(name, body, args_out=None):
- print "const char %s[] ="%name
- body = "\n".join(line.rstrip() for line in body.split("\n"))+"\n"
- b = signdesc(body, args_out)
- for line in b.split("\n"):
- print ' "%s\\n"'%line
+def print_c_string(ident, body):
+ print "static const char %s[] =" % ident
+ for line in body.split("\n"):
+ print ' "%s\\n"' %(line)
print " ;"
+def emit_ri(name, body):
+ info = OnDemandKeys()
+ body = body.format(d=info)
+ body = info.sign_desc(body)
+ print_c_string("EX_RI_%s"%name.upper(), body)
+
def emit_ei(name, body):
- args = { 'NAME' : name }
- emit_ri(name, body, args)
- args['key'] = "\n".join(
- ' "%s\\n"'%line for line in args['RSA-IDENTITY'].split("\n"))
- print """
-const char {NAME}_fp[] = "{FINGERPRINT-NOSPACE}";
-const char {NAME}_key[] =
-{key};""".format(**args)
-
-if 0:
- emit_ri("minimal",
- """\
-router fred 127.0.0.1 9001 0 9002
-signing-key
-{RSA-IDENTITY}
-onion-key
-{ONION-KEY}
-published 2014-10-05 12:00:00
-bandwidth 1000 1000 1000
-reject *:*
-router-signature
-{RSA-SIGNATURE}
-""")
-
-if 0:
- emit_ri("maximal",
- """\
-router fred 127.0.0.1 9001 0 9002
-signing-key
-{RSA-IDENTITY}
-onion-key
-{ONION-KEY}
-published 2014-10-05 12:00:00
-bandwidth 1000 1000 1000
-reject 127.0.0.1:*
-accept *:80
-reject *:*
-ipv6-policy accept 80,100,101
-ntor-onion-key s7rSohmz9SXn8WWh1EefTHIsWePthsEntQi0WL+ScVw
-uptime 1000
-hibernating 0
-unrecognized-keywords are just dandy in this format
-platform Tor 0.2.4.23 on a Banana PC Jr 6000 Series
-contact O.W.Jones
-fingerprint {FINGERPRINT}
-read-history 900 1,2,3,4
-write-history 900 1,2,3,4
-extra-info-digest AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-hidden-service-dir
-allow-single-hop-exits
-family $AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA $BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
-caches-extra-info
-or-address [::1:2:3:4]:9999
-or-address 127.0.0.99:10000
-opt fred is a fine router
-router-signature
-{RSA-SIGNATURE}
-""")
-
-if 0:
- emit_ei("maximal",
-"""\
-extra-info bob {FINGERPRINT-NOSPACE}
-published 2014-10-05 20:07:00
-opt foobarbaz
-read-history 900 1,2,3
-write-history 900 1,2,3
-dirreq-v2-ips 1
-dirreq-v3-ips 100
-dirreq-v3-reqs blahblah
-dirreq-v2-share blahblah
-dirreq-v3-share blahblah
-dirreq-v2-resp djfkdj
-dirreq-v3-resp djfkdj
-dirreq-v2-direct-dl djfkdj
-dirreq-v3-direct-dl djfkdj
-dirreq-v2-tunneled-dl djfkdj
-dirreq-v3-tunneled-dl djfkdj
-dirreq-stats-end foobar
-entry-ips jfsdfds
-entry-stats-end ksdflkjfdkf
-cell-stats-end FOO
-cell-processed-cells FOO
-cell-queued-cells FOO
-cell-time-in-queue FOO
-cell-circuits-per-decile FOO
-exit-stats-end FOO
-exit-kibibytes-written FOO
-exit-kibibytes-read FOO
-exit-streams-opened FOO
-router-signature
-{RSA-SIGNATURE}
-""")
-
-if 0:
- emit_ei("minimal",
-"""\
-extra-info bob {FINGERPRINT-NOSPACE}
-published 2014-10-05 20:07:00
-router-signature
-{RSA-SIGNATURE}
-""")
+ info = OnDemandKeys()
+ body = body.format(d=info)
+ body = info.sign_desc(body)
+ print_c_string("EX_EI_%s"%name.upper(), body)
+
+ print 'const char EX_EI_{NAME}_FP[] = "{d.RSA_FINGERPRINT_NOSPACE}";'.format(
+ d=info, NAME=name.upper())
+ print_c_string("EX_EI_%s_KEY"%name.upper(), info.RSA_IDENTITY)
+
+def analyze(s):
+ fields = {}
+ while s.startswith(":::"):
+ first,s=s.split("\n", 1)
+ m = re.match(r'^:::(\w+)=(.*)',first)
+ if not m:
+ raise ValueError(first)
+ k,v = m.groups()
+ fields[k] = v
+ return fields, s
+
+def process_file(s):
+ fields, s = analyze(s)
+ try:
+ name = fields['name']
+ tp = fields['type']
+ except KeyError:
+ raise ValueError("missing required field")
+
+ if tp == 'ei':
+ emit_ei(name, s)
+ elif tp == 'ri':
+ emit_ri(name, s)
+ else:
+ raise ValueError("unrecognized type")
+if __name__ == '__main__':
+ import sys
+ for fn in sys.argv[1:]:
+ process_file(open(fn).read())
diff --git a/scripts/codegen/run_trunnel.sh b/scripts/codegen/run_trunnel.sh
index 5f694ce6c9..d2669931e9 100755
--- a/scripts/codegen/run_trunnel.sh
+++ b/scripts/codegen/run_trunnel.sh
@@ -5,7 +5,7 @@ if test "x$TRUNNEL_PATH" != "x"; then
export PYTHONPATH
fi
-python -m trunnel --require-version=1.2 ./src/trunnel/*.trunnel
+python -m trunnel --require-version=1.4 ./src/trunnel/*.trunnel
-python -m trunnel --require-version=1.2 --write-c-files --target-dir=./src/ext/trunnel/
+python -m trunnel --require-version=1.4 --write-c-files --target-dir=./src/ext/trunnel/
diff --git a/scripts/maint/checkOptionDocs.pl b/scripts/maint/checkOptionDocs.pl.in
index 94307c6cef..1f53adf099 100755..100644
--- a/scripts/maint/checkOptionDocs.pl
+++ b/scripts/maint/checkOptionDocs.pl.in
@@ -7,7 +7,7 @@ my %torrcSampleOptions = ();
my %manPageOptions = ();
# Load the canonical list as actually accepted by Tor.
-open(F, "./src/or/tor --list-torrc-options |") or die;
+open(F, "@abs_top_builddir@/src/or/tor --list-torrc-options |") or die;
while (<F>) {
next if m!\[notice\] Tor v0\.!;
if (m!^([A-Za-z0-9_]+)!) {
@@ -34,12 +34,12 @@ sub loadTorrc {
0;
}
-loadTorrc("./src/config/torrc.sample.in", \%torrcSampleOptions);
+loadTorrc("@abs_top_srcdir@/src/config/torrc.sample.in", \%torrcSampleOptions);
# Try to figure out what's in the man page.
my $considerNextLine = 0;
-open(F, "./doc/tor.1.txt") or die;
+open(F, "@abs_top_srcdir@/doc/tor.1.txt") or die;
while (<F>) {
if (m!^(?:\[\[([A-za-z0-9_]+)\]\] *)?\*\*([A-Za-z0-9_]+)\*\*!) {
$manPageOptions{$2} = 1;
@@ -67,5 +67,3 @@ subtractHashes("Orphaned in torrc.sample.in", \%torrcSampleOptions, \%options);
subtractHashes("Not in man page", \%options, \%manPageOptions);
subtractHashes("Orphaned in man page", \%manPageOptions, \%options);
-
-
diff --git a/scripts/maint/format_changelog.py b/scripts/maint/format_changelog.py
index d1b4a3dff3..a557fcaf40 100755
--- a/scripts/maint/format_changelog.py
+++ b/scripts/maint/format_changelog.py
@@ -59,7 +59,7 @@ def generate_wrapping(words, divisions):
w = words[last:i]
last = i
line = " ".join(w).replace("\xff ","-").replace("\xff","-")
- lines.append(line)
+ lines.append(line.strip())
return lines
def wrapping_quality(words, divisions, width1, width2):
diff --git a/scripts/maint/lintChanges.py b/scripts/maint/lintChanges.py
index 69963aea28..c2fc01d2bf 100755
--- a/scripts/maint/lintChanges.py
+++ b/scripts/maint/lintChanges.py
@@ -1,19 +1,22 @@
#!/usr/bin/python
+from __future__ import print_function
+from __future__ import with_statement
import sys
import re
-
+import os
def lintfile(fname):
have_warned = []
+
def warn(s):
if not have_warned:
have_warned.append(1)
- print fname,":"
- print "\t",s
+ print("{}:".format(fname))
+ print("\t{}".format(s))
- m = re.search(r'(\d{3,})', fname)
+ m = re.search(r'(\d{3,})', os.path.basename(fname))
if m:
bugnum = m.group(1)
else:
@@ -23,12 +26,12 @@ def lintfile(fname):
contents = f.read()
if bugnum and bugnum not in contents:
- warn("bug number %s does not appear"%bugnum)
+ warn("bug number {} does not appear".format(bugnum))
lines = contents.split("\n")
isBug = ("bug" in lines[0] or "fix" in lines[0])
- if not re.match(r'^ +o (.*)', contents):
+ if not re.match(r'^[ ]{2}o (.*)', contents):
warn("header not in format expected")
contents = " ".join(contents.split())
@@ -44,11 +47,12 @@ def lintfile(fname):
if re.search(r'[bB]ug (\d+)', contents):
if not re.search(r'[Bb]ugfix on ', contents):
warn("bugfix does not say 'bugfix on X.Y.Z'")
- elif not re.search('[fF]ixes ([a-z ]*)bug (\d+); bugfix on ', contents):
+ elif not re.search('[fF]ixes ([a-z ]*)bug (\d+); bugfix on ',
+ contents):
warn("bugfix incant is not semicoloned")
-if __name__=='__main__':
+if __name__ == '__main__':
for fname in sys.argv[1:]:
if fname.endswith("~"):
continue
diff --git a/scripts/maint/sortChanges.py b/scripts/maint/sortChanges.py
index ad28c79d9d..7e25cefd53 100755
--- a/scripts/maint/sortChanges.py
+++ b/scripts/maint/sortChanges.py
@@ -5,8 +5,6 @@
"""This script sorts a bunch of changes files listed on its command
line into roughly the order in which they should appear in the
changelog.
-
- TODO: collation support.
"""
import re
@@ -19,7 +17,7 @@ def fetch(fn):
return s
def score(s,fname=None):
- m = re.match(r'^ +o (.*)', s)
+ m = re.match(r'^ +o ([^\n]*)\n(.*)', s, re.M|re.S)
if not m:
print >>sys.stderr, "Can't score %r from %s"%(s,fname)
lw = m.group(1).lower()
@@ -38,12 +36,47 @@ def score(s,fname=None):
else:
score = 100
- return (score, lw, s)
+ return (score, lw, m.group(1), m.group(2))
+
+def splitChanges(s):
+ this_entry = []
+ for line in s.split("\n"):
+ if line.strip() == "":
+ continue
+ if re.match(r" +o ", line):
+ if len(this_entry) > 2:
+ yield "".join(this_entry)
+ curHeader = line
+ this_entry = [ curHeader, "\n" ]
+ continue
+ elif re.match(r" +- ", line):
+ if len(this_entry) > 2:
+ yield "".join(this_entry)
+ this_entry = [ curHeader, "\n" ]
+
+ this_entry.append(line)
+ this_entry.append("\n")
+ if len(this_entry) > 2:
+ yield "".join(this_entry)
-changes = [ score(fetch(fn),fn) for fn in sys.argv[1:] if not fn.endswith('~') ]
+
+changes = []
+
+for fn in sys.argv[1:]:
+ if fn.endswith('~'):
+ continue
+ for change in splitChanges(fetch(fn)):
+ changes.append(score(change,fn))
changes.sort()
-for _, _, s in changes:
- print s
+last_lw = "this is not a header"
+for _, lw, header, rest in changes:
+ if lw == last_lw:
+ print rest,
+ else:
+ print
+ print " o",header
+ print rest,
+ last_lw = lw
diff --git a/scripts/maint/updateVersions.pl b/scripts/maint/updateVersions.pl.in
index 15c83b80a7..65c51a1f2d 100755
--- a/scripts/maint/updateVersions.pl
+++ b/scripts/maint/updateVersions.pl.in
@@ -1,8 +1,8 @@
#!/usr/bin/perl -w
-$CONFIGURE_IN = './configure.ac';
-$ORCONFIG_H = './src/win32/orconfig.h';
-$TOR_NSI = './contrib/win32build/tor-mingw.nsi.in';
+$CONFIGURE_IN = '@abs_top_srcdir@/configure.ac';
+$ORCONFIG_H = '@abs_top_srcdir@/src/win32/orconfig.h';
+$TOR_NSI = '@abs_top_srcdir@/contrib/win32build/tor-mingw.nsi.in';
$quiet = 1;
diff --git a/scripts/test/scan-build.sh b/scripts/test/scan-build.sh
index 623b227fe4..2375ab6107 100644
--- a/scripts/test/scan-build.sh
+++ b/scripts/test/scan-build.sh
@@ -25,15 +25,13 @@ CHECKERS="\
-enable-checker security.insecureAPI.strcpy
"
-/opt/clang-3.4/bin/scan-build/scan-build \
+scan-build \
$CHECKERS \
- --use-analyzer=/opt/clang-3.4/bin/clang \
./configure
-/opt/clang-3.4/bin/scan-build/scan-build \
+scan-build \
$CHECKERS \
- --use-analyzer=/opt/clang-3.4/bin/clang \
- make -j2
+ make -j2 -k
# Haven't tried this yet.
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 218c7bea1e..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;
}
@@ -405,7 +400,11 @@ crypto_global_init(int useAccel, const char *accelName, const char *accelDir)
void
crypto_thread_cleanup(void)
{
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+ ERR_remove_thread_state(NULL);
+#else
ERR_remove_state(0);
+#endif
}
/** used by tortls.c: wrap an RSA* in a crypto_pk_t. */
@@ -830,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);
@@ -856,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. */
@@ -997,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)
{
@@ -1069,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;
@@ -1084,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;
@@ -1298,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) {
@@ -1397,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>.
@@ -1724,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;
@@ -1732,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)
@@ -1768,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 */
@@ -2005,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
@@ -2079,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();
}
}
@@ -2134,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;
@@ -2419,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)
@@ -2497,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];
@@ -2505,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) {
@@ -2562,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)
{
@@ -2624,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;
@@ -2671,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
@@ -2745,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;
@@ -2835,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;
@@ -2857,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;
@@ -2895,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
@@ -3119,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
@@ -3140,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_);
@@ -3157,7 +3162,11 @@ int
crypto_global_cleanup(void)
{
EVP_cleanup();
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+ ERR_remove_thread_state(NULL);
+#else
ERR_remove_state(0);
+#endif
ERR_free_strings();
if (dh_param_p)
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/tortls.c b/src/common/tortls.c
index 97a82bf6e1..bd0eaffa27 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,15 +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;
-
/** 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;
@@ -137,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;
@@ -210,16 +205,6 @@ struct tor_tls_t {
void *callback_arg;
};
-#ifdef V2_HANDSHAKE_CLIENT
-/** An array of fake SSL_CIPHER objects that we use in order to trick OpenSSL
- * in client mode into advertising the ciphers we want. See
- * rectify_client_ciphers() for details. */
-static SSL_CIPHER *CLIENT_CIPHER_DUMMIES = NULL;
-/** A stack of SSL_CIPHER objects, some real, some fake.
- * See rectify_client_ciphers() for details. */
-static STACK_OF(SSL_CIPHER) *CLIENT_CIPHER_STACK = NULL;
-#endif
-
/** The ex_data index in which we store a pointer to an SSL object's
* corresponding tor_tls_t object. */
static int tor_tls_object_ex_data_index = -1;
@@ -479,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
@@ -588,12 +521,6 @@ tor_tls_free_all(void)
client_tls_context = NULL;
tor_tls_context_decref(ctx);
}
-#ifdef V2_HANDSHAKE_CLIENT
- if (CLIENT_CIPHER_DUMMIES)
- tor_free(CLIENT_CIPHER_DUMMIES);
- if (CLIENT_CIPHER_STACK)
- sk_SSL_CIPHER_free(CLIENT_CIPHER_STACK);
-#endif
}
/** We need to give OpenSSL a callback to verify certificates. This is
@@ -659,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);
@@ -783,13 +711,12 @@ const char UNRESTRICTED_SERVER_CIPHER_LIST[] =
* (SSL3_TXT_RSA_NULL_SHA). If you do this, you won't be able to communicate
* with any of the "real" Tors, though. */
-#ifdef V2_HANDSHAKE_CLIENT
#define CIPHER(id, name) name ":"
#define XCIPHER(id, name)
/** 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"
@@ -797,31 +724,9 @@ static const char CLIENT_CIPHER_LIST[] =
#undef CIPHER
#undef XCIPHER
-/** Holds a cipher that we want to advertise, and its 2-byte ID. */
-typedef struct cipher_info_t { unsigned id; const char *name; } cipher_info_t;
-/** A list of all the ciphers that clients should advertise, including items
- * that OpenSSL might not know about. */
-static const cipher_info_t CLIENT_CIPHER_INFO_LIST[] = {
-#define CIPHER(id, name) { id, name },
-#define XCIPHER(id, name) { id, #name },
-#include "./ciphers.inc"
-#undef CIPHER
-#undef XCIPHER
-};
-
-/** The length of CLIENT_CIPHER_INFO_LIST and CLIENT_CIPHER_DUMMIES. */
-static const int N_CLIENT_CIPHERS = ARRAY_LENGTH(CLIENT_CIPHER_INFO_LIST);
-#endif
-
-#ifndef V2_HANDSHAKE_CLIENT
-#undef CLIENT_CIPHER_LIST
-#define CLIENT_CIPHER_LIST (TLS1_TXT_DHE_RSA_WITH_AES_128_SHA ":" \
- SSL3_TXT_EDH_RSA_DES_192_CBC3_SHA)
-#endif
-
/** 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;
@@ -833,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;
@@ -850,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");
@@ -880,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();
@@ -902,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;
@@ -921,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);
@@ -934,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;
@@ -944,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;
}
@@ -957,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);
@@ -973,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)
@@ -1003,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);
@@ -1023,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;
@@ -1053,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();
@@ -1265,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);
@@ -1284,8 +1189,13 @@ tor_tls_context_new(crypto_pk_t *identity, unsigned int key_lifetime,
#endif
/* Tell OpenSSL to use TLS 1.0 or later but not SSL2 or SSL3. */
+#ifdef HAVE_TLS_METHOD
+ if (!(result->ctx = SSL_CTX_new(TLS_method())))
+ goto error;
+#else
if (!(result->ctx = SSL_CTX_new(SSLv23_method())))
goto error;
+#endif
SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(result->ctx, SSL_OP_NO_SSLv3);
@@ -1326,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);
@@ -1354,16 +1246,21 @@ 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);
}
+#ifdef SSL_OP_NO_COMPRESSION
+ SSL_CTX_set_options(result->ctx, SSL_OP_NO_COMPRESSION);
+#endif
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
#ifndef OPENSSL_NO_COMP
/* Don't actually allow compression; it uses ram and time, but the data
* we transmit is all encrypted anyway. */
if (result->ctx->comp_methods)
result->ctx->comp_methods = NULL;
#endif
+#endif
#ifdef SSL_MODE_RELEASE_BUFFERS
SSL_CTX_set_mode(result->ctx, SSL_MODE_RELEASE_BUFFERS);
#endif
@@ -1398,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;
@@ -1415,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 */
@@ -1511,10 +1403,23 @@ static int v2_cipher_list_pruned = 0;
/** Return 0 if <b>m</b> does not support the cipher with ID <b>cipher</b>;
* return 1 if it does support it, or if we have no way to tell. */
static int
-find_cipher_by_id(const SSL_METHOD *m, uint16_t cipher)
+find_cipher_by_id(const SSL *ssl, const SSL_METHOD *m, uint16_t cipher)
{
const SSL_CIPHER *c;
-#ifdef HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR
+#ifdef HAVE_SSL_CIPHER_FIND
+ {
+ unsigned char cipherid[3];
+ tor_assert(ssl);
+ set_uint16(cipherid, htons(cipher));
+ cipherid[2] = 0; /* If ssl23_get_cipher_by_char finds no cipher starting
+ * with a two-byte 'cipherid', it may look for a v2
+ * cipher with the appropriate 3 bytes. */
+ c = SSL_CIPHER_find((SSL*)ssl, cipherid);
+ if (c)
+ tor_assert((SSL_CIPHER_get_id(c) & 0xffff) == cipher);
+ return c != NULL;
+ }
+#elif defined(HAVE_STRUCT_SSL_METHOD_ST_GET_CIPHER_BY_CHAR)
if (m && m->get_cipher_by_char) {
unsigned char cipherid[3];
set_uint16(cipherid, htons(cipher));
@@ -1527,6 +1432,7 @@ find_cipher_by_id(const SSL_METHOD *m, uint16_t cipher)
return c != NULL;
} else
#endif
+#if OPENSSL_VERSION_NUMBER < OPENSSL_V_SERIES(1,1,0)
if (m && m->get_cipher && m->num_ciphers) {
/* It would seem that some of the "let's-clean-up-openssl" forks have
* removed the get_cipher_by_char function. Okay, so now you get a
@@ -1540,23 +1446,30 @@ find_cipher_by_id(const SSL_METHOD *m, uint16_t cipher)
}
}
return 0;
- } else {
- return 1; /* No way to search */
}
+#endif
+ (void) ssl;
+ (void) m;
+ (void) cipher;
+ return 1; /* No way to search */
}
/** Remove from v2_cipher_list every cipher that we don't support, so that
* comparing v2_cipher_list to a client's cipher list will give a sensible
* result. */
static void
-prune_v2_cipher_list(void)
+prune_v2_cipher_list(const SSL *ssl)
{
uint16_t *inp, *outp;
+#ifdef HAVE_TLS_METHOD
+ const SSL_METHOD *m = TLS_method();
+#else
const SSL_METHOD *m = SSLv23_method();
+#endif
inp = outp = v2_cipher_list;
while (*inp) {
- if (find_cipher_by_id(m, *inp)) {
+ if (find_cipher_by_id(ssl, m, *inp)) {
*outp++ = *inp++;
} else {
inp++;
@@ -1578,7 +1491,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
int i, res;
tor_tls_t *tor_tls;
if (PREDICT_UNLIKELY(!v2_cipher_list_pruned))
- prune_v2_cipher_list();
+ prune_v2_cipher_list(ssl);
tor_tls = tor_tls_get_by_ssl(ssl);
if (tor_tls && tor_tls->client_cipher_list_type)
@@ -1612,7 +1525,7 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
const uint16_t *v2_cipher = v2_cipher_list;
for (i = 0; i < sk_SSL_CIPHER_num(peer_ciphers); ++i) {
SSL_CIPHER *cipher = sk_SSL_CIPHER_value(peer_ciphers, i);
- uint16_t id = cipher->id & 0xffff;
+ uint16_t id = SSL_CIPHER_get_id(cipher) & 0xffff;
if (id == 0x00ff) /* extended renegotiation indicator. */
continue;
if (!id || id != *v2_cipher) {
@@ -1656,13 +1569,19 @@ tor_tls_classify_client_ciphers(const SSL *ssl,
static int
tor_tls_client_is_using_v2_ciphers(const SSL *ssl)
{
+ STACK_OF(SSL_CIPHER) *ciphers;
+#ifdef HAVE_SSL_GET_CLIENT_CIPHERS
+ ciphers = SSL_get_ciphers(ssl);
+#else
SSL_SESSION *session;
if (!(session = SSL_get_session((SSL *)ssl))) {
log_info(LD_NET, "No session on TLS?");
return CIPHERS_ERR;
}
+ ciphers = session->ciphers;
+#endif
- return tor_tls_classify_client_ciphers(ssl, session->ciphers) >= CIPHERS_V2;
+ return tor_tls_classify_client_ciphers(ssl, ciphers) >= CIPHERS_V2;
}
/** Invoked when we're accepting a connection on <b>ssl</b>, and the connection
@@ -1675,14 +1594,17 @@ static void
tor_tls_server_info_callback(const SSL *ssl, int type, int val)
{
tor_tls_t *tls;
+ int ssl_state;
(void) val;
tor_tls_debug_state_callback(ssl, type, val);
if (type != SSL_CB_ACCEPT_LOOP)
return;
- if ((ssl->state != SSL3_ST_SW_SRVR_HELLO_A) &&
- (ssl->state != SSL3_ST_SW_SRVR_HELLO_B))
+
+ ssl_state = SSL_state(ssl);
+ if ((ssl_state != SSL3_ST_SW_SRVR_HELLO_A) &&
+ (ssl_state != SSL3_ST_SW_SRVR_HELLO_B))
return;
tls = tor_tls_get_by_ssl(ssl);
@@ -1713,10 +1635,6 @@ tor_tls_server_info_callback(const SSL *ssl, int type, int val)
if (tls) {
tls->wasV2Handshake = 1;
-#ifdef USE_BUFFEREVENTS
- 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!");
}
@@ -1724,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.
*
@@ -1762,128 +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
-
-/** Explain which ciphers we're missing. */
-static void
-log_unsupported_ciphers(smartlist_t *unsupported)
-{
- char *joined;
-
- log_notice(LD_NET, "We weren't able to find support for all of the "
- "TLS ciphersuites that we wanted to advertise. This won't "
- "hurt security, but it might make your Tor (if run as a client) "
- "more easy for censors to block.");
-
- if (SSLeay() < 0x10000000L) {
- log_notice(LD_NET, "To correct this, use a more recent OpenSSL, "
- "built without disabling any secure ciphers or features.");
- } else {
- log_notice(LD_NET, "To correct this, use a version of OpenSSL "
- "built with none of its ciphers disabled.");
- }
-
- joined = smartlist_join_strings(unsupported, ":", 0, NULL);
- log_info(LD_NET, "The unsupported ciphers were: %s", joined);
- tor_free(joined);
-}
-
-/** Replace *<b>ciphers</b> with a new list of SSL ciphersuites: specifically,
- * a list designed to mimic a common web browser. We might not be able to do
- * that if OpenSSL doesn't support all the ciphers we want. Some of the
- * ciphers in the list won't actually be implemented by OpenSSL: that's okay
- * so long as the server doesn't select them.
- *
- * [If the server <b>does</b> select a bogus cipher, we won't crash or
- * anything; we'll just fail later when we try to look up the cipher in
- * ssl->cipher_list_by_id.]
- */
-static void
-rectify_client_ciphers(STACK_OF(SSL_CIPHER) **ciphers)
-{
-#ifdef V2_HANDSHAKE_CLIENT
- if (PREDICT_UNLIKELY(!CLIENT_CIPHER_STACK)) {
- /* We need to set CLIENT_CIPHER_STACK to an array of the ciphers
- * we want to use/advertise. */
- int i = 0, j = 0;
- smartlist_t *unsupported = smartlist_new();
-
- /* First, create a dummy SSL_CIPHER for every cipher. */
- CLIENT_CIPHER_DUMMIES =
- tor_malloc_zero(sizeof(SSL_CIPHER)*N_CLIENT_CIPHERS);
- for (i=0; i < N_CLIENT_CIPHERS; ++i) {
- CLIENT_CIPHER_DUMMIES[i].valid = 1;
- /* The "3<<24" here signifies that the cipher is supposed to work with
- * SSL3 and TLS1. */
- CLIENT_CIPHER_DUMMIES[i].id = CLIENT_CIPHER_INFO_LIST[i].id | (3<<24);
- CLIENT_CIPHER_DUMMIES[i].name = CLIENT_CIPHER_INFO_LIST[i].name;
- }
-
- CLIENT_CIPHER_STACK = sk_SSL_CIPHER_new_null();
- tor_assert(CLIENT_CIPHER_STACK);
-
- log_debug(LD_NET, "List was: %s", CLIENT_CIPHER_LIST);
- for (j = 0; j < sk_SSL_CIPHER_num(*ciphers); ++j) {
- SSL_CIPHER *cipher = sk_SSL_CIPHER_value(*ciphers, j);
- log_debug(LD_NET, "Cipher %d: %lx %s", j, cipher->id, cipher->name);
- }
-
- /* Then copy as many ciphers as we can from the good list, inserting
- * dummies as needed. Let j be an index into list of ciphers we have
- * (*ciphers) and let i be an index into the ciphers we want
- * (CLIENT_INFO_CIPHER_LIST). We are building a list of ciphers in
- * CLIENT_CIPHER_STACK.
- */
- for (i = j = 0; i < N_CLIENT_CIPHERS; ) {
- SSL_CIPHER *cipher = NULL;
- if (j < sk_SSL_CIPHER_num(*ciphers))
- cipher = sk_SSL_CIPHER_value(*ciphers, j);
- if (cipher && ((cipher->id >> 24) & 0xff) != 3) {
- /* Skip over non-v3 ciphers entirely. (This should no longer be
- * needed, thanks to saying !SSLv2 above.) */
- log_debug(LD_NET, "Skipping v%d cipher %s",
- (int)((cipher->id>>24) & 0xff),
- cipher->name);
- ++j;
- } else if (cipher &&
- (cipher->id & 0xffff) == CLIENT_CIPHER_INFO_LIST[i].id) {
- /* "cipher" is the cipher we expect. Put it on the list. */
- log_debug(LD_NET, "Found cipher %s", cipher->name);
- sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, cipher);
- ++j;
- ++i;
- } else if (!strcmp(CLIENT_CIPHER_DUMMIES[i].name,
- "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA")) {
- /* We found bogus cipher 0xfeff, which OpenSSL doesn't support and
- * never has. For this one, we need a dummy. */
- log_debug(LD_NET, "Inserting fake %s", CLIENT_CIPHER_DUMMIES[i].name);
- sk_SSL_CIPHER_push(CLIENT_CIPHER_STACK, &CLIENT_CIPHER_DUMMIES[i]);
- ++i;
- } else {
- /* OpenSSL doesn't have this one. */
- log_debug(LD_NET, "Completely omitting unsupported cipher %s",
- CLIENT_CIPHER_INFO_LIST[i].name);
- smartlist_add(unsupported, (char*) CLIENT_CIPHER_INFO_LIST[i].name);
- ++i;
- }
- }
-
- if (smartlist_len(unsupported))
- log_unsupported_ciphers(unsupported);
-
- smartlist_free(unsupported);
- }
-
- sk_SSL_CIPHER_free(*ciphers);
- *ciphers = sk_SSL_CIPHER_dup(CLIENT_CIPHER_STACK);
- tor_assert(*ciphers);
-
-#else
- (void)ciphers;
-#endif
-}
/** Create a new TLS object from a file descriptor, and a flag to
* determine whether it is functioning as a server.
@@ -1924,8 +1719,6 @@ tor_tls_new(int sock, int isServer)
tor_free(result);
goto err;
}
- if (!isServer)
- rectify_client_ciphers(&result->ssl->cipher_list);
result->socket = sock;
bio = BIO_new_socket(sock, BIO_NOCLOSE);
if (! bio) {
@@ -2018,13 +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. */
- if (use_unsafe_renegotiation_flag) {
- tls->ssl->s3->flags |= SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
- }
- 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
@@ -2034,21 +1822,19 @@ tor_tls_unblock_renegotiation(tor_tls_t *tls)
void
tor_tls_block_renegotiation(tor_tls_t *tls)
{
+#ifdef SUPPORT_UNSAFE_RENEGOTIATION_FLAG
tls->ssl->s3->flags &= ~SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION;
+#else
+ (void) tls;
+#endif
}
/** Assert that the flags that allow legacy renegotiation are still set */
void
tor_tls_assert_renegotiation_unblocked(tor_tls_t *tls)
{
- if (use_unsafe_renegotiation_flag) {
- tor_assert(0 != (tls->ssl->s3->flags &
- SSL3_FLAGS_ALLOW_UNSAFE_LEGACY_RENEGOTIATION));
- }
- 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
@@ -2091,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);
@@ -2179,7 +1965,7 @@ tor_tls_handshake(tor_tls_t *tls)
tor_assert(tls->ssl);
tor_assert(tls->state == TOR_TLS_ST_HANDSHAKE);
check_no_tls_errors();
- oldstate = tls->ssl->state;
+ oldstate = SSL_state(tls->ssl);
if (tls->isServer) {
log_debug(LD_HANDSHAKE, "About to call SSL_accept on %p (%s)", tls,
SSL_state_string_long(tls->ssl));
@@ -2189,7 +1975,7 @@ tor_tls_handshake(tor_tls_t *tls)
SSL_state_string_long(tls->ssl));
r = SSL_connect(tls->ssl);
}
- if (oldstate != tls->ssl->state)
+ if (oldstate != SSL_state(tls->ssl))
log_debug(LD_HANDSHAKE, "After call, %p was in state %s",
tls, SSL_state_string_long(tls->ssl));
/* We need to call this here and not earlier, since OpenSSL has a penchant
@@ -2224,8 +2010,7 @@ tor_tls_finish_handshake(tor_tls_t *tls)
if (tls->isServer) {
SSL_set_info_callback(tls->ssl, NULL);
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, always_accept_verify_cb);
- /* There doesn't seem to be a clear OpenSSL API to clear mode flags. */
- tls->ssl->mode &= ~SSL_MODE_NO_AUTO_CHAIN;
+ SSL_clear_mode(tls->ssl, SSL_MODE_NO_AUTO_CHAIN);
#ifdef V2_HANDSHAKE_SERVER
if (tor_tls_client_is_using_v2_ciphers(tls->ssl)) {
/* This check is redundant, but back when we did it in the callback,
@@ -2394,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. */
@@ -2816,33 +2601,107 @@ tor_tls_server_got_renegotiate(tor_tls_t *tls)
return tls->got_renegotiate;
}
+#ifndef HAVE_SSL_GET_CLIENT_RANDOM
+static size_t
+SSL_get_client_random(SSL *s, uint8_t *out, size_t len)
+{
+ if (len == 0)
+ return SSL3_RANDOM_SIZE;
+ tor_assert(len == SSL3_RANDOM_SIZE);
+ tor_assert(s->s3);
+ memcpy(out, s->s3->client_random, len);
+ return len;
+}
+#endif
+
+#ifndef HAVE_SSL_GET_SERVER_RANDOM
+static size_t
+SSL_get_server_random(SSL *s, uint8_t *out, size_t len)
+{
+ if (len == 0)
+ return SSL3_RANDOM_SIZE;
+ tor_assert(len == SSL3_RANDOM_SIZE);
+ tor_assert(s->s3);
+ memcpy(out, s->s3->server_random, len);
+ return len;
+}
+#endif
+
+#ifndef HAVE_SSL_SESSION_GET_MASTER_KEY
+static size_t
+SSL_SESSION_get_master_key(SSL_SESSION *s, uint8_t *out, size_t len)
+{
+ tor_assert(s);
+ if (len == 0)
+ return s->master_key_length;
+ tor_assert(len == (size_t)s->master_key_length);
+ tor_assert(out);
+ memcpy(out, s->master_key, len);
+ return len;
+}
+#endif
+
/** Set the DIGEST256_LEN buffer at <b>secrets_out</b> to the value used in
* 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"
- char buf[128];
+ uint8_t buf[128];
size_t len;
+
tor_assert(tls);
- tor_assert(tls->ssl);
- tor_assert(tls->ssl->s3);
- tor_assert(tls->ssl->session);
+
+ SSL *const ssl = tls->ssl;
+ SSL_SESSION *const session = SSL_get_session(ssl);
+
+ tor_assert(ssl);
+ tor_assert(session);
+
+ const size_t server_random_len = SSL_get_server_random(ssl, NULL, 0);
+ const size_t client_random_len = SSL_get_client_random(ssl, NULL, 0);
+ const size_t master_key_len = SSL_SESSION_get_master_key(session, NULL, 0);
+
+ tor_assert(server_random_len);
+ tor_assert(client_random_len);
+ tor_assert(master_key_len);
+
+ len = client_random_len + server_random_len + strlen(TLSSECRET_MAGIC) + 1;
+ tor_assert(len <= sizeof(buf));
+
+ {
+ size_t r = SSL_get_client_random(ssl, buf, client_random_len);
+ tor_assert(r == client_random_len);
+ }
+ {
+ size_t r = SSL_get_server_random(ssl,
+ buf+client_random_len,
+ server_random_len);
+ tor_assert(r == server_random_len);
+ }
+ uint8_t *master_key = tor_malloc_zero(master_key_len);
+ {
+ size_t r = SSL_SESSION_get_master_key(session, master_key, master_key_len);
+ tor_assert(r == master_key_len);
+ }
+
+ uint8_t *nextbuf = buf + client_random_len + server_random_len;
+ memcpy(nextbuf, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1);
+
/*
The value is an HMAC, using the TLS master key as the HMAC key, of
client_random | server_random | TLSSECRET_MAGIC
*/
- memcpy(buf + 0, tls->ssl->s3->client_random, 32);
- memcpy(buf + 32, tls->ssl->s3->server_random, 32);
- memcpy(buf + 64, TLSSECRET_MAGIC, strlen(TLSSECRET_MAGIC) + 1);
- len = 64 + strlen(TLSSECRET_MAGIC) + 1;
crypto_hmac_sha256((char*)secrets_out,
- (char*)tls->ssl->session->master_key,
- tls->ssl->session->master_key_length,
- buf, len);
+ (char*)master_key,
+ master_key_len,
+ (char*)buf, len);
memwipe(buf, 0, sizeof(buf));
+ memwipe(master_key, 0, master_key_len);
+ tor_free(master_key);
+
return 0;
}
@@ -2850,12 +2709,23 @@ tor_tls_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out)
* Set *<b>rbuf_capacity</b> to the amount of storage allocated for the read
* buffer and *<b>rbuf_bytes</b> to the amount actually used.
* Set *<b>wbuf_capacity</b> to the amount of storage allocated for the write
- * buffer and *<b>wbuf_bytes</b> to the amount actually used. */
-void
+ * buffer and *<b>wbuf_bytes</b> to the amount actually used.
+ *
+ * Return 0 on success, -1 on failure.*/
+int
tor_tls_get_buffer_sizes(tor_tls_t *tls,
size_t *rbuf_capacity, size_t *rbuf_bytes,
size_t *wbuf_capacity, size_t *wbuf_bytes)
{
+#if OPENSSL_VERSION_NUMBER >= OPENSSL_V_SERIES(1,1,0)
+ (void)tls;
+ (void)rbuf_capacity;
+ (void)rbuf_bytes;
+ (void)wbuf_capacity;
+ (void)wbuf_bytes;
+
+ return -1;
+#else
if (tls->ssl->s3->rbuf.buf)
*rbuf_capacity = tls->ssl->s3->rbuf.len;
else
@@ -2866,6 +2736,8 @@ tor_tls_get_buffer_sizes(tor_tls_t *tls,
*wbuf_capacity = 0;
*rbuf_bytes = tls->ssl->s3->rbuf.left;
*wbuf_bytes = tls->ssl->s3->wbuf.left;
+ return 0;
+#endif
}
#ifdef USE_BUFFEREVENTS
@@ -2940,3 +2812,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 f8c6d5913b..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);
@@ -92,7 +92,7 @@ size_t tor_tls_get_forced_write_size(tor_tls_t *tls);
void tor_tls_get_n_raw_bytes(tor_tls_t *tls,
size_t *n_read, size_t *n_written);
-void tor_tls_get_buffer_sizes(tor_tls_t *tls,
+int tor_tls_get_buffer_sizes(tor_tls_t *tls,
size_t *rbuf_capacity, size_t *rbuf_bytes,
size_t *wbuf_capacity, size_t *wbuf_bytes);
@@ -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..28c70ad22c 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 {
@@ -1832,6 +1843,12 @@ circuit_get_open_circ_or_launch(entry_connection_t *conn,
tor_assert(conn);
tor_assert(circp);
+ if (ENTRY_TO_CONN(conn)->state != AP_CONN_STATE_CIRCUIT_WAIT) {
+ connection_t *c = ENTRY_TO_CONN(conn);
+ log_err(LD_BUG, "Connection state mismatch: wanted "
+ "AP_CONN_STATE_CIRCUIT_WAIT, but got %d (%s)",
+ c->state, conn_state_to_string(c->type, c->state));
+ }
tor_assert(ENTRY_TO_CONN(conn)->state == AP_CONN_STATE_CIRCUIT_WAIT);
check_exit_policy =
conn->socks_request->command == SOCKS_COMMAND_CONNECT &&
@@ -2149,7 +2166,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 721ee20d27..bd17629210 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 65bfafba6c..ed38ba2259 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);
@@ -1931,6 +1995,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:
@@ -2753,6 +2827,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 e53922218d..17177b0df5 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.");
@@ -2315,12 +2367,13 @@ dumpstats(int severity)
if (conn->type == CONN_TYPE_OR) {
or_connection_t *or_conn = TO_OR_CONN(conn);
if (or_conn->tls) {
- tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len,
- &wbuf_cap, &wbuf_len);
- tor_log(severity, LD_GENERAL,
- "Conn %d: %d/%d bytes used on OpenSSL read buffer; "
- "%d/%d bytes used on write buffer.",
- i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap);
+ if (tor_tls_get_buffer_sizes(or_conn->tls, &rbuf_cap, &rbuf_len,
+ &wbuf_cap, &wbuf_len) == 0) {
+ tor_log(severity, LD_GENERAL,
+ "Conn %d: %d/%d bytes used on OpenSSL read buffer; "
+ "%d/%d bytes used on write buffer.",
+ i, (int)rbuf_len, (int)rbuf_cap, (int)wbuf_len, (int)wbuf_cap);
+ }
}
}
}
@@ -2484,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;
@@ -2675,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) {
@@ -2732,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();
@@ -2803,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")) {
@@ -2810,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;
}
@@ -3112,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/networkstatus.c b/src/or/networkstatus.c
index da110fdff6..9de1f88aaf 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -856,8 +856,8 @@ update_consensus_networkstatus_fetch_time_impl(time_t now, int flav)
dl_interval = interval/2;
}
} else {
- /* We're an ordinary client or a bridge. Give all the caches enough
- * time to download the consensus. */
+ /* We're an ordinary client, a bridge, or a hidden service.
+ * Give all the caches enough time to download the consensus. */
start = (time_t)(c->fresh_until + (interval*3)/4);
/* But download the next one well before this one is expired. */
dl_interval = ((c->valid_until - start) * 7 )/ 8;
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_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 9b2e84d3e6..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.9"
+#define VERSION "0.2.7.1-alpha-dev"