From f253aef14faf7640f94e9e76947b6a4461c3c1a4 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 30 Sep 2014 08:31:08 -0400 Subject: [PATCH 01/28] Mechanical rename: tor_cert_t -> tor_x509_cert_t --- src/common/tortls.c | 66 +++++++++++++++++++++--------------------- src/common/tortls.h | 26 ++++++++--------- src/or/channeltls.c | 26 ++++++++--------- src/or/connection_or.c | 24 +++++++-------- src/or/or.h | 4 +-- 5 files changed, 73 insertions(+), 73 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 97a82bf6e1..b4d50cda62 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -122,7 +122,7 @@ static int use_unsafe_renegotiation_op = 0; 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 +137,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; @@ -821,7 +821,7 @@ static const int N_CLIENT_CIPHERS = ARRAY_LENGTH(CLIENT_CIPHER_INFO_LIST); /** Free all storage held in cert */ void -tor_cert_free(tor_cert_t *cert) +tor_x509_cert_free(tor_x509_cert_t *cert) { if (! cert) return; @@ -833,14 +833,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 +850,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 +880,14 @@ tor_cert_new(X509 *x509_cert) } /** Read a DER-encoded X509 cert, of length exactly certificate_len, - * from a certificate. Return a newly allocated tor_cert_t on success + * from a certificate. Return a newly allocated tor_x509_cert_t on success * and NULL on failure. */ -tor_cert_t * -tor_cert_decode(const uint8_t *certificate, size_t certificate_len) +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 +902,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 +921,7 @@ tor_cert_decode(const uint8_t *certificate, size_t certificate_len) /** Set *encoded_out and *size_out to cert'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 +934,7 @@ tor_cert_get_der(const tor_cert_t *cert, /** Return a set of digests for the public key in cert, 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 +944,7 @@ tor_cert_get_id_digests(const tor_cert_t *cert) /** Return a set of digests for the public key in cert. */ 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 +957,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 +973,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 +1003,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); @@ -1024,7 +1024,7 @@ tor_tls_cert_get_key(tor_cert_t *cert) * the key certified in cert 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) +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 +1053,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 +1265,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); @@ -2394,7 +2394,7 @@ tor_tls_peer_has_cert(tor_tls_t *tls) } /** Return the peer certificate, or NULL if there isn't one. */ -tor_cert_t * +tor_x509_cert_t * tor_tls_get_peer_cert(tor_tls_t *tls) { X509 *cert; @@ -2402,7 +2402,7 @@ tor_tls_get_peer_cert(tor_tls_t *tls) 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. */ diff --git a/src/common/tortls.h b/src/common/tortls.h index f8c6d5913b..22b1a9eb0f 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,7 +72,7 @@ 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); +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, @@ -120,22 +120,22 @@ 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); +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); diff --git a/src/or/channeltls.c b/src/or/channeltls.c index e194c1c4df..10fcb281db 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -1747,9 +1747,9 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) 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; + tor_x509_cert_t *link_cert = NULL; + tor_x509_cert_t *id_cert = NULL; + tor_x509_cert_t *auth_cert = NULL; uint8_t *ptr; int n_certs, i; int send_netinfo = 0; @@ -1803,7 +1803,7 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) 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); + tor_x509_cert_t *cert = tor_x509_cert_decode(ptr + 3, cert_len); if (!cert) { log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL, "Received undecodable certificate in CERTS cell from %s:%d", @@ -1812,24 +1812,24 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) } else { if (cert_type == OR_CERT_TYPE_TLS_LINK) { if (link_cert) { - tor_cert_free(cert); + tor_x509_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); + tor_x509_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); + tor_x509_cert_free(cert); ERR("Too many AUTH_1024 certificates"); } auth_cert = cert; } else { - tor_cert_free(cert); + tor_x509_cert_free(cert); } } } @@ -1864,7 +1864,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"); @@ -1929,9 +1929,9 @@ 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); + tor_x509_cert_free(id_cert); + tor_x509_cert_free(link_cert); + tor_x509_cert_free(auth_cert); #undef ERR } @@ -2151,7 +2151,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/connection_or.c b/src/or/connection_or.c index 85462d899d..6c584a5c77 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1878,8 +1878,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); } @@ -2227,7 +2227,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; @@ -2242,8 +2242,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 */ + @@ -2342,13 +2342,13 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, ptr += 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); + 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]; @@ -2387,8 +2387,8 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, { /* 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 { @@ -2397,10 +2397,10 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, } if (!cert) return -1; - memcpy(ptr, tor_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); + memcpy(ptr, tor_x509_cert_get_cert_digests(cert)->d[DIGEST_SHA256], 32); if (freecert) - tor_cert_free(freecert); + tor_x509_cert_free(freecert); ptr += 32; } diff --git a/src/or/or.h b/src/or/or.h index 6723f93f77..19694a081f 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -1411,9 +1411,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; From a9720b90f860323781d37dbba6ce04f312ec3632 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 30 Sep 2014 08:35:02 -0400 Subject: [PATCH 02/28] Fix whitespace from tor_x509_cert rename --- src/common/tortls.c | 4 ++-- src/common/tortls.h | 3 ++- src/or/connection_or.c | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index b4d50cda62..97dca4d478 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -880,8 +880,8 @@ tor_x509_cert_new(X509 *x509_cert) } /** Read a DER-encoded X509 cert, of length exactly certificate_len, - * from a certificate. Return a newly allocated tor_x509_cert_t on success - * and NULL on failure. */ + * from a certificate. 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) { diff --git a/src/common/tortls.h b/src/common/tortls.h index 22b1a9eb0f..28a73e9ae4 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -132,7 +132,8 @@ int tor_tls_get_my_certs(int server, 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_x509_cert_t *cert); -int tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert); +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_x509_cert_t *cert, const tor_x509_cert_t *signing_cert, diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 6c584a5c77..4ab47f0aa8 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -2348,7 +2348,8 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, if (tor_tls_get_my_certs(server, &link_cert, &id_cert)) return -1; my_digests = tor_x509_cert_get_id_digests(id_cert); - their_digests = tor_x509_cert_get_id_digests(conn->handshake_state->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]; From 818e6f939d4bd241e762970da4c6360858993cd5 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 30 Sep 2014 16:00:17 -0400 Subject: [PATCH 03/28] prop220: Implement certificates and key storage/creation For prop220, we have a new ed25519 certificate type. This patch implements the code to create, parse, and validate those, along with code for routers to maintain their own sets of certificates and keys. (Some parts of master identity key encryption are done, but the implementation of that isn't finished) --- src/common/crypto_ed25519.c | 10 + src/common/crypto_ed25519.h | 5 +- src/or/config.h | 4 + src/or/include.am | 13 +- src/or/main.c | 2 + src/or/or.h | 1 + src/or/router.c | 5 + src/or/routerkeys.c | 453 ++++++++++++++++ src/or/routerkeys.h | 44 ++ src/or/torcert.c | 208 ++++++++ src/or/torcert.h | 66 +++ src/test/include.am | 2 +- src/test/test_routerkeys.c | 444 +++++++++++++++- src/trunnel/ed25519_cert.c | 887 +++++++++++++++++++++++++++++++ src/trunnel/ed25519_cert.h | 288 ++++++++++ src/trunnel/ed25519_cert.trunnel | 76 +++ src/trunnel/include.am | 18 +- 17 files changed, 2513 insertions(+), 13 deletions(-) create mode 100644 src/or/routerkeys.c create mode 100644 src/or/routerkeys.h create mode 100644 src/or/torcert.c create mode 100644 src/or/torcert.h create mode 100644 src/trunnel/ed25519_cert.c create mode 100644 src/trunnel/ed25519_cert.h create mode 100644 src/trunnel/ed25519_cert.trunnel diff --git a/src/common/crypto_ed25519.c b/src/common/crypto_ed25519.c index f2e6945ac8..7e8b00abef 100644 --- a/src/common/crypto_ed25519.c +++ b/src/common/crypto_ed25519.c @@ -351,3 +351,13 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out, return 0; } +void +ed25519_keypair_free(ed25519_keypair_t *kp) +{ + if (! kp) + return; + + memwipe(kp, 0, sizeof(*kp)); + tor_free(kp); +} + diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h index 7efa74bff5..8e06191fc5 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. */ @@ -109,5 +110,7 @@ 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); + #endif diff --git a/src/or/config.h b/src/or/config.h index b064f05321..e97248540c 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) \ diff --git a/src/or/include.am b/src/or/include.am index b44e1099dc..a85b0dde05 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -71,12 +71,14 @@ 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) \ @@ -87,7 +89,7 @@ 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_libtor_a_LIBADD = src/trunnel/libor-trunnel.a src_or_tor_SOURCES = src/or/tor_main.c AM_CPPFLAGS += -I$(srcdir)/src/or -Isrc/or @@ -109,7 +111,7 @@ 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@ @@ -120,7 +122,7 @@ 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 @@ -180,12 +182,15 @@ 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 diff --git a/src/or/main.c b/src/or/main.c index bc89458e87..8b82a31d7a 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -51,6 +51,7 @@ #include "rendservice.h" #include "rephist.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" #include "scheduler.h" @@ -2648,6 +2649,7 @@ tor_free_all(int postfork) config_free_all(); or_state_free_all(); router_free_all(); + routerkeys_free_all(); policies_free_all(); } if (!postfork) { diff --git a/src/or/or.h b/src/or/or.h index 19694a081f..2044e844c3 100644 --- a/src/or/or.h +++ b/src/or/or.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. diff --git a/src/or/router.c b/src/or/router.c index 2ddaa895fc..135561381f 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -26,6 +26,7 @@ #include "relay.h" #include "rephist.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" #include "statefile.h" @@ -861,6 +862,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); diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c new file mode 100644 index 0000000000..fb61c310bd --- /dev/null +++ b/src/or/routerkeys.c @@ -0,0 +1,453 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "or.h" +#include "config.h" +#include "routerkeys.h" +#include "torcert.h" + +/** + * Read an ed25519 key and associated certificates from files beginning with + * fname, with certificate type cert_type. On failure, return + * NULL; on success return the keypair. + * + * If INIT_ED_KEY_CREATE is set in flags, 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 flags, load/create a certificate + * too and store it in *cert_out. Fail if the cert can't be + * found/created. To create a certificate, signing_key must be set to + * the key that should sign it; now to the current time, and + * lifetime to the lifetime of the key. + * + * If INIT_ED_KEY_REPLACE is set in flags, then create and save new key + * whether we can read the old one or not. + * + * If INIT_ED_KEY_EXTRA_STRONG is set in flags, set the extra_strong + * flag when creating the secret key. + * + * If INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT is set in flags, and + * we create a new certificate, create it with the signing key embedded. + * + * If INIT_ED_KEY_SPLIT is set in flags, 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 flags, and we find a + * public key file but no secret key file, return successfully anyway. + */ +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 && + 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 && (flags & INIT_ED_KEY_MISSING_SECRET_OK)) { + 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 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. */ + 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. */ + if (cert) { + 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 (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: + 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); + + 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_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 *auth_key_cert = NULL; + +/** + * 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 *link = NULL; + ed25519_keypair_t *auth = NULL; + const ed25519_keypair_t *use_signing = NULL; + tor_cert_t *sign_cert = NULL; + tor_cert_t *link_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 */ + + /* XXXX use options. */ + (void) options; + + id = ed_key_init_from_file( + options_get_datadir_fname2(options, "keys", "ed25519_master_id"), + (INIT_ED_KEY_CREATE|INIT_ED_KEY_SPLIT| + INIT_ED_KEY_MISSING_SECRET_OK| + INIT_ED_KEY_EXTRA_STRONG), + LOG_WARN, NULL, 0, 0, 0, NULL); + if (!id) + FAIL("Missing identity key"); + + if (!master_signing_key || EXPIRES_SOON(signing_key_cert, 86400/*???*/)) { + uint32_t flags = (INIT_ED_KEY_CREATE| + INIT_ED_KEY_EXTRA_STRONG| + INIT_ED_KEY_NEEDCERT| + INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT); + const ed25519_keypair_t *sign_with_id = id; + if (master_signing_key) { + flags |= INIT_ED_KEY_REPLACE; /* it's expired, so force-replace it. */ + } + if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey))) { + sign_with_id = NULL; + flags &= ~INIT_ED_KEY_CREATE; + } + sign = ed_key_init_from_file( + options_get_datadir_fname2(options, "keys", "ed25519_signing"), + flags, LOG_WARN, + sign_with_id, now, 30*86400/*XXX option*/, + CERT_TYPE_ID_SIGNING, &sign_cert); + if (!sign) + FAIL("Missing signing key"); + use_signing = sign; + } else { + use_signing = master_signing_key; + } + + /* 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 (!current_link_key || EXPIRES_SOON(link_key_cert, 7200/*???*/)) { + link = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT, + now, 2*86400/*XXX option??*/, + CERT_TYPE_SIGNING_LINK, &link_cert); + + if (!link) + FAIL("Can't create link key"); + } + + if (!current_auth_key || EXPIRES_SOON(auth_key_cert, 7200)/*???*/) { + auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT, + now, 2*86400/*XXX option??*/, + 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 (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); + } + + 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; +#undef FAIL +#undef SET_KEY +#undef SET_CERT +#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_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) +{ + return link_key_cert; +} + +const tor_cert_t * +get_current_auth_key_cert(void) +{ + return auth_key_cert; +} + +void +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(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; +} + diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h new file mode 100644 index 0000000000..eb21401d56 --- /dev/null +++ b/src/or/routerkeys.h @@ -0,0 +1,44 @@ +/* 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) + +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_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_auth_key_cert(void); + +int load_ed_keys(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 new file mode 100644 index 0000000000..478ec4d5ac --- /dev/null +++ b/src/or/torcert.c @@ -0,0 +1,208 @@ +/* Copyright (c) 2014, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "torcert.h" +#include "crypto.h" +#include "ed25519_cert.h" +#include "torlog.h" +#include "util.h" +#include "compat.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); + + return torcert; + + err: + tor_cert_free(torcert); + ed25519_cert_free(cert); + tor_free(encoded); + return NULL; +} + +/** + * Create and return a new new certificate of type cert_type to + * authenticate signed_key using the key signing_key. The + * certificate should remain valid for at least lifetime seconds after + * now. + * + * If CERT_FLAG_INCLUDE_SIGNING_KEY is set in flags, embed + * the public part of signing_key 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 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 len bytes in encoded. */ +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; + } + } + + return cert; + err: + ed25519_cert_free(parsed); + tor_cert_free(cert); + return NULL; +} + +/** Fill in checkable_out with the information needed to check + * the signature on cert with pubkey. */ +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 cert with pubkey relative to + * the current time now. Return 0 on success, -1 on failure. + * Sets flags in cert 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; + } +} + diff --git a/src/or/torcert.h b/src/or/torcert.h new file mode 100644 index 0000000000..7e9c3f5b2d --- /dev/null +++ b/src/or/torcert.h @@ -0,0 +1,66 @@ +/* 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_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 encoded */ + 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); + +#endif + diff --git a/src/test/include.am b/src/test/include.am index d20d2f66b9..44f63a7230 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -104,7 +104,7 @@ 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@ diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 60b6bb5a72..63284b798b 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 +#endif + static void test_routerkeys_write_fingerprint(void *arg) { @@ -75,11 +81,447 @@ 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}; + tor_cert_t *parsed_cert[2] = {NULL, NULL}; + time_t now = 1412094534; + uint8_t *junk = 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 */ + tt_ptr_op(NULL, ==,tor_cert_parse(cert[0]->encoded, cert[0]->encoded_len-1)); + + /* - First byte modified */ + cert[0]->encoded[0] = 99; + tt_ptr_op(NULL, ==,tor_cert_parse(cert[0]->encoded, cert[0]->encoded_len)); + 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); + tt_ptr_op(NULL, ==, tor_cert_parse(junk, cert[0]->encoded_len+1)); + + /* - 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 */ + tt_ptr_op(NULL, ==, tor_cert_parse(junk, 104 + 36 * 2)); + + done: + tor_cert_free(cert[0]); + tor_cert_free(cert[1]); + tor_cert_free(parsed_cert[0]); + tor_cert_free(parsed_cert[1]); + tor_free(junk); +} + +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, link, auth; + // tor_cert_t *cert_is, *cert_sl, *cert_auth; + +#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_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_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)); + + /* 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)); + + /* 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_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_auth_key_cert()); + memcpy(&link, get_current_link_keypair(), sizeof(link)); + 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_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_auth_key_cert()); + memcpy(&link, get_current_link_keypair(), sizeof(link)); + 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_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_auth_key_cert()); + memcpy(&sign, get_master_signing_keypair(), sizeof(sign)); + memcpy(&link, get_current_link_keypair(), sizeof(link)); + 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_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(get_master_signing_key_cert()); + tt_assert(get_current_link_key_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); + routerkeys_free_all(); +} + #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), END_OF_TESTCASES }; diff --git a/src/trunnel/ed25519_cert.c b/src/trunnel/ed25519_cert.c new file mode 100644 index 0000000000..2a84e4b163 --- /dev/null +++ b/src/trunnel/ed25519_cert.c @@ -0,0 +1,887 @@ +/* ed25519_cert.c -- generated by Trunnel v1.2. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#include +#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: + if (result >= 0) result = -1; + 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..3ddf95ef9f --- /dev/null +++ b/src/trunnel/ed25519_cert.h @@ -0,0 +1,288 @@ +/* ed25519_cert.h -- generated by by Trunnel v1.2. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#ifndef TRUNNEL_ED25519_CERT_H +#define TRUNNEL_ED25519_CERT_H + +#include +#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..6e7851aafe 100644 --- a/src/trunnel/include.am +++ b/src/trunnel/include.am @@ -9,15 +9,21 @@ endif AM_CPPFLAGS += -I$(srcdir)/src/ext/trunnel -I$(srcdir)/src/trunnel +TRUNNELINPUTS = \ + src/trunnel/ed25519_cert.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/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/pwbox.h src_trunnel_libor_trunnel_a_SOURCES = $(TRUNNELSOURCES) src_trunnel_libor_trunnel_a_CPPFLAGS = -DTRUNNEL_LOCAL_H $(AM_CPPFLAGS) From fe5d2477aabbf06c940c33a266d6ebb3a7b19fe1 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 30 Sep 2014 23:36:47 -0400 Subject: [PATCH 04/28] Implement ed25519-signed descriptors Now that we have ed25519 keys, we can sign descriptors with them and check those signatures as documented in proposal 220. --- src/common/crypto.c | 82 ++++++++++++++++++++++++++++++++- src/common/crypto.h | 10 ++++ src/common/crypto_ed25519.c | 11 +++++ src/common/crypto_ed25519.h | 10 ++++ src/common/crypto_format.c | 39 ++++++++++++++++ src/or/or.h | 2 + src/or/router.c | 61 +++++++++++++++++++++++-- src/or/router.h | 3 +- src/or/routerkeys.c | 2 +- src/or/routerlist.c | 3 ++ src/or/routerparse.c | 91 +++++++++++++++++++++++++++++++++++++ src/or/routerparse.h | 2 + src/or/torcert.c | 10 ++++ src/or/torcert.h | 2 + src/test/test_crypto.c | 20 +++++++- src/test/test_dir.c | 34 ++++++++++++-- src/test/test_routerkeys.c | 2 + 17 files changed, 370 insertions(+), 14 deletions(-) diff --git a/src/common/crypto.c b/src/common/crypto.c index 218c7bea1e..06631e1604 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -1724,7 +1724,24 @@ crypto_digest_assign(crypto_digest_t *into, * out_len 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 lst, set the len_out-byte digest + * at digest_out to the hash of the concatenation of: the + * optional string prepend, those strings, + * and the optional string append, computed with the algorithm + * alg. + * out_len 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 +1749,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) @@ -2692,6 +2711,8 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen) return -1; if (destlen > SIZE_T_CEILING) return -1; + if (destlen) + *dest = 0; /* Ensure we always initialize the buffer */ EVP_EncodeInit(&ctx); EVP_EncodeUpdate(&ctx, (unsigned char*)dest, &len, @@ -2701,6 +2722,65 @@ base64_encode(char *dest, size_t destlen, const char *src, size_t srclen) return ret; } +/** 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); + 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; +} + /** @{ */ /** Special values used for the base64_decode_table */ #define X 255 diff --git a/src/common/crypto.h b/src/common/crypto.h index d305bc17a0..526619766d 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -207,6 +207,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); @@ -270,6 +275,11 @@ void smartlist_shuffle(struct smartlist_t *sl); int base64_encode(char *dest, size_t destlen, const char *src, size_t srclen); 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 7e8b00abef..6b93751dda 100644 --- a/src/common/crypto_ed25519.c +++ b/src/common/crypto_ed25519.c @@ -351,6 +351,7 @@ ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out, return 0; } +/** Release all storage held for kp. */ void ed25519_keypair_free(ed25519_keypair_t *kp) { @@ -361,3 +362,13 @@ ed25519_keypair_free(ed25519_keypair_t *kp) tor_free(kp); } +/** Return true iff key1 and key2 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 8e06191fc5..79b3db8f9a 100644 --- a/src/common/crypto_ed25519.h +++ b/src/common/crypto_ed25519.h @@ -95,6 +95,13 @@ int ed25519_public_from_base64(ed25519_public_key_t *pkey, int ed25519_public_to_base64(char *output, const ed25519_public_key_t *pkey); +#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, @@ -112,5 +119,8 @@ int ed25519_pubkey_read_from_file(ed25519_public_key_t *pubkey_out, 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..503d1de932 100644 --- a/src/common/crypto_format.c +++ b/src/common/crypto_format.c @@ -65,3 +65,42 @@ ed25519_public_to_base64(char *output, return digest256_to_base64(output, (const char *)pkey->pubkey); } +/** Encode the signature sig into the buffer at output, + * 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 input into an ed25519 signature. On + * success, store the value in sig 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/or/or.h b/src/or/or.h index 2044e844c3..437183e727 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2021,6 +2021,8 @@ 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; char *platform; /**< What software/operating system is this OR using? */ diff --git a/src/or/router.c b/src/or/router.c index 135561381f..6b2a238140 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -30,6 +30,7 @@ #include "routerlist.h" #include "routerparse.h" #include "statefile.h" +#include "torcert.h" #include "transports.h" #include "routerset.h" @@ -1881,6 +1882,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); @@ -1995,8 +1998,9 @@ router_rebuild_descriptor(int force) /* 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_master_signing_keypair())) ) { log_warn(LD_BUG, "Couldn't generate router descriptor."); routerinfo_free(ri); extrainfo_free(ei); @@ -2302,12 +2306,13 @@ get_platform_str(char *platform, size_t len) */ char * router_dump_router_to_string(routerinfo_t *router, - crypto_pk_t *ident_key) + crypto_pk_t *ident_key, + 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; @@ -2318,6 +2323,8 @@ router_dump_router_to_string(routerinfo_t *router, 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; /* Make sure the identity key matches the one in the routerinfo. */ if (!crypto_pk_eq_keys(ident_key, router->identity_pkey)) { @@ -2325,6 +2332,15 @@ 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"); + goto err; + } + } /* record our fingerprint, so we can include it in the descriptor */ if (crypto_pk_get_fingerprint(router->identity_pkey, fingerprint, 1)<0) { @@ -2332,6 +2348,21 @@ router_dump_router_to_string(routerinfo_t *router, goto err; } + if (emit_ed_sigs) { + /* Encode ed25519 signing cert */ + char ed_cert_base64[256]; + if (base64_encode(ed_cert_base64, sizeof(ed_cert_base64), + (const char*)router->signing_key_cert->encoded, + router->signing_key_cert->encoded_len) < 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); + } + /* PEM-encode the onion key */ if (crypto_pk_write_public_key_to_string(router->onion_pkey, &onion_pkey,&onion_pkeylen)<0) { @@ -2385,6 +2416,7 @@ 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" @@ -2399,6 +2431,7 @@ router_dump_router_to_string(routerinfo_t *router, 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, @@ -2455,7 +2488,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); @@ -2507,6 +2557,7 @@ 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); return output; } diff --git a/src/or/router.h b/src/or/router.h index 8108ffb22f..c53a104ebc 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -91,7 +91,8 @@ int router_is_me(const routerinfo_t *router); int router_pick_published_address(const or_options_t *options, uint32_t *addr); int router_rebuild_descriptor(int force); char *router_dump_router_to_string(routerinfo_t *router, - crypto_pk_t *ident_key); + crypto_pk_t *ident_key, + const ed25519_keypair_t *signing_keypair); char *router_dump_exit_policy_to_string(const routerinfo_t *router, int include_ipv4, int include_ipv6); diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index fb61c310bd..6609d89311 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -300,7 +300,7 @@ load_ed_keys(const or_options_t *options, time_t now) (void) options; id = ed_key_init_from_file( - options_get_datadir_fname2(options, "keys", "ed25519_master_id"), + options_get_datadir_fname2(options, "keys", "ed25519_master_id"), (INIT_ED_KEY_CREATE|INIT_ED_KEY_SPLIT| INIT_ED_KEY_MISSING_SECRET_OK| INIT_ED_KEY_EXTRA_STRONG), diff --git a/src/or/routerlist.c b/src/or/routerlist.c index f4f6200bbc..069f70d662 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" @@ -38,6 +39,7 @@ #include "routerparse.h" #include "routerset.h" #include "../common/sandbox.h" +#include "torcert.h" // #define DEBUG_ROUTERLIST /****************************************************************************/ @@ -2663,6 +2665,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); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 5a9626f0a2..dbd6fdd472 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -26,6 +26,8 @@ #include "rephist.h" #include "routerparse.h" #include "entrynodes.h" +#include "torcert.h" + #undef log #include @@ -83,6 +85,8 @@ typedef enum { K_HIDDEN_SERVICE_DIR, K_ALLOW_SINGLE_HOP_EXITS, K_IPV6_POLICY, + K_ROUTER_SIG_ED25519, + K_IDENTITY_ED25519, K_DIRREQ_END, K_DIRREQ_V2_IPS, @@ -293,6 +297,9 @@ 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("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ), + T01("allow-single-hop-exits",K_ALLOW_SINGLE_HOP_EXITS, NO_ARGS, NO_OBJ ), T01("family", K_FAMILY, ARGS, NO_OBJ ), @@ -506,6 +513,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, @@ -1302,6 +1313,86 @@ router_parse_entry_from_string(const char *s, const char *end, tor_memdup(&k, sizeof(curve25519_public_key_t)); } + { + 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); + if (!ed_sig_tok != !ed_cert_tok) { + log_warn(LD_DIR, "Router descriptor with only partial ed25519 support"); + goto err; + } + if (ed_sig_tok) { + tor_assert(ed_cert_tok); + if (ed_cert_tok != smartlist_get(tokens, 0) && + ed_cert_tok != smartlist_get(tokens, 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; + } + + 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; + 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, "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[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 signatures"); + goto err; + } + if (cert->valid_until < time(NULL)) { + log_warn(LD_DIR, "Expired ed25519 certificate in router descriptor"); + goto err; + } + } + } + tok = find_by_keyword(tokens, K_SIGNING_KEY); router->identity_pkey = tok->key; tok->key = NULL; /* Prevent free */ diff --git a/src/or/routerparse.h b/src/or/routerparse.h index fc21cb1041..52e53a833c 100644 --- a/src/or/routerparse.h +++ b/src/or/routerparse.h @@ -92,5 +92,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 index 478ec4d5ac..8fe9c12000 100644 --- a/src/or/torcert.c +++ b/src/or/torcert.c @@ -206,3 +206,13 @@ tor_cert_checksig(tor_cert_t *cert, } } +/** Return a new copy of cert */ +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; +} + diff --git a/src/or/torcert.h b/src/or/torcert.h index 7e9c3f5b2d..644cbf812d 100644 --- a/src/or/torcert.h +++ b/src/or/torcert.h @@ -62,5 +62,7 @@ int tor_cert_get_checkable_sig(ed25519_checkable_t *checkable_out, 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); + #endif diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index e9fb8bf084..15ba14b3bf 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -620,9 +620,18 @@ test_crypto_formats(void *arg) for (idx = 0; idx < 10; ++idx) { i = base64_encode(data2, 1024, data1, idx); 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); @@ -1172,6 +1181,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 +1202,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 +1341,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 +1355,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")); diff --git a/src/test/test_dir.c b/src/test/test_dir.c index a949f5de73..85ca40f3de 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -14,6 +14,7 @@ #define NETWORKSTATUS_PRIVATE #include "or.h" #include "config.h" +#include "crypto_ed25519.h" #include "directory.h" #include "dirserv.h" #include "dirvote.h" @@ -23,6 +24,7 @@ #include "routerlist.h" #include "routerparse.h" #include "test.h" +#include "torcert.h" static void test_dir_nicknames(void *arg) @@ -89,6 +91,7 @@ test_dir_formats(void *arg) routerlist_t *dir1 = NULL, *dir2 = 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,6 +130,22 @@ 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); r2->platform = tor_strdup(platform); r2->cache_info.published_on = 5; r2->or_port = 9005; @@ -150,7 +169,7 @@ test_dir_formats(void *arg) /* XXXX025 router_dump_to_string should really take this from ri.*/ options->ContactInfo = tor_strdup("Magri White " ""); - buf = router_dump_router_to_string(r1, pk2); + buf = router_dump_router_to_string(r1, pk2, NULL); tor_free(options->ContactInfo); tt_assert(buf); @@ -183,7 +202,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); tt_assert(buf); cp = buf; rp1 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL); @@ -201,6 +220,10 @@ test_dir_formats(void *arg) strlcpy(buf2, "router Fred 10.3.2.1 9005 0 0\n" + "identity-ed25519\n" + "-----BEGIN ED25519 CERT-----\n", sizeof(buf2)); + strlcat(buf2, cert_buf, sizeof(buf2)); + strlcat(buf2, "-----END ED25519 CERT-----\n" "platform Tor "VERSION" on ", sizeof(buf2)); strlcat(buf2, get_uname(), sizeof(buf2)); strlcat(buf2, "\n" @@ -219,15 +242,16 @@ test_dir_formats(void *arg) strlcat(buf2, "ntor-onion-key " "skyinAnvardNostarsNomoonNowindormistsorsnow=\n", 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, &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); tor_free(buf); - buf = router_dump_router_to_string(r2, pk1); + buf = router_dump_router_to_string(r2, pk1, NULL); cp = buf; rp2 = router_parse_entry_from_string((const char*)cp,NULL,1,0,NULL,NULL); tt_assert(rp2); diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 63284b798b..8780213a29 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -90,6 +90,7 @@ test_routerkeys_ed_certs(void *args) 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)); @@ -186,6 +187,7 @@ test_routerkeys_ed_certs(void *args) tor_cert_free(parsed_cert[0]); tor_cert_free(parsed_cert[1]); tor_free(junk); + tor_free(base64); } static void From efa21bb94188863e4ff5d8a288144cd93db00fe3 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 1 Oct 2014 11:54:07 -0400 Subject: [PATCH 05/28] Implement proposal 228: cross-certification with onion keys Routers now use TAP and ntor onion keys to sign their identity keys, and put these signatures in their descriptors. That allows other parties to be confident that the onion keys are indeed controlled by the router that generated the descriptor. --- src/common/crypto.c | 16 ++--- src/common/crypto.h | 10 +-- src/or/or.h | 5 ++ src/or/router.c | 72 +++++++++++++++++++++- src/or/router.h | 4 +- src/or/routerkeys.c | 87 ++++++++++++++++++++++++++ src/or/routerkeys.h | 16 +++++ src/or/routerlist.c | 5 ++ src/or/routerlist.h | 7 ++- src/or/routerparse.c | 122 +++++++++++++++++++++++++++++-------- src/or/routerparse.h | 2 +- src/or/torcert.h | 1 + src/test/test_dir.c | 54 +++++++++++++--- src/test/test_routerkeys.c | 70 +++++++++++++++++++++ 14 files changed, 417 insertions(+), 54 deletions(-) diff --git a/src/common/crypto.c b/src/common/crypto.c index 06631e1604..05dc43213c 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -830,7 +830,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 +856,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 env, 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 env, in bits. */ @@ -997,7 +997,7 @@ crypto_pk_private_decrypt(crypto_pk_t *env, char *to, * at least the length of the modulus of env. */ 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 +1069,7 @@ crypto_pk_public_checksig_digest(crypto_pk_t *env, const char *data, * at least the length of the modulus of env. */ 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 +1084,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 +1298,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) { diff --git a/src/common/crypto.h b/src/common/crypto.h index 526619766d..15d1f6e3a9 100644 --- a/src/common/crypto.h +++ b/src/common/crypto.h @@ -147,9 +147,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 +161,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); diff --git a/src/or/or.h b/src/or/or.h index 437183e727..d030189b61 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2023,6 +2023,9 @@ typedef struct { 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? */ @@ -5043,6 +5046,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/router.c b/src/or/router.c index 6b2a238140..2087a25fca 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2000,6 +2000,8 @@ router_rebuild_descriptor(int force) } 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); @@ -2306,7 +2308,9 @@ 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; @@ -2325,6 +2329,8 @@ router_dump_router_to_string(routerinfo_t *router, 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)) { @@ -2377,6 +2383,67 @@ 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) < 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)<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); @@ -2426,6 +2493,7 @@ router_dump_router_to_string(routerinfo_t *router, "%s%s%s%s" "onion-key\n%s" "signing-key\n%s" + "%s%s" "%s%s%s%s", router->nickname, address, @@ -2446,6 +2514,8 @@ router_dump_router_to_string(routerinfo_t *router, (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" : "", diff --git a/src/or/router.h b/src/or/router.h index c53a104ebc..cb813c6813 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -91,7 +91,9 @@ int router_is_me(const routerinfo_t *router); int router_pick_published_address(const or_options_t *options, uint32_t *addr); 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, diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 6609d89311..9ad5a89479 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -435,6 +435,93 @@ get_current_auth_key_cert(void) return auth_key_cert; } +/** Construct cross-certification for the master identity key with + * the ntor onion key. Store the sign of the corresponding ed25519 public key + * in *sign_out. */ +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 len_out 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) { diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h index eb21401d56..5e2ef45314 100644 --- a/src/or/routerkeys.h +++ b/src/or/routerkeys.h @@ -37,6 +37,22 @@ 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_auth_key_cert(void); +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); void routerkeys_free_all(void); diff --git a/src/or/routerlist.c b/src/or/routerlist.c index 069f70d662..dbb93e4a64 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -3294,6 +3294,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)) { 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 s 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 dbd6fdd472..b3bb565e58 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -24,6 +24,7 @@ #include "microdesc.h" #include "networkstatus.h" #include "rephist.h" +#include "routerkeys.h" #include "routerparse.h" #include "entrynodes.h" #include "torcert.h" @@ -87,6 +88,8 @@ typedef enum { K_IPV6_POLICY, K_ROUTER_SIG_ED25519, K_IDENTITY_ED25519, + K_ONION_KEY_CROSSCERT, + K_NTOR_ONION_KEY_CROSSCERT, K_DIRREQ_END, K_DIRREQ_V2_IPS, @@ -299,6 +302,9 @@ static token_rule_t routerdesc_token_table[] = { T01("hidden-service-dir", K_HIDDEN_SERVICE_DIR, NO_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 ), + 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 ), @@ -648,7 +654,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; @@ -868,8 +874,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); @@ -1116,6 +1122,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; @@ -1191,6 +1198,7 @@ router_parse_entry_from_string(const char *s, const char *end, 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; @@ -1313,16 +1321,30 @@ router_parse_entry_from_string(const char *s, const char *end, tor_memdup(&k, sizeof(curve25519_public_key_t)); } + tok = find_by_keyword(tokens, K_SIGNING_KEY); + router->identity_pkey = tok->key; + tok->key = NULL; /* Prevent free */ + if (crypto_pk_get_digest(router->identity_pkey, + router->cache_info.identity_digest)) { + log_warn(LD_DIR, "Couldn't calculate key digest"); goto err; + } + { - directory_token_t *ed_sig_tok, *ed_cert_tok; + directory_token_t *ed_sig_tok, *ed_cert_tok, *cc_tap_tok, *cc_ntor_tok; ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519); ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519); - if (!ed_sig_tok != !ed_cert_tok) { - log_warn(LD_DIR, "Router descriptor with only partial ed25519 support"); + 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 (ed_sig_tok) { - tor_assert(ed_cert_tok); + tor_assert(ed_cert_tok && cc_tap_tok && cc_ntor_tok); if (ed_cert_tok != smartlist_get(tokens, 0) && ed_cert_tok != smartlist_get(tokens, 1)) { log_warn(LD_DIR, "Ed25519 certificate in wrong position"); @@ -1336,10 +1358,25 @@ router_parse_entry_from_string(const char *s, const char *end, 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); @@ -1347,13 +1384,33 @@ router_parse_entry_from_string(const char *s, const char *end, log_warn(LD_DIR, "Couldn't parse ed25519 cert"); goto err; } - router->signing_key_cert = cert; + 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; } + ntor_cc_cert = tor_cert_parse((const uint8_t*)cc_ntor_tok->object_body, + cc_ntor_tok->object_size); + if (!cc_ntor_tok) { + 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) { @@ -1367,40 +1424,50 @@ router_parse_entry_from_string(const char *s, const char *end, crypto_digest_get_digest(d, (char*)d256, sizeof(d256)); crypto_digest_free(d); - ed25519_checkable_t check[2]; - int check_ok[2]; + 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 (ed25519_signature_from_base64(&check[1].signature, + 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[1].pubkey = &cert->signed_key; - check[1].msg = d256; - check[1].len = DIGEST256_LEN; + check[2].pubkey = &cert->signed_key; + check[2].msg = d256; + check[2].len = DIGEST256_LEN; - if (ed25519_checksig_batch(check_ok, check, 2) < 0) { - log_warn(LD_DIR, "Incorrect ed25519 signatures"); + if (ed25519_checksig_batch(check_ok, check, 3) < 0) { + log_warn(LD_DIR, "Incorrect ed25519 signature(s)"); goto err; } - if (cert->valid_until < time(NULL)) { - log_warn(LD_DIR, "Expired ed25519 certificate in router descriptor"); + + 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; } } - tok = find_by_keyword(tokens, K_SIGNING_KEY); - router->identity_pkey = tok->key; - tok->key = NULL; /* Prevent free */ - if (crypto_pk_get_digest(router->identity_pkey, - router->cache_info.identity_digest)) { - log_warn(LD_DIR, "Couldn't calculate key digest"); goto err; - } - 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]; @@ -1527,6 +1594,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); diff --git a/src/or/routerparse.h b/src/or/routerparse.h index 52e53a833c..7776063f03 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, diff --git a/src/or/torcert.h b/src/or/torcert.h index 644cbf812d..ae9361ff16 100644 --- a/src/or/torcert.h +++ b/src/or/torcert.h @@ -11,6 +11,7 @@ #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 diff --git a/src/test/test_dir.c b/src/test/test_dir.c index 85ca40f3de..d7b5e1b7c4 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -21,6 +21,7 @@ #include "hibernate.h" #include "networkstatus.h" #include "router.h" +#include "routerkeys.h" #include "routerlist.h" #include "routerparse.h" #include "test.h" @@ -89,6 +90,8 @@ 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; + tor_cert_t *ntor_cc = NULL; or_options_t *options = get_options_mutable(); const addr_policy_t *p; time_t now = time(NULL); @@ -152,8 +155,10 @@ test_dir_formats(void *arg) 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(); @@ -169,7 +174,7 @@ test_dir_formats(void *arg) /* XXXX025 router_dump_to_string should really take this from ri.*/ options->ContactInfo = tor_strdup("Magri White " ""); - buf = router_dump_router_to_string(r1, pk2, NULL); + buf = router_dump_router_to_string(r1, pk2, NULL, NULL, NULL); tor_free(options->ContactInfo); tt_assert(buf); @@ -202,7 +207,7 @@ test_dir_formats(void *arg) tt_str_op(buf,OP_EQ, buf2); tor_free(buf); - buf = router_dump_router_to_string(r1, pk2, NULL); + 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); @@ -238,20 +243,49 @@ 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); + 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; + 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); + 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); + strlcat(buf2, cert_buf, sizeof(buf2)); strlcat(buf2, "accept *:80\nreject 18.0.0.0/8:24\n", sizeof(buf2)); strlcat(buf2, "router-sig-ed25519 ", sizeof(buf2)); - buf = router_dump_router_to_string(r2, pk1, &kp2); + 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, NULL); + 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); @@ -304,6 +338,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); @@ -1418,6 +1453,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); diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 8780213a29..2434255b63 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -514,6 +514,74 @@ test_routerkeys_ed_keys_init_all(void *arg) 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); +} + #define TEST(name, flags) \ { #name , test_routerkeys_ ## name, (flags), NULL, NULL } @@ -524,6 +592,8 @@ struct testcase_t routerkeys_tests[] = { 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 }; From a2f317913fc48975381696cc381d5f78e25e92b6 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 28 May 2015 10:41:43 -0400 Subject: [PATCH 06/28] Implement proposal 228: cross-certification with onion keys Routers now use TAP and ntor onion keys to sign their identity keys, and put these signatures in their descriptors. That allows other parties to be confident that the onion keys are indeed controlled by the router that generated the descriptor. --- src/or/router.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/or/router.c b/src/or/router.c index 2087a25fca..97c2b8398d 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2628,6 +2628,8 @@ router_dump_router_to_string(routerinfo_t *router, 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); return output; } From eacbe03c71a9ddc7c3745ef8da88580a60021201 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 2 Oct 2014 15:34:23 -0400 Subject: [PATCH 07/28] Key-pinning back-end for 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 a # are ignored. Lines beginning with @ are reserved for future extensions. --- src/or/include.am | 2 + src/or/keypin.c | 391 +++++++++++++++++++++++++++++++++++++++++ src/or/keypin.h | 44 +++++ src/test/include.am | 1 + src/test/test.c | 2 + src/test/test_keypin.c | 255 +++++++++++++++++++++++++++ 6 files changed, 695 insertions(+) create mode 100644 src/or/keypin.c create mode 100644 src/or/keypin.h create mode 100644 src/test/test_keypin.c diff --git a/src/or/include.am b/src/or/include.am index a85b0dde05..68f091b3a9 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -53,6 +53,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 \ @@ -161,6 +162,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 \ diff --git a/src/or/keypin.c b/src/or/keypin.c new file mode 100644 index 0000000000..87e49cdff6 --- /dev/null +++ b/src/or/keypin.c @@ -0,0 +1,391 @@ +/* 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 +#endif +#ifdef HAVE_FCNTL_H +#include +#endif + +#ifdef _WIN32 +#include +#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 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 rsa_id_digest or for ed25519 key + * ed25519_id_key. 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) +{ + 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. */ + 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 ent 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 rsa_id_digest. 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 fname. 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, 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 rsa_id_digest and + * ed25519_id_key. */ +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 size-byte region at data. 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 fname. 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 cp. The input + * does not need to be NUL-terminated, but it does 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..16a0775208 --- /dev/null +++ b/src/or/keypin.h @@ -0,0 +1,44 @@ +/* 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_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/test/include.am b/src/test/include.am index 44f63a7230..b433fc50a9 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -44,6 +44,7 @@ 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_logging.c \ src/test/test_microdesc.c \ src/test/test_nodelist.c \ diff --git a/src/test/test.c b/src/test/test.c index 0524a6978f..cd8aa3f014 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1136,6 +1136,7 @@ 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 logging_tests[]; extern struct testcase_t microdesc_tests[]; extern struct testcase_t nodelist_tests[]; @@ -1181,6 +1182,7 @@ struct testgroup_t testgroups[] = { { "extorport/", extorport_tests }, { "hs/", hs_tests }, { "introduce/", introduce_tests }, + { "keypin/", keypin_tests }, { "nodelist/", nodelist_tests }, { "oom/", oom_tests }, { "options/", options_tests }, 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 +}; + From 592a43910706a67048c7d05e45d35dc79712820a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 8 Oct 2014 08:32:00 -0400 Subject: [PATCH 08/28] Tie key-pinning logic into directory authority operation With this patch: * Authorities load the key-pinning log at startup. * Authorities open a key-pinning log for writing at startup. * Authorities reject any router with an ed25519 key where they have previously seen that ed25519 key with a different RSA key, or vice versa. * Authorities warn about, but *do not* reject, RSA-only descriptors when the RSA key has previously gone along with an Ed25519 key. (We should make this a 'reject' too, but we can't do that until we're sure there's no legit reason to downgrade to 0.2.5.) --- src/or/dirserv.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ src/or/keypin.c | 28 +++++++++++++++++++++ src/or/keypin.h | 2 ++ src/or/main.c | 19 ++++++++++++++ src/or/router.c | 3 ++- 5 files changed, 115 insertions(+), 1 deletion(-) diff --git a/src/or/dirserv.c b/src/or/dirserv.c index e5a5b54303..f26a6bb216 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 router 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 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); diff --git a/src/or/keypin.c b/src/or/keypin.c index 87e49cdff6..7b0c0c7dcf 100644 --- a/src/or/keypin.c +++ b/src/or/keypin.c @@ -44,6 +44,9 @@ 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(); @@ -99,6 +102,28 @@ HT_GENERATE2(edmap, keypin_ent_st, edmap_node, keypin_ent_hash_ed, 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)); @@ -127,6 +152,9 @@ keypin_check_and_add(const uint8_t *rsa_id_digest, } /* 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); diff --git a/src/or/keypin.h b/src/or/keypin.h index 16a0775208..2a5b3f1786 100644 --- a/src/or/keypin.h +++ b/src/or/keypin.h @@ -8,6 +8,8 @@ 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); diff --git a/src/or/main.c b/src/or/main.c index 8b82a31d7a..70d075f432 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" @@ -1998,6 +1999,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."); @@ -2707,6 +2725,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(); diff --git a/src/or/router.c b/src/or/router.c index 97c2b8398d..242ec055c6 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -2343,7 +2343,8 @@ router_dump_router_to_string(routerinfo_t *router, !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"); + "ed25519 key chain %d", + router->signing_key_cert->signing_key_included); goto err; } } From 006b7ce5ff2a90a517e2842fcdd716ed60a90f14 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 8 Oct 2014 09:13:09 -0400 Subject: [PATCH 09/28] Fix the position-check for ed25519 certs to work with annotations When there are annotations on a router descriptor, the ed25519-identity element won't be at position 0 or 1; it will be at router+1 or router-1. This patch also adds a missing smartlist function to search a list for an item with a particular pointer. --- src/common/container.c | 13 +++++++++++++ src/common/container.h | 1 + src/or/routerparse.c | 7 +++++-- src/test/test_containers.c | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) 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 element is the same pointer as an element of sl, 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 sl has some element E such that * !strcasecmp(E,element) */ diff --git a/src/common/container.h b/src/common/container.h index d07697f019..64c9b7813e 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/or/routerparse.c b/src/or/routerparse.c index b3bb565e58..98104f4c08 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -1195,6 +1195,7 @@ 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)); @@ -1345,8 +1346,10 @@ router_parse_entry_from_string(const char *s, const char *end, } if (ed_sig_tok) { tor_assert(ed_cert_tok && cc_tap_tok && cc_ntor_tok); - if (ed_cert_tok != smartlist_get(tokens, 0) && - ed_cert_tok != smartlist_get(tokens, 1)) { + 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; } 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 @@ -495,6 +495,43 @@ test_container_smartlist_join(void *arg) tor_free(joined); } +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) { @@ -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), From 24b720a984cc6c05ebc51d0c699a36119c518ee4 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 8 Oct 2014 11:33:59 -0400 Subject: [PATCH 10/28] Include ed25519 keys in microdescriptors. --- src/or/dirvote.c | 19 ++++++-- src/or/dirvote.h | 6 ++- src/or/microdesc.c | 1 + src/or/or.h | 2 + src/or/routerparse.c | 22 +++++++++ src/test/test_microdesc.c | 93 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 138 insertions(+), 5 deletions(-) diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 7a5154dae5..94b4e5136a 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -17,6 +17,7 @@ #include "routerlist.h" #include "routerparse.h" #include "entrynodes.h" /* needed for guardfraction methods */ +#include "torcert.h" /** * \file dirvote.c @@ -3486,9 +3487,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 +3571,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..edd5751090 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,10 @@ * 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 + /** 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/microdesc.c b/src/or/microdesc.c index 0511e870d1..ee48f6a419 100644 --- a/src/or/microdesc.c +++ b/src/or/microdesc.c @@ -738,6 +738,7 @@ microdesc_free_(microdesc_t *md, const char *fname, int lineno) if (md->onion_pkey) crypto_pk_free(md->onion_pkey); tor_free(md->onion_curve25519_pkey); + tor_free(md->ed25519_identity_pkey); if (md->body && md->saved_location != SAVED_IN_CACHE) tor_free(md->body); diff --git a/src/or/or.h b/src/or/or.h index d030189b61..ba36ad87b0 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2234,6 +2234,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 */ diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 98104f4c08..e1bc295ef9 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -72,6 +72,7 @@ typedef enum { K_CLIENT_VERSIONS, K_SERVER_VERSIONS, K_OR_ADDRESS, + K_ID, K_P, K_P6, K_R, @@ -503,6 +504,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 ), @@ -4372,6 +4374,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) { diff --git a/src/test/test_microdesc.c b/src/test/test_microdesc.c index fb3df77edc..3c22e1809a 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,25 @@ 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); + + 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); From df05e195ee64d7ed1b5a1b5d74c5868683788ba2 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 8 Oct 2014 14:43:33 -0400 Subject: [PATCH 11/28] Add trunnel-generated items for link handshake code. This includes the link handshake variations for proposal220. We'll use this for testing first, and then use it to extend our current code to support prop220. --- scripts/codegen/run_trunnel.sh | 4 +- src/trunnel/include.am | 3 + src/trunnel/link_handshake.c | 1885 ++++++++++++++++++++++++++++ src/trunnel/link_handshake.h | 654 ++++++++++ src/trunnel/link_handshake.trunnel | 57 + 5 files changed, 2601 insertions(+), 2 deletions(-) create mode 100644 src/trunnel/link_handshake.c create mode 100644 src/trunnel/link_handshake.h create mode 100644 src/trunnel/link_handshake.trunnel 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/src/trunnel/include.am b/src/trunnel/include.am index 6e7851aafe..2d8c051f19 100644 --- a/src/trunnel/include.am +++ b/src/trunnel/include.am @@ -11,11 +11,13 @@ 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/ed25519_cert.c \ + src/trunnel/link_handshake.c \ src/trunnel/pwbox.c TRUNNELHEADERS = \ @@ -23,6 +25,7 @@ TRUNNELHEADERS = \ 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) diff --git a/src/trunnel/link_handshake.c b/src/trunnel/link_handshake.c new file mode 100644 index 0000000000..9630d1340d --- /dev/null +++ b/src/trunnel/link_handshake.c @@ -0,0 +1,1885 @@ +/* link_handshake.c -- generated by Trunnel v1.4-pre. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#include +#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: + if (result >= 0) result = -1; + 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..109fe8d065 --- /dev/null +++ b/src/trunnel/link_handshake.h @@ -0,0 +1,654 @@ +/* link_handshake.h -- generated by by Trunnel v1.4-pre. + * https://gitweb.torproject.org/trunnel.git + * You probably shouldn't edit this file. + */ +#ifndef TRUNNEL_LINK_HANDSHAKE_H +#define TRUNNEL_LINK_HANDSHAKE_H + +#include +#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[]; +} From b75361c5ed717cde787c1b4f36e8fb51ccfddc2b Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 8 Oct 2014 15:39:34 -0400 Subject: [PATCH 12/28] Start testing cell encoders/processers for the v3 handshake. An earlier version of these tests was broken; now they're a nicer, more robust, more black-box set of tests. The key is to have each test check a handshake message that is wrong in _one_ way. --- src/common/tortls.c | 4 +- src/common/tortls.h | 4 +- src/or/channeltls.c | 15 +- src/or/channeltls.h | 8 + src/or/connection_or.c | 18 +- src/or/connection_or.h | 12 +- src/test/include.am | 3 + src/test/test.c | 2 + src/test/test_link_handshake.c | 637 +++++++++++++++++++++++++++++++++ 9 files changed, 675 insertions(+), 28 deletions(-) create mode 100644 src/test/test_link_handshake.c diff --git a/src/common/tortls.c b/src/common/tortls.c index 97dca4d478..62e32c5dae 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -1023,8 +1023,8 @@ tor_tls_cert_get_key(tor_x509_cert_t *cert) /** Return true iff the other side of tls has authenticated to us, and * the key certified in cert is the same as the key they used to do it. */ -int -tor_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_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; diff --git a/src/common/tortls.h b/src/common/tortls.h index 28a73e9ae4..11ef09fed4 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -132,8 +132,8 @@ int tor_tls_get_my_certs(int server, 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_x509_cert_t *cert); -int tor_tls_cert_matches_key(const tor_tls_t *tls, - const 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_x509_cert_t *cert, const tor_x509_cert_t *signing_cert, diff --git a/src/or/channeltls.c b/src/or/channeltls.c index 10fcb281db..af7f474349 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" @@ -47,9 +49,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); @@ -91,10 +90,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); @@ -106,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; @@ -1744,7 +1739,7 @@ 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_x509_cert_t *link_cert = NULL; @@ -1946,7 +1941,7 @@ 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; diff --git a/src/or/channeltls.h b/src/or/channeltls.h index 507429420b..69f6e629c4 100644 --- a/src/or/channeltls.h +++ b/src/or/channeltls.h @@ -52,5 +52,13 @@ 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); +#endif + #endif diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 4ab47f0aa8..8602bcbcf4 100644 --- a/src/or/connection_or.c +++ b/src/or/connection_or.c @@ -1318,8 +1318,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; @@ -2012,9 +2012,9 @@ connection_or_write_cell_to_buf(const cell_t *cell, or_connection_t *conn) * conn's outbuf. Right now, this DOES NOT 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]; @@ -2157,8 +2157,8 @@ connection_or_send_versions(or_connection_t *conn, int v3_plus) /** Send a NETINFO cell on conn, 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); @@ -2443,8 +2443,8 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, /** Send an AUTHENTICATE cell on the connection conn. 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/test/include.am b/src/test/include.am index b433fc50a9..2a0c5437ad 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -13,6 +13,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. @@ -45,6 +47,7 @@ src_test_test_SOURCES = \ 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 \ diff --git a/src/test/test.c b/src/test/test.c index cd8aa3f014..dee5c001eb 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1137,6 +1137,7 @@ 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[]; @@ -1183,6 +1184,7 @@ struct testgroup_t testgroups[] = { { "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_link_handshake.c b/src/test/test_link_handshake.c new file mode 100644 index 0000000000..eb72a0c91a --- /dev/null +++ b/src/test/test_link_handshake.c @@ -0,0 +1,637 @@ +/* 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 "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); + tor_free(chan1); + tor_free(chan2); +} + +typedef struct certs_data_s { + or_connection_t *c; + channel_tls_t *chan; + certs_cell_t *ccell; + var_cell_t *cell; +} 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)); + tor_free(d->chan); + 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; + + crypto_pk_t *key1 = NULL, *key2 = NULL; + key1 = pk_generate(2); + key2 = pk_generate(3); + + tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + key1, 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)); + 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) + +#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 } + +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), + + END_OF_TESTCASES +}; + From 55bb7bbafd1272a1bc36a17d89bd2419d59b113a Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 16 Oct 2014 09:07:50 -0400 Subject: [PATCH 13/28] Tests for AUTHENTICATE cell functionality. --- src/common/tortls.c | 8 +- src/common/tortls.h | 4 +- src/or/channel.c | 8 +- src/or/channel.h | 5 +- src/or/channeltls.c | 4 +- src/or/channeltls.h | 2 + src/test/test_link_handshake.c | 261 ++++++++++++++++++++++++++++++++- 7 files changed, 276 insertions(+), 16 deletions(-) diff --git a/src/common/tortls.c b/src/common/tortls.c index 62e32c5dae..e498b2ac23 100644 --- a/src/common/tortls.c +++ b/src/common/tortls.c @@ -2394,8 +2394,8 @@ tor_tls_peer_has_cert(tor_tls_t *tls) } /** Return the peer certificate, or NULL if there isn't one. */ -tor_x509_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); @@ -2820,8 +2820,8 @@ tor_tls_server_got_renegotiate(tor_tls_t *tls) * the v3 handshake to prove that the client knows the TLS secrets for the * connection tls. 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]; diff --git a/src/common/tortls.h b/src/common/tortls.h index 11ef09fed4..1dc4b31b2a 100644 --- a/src/common/tortls.h +++ b/src/common/tortls.h @@ -72,7 +72,7 @@ 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_x509_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, @@ -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. */ 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 af7f474349..af63444f15 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -90,8 +90,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_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); @@ -2037,7 +2035,7 @@ 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]; diff --git a/src/or/channeltls.h b/src/or/channeltls.h index 69f6e629c4..a0df9faac2 100644 --- a/src/or/channeltls.h +++ b/src/or/channeltls.h @@ -58,6 +58,8 @@ STATIC void channel_tls_process_certs_cell(var_cell_t *cell, 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/test/test_link_handshake.c b/src/test/test_link_handshake.c index eb72a0c91a..e164022ef7 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -12,6 +12,7 @@ #include "connection_or.h" #include "channeltls.h" #include "link_handshake.h" +#include "scheduler.h" #include "test.h" @@ -582,6 +583,243 @@ AUTHCHALLENGE_FAIL(truncated, 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; +} 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)); + tor_free(d->chan2); + } + 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); + + crypto_pk_t *key1 = NULL, *key2 = NULL; + key1 = pk_generate(2); + key2 = pk_generate(3); + tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + key1, 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; + + /* 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]; + int n = crypto_pk_public_checksig( + tor_tls_cert_get_key(d->c2->handshake_state->auth_cert), + (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); +} + +#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, + d->c2->handshake_state->id_cert = NULL) +AUTHENTICATE_FAIL(noauthcert, + 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 } @@ -595,6 +833,10 @@ AUTHCHALLENGE_FAIL(nonzero_circid, 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), @@ -632,6 +874,23 @@ struct testcase_t link_handshake_tests[] = { 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 }; - From 4d1a0ece5c7c45e82206bd0abc3afec1358f8410 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 17 Oct 2014 16:30:42 -0400 Subject: [PATCH 14/28] Fix memory leaks in test_link_handshake.c --- src/test/test_link_handshake.c | 36 +++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index e164022ef7..bfdd6f3d1a 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -179,8 +179,12 @@ test_link_handshake_certs_ok(void *arg) tor_free(cell2); certs_cell_free(cc1); certs_cell_free(cc2); + circuitmux_free(chan1->base_.cmux); tor_free(chan1); + circuitmux_free(chan2->base_.cmux); tor_free(chan2); + crypto_pk_free(key1); + crypto_pk_free(key2); } typedef struct certs_data_s { @@ -188,6 +192,7 @@ typedef struct certs_data_s { channel_tls_t *chan; certs_cell_t *ccell; var_cell_t *cell; + crypto_pk_t *key1, *key2; } certs_data_t; @@ -204,7 +209,10 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj) 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; @@ -228,12 +236,11 @@ recv_certs_setup(const struct testcase_t *test) tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0); d->c->link_proto = 4; - crypto_pk_t *key1 = NULL, *key2 = NULL; - key1 = pk_generate(2); - key2 = pk_generate(3); + d->key1 = pk_generate(2); + d->key2 = pk_generate(3); tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, - key1, key2, 86400), ==, 0); + d->key1, d->key2, 86400), ==, 0); d->ccell = certs_cell_new(); ccc1 = certs_cell_cert_new(); certs_cell_add_certs(d->ccell, ccc1); @@ -460,6 +467,7 @@ recv_authchallenge_cleanup(const struct testcase_t *test, void *obj) 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); } @@ -614,6 +622,7 @@ 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 @@ -630,7 +639,11 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg) 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; @@ -652,11 +665,10 @@ authenticate_data_setup(const struct testcase_t *test) d->c1 = or_connection_new(CONN_TYPE_OR, AF_INET); d->c2 = or_connection_new(CONN_TYPE_OR, AF_INET); - crypto_pk_t *key1 = NULL, *key2 = NULL; - key1 = pk_generate(2); - key2 = pk_generate(3); + d->key1 = pk_generate(2); + d->key2 = pk_generate(3); tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, - key1, key2, 86400), ==, 0); + d->key1, d->key2, 86400), ==, 0); d->c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; d->c1->link_proto = 3; @@ -721,6 +733,7 @@ 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); @@ -744,8 +757,10 @@ test_link_handshake_auth_cell(void *arg) /* 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( - tor_tls_cert_get_key(d->c2->handshake_state->auth_cert), + auth_pubkey, (char*)sig, sizeof(sig), (char*)auth1_getarray_sig(auth1), auth1_getlen_sig(auth1)); tt_int_op(n, ==, 32); @@ -762,6 +777,7 @@ test_link_handshake_auth_cell(void *arg) done: auth1_free(auth1); + crypto_pk_free(auth_pubkey); } #define AUTHENTICATE_FAIL(name, code) \ @@ -800,8 +816,10 @@ test_link_handshake_auth_already_authenticated(void *arg) 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) From b29c1530c7864ddfe382b18f4fc6e88eb46c1595 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 17 Oct 2014 17:06:31 -0400 Subject: [PATCH 15/28] Refactor link handshake cell type implementations to use trunnel Unit tests still pass. --- src/or/channeltls.c | 138 ++++++++++++++++--------------------- src/or/connection_or.c | 153 +++++++++++++++++++++++++---------------- 2 files changed, 155 insertions(+), 136 deletions(-) diff --git a/src/or/channeltls.c b/src/or/channeltls.c index af63444f15..0376e74bcf 100644 --- a/src/or/channeltls.c +++ b/src/or/channeltls.c @@ -24,6 +24,7 @@ #include "connection.h" #include "connection_or.h" #include "control.h" +#include "link_handshake.h" #include "relay.h" #include "router.h" #include "routerlist.h" @@ -1740,13 +1741,14 @@ channel_tls_process_netinfo_cell(cell_t *cell, channel_tls_t *chan) STATIC void channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) { - tor_x509_cert_t *link_cert = NULL; - tor_x509_cert_t *id_cert = NULL; - tor_x509_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); @@ -1776,63 +1778,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_x509_cert_t *cert = tor_x509_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_x509_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_x509_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_x509_cert_free(cert); - ERR("Too many AUTH_1024 certificates"); - } - auth_cert = cert; - } else { - tor_x509_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)) @@ -1881,7 +1861,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 @@ -1908,7 +1888,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; @@ -1922,9 +1902,10 @@ channel_tls_process_certs_cell(var_cell_t *cell, channel_tls_t *chan) } err: - tor_x509_cert_free(id_cert); - tor_x509_cert_free(link_cert); - tor_x509_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 } @@ -1943,7 +1924,7 @@ 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); @@ -1956,7 +1937,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) @@ -1969,19 +1950,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; } @@ -1992,7 +1971,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) { @@ -2006,7 +1985,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, @@ -2019,9 +1998,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 } @@ -2038,7 +2020,7 @@ channel_tls_process_auth_challenge_cell(var_cell_t *cell, channel_tls_t *chan) 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; @@ -2094,11 +2076,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"); { diff --git a/src/or/connection_or.c b/src/or/connection_or.c index 8602bcbcf4..ba30987eab 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" @@ -2279,28 +2280,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); - var_cell_free(cell); - memwipe(challenge, 0, sizeof(challenge)); + r = 0; - return 0; + done: + var_cell_free(cell); + auth_challenge_cell_free(ac); + + return r; } /** Compute the main body of an AUTHENTICATE cell that a client can use @@ -2327,19 +2337,18 @@ 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_x509_cert_t *id_cert=NULL, *link_cert=NULL; @@ -2359,12 +2368,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,12 +2385,10 @@ 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); } { @@ -2396,49 +2401,79 @@ connection_or_compute_authenticate_cell_body(or_connection_t *conn, freecert = tor_tls_get_peer_cert(conn->tls); cert = freecert; } - if (!cert) - return -1; - memcpy(ptr, tor_x509_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_x509_cert_free(freecert); - ptr += 32; } /* 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); - - if (!signing_key) - return V3_AUTH_BODY_LEN; // ptr - out - - { - 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), - d, 32); - if (siglen < 0) - return -1; - - ptr += siglen; - tor_assert(ptr <= out+outlen); - return (int)(ptr - out); + 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 (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)); + + char d[32]; + 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) { + log_warn(LD_OR, "Unable to sign AUTH1 data."); + return -1; + } + + auth1_setlen_sig(auth, siglen); + + 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 conn. Return 0 on From b600b68b209984fa55f1dc0c311f77e2942a8a17 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 22 Oct 2014 14:19:18 -0400 Subject: [PATCH 16/28] Revise makedesc.py: teach it how to emit ed signatures and crosscerts Also, add a trivial ed25519-signed routerinfo to the tests. --- scripts/codegen/makedesc.py | 351 ++++++++++++++++++++++--------- src/test/failing_routerdescs.inc | 46 ++++ src/test/test_dir.c | 6 +- 3 files changed, 300 insertions(+), 103 deletions(-) diff --git a/scripts/codegen/makedesc.py b/scripts/codegen/makedesc.py index 833951945b..0ed1f7e47d 100644 --- a/scripts/codegen/makedesc.py +++ b/scripts/codegen/makedesc.py @@ -14,6 +14,17 @@ import binascii import ctypes import ctypes.util import hashlib +import optparse +import os +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 +35,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 +59,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 +79,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 = "<<<<<>>>>>" +MAGIC2 = "<<<<>>>>" + +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 = "<<<<<>>>>>" + MORE_MAGIC = "<<<<>>>>" args = { "RSA-IDENTITY" : ident_pem, "ONION-KEY" : onion_pem, @@ -81,6 +268,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 +296,72 @@ 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 EX %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) + info = OnDemandKeys() + body = body.format(d=info) + body = info.sign_desc(body) + print_c_string("EX_EI_%s"%name.upper(), body) + + print 'const char {NAME}_FP[] = "{d.RSA_FINGERPRINT_NOSPACE}";'.format( + d=info, NAME=name.upper()) + prnit_c_string("%s_KEY"%name.upper(), d.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): + 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 0: - emit_ri("minimal", + emit_ri("minimal_ed", """\ router fred 127.0.0.1 9001 0 9002 +identity-ed25519 +{d.ED_CERT} signing-key -{RSA-IDENTITY} +{d.RSA_IDENTITY} onion-key -{ONION-KEY} +{d.RSA_ONION_KEY} +ntor-onion-key {d.NTOR_ONION_KEY} +ntor-onion-key-crosscert {d.NTOR_CROSSCERT_SIGN} +{d.NTOR_CROSSCERT} +onion-key-crosscert +{d.RSA_CROSSCERT_ED} published 2014-10-05 12:00:00 bandwidth 1000 1000 1000 reject *:* +router-sig-ed25519 {d.ED_SIGNATURE} router-signature -{RSA-SIGNATURE} +{d.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} -""") - diff --git a/src/test/failing_routerdescs.inc b/src/test/failing_routerdescs.inc index b49d59fd8a..db3fd70fcf 100644 --- a/src/test/failing_routerdescs.inc +++ b/src/test/failing_routerdescs.inc @@ -666,3 +666,49 @@ 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" + ; diff --git a/src/test/test_dir.c b/src/test/test_dir.c index d7b5e1b7c4..20863bf66a 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -352,7 +352,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; @@ -377,6 +377,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, @@ -3168,7 +3170,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), From 79db24b3d5cb845b18d737bbc63510154f6a87c7 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 28 May 2015 10:42:22 -0400 Subject: [PATCH 17/28] Sign extrainfo documents with ed25519 Extrainfo documents are now ed-signed just as are router descriptors, according to proposal 220. This patch also includes some more tests for successful/failing parsing, and fixes a crash bug in ed25519 descriptor parsing. --- scripts/codegen/makedesc.py | 34 +- src/or/or.h | 6 + src/or/router.c | 90 +++- src/or/router.h | 3 +- src/or/routerlist.c | 1 + src/or/routerparse.c | 95 +++- src/test/example_extrainfo.inc | 233 +++++++++ src/test/failing_routerdescs.inc | 855 +++++++++++++++++++++++++++++++ src/test/test_dir.c | 43 ++ 9 files changed, 1318 insertions(+), 42 deletions(-) diff --git a/scripts/codegen/makedesc.py b/scripts/codegen/makedesc.py index 0ed1f7e47d..d4ba21efae 100644 --- a/scripts/codegen/makedesc.py +++ b/scripts/codegen/makedesc.py @@ -16,6 +16,7 @@ import ctypes.util import hashlib import optparse import os +import re import struct import time import UserDict @@ -297,7 +298,7 @@ def signdesc(body, args_out=None): return body.rstrip() def print_c_string(ident, body): - print "static const char EX %s[] =" % ident + print "static const char %s[] =" % ident for line in body.split("\n"): print ' "%s\\n"' %(line) print " ;" @@ -314,9 +315,9 @@ def emit_ei(name, body): body = info.sign_desc(body) print_c_string("EX_EI_%s"%name.upper(), body) - print 'const char {NAME}_FP[] = "{d.RSA_FINGERPRINT_NOSPACE}";'.format( + print 'const char EX_EI_{NAME}_FP[] = "{d.RSA_FINGERPRINT_NOSPACE}";'.format( d=info, NAME=name.upper()) - prnit_c_string("%s_KEY"%name.upper(), d.RSA_IDENTITY) + print_c_string("EX_EI_%s_KEY"%name.upper(), info.RSA_IDENTITY) def analyze(s): fields = {} @@ -330,6 +331,7 @@ def analyze(s): return fields, s def process_file(s): + fields, s = analyze(s) try: name = fields['name'] tp = fields['type'] @@ -343,25 +345,7 @@ def process_file(s): else: raise ValueError("unrecognized type") -if 0: - emit_ri("minimal_ed", - """\ -router fred 127.0.0.1 9001 0 9002 -identity-ed25519 -{d.ED_CERT} -signing-key -{d.RSA_IDENTITY} -onion-key -{d.RSA_ONION_KEY} -ntor-onion-key {d.NTOR_ONION_KEY} -ntor-onion-key-crosscert {d.NTOR_CROSSCERT_SIGN} -{d.NTOR_CROSSCERT} -onion-key-crosscert -{d.RSA_CROSSCERT_ED} -published 2014-10-05 12:00:00 -bandwidth 1000 1000 1000 -reject *:* -router-sig-ed25519 {d.ED_SIGNATURE} -router-signature -{d.RSA_SIGNATURE} -""") +if __name__ == '__main__': + import sys + for fn in sys.argv[1:]: + process_file(open(fn).read()) diff --git a/src/or/or.h b/src/or/or.h index ba36ad87b0..714b1c6ce6 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2004,6 +2004,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. */ @@ -2085,8 +2087,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; diff --git a/src/or/router.c b/src/or/router.c index 242ec055c6..c94667ab05 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -1974,10 +1974,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; @@ -1987,6 +1989,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. */ @@ -1994,6 +2000,9 @@ 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. */ @@ -2319,8 +2328,7 @@ router_dump_router_to_string(routerinfo_t *router, 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; @@ -2457,12 +2465,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 && @@ -2491,7 +2506,7 @@ router_dump_router_to_string(routerinfo_t *router, "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" @@ -2509,9 +2524,7 @@ 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, @@ -2631,6 +2644,7 @@ router_dump_router_to_string(routerinfo_t *router, tor_free(ed_cert_line); tor_free(rsa_tap_cc_line); tor_free(ntor_cc_line); + tor_free(extra_info_line); return output; } @@ -2774,7 +2788,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]; @@ -2784,18 +2799,44 @@ 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) < 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); @@ -2855,6 +2896,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); @@ -2903,7 +2961,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 " @@ -2925,6 +2984,7 @@ 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); return result; diff --git a/src/or/router.h b/src/or/router.h index cb813c6813..695cfd38e5 100644 --- a/src/or/router.h +++ b/src/or/router.h @@ -109,7 +109,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/routerlist.c b/src/or/routerlist.c index dbb93e4a64..b2784ae559 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -2684,6 +2684,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); diff --git a/src/or/routerparse.c b/src/or/routerparse.c index e1bc295ef9..03a5eafd88 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -324,6 +324,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 ), @@ -1398,7 +1400,7 @@ router_parse_entry_from_string(const char *s, const char *end, ntor_cc_cert = tor_cert_parse((const uint8_t*)cc_ntor_tok->object_body, cc_ntor_tok->object_size); - if (!cc_ntor_tok) { + if (!ntor_cc_cert) { log_warn(LD_DIR, "Couldn't parse ntor-onion-key-crosscert cert"); goto err; } @@ -1564,6 +1566,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)) { @@ -1666,6 +1676,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\""); @@ -1678,6 +1689,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])) { @@ -1700,6 +1712,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; 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 db3fd70fcf..e2b72c58a0 100644 --- a/src/test/failing_routerdescs.inc +++ b/src/test/failing_routerdescs.inc @@ -712,3 +712,858 @@ static const char EX_RI_MINIMAL_ED[] = "-----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/test_dir.c b/src/test/test_dir.c index 20863bf66a..5f68c34b39 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -437,8 +437,28 @@ test_dir_routerinfo_parsing(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 @@ -494,20 +514,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); @@ -518,6 +552,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 From 0b819a2a7c8a79a222ffd8af0b239133f9becd7c Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 24 Oct 2014 09:19:49 -0400 Subject: [PATCH 18/28] Enforce more correspondence between ri and ei In particular, they have to list the same ed25519 certificate, and the SHA256 digest of the ei needs to match. --- src/or/routerlist.c | 17 ++++++++++++++++- src/or/torcert.c | 21 +++++++++++++++++++++ src/or/torcert.h | 2 ++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/or/routerlist.c b/src/or/routerlist.c index b2784ae559..a5310519d5 100644 --- a/src/or/routerlist.c +++ b/src/or/routerlist.c @@ -4906,7 +4906,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) @@ -4919,6 +4919,11 @@ 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. */ @@ -4929,6 +4934,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, @@ -4955,6 +4965,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/torcert.c b/src/or/torcert.c index 8fe9c12000..15347307e1 100644 --- a/src/or/torcert.c +++ b/src/or/torcert.c @@ -216,3 +216,24 @@ tor_cert_dup(const tor_cert_t *cert) 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); +} diff --git a/src/or/torcert.h b/src/or/torcert.h index ae9361ff16..4680ca61e6 100644 --- a/src/or/torcert.h +++ b/src/or/torcert.h @@ -64,6 +64,8 @@ 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); #endif From 525383c46d1430abf680133e486fc532050d7123 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 29 Oct 2014 13:36:21 -0400 Subject: [PATCH 19/28] Checkpoint some work on voting on ed25519 identities * Include ed25519 identities in votes * Include "no ed25519 identity" in votes * Include some commented-out code about identity voting. (This will disappear.) * Include some functions for identity voting (These will disappear.) * Enforce uniqueness in ed25519 keys within a vote --- src/or/dirserv.c | 15 +++++++++++++++ src/or/dirvote.c | 32 ++++++++++++++++++++++++++++++++ src/or/dirvote.h | 3 +++ src/or/or.h | 3 +++ src/or/routerparse.c | 27 +++++++++++++++++++++++++++ 5 files changed, 80 insertions(+) diff --git a/src/or/dirserv.c b/src/or/dirserv.c index f26a6bb216..5b103bcf52 100644 --- a/src/or/dirserv.c +++ b/src/or/dirserv.c @@ -1993,6 +1993,16 @@ routerstatus_format_entry(const routerstatus_t *rs, const char *version, smartlist_add_asprintf(chunks, "p %s\n", summary); tor_free(summary); } + + if (format == NS_V3_VOTE && vrs) { + if (tor_mem_is_zero((char*)vrs->ed25519_id, ED25519_PUBKEY_LEN)) { + smartlist_add(chunks, tor_strdup("id ed25519 none\n")); + } else { + char ed_b64[BASE64_DIGEST256_LEN+1]; + digest256_to_base64(ed_b64, (const char*)vrs->ed25519_id); + smartlist_add_asprintf(chunks, "id ed25519 %s\n", ed_b64); + } + } } done: @@ -2815,6 +2825,11 @@ dirserv_generate_networkstatus_vote_obj(crypto_pk_t *private_key, listbadexits, vote_on_hsdirs); + if (ri->signing_key_cert) { + memcpy(vrs->ed25519_id, ri->signing_key_cert->signing_key.pubkey, + ED25519_PUBKEY_LEN); + } + if (digestmap_get(omit_as_sybil, ri->cache_info.identity_digest)) clear_status_flags_on_sybil(rs); diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 94b4e5136a..6c53b3c642 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -782,6 +782,28 @@ networkstatus_check_weights(int64_t Wgg, int64_t Wgd, int64_t Wmg, return berr; } +#if 0 +/** DOCDOC */ +static vote_identity_map_t * +networkstatus_compute_identity_mapping(const smartlist_t *votes) +{ + vote_identity_map_t *map = vote_identity_map_new(); + + SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, vote) { + SMARTLIST_FOREACH_BEGIN(vote->routerstatus_list, + vote_routerstatus_t *, vrs) { + vote_identity_map_add(map, vrs->status.identity_digest, + vrs->has_ed25519_listing ? vrs->ed25519_id : NULL, + vote_sl_idx); + } SMARTLIST_FOREACH_END(vrs); + } SMARTLIST_FOREACH_END(vote); + + vote_identity_map_resolve(map); + + return map; +} +#endif + /** * This function computes the bandwidth weights for consensus method 10. * @@ -1139,6 +1161,9 @@ networkstatus_compute_consensus(smartlist_t *votes, char *params = NULL; char *packages = NULL; int added_weights = 0; +#if 0 + vote_identity_map_t *id_map = NULL; +#endif tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC); tor_assert(total_authorities >= smartlist_len(votes)); @@ -1494,6 +1519,10 @@ networkstatus_compute_consensus(smartlist_t *votes, } ); +#if 0 + id_map = networkstatus_compute_identity_mapping(votes); +#endif + /* Now go through all the votes */ flag_counts = tor_calloc(smartlist_len(flags), sizeof(int)); while (1) { @@ -1981,6 +2010,9 @@ networkstatus_compute_consensus(smartlist_t *votes, done: +#if 0 + vote_identity_map_free(id_map); +#endif tor_free(client_versions); tor_free(server_versions); tor_free(packages); diff --git a/src/or/dirvote.h b/src/or/dirvote.h index edd5751090..0fb2b2599b 100644 --- a/src/or/dirvote.h +++ b/src/or/dirvote.h @@ -89,6 +89,9 @@ /** 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 diff --git a/src/or/or.h b/src/or/or.h index 714b1c6ce6..a41a14e17f 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2356,9 +2356,12 @@ 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 */ 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. */ diff --git a/src/or/routerparse.c b/src/or/routerparse.c index 03a5eafd88..71a0baa0bd 100644 --- a/src/or/routerparse.c +++ b/src/or/routerparse.c @@ -369,6 +369,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 }; @@ -2348,6 +2349,17 @@ 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); @@ -3172,6 +3184,21 @@ 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"); + 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(); From 6c564e6c081514bab56bacec89b1f6f9457a7022 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Wed, 12 Nov 2014 14:29:05 -0500 Subject: [PATCH 20/28] Refactor code that matches up routers with the same identity in votes This makes 'routerstatus collation' into a first-class concept, so we can change how that works for prop220. --- src/or/dircollate.c | 122 ++++++++++++++++++++++++++++++++++++++++++++ src/or/dircollate.h | 44 ++++++++++++++++ src/or/dirvote.c | 98 ++++++++++++----------------------- src/or/include.am | 2 + 4 files changed, 200 insertions(+), 66 deletions(-) create mode 100644 src/or/dircollate.c create mode 100644 src/or/dircollate.h diff --git a/src/or/dircollate.c b/src/or/dircollate.c new file mode 100644 index 0000000000..92f3dcc2e5 --- /dev/null +++ b/src/or/dircollate.c @@ -0,0 +1,122 @@ +/* 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" + +static void dircollator_collate_by_rsa(dircollator_t *dc); + +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; +} + +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(); + + return dc; +} + +void +dircollator_free(dircollator_t *dc) +{ + if (!dc) + return; + + digestmap_free(dc->by_rsa_sha1, tor_free_); + + 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) +{ + dircollator_collate_by_rsa(dc); +} + +static void +dircollator_collate_by_rsa(dircollator_t *dc) +{ + tor_assert(!dc->is_collated); + + dc->all_rsa_sha1_lst = smartlist_new(); + + 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; + + smartlist_sort_digests(dc->all_rsa_sha1_lst); + dc->is_collated = 1; +} + +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_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..073b6117e7 --- /dev/null +++ b/src/or/dircollate.h @@ -0,0 +1,44 @@ +/* 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 dircollator_n_routers(dircollator_t *dc); +vote_routerstatus_t **dircollator_get_votes_for_router(dircollator_t *dc, + int idx); + +#ifdef DIRCOLLATE_PRIVATE +struct dircollator_s { + /**DOCDOC */ + int is_collated; + int n_votes; + int n_authorities; + + int next_vote_num; + digestmap_t *by_rsa_sha1; + + smartlist_t *all_rsa_sha1_lst; +}; +#endif + +#endif diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 6c53b3c642..6a70c13377 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" @@ -782,28 +783,6 @@ networkstatus_check_weights(int64_t Wgg, int64_t Wgd, int64_t Wmg, return berr; } -#if 0 -/** DOCDOC */ -static vote_identity_map_t * -networkstatus_compute_identity_mapping(const smartlist_t *votes) -{ - vote_identity_map_t *map = vote_identity_map_new(); - - SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, vote) { - SMARTLIST_FOREACH_BEGIN(vote->routerstatus_list, - vote_routerstatus_t *, vrs) { - vote_identity_map_add(map, vrs->status.identity_digest, - vrs->has_ed25519_listing ? vrs->ed25519_id : NULL, - vote_sl_idx); - } SMARTLIST_FOREACH_END(vrs); - } SMARTLIST_FOREACH_END(vote); - - vote_identity_map_resolve(map); - - return map; -} -#endif - /** * This function computes the bandwidth weights for consensus method 10. * @@ -1161,9 +1140,7 @@ networkstatus_compute_consensus(smartlist_t *votes, char *params = NULL; char *packages = NULL; int added_weights = 0; -#if 0 - vote_identity_map_t *id_map = NULL; -#endif + dircollator_t *collator = NULL; tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC); tor_assert(total_authorities >= smartlist_len(votes)); @@ -1519,16 +1496,24 @@ networkstatus_compute_consensus(smartlist_t *votes, } ); -#if 0 - id_map = networkstatus_compute_identity_mapping(votes); -#endif + /* 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); /* 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; @@ -1536,23 +1521,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); @@ -1562,29 +1533,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 nothig 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); @@ -1605,7 +1572,7 @@ 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. */ @@ -1619,8 +1586,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; @@ -1644,7 +1612,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; @@ -2010,9 +1978,7 @@ networkstatus_compute_consensus(smartlist_t *votes, done: -#if 0 - vote_identity_map_free(id_map); -#endif + dircollator_free(collator); tor_free(client_versions); tor_free(server_versions); tor_free(packages); diff --git a/src/or/include.am b/src/or/include.am index 68f091b3a9..9245ade64f 100644 --- a/src/or/include.am +++ b/src/or/include.am @@ -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 \ @@ -151,6 +152,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 \ From d4a6b1a4209f0b3995c0f61ad9cbe687e09a6fb0 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 13 Nov 2014 10:03:55 -0500 Subject: [PATCH 21/28] Implement ed25519 identity collation for voting. This is a new collator type that follows proposal 220 for deciding which identities to include. The rule is (approximately): If a identity is listed by more than half of authorities, include it. And include all votes about that node as matching. Otherwise, if an <*,rsa> or identity is listed by more than half of the authorities, and no has been listed, include it. --- src/or/dircollate.c | 153 +++++++++++++++++++++++++++++++++++++++++--- src/or/dircollate.h | 7 +- src/or/dirvote.c | 2 +- src/or/or.h | 1 + 4 files changed, 152 insertions(+), 11 deletions(-) diff --git a/src/or/dircollate.c b/src/or/dircollate.c index 92f3dcc2e5..20dfb357a8 100644 --- a/src/or/dircollate.c +++ b/src/or/dircollate.c @@ -12,9 +12,55 @@ #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, @@ -29,7 +75,24 @@ dircollator_add_routerstatus(dircollator_t *dc, 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; } @@ -45,6 +108,7 @@ dircollator_new(int n_votes, int n_authorities) dc->n_authorities = n_authorities; dc->by_rsa_sha1 = digestmap_new(); + HT_INIT(double_digest_map, &dc->by_both_ids); return dc; } @@ -55,8 +119,20 @@ 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_); + 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); } @@ -75,18 +151,23 @@ dircollator_add_vote(dircollator_t *dc, networkstatus_t *v) } void -dircollator_collate(dircollator_t *dc) +dircollator_collate(dircollator_t *dc, int consensus_method) { - dircollator_collate_by_rsa(dc); + 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) { - tor_assert(!dc->is_collated); - - dc->all_rsa_sha1_lst = smartlist_new(); - const int total_authorities = dc->n_authorities; DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) { @@ -102,8 +183,62 @@ dircollator_collate_by_rsa(dircollator_t *dc) smartlist_add(dc->all_rsa_sha1_lst, (char *)k); } DIGESTMAP_FOREACH_END; - smartlist_sort_digests(dc->all_rsa_sha1_lst); - dc->is_collated = 1; + 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 @@ -116,7 +251,7 @@ 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_rsa_sha1, + 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 index 073b6117e7..9eba37a010 100644 --- a/src/or/dircollate.h +++ b/src/or/dircollate.h @@ -21,13 +21,15 @@ 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); +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; @@ -36,6 +38,9 @@ struct dircollator_s { 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; }; diff --git a/src/or/dirvote.c b/src/or/dirvote.c index 6a70c13377..549dec1131 100644 --- a/src/or/dirvote.c +++ b/src/or/dirvote.c @@ -1502,7 +1502,7 @@ networkstatus_compute_consensus(smartlist_t *votes, dircollator_add_vote(collator, v); } SMARTLIST_FOREACH_END(v); - dircollator_collate(collator); + dircollator_collate(collator, consensus_method); /* Now go through all the votes */ flag_counts = tor_calloc(smartlist_len(flags), sizeof(int)); diff --git a/src/or/or.h b/src/or/or.h index a41a14e17f..b07a596d2e 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -2357,6 +2357,7 @@ typedef struct vote_routerstatus_t { * 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; From 64450c5f775c6453568c5ac218669ff0525dc232 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 1 Mar 2015 14:05:00 +0100 Subject: [PATCH 22/28] Only load master ed25519 secret keys when we absolutely must. --- src/or/routerkeys.c | 88 ++++++++++++++++++++++++++++++++++----------- src/or/routerkeys.h | 1 + 2 files changed, 68 insertions(+), 21 deletions(-) diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index 9ad5a89479..ab12b90445 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -34,6 +34,9 @@ * * If INIT_ED_KEY_MISSING_SECRET_OK is set in flags, and we find a * public key file but no secret key file, return successfully anyway. + * + * If INIT_ED_KEY_OMIT_SECRET is set in flags, 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, @@ -63,6 +66,7 @@ ed_key_init_from_file(const char *fname, uint32_t flags, /* 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; @@ -80,7 +84,7 @@ ed_key_init_from_file(const char *fname, uint32_t flags, /* If it's absent and that's okay, try to read the pubkey. */ int found_public = 0; - if (!have_secret && try_to_load && (flags & INIT_ED_KEY_MISSING_SECRET_OK)) { + if (!have_secret && try_to_load) { tor_free(got_tag); found_public = ed25519_pubkey_read_from_file(&keypair->pubkey, &got_tag, public_fname) == 0; @@ -90,6 +94,10 @@ ed_key_init_from_file(const char *fname, uint32_t flags, } } + /* 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; @@ -274,7 +282,9 @@ load_ed_keys(const or_options_t *options, time_t now) 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; @@ -299,40 +309,76 @@ load_ed_keys(const or_options_t *options, time_t now) /* XXXX use options. */ (void) options; - id = ed_key_init_from_file( - options_get_datadir_fname2(options, "keys", "ed25519_master_id"), - (INIT_ED_KEY_CREATE|INIT_ED_KEY_SPLIT| - INIT_ED_KEY_MISSING_SECRET_OK| - INIT_ED_KEY_EXTRA_STRONG), - LOG_WARN, NULL, 0, 0, 0, NULL); - if (!id) - FAIL("Missing identity key"); + /* 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 { + sign = ed_key_init_from_file( + options_get_datadir_fname2(options, "keys", "ed25519_signing"), + INIT_ED_KEY_NEEDCERT| + INIT_ED_KEY_INCLUDE_SIGNING_KEY_IN_CERT, + LOG_INFO, + NULL, 0, 0, CERT_TYPE_ID_SIGNING, &sign_cert); + check_signing_cert = sign_cert; + use_signing = sign; + } - if (!master_signing_key || EXPIRES_SOON(signing_key_cert, 86400/*???*/)) { + 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, 86400/*???*/); + + { + 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; + + id = ed_key_init_from_file( + options_get_datadir_fname2(options, "keys", "ed25519_master_id"), + flags, + LOG_WARN, NULL, 0, 0, 0, NULL); + 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); - const ed25519_keypair_t *sign_with_id = id; - if (master_signing_key) { - flags |= INIT_ED_KEY_REPLACE; /* it's expired, so force-replace it. */ - } - if (tor_mem_is_zero((char*)id->seckey.seckey, sizeof(id->seckey))) { - sign_with_id = NULL; - flags &= ~INIT_ED_KEY_CREATE; - } sign = ed_key_init_from_file( options_get_datadir_fname2(options, "keys", "ed25519_signing"), flags, LOG_WARN, - sign_with_id, now, 30*86400/*XXX option*/, + sign_signing_key_with_id, now, + 30*86400/*XXX option*/, CERT_TYPE_ID_SIGNING, &sign_cert); if (!sign) FAIL("Missing signing key"); use_signing = sign; - } else { - use_signing = master_signing_key; + } 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)); diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h index 5e2ef45314..6c188b745f 100644 --- a/src/or/routerkeys.h +++ b/src/or/routerkeys.h @@ -13,6 +13,7 @@ #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, From 57189acd6f6b56a419d63a7acb012a9b8abac319 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Sun, 1 Mar 2015 14:36:40 +0100 Subject: [PATCH 23/28] # This is a combination of 2 commits. # The first commit's message is: Regenerate ed25519 keys when they will expire soon. Also, have testing-level options to set the lifetimes and expiration-tolerances of all key types, plus a non-testing-level option to set the lifetime of any auto-generated signing key. # The 2nd commit message will be skipped: # fixup! Regenerate ed25519 keys when they will expire soon. --- src/or/config.c | 20 ++++++++++++++++++++ src/or/main.c | 13 +++++++++++++ src/or/or.h | 15 +++++++++++++++ src/or/routerkeys.c | 34 ++++++++++++++++++++++++---------- src/or/routerkeys.h | 2 ++ src/test/test_routerkeys.c | 7 +++++++ 6 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index 5ba8c997fa..34e7e767a0 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -298,6 +298,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 +357,13 @@ static config_var_t option_vars_[] = { V(TestingTorNetwork, BOOL, "0"), V(TestingMinExitFlagThreshold, MEMUNIT, "0"), V(TestingMinFastFlagThreshold, MEMUNIT, "0"), + + V(TestingLinkKeyLifetime, 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"), @@ -3625,8 +3633,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(TestingLinkKeyLifetime); + 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->TestingLinkKeyLifetime < options->TestingAuthKeySlop*2) + REJECT("LinkKeyLifetime is too short."); + if (options->TestingAuthKeyLifetime < options->TestingLinkKeySlop*2) + REJECT("AuthKeyLifetime is too short."); + if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL_TESTING_INITIAL) { REJECT("TestingV3AuthInitialVotingInterval is insanely low."); diff --git a/src/or/main.c b/src/or/main.c index 70d075f432..c4b5af4ab4 100644 --- a/src/or/main.c +++ b/src/or/main.c @@ -134,6 +134,8 @@ time_t time_of_process_start = 0; long stats_n_seconds_working = 0; /** When do we next launch DNS wildcarding checks? */ static time_t time_to_check_for_correct_dns = 0; +/** When do we next make sure our Ed25519 keys aren't about to expire? */ +static time_t time_to_check_ed_keys = 0; /** How often will we honor SIGNEWNYM requests? */ #define MAX_SIGNEWNYM_RATE 10 @@ -1280,6 +1282,17 @@ 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) { + 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) { update_all_descriptor_downloads(now); diff --git a/src/or/or.h b/src/or/or.h index b07a596d2e..d45e19ad8d 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -4261,6 +4261,21 @@ 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 TestingLinkKeyLifetime; + /** 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. */ diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index ab12b90445..2482f59421 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -306,9 +306,6 @@ load_ed_keys(const or_options_t *options, time_t now) /* XXXX support encrypted identity keys fully */ - /* XXXX use options. */ - (void) options; - /* First try to get the signing key to see how it is. */ if (master_signing_key) { check_signing_cert = signing_key_cert; @@ -329,7 +326,7 @@ load_ed_keys(const or_options_t *options, time_t now) EXPIRES_SOON(check_signing_cert, 0); const int want_new_signing_key = need_new_signing_key || - EXPIRES_SOON(check_signing_cert, 86400/*???*/); + EXPIRES_SOON(check_signing_cert, options->TestingSigningKeySlop); { uint32_t flags = @@ -365,7 +362,7 @@ load_ed_keys(const or_options_t *options, time_t now) options_get_datadir_fname2(options, "keys", "ed25519_signing"), flags, LOG_WARN, sign_signing_key_with_id, now, - 30*86400/*XXX option*/, + options->SigningKeyLifetime, CERT_TYPE_ID_SIGNING, &sign_cert); if (!sign) FAIL("Missing signing key"); @@ -383,18 +380,22 @@ 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, 7200/*???*/)) { + if (!current_link_key || + EXPIRES_SOON(link_key_cert, options->TestingLinkKeySlop)) { link = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT, - now, 2*86400/*XXX option??*/, + now, + options->TestingLinkKeyLifetime, CERT_TYPE_SIGNING_LINK, &link_cert); if (!link) FAIL("Can't create link key"); } - if (!current_auth_key || EXPIRES_SOON(auth_key_cert, 7200)/*???*/) { + if (!current_auth_key || + EXPIRES_SOON(auth_key_cert, options->TestingAuthKeySlop)) { auth = ed_key_new(use_signing, INIT_ED_KEY_NEEDCERT, - now, 2*86400/*XXX option??*/, + now, + options->TestingAuthKeyLifetime, CERT_TYPE_SIGNING_AUTH, &auth_cert); if (!auth) @@ -434,9 +435,22 @@ load_ed_keys(const or_options_t *options, time_t now) #undef FAIL #undef SET_KEY #undef SET_CERT -#undef EXPIRES_SOON } +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)); +} + +#undef EXPIRES_SOON + const ed25519_public_key_t * get_master_identity_key(void) { diff --git a/src/or/routerkeys.h b/src/or/routerkeys.h index 6c188b745f..0c5042982c 100644 --- a/src/or/routerkeys.h +++ b/src/or/routerkeys.h @@ -55,6 +55,8 @@ int check_tap_onion_key_crosscert(const uint8_t *crosscert, 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); + void routerkeys_free_all(void); #endif diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 2434255b63..49174243ca 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -419,6 +419,13 @@ test_routerkeys_ed_keys_init_all(void *arg) ed25519_keypair_t sign, link, auth; // tor_cert_t *cert_is, *cert_sl, *cert_auth; + options->SigningKeyLifetime = 30*86400; + options->TestingAuthKeyLifetime = 2*86400; + options->TestingLinkKeyLifetime = 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")); From 32f59d73372f5843ceb305a9d58387573d90f4f6 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 28 May 2015 10:44:09 -0400 Subject: [PATCH 24/28] Regenerate ed25519 keys when they will expire soon. Also, have testing-level options to set the lifetimes and expiration-tolerances of all key types, plus a non-testing-level option to set the lifetime of any auto-generated signing key. --- src/or/config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/or/config.c b/src/or/config.c index 34e7e767a0..07451b321e 100644 --- a/src/or/config.c +++ b/src/or/config.c @@ -3643,9 +3643,9 @@ 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("LinkKeyLifetime is too short."); + REJECT("TestingLinkKeyLifetime is too short."); if (options->TestingAuthKeyLifetime < options->TestingLinkKeySlop*2) - REJECT("AuthKeyLifetime is too short."); + REJECT("TestingAuthKeyLifetime is too short."); if (options->TestingV3AuthInitialVotingInterval < MIN_VOTE_INTERVAL_TESTING_INITIAL) { From 3bee74c6d115131f4850a07a5c12db21ae6f3193 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 28 May 2015 10:47:42 -0400 Subject: [PATCH 25/28] 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 07451b321e..adda5287f9 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 c4b5af4ab4..4fac17a59c 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 d45e19ad8d..6b3ce77713 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 c94667ab05..1e433ed469 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 2482f59421..b90cc73fda 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 @@ -471,12 +519,6 @@ get_master_signing_key_cert(void) return signing_key_cert; } -const ed25519_keypair_t * -get_current_link_keypair(void) -{ - return current_link_key; -} - const ed25519_keypair_t * get_current_auth_keypair(void) { @@ -484,9 +526,9 @@ get_current_auth_keypair(void) } 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 *sign_out. */ @@ -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 0c5042982c..b45a22ac12 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 15347307e1..e2ddffd7a9 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 ed_key as the + * master ed25519 identity key for the RSA identity key rsa_key. + * Allocates and stores the encoded certificate in *cert, 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 4680ca61e6..b67dc525a2 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 49174243ca..06fc4ee217 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 From 8f15423b76557b8401aee28dafca810b512bd0e8 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Fri, 15 May 2015 11:09:10 -0400 Subject: [PATCH 26/28] Do not allocate our ed-link crosscert till after tls ctx We need this to prevent some annoying chutney crash-at-starts --- src/or/router.c | 10 ++++++++-- src/or/routerkeys.c | 7 +++---- src/test/test_routerkeys.c | 6 ++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/or/router.c b/src/or/router.c index 1e433ed469..00cd0578c6 100644 --- a/src/or/router.c +++ b/src/or/router.c @@ -866,8 +866,7 @@ init_keys(void) } /* 1d. Load all ed25519 keys */ - if (load_ed_keys(options,now) < 0 || - generate_ed_link_cert(options,now)) + if (load_ed_keys(options,now) < 0) return -1; /* 2. Read onion key. Make it if none is found. */ @@ -935,6 +934,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(); diff --git a/src/or/routerkeys.c b/src/or/routerkeys.c index b90cc73fda..556ab45732 100644 --- a/src/or/routerkeys.c +++ b/src/or/routerkeys.c @@ -418,9 +418,6 @@ load_ed_keys(const or_options_t *options, time_t now) 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); @@ -438,8 +435,10 @@ 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) + 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); diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 06fc4ee217..26f9701f49 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -446,6 +446,7 @@ test_routerkeys_ed_keys_init_all(void *arg) 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()); @@ -460,6 +461,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* 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)); @@ -468,6 +470,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* 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())); @@ -481,6 +484,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* 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())); @@ -494,6 +498,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* 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())); @@ -511,6 +516,7 @@ test_routerkeys_ed_keys_init_all(void *arg) 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())); From 277c9a3580db20a9814227531431433a37fe55b8 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Tue, 26 May 2015 16:09:34 -0400 Subject: [PATCH 27/28] Note some functions that should move or be merged --- src/common/crypto.c | 4 ++++ src/common/crypto_ed25519.h | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/common/crypto.c b/src/common/crypto.c index 05dc43213c..2ccff80c7b 100644 --- a/src/common/crypto.c +++ b/src/common/crypto.c @@ -2924,6 +2924,7 @@ base64_decode(char *dest, size_t destlen, const char *src, size_t srclen) /** Base64 encode DIGEST_LINE bytes from digest, remove the trailing = * and newline characters, and store the nul-terminated result in the first * BASE64_DIGEST_LEN+1 bytes of d64. */ +/* XXXX unify with crypto_format.c code */ int digest_to_base64(char *d64, const char *digest) { @@ -2937,6 +2938,7 @@ digest_to_base64(char *d64, const char *digest) /** Given a base64 encoded, nul-terminated digest in d64 (without * trailing newline or = characters), decode it and store the result in the * first DIGEST_LEN bytes at digest. */ +/* XXXX unify with crypto_format.c code */ int digest_from_base64(char *digest, const char *d64) { @@ -2962,6 +2964,7 @@ digest_from_base64(char *digest, const char *d64) /** Base64 encode DIGEST256_LINE bytes from digest, remove the * trailing = and newline characters, and store the nul-terminated result in * the first BASE64_DIGEST256_LEN+1 bytes of d64. */ +/* XXXX unify with crypto_format.c code */ int digest256_to_base64(char *d64, const char *digest) { @@ -2975,6 +2978,7 @@ digest256_to_base64(char *d64, const char *digest) /** Given a base64 encoded, nul-terminated digest in d64 (without * trailing newline or = characters), decode it and store the result in the * first DIGEST256_LEN bytes at digest. */ +/* XXXX unify with crypto_format.c code */ int digest256_from_base64(char *digest, const char *d64) { diff --git a/src/common/crypto_ed25519.h b/src/common/crypto_ed25519.h index 79b3db8f9a..8ffb9f26f3 100644 --- a/src/common/crypto_ed25519.h +++ b/src/common/crypto_ed25519.h @@ -88,13 +88,15 @@ int ed25519_public_blind(ed25519_public_key_t *out, const ed25519_public_key_t *inp, const uint8_t *param); -#define ED25519_BASE64_LEN 43 +/* 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, From 5eb584e2e91bd5d6d204b9bb62a95c0edf43ff71 Mon Sep 17 00:00:00 2001 From: Nick Mathewson Date: Thu, 28 May 2015 10:18:42 -0400 Subject: [PATCH 28/28] Document some ed25519 key options --- doc/tor.1.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/doc/tor.1.txt b/doc/tor.1.txt index e136bd0f7e..5f800944ea 100644 --- a/doc/tor.1.txt +++ b/doc/tor.1.txt @@ -1837,6 +1837,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 ------------------------ @@ -2319,6 +2326,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 -------