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/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 58968c6..2cda0ba 100644 --- a/edge.c +++ b/edge.c @@ -142,7 +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 | 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"); @@ -160,14 +160,14 @@ 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); } @@ -536,20 +536,6 @@ static void daemonize() { /* *************************************************** */ -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")); -} - -/* *************************************************** */ - /** Entry point to program from kernel. */ int main(int argc, char* argv[]) { int keep_on_running = 1; @@ -564,6 +550,7 @@ int main(int argc, char* argv[]) { /* Defaults */ edge_init_conf_defaults(&conf); + memset(&ec, 0, sizeof(ec)); ec.mtu = DEFAULT_MTU; ec.daemon = 1; /* By default run in daemon mode. */ #ifndef WIN32 @@ -579,6 +566,8 @@ int main(int argc, char* argv[]) { 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], &conf, &ec); @@ -591,8 +580,6 @@ int main(int argc, char* argv[]) { if(rc < 0) help(); - traceEvent(TRACE_NORMAL, "Starting n2n edge %s %s", PACKAGE_VERSION, PACKAGE_BUILDDATE); - if(0 == strcmp("dhcp", ec.ip_mode)) { traceEvent(TRACE_NORMAL, "Dynamic IP address assignment enabled."); @@ -611,6 +598,12 @@ int main(int argc, char* argv[]) { )) help(); +#ifndef WIN32 + /* If running suid root then we need to setuid before using the force. */ + setuid(0); + /* setgid(0); */ +#endif + if(tuntap_open(&tuntap, ec.tuntap_dev_name, ec.ip_mode, ec.ip_addr, ec.netmask, ec.device_mac, ec.mtu) < 0) return(-1); @@ -619,12 +612,6 @@ int main(int argc, char* argv[]) { exit(1); } -#ifndef WIN32 - /* If running suid root then we need to setuid before using the force. */ - setuid(0); - /* setgid(0); */ -#endif - #ifndef WIN32 if(ec.daemon) { setUseSyslog(1); /* traceEvent output now goes to syslog. */ @@ -644,11 +631,14 @@ int main(int argc, char* argv[]) { #endif traceEvent(TRACE_NORMAL, "edge started"); - rc = 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 d6e030f..229b0ae 100644 --- a/edge_utils.c +++ b/edge_utils.c @@ -59,6 +59,7 @@ static void check_peer(n2n_edge_t * eee, 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); /* ************************************** */ @@ -117,7 +118,7 @@ struct n2n_edge { * This also initialises the NULL transform operation opstruct. */ n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *rv) { - n2n_transform_id_t transop_id = conf->transop_id; + n2n_transform_t transop_id = conf->transop_id; n2n_edge_t *eee = calloc(1, sizeof(n2n_edge_t)); int rc = -1; @@ -166,15 +167,15 @@ n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *r switch(transop_id) { case N2N_TRANSFORM_ID_TWOFISH: - rc = n2n_transop_twofish_init(eee, &eee->transop); + rc = n2n_transop_twofish_init(&eee->conf, &eee->transop); break; #ifdef N2N_HAVE_AES case N2N_TRANSFORM_ID_AESCBC: - rc = n2n_transop_aes_init(eee, &eee->transop); + rc = n2n_transop_aes_cbc_init(&eee->conf, &eee->transop); break; #endif default: - rc = n2n_transop_null_init(eee, &eee->transop); + rc = n2n_transop_null_init(&eee->conf, &eee->transop); } if((rc < 0) || (eee->transop.fwd == NULL) || (eee->transop.transform_id != transop_id)) { @@ -208,7 +209,7 @@ edge_init_error: * 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; @@ -786,9 +787,9 @@ static int handle_PACKET(n2n_edge_t * eee, { uint8_t decodebuf[N2N_PKT_BUF_SIZE]; size_t eth_size; - n2n_transform_id_t rx_transop_id; + n2n_transform_t rx_transop_id; - rx_transop_id = (n2n_transform_id_t)pkt->transform; + rx_transop_id = (n2n_transform_t)pkt->transform; if(rx_transop_id == eee->conf.transop_id) { eth_payload = decodebuf; @@ -1575,32 +1576,7 @@ void edge_term(n2n_edge_t * eee) { clear_peer_list(&(eee->known_peers)); eee->transop.deinit(&eee->transop); -} - -/* ************************************** */ - -// TODO check -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; + memset(eee, 0, sizeof(*eee)); } /* ************************************** */ @@ -1661,6 +1637,26 @@ static 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); diff --git a/legacy/edge_keyschedule.c b/legacy/edge_keyschedule.c index 0c568a7..5e3af35 100644 --- a/legacy/edge_keyschedule.c +++ b/legacy/edge_keyschedule.c @@ -3,6 +3,7 @@ typedef struct n2n_tostat { 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 ); 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 2b7e93d..16c7f89 100644 --- a/n2n.h +++ b/n2n.h @@ -186,7 +186,7 @@ typedef char n2n_sn_name_t[N2N_EDGE_SN_HOST_SIZE]; typedef struct n2n_edge_conf { n2n_sn_name_t sn_ip_array[N2N_EDGE_NUM_SUPERNODES]; n2n_community_t community_name; /**< The community. 16 full octets. */ - n2n_transform_id_t transop_id; /**< The transop to use. */ + 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. */ @@ -230,10 +230,10 @@ typedef struct n2n_edge n2n_edge_t; /* Opaque, see edge_utils.c */ /* ************************************** */ /* Transop Init Functions */ -int n2n_transop_null_init(n2n_edge_t *eee, n2n_trans_op_t *op); -int n2n_transop_twofish_init(n2n_edge_t *eee, n2n_trans_op_t *op); +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_init(n2n_edge_t *eee, n2n_trans_op_t *op); +int n2n_transop_aes_cbc_init(const n2n_edge_conf_t *conf, n2n_trans_op_t *ttt); #endif /* Log */ @@ -258,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, @@ -289,6 +287,7 @@ void update_peer_address(n2n_edge_t * eee, 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 */ n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *rv); diff --git a/n2n_transforms.h b/n2n_transforms.h index f46dcac..25f05b2 100644 --- a/n2n_transforms.h +++ b/n2n_transforms.h @@ -24,12 +24,12 @@ #define N2N_TRANSFORM_ID_USER_START 64 #define N2N_TRANSFORM_ID_MAX 65535 -typedef enum n2n_transform_id { +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_id_t; +} n2n_transform_t; struct n2n_trans_op; @@ -50,8 +50,8 @@ typedef int (*n2n_transform_f)( struct n2n_trans_op * arg, */ typedef struct n2n_trans_op { void * priv; /* opaque data. Key schedule goes here. */ - uint8_t no_encryption; /* 1 if this transop does not perform encryption */ // TODO - 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; 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,88 +31,39 @@ #define AES192_KEY_BYTES (192/8) #define AES128_KEY_BYTES (128/8) +/* 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 + 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 */ +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 */ -}; - -typedef struct sa_aes sa_aes_t; - +} transop_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]); +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 */ - sa->sa_id=0; - } - - priv->num_sa=0; - priv->tx_sa=-1; +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 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 @@ -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 ) ) - { - 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; - } + AES_cbc_encrypt( (inbuf + TRANSOP_AES_PREAMBLE_SIZE), + assembly, /* destination */ + len, + &(priv->dec_key), + dec_ivec, AES_DECRYPT ); - } - else - { - /* Wrong security association; drop the packet as it is undecodable. */ - traceEvent( TRACE_ERROR, "decode_aes SA number %lu not found.", sa_rx ); + /* last byte is how much was padding: max value should be + * AES_BLOCKSIZE-1 */ + padding = assembly[ len-1 ] & 0xff; - /* REVISIT: should be able to load a new SA at this point to complete the decoding. */ + 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. */ + } 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) {} -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]; +/* 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); - if ( priv->num_sa < N2N_AES_NUM_SA ) - { - const char * op = (const char *)cspec->opaque; - const char * sep = index( op, '_' ); + memset(ttt, 0, sizeof(*ttt)); + ttt->transform_id = N2N_TRANSFORM_ID_AESCBC; - 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 */ + ttt->tick = transop_tick_aes; + ttt->deinit = transop_deinit_aes; + ttt->fwd = transop_encode_aes; + ttt->rev = transop_decode_aes; - priv->sa[priv->num_sa].spec = *cspec; - priv->sa[priv->num_sa].sa_id = strtoul(tmp, NULL, 10); + 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; - 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; + /* Setup the key */ + return(setup_aes_key(priv, encrypt_key, encrypt_key_len)); } - -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 n2n_tostat_t transop_tick_null( n2n_trans_op_t * arg, time_t now ) -{ - n2n_tostat_t r; +static void transop_tick_null(n2n_trans_op_t * arg, time_t now) {} - 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 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; +typedef struct transop_tf { + TWOFISH* enc_tf; /* tx state */ + TWOFISH* dec_tf; /* rx state */ +} transop_tf_t; - TwoFishDestroy(sa->dec_tf); /* deallocate TWOFISH */ - sa->dec_tf=NULL; +static int transop_deinit_twofish( n2n_trans_op_t * arg ) { + transop_tf_t *priv = (transop_tf_t *)arg->priv; - 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 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 ); - } + traceEvent(TRACE_DEBUG, "decode_twofish %lu", in_len); - return len; -} + 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); -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(len > 0) { + /* Step over 4-byte random nonce value */ + len -= TRANSOP_TF_NONCE_SIZE; /* size of ethernet packet */ - 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" ); + 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, "twofish priv is not allocated" ); + traceEvent( TRACE_ERROR, "decode_twofish inbuf wrong size (%ul) to decrypt.", in_len ); - return retval; + return len; } -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; +static void transop_tick_twofish( n2n_trans_op_t * arg, time_t now ) {} + +/* 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); + + memset(ttt, 0, sizeof(*ttt)); + ttt->transform_id = N2N_TRANSFORM_ID_TWOFISH; + + ttt->tick = transop_tick_twofish; + ttt->deinit = transop_deinit_twofish; + ttt->fwd = transop_encode_twofish; + ttt->rev = transop_decode_twofish; + + 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); }