Skip to content

Commit

Permalink
hs-v3: Generate all descriptor related keys
Browse files Browse the repository at this point in the history
We need to generate all the related keys when building the descriptor, so that
we can encrypt the descriptor.

Signed-off-by: David Goulet <dgoulet@torproject.org>
ppopth authored and dgoulet-tor committed Sep 7, 2018
1 parent 15af47e commit 08bbcff
Showing 4 changed files with 278 additions and 4 deletions.
107 changes: 107 additions & 0 deletions src/feature/hs/hs_descriptor.c
Original file line number Diff line number Diff line change
@@ -168,6 +168,26 @@ desc_plaintext_data_free_contents(hs_desc_plaintext_data_t *desc)
memwipe(desc, 0, sizeof(*desc));
}

/* Free the content of the superencrypted section of a descriptor. */
static void
desc_superencrypted_data_free_contents(hs_desc_superencrypted_data_t *desc)
{
if (!desc) {
return;
}

if (desc->encrypted_blob) {
tor_free(desc->encrypted_blob);
}
if (desc->clients) {
SMARTLIST_FOREACH(desc->clients, hs_desc_authorized_client_t *, client,
hs_desc_authorized_client_free(client));
smartlist_free(desc->clients);
}

memwipe(desc, 0, sizeof(*desc));
}

/* Free the content of the encrypted section of a descriptor. */
static void
desc_encrypted_data_free_contents(hs_desc_encrypted_data_t *desc)
@@ -2383,6 +2403,14 @@ hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc)
tor_free(desc);
}

/* Free the descriptor plaintext data object. */
void
hs_desc_superencrypted_data_free_(hs_desc_superencrypted_data_t *desc)
{
desc_superencrypted_data_free_contents(desc);
tor_free(desc);
}

/* Free the descriptor encrypted data object. */
void
hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc)
@@ -2400,6 +2428,7 @@ hs_descriptor_free_(hs_descriptor_t *desc)
}

desc_plaintext_data_free_contents(&desc->plaintext_data);
desc_superencrypted_data_free_contents(&desc->superencrypted_data);
desc_encrypted_data_free_contents(&desc->encrypted_data);
tor_free(desc);
}
@@ -2475,6 +2504,84 @@ hs_desc_intro_point_free_(hs_desc_intro_point_t *ip)
tor_free(ip);
}

/* Build a fake client info for the descriptor */
void
hs_desc_build_fake_authorized_client(hs_desc_authorized_client_t *client_out)
{
tor_assert(client_out);

crypto_rand((char *) client_out->client_id,
sizeof(client_out->client_id));
crypto_rand((char *) client_out->iv,
sizeof(client_out->iv));
crypto_rand((char *) client_out->encrypted_cookie,
sizeof(client_out->encrypted_cookie));
}

/* Using the client public key, auth ephemeral secret key, and descriptor
* cookie, build the auth client so we can then encode the descriptor for
* publication. client_out must be already allocated. */
void
hs_desc_build_authorized_client(const curve25519_public_key_t *client_pk,
const curve25519_secret_key_t *
auth_ephemeral_sk,
const uint8_t *descriptor_cookie,
hs_desc_authorized_client_t *client_out)
{
uint8_t secret_seed[CURVE25519_OUTPUT_LEN];
uint8_t keystream[HS_DESC_CLIENT_ID_LEN + HS_DESC_COOKIE_KEY_LEN];
uint8_t *cookie_key;
crypto_cipher_t *cipher;
crypto_xof_t *xof;

tor_assert(client_pk);
tor_assert(auth_ephemeral_sk);
tor_assert(descriptor_cookie);
tor_assert(client_out);
tor_assert(!tor_mem_is_zero((char *) auth_ephemeral_sk,
sizeof(*auth_ephemeral_sk)));
tor_assert(!tor_mem_is_zero((char *) client_pk, sizeof(*client_pk)));
tor_assert(!tor_mem_is_zero((char *) descriptor_cookie,
HS_DESC_DESCRIPTOR_COOKIE_LEN));

/* Calculate x25519(hs_y, client_X) */
curve25519_handshake(secret_seed,
auth_ephemeral_sk,
client_pk);

/* 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);

memcpy(client_out->client_id, keystream, HS_DESC_CLIENT_ID_LEN);
cookie_key = keystream + HS_DESC_CLIENT_ID_LEN;

/* Random IV */
crypto_strongest_rand(client_out->iv, sizeof(client_out->iv));

/* This creates a cipher for AES. It can't fail. */
cipher = crypto_cipher_new_with_iv_and_bits(cookie_key, client_out->iv,
HS_DESC_COOKIE_KEY_BIT_SIZE);
/* This can't fail. */
crypto_cipher_encrypt(cipher, (char *) client_out->encrypted_cookie,
(const char *) descriptor_cookie,
HS_DESC_DESCRIPTOR_COOKIE_LEN);

memwipe(secret_seed, 0, sizeof(secret_seed));
memwipe(keystream, 0, sizeof(keystream));

crypto_cipher_free(cipher);
}

/* Free an authoriezd client object. */
void
hs_desc_authorized_client_free_(hs_desc_authorized_client_t *client)
{
tor_free(client);
}

/* Free the given descriptor link specifier. */
void
hs_desc_link_specifier_free_(hs_desc_link_specifier_t *ls)
64 changes: 64 additions & 0 deletions src/feature/hs/hs_descriptor.h
Original file line number Diff line number Diff line change
@@ -59,6 +59,17 @@ struct link_specifier_t;
#define HS_DESC_ENCRYPTED_KEY_LEN CIPHER256_KEY_LEN
#define HS_DESC_ENCRYPTED_BIT_SIZE (HS_DESC_ENCRYPTED_KEY_LEN * 8)

/* Length of each components in the auth client section in the descriptor. */
#define HS_DESC_CLIENT_ID_LEN 8
#define HS_DESC_DESCRIPTOR_COOKIE_LEN 16
#define HS_DESC_COOKIE_KEY_LEN 32
#define HS_DESC_COOKIE_KEY_BIT_SIZE (HS_DESC_COOKIE_KEY_LEN * 8)
#define HS_DESC_ENCRYPED_COOKIE_LEN HS_DESC_DESCRIPTOR_COOKIE_LEN

/* The number of auth client entries in the descriptor must be the multiple
* of this constant. */
#define HS_DESC_AUTH_CLIENT_MULTIPLE 16

/* Type of authentication in the descriptor. */
typedef enum {
HS_DESC_AUTH_ED25519 = 1
@@ -126,6 +137,20 @@ typedef struct hs_desc_intro_point_t {
unsigned int cross_certified : 1;
} hs_desc_intro_point_t;

/* Authorized client information located in a descriptor. */
typedef struct hs_desc_authorized_client_t {
/* An identifier that the client will use to identify which auth client
* entry it needs to use. */
uint8_t client_id[HS_DESC_CLIENT_ID_LEN];

/* An IV that is used to decrypt the encrypted descriptor cookie. */
uint8_t iv[CIPHER_IV_LEN];

/* An encrypted descriptor cookie that the client needs to decrypt to use
* it to decrypt the descriptor. */
uint8_t encrypted_cookie[HS_DESC_ENCRYPED_COOKIE_LEN];
} hs_desc_authorized_client_t;

/* The encrypted data section of a descriptor. Obviously the data in this is
* in plaintext but encrypted once encoded. */
typedef struct hs_desc_encrypted_data_t {
@@ -144,6 +169,24 @@ typedef struct hs_desc_encrypted_data_t {
smartlist_t *intro_points;
} hs_desc_encrypted_data_t;

/* The superencrypted data section of a descriptor. Obviously the data in
* this is in plaintext but encrypted once encoded. */
typedef struct hs_desc_superencrypted_data_t {
/* This field contains ephemeral x25519 public key which is used by
* the encryption scheme in the client authorization. */
curve25519_public_key_t auth_ephemeral_pubkey;

/* A list of authorized clients. Contains hs_desc_authorized_client_t
* objects. */
smartlist_t *clients;

/* Decoding only: The b64-decoded encrypted blob from the descriptor */
uint8_t *encrypted_blob;

/* Decoding only: Size of the encrypted_blob */
size_t encrypted_blob_size;
} hs_desc_superencrypted_data_t;

/* Plaintext data that is unencrypted information of the descriptor. */
typedef struct hs_desc_plaintext_data_t {
/* Version of the descriptor format. Spec specifies this field as a
@@ -182,6 +225,11 @@ typedef struct hs_descriptor_t {
/* Contains the plaintext part of the descriptor. */
hs_desc_plaintext_data_t plaintext_data;

/* The following contains what's in the superencrypted part of the
* descriptor. It's only encrypted in the encoded version of the descriptor
* thus the data contained in that object is in plaintext. */
hs_desc_superencrypted_data_t superencrypted_data;

/* The following contains what's in the encrypted part of the descriptor.
* It's only encrypted in the encoded version of the descriptor thus the
* data contained in that object is in plaintext. */
@@ -211,6 +259,10 @@ void hs_descriptor_free_(hs_descriptor_t *desc);
void hs_desc_plaintext_data_free_(hs_desc_plaintext_data_t *desc);
#define hs_desc_plaintext_data_free(desc) \
FREE_AND_NULL(hs_desc_plaintext_data_t, hs_desc_plaintext_data_free_, (desc))
void hs_desc_superencrypted_data_free_(hs_desc_superencrypted_data_t *desc);
#define hs_desc_superencrypted_data_free(desc) \
FREE_AND_NULL(hs_desc_superencrypted_data_t, \
hs_desc_superencrypted_data_free_, (desc))
void hs_desc_encrypted_data_free_(hs_desc_encrypted_data_t *desc);
#define hs_desc_encrypted_data_free(desc) \
FREE_AND_NULL(hs_desc_encrypted_data_t, hs_desc_encrypted_data_free_, (desc))
@@ -243,10 +295,22 @@ hs_desc_intro_point_t *hs_desc_intro_point_new(void);
void hs_desc_intro_point_free_(hs_desc_intro_point_t *ip);
#define hs_desc_intro_point_free(ip) \
FREE_AND_NULL(hs_desc_intro_point_t, hs_desc_intro_point_free_, (ip))
void hs_desc_authorized_client_free_(hs_desc_authorized_client_t *client);
#define hs_desc_authorized_client_free(client) \
FREE_AND_NULL(hs_desc_authorized_client_t, \
hs_desc_authorized_client_free_, (client))

link_specifier_t *hs_desc_lspec_to_trunnel(
const hs_desc_link_specifier_t *spec);

void
hs_desc_build_fake_authorized_client(hs_desc_authorized_client_t *client_out);
void hs_desc_build_authorized_client(const curve25519_public_key_t *client_pk,
const curve25519_secret_key_t *
auth_ephemeral_sk,
const uint8_t *descriptor_cookie,
hs_desc_authorized_client_t *client_out);

#ifdef HS_DESCRIPTOR_PRIVATE

/* Encoding. */
104 changes: 100 additions & 4 deletions src/feature/hs/hs_service.c
Original file line number Diff line number Diff line change
@@ -1559,6 +1559,78 @@ build_service_desc_encrypted(const hs_service_t *service,
return 0;
}

/* Populate the descriptor superencrypted section from the given service
* object. This will generate a valid list of hs_desc_authorized_client_t
* of clients that are authorized to use the service. Return 0 on success
* else -1 on error. */
static int
build_service_desc_superencrypted(const hs_service_t *service,
hs_service_descriptor_t *desc)
{
const hs_service_config_t *config;
int i;
hs_desc_superencrypted_data_t *superencrypted;

tor_assert(service);
tor_assert(desc);

superencrypted = &desc->desc->superencrypted_data;
config = &service->config;

/* The ephemeral key pair is already generated, so this should not give
* an error. */
memcpy(&superencrypted->auth_ephemeral_pubkey,
&desc->auth_ephemeral_kp.pubkey,
sizeof(curve25519_public_key_t));

/* Create a smartlist to store clients */
superencrypted->clients = smartlist_new();

/* We do not need to build the desc authorized client if the client
* authorization is disabled */
if (config->is_client_auth_enabled) {
SMARTLIST_FOREACH_BEGIN(config->clients,
hs_service_authorized_client_t *, client) {
hs_desc_authorized_client_t *desc_client;
desc_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));

/* Prepare the client for descriptor and then add to the list in the
* superencrypted part of the descriptor */
hs_desc_build_authorized_client(&client->client_pk,
&desc->auth_ephemeral_kp.seckey,
desc->descriptor_cookie, desc_client);
smartlist_add(superencrypted->clients, desc_client);

} SMARTLIST_FOREACH_END(client);
}

/* We cannot let the number of auth-clients to be zero, so we need to
* make it be 16. If it is already a multiple of 16, we do not need to
* do anything. Otherwise, add the additional ones to make it a
* multiple of 16. */
int num_clients = smartlist_len(superencrypted->clients);
int num_clients_to_add;
if (num_clients == 0) {
num_clients_to_add = HS_DESC_AUTH_CLIENT_MULTIPLE;
} else if (num_clients % HS_DESC_AUTH_CLIENT_MULTIPLE == 0) {
num_clients_to_add = 0;
} else {
num_clients_to_add =
HS_DESC_AUTH_CLIENT_MULTIPLE
- (num_clients % HS_DESC_AUTH_CLIENT_MULTIPLE);
}

for (i = 0; i < num_clients_to_add; i++) {
hs_desc_authorized_client_t *desc_client;
desc_client = tor_malloc_zero(sizeof(hs_desc_authorized_client_t));

hs_desc_build_fake_authorized_client(desc_client);
smartlist_add(superencrypted->clients, desc_client);
}

return 0;
}

/* Populate the descriptor plaintext section from the given service object.
* The caller must make sure that the keys in the descriptors are valid that
* is are non-zero. Return 0 on success else -1 on error. */
@@ -1624,13 +1696,14 @@ generate_ope_cipher_for_desc(const hs_service_descriptor_t *hs_desc)
}

/* For the given service and descriptor object, create the key material which
* is the blinded keypair and the descriptor signing keypair. Return 0 on
* success else -1 on error where the generated keys MUST be ignored. */
* is the blinded keypair, the descriptor signing keypair, the ephemeral
* keypair, and the descriptor cookie. Return 0 on success else -1 on error
* where the generated keys MUST be ignored. */
static int
build_service_desc_keys(const hs_service_t *service,
hs_service_descriptor_t *desc)
{
int ret = 0;
int ret = -1;
ed25519_keypair_t kp;

tor_assert(desc);
@@ -1661,9 +1734,28 @@ build_service_desc_keys(const hs_service_t *service,
log_warn(LD_REND, "Can't generate descriptor signing keypair for "
"service %s",
safe_str_client(service->onion_address));
ret = -1;
goto end;
}

/* No need for extra strong, this is a temporary key only for this
* descriptor. Nothing long term. */
if (curve25519_keypair_generate(&desc->auth_ephemeral_kp, 0) < 0) {
log_warn(LD_REND, "Can't generate auth ephemeral keypair for "
"service %s",
safe_str_client(service->onion_address));
goto end;
}

/* Random a descriptor cookie to be used as a part of a key to encrypt the
* descriptor, if the client auth is enabled. */
if (service->config.is_client_auth_enabled) {
crypto_strongest_rand(desc->descriptor_cookie,
sizeof(desc->descriptor_cookie));
}

/* Success. */
ret = 0;
end:
return ret;
}

@@ -1697,6 +1789,10 @@ build_service_descriptor(hs_service_t *service, time_t now,
if (build_service_desc_plaintext(service, desc, now) < 0) {
goto err;
}
/* Setup superencrypted descriptor content. */
if (build_service_desc_superencrypted(service, desc) < 0) {
goto err;
}
/* Setup encrypted descriptor content. */
if (build_service_desc_encrypted(service, desc) < 0) {
goto err;
7 changes: 7 additions & 0 deletions src/feature/hs/hs_service.h
Original file line number Diff line number Diff line change
@@ -105,6 +105,13 @@ typedef struct hs_service_descriptor_t {
* publishes the descriptor. */
hs_descriptor_t *desc;

/* Client authorization ephemeral keypair. */
curve25519_keypair_t auth_ephemeral_kp;

/* Descriptor cookie used to encrypt the descriptor, when the client
* authorization is enabled */
uint8_t descriptor_cookie[HS_DESC_DESCRIPTOR_COOKIE_LEN];

/* Descriptor signing keypair. */
ed25519_keypair_t signing_kp;

0 comments on commit 08bbcff

Please sign in to comment.