[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [tor/master] Generate weird certificates correctly
commit 3bee74c6d115131f4850a07a5c12db21ae6f3193
Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date: Thu May 28 10:47:42 2015 -0400
Generate weird certificates correctly
(Our link protocol assumes that the link cert certifies the TLS key,
and there is an RSA->Ed25519 crosscert)
---
src/or/config.c | 8 +--
src/or/main.c | 3 +-
src/or/or.h | 4 +-
src/or/router.c | 5 +-
src/or/routerkeys.c | 127 ++++++++++++++++++++++++++++++--------------
src/or/routerkeys.h | 8 ++-
src/or/torcert.c | 43 ++++++++++++++-
src/or/torcert.h | 5 ++
src/test/test_routerkeys.c | 47 +++++++++-------
9 files changed, 183 insertions(+), 67 deletions(-)
diff --git a/src/or/config.c b/src/or/config.c
index 07451b3..adda528 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -358,7 +358,7 @@ static config_var_t option_vars_[] = {
V(TestingMinExitFlagThreshold, MEMUNIT, "0"),
V(TestingMinFastFlagThreshold, MEMUNIT, "0"),
- V(TestingLinkKeyLifetime, INTERVAL, "2 days"),
+ V(TestingLinkCertLifetime, INTERVAL, "2 days"),
V(TestingAuthKeyLifetime, INTERVAL, "2 days"),
V(TestingLinkKeySlop, INTERVAL, "3 hours"),
V(TestingAuthKeySlop, INTERVAL, "3 hours"),
@@ -3634,7 +3634,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
CHECK_DEFAULT(TestingMicrodescMaxDownloadTries);
CHECK_DEFAULT(TestingCertMaxDownloadTries);
CHECK_DEFAULT(TestingAuthKeyLifetime);
- CHECK_DEFAULT(TestingLinkKeyLifetime);
+ CHECK_DEFAULT(TestingLinkCertLifetime);
CHECK_DEFAULT(TestingSigningKeySlop);
CHECK_DEFAULT(TestingAuthKeySlop);
CHECK_DEFAULT(TestingLinkKeySlop);
@@ -3642,8 +3642,8 @@ options_validate(or_options_t *old_options, or_options_t *options,
if (options->SigningKeyLifetime < options->TestingSigningKeySlop*2)
REJECT("SigningKeyLifetime is too short.");
- if (options->TestingLinkKeyLifetime < options->TestingAuthKeySlop*2)
- REJECT("TestingLinkKeyLifetime 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.");
diff --git a/src/or/main.c b/src/or/main.c
index c4b5af4..4fac17a 100644
--- a/src/or/main.c
+++ b/src/or/main.c
@@ -1284,7 +1284,8 @@ run_scheduled_events(time_t now)
if (is_server && time_to_check_ed_keys < now) {
if (should_make_new_ed_keys(options, now)) {
- if (load_ed_keys(options, now) < 0) {
+ 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);
diff --git a/src/or/or.h b/src/or/or.h
index d45e19a..6b3ce77 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -1337,6 +1337,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
@@ -4265,7 +4267,7 @@ typedef struct {
/** 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 TestingLinkKeyLifetime;
+ int TestingLinkCertLifetime;
/** For how long (seconds) do we declare our auth keys to be valid? */
int TestingAuthKeyLifetime;
diff --git a/src/or/router.c b/src/or/router.c
index c94667a..1e433ed 100644
--- a/src/or/router.c
+++ b/src/or/router.c
@@ -206,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 */
@@ -864,7 +866,8 @@ init_keys(void)
}
/* 1d. Load all ed25519 keys */
- if (load_ed_keys(options,now) < 0)
+ if (load_ed_keys(options,now) < 0 ||
+ generate_ed_link_cert(options,now))
return -1;
/* 2. Read onion key. Make it if none is found. */
diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c
index 2482f59..b90cc73 100644
--- a/src/or/routerkeys.c
+++ b/src/or/routerkeys.c
@@ -3,6 +3,7 @@
#include "or.h"
#include "config.h"
+#include "router.h"
#include "routerkeys.h"
#include "torcert.h"
@@ -265,12 +266,14 @@ ed_key_new(const ed25519_keypair_t *signing_key,
static ed25519_keypair_t *master_identity_key = NULL;
static ed25519_keypair_t *master_signing_key = NULL;
-static ed25519_keypair_t *current_link_key = NULL;
static ed25519_keypair_t *current_auth_key = NULL;
static tor_cert_t *signing_key_cert = NULL;
-static tor_cert_t *link_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.
@@ -280,13 +283,11 @@ load_ed_keys(const or_options_t *options, time_t now)
{
ed25519_keypair_t *id = NULL;
ed25519_keypair_t *sign = NULL;
- ed25519_keypair_t *link = 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 *link_cert = NULL;
tor_cert_t *auth_cert = NULL;
#define FAIL(msg) do { \
@@ -380,15 +381,14 @@ load_ed_keys(const or_options_t *options, time_t now)
* it, if we loaded it in the first place. */
memwipe(id->seckey.seckey, 0, sizeof(id->seckey));
- if (!current_link_key ||
- EXPIRES_SOON(link_key_cert, options->TestingLinkKeySlop)) {
- link = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT,
- now,
- options->TestingLinkKeyLifetime,
- CERT_TYPE_SIGNING_LINK, &link_cert);
-
- if (!link)
- FAIL("Can't create link key");
+ 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 ||
@@ -413,40 +413,88 @@ load_ed_keys(const or_options_t *options, time_t now)
SET_KEY(master_signing_key, sign);
SET_CERT(signing_key_cert, sign_cert);
}
- if (link) {
- SET_KEY(current_link_key, link);
- SET_CERT(link_key_cert, link_cert);
- }
if (auth) {
SET_KEY(current_auth_key, auth);
SET_CERT(auth_key_cert, auth_cert);
}
+ if (generate_ed_link_cert(options, now) < 0)
+ FAIL("Couldn't make link cert");
+
return 0;
err:
ed25519_keypair_free(id);
ed25519_keypair_free(sign);
- ed25519_keypair_free(link);
ed25519_keypair_free(auth);
tor_cert_free(sign_cert);
- tor_cert_free(link_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)
+ 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)
{
- return (!master_identity_key ||
- !master_signing_key ||
- !current_link_key ||
- !current_auth_key ||
- EXPIRES_SOON(signing_key_cert, options->TestingSigningKeySlop) ||
- EXPIRES_SOON(link_key_cert, options->TestingLinkKeySlop) ||
- EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop));
+ 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
@@ -472,21 +520,15 @@ get_master_signing_key_cert(void)
}
const ed25519_keypair_t *
-get_current_link_keypair(void)
-{
- return current_link_key;
-}
-
-const ed25519_keypair_t *
get_current_auth_keypair(void)
{
return current_auth_key;
}
const tor_cert_t *
-get_current_link_key_cert(void)
+get_current_link_cert_cert(void)
{
- return link_key_cert;
+ return link_cert_cert;
}
const tor_cert_t *
@@ -495,6 +537,14 @@ 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>. */
@@ -587,14 +637,13 @@ routerkeys_free_all(void)
{
ed25519_keypair_free(master_identity_key);
ed25519_keypair_free(master_signing_key);
- ed25519_keypair_free(current_link_key);
ed25519_keypair_free(current_auth_key);
tor_cert_free(signing_key_cert);
- tor_cert_free(link_key_cert);
+ tor_cert_free(link_cert_cert);
tor_cert_free(auth_key_cert);
master_identity_key = master_signing_key = NULL;
- current_link_key = current_auth_key = NULL;
- signing_key_cert = link_key_cert = auth_key_cert = 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
index 0c50429..b45a22a 100644
--- a/src/or/routerkeys.h
+++ b/src/or/routerkeys.h
@@ -33,11 +33,13 @@ 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_link_keypair(void);
const ed25519_keypair_t *get_current_auth_keypair(void);
-const struct tor_cert_st *get_current_link_key_cert(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,
@@ -57,6 +59,8 @@ int check_tap_onion_key_crosscert(const uint8_t *crosscert,
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/torcert.c b/src/or/torcert.c
index 1534730..e2ddffd 100644
--- a/src/or/torcert.c
+++ b/src/or/torcert.c
@@ -1,12 +1,13 @@
/* Copyright (c) 2014, The Tor Project, Inc. */
/* See LICENSE for licensing information */
-#include "torcert.h"
#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.
@@ -237,3 +238,43 @@ tor_cert_opt_eq(const tor_cert_t *cert1, const tor_cert_t *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
index 4680ca6..b67dc52 100644
--- a/src/or/torcert.h
+++ b/src/or/torcert.h
@@ -67,5 +67,10 @@ 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/test/test_routerkeys.c b/src/test/test_routerkeys.c
index 4917424..06fc4ee 100644
--- a/src/test/test_routerkeys.c
+++ b/src/test/test_routerkeys.c
@@ -416,12 +416,21 @@ test_routerkeys_ed_keys_init_all(void *arg)
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, link, auth;
- // tor_cert_t *cert_is, *cert_sl, *cert_auth;
+ 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->TestingLinkKeyLifetime = 2*86400;
+ options->TestingLinkCertLifetime = 2*86400;
options->TestingSigningKeySlop = 2*86400;
options->TestingAuthKeySlop = 2*3600;
options->TestingLinkKeySlop = 2*3600;
@@ -440,59 +449,61 @@ test_routerkeys_ed_keys_init_all(void *arg)
tt_assert(get_master_identity_key());
tt_assert(get_master_identity_key());
tt_assert(get_master_signing_keypair());
- tt_assert(get_current_link_keypair());
tt_assert(get_current_auth_keypair());
tt_assert(get_master_signing_key_cert());
- tt_assert(get_current_link_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(&link, get_current_link_keypair(), sizeof(link));
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_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
- tt_mem_op(&link, ==, get_current_link_keypair(), sizeof(link));
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_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
- tt_mem_op(&link, !=, get_current_link_keypair(), sizeof(link));
+ 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_key_cert());
+ tt_assert(get_current_link_cert_cert());
tt_assert(get_current_auth_key_cert());
- memcpy(&link, get_current_link_keypair(), sizeof(link));
+ 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_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign));
- tt_mem_op(&link, !=, get_current_link_keypair(), sizeof(link));
+ 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_key_cert());
+ tt_assert(get_current_link_cert_cert());
tt_assert(get_current_auth_key_cert());
- memcpy(&link, get_current_link_keypair(), sizeof(link));
+ 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_mem_op(&id, ==, get_master_identity_key(), sizeof(id));
tt_mem_op(&sign, !=, get_master_signing_keypair(), sizeof(sign));
- tt_mem_op(&link, !=, get_current_link_keypair(), sizeof(link));
+ 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_key_cert());
+ tt_assert(get_current_link_cert_cert());
tt_assert(get_current_auth_key_cert());
memcpy(&sign, get_master_signing_keypair(), sizeof(sign));
- memcpy(&link, get_current_link_keypair(), sizeof(link));
+ 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 */
@@ -502,10 +513,10 @@ test_routerkeys_ed_keys_init_all(void *arg)
tt_int_op(0, ==, load_ed_keys(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(&link, !=, get_current_link_keypair(), sizeof(link));
+ 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_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
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits