Browse Source

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>
David Goulet 6 years ago
parent
commit
49f21b6ba3
3 changed files with 158 additions and 51 deletions
  1. 108 18
      src/or/control.c
  2. 5 4
      src/or/control.h
  3. 45 29
      src/test/test_controller.c

+ 108 - 18
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

+ 5 - 4
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);
 

+ 45 - 29
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);