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)]