diff options
Diffstat (limited to 'src/feature/hs/hs_descriptor.c')
-rw-r--r-- | src/feature/hs/hs_descriptor.c | 142 |
1 files changed, 137 insertions, 5 deletions
diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c index bb2cc1984b..3f9b505cbd 100644 --- a/src/feature/hs/hs_descriptor.c +++ b/src/feature/hs/hs_descriptor.c @@ -1181,6 +1181,42 @@ desc_encode_v3(const hs_descriptor_t *desc, /* === DECODING === */ +/* Given the token tok for an auth client, decode it as + * hs_desc_authorized_client_t. tok->args MUST contain at least 3 elements + * Return 0 on success else -1 on failure. */ +static int +decode_auth_client(const directory_token_t *tok, + hs_desc_authorized_client_t *client) +{ + int ret = -1; + + tor_assert(tok); + tor_assert(tok->n_args >= 3); + tor_assert(client); + + if (base64_decode((char *) client->client_id, sizeof(client->client_id), + tok->args[0], strlen(tok->args[0])) != + sizeof(client->client_id)) { + goto done; + } + if (base64_decode((char *) client->iv, sizeof(client->iv), + tok->args[1], strlen(tok->args[1])) != + sizeof(client->iv)) { + goto done; + } + if (base64_decode((char *) client->encrypted_cookie, + sizeof(client->encrypted_cookie), + tok->args[2], strlen(tok->args[2])) != + sizeof(client->encrypted_cookie)) { + goto done; + } + + /* Success. */ + ret = 0; + done: + return ret; +} + /* Given an encoded string of the link specifiers, return a newly allocated * list of decoded link specifiers. Return NULL on error. */ STATIC smartlist_t * @@ -1420,6 +1456,73 @@ encrypted_data_length_is_valid(size_t len) return 0; } +/* Decrypt the descriptor cookie given the descriptor, the auth client, + * and the client secret key. On sucess, return 0 and a newly allocated + * descriptor cookie descriptor_cookie_out. On error or if the client id + * is invalid, return -1 and descriptor_cookie_out is set to + * NULL. */ +static int +decrypt_descriptor_cookie(const hs_descriptor_t *desc, + const hs_desc_authorized_client_t *client, + const curve25519_secret_key_t *client_sk, + uint8_t **descriptor_cookie_out) +{ + int ret = -1; + uint8_t secret_seed[CURVE25519_OUTPUT_LEN]; + uint8_t keystream[HS_DESC_CLIENT_ID_LEN + HS_DESC_COOKIE_KEY_LEN]; + uint8_t *cookie_key = NULL; + uint8_t *descriptor_cookie = NULL; + crypto_cipher_t *cipher = NULL; + crypto_xof_t *xof = NULL; + + tor_assert(desc); + tor_assert(client); + tor_assert(client_sk); + tor_assert(!tor_mem_is_zero( + (char *) &desc->superencrypted_data.auth_ephemeral_pubkey, + sizeof(desc->superencrypted_data.auth_ephemeral_pubkey))); + tor_assert(!tor_mem_is_zero((char *) client_sk, + sizeof(*client_sk))); + + /* Calculate x25519(client_x, hs_Y) */ + curve25519_handshake(secret_seed, client_sk, + &desc->superencrypted_data.auth_ephemeral_pubkey); + + /* Calculate KEYS = KDF(SECRET_SEED, 40) */ + xof = crypto_xof_new(); + crypto_xof_add_bytes(xof, secret_seed, sizeof(secret_seed)); + crypto_xof_squeeze_bytes(xof, keystream, sizeof(keystream)); + crypto_xof_free(xof); + + /* If the client id of auth client is not the same as the calculcated + * client id, it means that this auth client is invaild according to the + * client secret key client_sk. */ + if (tor_memneq(client->client_id, keystream, HS_DESC_CLIENT_ID_LEN)) { + goto done; + } + cookie_key = keystream + HS_DESC_CLIENT_ID_LEN; + + /* This creates a cipher for AES. It can't fail. */ + cipher = crypto_cipher_new_with_iv_and_bits(cookie_key, client->iv, + HS_DESC_COOKIE_KEY_BIT_SIZE); + descriptor_cookie = tor_malloc_zero(HS_DESC_DESCRIPTOR_COOKIE_LEN); + /* This can't fail. */ + crypto_cipher_decrypt(cipher, (char *) descriptor_cookie, + (const char *) client->encrypted_cookie, + sizeof(client->encrypted_cookie)); + + /* Success. */ + ret = 0; + done: + *descriptor_cookie_out = descriptor_cookie; + if (cipher) { + crypto_cipher_free(cipher); + } + memwipe(secret_seed, 0, sizeof(secret_seed)); + memwipe(keystream, 0, sizeof(keystream)); + return ret; +} + /** Decrypt an encrypted descriptor layer at <b>encrypted_blob</b> of size * <b>encrypted_blob_size</b>. The descriptor cookie is optional. Use * the descriptor object <b>desc</b> and <b>descriptor_cookie</b> @@ -1588,18 +1691,30 @@ desc_decrypt_encrypted(const hs_descriptor_t *desc, { size_t encrypted_len = 0; char *encrypted_plaintext = NULL; + uint8_t *descriptor_cookie = NULL; tor_assert(desc); + tor_assert(desc->superencrypted_data.clients); tor_assert(decrypted_out); - /* XXX: We need to decrypt the descriptor properly when the client auth - * is enabled. */ - (void) client_sk; + /* If the client secret key is provided, try to find a valid descriptor + * cookie. Otherwise, leave it NULL. */ + if (client_sk) { + SMARTLIST_FOREACH_BEGIN(desc->superencrypted_data.clients, + hs_desc_authorized_client_t *, client) { + /* If we can decrypt the descriptor cookie successfully, we will use that + * descriptor cookie and break from the loop. */ + if (!decrypt_descriptor_cookie(desc, client, client_sk, + &descriptor_cookie)) { + break; + } + } SMARTLIST_FOREACH_END(client); + } encrypted_len = decrypt_desc_layer(desc, desc->superencrypted_data.encrypted_blob, desc->superencrypted_data.encrypted_blob_size, - NULL, 0, &encrypted_plaintext); + descriptor_cookie, 0, &encrypted_plaintext); if (!encrypted_len) { log_warn(LD_REND, "Decrypting encrypted desc failed."); goto err; @@ -1610,6 +1725,10 @@ desc_decrypt_encrypted(const hs_descriptor_t *desc, /* In case of error, encrypted_plaintext is already NULL, so the * following line makes sense. */ *decrypted_out = encrypted_plaintext; + if (descriptor_cookie) { + memwipe(descriptor_cookie, 0, HS_DESC_DESCRIPTOR_COOKIE_LEN); + } + tor_free(descriptor_cookie); /* This makes sense too, because, in case of error, this is zero. */ return encrypted_len; } @@ -2125,10 +2244,23 @@ desc_decode_superencrypted_v3(const hs_descriptor_t *desc, } /* Extract desc auth client items */ + if (!superencrypted->clients) { + superencrypted->clients = smartlist_new(); + } SMARTLIST_FOREACH_BEGIN(tokens, const directory_token_t *, token) { if (token->tp == R3_DESC_AUTH_CLIENT) { tor_assert(token->n_args >= 3); - /* XXX: Extract each auth client. */ + + hs_desc_authorized_client_t *client = + tor_malloc_zero(sizeof(hs_desc_authorized_client_t)); + + if (decode_auth_client(token, client) < 0) { + log_warn(LD_REND, "Descriptor client authorization section can't " + "be decoded."); + tor_free(client); + goto err; + } + smartlist_add(superencrypted->clients, client); } } SMARTLIST_FOREACH_END(token); |