diff options
author | Nick Mathewson <nickm@torproject.org> | 2008-08-19 15:41:28 +0000 |
---|---|---|
committer | Nick Mathewson <nickm@torproject.org> | 2008-08-19 15:41:28 +0000 |
commit | 24f1d29be17eef10c308049f484af1fc85a10696 (patch) | |
tree | c22c3879e8f5e730fbfa53c48549d2723f9b2c46 /src/or/rendcommon.c | |
parent | 0711408c2280b9b5c284061a81c9e34836429e7f (diff) | |
download | tor-24f1d29be17eef10c308049f484af1fc85a10696.tar.gz tor-24f1d29be17eef10c308049f484af1fc85a10696.zip |
Apply proposal 121 patch 3, with minor tweaks and a few comments.
svn:r16598
Diffstat (limited to 'src/or/rendcommon.c')
-rw-r--r-- | src/or/rendcommon.c | 279 |
1 files changed, 230 insertions, 49 deletions
diff --git a/src/or/rendcommon.c b/src/or/rendcommon.c index 7361903ac9..7cb4a8b0ed 100644 --- a/src/or/rendcommon.c +++ b/src/or/rendcommon.c @@ -150,15 +150,11 @@ rend_compute_v2_desc_id(char *desc_id_out, const char *service_id, return 0; } -/* Encode the introduction points in <b>desc</b>, optionally encrypt them with - * an optional <b>descriptor_cookie</b> of length REND_DESC_COOKIE_LEN, - * encode it in base64, and write it to a newly allocated string, and write a - * pointer to it to *<b>ipos_base64</b>. Return 0 for success, -1 - * otherwise. */ +/** Encode the introduction points in <b>desc</b> and write the result to a + * newly allocated string pointed to by <b>encoded</b>. Return 0 for + * success, -1 otherwise. */ static int -rend_encode_v2_intro_points(char **ipos_base64, - rend_service_descriptor_t *desc, - const char *descriptor_cookie) +rend_encode_v2_intro_points(char **encoded, rend_service_descriptor_t *desc) { size_t unenc_len; char *unenc = NULL; @@ -166,7 +162,6 @@ rend_encode_v2_intro_points(char **ipos_base64, int i; int r = -1; /* Assemble unencrypted list of introduction points. */ - *ipos_base64 = NULL; unenc_len = smartlist_len(desc->intro_nodes) * 1000; /* too long, but ok. */ unenc = tor_malloc_zero(unenc_len); for (i = 0; i < smartlist_len(desc->intro_nodes); i++) { @@ -231,37 +226,163 @@ rend_encode_v2_intro_points(char **ipos_base64, } unenc[unenc_written++] = '\n'; unenc[unenc_written++] = 0; - /* If a descriptor cookie is passed, encrypt introduction points. */ - if (descriptor_cookie) { - char *enc = tor_malloc_zero(unenc_written + CIPHER_IV_LEN); - crypto_cipher_env_t *cipher = - crypto_create_init_cipher(descriptor_cookie, 1); - int enclen = crypto_cipher_encrypt_with_iv(cipher, enc, - unenc_written + CIPHER_IV_LEN, - unenc, unenc_written); + *encoded = unenc; + r = 0; + done: + if (r<0) + tor_free(unenc); + return r; +} + +/** Encrypt the encoded introduction points in <b>encoded</b> using + * authorization type 'basic' with <b>client_cookies</b> and write the + * result to a newly allocated string pointed to by <b>encrypted_out</b> of + * length <b>encrypted_len</b>. Return 0 for success, -1 otherwise. */ +static int +rend_encrypt_v2_intro_points_basic(char **encrypted_out, + size_t *encrypted_len_out, + const char *encoded, + smartlist_t *client_cookies) +{ + int r = -1, i, pos, enclen, client_blocks; + size_t len, client_entries_len; + char *enc = NULL, iv[CIPHER_IV_LEN], *client_part = NULL, + session_key[CIPHER_KEY_LEN]; + smartlist_t *encrypted_session_keys = NULL; + crypto_digest_env_t *digest; + crypto_cipher_env_t *cipher; + tor_assert(encoded); + tor_assert(client_cookies && smartlist_len(client_cookies) > 0); + + /* Generate session key. */ + if (crypto_rand(session_key, CIPHER_KEY_LEN) < 0) { + log_warn(LD_REND, "Unable to generate random session key to encrypt " + "introduction point string."); + goto done; + } + + /* Determine length of encrypted introduction points including session + * keys. */ + client_blocks = 1 + ((smartlist_len(client_cookies) - 1) / + REND_BASIC_AUTH_CLIENT_MULTIPLE); + client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE * + REND_BASIC_AUTH_CLIENT_ENTRY_LEN; + len = 2 + client_entries_len + CIPHER_IV_LEN + strlen(encoded); + if (client_blocks >= 256) { + log_warn(LD_REND, "Too many clients in introduction point string."); + goto done; + } + enc = tor_malloc_zero(len); + enc[0] = 0x01; /* type of authorization. */ + enc[1] = (uint8_t)client_blocks; + + /* Encrypt with random session key. */ + cipher = crypto_create_init_cipher(session_key, 1); + enclen = crypto_cipher_encrypt_with_iv(cipher, + enc + 2 + client_entries_len, + CIPHER_IV_LEN + strlen(encoded), encoded, strlen(encoded)); + crypto_free_cipher_env(cipher); + if (enclen < 0) { + log_warn(LD_REND, "Could not encrypt introduction point string."); + goto done; + } + memcpy(iv, enc + 2 + client_entries_len, CIPHER_IV_LEN); + + /* Encrypt session key for cookies, determine client IDs, and put both + * in a smartlist. */ + encrypted_session_keys = smartlist_create(); + SMARTLIST_FOREACH_BEGIN(client_cookies, const char *, cookie) { + client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN); + /* Encrypt session key. */ + cipher = crypto_create_init_cipher(cookie, 1); + if (crypto_cipher_encrypt(cipher, client_part + + REND_BASIC_AUTH_CLIENT_ID_LEN, + session_key, CIPHER_KEY_LEN) < 0) { + log_warn(LD_REND, "Could not encrypt session key for client."); + crypto_free_cipher_env(cipher); + tor_free(client_part); + goto done; + } crypto_free_cipher_env(cipher); - if (enclen < 0) { - log_warn(LD_REND, "Could not encrypt introduction point string."); - tor_free(enc); + + /* Determine client ID. */ + digest = crypto_new_digest_env(); + crypto_digest_add_bytes(digest, cookie, REND_DESC_COOKIE_LEN); + crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN); + crypto_digest_get_digest(digest, client_part, + REND_BASIC_AUTH_CLIENT_ID_LEN); + crypto_free_digest_env(digest); + + /* Put both together. */ + smartlist_add(encrypted_session_keys, client_part); + } SMARTLIST_FOREACH_END(cookie); + + /* Add some fake client IDs and encrypted session keys. */ + for (i = (smartlist_len(client_cookies) - 1) % + REND_BASIC_AUTH_CLIENT_MULTIPLE; + i < REND_BASIC_AUTH_CLIENT_MULTIPLE - 1; i++) { + client_part = tor_malloc_zero(REND_BASIC_AUTH_CLIENT_ENTRY_LEN); + if (crypto_rand(client_part, REND_BASIC_AUTH_CLIENT_ENTRY_LEN) < 0) { + log_warn(LD_REND, "Unable to generate fake client entry."); + tor_free(client_part); goto done; } - /* Replace original string with the encrypted one. */ - tor_free(unenc); - unenc = enc; - unenc_written = enclen; + smartlist_add(encrypted_session_keys, client_part); } - /* Base64-encode introduction points. */ - *ipos_base64 = tor_malloc_zero(unenc_written * 2); - if (base64_encode(*ipos_base64, unenc_written * 2, unenc, unenc_written)<0) { - log_warn(LD_REND, "Could not encode introduction point string to " - "base64."); + /* Sort smartlist and put elements in result in order. */ + smartlist_sort_digests(encrypted_session_keys); + pos = 2; + SMARTLIST_FOREACH(encrypted_session_keys, const char *, entry, { + memcpy(enc + pos, entry, REND_BASIC_AUTH_CLIENT_ENTRY_LEN); + pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN; + }); + *encrypted_out = enc; + *encrypted_len_out = len; + enc = NULL; /* prevent free. */ + r = 0; + done: + tor_free(enc); + if (encrypted_session_keys) { + SMARTLIST_FOREACH(encrypted_session_keys, char *, d, tor_free(d);); + smartlist_free(encrypted_session_keys); + } + return r; +} + +/** Encrypt the encoded introduction points in <b>encoded</b> using + * authorization type 'stealth' with <b>descriptor_cookie</b> of length + * REND_DESC_COOKIE_LEN and write the result to a newly allocated string + * pointed to by <b>encrypted</b> of length <b>encrypted_len</b>. Return 0 + * for success, -1 otherwise. */ +static int +rend_encrypt_v2_intro_points_stealth(char **encrypted_out, + size_t *encrypted_len_out, + const char *encoded, + const char *descriptor_cookie) +{ + int r = -1, enclen; + crypto_cipher_env_t *cipher; + char *enc; + tor_assert(encoded); + tor_assert(descriptor_cookie); + + enc = tor_malloc_zero(1 + CIPHER_IV_LEN + strlen(encoded)); + enc[0] = 0x02; /* Auth type */ + cipher = crypto_create_init_cipher(descriptor_cookie, 1); + enclen = crypto_cipher_encrypt_with_iv(cipher, enc + 1, + CIPHER_IV_LEN+strlen(encoded), + encoded, strlen(encoded)); + crypto_free_cipher_env(cipher); + if (enclen < 0) { + log_warn(LD_REND, "Could not encrypt introduction point string."); goto done; } + *encrypted_out = enc; + *encrypted_len_out = enclen; + enc = NULL; /* prevent free */ r = 0; done: - if (r<0) - tor_free(*ipos_base64); - tor_free(unenc); + tor_free(enc); return r; } @@ -308,38 +429,97 @@ rend_intro_point_free(rend_intro_point_t *intro) } /** Encode a set of rend_encoded_v2_service_descriptor_t's for <b>desc</b> - * at time <b>now</b> using <b>descriptor_cookie</b> (may be <b>NULL</b> if - * introduction points shall not be encrypted) and <b>period</b> (e.g. 0 - * for the current period, 1 for the next period, etc.) and add them to - * the existing list <b>descs_out</b>; return the number of seconds that - * the descriptors will be found by clients, or -1 if the encoding was not - * successful. */ + * at time <b>now</b> using <b>service_key</b>, depending on + * <b>auth_type</b> a <b>descriptor_cookie</b> and a list of + * <b>client_cookies</b> (which are both <b>NULL</b> if no client + * authorization is performed), and <b>period</b> (e.g. 0 for the current + * period, 1 for the next period, etc.) and add them to the existing list + * <b>descs_out</b>; return the number of seconds that the descriptors will + * be found by clients, or -1 if the encoding was not successful. */ int rend_encode_v2_descriptors(smartlist_t *descs_out, rend_service_descriptor_t *desc, time_t now, - const char *descriptor_cookie, uint8_t period) + uint8_t period, rend_auth_type_t auth_type, + crypto_pk_env_t *client_key, + smartlist_t *client_cookies) { char service_id[DIGEST_LEN]; uint32_t time_period; - char *ipos_base64 = NULL; + char *ipos_base64 = NULL, *ipos = NULL, *ipos_encrypted = NULL, + *descriptor_cookie = NULL; + size_t ipos_len = 0, ipos_encrypted_len = 0; int k; uint32_t seconds_valid; + crypto_pk_env_t *service_key = auth_type == REND_STEALTH_AUTH ? + client_key : desc->pk; + tor_assert(service_key); + if (auth_type == REND_STEALTH_AUTH) { + descriptor_cookie = smartlist_get(client_cookies, 0); + tor_assert(descriptor_cookie); + } if (!desc) { log_warn(LD_REND, "Could not encode v2 descriptor: No desc given."); return -1; } /* Obtain service_id from public key. */ - crypto_pk_get_digest(desc->pk, service_id); + crypto_pk_get_digest(service_key, service_id); /* Calculate current time-period. */ time_period = get_time_period(now, period, service_id); /* Determine how many seconds the descriptor will be valid. */ seconds_valid = period * REND_TIME_PERIOD_V2_DESC_VALIDITY + get_seconds_valid(now, service_id); /* Assemble, possibly encrypt, and encode introduction points. */ - if (smartlist_len(desc->intro_nodes) > 0 && - rend_encode_v2_intro_points(&ipos_base64, desc, descriptor_cookie) < 0) { - log_warn(LD_REND, "Encoding of introduction points did not succeed."); - return -1; + if (smartlist_len(desc->intro_nodes) > 0) { + if (rend_encode_v2_intro_points(&ipos, desc) < 0) { + log_warn(LD_REND, "Encoding of introduction points did not succeed."); + return -1; + } + switch (auth_type) { + case REND_NO_AUTH: + ipos_len = strlen(ipos); + break; + case REND_BASIC_AUTH: + if (rend_encrypt_v2_intro_points_basic(&ipos_encrypted, + &ipos_encrypted_len, ipos, + client_cookies) < 0) { + log_warn(LD_REND, "Encrypting of introduction points did not " + "succeed."); + tor_free(ipos); + return -1; + } + tor_free(ipos); + ipos = ipos_encrypted; + ipos_len = ipos_encrypted_len; + break; + case REND_STEALTH_AUTH: + if (rend_encrypt_v2_intro_points_stealth(&ipos_encrypted, + &ipos_encrypted_len, ipos, + descriptor_cookie) < 0) { + log_warn(LD_REND, "Encrypting of introduction points did not " + "succeed."); + tor_free(ipos); + return -1; + } + tor_free(ipos); + ipos = ipos_encrypted; + ipos_len = ipos_encrypted_len; + break; + default: + log_warn(LD_REND|LD_BUG, "Unrecognized authorization type %d", + (int)auth_type); + tor_free(ipos); + return -1; + } + /* Base64-encode introduction points. */ + ipos_base64 = tor_malloc_zero(ipos_len * 2); + if (base64_encode(ipos_base64, ipos_len * 2, ipos, ipos_len)<0) { + log_warn(LD_REND, "Could not encode introduction point string to " + "base64. length=%d", ipos_len); + tor_free(ipos_base64); + tor_free(ipos); + return -1; + } + tor_free(ipos); } /* Encode REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS descriptors. */ for (k = 0; k < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; k++) { @@ -369,7 +549,7 @@ rend_encode_v2_descriptors(smartlist_t *descs_out, base32_encode(desc_id_base32, sizeof(desc_id_base32), enc->desc_id, DIGEST_LEN); /* PEM-encode the public key */ - if (crypto_pk_write_public_key_to_string(desc->pk, &permanent_key, + if (crypto_pk_write_public_key_to_string(service_key, &permanent_key, &permanent_key_len) < 0) { log_warn(LD_BUG, "Could not write public key to string."); rend_encoded_v2_service_descriptor_free(enc); @@ -437,7 +617,7 @@ rend_encode_v2_descriptors(smartlist_t *descs_out, } if (router_append_dirobj_signature(desc_str + written, desc_len - written, - desc_digest, desc->pk) < 0) { + desc_digest, service_key) < 0) { log_warn(LD_BUG, "Couldn't sign desc."); rend_encoded_v2_service_descriptor_free(enc); goto err; @@ -1085,6 +1265,7 @@ rend_cache_store_v2_desc_as_client(const char *desc, rend_cache_entry_t *e; tor_assert(rend_cache); tor_assert(desc); + (void) descriptor_cookie; /* We don't use it, yet. */ /* Parse the descriptor. */ if (rend_parse_v2_service_descriptor(&parsed, desc_id, &intro_content, &intro_size, &encoded_size, @@ -1103,8 +1284,8 @@ rend_cache_store_v2_desc_as_client(const char *desc, } /* Decode/decrypt introduction points. */ if (intro_content) { - if (rend_decrypt_introduction_points(parsed, descriptor_cookie, - intro_content, intro_size) < 0) { + if (rend_parse_introduction_points(parsed, intro_content, + intro_size) < 0) { log_warn(LD_PROTOCOL,"Couldn't decode/decrypt introduction points."); rend_service_descriptor_free(parsed); tor_free(intro_content); |