Index: /home/karsten/tor/tor-trunk-121-patches/src/or/or.h =================================================================== --- /home/karsten/tor/tor-trunk-121-patches/src/or/or.h (revision 16516) +++ /home/karsten/tor/tor-trunk-121-patches/src/or/or.h (working copy) @@ -651,6 +651,23 @@ * exchanging client authorization between hidden service and client. */ #define REND_DESC_COOKIE_LEN_BASE64 22 +/** Length of client identifier in encrypted introduction points for hidden + * service authorization type 'basic'. */ +#define REND_BASIC_AUTH_CLIENT_ID_LEN 4 + +/** Multiple of the number of clients to which the real number of clients + * is padded with fake clients for hidden service authorization type + * 'basic'. */ +#define REND_BASIC_AUTH_CLIENT_MULTIPLE 16 + +/** Length of client entry consisting of client identifier and encrypted + * session key for hidden service authorization type 'basic'. */ +#define REND_BASIC_AUTH_CLIENT_ENTRY_LEN (REND_BASIC_AUTH_CLIENT_ID_LEN \ + + CIPHER_KEY_LEN) + +/** Maximum size of v2 hidden service descriptors. */ +#define REND_DESC_MAX_SIZE (20 * 1024) + /** Legal characters for use in authorized client names for a hidden * service. */ #define REND_LEGAL_CLIENTNAME_CHARACTERS \ @@ -3907,7 +3924,9 @@ int rend_cache_size(void); 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); int rend_compute_v2_desc_id(char *desc_id_out, const char *service_id, const char *descriptor_cookie, time_t now, uint8_t replica); @@ -4295,10 +4314,14 @@ size_t *intro_points_encrypted_size_out, size_t *encoded_size_out, const char **next_out, const char *desc); -int rend_decrypt_introduction_points(rend_service_descriptor_t *parsed, +int rend_decrypt_introduction_points(char **ipos_decrypted, + size_t *ipos_decrypted_size, const char *descriptor_cookie, - const char *intro_content, - size_t intro_size); + const char *ipos_encrypted, + size_t ipos_encrypted_size); +int rend_parse_introduction_points(rend_service_descriptor_t *parsed, + const char *intro_points_encoded, + size_t intro_points_encoded_size); int rend_parse_client_keys(strmap_t *parsed_clients, const char *str); #endif Index: /home/karsten/tor/tor-trunk-121-patches/src/or/rendcommon.c =================================================================== --- /home/karsten/tor/tor-trunk-121-patches/src/or/rendcommon.c (revision 16516) +++ /home/karsten/tor/tor-trunk-121-patches/src/or/rendcommon.c (working copy) @@ -150,15 +150,11 @@ return 0; } -/* Encode the introduction points in desc, optionally encrypt them with - * an optional descriptor_cookie 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 *ipos_base64. Return 0 for success, -1 - * otherwise. */ +/** Encode the introduction points in desc and write the result to a + * newly allocated string pointed to by encoded. 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 @@ 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,157 @@ } 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 encoded using + * authorization type 'basic' with client_cookies and write the + * result to a newly allocated string pointed to by encrypted of + * length encrypted_len. Return 0 for success, -1 otherwise. */ +static int +rend_encrypt_v2_intro_points_basic(char **encrypted, size_t *encrypted_len, + 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); + enc = tor_malloc_zero(len); + enc[0] = 0x01; + 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(client_cookies, 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); + }); + /* 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); + } + /* Sort smartlist and put elements in result in order. */ + smartlist_sort_digests(encrypted_session_keys); + pos = 2; + SMARTLIST_FOREACH(encrypted_session_keys, char *, entry, { + memcpy(enc + pos, entry, REND_BASIC_AUTH_CLIENT_ENTRY_LEN); + pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN; + }); + *encrypted = enc; + *encrypted_len = len; + r = 0; + done: + if (r<0) + tor_free(enc); + if (encrypted_session_keys) { + SMARTLIST_FOREACH(encrypted_session_keys, char *, d, tor_free(d);); + smartlist_free(encrypted_session_keys); } - /* 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."); + return r; +} + +/** Encrypt the encoded introduction points in encoded using + * authorization type 'stealth' with descriptor_cookie of length + * REND_DESC_COOKIE_LEN and write the result to a newly allocated string + * pointed to by encrypted of length encrypted_len. Return 0 + * for success, -1 otherwise. */ +static int +rend_encrypt_v2_intro_points_stealth(char **encrypted, + size_t *encrypted_len, + 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; + 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 = enc; + *encrypted_len = enclen; r = 0; done: if (r<0) - tor_free(*ipos_base64); - tor_free(unenc); + tor_free(enc); return r; } @@ -308,22 +423,34 @@ } /** Encode a set of rend_encoded_v2_service_descriptor_t's for desc - * at time now using descriptor_cookie (may be NULL if - * introduction points shall not be encrypted) and period (e.g. 0 - * for the current period, 1 for the next period, etc.) and add them to - * the existing list descs_out; return the number of seconds that - * the descriptors will be found by clients, or -1 if the encoding was not - * successful. */ + * at time now using service_key, depending on + * auth_type a descriptor_cookie and a list of + * client_cookies (which are both NULL if no client + * authorization is performed), and period (e.g. 0 for the current + * period, 1 for the next period, etc.) and add them to the existing list + * descs_out; 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; @@ -329,7 +456,7 @@ 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. */ @@ -336,10 +463,52 @@ 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; + } + /* 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 +538,7 @@ 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 +606,7 @@ } 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 +1254,7 @@ 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 +1273,8 @@ } /* 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); Index: /home/karsten/tor/tor-trunk-121-patches/src/or/rendservice.c =================================================================== --- /home/karsten/tor/tor-trunk-121-patches/src/or/rendservice.c (revision 16516) +++ /home/karsten/tor/tor-trunk-121-patches/src/or/rendservice.c (working copy) @@ -487,9 +487,8 @@ d->timestamp = time(NULL); d->version = service->descriptor_version; d->intro_nodes = smartlist_create(); - /* Whoever understands descriptor version 2 also understands intro - * protocol 2. So we only support 2. */ - d->protocols = 1 << 2; + /* Support intro protocols 2 and 3. */ + d->protocols = (1 << 2) + (1 << 3); for (i = 0; i < smartlist_len(service->intro_nodes); ++i) { rend_intro_point_t *intro_svc = smartlist_get(service->intro_nodes, i); @@ -1446,40 +1445,38 @@ get_options()->PublishHidServDescriptors) { networkstatus_t *c = networkstatus_get_latest_consensus(); if (c && smartlist_len(c->routerstatus_list) > 0) { - int seconds_valid; + int seconds_valid, i, j, num_descs; smartlist_t *descs = smartlist_create(); - int i; - /* Encode the current descriptor. */ - seconds_valid = rend_encode_v2_descriptors(descs, service->desc, now, - NULL, 0); - if (seconds_valid < 0) { - log_warn(LD_BUG, "Internal error: couldn't encode service descriptor; " - "not uploading."); - smartlist_free(descs); - return; - } - /* Post the current descriptors to the hidden service directories. */ - rend_get_service_id(service->desc->pk, serviceid); - log_info(LD_REND, "Sending publish request for hidden service %s", - serviceid); - directory_post_to_hs_dir(descs, serviceid, seconds_valid); - /* Free memory for descriptors. */ - for (i = 0; i < smartlist_len(descs); i++) - rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); - smartlist_clear(descs); - /* Update next upload time. */ - if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS - > rendpostperiod) - service->next_upload_time = now + rendpostperiod; - else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) - service->next_upload_time = now + seconds_valid + 1; - else - service->next_upload_time = now + seconds_valid - - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1; - /* Post also the next descriptors, if necessary. */ - if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) { + smartlist_t *client_cookies = smartlist_create(); + /* Either upload a single descriptor (including replicas) or one + * descriptor for each authorized client in case of authorization + * type 'stealth'. */ + num_descs = service->auth_type == REND_STEALTH_AUTH ? + smartlist_len(service->clients) : 1; + for (j = 0; j < num_descs; j++) { + crypto_pk_env_t *client_key = NULL; + rend_authorized_client_t *client = NULL; + smartlist_clear(client_cookies); + switch (service->auth_type) { + case REND_NO_AUTH: + /* Do nothing here. */ + break; + case REND_BASIC_AUTH: + SMARTLIST_FOREACH(service->clients, rend_authorized_client_t *, + cl, smartlist_add(client_cookies, cl->descriptor_cookie)); + break; + case REND_STEALTH_AUTH: + client = smartlist_get(service->clients, j); + client_key = client->client_key; + smartlist_add(client_cookies, client->descriptor_cookie); + break; + } + /* Encode the current descriptor. */ seconds_valid = rend_encode_v2_descriptors(descs, service->desc, - now, NULL, 1); + now, 0, + service->auth_type, + client_key, + client_cookies); if (seconds_valid < 0) { log_warn(LD_BUG, "Internal error: couldn't encode service " "descriptor; not uploading."); @@ -1484,8 +1481,13 @@ log_warn(LD_BUG, "Internal error: couldn't encode service " "descriptor; not uploading."); smartlist_free(descs); + smartlist_free(client_cookies); return; } + /* Post the current descriptors to the hidden service directories. */ + rend_get_service_id(service->desc->pk, serviceid); + log_info(LD_REND, "Sending publish request for hidden service %s", + serviceid); directory_post_to_hs_dir(descs, serviceid, seconds_valid); /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) @@ -1490,8 +1492,39 @@ /* Free memory for descriptors. */ for (i = 0; i < smartlist_len(descs); i++) rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + smartlist_clear(descs); + /* Update next upload time. */ + if (seconds_valid - REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + > rendpostperiod) + service->next_upload_time = now + rendpostperiod; + else if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) + service->next_upload_time = now + seconds_valid + 1; + else + service->next_upload_time = now + seconds_valid - + REND_TIME_PERIOD_OVERLAPPING_V2_DESCS + 1; + /* Post also the next descriptors, if necessary. */ + if (seconds_valid < REND_TIME_PERIOD_OVERLAPPING_V2_DESCS) { + seconds_valid = rend_encode_v2_descriptors(descs, service->desc, + now, 1, + service->auth_type, + client_key, + client_cookies); + if (seconds_valid < 0) { + log_warn(LD_BUG, "Internal error: couldn't encode service " + "descriptor; not uploading."); + smartlist_free(descs); + smartlist_free(client_cookies); + return; + } + directory_post_to_hs_dir(descs, serviceid, seconds_valid); + /* Free memory for descriptors. */ + for (i = 0; i < smartlist_len(descs); i++) + rend_encoded_v2_service_descriptor_free(smartlist_get(descs, i)); + smartlist_clear(descs); + } } smartlist_free(descs); + smartlist_free(client_cookies); uploaded = 1; log_info(LD_REND, "Successfully uploaded v2 rend descriptors!"); } Index: /home/karsten/tor/tor-trunk-121-patches/src/or/routerparse.c =================================================================== --- /home/karsten/tor/tor-trunk-121-patches/src/or/routerparse.c (revision 16516) +++ /home/karsten/tor/tor-trunk-121-patches/src/or/routerparse.c (working copy) @@ -3421,6 +3421,13 @@ eos = desc + strlen(desc); else eos = eos + 1; + /* Check length. */ + if (strlen(desc) > REND_DESC_MAX_SIZE) { + log_warn(LD_REND, "Descriptor length is %i which exceeds " + "maximum rendezvous descriptor size of %i kilobytes.", + strlen(desc), REND_DESC_MAX_SIZE); + goto err; + } /* Tokenize descriptor. */ area = memarea_new(4096); if (tokenize_string(area, desc, eos, tokens, desc_token_table, 0)) { @@ -3560,21 +3567,123 @@ return -1; } -/** Decrypt and decode the introduction points in - * intro_points_encrypted of length - * intro_points_encrypted_size using descriptor_cookie - * (which may also be NULL if no decryption, but only parsing is - * required), parse the introduction points, and write the result to - * parsed; return the number of successfully parsed introduction - * points or -1 in case of a failure. - */ +/** Decrypt the encrypted introduction points in ipos_encrypted of + * length ipos_encrypted_size using descriptor_cookie and + * write the result to a newly allocated string that is pointed to by + * ipos_decrypted and its length to ipos_decrypted_size. + * Return 0 if decryption was successful and -1 otherwise. */ int -rend_decrypt_introduction_points(rend_service_descriptor_t *parsed, +rend_decrypt_introduction_points(char **ipos_decrypted, + size_t *ipos_decrypted_size, const char *descriptor_cookie, - const char *intro_points_encrypted, - size_t intro_points_encrypted_size) + const char *ipos_encrypted, + size_t ipos_encrypted_size) { - char *ipos_decrypted = NULL; + tor_assert(ipos_encrypted); + tor_assert(descriptor_cookie); + if (ipos_encrypted_size < 2) { + log_warn(LD_REND, "Size of encrypted introduction points is too " + "small."); + return -1; + } + if (ipos_encrypted[0] == (int)REND_BASIC_AUTH) { + char iv[CIPHER_IV_LEN], client_id[REND_BASIC_AUTH_CLIENT_ID_LEN], + session_key[CIPHER_KEY_LEN], *dec; + int declen, client_blocks; + size_t pos = 0, len, client_entries_len; + crypto_digest_env_t *digest; + crypto_cipher_env_t *cipher; + client_blocks = (int) ipos_encrypted[1]; + client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE * + REND_BASIC_AUTH_CLIENT_ENTRY_LEN; + if (ipos_encrypted_size < 2 + client_entries_len + CIPHER_IV_LEN + 1) { + log_warn(LD_REND, "Size of encrypted introduction points is too " + "small."); + return -1; + } + memcpy(iv, ipos_encrypted + 2 + client_entries_len, CIPHER_IV_LEN); + digest = crypto_new_digest_env(); + crypto_digest_add_bytes(digest, descriptor_cookie, REND_DESC_COOKIE_LEN); + crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN); + crypto_digest_get_digest(digest, client_id, + REND_BASIC_AUTH_CLIENT_ID_LEN); + crypto_free_digest_env(digest); + for (pos = 2; pos < 2 + client_entries_len; + pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN) { + if (!memcmp(ipos_encrypted + pos, client_id, + REND_BASIC_AUTH_CLIENT_ID_LEN)) { + /* Attempt to decrypt introduction points. */ + cipher = crypto_create_init_cipher(descriptor_cookie, 0); + if (crypto_cipher_decrypt(cipher, session_key, ipos_encrypted + + pos + REND_BASIC_AUTH_CLIENT_ID_LEN, + CIPHER_KEY_LEN) < 0) { + log_warn(LD_REND, "Could not decrypt session key for client."); + crypto_free_cipher_env(cipher); + return -1; + } + crypto_free_cipher_env(cipher); + cipher = crypto_create_init_cipher(session_key, 0); + len = ipos_encrypted_size - 2 - client_entries_len - CIPHER_IV_LEN; + dec = tor_malloc_zero(len); + declen = crypto_cipher_decrypt_with_iv(cipher, dec, len, + ipos_encrypted + 2 + client_entries_len, + ipos_encrypted_size - 2 - client_entries_len); + crypto_free_cipher_env(cipher); + if (declen < 0) { + log_warn(LD_REND, "Could not decrypt introduction point string."); + tor_free(dec); + return -1; + } + if (strcmpstart(dec, "introduction-point ")) { + log_warn(LD_REND, "Decrypted introduction points don't " + "look like we could parse them."); + tor_free(dec); + continue; + } + *ipos_decrypted = dec; + *ipos_decrypted_size = declen; + return 0; + } + } + log_warn(LD_REND, "Could not decrypt introduction points. Please " + "check your authorization for this service!"); + return -1; + } else if (ipos_encrypted[0] == (int)REND_STEALTH_AUTH) { + crypto_cipher_env_t *cipher; + char *dec; + int declen; + dec = tor_malloc_zero(ipos_encrypted_size - CIPHER_IV_LEN - 1); + cipher = crypto_create_init_cipher(descriptor_cookie, 0); + declen = crypto_cipher_decrypt_with_iv(cipher, dec, + ipos_encrypted_size - + CIPHER_IV_LEN - 1, + ipos_encrypted + 1, + ipos_encrypted_size - 1); + crypto_free_cipher_env(cipher); + if (declen < 0) { + log_warn(LD_REND, "Decrypting introduction points failed!"); + tor_free(dec); + return -1; + } + *ipos_decrypted = dec; + *ipos_decrypted_size = declen; + return 0; + } else { + log_warn(LD_REND, "Unknown authorization type number: %d", + ipos_encrypted[0]); + return -1; + } +} + +/** Parse the encoded introduction points in intro_points_encoded of + * length intro_points_encoded_size and write the result to the + * descriptor in parsed; return the number of successfully parsed + * introduction points or -1 in case of a failure. */ +int +rend_parse_introduction_points(rend_service_descriptor_t *parsed, + const char *intro_points_encoded, + size_t intro_points_encoded_size) +{ const char **current_ipo; smartlist_t *tokens; directory_token_t *tok; @@ -3585,28 +3694,10 @@ tor_assert(parsed); /** Function may only be invoked once. */ tor_assert(!parsed->intro_nodes); - tor_assert(intro_points_encrypted); - tor_assert(intro_points_encrypted_size > 0); - /* Decrypt introduction points, if required. */ - if (descriptor_cookie) { - crypto_cipher_env_t *cipher; - int unenclen; - ipos_decrypted = tor_malloc_zero(intro_points_encrypted_size - 16); - cipher = crypto_create_init_cipher(descriptor_cookie, 0); - unenclen = crypto_cipher_decrypt_with_iv(cipher, ipos_decrypted, - intro_points_encrypted_size - 16, - intro_points_encrypted, - intro_points_encrypted_size); - crypto_free_cipher_env(cipher); - if (unenclen < 0) { - tor_free(ipos_decrypted); - return -1; - } - intro_points_encrypted = ipos_decrypted; - intro_points_encrypted_size = unenclen; - } + tor_assert(intro_points_encoded); + tor_assert(intro_points_encoded_size > 0); /* Consider one intro point after the other. */ - current_ipo = &intro_points_encrypted; + current_ipo = &intro_points_encoded; tokens = smartlist_create(); parsed->intro_nodes = smartlist_create(); area = memarea_new(4096); Index: /home/karsten/tor/tor-trunk-121-patches/src/or/test.c =================================================================== --- /home/karsten/tor/tor-trunk-121-patches/src/or/test.c (revision 16516) +++ /home/karsten/tor/tor-trunk-121-patches/src/or/test.c (working copy) @@ -3907,8 +3907,8 @@ intro->intro_key = crypto_pk_dup_key(pk2); smartlist_add(generated->intro_nodes, intro); } - test_assert(rend_encode_v2_descriptors(descs, generated, now, - NULL, 0) > 0); + test_assert(rend_encode_v2_descriptors(descs, generated, now, 0, + REND_NO_AUTH, NULL, NULL) > 0); test_assert(rend_compute_v2_desc_id(computed_desc_id, service_id_base32, NULL, now, 0) == 0); test_memeq(((rend_encoded_v2_service_descriptor_t *) @@ -3923,9 +3923,8 @@ test_assert(parsed); test_memeq(((rend_encoded_v2_service_descriptor_t *) smartlist_get(descs, 0))->desc_id, parsed_desc_id, DIGEST_LEN); - test_assert(rend_decrypt_introduction_points(parsed, NULL, - intro_points_encrypted, - intro_points_size) == 3); + test_assert(rend_parse_introduction_points(parsed, intro_points_encrypted, + intro_points_size) == 3); test_assert(!crypto_pk_cmp_keys(generated->pk, parsed->pk)); test_eq(parsed->timestamp, now); test_eq(parsed->version, 2);