commit 49f21b6ba30a07369ff3282615465d0f3ad40e5b Author: David Goulet dgoulet@torproject.org Date: Thu Nov 9 12:20:29 2017 -0500
control: Support HSv3 interface for ADD_ONION
At this commit, the key handling and generation is supported for a v3 service (ED25519-V3). However, the service creation is not yet implemented. This only adds the interface and code to deal with the new ED25519-V3 key type.
Tests have been updated for RSA key type but nothing yet for ED25519-v3.
Part of #20699
Signed-off-by: David Goulet dgoulet@torproject.org --- src/or/control.c | 126 ++++++++++++++++++++++++++++++++++++++------- src/or/control.h | 9 ++-- src/test/test_controller.c | 74 +++++++++++++++----------- 3 files changed, 158 insertions(+), 51 deletions(-)
diff --git a/src/or/control.c b/src/or/control.c index 405c0c935..ef1e0edcd 100644 --- a/src/or/control.c +++ b/src/or/control.c @@ -4424,6 +4424,49 @@ handle_control_hspost(control_connection_t *conn, return 0; }
+/* Helper function for ADD_ONION that adds an ephemeral service depending on + * the given hs_version. The pk's type defers depending on the version so the + * caller should make sure those two matches. Both port_cfgs and auth_clients + * are now owned by the hidden service subsystem so the caller must stop + * accessing them. + * + * On success (RSAE_OKAY), the address_out points to a newly allocated string + * containing the onion address without the .onion part. On error, address_out + * is untouched. */ +static rend_service_add_ephemeral_status_t +add_onion_helper_add_service(int hs_version, void *pk, smartlist_t *port_cfgs, + int max_streams, int max_streams_close_circuit, + int auth_type, smartlist_t *auth_clients, + char **address_out) +{ + rend_service_add_ephemeral_status_t ret; + + tor_assert(pk); + tor_assert(port_cfgs); + tor_assert(address_out); + + switch (hs_version) { + case HS_VERSION_TWO: + { + ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams, + max_streams_close_circuit, auth_type, + auth_clients, address_out); + break; + } + case HS_VERSION_THREE: + { + /* XXX: Not implemented yet. */ + *address_out = tor_strdup("this is a v3 address"); + ret = RSAE_OKAY; + break; + } + default: + tor_assert_unreached(); + } + + return ret; +} + /** Called when we get a ADD_ONION command; parse the body, and set up * the new ephemeral Onion Service. */ static int @@ -4605,15 +4648,15 @@ handle_control_add_onion(control_connection_t *conn, }
/* Parse the "keytype:keyblob" argument. */ - crypto_pk_t *pk = NULL; + int hs_version = 0; + void *pk = NULL; const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL;
- pk = add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk, - &key_new_alg, &key_new_blob, - &err_msg); - if (!pk) { + if (add_onion_helper_keyarg(smartlist_get(args, 0), discard_pk, + &key_new_alg, &key_new_blob, &pk, &hs_version, + &err_msg) < 0) { if (err_msg) { connection_write_str_to_buf(err_msg, conn); tor_free(err_msg); @@ -4622,16 +4665,23 @@ handle_control_add_onion(control_connection_t *conn, } tor_assert(!err_msg);
+ /* Hidden service version 3 don't have client authentication support so if + * ClientAuth was given, send back an error. */ + if (hs_version == HS_VERSION_THREE && auth_clients) { + connection_printf_to_buf(conn, "513 ClientAuth not supported\r\n"); + goto out; + } + /* Create the HS, using private key pk, client authentication auth_type, * the list of auth_clients, and port config port_cfg. * rend_service_add_ephemeral() will take ownership of pk and port_cfg, * regardless of success/failure. */ char *service_id = NULL; - int ret = rend_service_add_ephemeral(pk, port_cfgs, max_streams, - max_streams_close_circuit, - auth_type, auth_clients, - &service_id); + int ret = add_onion_helper_add_service(hs_version, pk, port_cfgs, + max_streams, + max_streams_close_circuit, auth_type, + auth_clients, &service_id); port_cfgs = NULL; /* port_cfgs is now owned by the rendservice code. */ auth_clients = NULL; /* so is auth_clients */ switch (ret) { @@ -4724,9 +4774,10 @@ handle_control_add_onion(control_connection_t *conn, * Note: The error messages returned are deliberately vague to avoid echoing * key material. */ -STATIC crypto_pk_t * +STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, const char **key_new_alg_out, char **key_new_blob_out, + void **decoded_key, int *hs_version, char **err_msg_out) { smartlist_t *key_args = smartlist_new(); @@ -4734,7 +4785,9 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; - int ok = 0; + int ret = -1; + + *decoded_key = NULL;
smartlist_split_string(key_args, arg, ":", SPLIT_IGNORE_BLANK, 0); if (smartlist_len(key_args) != 2) { @@ -4746,6 +4799,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, static const char *key_type_new = "NEW"; static const char *key_type_best = "BEST"; static const char *key_type_rsa1024 = "RSA1024"; + static const char *key_type_ed25519_v3 = "ED25519-V3";
const char *key_type = smartlist_get(key_args, 0); const char *key_blob = smartlist_get(key_args, 1); @@ -4758,9 +4812,23 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, goto err; } if (crypto_pk_num_bits(pk) != PK_BYTES*8) { + crypto_pk_free(pk); err_msg = tor_strdup("512 Invalid RSA key size\r\n"); goto err; } + *decoded_key = pk; + *hs_version = HS_VERSION_TWO; + } else if (!strcasecmp(key_type_ed25519_v3, key_type)) { + /* "ED25519-V3:<Base64 Blob>" - Loading a pre-existing ed25519 key. */ + ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); + if (base64_decode((char *) sk->seckey, sizeof(sk->seckey), key_blob, + strlen(key_blob)) != sizeof(sk->seckey)) { + tor_free(sk); + err_msg = tor_strdup("512 Failed to decode ED25519-V3 key\r\n"); + goto err; + } + *decoded_key = sk; + *hs_version = HS_VERSION_THREE; } else if (!strcasecmp(key_type_new, key_type)) { /* "NEW:<Algorithm>" - Generating a new key, blob as algorithm. */ if (!strcasecmp(key_type_rsa1024, key_blob) || @@ -4774,12 +4842,38 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, } if (!discard_pk) { if (crypto_pk_base64_encode(pk, &key_new_blob)) { + crypto_pk_free(pk); tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", key_type_rsa1024); goto err; } key_new_alg = key_type_rsa1024; } + *decoded_key = pk; + *hs_version = HS_VERSION_TWO; + } else if (!strcasecmp(key_type_ed25519_v3, key_blob)) { + ed25519_secret_key_t *sk = tor_malloc_zero(sizeof(*sk)); + if (ed25519_secret_key_generate(sk, 1) < 0) { + tor_free(sk); + tor_asprintf(&err_msg, "551 Failed to generate %s key\r\n", + key_type_ed25519_v3); + goto err; + } + if (!discard_pk) { + ssize_t len = base64_encode_size(sizeof(sk->seckey), 0) + 1; + key_new_blob = tor_malloc_zero(len); + if (base64_encode(key_new_blob, len, (const char *) sk->seckey, + sizeof(sk->seckey), 0) != (len - 1)) { + tor_free(sk); + tor_free(key_new_blob); + tor_asprintf(&err_msg, "551 Failed to encode %s key\r\n", + key_type_ed25519_v3); + goto err; + } + key_new_alg = key_type_ed25519_v3; + } + *decoded_key = sk; + *hs_version = HS_VERSION_THREE; } else { err_msg = tor_strdup("513 Invalid key type\r\n"); goto err; @@ -4790,8 +4884,8 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, }
/* Succeded in loading or generating a private key. */ - tor_assert(pk); - ok = 1; + tor_assert(*decoded_key); + ret = 0;
err: SMARTLIST_FOREACH(key_args, char *, cp, { @@ -4800,10 +4894,6 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, }); smartlist_free(key_args);
- if (!ok) { - crypto_pk_free(pk); - pk = NULL; - } if (err_msg_out) { *err_msg_out = err_msg; } else { @@ -4812,7 +4902,7 @@ add_onion_helper_keyarg(const char *arg, int discard_pk, *key_new_alg_out = key_new_alg; *key_new_blob_out = key_new_blob;
- return pk; + return ret; }
/** Helper function to handle parsing a ClientAuth argument to the diff --git a/src/or/control.h b/src/or/control.h index 7ec182cb7..74601b978 100644 --- a/src/or/control.h +++ b/src/or/control.h @@ -256,10 +256,11 @@ void format_cell_stats(char **event_string, circuit_t *circ, cell_stats_t *cell_stats); STATIC char *get_bw_samples(void);
-STATIC crypto_pk_t *add_onion_helper_keyarg(const char *arg, int discard_pk, - const char **key_new_alg_out, - char **key_new_blob_out, - char **err_msg_out); +STATIC int add_onion_helper_keyarg(const char *arg, int discard_pk, + const char **key_new_alg_out, + char **key_new_blob_out, void **decoded_key, + int *hs_version, char **err_msg_out); + STATIC rend_authorized_client_t * add_onion_helper_clientauth(const char *arg, int *created, char **err_msg_out);
diff --git a/src/test/test_controller.c b/src/test/test_controller.c index 472fcb8c5..056d9333f 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -6,6 +6,7 @@ #include "bridges.h" #include "control.h" #include "entrynodes.h" +#include "hs_common.h" #include "networkstatus.h" #include "rendservice.h" #include "routerlist.h" @@ -15,8 +16,9 @@ static void test_add_onion_helper_keyarg(void *arg) { + int ret, hs_version; + void *pk_ptr = NULL; crypto_pk_t *pk = NULL; - crypto_pk_t *pk2 = NULL; const char *key_new_alg = NULL; char *key_new_blob = NULL; char *err_msg = NULL; @@ -26,38 +28,46 @@ test_add_onion_helper_keyarg(void *arg) (void) arg;
/* Test explicit RSA1024 key generation. */ - pk = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk); + ret = add_onion_helper_keyarg("NEW:RSA1024", 0, &key_new_alg, &key_new_blob, + &pk_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk_ptr); tt_str_op(key_new_alg, OP_EQ, "RSA1024"); tt_assert(key_new_blob); tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test "BEST" key generation (Assumes BEST = RSA1024). */ - crypto_pk_free(pk); + crypto_pk_free(pk_ptr); pk_ptr = NULL; tor_free(key_new_blob); - pk = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk); + ret = add_onion_helper_keyarg("NEW:BEST", 0, &key_new_alg, &key_new_blob, + &pk_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk_ptr); tt_str_op(key_new_alg, OP_EQ, "RSA1024"); tt_assert(key_new_blob); tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test discarding the private key. */ - crypto_pk_free(pk); + crypto_pk_free(pk_ptr); pk_ptr = NULL; tor_free(key_new_blob); - pk = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk); + ret = add_onion_helper_keyarg("NEW:BEST", 1, &key_new_alg, &key_new_blob, + &pk_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk_ptr); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_ptr_op(err_msg, OP_EQ, NULL);
/* Test generating a invalid key type. */ - crypto_pk_free(pk); - pk = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_ptr_op(pk, OP_EQ, NULL); + crypto_pk_free(pk_ptr); pk_ptr = NULL; + ret = add_onion_helper_keyarg("NEW:RSA512", 0, &key_new_alg, &key_new_blob, + &pk_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_ptr_op(pk_ptr, OP_EQ, NULL); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_assert(err_msg); @@ -67,41 +77,47 @@ test_add_onion_helper_keyarg(void *arg) pk = pk_generate(0); tt_int_op(0, OP_EQ, crypto_pk_base64_encode(pk, &encoded)); tor_asprintf(&arg_str, "RSA1024:%s", encoded); - pk2 = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_assert(pk2); + ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &pk_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_assert(pk_ptr); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_ptr_op(err_msg, OP_EQ, NULL); - tt_int_op(crypto_pk_cmp_keys(pk, pk2), OP_EQ, 0); + tt_int_op(crypto_pk_cmp_keys(pk, pk_ptr), OP_EQ, 0);
/* Test loading a invalid key type. */ tor_free(arg_str); crypto_pk_free(pk); pk = NULL; + crypto_pk_free(pk_ptr); pk_ptr = NULL; tor_asprintf(&arg_str, "RSA512:%s", encoded); - pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_ptr_op(pk, OP_EQ, NULL); + ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &pk_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_ptr_op(pk_ptr, OP_EQ, NULL); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_assert(err_msg);
/* Test loading a invalid key. */ tor_free(arg_str); - crypto_pk_free(pk); pk = NULL; + crypto_pk_free(pk_ptr); pk_ptr = NULL; tor_free(err_msg); encoded[strlen(encoded)/2] = '\0'; tor_asprintf(&arg_str, "RSA1024:%s", encoded); - pk = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, - &err_msg); - tt_ptr_op(pk, OP_EQ, NULL); + ret = add_onion_helper_keyarg(arg_str, 0, &key_new_alg, &key_new_blob, + &pk_ptr, &hs_version, &err_msg); + tt_int_op(ret, OP_EQ, -1); + tt_int_op(hs_version, OP_EQ, HS_VERSION_TWO); + tt_ptr_op(pk_ptr, OP_EQ, NULL); tt_ptr_op(key_new_alg, OP_EQ, NULL); tt_ptr_op(key_new_blob, OP_EQ, NULL); tt_assert(err_msg);
done: - crypto_pk_free(pk); - crypto_pk_free(pk2); + crypto_pk_free(pk_ptr); tor_free(key_new_blob); tor_free(err_msg); tor_free(encoded);