diff --git a/Makefile.in b/Makefile.in index 943a6c9..393eb87 100644 --- a/Makefile.in +++ b/Makefile.in @@ -44,7 +44,7 @@ MAN7DIR=$(MANDIR)/man7 MAN8DIR=$(MANDIR)/man8 N2N_LIB=libn2n.a -N2N_OBJS=n2n.o n2n_keyfile.o wire.o minilzo.o twofish.o \ +N2N_OBJS=n2n.o wire.o minilzo.o twofish.o \ edge_utils.o \ transform_null.o transform_tf.o transform_aes.o \ tuntap_freebsd.o tuntap_netbsd.o tuntap_linux.o \ @@ -81,7 +81,7 @@ benchmark: benchmark.c $(N2N_LIB) n2n_wire.h n2n.h Makefile example_edge_embed: example_edge_embed.c $(N2N_LIB) n2n.h $(CC) $(CFLAGS) example_edge_embed.c $(N2N_LIB) $(LIBS_EDGE) -o example_edge_embed -.c.o: n2n.h n2n_keyfile.h n2n_transforms.h n2n_wire.h twofish.h Makefile +.c.o: n2n.h n2n_transforms.h n2n_wire.h twofish.h Makefile $(CC) $(CFLAGS) -c $< %.gz : % diff --git a/android/edge_android.c b/android/edge_android.c index d6f5258..68376a8 100644 --- a/android/edge_android.c +++ b/android/edge_android.c @@ -137,6 +137,7 @@ static int scan_address(char * ip_addr, size_t addr_size, /* *************************************************** */ +//TODO use new API int start_edge(const n2n_edge_cmd_t* cmd) { int keep_on_running = 0; @@ -177,13 +178,7 @@ int start_edge(const n2n_edge_cmd_t* cmd) return 1; } eee.device.fd = cmd->vpn_fd; - if (cmd->enc_key_file) - { - strncpy(eee.keyschedule, cmd->enc_key_file, N2N_PATHNAME_MAXLEN-1); - eee.keyschedule[N2N_PATHNAME_MAXLEN-1]=0; /* strncpy does not add NULL if the source has no NULL. */ - traceEvent(TRACE_DEBUG, "keyfile = '%s'\n", eee.keyschedule); - } - else if (cmd->enc_key) + if (cmd->enc_key) { encrypt_key = strdup(cmd->enc_key); traceEvent(TRACE_DEBUG, "encrypt_key = '%s'\n", encrypt_key); @@ -242,7 +237,7 @@ int start_edge(const n2n_edge_cmd_t* cmd) traceEvent(TRACE_NORMAL, "supernode %u => %s\n", i, (eee.sn_ip_array[i])); } supernode2addr(&(eee.supernode), eee.sn_ip_array[eee.sn_idx]); - if (encrypt_key == NULL && strlen(eee.keyschedule) == 0) + if (encrypt_key == NULL) { traceEvent(TRACE_WARNING, "Encryption is disabled in edge."); eee.null_transop = 1; @@ -277,15 +272,6 @@ int start_edge(const n2n_edge_cmd_t* cmd) free(encrypt_key); encrypt_key = NULL; } - else if (strlen(eee.keyschedule) > 0) - { - if (edge_init_keyschedule(&eee) != 0) - { - traceEvent(TRACE_ERROR, "keyschedule setup failed.\n"); - free(encrypt_key); - return 1; - } - } /* else run in NULL mode */ eee.udp_sock = open_socket(local_port, 1 /*bind ANY*/ ); if(eee.udp_sock < 0) diff --git a/benchmark.c b/benchmark.c index c97db87..381f511 100644 --- a/benchmark.c +++ b/benchmark.c @@ -70,7 +70,7 @@ uint8_t PKT_CONTENT[]={ /* Prototypes */ static ssize_t do_encode_packet( uint8_t * pktbuf, size_t bufsize, const n2n_community_t c ); -static void run_transop_benchmark(const char *op_name, n2n_trans_op_t *op_fn, uint8_t *pktbuf, n2n_community_t c); +static void run_transop_benchmark(const char *op_name, n2n_trans_op_t *op_fn, n2n_edge_conf_t *conf, uint8_t *pktbuf); static int perform_decryption = 0; static void usage() { @@ -93,34 +93,44 @@ static void parseArgs(int argc, char * argv[]) { int main(int argc, char * argv[]) { uint8_t pktbuf[N2N_PKT_BUF_SIZE]; - n2n_community_t c; - n2n_trans_op_t transop_null, transop_twofish, transop_aes_cbc; - u_char encrypt_pwd[] = "SoMEVer!S$cUREPassWORD"; + n2n_trans_op_t transop_null, transop_twofish; +#ifdef N2N_HAVE_AES + n2n_trans_op_t transop_aes_cbc; +#endif + n2n_edge_conf_t conf; parseArgs(argc, argv); - memset(c,0,sizeof(N2N_COMMUNITY_SIZE)); - memcpy(c, "abc123def456", 12); + /* Init configuration */ + edge_init_conf_defaults(&conf); + strncpy((char*)conf.community_name, "abc123def456", sizeof(conf.community_name)); + conf.encrypt_key = "SoMEVer!S$cUREPassWORD"; /* Init transopts */ - memset(&transop_null, 0, sizeof(transop_null)); - transop_null_init(&transop_null); - memset(&transop_twofish, 0, sizeof(transop_twofish)); - transop_twofish_init(&transop_twofish); - transop_twofish_setup_psk(&transop_twofish, 0, encrypt_pwd, sizeof(encrypt_pwd)-1); - memset(&transop_aes_cbc, 0, sizeof(transop_aes_cbc)); - transop_aes_init(&transop_aes_cbc); - transop_aes_setup_psk(&transop_aes_cbc, 0, encrypt_pwd, sizeof(encrypt_pwd)-1); + n2n_transop_null_init(&conf, &transop_null); + n2n_transop_twofish_init(&conf, &transop_twofish); +#ifdef N2N_HAVE_AES + n2n_transop_aes_cbc_init(&conf, &transop_aes_cbc); +#endif /* Run the tests */ - run_transop_benchmark("transop_null", &transop_null, pktbuf, c); - run_transop_benchmark("transop_twofish", &transop_twofish, pktbuf, c); - run_transop_benchmark("transop_aes", &transop_aes_cbc, pktbuf, c); + run_transop_benchmark("transop_null", &transop_null, &conf, pktbuf); + run_transop_benchmark("transop_twofish", &transop_twofish, &conf, pktbuf); +#ifdef N2N_HAVE_AES + run_transop_benchmark("transop_aes", &transop_aes_cbc, &conf, pktbuf); +#endif + + /* Cleanup */ + transop_null.deinit(&transop_null); + transop_twofish.deinit(&transop_twofish); +#ifdef N2N_HAVE_AES + transop_aes_cbc.deinit(&transop_aes_cbc); +#endif return 0; } -static void run_transop_benchmark(const char *op_name, n2n_trans_op_t *op_fn, uint8_t *pktbuf, n2n_community_t c) { +static void run_transop_benchmark(const char *op_name, n2n_trans_op_t *op_fn, n2n_edge_conf_t *conf, uint8_t *pktbuf) { n2n_common_t cmn; n2n_PACKET_t pkt; n2n_mac_t mac_buf; @@ -142,7 +152,7 @@ static void run_transop_benchmark(const char *op_name, n2n_trans_op_t *op_fn, ui gettimeofday( &t1, NULL ); while(tdiff < target_usec) { - nw = do_encode_packet( pktbuf, N2N_PKT_BUF_SIZE, c); + nw = do_encode_packet( pktbuf, N2N_PKT_BUF_SIZE, conf->community_name); nw += op_fn->fwd(op_fn, pktbuf+nw, N2N_PKT_BUF_SIZE-nw, diff --git a/edge.c b/edge.c index 327b5da..2cda0ba 100644 --- a/edge.c +++ b/edge.c @@ -35,22 +35,20 @@ /* ***************************************************** */ -typedef struct { - int local_port; - int mgmt_port; - char tuntap_dev_name[N2N_IFNAMSIZ]; - char ip_mode[N2N_IF_MODE_SIZE]; - char ip_addr[N2N_NETMASK_STR_SIZE]; - char netmask[N2N_NETMASK_STR_SIZE]; - int mtu; - int got_s; - char device_mac[N2N_MACNAMSIZ]; - char * encrypt_key; +typedef struct n2n_priv_config { + char tuntap_dev_name[N2N_IFNAMSIZ]; + char ip_mode[N2N_IF_MODE_SIZE]; + char ip_addr[N2N_NETMASK_STR_SIZE]; + char netmask[N2N_NETMASK_STR_SIZE]; + char device_mac[N2N_MACNAMSIZ]; + int mtu; + uint8_t got_s; + uint8_t daemon; #ifndef WIN32 - uid_t userid; - gid_t groupid; + uid_t userid; + gid_t groupid; #endif -} edge_conf_t; +} n2n_priv_config_t; /* ***************************************************** */ @@ -144,8 +142,7 @@ static void help() { printf("-a | Set interface address. For DHCP use '-r -a dhcp:0.0.0.0'\n"); printf("-c | n2n community name the edge belongs to.\n"); - printf("-k | Encryption key (ASCII) - also N2N_KEY=. Not with -K.\n"); - printf("-K | Specify a key schedule file to load. Not with -k.\n"); + printf("-k | Encryption key (ASCII) - also N2N_KEY=.\n"); printf("-s | Edge interface netmask in dotted decimal notation (255.255.255.0).\n"); printf("-l | Supernode IP:port\n"); printf("-b | Periodically resolve supernode IP\n"); @@ -163,39 +160,24 @@ static void help() { printf("-M | Specify n2n MTU of edge interface (default %d).\n", DEFAULT_MTU); printf("-r | Enable packet forwarding through n2n community.\n"); #ifdef N2N_HAVE_AES - printf("-A | Set AES CBC as the preferred encryption mode.\n"); + printf("-A | Use AES CBC for encryption (default=use twofish).\n"); #endif printf("-E | Accept multicast MAC addresses (default=drop).\n"); printf("-v | Make more verbose. Repeat as required.\n"); printf("-t | Management UDP Port (for multiple edges on a machine).\n"); printf("\nEnvironment variables:\n"); - printf(" N2N_KEY | Encryption key (ASCII). Not with -K or -k.\n"); + printf(" N2N_KEY | Encryption key (ASCII). Not with -k.\n"); exit(0); } /* *************************************************** */ -static int setOption(int optkey, char *optargument, edge_conf_t *ec, n2n_edge_t *eee) { +static int setOption(int optkey, char *optargument, n2n_priv_config_t *ec, n2n_edge_conf_t *conf) { /* traceEvent(TRACE_NORMAL, "Option %c = %s", optkey, optargument ? optargument : ""); */ switch(optkey) { - case'K': - { - if(ec->encrypt_key) { - traceEvent(TRACE_ERROR, "Error: -K and -k options are mutually exclusive"); - exit(1); - } else { - strncpy(eee->keyschedule, optargument, N2N_PATHNAME_MAXLEN-1); - /* strncpy does not add NULL if the source has no NULL. */ - eee->keyschedule[N2N_PATHNAME_MAXLEN-1] = 0; - - traceEvent(TRACE_NORMAL, "keyfile = '%s'\n", eee->keyschedule); - } - break; - } - case 'a': /* IP address and mode of TUNTAP interface */ { scan_address(ec->ip_addr, N2N_NETMASK_STR_SIZE, @@ -206,14 +188,14 @@ static int setOption(int optkey, char *optargument, edge_conf_t *ec, n2n_edge_t case 'c': /* community as a string */ { - memset(eee->community_name, 0, N2N_COMMUNITY_SIZE); - strncpy((char *)eee->community_name, optargument, N2N_COMMUNITY_SIZE); + memset(conf->community_name, 0, N2N_COMMUNITY_SIZE); + strncpy((char *)conf->community_name, optargument, N2N_COMMUNITY_SIZE); break; } case 'E': /* multicast ethernet addresses accepted. */ { - eee->drop_multicast=0; + conf->drop_multicast=0; traceEvent(TRACE_DEBUG, "Enabling ethernet multicast traffic"); break; } @@ -235,7 +217,7 @@ static int setOption(int optkey, char *optargument, edge_conf_t *ec, n2n_edge_t #ifndef WIN32 case 'f' : /* do not fork as daemon */ { - eee->daemon=0; + ec->daemon=0; break; } #endif /* #ifndef WIN32 */ @@ -254,37 +236,29 @@ static int setOption(int optkey, char *optargument, edge_conf_t *ec, n2n_edge_t case 'k': /* encrypt key */ { - if(strlen(eee->keyschedule) > 0) { - traceEvent(TRACE_ERROR, "-K and -k options are mutually exclusive"); - exit(1); - } else { - traceEvent(TRACE_DEBUG, "encrypt_key = '%s'\n", ec->encrypt_key); - ec->encrypt_key = strdup(optargument); - } + if(conf->encrypt_key) free(conf->encrypt_key); + conf->encrypt_key = strdup(optargument); + traceEvent(TRACE_DEBUG, "encrypt_key = '%s'\n", conf->encrypt_key); break; } case 'r': /* enable packet routing across n2n endpoints */ { - eee->allow_routing = 1; + conf->allow_routing = 1; break; } #ifdef N2N_HAVE_AES case 'A': { - eee->preferred_aes = 1; + conf->transop_id = N2N_TRANSFORM_ID_AESCBC; break; } #endif case 'l': /* supernode-list */ if(optargument) { - if(eee->sn_num < N2N_EDGE_NUM_SUPERNODES) { - strncpy((eee->sn_ip_array[eee->sn_num]), optargument, N2N_EDGE_SN_HOST_SIZE); - traceEvent(TRACE_NORMAL, "Adding supernode[%u] = %s", (unsigned int)eee->sn_num, (eee->sn_ip_array[eee->sn_num])); - ++eee->sn_num; - } else { + if(edge_conf_add_supernode(conf, optargument) != 0) { traceEvent(TRACE_WARNING, "Too many supernodes!"); exit(1); } @@ -301,19 +275,19 @@ static int setOption(int optkey, char *optargument, edge_conf_t *ec, n2n_edge_t case 'b': { - eee->re_resolve_supernode_ip = 1; + conf->re_resolve_supernode_ip = 1; break; } case 'p': { - ec->local_port = atoi(optargument); + conf->local_port = atoi(optargument); break; } case 't': { - ec->mgmt_port = atoi(optargument); + conf->mgmt_port = atoi(optargument); break; } @@ -363,7 +337,7 @@ static const struct option long_options[] = { /* *************************************************** */ /* read command line options */ -static int loadFromCLI(int argc, char *argv[], edge_conf_t *ec, n2n_edge_t *eee) { +static int loadFromCLI(int argc, char *argv[], n2n_edge_conf_t *conf, n2n_priv_config_t *ec) { u_char c; while((c = getopt_long(argc, argv, @@ -374,7 +348,7 @@ static int loadFromCLI(int argc, char *argv[], edge_conf_t *ec, n2n_edge_t *eee) , long_options, NULL)) != '?') { if(c == 255) break; - setOption(c, optarg, ec, eee); + setOption(c, optarg, ec, conf); } return 0; @@ -400,7 +374,7 @@ static char *trim(char *s) { /* *************************************************** */ /* parse the configuration file */ -static int loadFromFile(const char *path, edge_conf_t *ec, n2n_edge_t *eee) { +static int loadFromFile(const char *path, n2n_edge_conf_t *conf, n2n_priv_config_t *ec) { char buffer[4096], *line, *key, *value; u_int line_len, opt_name_len; FILE *fd; @@ -414,7 +388,6 @@ static int loadFromFile(const char *path, edge_conf_t *ec, n2n_edge_t *eee) { } while((line = fgets(buffer, sizeof(buffer), fd)) != NULL) { - line = trim(line); value = NULL; @@ -437,7 +410,7 @@ static int loadFromFile(const char *path, edge_conf_t *ec, n2n_edge_t *eee) { if(line_len > opt_name_len + 1) value = trim(&key[opt_name_len + 1]); // traceEvent(TRACE_NORMAL, "long key: %s value: %s", key, value); - setOption(opt->val, value, ec, eee); + setOption(opt->val, value, ec, conf); break; } @@ -449,7 +422,7 @@ static int loadFromFile(const char *path, edge_conf_t *ec, n2n_edge_t *eee) { if(line_len > 2) value = trim(&key[2]); // traceEvent(TRACE_NORMAL, "key: %c value: %s", key[0], value); - setOption(key[0], value, ec, eee); + setOption(key[0], value, ec, conf); } else { traceEvent(TRACE_WARNING, "Skipping unrecognized line: %s", line); continue; @@ -565,105 +538,87 @@ static void daemonize() { /** Entry point to program from kernel. */ int main(int argc, char* argv[]) { - int keep_on_running = 1; - int rc; - int i; - n2n_edge_t eee; /* single instance for this program */ - edge_conf_t ec; + int keep_on_running = 1; + int rc; + tuntap_dev tuntap; /* a tuntap device */ + n2n_edge_t *eee; /* single instance for this program */ + n2n_edge_conf_t conf; /* generic N2N edge config */ + n2n_priv_config_t ec; /* config used for standalone program execution */ if(argc == 1) help(); - - ec.local_port = 0 /* any port */; - ec.mgmt_port = N2N_EDGE_MGMT_PORT; /* 5644 by default */ - snprintf(ec.tuntap_dev_name, sizeof(ec.tuntap_dev_name), "edge0"); - snprintf(ec.ip_mode, sizeof(ec.ip_mode), "static"); - snprintf(ec.netmask, sizeof(ec.netmask), "255.255.255.0"); - ec.ip_addr[0] = '\0'; - ec.device_mac[0] = '\0'; + + /* Defaults */ + edge_init_conf_defaults(&conf); + memset(&ec, 0, sizeof(ec)); ec.mtu = DEFAULT_MTU; - ec.got_s = 0; - ec.encrypt_key = NULL; + ec.daemon = 1; /* By default run in daemon mode. */ #ifndef WIN32 ec.userid = 0; /* root is the only guaranteed ID */ ec.groupid = 0; /* root is the only guaranteed ID */ #endif - if(-1 == edge_init(&eee)) { - traceEvent(TRACE_ERROR, "Failed in edge_init"); - exit(1); - } - - if(getenv("N2N_KEY")) { - ec.encrypt_key = strdup(getenv("N2N_KEY")); - } - #ifdef WIN32 ec.tuntap_dev_name[0] = '\0'; +#else + snprintf(ec.tuntap_dev_name, sizeof(ec.tuntap_dev_name), "edge0"); #endif - memset(&(eee.supernode), 0, sizeof(eee.supernode)); - eee.supernode.family = AF_INET; + snprintf(ec.ip_mode, sizeof(ec.ip_mode), "static"); + snprintf(ec.netmask, sizeof(ec.netmask), "255.255.255.0"); + + traceEvent(TRACE_NORMAL, "Starting n2n edge %s %s", PACKAGE_VERSION, PACKAGE_BUILDDATE); #ifndef WIN32 if((argc >= 2) && (argv[1][0] != '-')) { - rc = loadFromFile(argv[1], &ec, &eee); + rc = loadFromFile(argv[1], &conf, &ec); if(argc > 2) - rc = loadFromCLI(argc, argv, &ec, &eee); + rc = loadFromCLI(argc, argv, &conf, &ec); } else #endif - rc = loadFromCLI(argc, argv, &ec, &eee); + rc = loadFromCLI(argc, argv, &conf, &ec); - if((rc < 0) || (eee.sn_num == 0)) + if(rc < 0) help(); - - traceEvent(TRACE_NORMAL, "Starting n2n edge %s %s", PACKAGE_VERSION, PACKAGE_BUILDDATE); - for (i=0; i %s\n", i, (eee.sn_ip_array[i])); + if(0 == strcmp("dhcp", ec.ip_mode)) { + traceEvent(TRACE_NORMAL, "Dynamic IP address assignment enabled."); - supernode2addr(&(eee.supernode), eee.sn_ip_array[eee.sn_idx]); + conf.dyn_ip_mode = 1; + } else + traceEvent(TRACE_NORMAL, "ip_mode='%s'", ec.ip_mode); + + if(edge_verify_conf(&conf) != 0) + help(); if(!( #ifdef __linux__ (ec.tuntap_dev_name[0] != 0) && #endif - (eee.community_name[0] != 0) && (ec.ip_addr[0] != 0) )) - { - help(); - } + help(); -#ifndef WIN32 - if(eee.daemon) { - setUseSyslog(1); /* traceEvent output now goes to syslog. */ - daemonize(); - } -#endif /* #ifndef WIN32 */ - - if((NULL == ec.encrypt_key) && (0 == strlen(eee.keyschedule))) - { - traceEvent(TRACE_WARNING, "Encryption is disabled in edge."); - - eee.null_transop = 1; - } - #ifndef WIN32 /* If running suid root then we need to setuid before using the force. */ setuid(0); /* setgid(0); */ #endif - if(0 == strcmp("dhcp", ec.ip_mode)) { - traceEvent(TRACE_NORMAL, "Dynamic IP address assignment enabled."); - - eee.dyn_ip_mode = 1; - } else - traceEvent(TRACE_NORMAL, "ip_mode='%s'", ec.ip_mode); - - if(tuntap_open(&(eee.device), ec.tuntap_dev_name, ec.ip_mode, ec.ip_addr, ec.netmask, ec.device_mac, ec.mtu) < 0) + if(tuntap_open(&tuntap, ec.tuntap_dev_name, ec.ip_mode, ec.ip_addr, ec.netmask, ec.device_mac, ec.mtu) < 0) return(-1); + if((eee = edge_init(&tuntap, &conf, &rc)) == NULL) { + traceEvent(TRACE_ERROR, "Failed in edge_init"); + exit(1); + } + +#ifndef WIN32 + if(ec.daemon) { + setUseSyslog(1); /* traceEvent output now goes to syslog. */ + daemonize(); + } +#endif /* #ifndef WIN32 */ + #ifndef WIN32 if((ec.userid != 0) || (ec.groupid != 0)) { traceEvent(TRACE_NORMAL, "Interface up. Dropping privileges to uid=%d, gid=%d", @@ -675,30 +630,16 @@ int main(int argc, char* argv[]) { } #endif - if(ec.local_port > 0) - traceEvent(TRACE_NORMAL, "Binding to local port %d", (signed int)ec.local_port); - - if(ec.encrypt_key) { - if(edge_init_encryption(&eee, (uint8_t *)ec.encrypt_key, strlen(ec.encrypt_key)) != 0) { - traceEvent(TRACE_ERROR, "Error: encryption setup failed"); - return(-1); - } - } else if(strlen(eee.keyschedule) > 0) { - if(edge_init_keyschedule(&eee) < 0) { - traceEvent(TRACE_ERROR, "Error: keyschedule setup failed"); - return(-1); - } - } - /* else run in NULL mode */ - - if(edge_init_sockets(&eee, ec.local_port, ec.mgmt_port) < 0) { - traceEvent(TRACE_ERROR, "Error: socket setup failed"); - return(-1); - } - traceEvent(TRACE_NORMAL, "edge started"); + rc = run_edge_loop(eee, &keep_on_running); - return run_edge_loop(&eee, &keep_on_running); + /* Cleanup */ + edge_term(eee); + tuntap_close(&tuntap); + + if(conf.encrypt_key) free(conf.encrypt_key); + + return(rc); } /* ************************************** */ diff --git a/edge_utils.c b/edge_utils.c index d57c0b3..229b0ae 100644 --- a/edge_utils.c +++ b/edge_utils.c @@ -46,15 +46,6 @@ #define ARP_PERIOD_INTERVAL (10) /* sec */ #endif -/** Positions in the transop array where various transforms are stored. - * - * Used by transop_enum_to_index(). See also the transform enumerations in - * n2n_transforms.h */ -#define N2N_TRANSOP_NULL_IDX 0 -#define N2N_TRANSOP_TF_IDX 1 -#define N2N_TRANSOP_AESCBC_IDX 2 -/* etc. */ - #define ETH_FRAMESIZE 14 #define IP4_SRCOFFSET 12 #define IP4_DSTOFFSET 16 @@ -67,6 +58,58 @@ static void check_peer(n2n_edge_t * eee, uint8_t from_supernode, const n2n_mac_t mac, const n2n_sock_t * peer); +static int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port); +static void supernode2addr(n2n_sock_t * sn, const n2n_sn_name_t addrIn); + +/* ************************************** */ + +int edge_verify_conf(const n2n_edge_conf_t *conf) { + if(conf->community_name[0] == 0) + return(-1); + + if(conf->sn_num == 0) + return(-2); + + return(0); +} + +/* ************************************** */ + +struct n2n_edge { + n2n_edge_conf_t conf; + + /* Status */ + uint8_t sn_idx; /**< Currently active supernode. */ + uint8_t sn_wait; /**< Whether we are waiting for a supernode response. */ + size_t sup_attempts; /**< Number of remaining attempts to this supernode. */ + tuntap_dev device; /**< All about the TUNTAP device */ + n2n_trans_op_t transop; /**< The transop to use when encoding */ + n2n_cookie_t last_cookie; /**< Cookie sent in last REGISTER_SUPER. */ + + /* Sockets */ + n2n_sock_t supernode; + n2n_sock_t multicast_peer; /**< Multicast peer group (for local edges) */ + int udp_sock; + int udp_mgmt_sock; /**< socket for status info. */ + int udp_multicast_sock; /**< socket for local multicast registrations. */ + + /* Peers */ + struct peer_info * known_peers; /**< Edges we are connected to. */ + struct peer_info * pending_peers; /**< Edges we have tried to register with. */ + + /* Timers */ + time_t last_register_req; /**< Check if time to re-register with super*/ + size_t register_lifetime; /**< Time distance after last_register_req at which to re-register. */ + time_t last_p2p; /**< Last time p2p traffic was received. */ + time_t last_sup; /**< Last time a packet arrived from supernode. */ + time_t start_time; /**< For calculating uptime */ + + /* Statistics */ + size_t tx_p2p; + size_t rx_p2p; + size_t tx_sup; + size_t rx_sup; +}; /* ************************************** */ @@ -74,49 +117,89 @@ static void check_peer(n2n_edge_t * eee, * * This also initialises the NULL transform operation opstruct. */ -int edge_init(n2n_edge_t * eee) { +n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *rv) { + n2n_transform_t transop_id = conf->transop_id; + n2n_edge_t *eee = calloc(1, sizeof(n2n_edge_t)); + int rc = -1; + + if((rc = edge_verify_conf(conf)) != 0) { + traceEvent(TRACE_ERROR, "Invalid configuration"); + goto edge_init_error; + } + + if(!eee) { + traceEvent(TRACE_ERROR, "Cannot allocate memory"); + goto edge_init_error; + } + #ifdef WIN32 initWin32(); #endif - memset(eee, 0, sizeof(n2n_edge_t)); + memcpy(&eee->conf, conf, sizeof(*conf)); + memcpy(&eee->device, dev, sizeof(*dev)); eee->start_time = time(NULL); /* REVISIT: BbMaj7 : Should choose something with less predictability * particularly for embedded targets with no real-time clock. */ srand(eee->start_time); - transop_null_init( &(eee->transop[N2N_TRANSOP_NULL_IDX])); - transop_twofish_init(&(eee->transop[N2N_TRANSOP_TF_IDX] )); - transop_aes_init(&(eee->transop[N2N_TRANSOP_AESCBC_IDX] )); - - eee->tx_transop_idx = N2N_TRANSOP_NULL_IDX; /* No guarantee the others have been setup */ - - eee->daemon = 1; /* By default run in daemon mode. */ - eee->preferred_aes = 0; /* Disable AES by default (for compatibility) */ - eee->re_resolve_supernode_ip = 0; - /* keyschedule set to NULLs by memset */ - /* community_name set to NULLs by memset */ - eee->null_transop = 0; - eee->udp_sock = -1; - eee->udp_mgmt_sock = -1; - eee->udp_multicast_sock = -1; - eee->dyn_ip_mode = 0; - eee->allow_routing = 0; - eee->drop_multicast = 1; eee->known_peers = NULL; eee->pending_peers = NULL; - eee->last_register_req = 0; eee->register_lifetime = REGISTER_SUPER_INTERVAL_DFL; - eee->last_p2p = 0; - eee->last_sup = 0; eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; +#ifdef NOT_USED if(lzo_init() != LZO_E_OK) { traceEvent(TRACE_ERROR, "LZO compression error"); - return(-1); + goto edge_init_error; + } +#endif + + for(int i=0; isn_num; ++i) + traceEvent(TRACE_NORMAL, "supernode %u => %s\n", i, (conf->sn_ip_array[i])); + + /* Set the active supernode */ + supernode2addr(&(eee->supernode), conf->sn_ip_array[eee->sn_idx]); + + /* Set active transop */ + if(conf->encrypt_key == NULL) + transop_id = N2N_TRANSFORM_ID_NULL; + + switch(transop_id) { + case N2N_TRANSFORM_ID_TWOFISH: + rc = n2n_transop_twofish_init(&eee->conf, &eee->transop); + break; +#ifdef N2N_HAVE_AES + case N2N_TRANSFORM_ID_AESCBC: + rc = n2n_transop_aes_cbc_init(&eee->conf, &eee->transop); + break; +#endif + default: + rc = n2n_transop_null_init(&eee->conf, &eee->transop); } - return(0); + if((rc < 0) || (eee->transop.fwd == NULL) || (eee->transop.transform_id != transop_id)) { + traceEvent(TRACE_ERROR, "Transop init failed"); + goto edge_init_error; + } + + if(eee->transop.no_encryption) + traceEvent(TRACE_WARNING, "Encryption is disabled in edge"); + + if(edge_init_sockets(eee, conf->local_port, conf->mgmt_port) < 0) { + traceEvent(TRACE_ERROR, "Error: socket setup failed"); + goto edge_init_error; + } + +//edge_init_success: + *rv = 0; + return(eee); + +edge_init_error: + if(eee) + free(eee); + *rv = rc; + return(NULL); } /* ***************************************************** */ @@ -126,7 +209,7 @@ int edge_init(n2n_edge_t * eee) { * REVISIT: This is a really bad idea. The edge will block completely while the * hostname resolution is performed. This could take 15 seconds. */ -void supernode2addr(n2n_sock_t * sn, const n2n_sn_name_t addrIn) { +static void supernode2addr(n2n_sock_t * sn, const n2n_sn_name_t addrIn) { n2n_sn_name_t addr; const char *supernode_host; @@ -501,7 +584,7 @@ static void send_register_super(n2n_edge_t * eee, cmn.ttl=N2N_DEFAULT_TTL; cmn.pc = n2n_register_super; cmn.flags = 0; - memcpy(cmn.community, eee->community_name, N2N_COMMUNITY_SIZE); + memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); for(idx=0; idx < N2N_COOKIE_SIZE; ++idx) eee->last_cookie[idx] = rand() % 0xff; @@ -538,7 +621,7 @@ static void send_register(n2n_edge_t * eee, cmn.ttl=N2N_DEFAULT_TTL; cmn.pc = n2n_register; cmn.flags = 0; - memcpy(cmn.community, eee->community_name, N2N_COMMUNITY_SIZE); + memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); idx=0; encode_uint32(reg.cookie, &idx, 123456789); @@ -572,7 +655,7 @@ static void send_register_ack(n2n_edge_t * eee, cmn.ttl=N2N_DEFAULT_TTL; cmn.pc = n2n_register_ack; cmn.flags = 0; - memcpy(cmn.community, eee->community_name, N2N_COMMUNITY_SIZE); + memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); memset(&ack, 0, sizeof(ack)); memcpy(ack.cookie, reg->cookie, N2N_COOKIE_SIZE); @@ -608,24 +691,24 @@ static void update_supernode_reg(n2n_edge_t * eee, time_t nowTime) { /* Give up on that supernode and try the next one. */ ++(eee->sn_idx); - if (eee->sn_idx >= eee->sn_num) { + if (eee->sn_idx >= eee->conf.sn_num) { /* Got to end of list, go back to the start. Also works for list of one entry. */ eee->sn_idx=0; } traceEvent(TRACE_WARNING, "Supernode not responding - moving to %u of %u", - (unsigned int)eee->sn_idx, (unsigned int)eee->sn_num); + (unsigned int)eee->sn_idx, (unsigned int)eee->conf.sn_num); eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; } else --(eee->sup_attempts); - for(sn_idx=0; sn_idxsn_num; sn_idx++) { - supernode2addr(&(eee->supernode), eee->sn_ip_array[sn_idx]); + for(sn_idx=0; sn_idxconf.sn_num; sn_idx++) { + supernode2addr(&(eee->supernode), eee->conf.sn_ip_array[sn_idx]); traceEvent(TRACE_INFO, "Registering with supernode [id: %u/%u][%s][attempts left %u]", - sn_idx+1, eee->sn_num, + sn_idx+1, eee->conf.sn_num, supernode_ip(eee), (unsigned int)eee->sup_attempts); send_register_super(eee, &(eee->supernode)); @@ -657,119 +740,7 @@ static void send_deregister(n2n_edge_t * eee, /** Return the IP address of the current supernode in the ring. */ static const char * supernode_ip(const n2n_edge_t * eee) { - return (eee->sn_ip_array)[eee->sn_idx]; -} - -/* ************************************** */ - -static int edge_init_twofish_psk(n2n_edge_t * eee, uint8_t *encrypt_pwd, - uint32_t encrypt_pwd_len) { - return transop_twofish_setup_psk(&(eee->transop[N2N_TRANSOP_TF_IDX]), - 0, encrypt_pwd, encrypt_pwd_len); -} - -/* ************************************** */ - -static int edge_init_aes_psk(n2n_edge_t * eee, uint8_t *encrypt_pwd, - uint32_t encrypt_pwd_len) { - return transop_aes_setup_psk(&(eee->transop[N2N_TRANSOP_AESCBC_IDX]), - 0, encrypt_pwd, encrypt_pwd_len); -} - -/* ************************************** */ - -int edge_init_encryption(n2n_edge_t * eee, uint8_t *encrypt_pwd, uint32_t encrypt_pwd_len) { -#ifdef N2N_HAVE_AES - if(edge_init_aes_psk(eee, encrypt_pwd, encrypt_pwd_len) < 0) { - traceEvent(TRACE_ERROR, "Error: AES PSK setup failed"); - return(-1); - } -#endif - - if(edge_init_twofish_psk(eee, encrypt_pwd, encrypt_pwd_len) < 0) { - traceEvent(TRACE_ERROR, "Error: twofish PSK setup failed"); - return(-1); - } - - return(0); -} - -/* ************************************** */ - -static n2n_tostat_t n2n_tick_aes(n2n_edge_t * eee, time_t now, size_t *trop) { - n2n_tostat_t tst = (eee->transop[N2N_TRANSOP_AESCBC_IDX].tick)(&(eee->transop[N2N_TRANSOP_AESCBC_IDX]), now); - - if(tst.can_tx) - { - traceEvent(TRACE_DEBUG, "can_tx AESCBC (idx=%u)", (unsigned int)N2N_TRANSOP_AESCBC_IDX); - *trop = N2N_TRANSOP_AESCBC_IDX; - } - - return tst; -} - -/* ************************************** */ -static n2n_tostat_t n2n_tick_twofish(n2n_edge_t * eee, time_t now, size_t *trop) { - n2n_tostat_t tst = (eee->transop[N2N_TRANSOP_TF_IDX].tick)(&(eee->transop[N2N_TRANSOP_TF_IDX]), now); - if(tst.can_tx) - { - traceEvent(TRACE_DEBUG, "can_tx TF (idx=%u)", (unsigned int)N2N_TRANSOP_TF_IDX); - *trop = N2N_TRANSOP_TF_IDX; - } - - return tst; -} - -/* ************************************** */ - -/** Called periodically to roll keys and do any periodic maintenance in the - * tranform operations state machines. */ -static int n2n_tick_transop(n2n_edge_t * eee, time_t now) -{ - size_t trop = eee->tx_transop_idx; - - /* Tests are done in order that most preferred transform is last and causes - * tx_transop_idx to be left at most preferred valid transform. */ - (eee->transop[N2N_TRANSOP_NULL_IDX].tick)(&(eee->transop[N2N_TRANSOP_NULL_IDX]), now); - - if(eee->preferred_aes) { - n2n_tick_twofish(eee, now, &trop); - n2n_tick_aes(eee, now, &trop); - } else { - n2n_tick_aes(eee, now, &trop); - n2n_tick_twofish(eee, now, &trop); - } - - if(trop != eee->tx_transop_idx) - { - eee->tx_transop_idx = trop; - traceEvent(TRACE_INFO, "Chose new tx_transop_idx=%u", (unsigned int)(eee->tx_transop_idx)); - } - - return 0; -} - -/* ************************************** */ - -/** Find the transop op-struct for the transform enumeration required. - * - * @return - index into the transop array, or -1 on failure. - */ -static int transop_enum_to_index(n2n_transform_t id) { - switch (id) - { - case N2N_TRANSFORM_ID_TWOFISH: - return N2N_TRANSOP_TF_IDX; - break; - case N2N_TRANSFORM_ID_NULL: - return N2N_TRANSOP_NULL_IDX; - break; - case N2N_TRANSFORM_ID_AESCBC: - return N2N_TRANSOP_AESCBC_IDX; - break; - default: - return -1; - } + return (eee->conf.sn_ip_array)[eee->sn_idx]; } /* ************************************** */ @@ -816,20 +787,19 @@ static int handle_PACKET(n2n_edge_t * eee, { uint8_t decodebuf[N2N_PKT_BUF_SIZE]; size_t eth_size; - int rx_transop_idx; + n2n_transform_t rx_transop_id; - rx_transop_idx = transop_enum_to_index(pkt->transform); + rx_transop_id = (n2n_transform_t)pkt->transform; - if(rx_transop_idx >=0) - { + if(rx_transop_id == eee->conf.transop_id) { eth_payload = decodebuf; eh = (ether_hdr_t*)eth_payload; - eth_size = eee->transop[rx_transop_idx].rev(&(eee->transop[rx_transop_idx]), + eth_size = eee->transop.rev(&eee->transop, eth_payload, N2N_PKT_BUF_SIZE, payload, psize, pkt->srcMac); - ++(eee->transop[rx_transop_idx].rx_cnt); /* stats */ + ++(eee->transop.rx_cnt); /* stats */ - if(!(eee->allow_routing)) { + if(!(eee->conf.allow_routing)) { if(ntohs(eh->type) == 0x0800) { uint32_t *dst = (uint32_t*)ð_payload[ETH_FRAMESIZE + IP4_DSTOFFSET]; @@ -857,8 +827,8 @@ static int handle_PACKET(n2n_edge_t * eee, } else { - traceEvent(TRACE_ERROR, "handle_PACKET dropped unknown transform enum %u", - (unsigned int)pkt->transform); + traceEvent(TRACE_ERROR, "invalid transop ID: %u, expected %u", + rx_transop_id, eee->conf.transop_id); } } @@ -910,7 +880,6 @@ static void readFromMgmtSocket(n2n_edge_t * eee, int * keep_running) { " help This help message\n" " +verb Increase verbosity of logging\n" " -verb Decrease verbosity of logging\n" - " reload Re-read the keyschedule\n" " Display statistics\n\n"); sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, @@ -962,25 +931,6 @@ static void readFromMgmtSocket(n2n_edge_t * eee, int * keep_running) { } } - if(recvlen >= 6) - { - if(0 == memcmp(udp_buf, "reload", 6)) - { - if(strlen(eee->keyschedule) > 0) - { - if(edge_init_keyschedule(eee) == 0) - { - msg_len=0; - msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len), - "> OK\n"); - sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, - (struct sockaddr *)&sender_sock, sizeof(struct sockaddr_in)); - } - return; - } - } - } - traceEvent(TRACE_DEBUG, "mgmt status rq"); msg_len=0; @@ -999,15 +949,9 @@ static void readFromMgmtSocket(n2n_edge_t * eee, int * keep_running) { (unsigned int)eee->rx_p2p); msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len), - "trans:null |%6u|%6u|\n" - "trans:tf |%6u|%6u|\n" - "trans:aes |%6u|%6u|\n", - (unsigned int)eee->transop[N2N_TRANSOP_NULL_IDX].tx_cnt, - (unsigned int)eee->transop[N2N_TRANSOP_NULL_IDX].rx_cnt, - (unsigned int)eee->transop[N2N_TRANSOP_TF_IDX].tx_cnt, - (unsigned int)eee->transop[N2N_TRANSOP_TF_IDX].rx_cnt, - (unsigned int)eee->transop[N2N_TRANSOP_AESCBC_IDX].tx_cnt, - (unsigned int)eee->transop[N2N_TRANSOP_AESCBC_IDX].rx_cnt); + "transop |%6u|%6u|\n", + (unsigned int)eee->transop.tx_cnt, + (unsigned int)eee->transop.rx_cnt); msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len), "peers pend:%u full:%u\n", @@ -1071,25 +1015,6 @@ static int is_ethMulticast(const void * buf, size_t bufsize) { /* ************************************** */ -/* Choose the transop for Tx. This should be based on the newest valid - * cipherspec in the key schedule. - * - * Never fall back to NULL tranform unless no key sources were specified. It is - * better to render edge inoperative than to expose user data in the clear. In - * the case where all SAs are expired an arbitrary transform will be chosen for - * Tx. It will fail having no valid SAs but one must be selected. - */ -static size_t edge_choose_tx_transop(const n2n_edge_t * eee) { - if(eee->null_transop) - { - return N2N_TRANSOP_NULL_IDX; - } - - return eee->tx_transop_idx; -} - -/* ************************************** */ - /* @return 1 if destination is a peer, 0 if destination is supernode */ static int find_peer_destination(n2n_edge_t * eee, n2n_mac_t mac_address, @@ -1173,7 +1098,7 @@ void send_packet2net(n2n_edge_t * eee, uint8_t pktbuf[N2N_PKT_BUF_SIZE]; size_t idx=0; - size_t tx_transop_idx=0; + n2n_transform_t tx_transop_idx = eee->transop.transform_id; ether_hdr_t eh; @@ -1181,7 +1106,7 @@ void send_packet2net(n2n_edge_t * eee, memcpy(&eh, tap_pkt, sizeof(ether_hdr_t)); /* Discard IP packets that are not originated by this hosts */ - if(!(eee->allow_routing)) { + if(!(eee->conf.allow_routing)) { if(ntohs(eh.type) == 0x0800) { /* This is an IP packet from the local source address - not forwarded. */ uint32_t *src = (uint32_t*)&tap_pkt[ETH_FRAMESIZE + IP4_SRCOFFSET]; @@ -1209,26 +1134,24 @@ void send_packet2net(n2n_edge_t * eee, cmn.ttl = N2N_DEFAULT_TTL; cmn.pc = n2n_packet; cmn.flags=0; /* no options, not from supernode, no socket */ - memcpy(cmn.community, eee->community_name, N2N_COMMUNITY_SIZE); + memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); memset(&pkt, 0, sizeof(pkt)); memcpy(pkt.srcMac, eee->device.mac_addr, N2N_MAC_SIZE); memcpy(pkt.dstMac, destMac, N2N_MAC_SIZE); - tx_transop_idx = edge_choose_tx_transop(eee); - pkt.sock.family=0; /* do not encode sock */ - pkt.transform = eee->transop[tx_transop_idx].transform_id; + pkt.transform = tx_transop_idx; idx=0; encode_PACKET(pktbuf, &idx, &cmn, &pkt); - traceEvent(TRACE_DEBUG, "encoded PACKET header of size=%u transform %u (idx=%u)", - (unsigned int)idx, (unsigned int)pkt.transform, (unsigned int)tx_transop_idx); + traceEvent(TRACE_DEBUG, "encoded PACKET header of size=%u transform %u", + (unsigned int)idx, tx_transop_idx); - idx += eee->transop[tx_transop_idx].fwd(&(eee->transop[tx_transop_idx]), + idx += eee->transop.fwd(&eee->transop, pktbuf+idx, N2N_PKT_BUF_SIZE-idx, tap_pkt, len, pkt.dstMac); - ++(eee->transop[tx_transop_idx].tx_cnt); /* stats */ + eee->transop.tx_cnt++; /* stats */ send_packet(eee, destMac, pktbuf, idx); /* to peer or supernode */ } @@ -1269,7 +1192,7 @@ static void readFromTAPSocket(n2n_edge_t * eee) { traceEvent(TRACE_INFO, "### Rx TAP packet (%4d) for %s", (signed int)len, macaddr_str(mac_buf, mac)); - if(eee->drop_multicast && + if(eee->conf.drop_multicast && (is_ip6_discovery(eth_pkt, len) || is_ethMulticast(eth_pkt, len) ) @@ -1383,7 +1306,7 @@ static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { msg_type = cmn.pc; /* packet code */ from_supernode= cmn.flags & N2N_FLAGS_FROM_SUPERNODE; - if(0 == memcmp(cmn.community, eee->community_name, N2N_COMMUNITY_SIZE)) { + if(0 == memcmp(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE)) { if(msg_type == MSG_TYPE_PACKET) { /* process PACKET - most frequent so first in list. */ n2n_PACKET_t pkt; @@ -1560,8 +1483,8 @@ int run_edge_loop(n2n_edge_t * eee, int *keep_running) { /* Make sure ciphers are updated before the packet is treated. */ if((nowTime - lastTransop) > TRANSOP_TICK_INTERVAL) { lastTransop = nowTime; - - n2n_tick_transop(eee, nowTime); + + eee->transop.tick(&eee->transop, nowTime); } if(rc > 0) { @@ -1614,7 +1537,7 @@ int run_edge_loop(n2n_edge_t * eee, int *keep_running) { (unsigned int)peer_list_size(eee->known_peers)); } - if(eee->dyn_ip_mode && + if(eee->conf.dyn_ip_mode && ((nowTime - lastIfaceCheck) > IFACE_UPDATE_INTERVAL)) { traceEvent(TRACE_NORMAL, "Re-checking dynamic IP address."); tuntap_get_address(&(eee->device)); @@ -1632,75 +1555,12 @@ int run_edge_loop(n2n_edge_t * eee, int *keep_running) { send_deregister(eee, &(eee->supernode)); closesocket(eee->udp_sock); - tuntap_close(&(eee->device)); - - edge_term(eee); return(0); } /* ************************************** */ -/** Read in a key-schedule file, parse the lines and pass each line to the - * appropriate trans_op for parsing of key-data and adding key-schedule - * entries. The lookup table of time->trans_op is constructed such that - * encoding can be passed to the correct trans_op. The trans_op internal table - * will then determine the best SA for that trans_op from the key schedule to - * use for encoding. */ - -int edge_init_keyschedule(n2n_edge_t * eee) { -#define N2N_NUM_CIPHERSPECS 32 - - int retval = -1; - ssize_t numSpecs=0; - n2n_cipherspec_t specs[N2N_NUM_CIPHERSPECS]; - size_t i; - time_t now = time(NULL); - - numSpecs = n2n_read_keyfile(specs, N2N_NUM_CIPHERSPECS, eee->keyschedule); - - if(numSpecs > 0) - { - traceEvent(TRACE_NORMAL, "keyfile = %s read -> %d specs.\n", optarg, (signed int)numSpecs); - - for (i=0; i < (size_t)numSpecs; ++i) - { - int idx; - - idx = transop_enum_to_index(specs[i].t); - - switch (idx) - { - case N2N_TRANSOP_TF_IDX: - case N2N_TRANSOP_AESCBC_IDX: - { - retval = (eee->transop[idx].addspec)(&(eee->transop[idx]), - &(specs[i])); - break; - } - default: - retval = -1; - } - - if (0 != retval) - { - traceEvent(TRACE_ERROR, "keyschedule failed to add spec[%u] to transop[%d].\n", - (unsigned int)i, idx); - - return retval; - } - } - - n2n_tick_transop(eee, now); - } - else - traceEvent(TRACE_ERROR, "Failed to process '%s'", eee->keyschedule); - - return retval; -} - -/* ************************************** */ - /** Deinitialise the edge and deallocate any owned memory. */ void edge_term(n2n_edge_t * eee) { if(eee->udp_sock >= 0) @@ -1715,38 +1575,13 @@ void edge_term(n2n_edge_t * eee) { clear_peer_list(&(eee->pending_peers)); clear_peer_list(&(eee->known_peers)); - (eee->transop[N2N_TRANSOP_TF_IDX].deinit)(&eee->transop[N2N_TRANSOP_TF_IDX]); - (eee->transop[N2N_TRANSOP_NULL_IDX].deinit)(&eee->transop[N2N_TRANSOP_NULL_IDX]); + eee->transop.deinit(&eee->transop); + memset(eee, 0, sizeof(*eee)); } /* ************************************** */ -const char *random_device_mac(void) -{ - const char key[] = "0123456789abcdef"; - static char mac[18]; - int i; - - for (i = 0; i < sizeof(mac) - 1; ++i) { - if ((i + 1) % 3 == 0) { - mac[i] = ':'; - continue; - } -#ifdef WIN32 -#define random rand -#endif - mac[i] = key[random() % sizeof(key)]; -#ifdef WIN32 -#undef random -#endif - } - mac[sizeof(mac) - 1] = '\0'; - return mac; -} - -/* ************************************** */ - -int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port) { +static int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port) { /* Populate the multicast group for local edge */ eee->multicast_peer.family = AF_INET; eee->multicast_peer.port = N2N_MULTICAST_PORT; @@ -1755,6 +1590,9 @@ int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port) { eee->multicast_peer.addr.v4[2] = 0; eee->multicast_peer.addr.v4[3] = 68; + if(udp_local_port > 0) + traceEvent(TRACE_NORMAL, "Binding to local port %d", udp_local_port); + eee->udp_sock = open_socket(udp_local_port, 1 /* bind ANY */); if(eee->udp_sock < 0) { traceEvent(TRACE_ERROR, "Failed to bind main UDP port %u", udp_local_port); @@ -1799,28 +1637,73 @@ int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port) { /* ************************************** */ +void edge_init_conf_defaults(n2n_edge_conf_t *conf) { + memset(conf, 0, sizeof(*conf)); + + conf->local_port = 0 /* any port */; + conf->mgmt_port = N2N_EDGE_MGMT_PORT; /* 5644 by default */ + conf->transop_id = N2N_TRANSFORM_ID_TWOFISH; /* use twofish for compatibility */ + conf->drop_multicast = 1; + + if(getenv("N2N_KEY")) + conf->encrypt_key = strdup(getenv("N2N_KEY")); +} + +/* ************************************** */ + +const n2n_edge_conf_t* edge_get_conf(const n2n_edge_t *eee) { + return(&eee->conf); +} + +/* ************************************** */ + +int edge_conf_add_supernode(n2n_edge_conf_t *conf, const char *ip_and_port) { + if(conf->sn_num >= N2N_EDGE_NUM_SUPERNODES) + return(-1); + + strncpy((conf->sn_ip_array[conf->sn_num]), ip_and_port, N2N_EDGE_SN_HOST_SIZE); + traceEvent(TRACE_NORMAL, "Adding supernode[%u] = %s", (unsigned int)conf->sn_num, (conf->sn_ip_array[conf->sn_num])); + conf->sn_num++; + + return(0); +} + +/* ************************************** */ + int quick_edge_init(char *device_name, char *community_name, char *encrypt_key, char *device_mac, char *local_ip_address, char *supernode_ip_address_port, int *keep_on_running) { - n2n_edge_t eee; + tuntap_dev tuntap; + n2n_edge_t *eee; + n2n_edge_conf_t conf; + int rv; - edge_init(&eee); + /* Setup the configuration */ + edge_init_conf_defaults(&conf); + conf.encrypt_key = encrypt_key; + snprintf((char*)conf.community_name, sizeof(conf.community_name), "%s", community_name); + edge_conf_add_supernode(&conf, supernode_ip_address_port); - if(tuntap_open(&(eee.device), device_name, "static", - local_ip_address, "255.255.255.0", - device_mac, DEFAULT_MTU) < 0) + /* Validate configuration */ + if(edge_verify_conf(&conf) != 0) return(-1); - if(edge_init_encryption(&eee, (uint8_t *)encrypt_key, strlen(encrypt_key) < 0)) + /* Open the tuntap device */ + if(tuntap_open(&tuntap, device_name, "static", + local_ip_address, "255.255.255.0", + device_mac, DEFAULT_MTU) < 0) return(-2); - snprintf((char*)eee.community_name, sizeof(eee.community_name), "%s", community_name); - supernode2addr(&(eee.supernode), supernode_ip_address_port); + /* Init edge */ + if((eee = edge_init(&tuntap, &conf, &rv)) == NULL) + goto quick_edge_init_end; - if(edge_init_sockets(&eee, 0 /* ANY port */, 0 /* ANY port */) < 0) - return(-3); + rv = run_edge_loop(eee, keep_on_running); + edge_term(eee); - return(run_edge_loop(&eee, keep_on_running)); +quick_edge_init_end: + tuntap_close(&tuntap); + return(rv); } diff --git a/legacy/README.md b/legacy/README.md new file mode 100644 index 0000000..174c114 --- /dev/null +++ b/legacy/README.md @@ -0,0 +1,33 @@ +# Removed Features + +This folder contains a list N2N legacy features which have been dropped due to +maintainance cost versus effective use and benefits. + +Multiple Transops +----------------- + +N2N used to initialize all the available transops and use the "tick" function of +the transops to decide which transop to use before sending a packet. This however +has the following problems: + +- It only works with the keyfile, whereas with normal encryption we inizialize and + keep structures that we don't need. +- It is unfeasable as an edge node is required to implement all the transops in order + to properly talk with other edge nodes (via keyfile). +- It rises the complexity of the code. +- It is not clear which transop will be used. +- Mixing multiple encyptions together is not necessarily a good idea to improve security + as a vulnerability in at least one encryption method will leak some information. + +Keyfile and Key Rotation +------------------------ + +The keyfile mechanism allowed N2N users to specify a keyfile to be used to periodically +rotate keys and encryption methods. However, it has the following problems: + +- This feature is obscure for most of the users and poorly documented. +- It is tightly integrated in the core whereas it is used by only a few people (if any). + +In conclusion the main problem is the complexity that it adds to the code. In a possible +future rework this could be integrated as an extention (e.g. a specific trasop) without +rising the core complexity. diff --git a/legacy/edge_keyschedule.c b/legacy/edge_keyschedule.c new file mode 100644 index 0000000..5e3af35 --- /dev/null +++ b/legacy/edge_keyschedule.c @@ -0,0 +1,103 @@ +typedef struct n2n_tostat { + uint8_t can_tx; /* Does this transop have a valid SA for encoding. */ + n2n_cipherspec_t tx_spec; /* If can_tx, the spec used to encode. */ +} n2n_tostat_t; + +typedef uint32_t n2n_sa_t; /* security association number */ +typedef int (*n2n_transaddspec_f)( struct n2n_trans_op * arg, + const n2n_cipherspec_t * cspec ); + +typedef n2n_tostat_t (*n2n_transtick_f)( struct n2n_trans_op * arg, + time_t now ); + +/** Read in a key-schedule file, parse the lines and pass each line to the + * appropriate trans_op for parsing of key-data and adding key-schedule + * entries. The lookup table of time->trans_op is constructed such that + * encoding can be passed to the correct trans_op. The trans_op internal table + * will then determine the best SA for that trans_op from the key schedule to + * use for encoding. */ + +static int edge_init_keyschedule(n2n_edge_t *eee) { +#define N2N_NUM_CIPHERSPECS 32 + + int retval = -1; + ssize_t numSpecs=0; + n2n_cipherspec_t specs[N2N_NUM_CIPHERSPECS]; + size_t i; + time_t now = time(NULL); + + numSpecs = n2n_read_keyfile(specs, N2N_NUM_CIPHERSPECS, eee->conf.keyschedule); + + if(numSpecs > 0) + { + traceEvent(TRACE_NORMAL, "keyfile = %s read -> %d specs.\n", optarg, (signed int)numSpecs); + + for (i=0; i < (size_t)numSpecs; ++i) + { + n2n_transform_t idx = (n2n_transform_t) specs[i].t; + if(idx != eee->transop.transform_id) { + traceEvent(TRACE_ERROR, "changing transop in keyschedule is not supported"); + retval = -1; + } + + if(eee->transop.addspec != NULL) + retval = eee->transop.addspec(&eee->transop, &(specs[i])); + + if (0 != retval) + { + traceEvent(TRACE_ERROR, "keyschedule failed to add spec[%u] to transop[%d].\n", + (unsigned int)i, idx); + + return retval; + } + } + + n2n_tick_transop(eee, now); + } + else + traceEvent(TRACE_ERROR, "Failed to process '%s'", eee->conf.keyschedule); + + return retval; +} + +#if 0 + if(recvlen >= 6) + { + if(0 == memcmp(udp_buf, "reload", 6)) + { + if(strlen(eee->conf.keyschedule) > 0) + { + if(edge_init_keyschedule(eee) == 0) + { + msg_len=0; + msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len), + "> OK\n"); + sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, + (struct sockaddr *)&sender_sock, sizeof(struct sockaddr_in)); + } + return; + } + } + } +#endif + +#if 0 + case'K': + { + if(conf->encrypt_key) { + traceEvent(TRACE_ERROR, "Error: -K and -k options are mutually exclusive"); + exit(1); + } else { + strncpy(conf->keyschedule, optargument, N2N_PATHNAME_MAXLEN-1); + /* strncpy does not add NULL if the source has no NULL. */ + conf->keyschedule[N2N_PATHNAME_MAXLEN-1] = 0; + + traceEvent(TRACE_NORMAL, "keyfile = '%s'\n", conf->keyschedule); + } + break; + } +#endif + +#if 0 + printf("-K | Specify a key schedule file to load. Not with -k.\n"); +#endif diff --git a/gen_keyfile.py b/legacy/gen_keyfile.py similarity index 100% rename from gen_keyfile.py rename to legacy/gen_keyfile.py diff --git a/n2n_keyfile.c b/legacy/n2n_keyfile.c similarity index 100% rename from n2n_keyfile.c rename to legacy/n2n_keyfile.c diff --git a/n2n_keyfile.h b/legacy/n2n_keyfile.h similarity index 100% rename from n2n_keyfile.h rename to legacy/n2n_keyfile.h diff --git a/legacy/transform_aes.c b/legacy/transform_aes.c new file mode 100644 index 0000000..7b198ff --- /dev/null +++ b/legacy/transform_aes.c @@ -0,0 +1,730 @@ +/** + * (C) 2007-18 - ntop.org and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not see see + * + */ + +#include "n2n.h" +#include "n2n_transforms.h" + +#if defined(N2N_HAVE_AES) + +#include "openssl/aes.h" +#include "openssl/sha.h" +#ifndef _MSC_VER +/* Not included in Visual Studio 2008 */ +#include /* index() */ +#endif + +#define N2N_AES_NUM_SA 32 /* space for SAa */ + +#define N2N_AES_TRANSFORM_VERSION 1 /* version of the transform encoding */ +#define N2N_AES_IVEC_SIZE 32 /* Enough space for biggest AES ivec */ + +#define AES256_KEY_BYTES (256/8) +#define AES192_KEY_BYTES (192/8) +#define AES128_KEY_BYTES (128/8) + +typedef unsigned char n2n_aes_ivec_t[N2N_AES_IVEC_SIZE]; + +struct sa_aes +{ + n2n_cipherspec_t spec; /* cipher spec parameters */ + n2n_sa_t sa_id; /* security association index */ + AES_KEY enc_key; /* tx key */ + AES_KEY dec_key; /* tx key */ + AES_KEY iv_enc_key; /* key used to encrypt the IV */ + uint8_t iv_ext_val[AES128_KEY_BYTES]; /* key used to extend the random IV seed to full block size */ +}; + +typedef struct sa_aes sa_aes_t; + + +/** Aes transform state data. + * + * With a key-schedule in place this will be populated with a number of + * SAs. Each SA has a lifetime and some opque data. The opaque data for aes + * consists of the SA number and key material. + * + */ +struct transop_aes +{ + ssize_t tx_sa; + size_t num_sa; + sa_aes_t sa[N2N_AES_NUM_SA]; + u_int8_t psk_mode; +}; + +typedef struct transop_aes transop_aes_t; + +static ssize_t aes_find_sa( const transop_aes_t * priv, const n2n_sa_t req_id ); +static int setup_aes_key(transop_aes_t *priv, const uint8_t *key, ssize_t key_size, size_t sa_num); + +static int transop_deinit_aes( n2n_trans_op_t * arg ) +{ + transop_aes_t * priv = (transop_aes_t *)arg->priv; + size_t i; + + if ( priv ) + { + /* Memory was previously allocated */ + for (i=0; isa[i]); + + sa->sa_id=0; + } + + priv->num_sa=0; + priv->tx_sa=-1; + + free(priv); + } + + arg->priv=NULL; /* return to fully uninitialised state */ + + return 0; +} + +static size_t aes_choose_tx_sa( transop_aes_t * priv, const u_int8_t * peer_mac ) { + return priv->tx_sa; /* set in tick */ +} + +static ssize_t aes_choose_rx_sa( transop_aes_t * priv, const u_int8_t * peer_mac, ssize_t sa_rx) { + if(!priv->psk_mode) + return aes_find_sa(priv, sa_rx); + else + /* NOTE the sa_rx of the packet is ignored in this case */ + return 0; +} + +/* AES plaintext preamble */ +#define TRANSOP_AES_VER_SIZE 1 /* Support minor variants in encoding in one module. */ +#define TRANSOP_AES_SA_SIZE 4 +#define TRANSOP_AES_IV_SEED_SIZE 8 +#define TRANSOP_AES_PREAMBLE_SIZE (TRANSOP_AES_VER_SIZE + TRANSOP_AES_SA_SIZE + TRANSOP_AES_IV_SEED_SIZE) + +/* AES ciphertext preamble */ +#define TRANSOP_AES_NONCE_SIZE 4 + +/* Return the best acceptable AES key size (in bytes) given an input keysize. + * + * The value returned will be one of AES128_KEY_BYTES, AES192_KEY_BYTES or + * AES256_KEY_BYTES. + */ +static size_t aes_best_keysize(size_t numBytes) +{ + if (numBytes >= AES256_KEY_BYTES ) + { + return AES256_KEY_BYTES; + } + else if (numBytes >= AES192_KEY_BYTES) + { + return AES192_KEY_BYTES; + } + else + { + return AES128_KEY_BYTES; + } +} + +static void set_aes_cbc_iv(sa_aes_t *sa, n2n_aes_ivec_t ivec, uint64_t iv_seed) { + uint8_t iv_full[AES_BLOCK_SIZE]; + + /* Extend the seed to full block size via the fixed ext value */ + memcpy(iv_full, sa->iv_ext_val, sizeof(iv_seed)); // note: only 64bits used of 128 available + memcpy(iv_full + sizeof(iv_seed), &iv_seed, sizeof(iv_seed)); + + /* Encrypt the IV with secret key to make it unpredictable. + * As discussed in https://github.com/ntop/n2n/issues/72, it's important to + * have an unpredictable IV since the initial part of the packet plaintext + * can be easily reconstructed from plaintext headers and used by an attacker + * to perform differential analysis. + */ + AES_ecb_encrypt(iv_full, ivec, &sa->iv_enc_key, AES_ENCRYPT); +} + +/** The aes packet format consists of: + * + * - a 8-bit aes encoding version in clear text + * - a 32-bit SA number in clear text + * - a 64-bit random IV seed + * - ciphertext encrypted from a 32-bit nonce followed by the payload. + * + * [V|SSSS|II|nnnnDDDDDDDDDDDDDDDDDDDDD] + * |<------ encrypted ------>| + */ +static int transop_encode_aes( n2n_trans_op_t * arg, + uint8_t * outbuf, + size_t out_len, + const uint8_t * inbuf, + size_t in_len, + const uint8_t * peer_mac) +{ + int len2=-1; + transop_aes_t * priv = (transop_aes_t *)arg->priv; + uint8_t assembly[N2N_PKT_BUF_SIZE] = {0}; + uint32_t * pnonce; + + if ( (in_len + TRANSOP_AES_NONCE_SIZE) <= N2N_PKT_BUF_SIZE ) + { + if ( (in_len + TRANSOP_AES_NONCE_SIZE + TRANSOP_AES_PREAMBLE_SIZE) <= out_len ) + { + int len=-1; + size_t idx=0; + sa_aes_t * sa; + size_t tx_sa_num = 0; + uint64_t iv_seed = 0; + uint8_t padding = 0; + n2n_aes_ivec_t enc_ivec = {0}; + + /* The transmit sa is periodically updated */ + tx_sa_num = aes_choose_tx_sa( priv, peer_mac ); + + sa = &(priv->sa[tx_sa_num]); /* Proper Tx SA index */ + + traceEvent( TRACE_DEBUG, "encode_aes %lu with SA %lu.", in_len, sa->sa_id ); + + /* Encode the aes format version. */ + encode_uint8( outbuf, &idx, N2N_AES_TRANSFORM_VERSION ); + + /* Encode the security association (SA) number */ + encode_uint32( outbuf, &idx, sa->sa_id ); + + /* Generate and encode the IV seed. + * Using two calls to rand() because RAND_MAX is usually < 64bit + * (e.g. linux) and sometimes < 32bit (e.g. Windows). + */ + ((uint32_t*)&iv_seed)[0] = rand(); + ((uint32_t*)&iv_seed)[1] = rand(); + encode_buf(outbuf, &idx, &iv_seed, sizeof(iv_seed)); + + /* Encrypt the assembly contents and write the ciphertext after the SA. */ + len = in_len + TRANSOP_AES_NONCE_SIZE; + + /* The assembly buffer is a source for encrypting data. The nonce is + * written in first followed by the packet payload. The whole + * contents of assembly are encrypted. */ + pnonce = (uint32_t *)assembly; + *pnonce = rand(); + memcpy( assembly + TRANSOP_AES_NONCE_SIZE, inbuf, in_len ); + + /* Need at least one encrypted byte at the end for the padding. */ + len2 = ( (len / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE; /* Round up to next whole AES adding at least one byte. */ + padding = (len2-len); + assembly[len2 - 1] = padding; + traceEvent( TRACE_DEBUG, "padding = %u, seed = %016lx", padding, iv_seed ); + + set_aes_cbc_iv(sa, enc_ivec, iv_seed); + + AES_cbc_encrypt( assembly, /* source */ + outbuf + TRANSOP_AES_PREAMBLE_SIZE, /* dest */ + len2, /* enc size */ + &(sa->enc_key), enc_ivec, AES_ENCRYPT ); + + len2 += TRANSOP_AES_PREAMBLE_SIZE; /* size of data carried in UDP. */ + } + else + { + traceEvent( TRACE_ERROR, "encode_aes outbuf too small." ); + } + } + else + { + traceEvent( TRACE_ERROR, "encode_aes inbuf too big to encrypt." ); + } + + return len2; +} + + +/* Search through the array of SAs to find the one with the required ID. + * + * @return array index where found or -1 if not found + */ +static ssize_t aes_find_sa( const transop_aes_t * priv, const n2n_sa_t req_id ) +{ + size_t i; + + for (i=0; i < priv->num_sa; ++i) + { + const sa_aes_t * sa=NULL; + + sa = &(priv->sa[i]); + if (req_id == sa->sa_id) + { + return i; + } + } + + return -1; +} + + +/* See transop_encode_aes for packet format */ +static int transop_decode_aes( n2n_trans_op_t * arg, + uint8_t * outbuf, + size_t out_len, + const uint8_t * inbuf, + size_t in_len, + const uint8_t * peer_mac) +{ + int len=0; + transop_aes_t * priv = (transop_aes_t *)arg->priv; + uint8_t assembly[N2N_PKT_BUF_SIZE]; + + if ( ( (in_len - TRANSOP_AES_PREAMBLE_SIZE) <= N2N_PKT_BUF_SIZE ) /* Cipher text fits in assembly */ + && (in_len >= (TRANSOP_AES_PREAMBLE_SIZE + TRANSOP_AES_NONCE_SIZE) ) /* Has at least version, SA, iv seed and nonce */ + ) + { + n2n_sa_t sa_rx; + ssize_t sa_idx=-1; + size_t rem=in_len; + size_t idx=0; + uint8_t aes_enc_ver=0; + uint64_t iv_seed=0; + + /* Get the encoding version to make sure it is supported */ + decode_uint8( &aes_enc_ver, inbuf, &rem, &idx ); + + if ( N2N_AES_TRANSFORM_VERSION == aes_enc_ver ) + { + /* Get the SA number and make sure we are decrypting with the right one. */ + decode_uint32( &sa_rx, inbuf, &rem, &idx ); + + sa_idx = aes_choose_rx_sa(priv, peer_mac, sa_rx); + + if ( sa_idx >= 0 ) + { + sa_aes_t * sa = &(priv->sa[sa_idx]); + + /* Get the IV seed */ + decode_buf((uint8_t *)&iv_seed, sizeof(iv_seed), inbuf, &rem, &idx); + + traceEvent( TRACE_DEBUG, "decode_aes %lu with SA %lu and seed %016lx", in_len, sa->sa_id, iv_seed ); + + len = (in_len - TRANSOP_AES_PREAMBLE_SIZE); + + if ( 0 == (len % AES_BLOCK_SIZE ) ) + { + uint8_t padding; + n2n_aes_ivec_t dec_ivec = {0}; + + set_aes_cbc_iv(sa, dec_ivec, iv_seed); + + AES_cbc_encrypt( (inbuf + TRANSOP_AES_PREAMBLE_SIZE), + assembly, /* destination */ + len, + &(sa->dec_key), + dec_ivec, AES_DECRYPT ); + + /* last byte is how much was padding: max value should be + * AES_BLOCKSIZE-1 */ + padding = assembly[ len-1 ] & 0xff; + + if ( len >= (padding + TRANSOP_AES_NONCE_SIZE)) + { + /* strictly speaking for this to be an ethernet packet + * it is going to need to be even bigger; but this is + * enough to prevent segfaults. */ + traceEvent( TRACE_DEBUG, "padding = %u", padding ); + len -= padding; + + len -= TRANSOP_AES_NONCE_SIZE; /* size of ethernet packet */ + + /* Step over 4-byte random nonce value */ + memcpy( outbuf, + assembly + TRANSOP_AES_NONCE_SIZE, + len ); + } + else + { + traceEvent( TRACE_WARNING, "UDP payload decryption failed." ); + } + } + else + { + traceEvent( TRACE_WARNING, "Encrypted length %d is not a multiple of AES_BLOCK_SIZE (%d)", len, AES_BLOCK_SIZE ); + len = 0; + } + + } + else + { + /* Wrong security association; drop the packet as it is undecodable. */ + traceEvent( TRACE_ERROR, "decode_aes SA number %lu not found.", sa_rx ); + + /* REVISIT: should be able to load a new SA at this point to complete the decoding. */ + } + } + else + { + /* Wrong security association; drop the packet as it is undecodable. */ + traceEvent( TRACE_ERROR, "decode_aes unsupported aes version %u.", aes_enc_ver ); + + /* REVISIT: should be able to load a new SA at this point to complete the decoding. */ + } + } + else + { + traceEvent( TRACE_ERROR, "decode_aes inbuf wrong size (%ul) to decrypt.", in_len ); + } + + return len; +} + +struct sha512_keybuf { + uint8_t enc_dec_key[AES256_KEY_BYTES]; /* The key to use for AES CBC encryption/decryption */ + uint8_t iv_enc_key[AES128_KEY_BYTES]; /* The key to use to encrypt the IV with AES ECB */ + uint8_t iv_ext_val[AES128_KEY_BYTES]; /* A value to extend the IV seed */ +}; /* size: SHA512_DIGEST_LENGTH */ + +/* NOTE: the caller should adjust priv->num_sa accordingly */ +static int setup_aes_key(transop_aes_t *priv, const uint8_t *key, ssize_t key_size, size_t sa_num) { + sa_aes_t * sa = &(priv->sa[sa_num]); + size_t aes_keysize_bytes; + size_t aes_keysize_bits; + struct sha512_keybuf keybuf; + + /* Clear out any old possibly longer key matter. */ + memset( &(sa->enc_key), 0, sizeof(sa->enc_key) ); + memset( &(sa->dec_key), 0, sizeof(sa->dec_key) ); + memset( &(sa->iv_enc_key), 0, sizeof(sa->iv_enc_key) ); + memset( &(sa->iv_ext_val), 0, sizeof(sa->iv_ext_val) ); + + /* We still use aes_best_keysize (even not necessary since we hash the key + * into the 256bits enc_dec_key) to let the users choose the degree of encryption. + * Long keys will pick AES192 or AES256 with more robust but expensive encryption. + */ + aes_keysize_bytes = aes_best_keysize(key_size); + aes_keysize_bits = 8 * aes_keysize_bytes; + + /* Hash the main key to generate subkeys */ + SHA512(key, key_size, (u_char*)&keybuf); + + /* setup of enc_key/dec_key, used for the CBC encryption */ + AES_set_encrypt_key(keybuf.enc_dec_key, aes_keysize_bits, &(sa->enc_key)); + AES_set_decrypt_key(keybuf.enc_dec_key, aes_keysize_bits, &(sa->dec_key)); + + /* setup of iv_enc_key and iv_ext_val, used for generating the CBC IV */ + AES_set_encrypt_key(keybuf.iv_enc_key, sizeof(keybuf.iv_enc_key) * 8, &(sa->iv_enc_key)); + memcpy(sa->iv_ext_val, keybuf.iv_ext_val, sizeof(keybuf.iv_ext_val)); + + traceEvent( TRACE_DEBUG, "transop_addspec_aes sa_id=%u, %u bits key=%s.\n", + priv->sa[sa_num].sa_id, aes_keysize_bits, key); + + return(0); +} + +/* + * priv: pointer to transform state + * keybuf: buffer holding the key + * pstat: length of keybuf + */ +static void add_aes_key(transop_aes_t *priv, uint8_t *keybuf, ssize_t pstat) { + setup_aes_key(priv, keybuf, pstat, priv->num_sa); + ++(priv->num_sa); +} + +static int transop_addspec_aes( n2n_trans_op_t * arg, const n2n_cipherspec_t * cspec ) +{ + int retval = 1; + ssize_t pstat=-1; + transop_aes_t * priv = (transop_aes_t *)arg->priv; + uint8_t keybuf[N2N_MAX_KEYSIZE]; + + if ( priv->num_sa < N2N_AES_NUM_SA ) + { + const char * op = (const char *)cspec->opaque; + const char * sep = index( op, '_' ); + + if ( sep ) + { + char tmp[256]; + size_t s; + + s = sep - op; + memcpy( tmp, cspec->opaque, s ); + tmp[s]=0; + + s = strlen(sep+1); /* sep is the _ which might be immediately followed by NULL */ + + priv->sa[priv->num_sa].spec = *cspec; + priv->sa[priv->num_sa].sa_id = strtoul(tmp, NULL, 10); + + memset( keybuf, 0, N2N_MAX_KEYSIZE ); + pstat = n2n_parse_hex( keybuf, N2N_MAX_KEYSIZE, sep+1, s ); + if ( pstat > 0 ) + { + add_aes_key(priv, keybuf, pstat); + retval = 0; + } + } + else + { + traceEvent( TRACE_ERROR, "transop_addspec_aes : bad key data - missing '_'.\n"); + } + } + else + { + traceEvent( TRACE_ERROR, "transop_addspec_aes : full.\n"); + } + + return retval; +} + + +static n2n_tostat_t transop_tick_aes( n2n_trans_op_t * arg, time_t now ) +{ + transop_aes_t * priv = (transop_aes_t *)arg->priv; + size_t i; + int found=0; + n2n_tostat_t r; + + memset( &r, 0, sizeof(r) ); + + traceEvent( TRACE_DEBUG, "transop_aes tick num_sa=%u now=%lu", priv->num_sa, now ); + + for ( i=0; i < priv->num_sa; ++i ) + { + if ( 0 == validCipherSpec( &(priv->sa[i].spec), now ) ) + { + time_t remaining = priv->sa[i].spec.valid_until - now; + + traceEvent( TRACE_INFO, "transop_aes choosing tx_sa=%u (valid for %lu sec)", priv->sa[i].sa_id, remaining ); + priv->tx_sa=i; + found=1; + break; + } + else + { + traceEvent( TRACE_DEBUG, "transop_aes tick rejecting sa=%u %lu -> %lu", + priv->sa[i].sa_id, priv->sa[i].spec.valid_from, priv->sa[i].spec.valid_until ); + } + } + + if ( 0==found) + { + traceEvent( TRACE_INFO, "transop_aes no keys are currently valid. Keeping tx_sa=%u", priv->tx_sa ); + } + else + { + r.can_tx = 1; + r.tx_spec.t = N2N_TRANSFORM_ID_AESCBC; + r.tx_spec = priv->sa[priv->tx_sa].spec; + } + + return r; +} + +static n2n_tostat_t transop_tick_aes_psk(n2n_trans_op_t * arg, time_t now) { + transop_aes_t * priv = (transop_aes_t *)arg->priv; + n2n_tostat_t r; + + memset(&r, 0, sizeof(r)); + + // Always tx + r.can_tx = 1; + r.tx_spec.t = N2N_TRANSFORM_ID_AESCBC; + r.tx_spec = priv->sa[priv->tx_sa].spec; + + return r; +} + +int transop_aes_init( n2n_trans_op_t * ttt ) +{ + int retval = 1; + transop_aes_t * priv = NULL; + + if ( ttt->priv ) + { + transop_deinit_aes( ttt ); + } + + memset( ttt, 0, sizeof( n2n_trans_op_t ) ); + + priv = (transop_aes_t *) calloc(1, sizeof(transop_aes_t)); + + if ( NULL != priv ) + { + size_t i; + sa_aes_t * sa=NULL; + + /* install the private structure. */ + ttt->priv = priv; + priv->num_sa=0; + priv->tx_sa=0; /* We will use this sa index for encoding. */ + priv->psk_mode = 0; + + ttt->transform_id = N2N_TRANSFORM_ID_AESCBC; + ttt->addspec = transop_addspec_aes; + ttt->tick = transop_tick_aes; /* chooses a new tx_sa */ + ttt->deinit = transop_deinit_aes; + ttt->fwd = transop_encode_aes; + ttt->rev = transop_decode_aes; + + for(i=0; isa[i]); + sa->sa_id=0; + memset( &(sa->spec), 0, sizeof(n2n_cipherspec_t) ); + memset( &(sa->enc_key), 0, sizeof(sa->enc_key) ); + memset( &(sa->dec_key), 0, sizeof(sa->dec_key) ); + memset( &(sa->iv_enc_key), 0, sizeof(sa->iv_enc_key) ); + memset( &(sa->iv_ext_val), 0, sizeof(sa->iv_ext_val) ); + } + + retval = 0; + } + else + { + memset( ttt, 0, sizeof(n2n_trans_op_t) ); + traceEvent( TRACE_ERROR, "Failed to allocate priv for aes" ); + } + + return retval; +} + +/* Setup AES in pre-shared key mode */ +int transop_aes_setup_psk(n2n_trans_op_t *ttt, + n2n_sa_t sa_num, + uint8_t *encrypt_pwd, + uint32_t encrypt_pwd_len) { + int retval = 1; + transop_aes_t *priv = (transop_aes_t *)ttt->priv; + + if(ttt->priv) { + /* Replace the tick function with the PSK version of it */ + ttt->tick = transop_tick_aes_psk; + priv->psk_mode = 1; + priv->num_sa=0; + priv->tx_sa=0; + + /* Setup the key to use for encryption/decryption */ + add_aes_key(priv, encrypt_pwd, encrypt_pwd_len); + + retval = 0; + } else + traceEvent(TRACE_ERROR, "AES priv is not allocated"); + + return retval; +} + +#else /* #if defined(N2N_HAVE_AES) */ + +struct transop_aes +{ + ssize_t tx_sa; +}; + +typedef struct transop_aes transop_aes_t; + + +static int transop_deinit_aes( n2n_trans_op_t * arg ) +{ + transop_aes_t * priv = (transop_aes_t *)arg->priv; + + if ( priv ) + { + free(priv); + } + + arg->priv=NULL; /* return to fully uninitialised state */ + + return 0; +} + +static int transop_encode_aes( n2n_trans_op_t * arg, + uint8_t * outbuf, + size_t out_len, + const uint8_t * inbuf, + size_t in_len ) +{ + return -1; +} + +static int transop_decode_aes( n2n_trans_op_t * arg, + uint8_t * outbuf, + size_t out_len, + const uint8_t * inbuf, + size_t in_len ) +{ + return -1; +} + +static int transop_addspec_aes( n2n_trans_op_t * arg, const n2n_cipherspec_t * cspec ) +{ + traceEvent( TRACE_DEBUG, "transop_addspec_aes AES not built into edge.\n"); + + return -1; +} + +static n2n_tostat_t transop_tick_aes( n2n_trans_op_t * arg, time_t now ) +{ + n2n_tostat_t r; + + memset( &r, 0, sizeof(r) ); + + return r; +} + +int transop_aes_init( n2n_trans_op_t * ttt ) +{ + int retval = 1; + transop_aes_t * priv = NULL; + + if ( ttt->priv ) + { + transop_deinit_aes( ttt ); + } + + memset( ttt, 0, sizeof( n2n_trans_op_t ) ); + + priv = (transop_aes_t *) malloc( sizeof(transop_aes_t) ); + + if ( NULL != priv ) + { + /* install the private structure. */ + ttt->priv = priv; + priv->tx_sa=0; /* We will use this sa index for encoding. */ + + ttt->transform_id = N2N_TRANSFORM_ID_AESCBC; + ttt->addspec = transop_addspec_aes; + ttt->tick = transop_tick_aes; /* chooses a new tx_sa */ + ttt->deinit = transop_deinit_aes; + ttt->fwd = transop_encode_aes; + ttt->rev = transop_decode_aes; + + retval = 0; + } + else + { + memset( ttt, 0, sizeof(n2n_trans_op_t) ); + traceEvent( TRACE_ERROR, "Failed to allocate priv for aes" ); + } + + return retval; +} + + +int transop_aes_setup_psk(n2n_trans_op_t *ttt, + n2n_sa_t sa_num, + uint8_t *encrypt_pwd, + uint32_t encrypt_pwd_len) { + return 0; +} + +#endif /* #if defined(N2N_HAVE_AES) */ + diff --git a/legacy/transform_tf.c b/legacy/transform_tf.c new file mode 100644 index 0000000..6b4d606 --- /dev/null +++ b/legacy/transform_tf.c @@ -0,0 +1,467 @@ +/** + * (C) 2007-18 - ntop.org and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not see see + * + */ + +#include "n2n.h" +#include "n2n_transforms.h" +#include "twofish.h" +#ifndef _MSC_VER +/* Not included in Visual Studio 2008 */ +#include /* index() */ +#endif + +#define N2N_TWOFISH_NUM_SA 32 /* space for SAa */ + +#define N2N_TWOFISH_TRANSFORM_VERSION 1 /* version of the transform encoding */ + +struct sa_twofish +{ + n2n_cipherspec_t spec; /* cipher spec parameters */ + n2n_sa_t sa_id; /* security association index */ + TWOFISH * enc_tf; /* tx state */ + TWOFISH * dec_tf; /* rx state */ +}; + +typedef struct sa_twofish sa_twofish_t; + + +/** Twofish transform state data. + * + * With a key-schedule in place this will be populated with a number of + * SAs. Each SA has a lifetime and some opque data. The opaque data for twofish + * consists of the SA number and key material. + * + */ +struct transop_tf +{ + ssize_t tx_sa; + size_t num_sa; + sa_twofish_t sa[N2N_TWOFISH_NUM_SA]; +}; + +typedef struct transop_tf transop_tf_t; + +static int transop_deinit_twofish( n2n_trans_op_t * arg ) +{ + transop_tf_t * priv = (transop_tf_t *)arg->priv; + size_t i; + + if ( priv ) + { + /* Memory was previously allocated */ + for (i=0; isa[i]); + + TwoFishDestroy(sa->enc_tf); /* deallocate TWOFISH */ + sa->enc_tf=NULL; + + TwoFishDestroy(sa->dec_tf); /* deallocate TWOFISH */ + sa->dec_tf=NULL; + + sa->sa_id=0; + } + + priv->num_sa=0; + priv->tx_sa=-1; + + free(priv); + } + + arg->priv=NULL; /* return to fully uninitialised state */ + + return 0; +} + +static size_t tf_choose_tx_sa( transop_tf_t * priv ) +{ + return priv->tx_sa; /* set in tick */ +} + +#define TRANSOP_TF_VER_SIZE 1 /* Support minor variants in encoding in one module. */ +#define TRANSOP_TF_NONCE_SIZE 4 +#define TRANSOP_TF_SA_SIZE 4 + +/** The twofish packet format consists of: + * + * - a 8-bit twofish encoding version in clear text + * - a 32-bit SA number in clear text + * - ciphertext encrypted from a 32-bit nonce followed by the payload. + * + * [V|SSSS|nnnnDDDDDDDDDDDDDDDDDDDDD] + * |<------ encrypted ------>| + */ +static int transop_encode_twofish( n2n_trans_op_t * arg, + uint8_t * outbuf, + size_t out_len, + const uint8_t * inbuf, + size_t in_len, + const uint8_t * peer_mac) +{ + int len=-1; + transop_tf_t * priv = (transop_tf_t *)arg->priv; + uint8_t assembly[N2N_PKT_BUF_SIZE]; + uint32_t * pnonce; + + if ( (in_len + TRANSOP_TF_NONCE_SIZE) <= N2N_PKT_BUF_SIZE ) + { + if ( (in_len + TRANSOP_TF_NONCE_SIZE + TRANSOP_TF_SA_SIZE + TRANSOP_TF_VER_SIZE) <= out_len ) + { + size_t idx=0; + sa_twofish_t * sa; + size_t tx_sa_num = 0; + + /* The transmit sa is periodically updated */ + tx_sa_num = tf_choose_tx_sa( priv ); + + sa = &(priv->sa[tx_sa_num]); /* Proper Tx SA index */ + + traceEvent( TRACE_DEBUG, "encode_twofish %lu with SA %lu.", in_len, sa->sa_id ); + + /* Encode the twofish format version. */ + encode_uint8( outbuf, &idx, N2N_TWOFISH_TRANSFORM_VERSION ); + + /* Encode the security association (SA) number */ + encode_uint32( outbuf, &idx, sa->sa_id ); + + /* The assembly buffer is a source for encrypting data. The nonce is + * written in first followed by the packet payload. The whole + * contents of assembly are encrypted. */ + pnonce = (uint32_t *)assembly; + *pnonce = rand(); + memcpy( assembly + TRANSOP_TF_NONCE_SIZE, inbuf, in_len ); + + /* Encrypt the assembly contents and write the ciphertext after the SA. */ + len = TwoFishEncryptRaw( assembly, /* source */ + outbuf + TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE, + in_len + TRANSOP_TF_NONCE_SIZE, /* enc size */ + sa->enc_tf); + if ( len > 0 ) + { + len += TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE; /* size of data carried in UDP. */ + } + else + { + traceEvent( TRACE_ERROR, "encode_twofish encryption failed." ); + } + + } + else + { + traceEvent( TRACE_ERROR, "encode_twofish outbuf too small." ); + } + } + else + { + traceEvent( TRACE_ERROR, "encode_twofish inbuf too big to encrypt." ); + } + + return len; +} + + +/* Search through the array of SAs to find the one with the required ID. + * + * @return array index where found or -1 if not found + */ +static ssize_t twofish_find_sa( const transop_tf_t * priv, const n2n_sa_t req_id ) +{ + size_t i; + + for (i=0; i < priv->num_sa; ++i) + { + const sa_twofish_t * sa=NULL; + + sa = &(priv->sa[i]); + if (req_id == sa->sa_id) + { + return i; + } + } + + return -1; +} + + +/** The twofish packet format consists of: + * + * - a 8-bit twofish encoding version in clear text + * - a 32-bit SA number in clear text + * - ciphertext encrypted from a 32-bit nonce followed by the payload. + * + * [V|SSSS|nnnnDDDDDDDDDDDDDDDDDDDDD] + * |<------ encrypted ------>| + */ +static int transop_decode_twofish( n2n_trans_op_t * arg, + uint8_t * outbuf, + size_t out_len, + const uint8_t * inbuf, + size_t in_len, + const uint8_t * peer_mac) +{ + int len=0; + transop_tf_t * priv = (transop_tf_t *)arg->priv; + uint8_t assembly[N2N_PKT_BUF_SIZE]; + + if ( ( (in_len - (TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE)) <= N2N_PKT_BUF_SIZE ) /* Cipher text fits in assembly */ + && (in_len >= (TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE + TRANSOP_TF_NONCE_SIZE) ) /* Has at least version, SA and nonce */ + ) + { + n2n_sa_t sa_rx; + ssize_t sa_idx=-1; + size_t rem=in_len; + size_t idx=0; + uint8_t tf_enc_ver=0; + + /* Get the encoding version to make sure it is supported */ + decode_uint8( &tf_enc_ver, inbuf, &rem, &idx ); + + if ( N2N_TWOFISH_TRANSFORM_VERSION == tf_enc_ver ) + { + /* Get the SA number and make sure we are decrypting with the right one. */ + decode_uint32( &sa_rx, inbuf, &rem, &idx ); + + sa_idx = twofish_find_sa(priv, sa_rx); + if ( sa_idx >= 0 ) + { + sa_twofish_t * sa = &(priv->sa[sa_idx]); + + traceEvent( TRACE_DEBUG, "decode_twofish %lu with SA %lu.", in_len, sa_rx, sa->sa_id ); + + len = TwoFishDecryptRaw( (void *)(inbuf + TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE), + assembly, /* destination */ + (in_len - (TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE)), + sa->dec_tf); + + if ( len > 0 ) + { + /* Step over 4-byte random nonce value */ + len -= TRANSOP_TF_NONCE_SIZE; /* size of ethernet packet */ + + memcpy( outbuf, + assembly + TRANSOP_TF_NONCE_SIZE, + len ); + } + else + { + traceEvent( TRACE_ERROR, "decode_twofish decryption failed." ); + } + + } + else + { + /* Wrong security association; drop the packet as it is undecodable. */ + traceEvent( TRACE_ERROR, "decode_twofish SA number %lu not found.", sa_rx ); + + /* REVISIT: should be able to load a new SA at this point to complete the decoding. */ + } + } + else + { + /* Wrong security association; drop the packet as it is undecodable. */ + traceEvent( TRACE_ERROR, "decode_twofish unsupported twofish version %u.", tf_enc_ver ); + + /* REVISIT: should be able to load a new SA at this point to complete the decoding. */ + } + } + else + { + traceEvent( TRACE_ERROR, "decode_twofish inbuf wrong size (%ul) to decrypt.", in_len ); + } + + return len; +} + +static int transop_addspec_twofish( n2n_trans_op_t * arg, const n2n_cipherspec_t * cspec ) +{ + int retval = 1; + ssize_t pstat=-1; + transop_tf_t * priv = (transop_tf_t *)arg->priv; + uint8_t keybuf[N2N_MAX_KEYSIZE]; + + if ( priv->num_sa < N2N_TWOFISH_NUM_SA ) + { + const char * op = (const char *)cspec->opaque; +#ifdef __ANDROID_NDK__ + const char *sep = strchr(op, '_'); +#else + const char * sep = index( op, '_' ); +#endif // __ANDROID_NDK__ + + if ( sep ) + { + char tmp[256]; + size_t s; + + s = sep - op; + memcpy( tmp, cspec->opaque, s ); + tmp[s]=0; + + s = strlen(sep+1); /* sep is the _ which might be immediately followed by NULL */ + + priv->sa[priv->num_sa].spec = *cspec; + priv->sa[priv->num_sa].sa_id = strtoul(tmp, NULL, 10); + + pstat = n2n_parse_hex( keybuf, N2N_MAX_KEYSIZE, sep+1, s ); + if ( pstat > 0 ) + { + priv->sa[priv->num_sa].enc_tf = TwoFishInit( keybuf, pstat); + priv->sa[priv->num_sa].dec_tf = TwoFishInit( keybuf, pstat); + + traceEvent( TRACE_DEBUG, "transop_addspec_twofish sa_id=%u data=%s.\n", + priv->sa[priv->num_sa].sa_id, sep+1); + + ++(priv->num_sa); + retval = 0; + } + } + else + { + traceEvent( TRACE_ERROR, "transop_addspec_twofish : bad key data - missing '_'.\n"); + } + } + else + { + traceEvent( TRACE_ERROR, "transop_addspec_twofish : full.\n"); + } + + return retval; +} + + +static n2n_tostat_t transop_tick_twofish( n2n_trans_op_t * arg, time_t now ) +{ + transop_tf_t * priv = (transop_tf_t *)arg->priv; + size_t i; + int found=0; + n2n_tostat_t r; + + memset( &r, 0, sizeof(r) ); + + traceEvent( TRACE_DEBUG, "transop_tf tick num_sa=%u", priv->num_sa ); + + for ( i=0; i < priv->num_sa; ++i ) + { + if ( 0 == validCipherSpec( &(priv->sa[i].spec), now ) ) + { + time_t remaining = priv->sa[i].spec.valid_until - now; + + traceEvent( TRACE_INFO, "transop_tf choosing tx_sa=%u (valid for %lu sec)", priv->sa[i].sa_id, remaining ); + priv->tx_sa=i; + found=1; + break; + } + else + { + traceEvent( TRACE_DEBUG, "transop_tf tick rejecting sa=%u %lu -> %lu", + priv->sa[i].sa_id, priv->sa[i].spec.valid_from, priv->sa[i].spec.valid_until ); + } + } + + if ( 0==found) + { + traceEvent( TRACE_INFO, "transop_tf no keys are currently valid. Keeping tx_sa=%u", priv->tx_sa ); + } + else + { + r.can_tx = 1; + r.tx_spec.t = N2N_TRANSFORM_ID_TWOFISH; + r.tx_spec = priv->sa[priv->tx_sa].spec; + } + + return r; +} + +int transop_twofish_setup_psk( n2n_trans_op_t * ttt, + n2n_sa_t sa_num, + uint8_t * encrypt_pwd, + uint32_t encrypt_pwd_len ) +{ + int retval = 1; + transop_tf_t * priv = (transop_tf_t *)ttt->priv; + + if(priv) { + sa_twofish_t *sa; + + priv->num_sa=1; /* There is one SA in the array. */ + priv->tx_sa=0; + sa = &(priv->sa[priv->tx_sa]); + sa->sa_id=sa_num; + sa->spec.valid_until = 0x7fffffff; + + /* This is a preshared key setup. Both Tx and Rx are using the same security association. */ + + sa->enc_tf = TwoFishInit(encrypt_pwd, encrypt_pwd_len); + sa->dec_tf = TwoFishInit(encrypt_pwd, encrypt_pwd_len); + + if ( (sa->enc_tf) && (sa->dec_tf) ) + retval = 0; + else + traceEvent( TRACE_ERROR, "transop_twofish_setup_psk" ); + } else + traceEvent( TRACE_ERROR, "twofish priv is not allocated" ); + + return retval; +} + +int transop_twofish_init( n2n_trans_op_t * ttt ) +{ + int retval = 1; + transop_tf_t * priv = NULL; + + if ( ttt->priv ) + { + transop_deinit_twofish( ttt ); + } + + memset( ttt, 0, sizeof( n2n_trans_op_t ) ); + + priv = (transop_tf_t *) malloc( sizeof(transop_tf_t) ); + + if ( NULL != priv ) { + size_t i; + sa_twofish_t * sa=NULL; + + /* install the private structure. */ + ttt->priv = priv; + priv->num_sa=0; + priv->tx_sa=0; /* We will use this sa index for encoding. */ + + ttt->transform_id = N2N_TRANSFORM_ID_TWOFISH; + ttt->addspec = transop_addspec_twofish; + ttt->tick = transop_tick_twofish; /* chooses a new tx_sa */ + ttt->deinit = transop_deinit_twofish; + ttt->fwd = transop_encode_twofish; + ttt->rev = transop_decode_twofish; + + for(i=0; isa[i]); + sa->sa_id=0; + memset( &(sa->spec), 0, sizeof(n2n_cipherspec_t) ); + sa->enc_tf=NULL; + sa->dec_tf=NULL; + } + + retval = 0; + } else { + memset( ttt, 0, sizeof(n2n_trans_op_t) ); + traceEvent( TRACE_ERROR, "Failed to allocate priv for twofish" ); + } + + return retval; +} diff --git a/n2n.c b/n2n.c index 88f6f97..7fb6da7 100644 --- a/n2n.c +++ b/n2n.c @@ -87,8 +87,8 @@ void traceEvent(int eventTraceLevel, char* file, int line, char * format, ...) { va_list va_ap; if(eventTraceLevel <= traceLevel) { - char buf[2048]; - char out_buf[640]; + char buf[1024]; + char out_buf[1280]; char theDate[N2N_TRACE_DATESIZE]; char *extra_msg = ""; time_t theTime = time(NULL); diff --git a/n2n.h b/n2n.h index e7877e3..16c7f89 100644 --- a/n2n.h +++ b/n2n.h @@ -174,64 +174,30 @@ struct peer_info { time_t last_seen; }; -struct n2n_edge; /* defined in edge.c */ -typedef struct n2n_edge n2n_edge_t; - #define N2N_EDGE_SN_HOST_SIZE 48 #define N2N_EDGE_NUM_SUPERNODES 2 #define N2N_EDGE_SUP_ATTEMPTS 3 /* Number of failed attmpts before moving on to next supernode. */ #define N2N_PATHNAME_MAXLEN 256 -#define N2N_MAX_TRANSFORMS 16 #define N2N_EDGE_MGMT_PORT 5644 typedef char n2n_sn_name_t[N2N_EDGE_SN_HOST_SIZE]; -struct n2n_edge { - int daemon; /**< Non-zero if edge should detach and run in the background. */ - int preferred_aes; /**< Non-zero if AES is the preferred encryption meothd. */ - uint8_t re_resolve_supernode_ip; - - n2n_sock_t supernode; - - size_t sn_idx; /**< Currently active supernode. */ - size_t sn_num; /**< Number of supernode addresses defined. */ +typedef struct n2n_edge_conf { n2n_sn_name_t sn_ip_array[N2N_EDGE_NUM_SUPERNODES]; - int sn_wait; /**< Whether we are waiting for a supernode response. */ - n2n_community_t community_name; /**< The community. 16 full octets. */ - char keyschedule[N2N_PATHNAME_MAXLEN]; - int null_transop; /**< Only allowed if no key sources defined. */ + n2n_transform_t transop_id; /**< The transop to use. */ + uint8_t re_resolve_supernode_ip; + uint8_t dyn_ip_mode; /**< Interface IP address is dynamically allocated, eg. DHCP. */ + uint8_t allow_routing; /**< Accept packet no to interface address. */ + uint8_t drop_multicast; /**< Multicast ethernet addresses. */ + uint8_t sn_num; /**< Number of supernode addresses defined. */ + char *encrypt_key; + int local_port; + int mgmt_port; +} n2n_edge_conf_t; - int udp_sock; - int udp_mgmt_sock; /**< socket for status info. */ - int udp_multicast_sock; /**< socket for local multicast registrations. */ - - tuntap_dev device; /**< All about the TUNTAP device */ - int dyn_ip_mode; /**< Interface IP address is dynamically allocated, eg. DHCP. */ - int allow_routing; /**< Accept packet no to interface address. */ - int drop_multicast; /**< Multicast ethernet addresses. */ - - n2n_trans_op_t transop[N2N_MAX_TRANSFORMS]; /* one for each transform at fixed positions */ - size_t tx_transop_idx; /**< The transop to use when encoding. */ - n2n_sock_t multicast_peer; /**< Multicast peer group (for local edges) */ - struct peer_info * known_peers; /**< Edges we are connected to. */ - struct peer_info * pending_peers; /**< Edges we have tried to register with. */ - time_t last_register_req; /**< Check if time to re-register with super*/ - size_t register_lifetime; /**< Time distance after last_register_req at which to re-register. */ - time_t last_p2p; /**< Last time p2p traffic was received. */ - time_t last_sup; /**< Last time a packet arrived from supernode. */ - size_t sup_attempts; /**< Number of remaining attempts to this supernode. */ - n2n_cookie_t last_cookie; /**< Cookie sent in last REGISTER_SUPER. */ - - time_t start_time; /**< For calculating uptime */ - - /* Statistics */ - size_t tx_p2p; - size_t rx_p2p; - size_t tx_sup; - size_t rx_sup; -}; +typedef struct n2n_edge n2n_edge_t; /* Opaque, see edge_utils.c */ /* ************************************** */ @@ -263,6 +229,13 @@ struct n2n_edge { /* ************************************** */ +/* Transop Init Functions */ +int n2n_transop_null_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt); +int n2n_transop_twofish_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt); +#ifdef N2N_HAVE_AES +int n2n_transop_aes_cbc_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt); +#endif + /* Log */ void setTraceLevel(int level); void setUseSyslog(int use_syslog); @@ -285,9 +258,7 @@ uint8_t is_multi_broadcast(const uint8_t * dest_mac); char* msg_type2str(uint16_t msg_type); void hexdump(const uint8_t * buf, size_t len); void print_n2n_version(); -void supernode2addr(n2n_sock_t * sn, const n2n_sn_name_t addrIn); int is_empty_ip_address(const n2n_sock_t * sock); -const char *random_device_mac(void); /* Sockets */ char* sock_to_cstr( n2n_sock_str_t out, @@ -312,21 +283,21 @@ void update_peer_address(n2n_edge_t * eee, const n2n_sock_t * peer, time_t when); +/* Edge conf */ +void edge_init_conf_defaults(n2n_edge_conf_t *conf); +int edge_verify_conf(const n2n_edge_conf_t *conf); +int edge_conf_add_supernode(n2n_edge_conf_t *conf, const char *ip_and_port); +const n2n_edge_conf_t* edge_get_conf(const n2n_edge_t *eee); + /* Public functions */ -int edge_init_keyschedule(n2n_edge_t * eee); -void send_packet2net(n2n_edge_t * eee, - uint8_t *tap_pkt, size_t len); -int edge_init_encryption(n2n_edge_t * eee, uint8_t *encrypt_pwd, uint32_t encrypt_pwd_len); -int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port); - -int edge_init(n2n_edge_t * eee); -void edge_term(n2n_edge_t * eee); -int run_edge_loop(n2n_edge_t * eee, int *keep_running); - +n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *rv); +void edge_term(n2n_edge_t *eee); +int run_edge_loop(n2n_edge_t *eee, int *keep_running); int quick_edge_init(char *device_name, char *community_name, char *encrypt_key, char *device_mac, char *local_ip_address, char *supernode_ip_address_port, int *keep_on_running); - + +void send_packet2net(n2n_edge_t * eee, uint8_t *tap_pkt, size_t len); #endif /* _N2N_H_ */ diff --git a/n2n_transforms.h b/n2n_transforms.h index d7cb48b..25f05b2 100644 --- a/n2n_transforms.h +++ b/n2n_transforms.h @@ -19,39 +19,23 @@ #if !defined(N2N_TRANSFORMS_H_) #define N2N_TRANSFORMS_H_ -#include "n2n_keyfile.h" #include "n2n_wire.h" - -#define N2N_TRANSFORM_ID_INVAL 0 /* marks uninitialised data */ -#define N2N_TRANSFORM_ID_NULL 1 -#define N2N_TRANSFORM_ID_TWOFISH 2 -#define N2N_TRANSFORM_ID_AESCBC 3 -#define N2N_TRANSFORM_ID_LZO 4 -#define N2N_TRANSFORM_ID_TWOFISH_LZO 5 -#define N2N_TRANSFORM_ID_AESCBC_LZO 6 #define N2N_TRANSFORM_ID_USER_START 64 #define N2N_TRANSFORM_ID_MAX 65535 +typedef enum n2n_transform { + N2N_TRANSFORM_ID_INVAL = 0, + N2N_TRANSFORM_ID_NULL = 1, + N2N_TRANSFORM_ID_TWOFISH = 2, + N2N_TRANSFORM_ID_AESCBC = 3, +} n2n_transform_t; struct n2n_trans_op; -typedef struct n2n_trans_op n2n_trans_op_t; -struct n2n_tostat { - uint8_t can_tx; /* Does this transop have a valid SA for encoding. */ - n2n_cipherspec_t tx_spec; /* If can_tx, the spec used to encode. */ -}; - -typedef struct n2n_tostat n2n_tostat_t; - - -typedef int (*n2n_transdeinit_f)( n2n_trans_op_t * arg ); -typedef int (*n2n_transaddspec_f)( n2n_trans_op_t * arg, - const n2n_cipherspec_t * cspec ); -typedef n2n_tostat_t (*n2n_transtick_f)( n2n_trans_op_t * arg, - time_t now ); - -typedef int (*n2n_transform_f)( n2n_trans_op_t * arg, +typedef int (*n2n_transdeinit_f)( struct n2n_trans_op * arg ); +typedef void (*n2n_transtick_f)( struct n2n_trans_op * arg, time_t now ); +typedef int (*n2n_transform_f)( struct n2n_trans_op * arg, uint8_t * outbuf, size_t out_len, const uint8_t * inbuf, @@ -64,35 +48,18 @@ typedef int (*n2n_transform_f)( n2n_trans_op_t * arg, * to use to decode the packet content. The transform code then decodes the * packet and consults its internal key lookup. */ -struct n2n_trans_op { +typedef struct n2n_trans_op { void * priv; /* opaque data. Key schedule goes here. */ - - n2n_transform_t transform_id; /* link header enum to a transform */ + uint8_t no_encryption; /* 1 if this transop does not perform encryption */ + n2n_transform_t transform_id; size_t tx_cnt; size_t rx_cnt; n2n_transdeinit_f deinit; /* destructor function */ - n2n_transaddspec_f addspec; /* parse opaque data from a key schedule file. */ - n2n_transtick_f tick; /* periodic maintenance */ + n2n_transtick_f tick; /* periodic maintenance */ n2n_transform_f fwd; /* encode a payload */ n2n_transform_f rev; /* decode a payload */ -}; - -/* Setup a single twofish SA for single-key operation. */ -int transop_twofish_setup_psk( n2n_trans_op_t * ttt, - n2n_sa_t sa_num, - uint8_t * encrypt_pwd, - uint32_t encrypt_pwd_len ); -/* Setup a single AES SA for single-key operation. */ -int transop_aes_setup_psk( n2n_trans_op_t * ttt, - n2n_sa_t sa_num, - uint8_t * encrypt_pwd, - uint32_t encrypt_pwd_len ); - -/* Initialise an empty transop ready to receive cipherspec elements. */ -int transop_twofish_init( n2n_trans_op_t * ttt ); -int transop_aes_init( n2n_trans_op_t * ttt ); -void transop_null_init( n2n_trans_op_t * ttt ); +} n2n_trans_op_t; #endif /* #if !defined(N2N_TRANSFORMS_H_) */ diff --git a/n2n_wire.h b/n2n_wire.h index 306e09e..0ca6fca 100644 --- a/n2n_wire.h +++ b/n2n_wire.h @@ -51,7 +51,7 @@ typedef uint8_t n2n_cookie_t[N2N_COOKIE_SIZE]; typedef char n2n_sock_str_t[N2N_SOCKBUF_SIZE]; /* tracing string buffer */ -enum n2n_pc +typedef enum n2n_pc { n2n_ping=0, /* Not used */ n2n_register=1, /* Register edge to edge */ @@ -62,9 +62,7 @@ enum n2n_pc n2n_register_super_ack=6, /* ACK from supernode to edge */ n2n_register_super_nak=7, /* NAK from supernode to edge - registration refused */ n2n_federation=8 /* Not used by edge */ -}; - -typedef enum n2n_pc n2n_pc_t; +} n2n_pc_t; #define N2N_FLAGS_OPTIONS 0x0080 #define N2N_FLAGS_SOCKET 0x0040 @@ -86,11 +84,6 @@ typedef enum n2n_pc n2n_pc_t; #define N2N_EINVAL -3 #define N2N_ENOSPACE -4 - -typedef uint16_t n2n_flags_t; -typedef uint16_t n2n_transform_t; /* Encryption, compression type. */ -typedef uint32_t n2n_sa_t; /* security association number */ - struct n2n_sock { uint8_t family; /* AF_INET or AF_INET6; or 0 if invalid */ @@ -118,8 +111,8 @@ struct n2n_common { /* int version; */ uint8_t ttl; - n2n_pc_t pc; - n2n_flags_t flags; + uint8_t pc; + uint16_t flags; n2n_community_t community; }; @@ -150,7 +143,7 @@ struct n2n_PACKET n2n_mac_t srcMac; n2n_mac_t dstMac; n2n_sock_t sock; - n2n_transform_t transform; + uint16_t transform; }; typedef struct n2n_PACKET n2n_PACKET_t; diff --git a/transform_aes.c b/transform_aes.c index 7b198ff..cce51ff 100644 --- a/transform_aes.c +++ b/transform_aes.c @@ -19,16 +19,10 @@ #include "n2n.h" #include "n2n_transforms.h" -#if defined(N2N_HAVE_AES) +#ifdef N2N_HAVE_AES #include "openssl/aes.h" #include "openssl/sha.h" -#ifndef _MSC_VER -/* Not included in Visual Studio 2008 */ -#include /* index() */ -#endif - -#define N2N_AES_NUM_SA 32 /* space for SAa */ #define N2N_AES_TRANSFORM_VERSION 1 /* version of the transform encoding */ #define N2N_AES_IVEC_SIZE 32 /* Enough space for biggest AES ivec */ @@ -37,79 +31,6 @@ #define AES192_KEY_BYTES (192/8) #define AES128_KEY_BYTES (128/8) -typedef unsigned char n2n_aes_ivec_t[N2N_AES_IVEC_SIZE]; - -struct sa_aes -{ - n2n_cipherspec_t spec; /* cipher spec parameters */ - n2n_sa_t sa_id; /* security association index */ - AES_KEY enc_key; /* tx key */ - AES_KEY dec_key; /* tx key */ - AES_KEY iv_enc_key; /* key used to encrypt the IV */ - uint8_t iv_ext_val[AES128_KEY_BYTES]; /* key used to extend the random IV seed to full block size */ -}; - -typedef struct sa_aes sa_aes_t; - - -/** Aes transform state data. - * - * With a key-schedule in place this will be populated with a number of - * SAs. Each SA has a lifetime and some opque data. The opaque data for aes - * consists of the SA number and key material. - * - */ -struct transop_aes -{ - ssize_t tx_sa; - size_t num_sa; - sa_aes_t sa[N2N_AES_NUM_SA]; - u_int8_t psk_mode; -}; - -typedef struct transop_aes transop_aes_t; - -static ssize_t aes_find_sa( const transop_aes_t * priv, const n2n_sa_t req_id ); -static int setup_aes_key(transop_aes_t *priv, const uint8_t *key, ssize_t key_size, size_t sa_num); - -static int transop_deinit_aes( n2n_trans_op_t * arg ) -{ - transop_aes_t * priv = (transop_aes_t *)arg->priv; - size_t i; - - if ( priv ) - { - /* Memory was previously allocated */ - for (i=0; isa[i]); - - sa->sa_id=0; - } - - priv->num_sa=0; - priv->tx_sa=-1; - - free(priv); - } - - arg->priv=NULL; /* return to fully uninitialised state */ - - return 0; -} - -static size_t aes_choose_tx_sa( transop_aes_t * priv, const u_int8_t * peer_mac ) { - return priv->tx_sa; /* set in tick */ -} - -static ssize_t aes_choose_rx_sa( transop_aes_t * priv, const u_int8_t * peer_mac, ssize_t sa_rx) { - if(!priv->psk_mode) - return aes_find_sa(priv, sa_rx); - else - /* NOTE the sa_rx of the packet is ignored in this case */ - return 0; -} - /* AES plaintext preamble */ #define TRANSOP_AES_VER_SIZE 1 /* Support minor variants in encoding in one module. */ #define TRANSOP_AES_SA_SIZE 4 @@ -119,6 +40,30 @@ static ssize_t aes_choose_rx_sa( transop_aes_t * priv, const u_int8_t * peer_mac /* AES ciphertext preamble */ #define TRANSOP_AES_NONCE_SIZE 4 +typedef unsigned char n2n_aes_ivec_t[N2N_AES_IVEC_SIZE]; + +typedef struct transop_aes { + AES_KEY enc_key; /* tx key */ + AES_KEY dec_key; /* tx key */ + AES_KEY iv_enc_key; /* key used to encrypt the IV */ + uint8_t iv_ext_val[AES128_KEY_BYTES]; /* key used to extend the random IV seed to full block size */ +} transop_aes_t; + +struct sha512_keybuf { + uint8_t enc_dec_key[AES256_KEY_BYTES]; /* The key to use for AES CBC encryption/decryption */ + uint8_t iv_enc_key[AES128_KEY_BYTES]; /* The key to use to encrypt the IV with AES ECB */ + uint8_t iv_ext_val[AES128_KEY_BYTES]; /* A value to extend the IV seed */ +}; /* size: SHA512_DIGEST_LENGTH */ + +static int transop_deinit_aes(n2n_trans_op_t *arg) { + transop_aes_t *priv = (transop_aes_t *)arg->priv; + + if(priv) + free(priv); + + return 0; +} + /* Return the best acceptable AES key size (in bytes) given an input keysize. * * The value returned will be one of AES128_KEY_BYTES, AES192_KEY_BYTES or @@ -140,11 +85,11 @@ static size_t aes_best_keysize(size_t numBytes) } } -static void set_aes_cbc_iv(sa_aes_t *sa, n2n_aes_ivec_t ivec, uint64_t iv_seed) { +static void set_aes_cbc_iv(transop_aes_t *priv, n2n_aes_ivec_t ivec, uint64_t iv_seed) { uint8_t iv_full[AES_BLOCK_SIZE]; /* Extend the seed to full block size via the fixed ext value */ - memcpy(iv_full, sa->iv_ext_val, sizeof(iv_seed)); // note: only 64bits used of 128 available + memcpy(iv_full, priv->iv_ext_val, sizeof(iv_seed)); // note: only 64bits used of 128 available memcpy(iv_full + sizeof(iv_seed), &iv_seed, sizeof(iv_seed)); /* Encrypt the IV with secret key to make it unpredictable. @@ -153,7 +98,7 @@ static void set_aes_cbc_iv(sa_aes_t *sa, n2n_aes_ivec_t ivec, uint64_t iv_seed) * can be easily reconstructed from plaintext headers and used by an attacker * to perform differential analysis. */ - AES_ecb_encrypt(iv_full, ivec, &sa->iv_enc_key, AES_ENCRYPT); + AES_ecb_encrypt(iv_full, ivec, &priv->iv_enc_key, AES_ENCRYPT); } /** The aes packet format consists of: @@ -178,30 +123,22 @@ static int transop_encode_aes( n2n_trans_op_t * arg, uint8_t assembly[N2N_PKT_BUF_SIZE] = {0}; uint32_t * pnonce; - if ( (in_len + TRANSOP_AES_NONCE_SIZE) <= N2N_PKT_BUF_SIZE ) - { - if ( (in_len + TRANSOP_AES_NONCE_SIZE + TRANSOP_AES_PREAMBLE_SIZE) <= out_len ) - { + if ( (in_len + TRANSOP_AES_NONCE_SIZE) <= N2N_PKT_BUF_SIZE ) { + if ( (in_len + TRANSOP_AES_NONCE_SIZE + TRANSOP_AES_PREAMBLE_SIZE) <= out_len ) { int len=-1; size_t idx=0; - sa_aes_t * sa; - size_t tx_sa_num = 0; + size_t tx_sa_num = 0; // Not used uint64_t iv_seed = 0; uint8_t padding = 0; n2n_aes_ivec_t enc_ivec = {0}; - /* The transmit sa is periodically updated */ - tx_sa_num = aes_choose_tx_sa( priv, peer_mac ); + traceEvent( TRACE_DEBUG, "encode_aes %lu", in_len); - sa = &(priv->sa[tx_sa_num]); /* Proper Tx SA index */ - - traceEvent( TRACE_DEBUG, "encode_aes %lu with SA %lu.", in_len, sa->sa_id ); - /* Encode the aes format version. */ encode_uint8( outbuf, &idx, N2N_AES_TRANSFORM_VERSION ); /* Encode the security association (SA) number */ - encode_uint32( outbuf, &idx, sa->sa_id ); + encode_uint32( outbuf, &idx, tx_sa_num ); // Not used /* Generate and encode the IV seed. * Using two calls to rand() because RAND_MAX is usually < 64bit @@ -227,60 +164,29 @@ static int transop_encode_aes( n2n_trans_op_t * arg, assembly[len2 - 1] = padding; traceEvent( TRACE_DEBUG, "padding = %u, seed = %016lx", padding, iv_seed ); - set_aes_cbc_iv(sa, enc_ivec, iv_seed); + set_aes_cbc_iv(priv, enc_ivec, iv_seed); AES_cbc_encrypt( assembly, /* source */ outbuf + TRANSOP_AES_PREAMBLE_SIZE, /* dest */ len2, /* enc size */ - &(sa->enc_key), enc_ivec, AES_ENCRYPT ); + &(priv->enc_key), enc_ivec, AES_ENCRYPT ); len2 += TRANSOP_AES_PREAMBLE_SIZE; /* size of data carried in UDP. */ - } - else - { + } else traceEvent( TRACE_ERROR, "encode_aes outbuf too small." ); - } - } - else - { + } else traceEvent( TRACE_ERROR, "encode_aes inbuf too big to encrypt." ); - } return len2; } - -/* Search through the array of SAs to find the one with the required ID. - * - * @return array index where found or -1 if not found - */ -static ssize_t aes_find_sa( const transop_aes_t * priv, const n2n_sa_t req_id ) -{ - size_t i; - - for (i=0; i < priv->num_sa; ++i) - { - const sa_aes_t * sa=NULL; - - sa = &(priv->sa[i]); - if (req_id == sa->sa_id) - { - return i; - } - } - - return -1; -} - - /* See transop_encode_aes for packet format */ static int transop_decode_aes( n2n_trans_op_t * arg, uint8_t * outbuf, size_t out_len, const uint8_t * inbuf, size_t in_len, - const uint8_t * peer_mac) -{ + const uint8_t * peer_mac) { int len=0; transop_aes_t * priv = (transop_aes_t *)arg->priv; uint8_t assembly[N2N_PKT_BUF_SIZE]; @@ -289,8 +195,7 @@ static int transop_decode_aes( n2n_trans_op_t * arg, && (in_len >= (TRANSOP_AES_PREAMBLE_SIZE + TRANSOP_AES_NONCE_SIZE) ) /* Has at least version, SA, iv seed and nonce */ ) { - n2n_sa_t sa_rx; - ssize_t sa_idx=-1; + uint32_t sa_rx=0; // Not used size_t rem=in_len; size_t idx=0; uint8_t aes_enc_ver=0; @@ -299,110 +204,71 @@ static int transop_decode_aes( n2n_trans_op_t * arg, /* Get the encoding version to make sure it is supported */ decode_uint8( &aes_enc_ver, inbuf, &rem, &idx ); - if ( N2N_AES_TRANSFORM_VERSION == aes_enc_ver ) - { - /* Get the SA number and make sure we are decrypting with the right one. */ + if ( N2N_AES_TRANSFORM_VERSION == aes_enc_ver ) { + /* Get the SA number and make sure we are decrypting with the right one. - Not used*/ decode_uint32( &sa_rx, inbuf, &rem, &idx ); - sa_idx = aes_choose_rx_sa(priv, peer_mac, sa_rx); + /* Get the IV seed */ + decode_buf((uint8_t *)&iv_seed, sizeof(iv_seed), inbuf, &rem, &idx); - if ( sa_idx >= 0 ) - { - sa_aes_t * sa = &(priv->sa[sa_idx]); + traceEvent( TRACE_DEBUG, "decode_aes %lu with seed %016lx", in_len, iv_seed ); - /* Get the IV seed */ - decode_buf((uint8_t *)&iv_seed, sizeof(iv_seed), inbuf, &rem, &idx); + len = (in_len - TRANSOP_AES_PREAMBLE_SIZE); + + if ( 0 == (len % AES_BLOCK_SIZE ) ) { + uint8_t padding; + n2n_aes_ivec_t dec_ivec = {0}; - traceEvent( TRACE_DEBUG, "decode_aes %lu with SA %lu and seed %016lx", in_len, sa->sa_id, iv_seed ); + set_aes_cbc_iv(priv, dec_ivec, iv_seed); - len = (in_len - TRANSOP_AES_PREAMBLE_SIZE); - - if ( 0 == (len % AES_BLOCK_SIZE ) ) + AES_cbc_encrypt( (inbuf + TRANSOP_AES_PREAMBLE_SIZE), + assembly, /* destination */ + len, + &(priv->dec_key), + dec_ivec, AES_DECRYPT ); + + /* last byte is how much was padding: max value should be + * AES_BLOCKSIZE-1 */ + padding = assembly[ len-1 ] & 0xff; + + if ( len >= (padding + TRANSOP_AES_NONCE_SIZE)) { - uint8_t padding; - n2n_aes_ivec_t dec_ivec = {0}; + /* strictly speaking for this to be an ethernet packet + * it is going to need to be even bigger; but this is + * enough to prevent segfaults. */ + traceEvent( TRACE_DEBUG, "padding = %u", padding ); + len -= padding; - set_aes_cbc_iv(sa, dec_ivec, iv_seed); - - AES_cbc_encrypt( (inbuf + TRANSOP_AES_PREAMBLE_SIZE), - assembly, /* destination */ - len, - &(sa->dec_key), - dec_ivec, AES_DECRYPT ); - - /* last byte is how much was padding: max value should be - * AES_BLOCKSIZE-1 */ - padding = assembly[ len-1 ] & 0xff; - - if ( len >= (padding + TRANSOP_AES_NONCE_SIZE)) - { - /* strictly speaking for this to be an ethernet packet - * it is going to need to be even bigger; but this is - * enough to prevent segfaults. */ - traceEvent( TRACE_DEBUG, "padding = %u", padding ); - len -= padding; - - len -= TRANSOP_AES_NONCE_SIZE; /* size of ethernet packet */ - - /* Step over 4-byte random nonce value */ - memcpy( outbuf, - assembly + TRANSOP_AES_NONCE_SIZE, - len ); - } - else - { - traceEvent( TRACE_WARNING, "UDP payload decryption failed." ); - } - } - else - { - traceEvent( TRACE_WARNING, "Encrypted length %d is not a multiple of AES_BLOCK_SIZE (%d)", len, AES_BLOCK_SIZE ); - len = 0; - } + len -= TRANSOP_AES_NONCE_SIZE; /* size of ethernet packet */ + /* Step over 4-byte random nonce value */ + memcpy( outbuf, + assembly + TRANSOP_AES_NONCE_SIZE, + len ); + } else + traceEvent( TRACE_WARNING, "UDP payload decryption failed." ); + } else { + traceEvent( TRACE_WARNING, "Encrypted length %d is not a multiple of AES_BLOCK_SIZE (%d)", len, AES_BLOCK_SIZE ); + len = 0; } - else - { - /* Wrong security association; drop the packet as it is undecodable. */ - traceEvent( TRACE_ERROR, "decode_aes SA number %lu not found.", sa_rx ); - - /* REVISIT: should be able to load a new SA at this point to complete the decoding. */ - } - } - else - { - /* Wrong security association; drop the packet as it is undecodable. */ + } else traceEvent( TRACE_ERROR, "decode_aes unsupported aes version %u.", aes_enc_ver ); - - /* REVISIT: should be able to load a new SA at this point to complete the decoding. */ - } - } - else - { + } else traceEvent( TRACE_ERROR, "decode_aes inbuf wrong size (%ul) to decrypt.", in_len ); - } return len; } -struct sha512_keybuf { - uint8_t enc_dec_key[AES256_KEY_BYTES]; /* The key to use for AES CBC encryption/decryption */ - uint8_t iv_enc_key[AES128_KEY_BYTES]; /* The key to use to encrypt the IV with AES ECB */ - uint8_t iv_ext_val[AES128_KEY_BYTES]; /* A value to extend the IV seed */ -}; /* size: SHA512_DIGEST_LENGTH */ - -/* NOTE: the caller should adjust priv->num_sa accordingly */ -static int setup_aes_key(transop_aes_t *priv, const uint8_t *key, ssize_t key_size, size_t sa_num) { - sa_aes_t * sa = &(priv->sa[sa_num]); +static int setup_aes_key(transop_aes_t *priv, const uint8_t *key, ssize_t key_size) { size_t aes_keysize_bytes; size_t aes_keysize_bits; struct sha512_keybuf keybuf; /* Clear out any old possibly longer key matter. */ - memset( &(sa->enc_key), 0, sizeof(sa->enc_key) ); - memset( &(sa->dec_key), 0, sizeof(sa->dec_key) ); - memset( &(sa->iv_enc_key), 0, sizeof(sa->iv_enc_key) ); - memset( &(sa->iv_ext_val), 0, sizeof(sa->iv_ext_val) ); + memset( &(priv->enc_key), 0, sizeof(priv->enc_key) ); + memset( &(priv->dec_key), 0, sizeof(priv->dec_key) ); + memset( &(priv->iv_enc_key), 0, sizeof(priv->iv_enc_key) ); + memset( &(priv->iv_ext_val), 0, sizeof(priv->iv_ext_val) ); /* We still use aes_best_keysize (even not necessary since we hash the key * into the 256bits enc_dec_key) to let the users choose the degree of encryption. @@ -415,316 +281,44 @@ static int setup_aes_key(transop_aes_t *priv, const uint8_t *key, ssize_t key_si SHA512(key, key_size, (u_char*)&keybuf); /* setup of enc_key/dec_key, used for the CBC encryption */ - AES_set_encrypt_key(keybuf.enc_dec_key, aes_keysize_bits, &(sa->enc_key)); - AES_set_decrypt_key(keybuf.enc_dec_key, aes_keysize_bits, &(sa->dec_key)); + AES_set_encrypt_key(keybuf.enc_dec_key, aes_keysize_bits, &(priv->enc_key)); + AES_set_decrypt_key(keybuf.enc_dec_key, aes_keysize_bits, &(priv->dec_key)); /* setup of iv_enc_key and iv_ext_val, used for generating the CBC IV */ - AES_set_encrypt_key(keybuf.iv_enc_key, sizeof(keybuf.iv_enc_key) * 8, &(sa->iv_enc_key)); - memcpy(sa->iv_ext_val, keybuf.iv_ext_val, sizeof(keybuf.iv_ext_val)); + AES_set_encrypt_key(keybuf.iv_enc_key, sizeof(keybuf.iv_enc_key) * 8, &(priv->iv_enc_key)); + memcpy(priv->iv_ext_val, keybuf.iv_ext_val, sizeof(keybuf.iv_ext_val)); - traceEvent( TRACE_DEBUG, "transop_addspec_aes sa_id=%u, %u bits key=%s.\n", - priv->sa[sa_num].sa_id, aes_keysize_bits, key); + traceEvent( TRACE_DEBUG, "AES %u bits setup completed\n", + aes_keysize_bits, key); return(0); } -/* - * priv: pointer to transform state - * keybuf: buffer holding the key - * pstat: length of keybuf - */ -static void add_aes_key(transop_aes_t *priv, uint8_t *keybuf, ssize_t pstat) { - setup_aes_key(priv, keybuf, pstat, priv->num_sa); - ++(priv->num_sa); +static void transop_tick_aes(n2n_trans_op_t * arg, time_t now) {} + +/* AES initialization function */ +int n2n_transop_aes_cbc_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt) { + transop_aes_t *priv; + const u_char *encrypt_key = (const u_char *)conf->encrypt_key; + size_t encrypt_key_len = strlen(conf->encrypt_key); + + memset(ttt, 0, sizeof(*ttt)); + ttt->transform_id = N2N_TRANSFORM_ID_AESCBC; + + ttt->tick = transop_tick_aes; + ttt->deinit = transop_deinit_aes; + ttt->fwd = transop_encode_aes; + ttt->rev = transop_decode_aes; + + priv = (transop_aes_t*) calloc(1, sizeof(transop_aes_t)); + if(!priv) { + traceEvent(TRACE_ERROR, "cannot allocate transop_aes_t memory"); + return(-1); + } + ttt->priv = priv; + + /* Setup the key */ + return(setup_aes_key(priv, encrypt_key, encrypt_key_len)); } -static int transop_addspec_aes( n2n_trans_op_t * arg, const n2n_cipherspec_t * cspec ) -{ - int retval = 1; - ssize_t pstat=-1; - transop_aes_t * priv = (transop_aes_t *)arg->priv; - uint8_t keybuf[N2N_MAX_KEYSIZE]; - - if ( priv->num_sa < N2N_AES_NUM_SA ) - { - const char * op = (const char *)cspec->opaque; - const char * sep = index( op, '_' ); - - if ( sep ) - { - char tmp[256]; - size_t s; - - s = sep - op; - memcpy( tmp, cspec->opaque, s ); - tmp[s]=0; - - s = strlen(sep+1); /* sep is the _ which might be immediately followed by NULL */ - - priv->sa[priv->num_sa].spec = *cspec; - priv->sa[priv->num_sa].sa_id = strtoul(tmp, NULL, 10); - - memset( keybuf, 0, N2N_MAX_KEYSIZE ); - pstat = n2n_parse_hex( keybuf, N2N_MAX_KEYSIZE, sep+1, s ); - if ( pstat > 0 ) - { - add_aes_key(priv, keybuf, pstat); - retval = 0; - } - } - else - { - traceEvent( TRACE_ERROR, "transop_addspec_aes : bad key data - missing '_'.\n"); - } - } - else - { - traceEvent( TRACE_ERROR, "transop_addspec_aes : full.\n"); - } - - return retval; -} - - -static n2n_tostat_t transop_tick_aes( n2n_trans_op_t * arg, time_t now ) -{ - transop_aes_t * priv = (transop_aes_t *)arg->priv; - size_t i; - int found=0; - n2n_tostat_t r; - - memset( &r, 0, sizeof(r) ); - - traceEvent( TRACE_DEBUG, "transop_aes tick num_sa=%u now=%lu", priv->num_sa, now ); - - for ( i=0; i < priv->num_sa; ++i ) - { - if ( 0 == validCipherSpec( &(priv->sa[i].spec), now ) ) - { - time_t remaining = priv->sa[i].spec.valid_until - now; - - traceEvent( TRACE_INFO, "transop_aes choosing tx_sa=%u (valid for %lu sec)", priv->sa[i].sa_id, remaining ); - priv->tx_sa=i; - found=1; - break; - } - else - { - traceEvent( TRACE_DEBUG, "transop_aes tick rejecting sa=%u %lu -> %lu", - priv->sa[i].sa_id, priv->sa[i].spec.valid_from, priv->sa[i].spec.valid_until ); - } - } - - if ( 0==found) - { - traceEvent( TRACE_INFO, "transop_aes no keys are currently valid. Keeping tx_sa=%u", priv->tx_sa ); - } - else - { - r.can_tx = 1; - r.tx_spec.t = N2N_TRANSFORM_ID_AESCBC; - r.tx_spec = priv->sa[priv->tx_sa].spec; - } - - return r; -} - -static n2n_tostat_t transop_tick_aes_psk(n2n_trans_op_t * arg, time_t now) { - transop_aes_t * priv = (transop_aes_t *)arg->priv; - n2n_tostat_t r; - - memset(&r, 0, sizeof(r)); - - // Always tx - r.can_tx = 1; - r.tx_spec.t = N2N_TRANSFORM_ID_AESCBC; - r.tx_spec = priv->sa[priv->tx_sa].spec; - - return r; -} - -int transop_aes_init( n2n_trans_op_t * ttt ) -{ - int retval = 1; - transop_aes_t * priv = NULL; - - if ( ttt->priv ) - { - transop_deinit_aes( ttt ); - } - - memset( ttt, 0, sizeof( n2n_trans_op_t ) ); - - priv = (transop_aes_t *) calloc(1, sizeof(transop_aes_t)); - - if ( NULL != priv ) - { - size_t i; - sa_aes_t * sa=NULL; - - /* install the private structure. */ - ttt->priv = priv; - priv->num_sa=0; - priv->tx_sa=0; /* We will use this sa index for encoding. */ - priv->psk_mode = 0; - - ttt->transform_id = N2N_TRANSFORM_ID_AESCBC; - ttt->addspec = transop_addspec_aes; - ttt->tick = transop_tick_aes; /* chooses a new tx_sa */ - ttt->deinit = transop_deinit_aes; - ttt->fwd = transop_encode_aes; - ttt->rev = transop_decode_aes; - - for(i=0; isa[i]); - sa->sa_id=0; - memset( &(sa->spec), 0, sizeof(n2n_cipherspec_t) ); - memset( &(sa->enc_key), 0, sizeof(sa->enc_key) ); - memset( &(sa->dec_key), 0, sizeof(sa->dec_key) ); - memset( &(sa->iv_enc_key), 0, sizeof(sa->iv_enc_key) ); - memset( &(sa->iv_ext_val), 0, sizeof(sa->iv_ext_val) ); - } - - retval = 0; - } - else - { - memset( ttt, 0, sizeof(n2n_trans_op_t) ); - traceEvent( TRACE_ERROR, "Failed to allocate priv for aes" ); - } - - return retval; -} - -/* Setup AES in pre-shared key mode */ -int transop_aes_setup_psk(n2n_trans_op_t *ttt, - n2n_sa_t sa_num, - uint8_t *encrypt_pwd, - uint32_t encrypt_pwd_len) { - int retval = 1; - transop_aes_t *priv = (transop_aes_t *)ttt->priv; - - if(ttt->priv) { - /* Replace the tick function with the PSK version of it */ - ttt->tick = transop_tick_aes_psk; - priv->psk_mode = 1; - priv->num_sa=0; - priv->tx_sa=0; - - /* Setup the key to use for encryption/decryption */ - add_aes_key(priv, encrypt_pwd, encrypt_pwd_len); - - retval = 0; - } else - traceEvent(TRACE_ERROR, "AES priv is not allocated"); - - return retval; -} - -#else /* #if defined(N2N_HAVE_AES) */ - -struct transop_aes -{ - ssize_t tx_sa; -}; - -typedef struct transop_aes transop_aes_t; - - -static int transop_deinit_aes( n2n_trans_op_t * arg ) -{ - transop_aes_t * priv = (transop_aes_t *)arg->priv; - - if ( priv ) - { - free(priv); - } - - arg->priv=NULL; /* return to fully uninitialised state */ - - return 0; -} - -static int transop_encode_aes( n2n_trans_op_t * arg, - uint8_t * outbuf, - size_t out_len, - const uint8_t * inbuf, - size_t in_len ) -{ - return -1; -} - -static int transop_decode_aes( n2n_trans_op_t * arg, - uint8_t * outbuf, - size_t out_len, - const uint8_t * inbuf, - size_t in_len ) -{ - return -1; -} - -static int transop_addspec_aes( n2n_trans_op_t * arg, const n2n_cipherspec_t * cspec ) -{ - traceEvent( TRACE_DEBUG, "transop_addspec_aes AES not built into edge.\n"); - - return -1; -} - -static n2n_tostat_t transop_tick_aes( n2n_trans_op_t * arg, time_t now ) -{ - n2n_tostat_t r; - - memset( &r, 0, sizeof(r) ); - - return r; -} - -int transop_aes_init( n2n_trans_op_t * ttt ) -{ - int retval = 1; - transop_aes_t * priv = NULL; - - if ( ttt->priv ) - { - transop_deinit_aes( ttt ); - } - - memset( ttt, 0, sizeof( n2n_trans_op_t ) ); - - priv = (transop_aes_t *) malloc( sizeof(transop_aes_t) ); - - if ( NULL != priv ) - { - /* install the private structure. */ - ttt->priv = priv; - priv->tx_sa=0; /* We will use this sa index for encoding. */ - - ttt->transform_id = N2N_TRANSFORM_ID_AESCBC; - ttt->addspec = transop_addspec_aes; - ttt->tick = transop_tick_aes; /* chooses a new tx_sa */ - ttt->deinit = transop_deinit_aes; - ttt->fwd = transop_encode_aes; - ttt->rev = transop_decode_aes; - - retval = 0; - } - else - { - memset( ttt, 0, sizeof(n2n_trans_op_t) ); - traceEvent( TRACE_ERROR, "Failed to allocate priv for aes" ); - } - - return retval; -} - - -int transop_aes_setup_psk(n2n_trans_op_t *ttt, - n2n_sa_t sa_num, - uint8_t *encrypt_pwd, - uint32_t encrypt_pwd_len) { - return 0; -} - -#endif /* #if defined(N2N_HAVE_AES) */ - +#endif /* N2N_HAVE_AES */ diff --git a/transform_null.c b/transform_null.c index 61fb6de..062ddf2 100644 --- a/transform_null.c +++ b/transform_null.c @@ -71,32 +71,17 @@ static int transop_decode_null( n2n_trans_op_t * arg, return retval; } -static int transop_addspec_null( n2n_trans_op_t * arg, const n2n_cipherspec_t * cspec ) -{ - return 0; -} +static void transop_tick_null(n2n_trans_op_t * arg, time_t now) {} -static n2n_tostat_t transop_tick_null( n2n_trans_op_t * arg, time_t now ) -{ - n2n_tostat_t r; - - r.can_tx=1; - r.tx_spec.t = N2N_TRANSFORM_ID_NULL; - r.tx_spec.valid_from = 0; - r.tx_spec.valid_until = (time_t)(-1); - r.tx_spec.opaque_size=0; - - return r; -} - -void transop_null_init( n2n_trans_op_t * ttt ) -{ +int n2n_transop_null_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt) { memset(ttt, 0, sizeof(n2n_trans_op_t) ); ttt->transform_id = N2N_TRANSFORM_ID_NULL; + ttt->no_encryption = 1; ttt->deinit = transop_deinit_null; - ttt->addspec = transop_addspec_null; ttt->tick = transop_tick_null; ttt->fwd = transop_encode_null; ttt->rev = transop_decode_null; + + return(0); } diff --git a/transform_tf.c b/transform_tf.c index 6b4d606..817b09f 100644 --- a/transform_tf.c +++ b/transform_tf.c @@ -28,70 +28,23 @@ #define N2N_TWOFISH_TRANSFORM_VERSION 1 /* version of the transform encoding */ -struct sa_twofish -{ - n2n_cipherspec_t spec; /* cipher spec parameters */ - n2n_sa_t sa_id; /* security association index */ - TWOFISH * enc_tf; /* tx state */ - TWOFISH * dec_tf; /* rx state */ -}; +typedef struct transop_tf { + TWOFISH* enc_tf; /* tx state */ + TWOFISH* dec_tf; /* rx state */ +} transop_tf_t; -typedef struct sa_twofish sa_twofish_t; +static int transop_deinit_twofish( n2n_trans_op_t * arg ) { + transop_tf_t *priv = (transop_tf_t *)arg->priv; - -/** Twofish transform state data. - * - * With a key-schedule in place this will be populated with a number of - * SAs. Each SA has a lifetime and some opque data. The opaque data for twofish - * consists of the SA number and key material. - * - */ -struct transop_tf -{ - ssize_t tx_sa; - size_t num_sa; - sa_twofish_t sa[N2N_TWOFISH_NUM_SA]; -}; - -typedef struct transop_tf transop_tf_t; - -static int transop_deinit_twofish( n2n_trans_op_t * arg ) -{ - transop_tf_t * priv = (transop_tf_t *)arg->priv; - size_t i; - - if ( priv ) - { - /* Memory was previously allocated */ - for (i=0; isa[i]); - - TwoFishDestroy(sa->enc_tf); /* deallocate TWOFISH */ - sa->enc_tf=NULL; - - TwoFishDestroy(sa->dec_tf); /* deallocate TWOFISH */ - sa->dec_tf=NULL; - - sa->sa_id=0; - } - - priv->num_sa=0; - priv->tx_sa=-1; - - free(priv); - } - - arg->priv=NULL; /* return to fully uninitialised state */ + if(priv) { + TwoFishDestroy(priv->enc_tf); /* deallocate TWOFISH */ + TwoFishDestroy(priv->dec_tf); /* deallocate TWOFISH */ + free(priv); + } return 0; } -static size_t tf_choose_tx_sa( transop_tf_t * priv ) -{ - return priv->tx_sa; /* set in tick */ -} - #define TRANSOP_TF_VER_SIZE 1 /* Support minor variants in encoding in one module. */ #define TRANSOP_TF_NONCE_SIZE 4 #define TRANSOP_TF_SA_SIZE 4 @@ -122,21 +75,15 @@ static int transop_encode_twofish( n2n_trans_op_t * arg, if ( (in_len + TRANSOP_TF_NONCE_SIZE + TRANSOP_TF_SA_SIZE + TRANSOP_TF_VER_SIZE) <= out_len ) { size_t idx=0; - sa_twofish_t * sa; - size_t tx_sa_num = 0; + uint32_t sa_id=0; // Not used - /* The transmit sa is periodically updated */ - tx_sa_num = tf_choose_tx_sa( priv ); - - sa = &(priv->sa[tx_sa_num]); /* Proper Tx SA index */ - - traceEvent( TRACE_DEBUG, "encode_twofish %lu with SA %lu.", in_len, sa->sa_id ); + traceEvent(TRACE_DEBUG, "encode_twofish %lu", in_len); /* Encode the twofish format version. */ encode_uint8( outbuf, &idx, N2N_TWOFISH_TRANSFORM_VERSION ); /* Encode the security association (SA) number */ - encode_uint32( outbuf, &idx, sa->sa_id ); + encode_uint32( outbuf, &idx, sa_id ); /* The assembly buffer is a source for encrypting data. The nonce is * written in first followed by the packet payload. The whole @@ -149,7 +96,7 @@ static int transop_encode_twofish( n2n_trans_op_t * arg, len = TwoFishEncryptRaw( assembly, /* source */ outbuf + TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE, in_len + TRANSOP_TF_NONCE_SIZE, /* enc size */ - sa->enc_tf); + priv->enc_tf); if ( len > 0 ) { len += TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE; /* size of data carried in UDP. */ @@ -173,30 +120,6 @@ static int transop_encode_twofish( n2n_trans_op_t * arg, return len; } - -/* Search through the array of SAs to find the one with the required ID. - * - * @return array index where found or -1 if not found - */ -static ssize_t twofish_find_sa( const transop_tf_t * priv, const n2n_sa_t req_id ) -{ - size_t i; - - for (i=0; i < priv->num_sa; ++i) - { - const sa_twofish_t * sa=NULL; - - sa = &(priv->sa[i]); - if (req_id == sa->sa_id) - { - return i; - } - } - - return -1; -} - - /** The twofish packet format consists of: * * - a 8-bit twofish encoding version in clear text @@ -219,249 +142,77 @@ static int transop_decode_twofish( n2n_trans_op_t * arg, if ( ( (in_len - (TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE)) <= N2N_PKT_BUF_SIZE ) /* Cipher text fits in assembly */ && (in_len >= (TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE + TRANSOP_TF_NONCE_SIZE) ) /* Has at least version, SA and nonce */ - ) - { - n2n_sa_t sa_rx; - ssize_t sa_idx=-1; + ) { size_t rem=in_len; size_t idx=0; uint8_t tf_enc_ver=0; + uint32_t sa_rx=0; // Not used /* Get the encoding version to make sure it is supported */ decode_uint8( &tf_enc_ver, inbuf, &rem, &idx ); - if ( N2N_TWOFISH_TRANSFORM_VERSION == tf_enc_ver ) - { + if ( N2N_TWOFISH_TRANSFORM_VERSION == tf_enc_ver ) { /* Get the SA number and make sure we are decrypting with the right one. */ decode_uint32( &sa_rx, inbuf, &rem, &idx ); - sa_idx = twofish_find_sa(priv, sa_rx); - if ( sa_idx >= 0 ) - { - sa_twofish_t * sa = &(priv->sa[sa_idx]); + traceEvent(TRACE_DEBUG, "decode_twofish %lu", in_len); - traceEvent( TRACE_DEBUG, "decode_twofish %lu with SA %lu.", in_len, sa_rx, sa->sa_id ); + len = TwoFishDecryptRaw( (void *)(inbuf + TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE), + assembly, /* destination */ + (in_len - (TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE)), + priv->dec_tf); - len = TwoFishDecryptRaw( (void *)(inbuf + TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE), - assembly, /* destination */ - (in_len - (TRANSOP_TF_VER_SIZE + TRANSOP_TF_SA_SIZE)), - sa->dec_tf); + if(len > 0) { + /* Step over 4-byte random nonce value */ + len -= TRANSOP_TF_NONCE_SIZE; /* size of ethernet packet */ - if ( len > 0 ) - { - /* Step over 4-byte random nonce value */ - len -= TRANSOP_TF_NONCE_SIZE; /* size of ethernet packet */ - - memcpy( outbuf, - assembly + TRANSOP_TF_NONCE_SIZE, - len ); - } - else - { - traceEvent( TRACE_ERROR, "decode_twofish decryption failed." ); - } - - } - else - { - /* Wrong security association; drop the packet as it is undecodable. */ - traceEvent( TRACE_ERROR, "decode_twofish SA number %lu not found.", sa_rx ); - - /* REVISIT: should be able to load a new SA at this point to complete the decoding. */ - } - } - else - { - /* Wrong security association; drop the packet as it is undecodable. */ - traceEvent( TRACE_ERROR, "decode_twofish unsupported twofish version %u.", tf_enc_ver ); - - /* REVISIT: should be able to load a new SA at this point to complete the decoding. */ - } - } - else - { - traceEvent( TRACE_ERROR, "decode_twofish inbuf wrong size (%ul) to decrypt.", in_len ); - } + memcpy( outbuf, + assembly + TRANSOP_TF_NONCE_SIZE, + len ); + } else + traceEvent(TRACE_ERROR, "decode_twofish decryption failed"); + } else + traceEvent( TRACE_ERROR, "decode_twofish unsupported twofish version %u.", tf_enc_ver ); + } else + traceEvent( TRACE_ERROR, "decode_twofish inbuf wrong size (%ul) to decrypt.", in_len ); return len; } -static int transop_addspec_twofish( n2n_trans_op_t * arg, const n2n_cipherspec_t * cspec ) -{ - int retval = 1; - ssize_t pstat=-1; - transop_tf_t * priv = (transop_tf_t *)arg->priv; - uint8_t keybuf[N2N_MAX_KEYSIZE]; +static void transop_tick_twofish( n2n_trans_op_t * arg, time_t now ) {} - if ( priv->num_sa < N2N_TWOFISH_NUM_SA ) - { - const char * op = (const char *)cspec->opaque; -#ifdef __ANDROID_NDK__ - const char *sep = strchr(op, '_'); -#else - const char * sep = index( op, '_' ); -#endif // __ANDROID_NDK__ +/* Twofish initialization function */ +int n2n_transop_twofish_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt) { + transop_tf_t *priv; + const u_char *encrypt_key = (const u_char *)conf->encrypt_key; + size_t encrypt_key_len = strlen(conf->encrypt_key); - if ( sep ) - { - char tmp[256]; - size_t s; - - s = sep - op; - memcpy( tmp, cspec->opaque, s ); - tmp[s]=0; - - s = strlen(sep+1); /* sep is the _ which might be immediately followed by NULL */ + memset(ttt, 0, sizeof(*ttt)); + ttt->transform_id = N2N_TRANSFORM_ID_TWOFISH; - priv->sa[priv->num_sa].spec = *cspec; - priv->sa[priv->num_sa].sa_id = strtoul(tmp, NULL, 10); + ttt->tick = transop_tick_twofish; + ttt->deinit = transop_deinit_twofish; + ttt->fwd = transop_encode_twofish; + ttt->rev = transop_decode_twofish; - pstat = n2n_parse_hex( keybuf, N2N_MAX_KEYSIZE, sep+1, s ); - if ( pstat > 0 ) - { - priv->sa[priv->num_sa].enc_tf = TwoFishInit( keybuf, pstat); - priv->sa[priv->num_sa].dec_tf = TwoFishInit( keybuf, pstat); - - traceEvent( TRACE_DEBUG, "transop_addspec_twofish sa_id=%u data=%s.\n", - priv->sa[priv->num_sa].sa_id, sep+1); - - ++(priv->num_sa); - retval = 0; - } - } - else - { - traceEvent( TRACE_ERROR, "transop_addspec_twofish : bad key data - missing '_'.\n"); - } - } - else - { - traceEvent( TRACE_ERROR, "transop_addspec_twofish : full.\n"); - } - - return retval; -} - - -static n2n_tostat_t transop_tick_twofish( n2n_trans_op_t * arg, time_t now ) -{ - transop_tf_t * priv = (transop_tf_t *)arg->priv; - size_t i; - int found=0; - n2n_tostat_t r; - - memset( &r, 0, sizeof(r) ); - - traceEvent( TRACE_DEBUG, "transop_tf tick num_sa=%u", priv->num_sa ); - - for ( i=0; i < priv->num_sa; ++i ) - { - if ( 0 == validCipherSpec( &(priv->sa[i].spec), now ) ) - { - time_t remaining = priv->sa[i].spec.valid_until - now; - - traceEvent( TRACE_INFO, "transop_tf choosing tx_sa=%u (valid for %lu sec)", priv->sa[i].sa_id, remaining ); - priv->tx_sa=i; - found=1; - break; - } - else - { - traceEvent( TRACE_DEBUG, "transop_tf tick rejecting sa=%u %lu -> %lu", - priv->sa[i].sa_id, priv->sa[i].spec.valid_from, priv->sa[i].spec.valid_until ); - } - } - - if ( 0==found) - { - traceEvent( TRACE_INFO, "transop_tf no keys are currently valid. Keeping tx_sa=%u", priv->tx_sa ); - } - else - { - r.can_tx = 1; - r.tx_spec.t = N2N_TRANSFORM_ID_TWOFISH; - r.tx_spec = priv->sa[priv->tx_sa].spec; - } - - return r; -} - -int transop_twofish_setup_psk( n2n_trans_op_t * ttt, - n2n_sa_t sa_num, - uint8_t * encrypt_pwd, - uint32_t encrypt_pwd_len ) -{ - int retval = 1; - transop_tf_t * priv = (transop_tf_t *)ttt->priv; - - if(priv) { - sa_twofish_t *sa; - - priv->num_sa=1; /* There is one SA in the array. */ - priv->tx_sa=0; - sa = &(priv->sa[priv->tx_sa]); - sa->sa_id=sa_num; - sa->spec.valid_until = 0x7fffffff; - - /* This is a preshared key setup. Both Tx and Rx are using the same security association. */ - - sa->enc_tf = TwoFishInit(encrypt_pwd, encrypt_pwd_len); - sa->dec_tf = TwoFishInit(encrypt_pwd, encrypt_pwd_len); - - if ( (sa->enc_tf) && (sa->dec_tf) ) - retval = 0; - else - traceEvent( TRACE_ERROR, "transop_twofish_setup_psk" ); - } else - traceEvent( TRACE_ERROR, "twofish priv is not allocated" ); - - return retval; -} - -int transop_twofish_init( n2n_trans_op_t * ttt ) -{ - int retval = 1; - transop_tf_t * priv = NULL; - - if ( ttt->priv ) - { - transop_deinit_twofish( ttt ); - } - - memset( ttt, 0, sizeof( n2n_trans_op_t ) ); - - priv = (transop_tf_t *) malloc( sizeof(transop_tf_t) ); - - if ( NULL != priv ) { - size_t i; - sa_twofish_t * sa=NULL; - - /* install the private structure. */ - ttt->priv = priv; - priv->num_sa=0; - priv->tx_sa=0; /* We will use this sa index for encoding. */ - - ttt->transform_id = N2N_TRANSFORM_ID_TWOFISH; - ttt->addspec = transop_addspec_twofish; - ttt->tick = transop_tick_twofish; /* chooses a new tx_sa */ - ttt->deinit = transop_deinit_twofish; - ttt->fwd = transop_encode_twofish; - ttt->rev = transop_decode_twofish; - - for(i=0; isa[i]); - sa->sa_id=0; - memset( &(sa->spec), 0, sizeof(n2n_cipherspec_t) ); - sa->enc_tf=NULL; - sa->dec_tf=NULL; - } - - retval = 0; - } else { - memset( ttt, 0, sizeof(n2n_trans_op_t) ); - traceEvent( TRACE_ERROR, "Failed to allocate priv for twofish" ); - } - - return retval; + priv = (transop_tf_t*) calloc(1, sizeof(transop_tf_t)); + if(!priv) { + traceEvent(TRACE_ERROR, "cannot allocate transop_tf_t memory"); + return(-1); + } + ttt->priv = priv; + + /* This is a preshared key setup. Both Tx and Rx are using the same security association. */ + priv->enc_tf = TwoFishInit(encrypt_key, encrypt_key_len); + priv->dec_tf = TwoFishInit(encrypt_key, encrypt_key_len); + + if((!priv->enc_tf) || (!priv->dec_tf)) { + if(priv->enc_tf) TwoFishDestroy(priv->enc_tf); + if(priv->dec_tf) TwoFishDestroy(priv->dec_tf); + free(priv); + traceEvent(TRACE_ERROR, "TwoFishInit failed"); + return(-2); + } + + return(0); }