mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-28 06:13:31 +01:00
hs-v3: Decrypt the descriptor with client private key
Parse the client authorization section from the descriptor, use the client private key to decrypt the auth clients, and then use the descriptor cookie to decrypt the descriptor. Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
parent
63576b0166
commit
7acb720027
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user