From 3ca5fe81e33ab7848c848b683bffe12e743398f3 Mon Sep 17 00:00:00 2001 From: Karsten Loesing Date: Wed, 26 Feb 2014 10:44:55 +0100 Subject: [PATCH 1/4] Write hashed bridge fingerprint to logs and to disk. Implements #10884. --- changes/bug10884 | 5 +++ doc/tor.1.txt | 4 ++ src/common/crypto.c | 22 ++++++++++ src/common/crypto.h | 1 + src/or/main.c | 2 + src/or/router.c | 100 +++++++++++++++++++++++++++++--------------- 6 files changed, 101 insertions(+), 33 deletions(-) create mode 100644 changes/bug10884 diff --git a/changes/bug10884 b/changes/bug10884 new file mode 100644 index 0000000000..815d893de3 --- /dev/null +++ b/changes/bug10884 @@ -0,0 +1,5 @@ + o Minor features: + - Bridges write the SHA1 digest of their identity key fingerprint to + notice-level logs and to hashed-fingerprint, so that bridge + operators can look up their bridge in Globe and similar tools. + diff --git a/doc/tor.1.txt b/doc/tor.1.txt index f2960af29d..dc67889353 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -2305,6 +2305,10 @@ __DataDirectory__**/keys/***:: __DataDirectory__**/fingerprint**:: Only used by servers. Holds the fingerprint of the server's identity key. +__DataDirectory__**/hashed-fingerprint**:: + Only used by bridges. Holds the hashed fingerprint of the bridge's + identity key. (That is, the hash of the hash of the identity key.) + __DataDirectory__**/approved-routers**:: Only for naming authoritative directory servers (see **NamingAuthoritativeDirectory**). This file lists nickname to identity diff --git a/src/common/crypto.c b/src/common/crypto.c index 49dc55a3e3..80d835131b 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1374,6 +1374,28 @@ crypto_pk_get_fingerprint(crypto_pk_t *pk, char *fp_out, int add_space) return 0; } +/** Given a private or public key pk, put a hashed fingerprint of + * the public key into fp_out (must have at least FINGERPRINT_LEN+1 + * bytes of space). Return 0 on success, -1 on failure. + * + * Hashed fingerprints are computed as the SHA1 digest of the SHA1 digest + * of the ASN.1 encoding of the public key, converted to hexadecimal, in + * upper case. + */ +int +crypto_pk_get_hashed_fingerprint(crypto_pk_t *pk, char *fp_out) +{ + char digest[DIGEST_LEN], hashed_digest[DIGEST_LEN]; + if (crypto_pk_get_digest(pk, digest)) { + return -1; + } + if (crypto_digest(hashed_digest, digest, DIGEST_LEN)) { + return -1; + } + base16_encode(fp_out, FINGERPRINT_LEN + 1, hashed_digest, DIGEST_LEN); + return 0; +} + /* symmetric crypto */ /** Return a pointer to the key set for the cipher in env. diff --git a/src/common/crypto.h b/src/common/crypto.h index 3666d5f9a3..4f0f1c10c3 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -182,6 +182,7 @@ crypto_pk_t *crypto_pk_asn1_decode(const char *str, size_t len); int crypto_pk_get_digest(crypto_pk_t *pk, char *digest_out); 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); /* symmetric crypto */ const char *crypto_cipher_get_key(crypto_cipher_t *env); diff --git a/src/or/main.c b/src/or/main.c index 5404e962cd..b0529cde8a 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -2764,6 +2764,8 @@ sandbox_init_filter(void) get_datadir_fname2("keys", "secret_id_key.tmp"), 1, get_datadir_fname("fingerprint"), 1, get_datadir_fname("fingerprint.tmp"), 1, + get_datadir_fname("hashed-fingerprint"), 1, + get_datadir_fname("hashed-fingerprint.tmp"), 1, get_datadir_fname("cached-consensus"), 1, get_datadir_fname("cached-consensus.tmp"), 1, "/etc/resolv.conf", 0, diff --git a/src/or/router.c b/src/or/router.c index c22f4c3671..b7d96ead2a 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -684,6 +684,68 @@ router_initialize_tls_context(void) (unsigned int)lifetime); } +/** Compute fingerprint (or hashed fingerprint if hashed is 1) and write + * it to 'fingerprint' (or 'hashed-fingerprint'). Return 0 on success, or + * -1 if Tor should die, + */ +static int +router_write_fingerprint(int hashed) +{ + char *keydir, *cp; + const char *fname = hashed ? "hashed-fingerprint" : + "fingerprint"; + char fingerprint[FINGERPRINT_LEN+1]; + const or_options_t *options = get_options(); + /*nicknamefp\n\0 */ + char fingerprint_line[MAX_NICKNAME_LEN+FINGERPRINT_LEN+3]; + keydir = get_datadir_fname(fname); + log_info(LD_GENERAL,"Dumping %sfingerprint to \"%s\"...", + hashed ? "hashed " : "", keydir); + if (!hashed) { + if (crypto_pk_get_fingerprint(get_server_identity_key(), + fingerprint, 0) < 0) { + log_err(LD_GENERAL,"Error computing fingerprint"); + tor_free(keydir); + return -1; + } + } else { + if (crypto_pk_get_hashed_fingerprint(get_server_identity_key(), + fingerprint) < 0) { + log_err(LD_GENERAL,"Error computing hashed fingerprint"); + tor_free(keydir); + return -1; + } + } + tor_assert(strlen(options->Nickname) <= MAX_NICKNAME_LEN); + if (tor_snprintf(fingerprint_line, sizeof(fingerprint_line), + "%s %s\n",options->Nickname, fingerprint) < 0) { + log_err(LD_GENERAL,"Error writing %sfingerprint line", + hashed ? "hashed " : ""); + tor_free(keydir); + return -1; + } + /* Check whether we need to write the (hashed-)fingerprint file. */ + cp = NULL; + if (file_status(keydir) == FN_FILE) + cp = read_file_to_str(keydir, 0, NULL); + if (!cp || strcmp(cp, fingerprint_line)) { + if (write_str_to_file(keydir, fingerprint_line, 0)) { + log_err(LD_FS, "Error writing %sfingerprint line to file", + hashed ? "hashed " : ""); + tor_free(keydir); + tor_free(cp); + return -1; + } + } + tor_free(cp); + tor_free(keydir); + + log_notice(LD_GENERAL, "Your Tor %s identity key fingerprint is '%s %s'", + hashed ? "bridge's hashed" : "server's", options->Nickname, + fingerprint); + return 0; +} + /** Initialize all OR private keys, and the TLS context, as necessary. * On OPs, this only initializes the tls context. Return 0 on success, * or -1 if Tor should die. @@ -692,14 +754,10 @@ int init_keys(void) { char *keydir; - char fingerprint[FINGERPRINT_LEN+1]; - /*nicknamefp\n\0 */ - char fingerprint_line[MAX_NICKNAME_LEN+FINGERPRINT_LEN+3]; const char *mydesc; crypto_pk_t *prkey; char digest[DIGEST_LEN]; char v3_digest[DIGEST_LEN]; - char *cp; const or_options_t *options = get_options(); dirinfo_type_t type; time_t now = time(NULL); @@ -889,40 +947,16 @@ init_keys(void) } } - /* 5. Dump fingerprint to 'fingerprint' */ - keydir = get_datadir_fname("fingerprint"); - log_info(LD_GENERAL,"Dumping fingerprint to \"%s\"...",keydir); - if (crypto_pk_get_fingerprint(get_server_identity_key(), - fingerprint, 0) < 0) { - log_err(LD_GENERAL,"Error computing fingerprint"); - tor_free(keydir); + /* 5. Dump fingerprint and possibly hashed fingerprint to files. */ + if (router_write_fingerprint(0)) { + log_err(LD_FS, "Error writing fingerprint to file"); return -1; } - tor_assert(strlen(options->Nickname) <= MAX_NICKNAME_LEN); - if (tor_snprintf(fingerprint_line, sizeof(fingerprint_line), - "%s %s\n",options->Nickname, fingerprint) < 0) { - log_err(LD_GENERAL,"Error writing fingerprint line"); - tor_free(keydir); + if (!public_server_mode(options) && router_write_fingerprint(1)) { + log_err(LD_FS, "Error writing hashed fingerprint to file"); return -1; } - /* Check whether we need to write the fingerprint file. */ - cp = NULL; - if (file_status(keydir) == FN_FILE) - cp = read_file_to_str(keydir, 0, NULL); - if (!cp || strcmp(cp, fingerprint_line)) { - if (write_str_to_file(keydir, fingerprint_line, 0)) { - log_err(LD_FS, "Error writing fingerprint line to file"); - tor_free(keydir); - tor_free(cp); - return -1; - } - } - tor_free(cp); - tor_free(keydir); - log_notice(LD_GENERAL, - "Your Tor server's identity key fingerprint is '%s %s'", - options->Nickname, fingerprint); if (!authdir_mode(options)) return 0; /* 6. [authdirserver only] load approved-routers file */ From 25c0435aa577400a024c1514d545037b73f5a0ea Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 26 Feb 2014 10:52:20 -0500 Subject: [PATCH 2/4] Tighten router_write_fingerprint impl --- src/or/router.c | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/or/router.c b/src/or/router.c index b7d96ead2a..2b58de8ff1 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -691,13 +691,14 @@ router_initialize_tls_context(void) static int router_write_fingerprint(int hashed) { - char *keydir, *cp; + char *keydir = NULL, *cp = NULL; const char *fname = hashed ? "hashed-fingerprint" : "fingerprint"; char fingerprint[FINGERPRINT_LEN+1]; const or_options_t *options = get_options(); - /*nicknamefp\n\0 */ - char fingerprint_line[MAX_NICKNAME_LEN+FINGERPRINT_LEN+3]; + char *fingerprint_line = NULL; + int result = -1; + keydir = get_datadir_fname(fname); log_info(LD_GENERAL,"Dumping %sfingerprint to \"%s\"...", hashed ? "hashed " : "", keydir); @@ -705,45 +706,39 @@ router_write_fingerprint(int hashed) if (crypto_pk_get_fingerprint(get_server_identity_key(), fingerprint, 0) < 0) { log_err(LD_GENERAL,"Error computing fingerprint"); - tor_free(keydir); - return -1; + goto done; } } else { if (crypto_pk_get_hashed_fingerprint(get_server_identity_key(), fingerprint) < 0) { log_err(LD_GENERAL,"Error computing hashed fingerprint"); - tor_free(keydir); - return -1; + goto done; } } - tor_assert(strlen(options->Nickname) <= MAX_NICKNAME_LEN); - if (tor_snprintf(fingerprint_line, sizeof(fingerprint_line), - "%s %s\n",options->Nickname, fingerprint) < 0) { - log_err(LD_GENERAL,"Error writing %sfingerprint line", - hashed ? "hashed " : ""); - tor_free(keydir); - return -1; - } + + tor_asprintf(&fingerprint_line, "%s %s\n", options->Nickname, fingerprint); + /* Check whether we need to write the (hashed-)fingerprint file. */ - cp = NULL; - if (file_status(keydir) == FN_FILE) - cp = read_file_to_str(keydir, 0, NULL); + + cp = read_file_to_str(keydir, RFTS_IGNORE_MISSING, NULL); if (!cp || strcmp(cp, fingerprint_line)) { if (write_str_to_file(keydir, fingerprint_line, 0)) { log_err(LD_FS, "Error writing %sfingerprint line to file", hashed ? "hashed " : ""); - tor_free(keydir); - tor_free(cp); - return -1; + goto done; } } - tor_free(cp); - tor_free(keydir); log_notice(LD_GENERAL, "Your Tor %s identity key fingerprint is '%s %s'", hashed ? "bridge's hashed" : "server's", options->Nickname, fingerprint); - return 0; + + result = 0; + done: + tor_free(cp); + tor_free(keydir); + tor_free(fingerprint_line); + return result; } /** Initialize all OR private keys, and the TLS context, as necessary. From 0be9e6099b4ca5597459ce1a9561812a8e7343e0 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 26 Feb 2014 11:15:43 -0500 Subject: [PATCH 3/4] Unit tests for pk fingerprint functions --- src/test/test_crypto.c | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 00b8693b18..1fda334760 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -509,6 +509,56 @@ test_crypto_pk(void) tor_free(encoded); } +static void +test_crypto_pk_fingerprints(void *arg) +{ + crypto_pk_t *pk = NULL; + char encoded[512]; + char d[DIGEST_LEN], d2[DIGEST_LEN]; + char fingerprint[FINGERPRINT_LEN+1]; + int n; + unsigned i; + char *mem_op_hex_tmp=NULL; + + (void)arg; + + pk = pk_generate(1); + tt_assert(pk); + n = crypto_pk_asn1_encode(pk, encoded, sizeof(encoded)); + tt_int_op(n, >, 0); + tt_int_op(n, >, 128); + tt_int_op(n, <, 256); + + /* Is digest as expected? */ + crypto_digest(d, encoded, n); + tt_int_op(0, ==, crypto_pk_get_digest(pk, d2)); + test_memeq(d, d2, DIGEST_LEN); + + /* Is fingerprint right? */ + tt_int_op(0, ==, crypto_pk_get_fingerprint(pk, fingerprint, 0)); + tt_int_op(strlen(fingerprint), ==, DIGEST_LEN * 2); + test_memeq_hex(d, fingerprint); + + /* Are spaces right? */ + tt_int_op(0, ==, crypto_pk_get_fingerprint(pk, fingerprint, 1)); + for (i = 4; i < strlen(fingerprint); i += 5) { + tt_int_op(fingerprint[i], ==, ' '); + } + tor_strstrip(fingerprint, " "); + tt_int_op(strlen(fingerprint), ==, DIGEST_LEN * 2); + test_memeq_hex(d, fingerprint); + + /* Now hash again and check crypto_pk_get_hashed_fingerprint. */ + crypto_digest(d2, d, sizeof(d)); + tt_int_op(0, ==, crypto_pk_get_hashed_fingerprint(pk, fingerprint)); + tt_int_op(strlen(fingerprint), ==, DIGEST_LEN * 2); + test_memeq_hex(d2, fingerprint); + + done: + crypto_pk_free(pk); + tor_free(mem_op_hex_tmp); +} + /** Sanity check for crypto pk digests */ static void test_crypto_digests(void) @@ -1234,6 +1284,7 @@ struct testcase_t crypto_tests[] = { { "aes_EVP", test_crypto_aes, TT_FORK, &pass_data, (void*)"evp" }, CRYPTO_LEGACY(sha), CRYPTO_LEGACY(pk), + { "pk_fingerprints", test_crypto_pk_fingerprints, TT_FORK, NULL, NULL }, CRYPTO_LEGACY(digests), CRYPTO_LEGACY(dh), CRYPTO_LEGACY(s2k), From 886d4be14977f6033442161901a6cdd2ad0a5076 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 26 Feb 2014 13:42:21 -0500 Subject: [PATCH 4/4] Unit tests for test_routerkeys_write_fingerprint --- src/or/router.c | 2 +- src/or/router.h | 1 + src/test/include.am | 1 + src/test/test.c | 2 + src/test/test_routerkeys.c | 84 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/test/test_routerkeys.c diff --git a/src/or/router.c b/src/or/router.c index 2b58de8ff1..6fa9f65e10 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -688,7 +688,7 @@ router_initialize_tls_context(void) * it to 'fingerprint' (or 'hashed-fingerprint'). Return 0 on success, or * -1 if Tor should die, */ -static int +STATIC int router_write_fingerprint(int hashed) { char *keydir = NULL, *cp = NULL; diff --git a/src/or/router.h b/src/or/router.h index 74b673fbe5..630724681a 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -147,6 +147,7 @@ smartlist_t *router_get_all_orports(const routerinfo_t *ri); #ifdef ROUTER_PRIVATE /* Used only by router.c and test.c */ STATIC void get_platform_str(char *platform, size_t len); +STATIC int router_write_fingerprint(int hashed); #endif #endif diff --git a/src/test/include.am b/src/test/include.am index 5f978b518c..e7aebac389 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -35,6 +35,7 @@ src_test_test_SOURCES = \ src/test/test_options.c \ src/test/test_pt.c \ src/test/test_replay.c \ + src/test/test_routerkeys.c \ src/test/test_socks.c \ src/test/test_util.c \ src/test/test_config.c \ diff --git a/src/test/test.c b/src/test/test.c index 45f7c097d9..2529e2902a 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1629,6 +1629,7 @@ extern struct testcase_t logging_tests[]; extern struct testcase_t backtrace_tests[]; extern struct testcase_t hs_tests[]; extern struct testcase_t nodelist_tests[]; +extern struct testcase_t routerkeys_tests[]; static struct testgroup_t testgroups[] = { { "", test_array }, @@ -1654,6 +1655,7 @@ static struct testgroup_t testgroups[] = { { "control/", controller_event_tests }, { "hs/", hs_tests }, { "nodelist/", nodelist_tests }, + { "routerkeys/", routerkeys_tests }, END_OF_GROUPS }; diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c new file mode 100644 index 0000000000..ff52a7e7c1 --- /dev/null +++ b/src/test/test_routerkeys.c @@ -0,0 +1,84 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2013, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#define ROUTER_PRIVATE +#include "or.h" +#include "config.h" +#include "router.h" +#include "util.h" +#include "crypto.h" + +#include "test.h" + +static void +test_routerkeys_write_fingerprint(void *arg) +{ + crypto_pk_t *key = pk_generate(2); + or_options_t *options = get_options_mutable(); + const char *ddir = get_fname("write_fingerprint"); + char *cp = NULL, *cp2 = NULL; + char fp[FINGERPRINT_LEN+1]; + + (void)arg; + + tt_assert(key); + + options->ORPort_set = 1; /* So that we can get the server ID key */ + options->DataDirectory = tor_strdup(ddir); + options->Nickname = tor_strdup("haflinger"); + set_server_identity_key(key); + set_client_identity_key(crypto_pk_dup_key(key)); + + check_private_dir(ddir, CPD_CREATE, NULL); + tt_int_op(crypto_pk_cmp_keys(get_server_identity_key(),key),==,0); + + /* Write fingerprint file */ + tt_int_op(0, ==, router_write_fingerprint(0)); + cp = read_file_to_str(get_fname("write_fingerprint/fingerprint"), + 0, NULL); + crypto_pk_get_fingerprint(key, fp, 0); + tor_asprintf(&cp2, "haflinger %s\n", fp); + tt_str_op(cp, ==, cp2); + tor_free(cp); + tor_free(cp2); + + /* Write hashed-fingerprint file */ + tt_int_op(0, ==, router_write_fingerprint(1)); + cp = read_file_to_str(get_fname("write_fingerprint/hashed-fingerprint"), + 0, NULL); + crypto_pk_get_hashed_fingerprint(key, fp); + tor_asprintf(&cp2, "haflinger %s\n", fp); + tt_str_op(cp, ==, cp2); + tor_free(cp); + tor_free(cp2); + + /* Replace outdated file */ + write_str_to_file(get_fname("write_fingerprint/hashed-fingerprint"), + "junk goes here", 0); + tt_int_op(0, ==, router_write_fingerprint(1)); + cp = read_file_to_str(get_fname("write_fingerprint/hashed-fingerprint"), + 0, NULL); + crypto_pk_get_hashed_fingerprint(key, fp); + tor_asprintf(&cp2, "haflinger %s\n", fp); + tt_str_op(cp, ==, cp2); + tor_free(cp); + tor_free(cp2); + + done: + crypto_pk_free(key); + set_client_identity_key(NULL); + tor_free(cp); + tor_free(cp2); +} + +#define TEST(name, flags) \ + { #name , test_routerkeys_ ## name, (flags), NULL, NULL } + +struct testcase_t routerkeys_tests[] = { + TEST(write_fingerprint, TT_FORK), + END_OF_TESTCASES +}; +