From 68aa52b4137fb3e3cec8c6efbb8c4785f0286a60 Mon Sep 17 00:00:00 2001 From: Mingye Chen Date: Fri, 25 Oct 2024 17:13:07 -0600 Subject: [PATCH] Support multiple keys for key rotation (#278) Let the station be able to use multiple private keys so that the key can be rotated over time. Updated components: - Detector: try multiple private keys while checking decoy registration in TLS client hello packets - Application: - separate the ZMQ private key apart from the application private key - try multiple keys while authenticating with the client in prefix transport Also fixes an issue with pfring interface name in the detector startup script, and issues with rust tests linking with libtapdance. --- build.rs | 15 +- cmd/application/app_config.toml | 26 +- cmd/application/main.go | 7 +- detect.c | 318 ++++++++++-------- internal/transport_integration_test.go | 2 +- libtapdance/Makefile | 2 +- loadkey.c | 85 +++-- loadkey.h | 23 +- pkg/station/lib/config.go | 69 +++- pkg/transports/connecting/dtls/dtls.go | 11 +- pkg/transports/wrapping/prefix/prefix.go | 32 +- pkg/transports/wrapping/prefix/prefix_test.go | 10 +- proto/signalling.rs | 1 + rust_foreign_interface.h | 7 +- scripts/start_detector.sh | 2 +- scripts/start_zbalance_ipc.sh | 2 +- src/elligator.rs | 130 ++++--- src/lib.rs | 21 +- src/process_packet.rs | 15 +- src/signalling.rs | 1 + 20 files changed, 495 insertions(+), 284 deletions(-) diff --git a/build.rs b/build.rs index 0896ba5d..9083d8e7 100644 --- a/build.rs +++ b/build.rs @@ -2,7 +2,20 @@ extern crate cc; fn main() { cc::Build::new() - .file("libtapdance/tapdance.c") + .files(&[ + "libtapdance/tapdance.c", + "libtapdance/ssl_api.c", + "libtapdance/elligator2.c", + "libtapdance/curve25519-donna-c64.c", + "libtapdance/loadkey.c", + "libtapdance/tapdance_rst_spoof.c", + "libtapdance/tapdance_rust_util.c", + ]) .include("src") .compile("libtapdance.a"); + + println!("cargo:rustc-link-lib=tapdance"); + println!("cargo:rustc-link-search=libtapdance"); + println!("cargo:rustc-link-lib=gmp"); + println!("cargo:rustc-link-lib=crypto"); } diff --git a/cmd/application/app_config.toml b/cmd/application/app_config.toml index 3758ef96..b1ae73be 100644 --- a/cmd/application/app_config.toml +++ b/cmd/application/app_config.toml @@ -1,13 +1,16 @@ ## ------ Application General ------ -# Absolute path to private key to use when authenticating with servers. +# Absolute path to private key to use when authenticating with clients. # Can be either privkey or privkey || pubkey; only first 32 bytes will # be used. If this is blank then the environment variable CJ_PRIVKEY # which is defined in conjure.conf will be used (if that fails to parse # the station will shutdown). privkey_path = "" +# Same as privkey but used for zmq auth +zmq_privkey_path = "" + # Log level, one of the following: info, error, warn, debug, trace log_level = "error" @@ -60,13 +63,13 @@ ingest_worker_count = 100 # be othewise firewalled. covert_blocklist_domains = ["localhost"] covert_blocklist_subnets = [ - "127.0.0.1/32", # localhost ipv4 - "10.0.0.0/8", # reserved ipv4 - "172.16.0.0/12", # reserved ipv4 - "192.168.0.0/16", # reserved ipv4 - "fc00::/7 ", # private network ipv6 - "fe80::0/16", # link local ipv6 - "::1/128", # localhost ipv6 + "127.0.0.1/32", # localhost ipv4 + "10.0.0.0/8", # reserved ipv4 + "172.16.0.0/12", # reserved ipv4 + "192.168.0.0/16", # reserved ipv4 + "fc00::/7 ", # private network ipv6 + "fe80::0/16", # link local ipv6 + "::1/128", # localhost ipv6 ] # Automatically add all addresses and subnets associated with local devices to @@ -81,16 +84,13 @@ covert_allowlist_subnets = [] # If a registration is received and the phantom address is in one of these # subnets the registration will be dropped. This allows us to exclude subnets to # prevent stations from interfering. -phantom_blocklist = [ ] +phantom_blocklist = [] # List of addresses to filter out traffic from the detector. The primary functionality # of this is to prevent liveness testing from other stations in a conjure cluster from # clogging up the logs with connection notifications. To accomplish this goal add all station # ip addresses to this list when configuring station detectors. -detector_filter_list = [ - "127.0.0.1", - "::1", -] +detector_filter_list = ["127.0.0.1", "::1"] ## ------ GeoIP Info ------ diff --git a/cmd/application/main.go b/cmd/application/main.go index 2646b9b8..13466aa7 100644 --- a/cmd/application/main.go +++ b/cmd/application/main.go @@ -107,6 +107,11 @@ func main() { logger.Fatalf("error parseing private key: %s", err) } + zmqPrivKey, err := conf.ParseZMQPrivateKey() + if err != nil { + logger.Fatalf("error parseing private key: %s", err) + } + var prefixTransport cj.Transport if conf.DisableDefaultPrefixes { prefixTransport, err = prefix.New(privkey, conf.PrefixFilePath) @@ -130,7 +135,7 @@ func main() { ctx, cancel := context.WithCancel(context.Background()) wg := new(sync.WaitGroup) regChan := make(chan interface{}, 10000) - zmqIngester, err := cj.NewZMQIngest(zmqAddress, regChan, privkey, conf.ZMQConfig) + zmqIngester, err := cj.NewZMQIngest(zmqAddress, regChan, zmqPrivKey, conf.ZMQConfig) if err != nil { logger.Fatal("error creating ZMQ Ingest: %w", err) } diff --git a/detect.c b/detect.c index 806c5abe..edd5b776 100644 --- a/detect.c +++ b/detect.c @@ -27,15 +27,15 @@ #define pfring_maybezc_stat pfring_stat #define pfring_maybezc_stats pfring_stats #endif -#define likely(x) __builtin_expect((x),1) -#define unlikely(x) __builtin_expect((x),0) +#define likely(x) __builtin_expect((x), 1) +#define unlikely(x) __builtin_expect((x), 0) #include "rust_foreign_interface.h" #include "loadkey.h" // Provided by libtapdance -size_t write_reporter(uint8_t* buf, size_t len); +size_t write_reporter(uint8_t *buf, size_t len); // NOTE / TODO: // Once we are receiving filtered, 443-only traffic, we might need a lower @@ -55,32 +55,34 @@ size_t write_reporter(uint8_t* buf, size_t len); #define MAX_NUM_FORKED_PROCS 256 pid_t g_forked_pids[MAX_NUM_FORKED_PROCS]; #ifdef TAPDANCE_USE_PF_RING_ZERO_COPY -pfring_zc_queue* g_ring = 0; -pfring_zc_buffer_pool* g_pool = 0; -pfring_zc_pkt_buff* g_buf[PF_BURST_SIZE]; +pfring_zc_queue *g_ring = 0; +pfring_zc_buffer_pool *g_pool = 0; +pfring_zc_pkt_buff *g_buf[PF_BURST_SIZE]; #else -pfring* g_ring = 0; -const char* g_iface_name = 0; +pfring *g_ring = 0; +const char *g_iface_name = 0; #endif int g_num_worker_procs = 0; -void* g_rust_cli_conf_proto_ptr = 0; -void* g_rust_failed_map = 0; +void *g_rust_cli_conf_proto_ptr = 0; +void *g_rust_failed_map = 0; int g_update_cli_conf_when_convenient = 0; int g_update_overloaded_decoys_when_convenient = 0; -#define TIMESPEC_DIFF(a, b) ((a.tv_sec - b.tv_sec)*1000000000LL + \ +#define TIMESPEC_DIFF(a, b) ((a.tv_sec - b.tv_sec) * 1000000000LL + \ ((int64_t)a.tv_nsec - (int64_t)b.tv_nsec)) void the_program(uint8_t core_id, unsigned int log_interval, - uint8_t* station_key, char* workers_socket_addr) + uint8_t (*station_keys)[TD_KEYLEN_BYTES], uint8_t numkeys, + char *workers_socket_addr) { - struct RustGlobalsStruct rust_globals = rust_detect_init(core_id, station_key, workers_socket_addr); - //g_rust_failed_map = rust_globals.fail_map; - //g_rust_cli_conf_proto_ptr = rust_globals.cli_conf; - void* rust_ptr = rust_globals.global; + struct RustGlobalsStruct rust_globals = rust_detect_init(core_id, station_keys, numkeys, workers_socket_addr); - //rust_update_cli_conf(g_rust_cli_conf_proto_ptr); + // g_rust_failed_map = rust_globals.fail_map; + // g_rust_cli_conf_proto_ptr = rust_globals.cli_conf; + void *rust_ptr = rust_globals.global; + + // rust_update_cli_conf(g_rust_cli_conf_proto_ptr); printf(">>>> starting core %d\n", core_id); #ifdef TAPDANCE_USE_PF_RING_ZERO_COPY printf("Zero-copy TapDance child proc started on core %d!\n", core_id); @@ -88,7 +90,7 @@ void the_program(uint8_t core_id, unsigned int log_interval, printf("NON-zero-copy TapDance child proc started on core %d!\n", core_id); // For pfring_recv()s struct pfring_pkthdr hdr = {0}; - uint8_t* pkt_buf_ptr; + uint8_t *pkt_buf_ptr; #endif int recvd_pkts = 0; @@ -102,7 +104,6 @@ void the_program(uint8_t core_id, unsigned int log_interval, minimum_sleep_dur.tv_sec = 0; minimum_sleep_dur.tv_nsec = 1; - // For rare (once per second) rust_periodic_cleanup() and packet drop check struct timespec prev_rust_drop; struct timespec prev_status_report; @@ -118,16 +119,16 @@ void the_program(uint8_t core_id, unsigned int log_interval, unsigned long drops_prev = stats.drop; unsigned long drops_cur = stats.drop; - while(1) + while (1) { - while(recvd_pkts < PKT_BURST_SIZE) + while (recvd_pkts < PKT_BURST_SIZE) { #ifdef TAPDANCE_USE_PF_RING_ZERO_COPY int cur_recvd_pkts; - if((cur_recvd_pkts = - pfring_zc_recv_pkt_burst(g_ring, g_buf, PF_BURST_SIZE, 0)) > 0) + if ((cur_recvd_pkts = + pfring_zc_recv_pkt_burst(g_ring, g_buf, PF_BURST_SIZE, 0)) > 0) { - for(int i=0; i< cur_recvd_pkts; i++) + for (int i = 0; i < cur_recvd_pkts; i++) { rust_process_packet( rust_ptr, pfring_zc_pkt_buff_data(g_buf[i], g_ring), @@ -138,7 +139,7 @@ void the_program(uint8_t core_id, unsigned int log_interval, else break; #else - if(pfring_recv(g_ring, &pkt_buf_ptr, 0, &hdr, 0) > 0) + if (pfring_recv(g_ring, &pkt_buf_ptr, 0, &hdr, 0) > 0) rust_process_packet(rust_ptr, pkt_buf_ptr, hdr.len); else break; @@ -153,29 +154,29 @@ void the_program(uint8_t core_id, unsigned int log_interval, clock_gettime(CLOCK_MONOTONIC, &after_rust_events); rust_dur_ns = TIMESPEC_DIFF(after_rust_events, before_rust_events); - if(unlikely(recvd_pkts == 0 && rust_dur_ns < DESIRED_PAUSE_DUR_NS)) + if (unlikely(recvd_pkts == 0 && rust_dur_ns < DESIRED_PAUSE_DUR_NS)) nanosleep(&minimum_sleep_dur, 0); recvd_pkts = 0; clock_gettime(CLOCK_MONOTONIC, &cur_time_ns); ns_since_last_drop = TIMESPEC_DIFF(cur_time_ns, prev_rust_drop); ns_since_status_report = TIMESPEC_DIFF(cur_time_ns, prev_status_report); - if(unlikely(ns_since_last_drop > 100LL*1000LL*1000LL)) // 100ms + if (unlikely(ns_since_last_drop > 100LL * 1000LL * 1000LL)) // 100ms { prev_rust_drop = cur_time_ns; rust_periodic_cleanup(rust_ptr); - if(unlikely(g_update_cli_conf_when_convenient)) + if (unlikely(g_update_cli_conf_when_convenient)) { g_update_cli_conf_when_convenient = 0; - //rust_update_cli_conf(g_rust_cli_conf_proto_ptr); + // rust_update_cli_conf(g_rust_cli_conf_proto_ptr); } - if(unlikely(g_update_overloaded_decoys_when_convenient)) + if (unlikely(g_update_overloaded_decoys_when_convenient)) { g_update_overloaded_decoys_when_convenient = 0; - //rust_update_overloaded_decoys(rust_ptr); + // rust_update_overloaded_decoys(rust_ptr); } } - if(unlikely(ns_since_status_report > log_interval_ns)) + if (unlikely(ns_since_status_report > log_interval_ns)) { prev_status_report = cur_time_ns; rust_periodic_report(rust_ptr); @@ -186,7 +187,7 @@ void the_program(uint8_t core_id, unsigned int log_interval, char buf[50]; // Enough for "drop x x\n" for x=2**64 snprintf(buf, sizeof(buf), "drop %lu %lu\n", (drops_cur - drops_prev), drops_cur); - write_reporter((uint8_t*)buf, strlen(buf)); + write_reporter((uint8_t *)buf, strlen(buf)); drops_prev = drops_cur; } @@ -198,12 +199,12 @@ void ignore_sigpipe(int sig) printf("received a SIGPIPE, ignoring\n"); } -static void notify_cli_conf_file_update(int sig, siginfo_t* si, void* junk) +static void notify_cli_conf_file_update(int sig, siginfo_t *si, void *junk) { g_update_cli_conf_when_convenient = 1; } -static void notify_overloaded_decoys_file_update(int sig, siginfo_t* si, - void* junk) +static void notify_overloaded_decoys_file_update(int sig, siginfo_t *si, + void *junk) { g_update_overloaded_decoys_when_convenient = 1; } @@ -211,11 +212,14 @@ static void notify_overloaded_decoys_file_update(int sig, siginfo_t* si, void sigproc_child(int sig) { static char called = 0; - if(called) return; else called = 1; + if (called) + return; + else + called = 1; #ifdef TAPDANCE_USE_PF_RING_ZERO_COPY pfring_zc_queue_breakloop(g_ring); - for (int i=0; i>> Child proc %d created\n", core_affinity); startup_pfring_maybezc(cluster_id, proc_ind); @@ -337,7 +346,7 @@ pid_t start_tapdance_process(int core_affinity, unsigned int cluster_id, signal(SIGINT, sigproc_child); signal(SIGTERM, sigproc_child); signal(SIGPIPE, ignore_sigpipe); - the_program(proc_ind, log_interval, station_key, workers_socket_addr); + the_program(proc_ind, log_interval, station_keys, numkeys, workers_socket_addr); } printf("Core %d: PID %d, lcore %d\n", proc_ind, the_pid, core_affinity); return the_pid; @@ -346,44 +355,49 @@ pid_t start_tapdance_process(int core_affinity, unsigned int cluster_id, struct cmd_options { // Number of cores to spread across. - uint8_t cpu_procs; + uint8_t cpu_procs; // An integer that works as a handle to a PF_RING "cluster". These don't // need to be allocated or whatever; just pick one to pass as the -c arg to // zbalance_ipc, and pass the same one as the -c of this program. Can be // 1, 99, probably whatever. - unsigned int cluster_id; + unsigned int cluster_id; // Instead of starting at core 0 to core $cpu_procs, we'll do core // $core_affinity_offset to core $core_affinity_offset+$cpu_procs. // This allows us to run debug/production pf_rings on different cores // entirely (which rust likes), and with different cluster_ids. - uint8_t core_affinity_offset; + uint8_t core_affinity_offset; - // instead of connecting to zbalance $cluster_id@0, $cluster_id@1, ... + // instead of connecting to zbalance $cluster_id@0, $cluster_id@1, ... // start at $cluster_id@$pfring_offset. - uint8_t pfring_offset; + uint8_t pfring_offset; // In seconds, interval between logging of bandwidth, tag checks/s, etc. - unsigned int log_interval; - uint8_t* station_key; // the station key - uint8_t* public_key; // the public key, used only for diagnostic - // (all nuls if not provided) - int skip_core; // -1 if not skipping any core, otherwise the core to skip - char* zmq_address; // address of output ZMQ socket to bind - char* zmq_worker_address; // address of ZMQ socket to bind for communication between threads -}; + unsigned int log_interval; + + // Number of keys + uint8_t numkeys; + + // Station keys (supports multiple keys) + uint8_t (*station_key)[TD_KEYLEN_BYTES]; // Array of station keys + + // Public keys (supports multiple keys) + uint8_t (*public_key)[TD_KEYLEN_BYTES]; // Array of public keys used for diagnostics -static uint8_t station_key[TD_KEYLEN_BYTES] = { - 224, 192, 103, 26, 96, 135, 130, 174, - 250, 208, 30, 113, 46, 128, 127, 111, - 215, 199, 5, 141, 38, 124, 34, 127, - 102, 142, 245, 81, 49, 70, 119, 119 + int skip_core; // -1 if not skipping any core, otherwise the core to skip + char *zmq_address; // address of output ZMQ socket to bind + char *zmq_worker_address; // address of ZMQ socket to bind for communication between threads }; -static uint8_t public_key[TD_KEYLEN_BYTES] = { 0 }; +static uint8_t station_key[][TD_KEYLEN_BYTES] = {{224, 192, 103, 26, 96, 135, 130, 174, + 250, 208, 30, 113, 46, 128, 127, 111, + 215, 199, 5, 141, 38, 124, 34, 127, + 102, 142, 245, 81, 49, 70, 119, 119}}; -void parse_cmd_args(int argc, char* argv[], struct cmd_options* options) +static uint8_t public_key[][TD_KEYLEN_BYTES] = {{0}}; + +void parse_cmd_args(int argc, char *argv[], struct cmd_options *options) { // Defaults, development int32_t cpu_procs_i32 = 1; // struct member is a u8! catch overflow! @@ -391,59 +405,59 @@ void parse_cmd_args(int argc, char* argv[], struct cmd_options* options) options->core_affinity_offset = 0; options->pfring_offset = 0; options->log_interval = 1000; // milliseconds - int skip_core = -1; // If >0, skip this core when incrementing + int skip_core = -1; // If >0, skip this core when incrementing options->zmq_address = "ipc://@detector"; options->zmq_worker_address = "ipc://@detector-workers"; - char* keyfile_name = 0; + char *keyfiles_path = 0; options->station_key = station_key; options->public_key = public_key; char c; - while ((c = getopt(argc,argv,"i:n:c:o:l:K:s:a:w:z:")) != -1) + while ((c = getopt(argc, argv, "i:n:c:o:l:K:s:a:w:z:")) != -1) { switch (c) { - case 'i': + case 'i': #ifdef TAPDANCE_USE_PF_RING_ZERO_COPY - fprintf(stderr, "Warning: -i unused in zero copy mode\n"); + fprintf(stderr, "Warning: -i unused in zero copy mode\n"); #else - g_iface_name = optarg; + g_iface_name = optarg; #endif - break; - case 'n': - cpu_procs_i32 = atoi(optarg); - break; - case 'c': - options->cluster_id = atoi(optarg); - break; - case 'o': - options->core_affinity_offset = atoi(optarg); - break; - case 'l': - options->log_interval = 1000*atoi(optarg); - break; - case 'K': - keyfile_name = optarg; - break; - case 's': - skip_core = atoi(optarg); - break; - case 'a': - options->zmq_address = malloc(strlen(optarg)); - strcpy(options->zmq_address, optarg); - break; - case 'w': - options->zmq_worker_address = malloc(strlen(optarg)); - strcpy(options->zmq_worker_address, optarg); - break; - case 'z': - options->pfring_offset = atoi(optarg); - break; - default: - fprintf(stderr, "Unknown option %c\n", c); - break; + break; + case 'n': + cpu_procs_i32 = atoi(optarg); + break; + case 'c': + options->cluster_id = atoi(optarg); + break; + case 'o': + options->core_affinity_offset = atoi(optarg); + break; + case 'l': + options->log_interval = 1000 * atoi(optarg); + break; + case 'K': + keyfiles_path = optarg; + break; + case 's': + skip_core = atoi(optarg); + break; + case 'a': + options->zmq_address = malloc(strlen(optarg)); + strcpy(options->zmq_address, optarg); + break; + case 'w': + options->zmq_worker_address = malloc(strlen(optarg)); + strcpy(options->zmq_worker_address, optarg); + break; + case 'z': + options->pfring_offset = atoi(optarg); + break; + default: + fprintf(stderr, "Unknown option %c\n", c); + break; } } if (options->cluster_id == 987654321) @@ -452,22 +466,28 @@ void parse_cmd_args(int argc, char* argv[], struct cmd_options* options) exit(-1); } - if (keyfile_name != NULL) + if (keyfiles_path != NULL) { - int rc = td_load_station_key(keyfile_name, options->station_key, - options->public_key); + int rc = td_load_station_keys(keyfiles_path, options->station_key, + options->public_key, &options->numkeys, 100); + printf("%d private keys\n", options->numkeys); if (rc != 0) { fprintf(stderr, "Error: can't load keyfile [%s]: %d\n", - keyfile_name, rc); + keyfiles_path, rc); exit(-1); } else { - printf("Using public key: "); - td_print_key(options->public_key); + printf("Using public keys: \n"); + for (int i = 0; i < options->numkeys; i++) + { + printf("Key %d: ", i + 1); + td_print_key(options->public_key[i]); + printf("\n"); + } printf("\n"); - fflush(stdout); + fflush(stdout); } } else @@ -476,16 +496,18 @@ void parse_cmd_args(int argc, char* argv[], struct cmd_options* options) } int last_core_id_requested = (options->core_affinity_offset + - cpu_procs_i32) - 1; - if (skip_core > 0) last_core_id_requested++; + cpu_procs_i32) - + 1; + if (skip_core > 0) + last_core_id_requested++; if (last_core_id_requested >= MAX_NUM_FORKED_PROCS) { fprintf(stderr, - "Error: highest requested core ID %d is too high of a core ID to\n" - "ask for. This program can only use 0 through %d inclusive (even\n" - "if your machine has more).\n", - last_core_id_requested, MAX_NUM_FORKED_PROCS-1); - if(options->core_affinity_offset != 0) + "Error: highest requested core ID %d is too high of a core ID to\n" + "ask for. This program can only use 0 through %d inclusive (even\n" + "if your machine has more).\n", + last_core_id_requested, MAX_NUM_FORKED_PROCS - 1); + if (options->core_affinity_offset != 0) { fprintf(stderr, "Hint: you specified a non-zero core offset (-o).\n" "Try again without that argument.\n"); @@ -493,13 +515,14 @@ void parse_cmd_args(int argc, char* argv[], struct cmd_options* options) exit(-1); } int cores_online = get_nprocs_conf(); - if(last_core_id_requested >= cores_online) + if (last_core_id_requested >= cores_online) { fprintf(stderr, - "Error: highest requested core ID %d is beyond the range of core\n" - "IDs currently available on this machine. Cores 0 to %d inclusive\n" - "are available.\n", last_core_id_requested, cores_online - 1); - if(options->core_affinity_offset != 0) + "Error: highest requested core ID %d is beyond the range of core\n" + "IDs currently available on this machine. Cores 0 to %d inclusive\n" + "are available.\n", + last_core_id_requested, cores_online - 1); + if (options->core_affinity_offset != 0) { fprintf(stderr, "Hint: you specified a non-zero core offset (-o).\n" "Try again without that argument.\n"); @@ -507,7 +530,7 @@ void parse_cmd_args(int argc, char* argv[], struct cmd_options* options) exit(-1); } #ifndef TAPDANCE_USE_PF_RING_ZERO_COPY - if(g_iface_name == 0) + if (g_iface_name == 0) { fprintf(stderr, "Error: you are running in non-zero-copy mode and did\n" " not specify a network interface with -i.\n"); @@ -524,7 +547,8 @@ void parse_cmd_args(int argc, char* argv[], struct cmd_options* options) int handle_zmq_proxy(char *socket_addr, char *workers_socket_addr) { int pid = fork(); - if (pid == 0) { + if (pid == 0) + { // Set up ZMQ sockets, one for publishing to the proxy and one for taking in // messages from other threads void *ctx = zmq_ctx_new(); @@ -533,14 +557,16 @@ int handle_zmq_proxy(char *socket_addr, char *workers_socket_addr) // Bind the socket for publishing to the proxy printf("binding zmq socket to %s\n", socket_addr); int rc = zmq_bind(pub, socket_addr); - if (rc != 0) { + if (rc != 0) + { printf("bind on pub socket failed: %s\n", zmq_strerror(errno)); return rc; } void *sub = zmq_socket(ctx, ZMQ_SUB); rc = zmq_setsockopt(sub, ZMQ_SUBSCRIBE, NULL, 0); - if (rc != 0) { + if (rc != 0) + { printf("failed to set sock opt: %s\n", zmq_strerror(errno)); return rc; } @@ -550,14 +576,16 @@ int handle_zmq_proxy(char *socket_addr, char *workers_socket_addr) // need to use IPC rather than inproc communication) printf("binding zmq worker socket to %s\n", workers_socket_addr); rc = zmq_bind(sub, workers_socket_addr); - if (rc != 0) { + if (rc != 0) + { printf("bind on sub socket failed: %s\n", zmq_strerror(errno)); return rc; } // Proxy traffic between worker threads and the outgoing socket rc = zmq_proxy(sub, pub, NULL); - if (rc != 0) { + if (rc != 0) + { printf("proxy returned error: %s\n", zmq_strerror(errno)); return rc; } @@ -565,7 +593,7 @@ int handle_zmq_proxy(char *socket_addr, char *workers_socket_addr) return 0; } -int main(int argc, char* argv[]) +int main(int argc, char *argv[]) { struct cmd_options options; parse_cmd_args(argc, argv, &options); @@ -596,21 +624,22 @@ int main(int argc, char* argv[]) int i; int core_num = options.core_affinity_offset; - for (i=0; i #include #include +#include +#include #include "loadkey.h" -// Load a station key (and the matching public key) from the given file. -// Returns 0 if successful, < 0 otherwise. -// -// The key file must contain the private key, followed by the public key. -// -// The key file could also contain gibberish -- they do not -// contain any sort of integrity check. So take care with your keys. - -int td_load_station_key(const char *fname, - uint8_t stationkey[TD_KEYLEN_BYTES], - uint8_t pubkey[TD_KEYLEN_BYTES]) +// Load a station key and public key from a single file +int td_load_single_station_key(const char *fname, uint8_t stationkey[TD_KEYLEN_BYTES], uint8_t pubkey[TD_KEYLEN_BYTES]) { FILE *fin = fopen(fname, "r"); int rc; @@ -25,29 +18,77 @@ int td_load_station_key(const char *fname, return -1; rc = fread(stationkey, TD_KEYLEN_BYTES, 1, fin); - if (rc != 1) { + if (rc != 1) + { fclose(fin); return -2; } - // If it's a station key, then the public key follows it (as an idenitifer) rc = fread(pubkey, TD_KEYLEN_BYTES, 1, fin); - if (rc != 1) { + if (rc != 1) + { fclose(fin); return -3; } - // See if there's anything more in the file, which might indicate that it's - // a proper key file. - if (1 != fread(&extra, sizeof(extra), 1, fin)) { - // TODO: ? + // Check for any extra data in the file (optional step) + if (1 == fread(&extra, sizeof(extra), 1, fin)) + { + // Handle the extra data if necessary + // TODO: Decide if you want to return an error or process the extra data } fclose(fin); - return 0; } +// Function to load station keys from a file or directory +int td_load_station_keys(const char *path, uint8_t stationkeys[][TD_KEYLEN_BYTES], uint8_t pubkeys[][TD_KEYLEN_BYTES], uint8_t *key_count, int max_keys) +{ + struct stat path_stat; + if (stat(path, &path_stat) != 0) + { + return -1; // Error accessing the path + } + + if (S_ISDIR(path_stat.st_mode)) + { + DIR *dir = opendir(path); + if (!dir) + { + return -2; // Failed to open directory + } + + struct dirent *entry; + int count = 0; + while ((entry = readdir(dir)) != NULL && count < max_keys) + { + char filepath[1024]; + snprintf(filepath, sizeof(filepath), "%s/%s", path, entry->d_name); + + if (td_load_single_station_key(filepath, stationkeys[count], pubkeys[count]) == 0) + { + count++; + } + } + closedir(dir); + *key_count = count; + return (count > 0) ? 0 : -3; // Return 0 if at least one key is loaded, otherwise error + } + else + { + if (td_load_single_station_key(path, stationkeys[0], pubkeys[0]) == 0) + { + *key_count = 1; + return 0; + } + else + { + return -4; // Error loading key from file + } + } +} + // Load a public key from the given file. Returns 0 if successful, < 0 otherwise int td_load_public_key(const char *fname, uint8_t keybuf[TD_KEYLEN_BYTES]) { @@ -59,14 +100,16 @@ int td_load_public_key(const char *fname, uint8_t keybuf[TD_KEYLEN_BYTES]) return -1; rc = fread(keybuf, TD_KEYLEN_BYTES, 1, fin); - if (rc != 1) { + if (rc != 1) + { fclose(fin); return -2; } // See if there's anything more in the file, which might indicate that it's // a proper key file. - if (1 != fread(&extra, sizeof(extra), 1, fin)) { + if (1 != fread(&extra, sizeof(extra), 1, fin)) + { // TODO: ? } fclose(fin); diff --git a/loadkey.h b/loadkey.h index c6a25aa2..f502253e 100644 --- a/loadkey.h +++ b/loadkey.h @@ -1,20 +1,27 @@ #ifndef _TD_LOADKEY_H_ #define _TD_LOADKEY_H_ 1 -#define TD_KEYLEN_BYTES (32) -#define TD_IDLEN_BYTES (16) +#define TD_KEYLEN_BYTES (32) +#define TD_IDLEN_BYTES (16) #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif // __cplusplus -int td_load_station_key(const char *fname, - uint8_t stationkey[TD_KEYLEN_BYTES], - uint8_t pubkey[TD_KEYLEN_BYTES]); + int td_load_single_station_key(const char *fname, + uint8_t stationkey[TD_KEYLEN_BYTES], + uint8_t pubkey[TD_KEYLEN_BYTES]); -void td_print_key(const uint8_t key[TD_KEYLEN_BYTES]); + int td_load_station_keys(const char *path, + uint8_t stationkeys[][TD_KEYLEN_BYTES], + uint8_t pubkeys[][TD_KEYLEN_BYTES], + uint8_t *key_count, + int max_keys); -char *td_key2str(const uint8_t key[TD_KEYLEN_BYTES]); + void td_print_key(const uint8_t key[TD_KEYLEN_BYTES]); + + char *td_key2str(const uint8_t key[TD_KEYLEN_BYTES]); #ifdef __cplusplus }; diff --git a/pkg/station/lib/config.go b/pkg/station/lib/config.go index 8bfca8ad..e8089983 100644 --- a/pkg/station/lib/config.go +++ b/pkg/station/lib/config.go @@ -3,6 +3,7 @@ package lib import ( "fmt" "os" + "path/filepath" "github.com/BurntSushi/toml" ) @@ -18,6 +19,9 @@ type Config struct { // Path to private key file PrivateKeyPath string `toml:"privkey_path"` + // Path to ZMQ private key file + ZMQPrivateKeyPath string `toml:"zmq_privkey_path"` + // PrefixFilePath provides a path to a file containing supported prefix specifications for the // prefix transport. // [TODO] refactor into a more general transport config object @@ -43,27 +47,78 @@ func ParseConfig() (*Config, error) { // PrivateKeyLength is the expected length of the station (ed25519) private key in bytes. const PrivateKeyLength = 32 +func (c *Config) ParseZMQPrivateKey() ([PrivateKeyLength]byte, error) { + privkeyPath := c.ZMQPrivateKeyPath + if privkeyPath == "" { + privkeyPath = os.Getenv("ZMQ_PRIVKEY") + } + if privkeyPath == "" { + return [PrivateKeyLength]byte{}, fmt.Errorf("no path to ZMQ private key") + } + + return loadPrivateKey(privkeyPath) +} + // ParsePrivateKey tries to use either the PrivateKeyPath (`privkey_path`) config variable or the -// CJ_PRIVKEY environment variable to locate the file from which it can parse the station private key -func (c *Config) ParsePrivateKey() ([32]byte, error) { +// CJ_PRIVKEY environment variable to locate the file or directory containing the station private key(s). +func (c *Config) ParsePrivateKey() ([][PrivateKeyLength]byte, error) { privkeyPath := c.PrivateKeyPath if privkeyPath == "" { privkeyPath = os.Getenv("CJ_PRIVKEY") } if privkeyPath == "" { - return [32]byte{}, fmt.Errorf("no path to private key") + return nil, fmt.Errorf("no path to application private key") + } + + fileInfo, err := os.Stat(privkeyPath) + if err != nil { + return nil, fmt.Errorf("failed to access private key path: %w", err) } - privkey, err := os.ReadFile(privkeyPath) + if fileInfo.IsDir() { + files, err := os.ReadDir(privkeyPath) + if err != nil { + return nil, fmt.Errorf("failed to read directory: %w", err) + } + + var keys [][PrivateKeyLength]byte + + for _, file := range files { + if !file.IsDir() { + key, err := loadPrivateKey(filepath.Join(privkeyPath, file.Name())) + if err != nil { + return nil, err + } + keys = append(keys, key) + } + } + + if len(keys) == 0 { + return nil, fmt.Errorf("no valid keys found in directory") + } + + return keys, nil + } + + key, err := loadPrivateKey(privkeyPath) + if err != nil { + return nil, err + } + + return [][PrivateKeyLength]byte{key}, nil +} + +func loadPrivateKey(path string) ([32]byte, error) { + privkey, err := os.ReadFile(path) if err != nil { - return [32]byte{}, fmt.Errorf("failed to load private key: %w", err) + return [32]byte{}, fmt.Errorf("failed to load private key from %s: %w", path, err) } if len(privkey) < PrivateKeyLength { - return [32]byte{}, fmt.Errorf("privkey error - not enough bytes") + return [32]byte{}, fmt.Errorf("privkey error - not enough bytes in %s", path) } - var out [32]byte + var out [PrivateKeyLength]byte copy(out[:], privkey[:]) return out, nil } diff --git a/pkg/transports/connecting/dtls/dtls.go b/pkg/transports/connecting/dtls/dtls.go index 824d0ef7..c23d5103 100644 --- a/pkg/transports/connecting/dtls/dtls.go +++ b/pkg/transports/connecting/dtls/dtls.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net" - "strings" "github.com/libp2p/go-reuseport" "github.com/refraction-networking/conjure/pkg/core" @@ -175,12 +174,12 @@ func (t *Transport) Connect(ctx context.Context, reg transports.Registration) (n // combine errors into a single error var combinedErr error - if len(errs) > 0 { - errStrings := make([]string, len(errs)) - for i, err := range errs { - errStrings[i] = err.Error() + for _, err := range errs { + if combinedErr == nil { + combinedErr = err + } else { + combinedErr = fmt.Errorf("%v, %v", combinedErr, err) } - combinedErr = fmt.Errorf(strings.Join(errStrings, "; ")) } return nil, combinedErr // if we reached here, both attempts failed diff --git a/pkg/transports/wrapping/prefix/prefix.go b/pkg/transports/wrapping/prefix/prefix.go index 414153d5..89cc8f3f 100644 --- a/pkg/transports/wrapping/prefix/prefix.go +++ b/pkg/transports/wrapping/prefix/prefix.go @@ -170,7 +170,7 @@ var defaultPrefixes = map[PrefixID]prefix{ type Transport struct { SupportedPrefixes map[PrefixID]prefix TagObfuscator transports.Obfuscator - Privkey [32]byte + Privkeys [][32]byte } // Name returns the human-friendly name of the transport, implementing the @@ -281,6 +281,19 @@ func (t Transport) WrapConnection(data *bytes.Buffer, c net.Conn, originalDst ne return reg, transports.PrependToConn(c, data), nil } +func (t Transport) getReg(obfuscatedID []byte, rm transports.RegManager, originalDst net.IP) (transports.Registration, error) { + for _, privkey := range t.Privkeys { + hmacID, err := t.TagObfuscator.TryReveal(obfuscatedID, privkey) + if err != nil || hmacID == nil { + continue + } + if reg, ok := rm.GetRegistrations(originalDst)[string(hmacID)]; ok { + return reg, nil + } + } + return nil, fmt.Errorf("no matching key") +} + func (t Transport) tryFindReg(data *bytes.Buffer, originalDst net.IP, regManager transports.RegManager) (transports.Registration, error) { if data.Len() == 0 { return nil, transports.ErrTryAgain @@ -324,13 +337,8 @@ func (t Transport) tryFindReg(data *bytes.Buffer, originalDst net.IP, regManager obfuscatedID = data.Bytes()[prefix.Offset : prefix.Offset+minTagLength] // } - hmacID, err := t.TagObfuscator.TryReveal(obfuscatedID, t.Privkey) - if err != nil || hmacID == nil { - continue - } - - reg, ok := regManager.GetRegistrations(originalDst)[string(hmacID)] - if !ok { + reg, err := t.getReg(obfuscatedID, regManager, originalDst) + if err != nil { continue } @@ -367,7 +375,7 @@ func (t Transport) tryFindReg(data *bytes.Buffer, originalDst net.IP, regManager // prefixes. The optional filepath specifies a file from which to read extra prefixes. If provided // only the first variadic string will be used to attempt to parse prefixes. There can be no // colliding PrefixIDs - within the file first defined takes precedence. -func New(privkey [32]byte, filepath ...string) (*Transport, error) { +func New(privkeys [][32]byte, filepath ...string) (*Transport, error) { var prefixes map[PrefixID]prefix = make(map[PrefixID]prefix) var err error if len(filepath) > 0 && filepath[0] != "" { @@ -377,7 +385,7 @@ func New(privkey [32]byte, filepath ...string) (*Transport, error) { } } return &Transport{ - Privkey: privkey, + Privkeys: privkeys, SupportedPrefixes: prefixes, TagObfuscator: transports.CTRObfuscator{}, }, nil @@ -388,8 +396,8 @@ func New(privkey [32]byte, filepath ...string) (*Transport, error) { // If provided only the first variadic string will be used to attempt to parse prefixes. There can // be no colliding PrefixIDs - file defined prefixes take precedent over defaults, and within the // file first defined takes precedence. -func Default(privkey [32]byte, filepath ...string) (*Transport, error) { - t, err := New(privkey, filepath...) +func Default(privkeys [][32]byte, filepath ...string) (*Transport, error) { + t, err := New(privkeys, filepath...) if err != nil { return nil, err } diff --git a/pkg/transports/wrapping/prefix/prefix_test.go b/pkg/transports/wrapping/prefix/prefix_test.go index 3dac3b34..11ad85ab 100644 --- a/pkg/transports/wrapping/prefix/prefix_test.go +++ b/pkg/transports/wrapping/prefix/prefix_test.go @@ -39,7 +39,7 @@ func TestSuccessfulWrap(t *testing.T) { var transport = Transport{ TagObfuscator: transports.CTRObfuscator{}, - Privkey: curve25519Private, + Privkeys: [][32]byte{curve25519Private}, SupportedPrefixes: defaultPrefixes, } message := []byte(`test message!`) @@ -93,7 +93,7 @@ func TestSuccessfulWrap(t *testing.T) { func TestUnsuccessfulWrap(t *testing.T) { var transport = Transport{ TagObfuscator: transports.CTRObfuscator{}, - Privkey: [32]byte{}, + Privkeys: [][32]byte{}, SupportedPrefixes: defaultPrefixes, } @@ -126,7 +126,7 @@ func TestUnsuccessfulWrap(t *testing.T) { func TestTryAgain(t *testing.T) { var transport = Transport{ TagObfuscator: transports.CTRObfuscator{}, - Privkey: [32]byte{}, + Privkeys: [][32]byte{}, SupportedPrefixes: defaultPrefixes, } @@ -222,7 +222,7 @@ var _cases = []struct { func TestPrefixGetDstPortServer(t *testing.T) { clv := randomizeDstPortMinVersion seed, _ := hex.DecodeString("0000000000000000000000000000000000") - transport, err := Default([32]byte{}) + transport, err := Default([][32]byte{}) require.Nil(t, err) for _, testCase := range _cases { @@ -427,7 +427,7 @@ func TestPrefixEndToEnd(t *testing.T) { var transport = Transport{ TagObfuscator: transports.CTRObfuscator{}, - Privkey: curve25519Private, + Privkeys: [][32]byte{curve25519Private}, SupportedPrefixes: defaultPrefixes, } message := []byte(`test message!`) diff --git a/proto/signalling.rs b/proto/signalling.rs index f70cc7c1..2830fe7e 100644 --- a/proto/signalling.rs +++ b/proto/signalling.rs @@ -5,6 +5,7 @@ // https://github.com/rust-lang/rust-clippy/issues/702 #![allow(unknown_lints)] #![allow(clippy::all)] +#![allow(renamed_and_removed_lints)] #![allow(unused_attributes)] #![cfg_attr(rustfmt, rustfmt::skip)] diff --git a/rust_foreign_interface.h b/rust_foreign_interface.h index a5128d4b..b7ddcb91 100644 --- a/rust_foreign_interface.h +++ b/rust_foreign_interface.h @@ -1,14 +1,17 @@ #ifndef _INCLGUARD_CLONERING_RUST_INTERFACE_H_ #define _INCLGUARD_CLONERING_RUST_INTERFACE_H_ -struct RustGlobalsStruct { +#define TD_KEYLEN_BYTES (32) + +struct RustGlobalsStruct +{ void *global; }; // We specifically name this something different to avoid accidentally linking // against the otherwise compatible rust_tapdance struct RustGlobalsStruct rust_detect_init( - int32_t cur_lcore_id, uint8_t *station_key, char *workers_socket_addr); + int32_t cur_lcore_id, uint8_t (*station_keys)[TD_KEYLEN_BYTES], uint8_t numkeys, char *workers_socket_addr); uint8_t rust_update_cli_conf(void *conf_ptr); uint8_t rust_process_packet( void *rust_global, void *c_raw_ethframe, size_t c_frame_len); diff --git a/scripts/start_detector.sh b/scripts/start_detector.sh index 1cb62c12..be8d30e7 100755 --- a/scripts/start_detector.sh +++ b/scripts/start_detector.sh @@ -13,7 +13,7 @@ source /opt/conjure/sysconfig/conjure.conf set +a -if [ ! -f $CJ_PRIVKEY ]; then +if [ ! -e $CJ_PRIVKEY ]; then echo "Failed to open \$CJ_PRIVKEY=$CJ_PRIVKEY." echo "You may want to set CJ_PRIVKEY in the conjure.conf file before running the script" exit 1 diff --git a/scripts/start_zbalance_ipc.sh b/scripts/start_zbalance_ipc.sh index 81f30649..64a388be 100755 --- a/scripts/start_zbalance_ipc.sh +++ b/scripts/start_zbalance_ipc.sh @@ -20,7 +20,7 @@ fi check_ZC_driver() { ifcname="$1" - if [[ $ifc = "zc:"* ]]; then + if [[ $ifcname = "zc:"* ]]; then ifcname="${ifcname#zc:}" fi if grep -q "ZC" "/proc/net/pf_ring/dev/${ifcname}/info"; then diff --git a/src/elligator.rs b/src/elligator.rs index 01a595b3..6e9e86fe 100644 --- a/src/elligator.rs +++ b/src/elligator.rs @@ -35,6 +35,21 @@ fn extract_stego_bytes(in_buf: &[u8], out_buf: &mut [u8]) { type PayloadElements = ([u8; 32], [u8; FSP::LENGTH], ClientToStation); +pub fn extract_payloads_multiple_keys( + secret_keys: &Vec<[u8; 32]>, + tls_record: &[u8], +) -> Result> { + for key in secret_keys { + if let Ok(payload_elements) = extract_payloads(key, tls_record) { + return Ok(payload_elements); + } + } + Err(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + "No secret keys worked", + ))) +} + // Returns either (Shared Secret, Fixed Size Payload, Variable Size Payload) or Box // Boxed error becuase size of return isn't known at compile time pub fn extract_payloads( @@ -180,50 +195,77 @@ pub fn extract_payloads( } } // end extract_payloads_new -/* Uses a function from an external library; run separately from other tests. +// Uses a function from an external library; run separately from other tests. #[cfg(test)] mod tests { -use elligator; -#[test] -fn elligator_extracts_telex_tag() -{ - let secret_key : [u8; 32] = [ - 224, 192, 103, 26, 96, 135, 130, 174, 250, 208, 30, 113, 46, 128, 127, 111, - 215, 199, 5, 141, 38, 124, 34, 127, 102, 142, 245, 81, 49, 70, 119, 119]; - - let tls_record : [u8; 325] = [23, 3, 3, 1, 64, 22, 160, 106, 230, 9, 73, - 117, 77, 155, 195, 52, 186, 101, 164, 19, 44, 80, 219, 142, 191, 38, 219, - 106, 55, 73, 194, 87, 48, 171, 18, 226, 115, 69, 64, 93, 64, 149, 98, 4, - 200, 150, 164, 213, 150, 8, 196, 75, 144, 134, 147, 8, 114, 48, 14, 213, -229, 117, 13, 49, 191, 104, 83, 80, 140, 68, 143, 184, 11, 152, 70, 140, 139, -215, 32, 14, 192, 4, 188, 36, 30, 173, 32, 4, 32, 187, 47, 129, 61, 70, 228, 77, -68, 145, 133, 72, 252, 96, 168, 103, 44, 148, 97, 207, 145, 166, 49, 228, 140, -134, 94, 231, 198, 251, 101, 119, 196, 149, 77, 186, 153, 34, 252, 110, 178, -151, 131, 167, 171, 238, 79, 57, 242, 23, 199, 190, 89, 106, 244, 215, 152, 120, -1, 208, 251, 204, 213, 148, 98, 170, 41, 103, 102, 15, 200, 222, 244, 60, 43, -159, 171, 71, 155, 218, 157, 218, 10, 141, 243, 2, 11, 199, 181, 166, 237, 106, -125, 221, 185, 25, 151, 203, 147, 150, 252, 31, 205, 232, 100, 127, 48, 143, -160, 186, 220, 133, 163, 193, 221, 115, 216, 91, 172, 131, 24, 58, 74, 109, 222, -123, 204, 144, 182, 185, 213, 107, 84, 135, 56, 137, 78, 134, 60, 190, 65, 13, -233, 188, 216, 1, 71, 172, 154, 171, 148, 182, 249, 155, 114, 42, 210, 86, 88, -95, 127, 179, 22, 25, 137, 231, 196, 185, 225, 233, 14, 87, 95, 159, 139, 205, -99, 1, 96, 225, 154, 157, 184, 10, 73, 158, 211, 235, 211, 104, 75, 68, 85, 253, -33, 19, 71, 127, 63, 223, 124, 186, 246, 62, 164, 223, 111, 207, 152, 161, 18, -71, 191, 103, 204, 75, 34, 108, 147, 10, 242, 64, 245, 135, 29, 49, 129, 244, -62, 36, 2, 230, 91, 129, 205, 98, 252]; - - let expected : [u8; 136] = [83, 80, 84, 69, 76, 69, 88, 48, 73, 119, 10, -208, 64, 218, 217, 76, 217, 166, 140, 244, 192, 78, 192, 30, 158, 239, 137, 71, -114, 81, 83, 224, 110, 188, 246, 146, 0, 187, 198, 116, 99, 106, 231, 176, 28, -178, 81, 235, 13, 53, 50, 46, 141, 30, 7, 161, 87, 113, 204, 12, 97, 66, 253, -45, 126, 235, 128, 248, 93, 203, 118, 136, 165, 253, 124, 55, 180, 23, 63, 52, -233, 52, 183, 196, 194, 40, 106, 21, 174, 245, 121, 58, 145, 158, 89, 49, 51, -118, 118, 188, 68, 91, 218, 164, 230, 198, 102, 213, 122, 255, 119, 78, 79, 17, -209, 84, 235, 44, 137, 36, 113, 230, 141, 192, 155, 130, 33, 180, 217, 98, 198, -200, 157, 165, 25, 21]; - - let out = elligator::extract_telex_tag(&secret_key, &tls_record); - assert_eq!(expected.to_vec(), out.to_vec()); -} + use std::convert::TryInto; + + use elligator; + + #[test] + fn test_extract_payload() { + const PRIVKEY: &str = "203963feed62ddda89b98857940f09866ae840f42e8c90160e411a0029b87e60"; + const REGTLSPAYLOAD: &str = "17030302320000000000000001f4914e06157d1d7999c8f0eab5c115f803bfd481857dc1751d3729cfd13f31c47370ba7703841c7fadf14e1fa722268519dba56ab53651a77a98804210f993341005e709ce352411731f7075331b8b10ea34fcefc4407210585ef439545cd79e03154ee0735c5b3f7cab57bc52e6ec99a8dbfca8f3a497f83c0d836447428652e8696dc5ca9b045e32dd6cb02687d9d1cfb8d852f3f72d2bee01b4a94474d497c4a56fc7435a67c56bca153664c13ea38a54a0cc612e023ddf8791415a4857fc91efd768ced5bd0d06cf91f0677df2f61daf14d5892e1a6a36b594d86ecad764d4868417dd7a12d17f9aae2491628a6ad3713dd544a6e5dd61bf673d79a102cd8ca2001197bbbc2f525c7ce5059d411687dde048f154d46fd60c7e144b5aa6ab00926c6a3a34b1300af7bc45da0abee0f836d93ae3ed3029f7aa3b062d5c278234869b60644e36221e60ce0674c88c1f642daba3fe3518ad1436d217e04934f9abef888cc710fde60cab81aaa35e6378539f18280981009e7866fa89b268a1d63f3fd4785eeecda89cee2db40d5d002c1aa21fac0577951d67358588562868ea5e34bf8fc04bd432e06ffc9dc0add6895113e3401f0e39a035e70ff191615f5c3269f765752f7c9b8da7396d02fc5d555eceb2901340ea435d3e420020145aa53b2b988b5b85affed117ff990386c8ff7ec8f1d6e0583c066fa7e6e7311a37fe67438c4d479f7d74632d65f2ea5676d36ad0ea311a434efd982dd1355b5f27b7ea1002ff8b1889895455"; + + const PRIVKEY2: &str = "f00a2d4fe4bb21e4deacef21c2d0ad135cb96747b14fc5ae208bc09bf4799479"; + const REGTLSPAYLOAD2: &str = "17030302450000000000000001b858a61acbd0360ac424062e0a0724d6774910ad27bec3c429e227ee44ff45d4aef082d991b57af6ad45c52206961a8ce4345f79916ce02dda177a823d3f9007420898b4a5d124509945a741ff06af6e84db99f6380c7099825c0cf9a89484577369b5e3e954aa9a4606894f34d1924995a0fa646e2e1bb93271da10531f415f5f2e30e34f86e6674bdb0cdf85c6dfa91ba76167d325750746e0fc12aaaa6b4e768e49446f53d14da3ba3eaa26bf9359ae0cc9736a4ba5ca797c6b5b0532bfb6f06bd3dc0e428e978e9178769dc9567c2900793a2112aacc8ed48d19380118482629f7b6962fdb07fba6f62bedab4d937db7cc866a257c0834ceeac21e6f248f3bb7a752205081fb4eb98af3fd4b02c6d8dcf511786e05b8615e18350d4399de86a38f102e91ed332dfe235abd644b43193fa51be9f25a80845b86f76ccf8a4f4609fca90ba62a83bb56a06e9613c4168462dc792b4f84be1c0bbf83483df9c67c283827fd0ec78df5a71ef14a28691b1376aa139aee27d496913a0f4c293828b2a854be7c6bebaba6bd39c59619be443edded7fe7a8bb3efaac11a7d57467bde9f4636a282c4e1ad003697ce2eab68b4e2edff05993ef4a43be26639e5ec08211b27c1f50ac44772a525ede4323dae23e4adb5c6b7ee39b7307512416cab1ee4dc14e23988be74de7ef2ec25e74dde68792dfcb824623a4289667abb3c67e600cad8457f780cf19552ee9fa432abd727da5dd7d16ef39c936c8a69ff81a679454c7da387c84f28ca6236d5ea889f9d3c9e4ae124d28ef6e7d62916ffc"; + + let privkey = hex::decode(PRIVKEY).expect("err decoding privkey"); + let tls_record = hex::decode(REGTLSPAYLOAD).expect("err decoding tls record"); + let privkey2 = hex::decode(PRIVKEY2).expect("err decoding privkey"); + let tls_record2 = hex::decode(REGTLSPAYLOAD2).expect("err decoding tls record"); + + assert!(elligator::extract_payloads(&privkey, &tls_record).is_ok()); + assert!(elligator::extract_payloads(&privkey2, &tls_record2).is_ok()); + assert!(elligator::extract_payloads(&privkey, &tls_record2).is_err()); + assert!(elligator::extract_payloads(&privkey2, &tls_record).is_err()); + let keys = &vec![ + privkey.clone().try_into().expect("not 32 bytes"), + privkey2.clone().try_into().expect("not 32 bytes"), + ]; + assert!(elligator::extract_payloads_multiple_keys(keys, &tls_record).is_ok()); + assert!(elligator::extract_payloads_multiple_keys(keys, &tls_record2).is_ok()); + } + + // #[test] + // fn elligator_extracts_telex_tag() + // { + // let secret_key : [u8; 32] = [ + // 224, 192, 103, 26, 96, 135, 130, 174, 250, 208, 30, 113, 46, 128, 127, 111, + // 215, 199, 5, 141, 38, 124, 34, 127, 102, 142, 245, 81, 49, 70, 119, 119]; + + // let tls_record : [u8; 325] = [23, 3, 3, 1, 64, 22, 160, 106, 230, 9, 73, + // 117, 77, 155, 195, 52, 186, 101, 164, 19, 44, 80, 219, 142, 191, 38, 219, + // 106, 55, 73, 194, 87, 48, 171, 18, 226, 115, 69, 64, 93, 64, 149, 98, 4, + // 200, 150, 164, 213, 150, 8, 196, 75, 144, 134, 147, 8, 114, 48, 14, 213, + // 229, 117, 13, 49, 191, 104, 83, 80, 140, 68, 143, 184, 11, 152, 70, 140, 139, + // 215, 32, 14, 192, 4, 188, 36, 30, 173, 32, 4, 32, 187, 47, 129, 61, 70, 228, 77, + // 68, 145, 133, 72, 252, 96, 168, 103, 44, 148, 97, 207, 145, 166, 49, 228, 140, + // 134, 94, 231, 198, 251, 101, 119, 196, 149, 77, 186, 153, 34, 252, 110, 178, + // 151, 131, 167, 171, 238, 79, 57, 242, 23, 199, 190, 89, 106, 244, 215, 152, 120, + // 1, 208, 251, 204, 213, 148, 98, 170, 41, 103, 102, 15, 200, 222, 244, 60, 43, + // 159, 171, 71, 155, 218, 157, 218, 10, 141, 243, 2, 11, 199, 181, 166, 237, 106, + // 125, 221, 185, 25, 151, 203, 147, 150, 252, 31, 205, 232, 100, 127, 48, 143, + // 160, 186, 220, 133, 163, 193, 221, 115, 216, 91, 172, 131, 24, 58, 74, 109, 222, + // 123, 204, 144, 182, 185, 213, 107, 84, 135, 56, 137, 78, 134, 60, 190, 65, 13, + // 233, 188, 216, 1, 71, 172, 154, 171, 148, 182, 249, 155, 114, 42, 210, 86, 88, + // 95, 127, 179, 22, 25, 137, 231, 196, 185, 225, 233, 14, 87, 95, 159, 139, 205, + // 99, 1, 96, 225, 154, 157, 184, 10, 73, 158, 211, 235, 211, 104, 75, 68, 85, 253, + // 33, 19, 71, 127, 63, 223, 124, 186, 246, 62, 164, 223, 111, 207, 152, 161, 18, + // 71, 191, 103, 204, 75, 34, 108, 147, 10, 242, 64, 245, 135, 29, 49, 129, 244, + // 62, 36, 2, 230, 91, 129, 205, 98, 252]; + + // let expected : [u8; 136] = [83, 80, 84, 69, 76, 69, 88, 48, 73, 119, 10, + // 208, 64, 218, 217, 76, 217, 166, 140, 244, 192, 78, 192, 30, 158, 239, 137, 71, + // 114, 81, 83, 224, 110, 188, 246, 146, 0, 187, 198, 116, 99, 106, 231, 176, 28, + // 178, 81, 235, 13, 53, 50, 46, 141, 30, 7, 161, 87, 113, 204, 12, 97, 66, 253, + // 45, 126, 235, 128, 248, 93, 203, 118, 136, 165, 253, 124, 55, 180, 23, 63, 52, + // 233, 52, 183, 196, 194, 40, 106, 21, 174, 245, 121, 58, 145, 158, 89, 49, 51, + // 118, 118, 188, 68, 91, 218, 164, 230, 198, 102, 213, 122, 255, 119, 78, 79, 17, + // 209, 84, 235, 44, 137, 36, 113, 230, 141, 192, 155, 130, 33, 180, 217, 98, 198, + // 200, 157, 165, 25, 21]; + + // let out = elligator::extract_telex_tag(&secret_key, &tls_record); + // assert_eq!(expected.to_vec(), out.to_vec()); + // } } // mod tests -*/ diff --git a/src/lib.rs b/src/lib.rs index 1ed09f26..6706bdba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,7 +44,7 @@ use flow_tracker::{Flow, FlowTracker}; // Global program state for one instance of a TapDance station process. pub struct PerCoreGlobal { - priv_key: [u8; 32], + priv_keys: Vec<[u8; 32]>, lcore: i32, pub flow_tracker: FlowTracker, @@ -106,7 +106,7 @@ struct StationConfig { const STATION_CONF_PATH: &str = "CJ_STATION_CONFIG"; impl PerCoreGlobal { - fn new(priv_key: [u8; 32], the_lcore: i32, workers_socket_addr: &str) -> PerCoreGlobal { + fn new(priv_keys: Vec<[u8; 32]>, the_lcore: i32, workers_socket_addr: &str) -> PerCoreGlobal { let tun = TunTap::new(IFF_TUN, &format!("tun{the_lcore}")).unwrap(); tun.set_up().unwrap(); @@ -146,7 +146,7 @@ impl PerCoreGlobal { debug!("gre_offset: {}", gre_offset); PerCoreGlobal { - priv_key, + priv_keys, lcore: the_lcore, // sessions: HashMap::new(), flow_tracker: FlowTracker::new(), @@ -264,12 +264,21 @@ pub struct RustGlobalsStruct { #[no_mangle] pub unsafe extern "C" fn rust_detect_init( lcore_id: i32, - ckey: *const u8, + station_keys: *const u8, + numkeys: u8, workers_socket_addr: *const c_char, ) -> RustGlobalsStruct { logging::init(log::Level::Debug, lcore_id); - let key = *array_ref![std::slice::from_raw_parts(ckey, 32_usize), 0, 32]; + // Create a slice of station keys from the pointer. + let keys_slice = std::slice::from_raw_parts(station_keys, (numkeys as usize) * 32); + + // Collect all keys into a Vec of 32-byte arrays + let mut keys: Vec<[u8; 32]> = Vec::with_capacity(numkeys as usize); + for i in 0..(numkeys as usize) { + let key = *array_ref![keys_slice, i * 32, 32]; + keys.push(key); + } let s = format!("/tmp/dark-decoy-reporter-{lcore_id}.fifo"); c_api::c_open_reporter(s); @@ -277,7 +286,7 @@ pub unsafe extern "C" fn rust_detect_init( let addr: &CStr = CStr::from_ptr(workers_socket_addr); - let global = PerCoreGlobal::new(key, lcore_id, addr.to_str().unwrap()); + let global = PerCoreGlobal::new(keys, lcore_id, addr.to_str().unwrap()); debug!("Initialized rust core {}", global.lcore); diff --git a/src/process_packet.rs b/src/process_packet.rs index 80eb9b67..3bb130e1 100644 --- a/src/process_packet.rs +++ b/src/process_packet.rs @@ -238,14 +238,11 @@ impl PerCoreGlobal { } if is_tls_app_pkt(&tcp_pkt) { - match self.check_dark_decoy_tag(&flow, &tcp_pkt) { - true => { - // debug!("New Conjure registration detected in {},", flow); - // self.flow_tracker.mark_dark_decoy(&cj_flow); - // not removing flow from stale_tracked_flows for optimization reasons: - // it will be removed later - } - false => {} + if self.check_dark_decoy_tag(&flow, &tcp_pkt) { + // debug!("New Conjure registration detected in {},", flow); + // self.flow_tracker.mark_dark_decoy(&cj_flow); + // not removing flow from stale_tracked_flows for optimization reasons: + // it will be removed later }; self.flow_tracker.stop_tracking_flow(&flow); } else { @@ -280,7 +277,7 @@ impl PerCoreGlobal { fn check_dark_decoy_tag(&mut self, flow: &Flow, tcp_pkt: &TcpPacket) -> bool { self.stats.elligator_this_period += 1; - match elligator::extract_payloads(&self.priv_key, tcp_pkt.payload()) { + match elligator::extract_payloads_multiple_keys(&self.priv_keys, tcp_pkt.payload()) { Ok(res) => { // res.0 => shared secret // res.1 => Fixed size payload diff --git a/src/signalling.rs b/src/signalling.rs index f70cc7c1..2830fe7e 100644 --- a/src/signalling.rs +++ b/src/signalling.rs @@ -5,6 +5,7 @@ // https://github.com/rust-lang/rust-clippy/issues/702 #![allow(unknown_lints)] #![allow(clippy::all)] +#![allow(renamed_and_removed_lints)] #![allow(unused_attributes)] #![cfg_attr(rustfmt, rustfmt::skip)]