mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-13 06:33:44 +01:00
prop224: Implement decoding of superencrypted HS descriptor.
[Consider starting review from desc_decrypt_all() ]
This commit is contained in:
parent
b2e37b87a7
commit
d0fe199269
@ -113,6 +113,15 @@ static token_rule_t hs_desc_v3_token_table[] = {
|
|||||||
END_OF_TABLE
|
END_OF_TABLE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Descriptor ruleset for the superencrypted section. */
|
||||||
|
static token_rule_t hs_desc_superencrypted_v3_token_table[] = {
|
||||||
|
T1_START(str_desc_auth_type, R3_DESC_AUTH_TYPE, GE(1), NO_OBJ),
|
||||||
|
T1(str_desc_auth_key, R3_DESC_AUTH_KEY, GE(1), NO_OBJ),
|
||||||
|
T1N(str_desc_auth_client, R3_DESC_AUTH_CLIENT, GE(3), NO_OBJ),
|
||||||
|
T1(str_encrypted, R3_ENCRYPTED, NO_ARGS, NEED_OBJ),
|
||||||
|
END_OF_TABLE
|
||||||
|
};
|
||||||
|
|
||||||
/* Descriptor ruleset for the encrypted section. */
|
/* Descriptor ruleset for the encrypted section. */
|
||||||
static token_rule_t hs_desc_encrypted_v3_token_table[] = {
|
static token_rule_t hs_desc_encrypted_v3_token_table[] = {
|
||||||
T1_START(str_create2_formats, R3_CREATE2_FORMATS, CONCAT_ARGS, NO_OBJ),
|
T1_START(str_create2_formats, R3_CREATE2_FORMATS, CONCAT_ARGS, NO_OBJ),
|
||||||
@ -1313,12 +1322,17 @@ encrypted_data_length_is_valid(size_t len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decrypt the encrypted section of the descriptor using the given descriptor
|
/** Decrypt an encrypted descriptor layer at <b>encrypted_blob</b> of size
|
||||||
* object desc. A newly allocated NUL terminated string is put in
|
* <b>encrypted_blob_size</b>. Use the descriptor object <b>desc</b> to
|
||||||
* decrypted_out. Return the length of decrypted_out on success else 0 is
|
* generate the right decryption keys; set <b>decrypted_out</b> to the
|
||||||
* returned and decrypted_out is set to NULL. */
|
* plaintext. If <b>is_superencrypted_layer</b> is set, this is the outter
|
||||||
|
* encrypted layer of the descriptor. */
|
||||||
static size_t
|
static size_t
|
||||||
desc_decrypt_data_v3(const hs_descriptor_t *desc, char **decrypted_out)
|
decrypt_desc_layer(const hs_descriptor_t *desc,
|
||||||
|
const uint8_t *encrypted_blob,
|
||||||
|
size_t encrypted_blob_size,
|
||||||
|
int is_superencrypted_layer,
|
||||||
|
char **decrypted_out)
|
||||||
{
|
{
|
||||||
uint8_t *decrypted = NULL;
|
uint8_t *decrypted = NULL;
|
||||||
uint8_t secret_key[HS_DESC_ENCRYPTED_KEY_LEN], secret_iv[CIPHER_IV_LEN];
|
uint8_t secret_key[HS_DESC_ENCRYPTED_KEY_LEN], secret_iv[CIPHER_IV_LEN];
|
||||||
@ -1328,41 +1342,33 @@ desc_decrypt_data_v3(const hs_descriptor_t *desc, char **decrypted_out)
|
|||||||
|
|
||||||
tor_assert(decrypted_out);
|
tor_assert(decrypted_out);
|
||||||
tor_assert(desc);
|
tor_assert(desc);
|
||||||
tor_assert(desc->plaintext_data.superencrypted_blob);
|
tor_assert(encrypted_blob);
|
||||||
|
|
||||||
/* Construction is as follow: SALT | ENCRYPTED_DATA | MAC */
|
/* Construction is as follow: SALT | ENCRYPTED_DATA | MAC .
|
||||||
if (!encrypted_data_length_is_valid(
|
* Make sure we have enough space for all these things. */
|
||||||
desc->plaintext_data.superencrypted_blob_size)) {
|
if (!encrypted_data_length_is_valid(encrypted_blob_size)) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Start of the blob thus the salt. */
|
/* Start of the blob thus the salt. */
|
||||||
salt = desc->plaintext_data.superencrypted_blob;
|
salt = encrypted_blob;
|
||||||
/* Next is the encrypted data. */
|
|
||||||
encrypted = desc->plaintext_data.superencrypted_blob +
|
|
||||||
HS_DESC_ENCRYPTED_SALT_LEN;
|
|
||||||
encrypted_len = desc->plaintext_data.superencrypted_blob_size -
|
|
||||||
(HS_DESC_ENCRYPTED_SALT_LEN + DIGEST256_LEN);
|
|
||||||
|
|
||||||
/* At the very end is the MAC. Make sure it's of the right size. */
|
/* Next is the encrypted data. */
|
||||||
{
|
encrypted = encrypted_blob + HS_DESC_ENCRYPTED_SALT_LEN;
|
||||||
desc_mac = encrypted + encrypted_len;
|
encrypted_len = encrypted_blob_size -
|
||||||
size_t desc_mac_size = desc->plaintext_data.superencrypted_blob_size -
|
(HS_DESC_ENCRYPTED_SALT_LEN + DIGEST256_LEN);
|
||||||
(desc_mac - desc->plaintext_data.superencrypted_blob);
|
tor_assert(encrypted_len > 0); /* guaranteed by the check above */
|
||||||
if (desc_mac_size != DIGEST256_LEN) {
|
|
||||||
log_warn(LD_REND, "Service descriptor MAC length of encrypted data "
|
/* And last comes the MAC. */
|
||||||
"is invalid (%lu, expected %u)",
|
desc_mac = encrypted_blob + encrypted_blob_size - DIGEST256_LEN;
|
||||||
(unsigned long) desc_mac_size, DIGEST256_LEN);
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* KDF construction resulting in a key from which the secret key, IV and MAC
|
/* KDF construction resulting in a key from which the secret key, IV and MAC
|
||||||
* key are extracted which is what we need for the decryption. */
|
* key are extracted which is what we need for the decryption. */
|
||||||
build_secret_key_iv_mac(desc, salt, HS_DESC_ENCRYPTED_SALT_LEN,
|
build_secret_key_iv_mac(desc, salt, HS_DESC_ENCRYPTED_SALT_LEN,
|
||||||
secret_key, sizeof(secret_key),
|
secret_key, sizeof(secret_key),
|
||||||
secret_iv, sizeof(secret_iv),
|
secret_iv, sizeof(secret_iv),
|
||||||
mac_key, sizeof(mac_key));
|
mac_key, sizeof(mac_key),
|
||||||
|
is_superencrypted_layer);
|
||||||
|
|
||||||
/* Build MAC. */
|
/* Build MAC. */
|
||||||
build_mac(mac_key, sizeof(mac_key), salt, HS_DESC_ENCRYPTED_SALT_LEN,
|
build_mac(mac_key, sizeof(mac_key), salt, HS_DESC_ENCRYPTED_SALT_LEN,
|
||||||
@ -1392,7 +1398,7 @@ desc_decrypt_data_v3(const hs_descriptor_t *desc, char **decrypted_out)
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
/* Adjust length to remove NULL padding bytes */
|
/* Adjust length to remove NUL padding bytes */
|
||||||
uint8_t *end = memchr(decrypted, 0, encrypted_len);
|
uint8_t *end = memchr(decrypted, 0, encrypted_len);
|
||||||
result_len = encrypted_len;
|
result_len = encrypted_len;
|
||||||
if (end) {
|
if (end) {
|
||||||
@ -1418,6 +1424,161 @@ desc_decrypt_data_v3(const hs_descriptor_t *desc, char **decrypted_out)
|
|||||||
return result_len;
|
return result_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Basic validation that the superencrypted client auth portion of the
|
||||||
|
* descriptor is well-formed and recognized. Return True if so, otherwise
|
||||||
|
* return False. */
|
||||||
|
static int
|
||||||
|
superencrypted_auth_data_is_valid(smartlist_t *tokens)
|
||||||
|
{
|
||||||
|
/* XXX: This is just basic validation for now. When we implement client auth,
|
||||||
|
we can refactor this function so that it actually parses and saves the
|
||||||
|
data. */
|
||||||
|
|
||||||
|
{ /* verify desc auth type */
|
||||||
|
const directory_token_t *tok;
|
||||||
|
tok = find_by_keyword(tokens, R3_DESC_AUTH_TYPE);
|
||||||
|
tor_assert(tok->n_args >= 1);
|
||||||
|
if (strcmp(tok->args[0], "x25519")) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* verify desc auth key */
|
||||||
|
const directory_token_t *tok;
|
||||||
|
curve25519_public_key_t k;
|
||||||
|
tok = find_by_keyword(tokens, R3_DESC_AUTH_KEY);
|
||||||
|
tor_assert(tok->n_args >= 1);
|
||||||
|
if (curve25519_public_from_base64(&k, tok->args[0]) < 0) {
|
||||||
|
log_warn(LD_DIR, "Bogus desc auth key in HS desc");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify desc auth client items */
|
||||||
|
SMARTLIST_FOREACH_BEGIN(tokens, const directory_token_t *, tok) {
|
||||||
|
if (tok->tp == R3_DESC_AUTH_CLIENT) {
|
||||||
|
tor_assert(tok->n_args >= 3);
|
||||||
|
}
|
||||||
|
} SMARTLIST_FOREACH_END(tok);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse <b>message</b>, the plaintext of the superencrypted portion of an HS
|
||||||
|
* descriptor. Set <b>encrypted_out</b> to the encrypted blob, and return its
|
||||||
|
* size */
|
||||||
|
STATIC size_t
|
||||||
|
decode_superencrypted(const char *message, size_t message_len,
|
||||||
|
uint8_t **encrypted_out)
|
||||||
|
{
|
||||||
|
int retval = 0;
|
||||||
|
memarea_t *area = NULL;
|
||||||
|
smartlist_t *tokens = NULL;
|
||||||
|
|
||||||
|
area = memarea_new();
|
||||||
|
tokens = smartlist_new();
|
||||||
|
if (tokenize_string(area, message, message + message_len, tokens,
|
||||||
|
hs_desc_superencrypted_v3_token_table, 0) < 0) {
|
||||||
|
log_warn(LD_REND, "Superencrypted portion is not parseable");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do some rudimentary validation of the authentication data */
|
||||||
|
if (!superencrypted_auth_data_is_valid(tokens)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract the encrypted data section. */
|
||||||
|
{
|
||||||
|
const directory_token_t *tok;
|
||||||
|
tok = find_by_keyword(tokens, R3_ENCRYPTED);
|
||||||
|
tor_assert(tok->object_body);
|
||||||
|
if (strcmp(tok->object_type, "MESSAGE") != 0) {
|
||||||
|
log_warn(LD_REND, "Desc superencrypted data section is invalid");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
/* Make sure the length of the encrypted blob is valid. */
|
||||||
|
if (!encrypted_data_length_is_valid(tok->object_size)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the encrypted blob to the descriptor object so we can handle it
|
||||||
|
* latter if needed. */
|
||||||
|
*encrypted_out = tor_memdup(tok->object_body, tok->object_size);
|
||||||
|
retval = tok->object_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
||||||
|
smartlist_free(tokens);
|
||||||
|
if (area) {
|
||||||
|
memarea_drop_all(area);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Decrypt both the superencrypted and the encrypted section of the descriptor
|
||||||
|
* using the given descriptor object <b>desc</b>. A newly allocated NUL
|
||||||
|
* terminated string is put in decrypted_out which contains the inner encrypted
|
||||||
|
* layer of the descriptor. Return the length of decrypted_out on success else
|
||||||
|
* 0 is returned and decrypted_out is set to NULL. */
|
||||||
|
static size_t
|
||||||
|
desc_decrypt_all(const hs_descriptor_t *desc, char **decrypted_out)
|
||||||
|
{
|
||||||
|
size_t decrypted_len = 0;
|
||||||
|
size_t encrypted_len = 0;
|
||||||
|
size_t superencrypted_len = 0;
|
||||||
|
char *superencrypted_plaintext = NULL;
|
||||||
|
uint8_t *encrypted_blob = NULL;
|
||||||
|
|
||||||
|
/** Function logic: This function takes us from the descriptor header to the
|
||||||
|
* inner encrypted layer, by decrypting and decoding the middle descriptor
|
||||||
|
* layer. In the end we return the contents of the inner encrypted layer to
|
||||||
|
* our caller. */
|
||||||
|
|
||||||
|
/* 1. Decrypt middle layer of descriptor */
|
||||||
|
superencrypted_len = decrypt_desc_layer(desc,
|
||||||
|
desc->plaintext_data.superencrypted_blob,
|
||||||
|
desc->plaintext_data.superencrypted_blob_size,
|
||||||
|
1,
|
||||||
|
&superencrypted_plaintext);
|
||||||
|
if (!superencrypted_len) {
|
||||||
|
log_warn(LD_REND, "Decrypting superencrypted desc failed.");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
tor_assert(superencrypted_plaintext);
|
||||||
|
|
||||||
|
/* 2. Parse "superencrypted" */
|
||||||
|
encrypted_len = decode_superencrypted(superencrypted_plaintext,
|
||||||
|
superencrypted_len,
|
||||||
|
&encrypted_blob);
|
||||||
|
if (!encrypted_len) {
|
||||||
|
log_warn(LD_REND, "Decrypting encrypted desc failed.");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
tor_assert(encrypted_blob);
|
||||||
|
|
||||||
|
/* 3. Decrypt "encrypted" and set decrypted_out */
|
||||||
|
char *decrypted_desc;
|
||||||
|
decrypted_len = decrypt_desc_layer(desc,
|
||||||
|
encrypted_blob, encrypted_len,
|
||||||
|
0, &decrypted_desc);
|
||||||
|
if (!decrypted_len) {
|
||||||
|
log_warn(LD_REND, "Decrypting encrypted desc failed.");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
tor_assert(decrypted_desc);
|
||||||
|
|
||||||
|
*decrypted_out = decrypted_desc;
|
||||||
|
|
||||||
|
err:
|
||||||
|
tor_free(superencrypted_plaintext);
|
||||||
|
tor_free(encrypted_blob);
|
||||||
|
|
||||||
|
return decrypted_len;
|
||||||
|
}
|
||||||
|
|
||||||
/* Given the start of a section and the end of it, decode a single
|
/* Given the start of a section and the end of it, decode a single
|
||||||
* introduction point from that section. Return a newly allocated introduction
|
* introduction point from that section. Return a newly allocated introduction
|
||||||
* point object containing the decoded data. Return NULL if the section can't
|
* point object containing the decoded data. Return NULL if the section can't
|
||||||
@ -1550,7 +1711,9 @@ decode_introduction_point(const hs_descriptor_t *desc, const char *start)
|
|||||||
tor_cert_free(cross_cert);
|
tor_cert_free(cross_cert);
|
||||||
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
||||||
smartlist_free(tokens);
|
smartlist_free(tokens);
|
||||||
|
if (area) {
|
||||||
memarea_drop_all(area);
|
memarea_drop_all(area);
|
||||||
|
}
|
||||||
|
|
||||||
return ip;
|
return ip;
|
||||||
}
|
}
|
||||||
@ -1804,10 +1967,9 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc,
|
|||||||
tor_assert(desc);
|
tor_assert(desc);
|
||||||
tor_assert(desc_encrypted_out);
|
tor_assert(desc_encrypted_out);
|
||||||
|
|
||||||
/* Decrypt the encrypted data that is located in the plaintext section in
|
/* Decrypt the superencrypted data that is located in the plaintext section
|
||||||
* the descriptor as a blob of bytes. The following functions will use the
|
* in the descriptor as a blob of bytes. */
|
||||||
* keys found in the same section. */
|
message_len = desc_decrypt_all(desc, &message);
|
||||||
message_len = desc_decrypt_data_v3(desc, &message);
|
|
||||||
if (!message_len) {
|
if (!message_len) {
|
||||||
log_warn(LD_REND, "Service descriptor decryption failed.");
|
log_warn(LD_REND, "Service descriptor decryption failed.");
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -228,6 +228,8 @@ STATIC int desc_sig_is_valid(const char *b64_sig,
|
|||||||
const ed25519_public_key_t *signing_pubkey,
|
const ed25519_public_key_t *signing_pubkey,
|
||||||
const char *encoded_desc, size_t encoded_len);
|
const char *encoded_desc, size_t encoded_len);
|
||||||
STATIC void desc_intro_point_free(hs_desc_intro_point_t *ip);
|
STATIC void desc_intro_point_free(hs_desc_intro_point_t *ip);
|
||||||
|
STATIC size_t decode_superencrypted(const char *message, size_t message_len,
|
||||||
|
uint8_t **encrypted_out);
|
||||||
#endif /* HS_DESCRIPTOR_PRIVATE */
|
#endif /* HS_DESCRIPTOR_PRIVATE */
|
||||||
|
|
||||||
#endif /* TOR_HS_DESCRIPTOR_H */
|
#endif /* TOR_HS_DESCRIPTOR_H */
|
||||||
|
@ -163,6 +163,10 @@ typedef enum {
|
|||||||
R3_INTRO_AUTH_KEY,
|
R3_INTRO_AUTH_KEY,
|
||||||
R3_INTRO_ENC_KEY,
|
R3_INTRO_ENC_KEY,
|
||||||
R3_INTRO_ENC_KEY_CERTIFICATION,
|
R3_INTRO_ENC_KEY_CERTIFICATION,
|
||||||
|
R3_DESC_AUTH_TYPE,
|
||||||
|
R3_DESC_AUTH_KEY,
|
||||||
|
R3_DESC_AUTH_CLIENT,
|
||||||
|
R3_ENCRYPTED,
|
||||||
|
|
||||||
R_IPO_IDENTIFIER,
|
R_IPO_IDENTIFIER,
|
||||||
R_IPO_IP_ADDRESS,
|
R_IPO_IP_ADDRESS,
|
||||||
|
Loading…
Reference in New Issue
Block a user