diff --git a/edge.c b/edge.c index 75bdee0..7aad4aa 100644 --- a/edge.c +++ b/edge.c @@ -136,7 +136,7 @@ static void help() { "-l \n" " " "[-p ] [-M ] " - "[-r] [-E] [-v] [-t ] [-b] [-h]\n\n"); + "[-r] [-E] [-v] [-t ] [-b] [-A] [-h]\n\n"); #ifdef __linux__ printf("-d | tun device name\n"); @@ -162,6 +162,7 @@ static void help() { " | eg. -m 01:02:03:04:05:06\n"); printf("-M | Specify n2n MTU of edge interface (default %d).\n", DEFAULT_MTU); printf("-r | Enable packet forwarding through n2n community.\n"); + printf("-A | Set AES CBC as the preferred encryption mode.\n"); 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"); @@ -268,6 +269,12 @@ static int setOption(int optkey, char *optargument, edge_conf_t *ec, n2n_edge_t break; } + case 'A': + { + eee->preferred_aes = 1; + break; + } + case 'l': /* supernode-list */ if(optargument) { if(eee->sn_num < N2N_EDGE_NUM_SUPERNODES) { @@ -357,7 +364,7 @@ static int loadFromCLI(int argc, char *argv[], edge_conf_t *ec, n2n_edge_t *eee) u_char c; while((c = getopt_long(argc, argv, - "K:k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:", + "K:k:a:bc:Eu:g:m:M:s:d:l:p:fvhrAt:", long_options, NULL)) != '?') { if(c == 255) break; setOption(c, optarg, ec, eee); @@ -665,8 +672,12 @@ int main(int argc, char* argv[]) { traceEvent(TRACE_NORMAL, "Binding to local port %d", (signed int)ec.local_port); if(ec.encrypt_key) { - if(edge_init_twofish(&eee, (uint8_t *)(ec.encrypt_key), strlen(ec.encrypt_key)) < 0) { - fprintf(stderr, "Error: twofish setup failed.\n"); + if(edge_init_aes_psk(&eee, (uint8_t *)(ec.encrypt_key), strlen(ec.encrypt_key)) < 0) { + fprintf(stderr, "Error: AES PSK setup failed.\n"); + return(-1); + } + if(edge_init_twofish_psk(&eee, (uint8_t *)(ec.encrypt_key), strlen(ec.encrypt_key)) < 0) { + fprintf(stderr, "Error: twofish PSK setup failed.\n"); return(-1); } } else if(strlen(eee.keyschedule) > 0) { diff --git a/edge_utils.c b/edge_utils.c index f1e398a..13bb7c9 100644 --- a/edge_utils.c +++ b/edge_utils.c @@ -75,6 +75,7 @@ int edge_init(n2n_edge_t * eee) { 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 */ @@ -644,38 +645,66 @@ const char * supernode_ip(const n2n_edge_t * eee) { /* ************************************** */ -int edge_init_twofish(n2n_edge_t * eee, uint8_t *encrypt_pwd, +int edge_init_twofish_psk(n2n_edge_t * eee, uint8_t *encrypt_pwd, uint32_t encrypt_pwd_len) { - return transop_twofish_setup(&(eee->transop[N2N_TRANSOP_TF_IDX]), + return transop_twofish_setup_psk(&(eee->transop[N2N_TRANSOP_TF_IDX]), 0, encrypt_pwd, encrypt_pwd_len); } /* ************************************** */ -/** 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) -{ - n2n_tostat_t tst; - size_t trop = eee->tx_transop_idx; +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); +} + +/* ************************************** */ + +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); - /* Tests are done in order that most preferred transform is last and causes - * tx_transop_idx to be left at most preferred valid transform. */ - tst = (eee->transop[N2N_TRANSOP_NULL_IDX].tick)(&(eee->transop[N2N_TRANSOP_NULL_IDX]), now); - 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; + *trop = N2N_TRANSOP_AESCBC_IDX; } - tst = (eee->transop[N2N_TRANSOP_TF_IDX].tick)(&(eee->transop[N2N_TRANSOP_TF_IDX]), now); + 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; + *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; @@ -1688,8 +1717,10 @@ int quick_edge_init(char *device_name, char *community_name, device_mac, DEFAULT_MTU) < 0) return(-1); - if(edge_init_twofish(&eee, (uint8_t *)encrypt_key, strlen(encrypt_key)) < 0) - return(-2); + if(edge_init_aes_psk(&eee, (uint8_t *)encrypt_key, strlen(encrypt_key)) < 0) + return(-2); + if(edge_init_twofish_psk(&eee, (uint8_t *)encrypt_key, strlen(encrypt_key)) < 0) + return(-2); snprintf((char*)eee.community_name, sizeof(eee.community_name), "%s", community_name); supernode2addr(&(eee.supernode), supernode_ip_address_port); diff --git a/n2n.h b/n2n.h index 9787e58..914df23 100644 --- a/n2n.h +++ b/n2n.h @@ -186,6 +186,7 @@ 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; @@ -337,7 +338,9 @@ void set_peer_operational(n2n_edge_t * eee, const n2n_mac_t mac, const n2n_sock_t * peer); const char * supernode_ip(const n2n_edge_t * eee); -int edge_init_twofish(n2n_edge_t * eee, uint8_t *encrypt_pwd, +int edge_init_twofish_psk(n2n_edge_t * eee, uint8_t *encrypt_pwd, + uint32_t encrypt_pwd_len); +int edge_init_aes_psk(n2n_edge_t * eee, uint8_t *encrypt_pwd, uint32_t encrypt_pwd_len); int run_edge_loop(n2n_edge_t * eee, int *keep_running); void edge_term(n2n_edge_t * eee); diff --git a/n2n_transforms.h b/n2n_transforms.h index 3db6a83..dd172c8 100644 --- a/n2n_transforms.h +++ b/n2n_transforms.h @@ -78,7 +78,12 @@ struct n2n_trans_op { }; /* Setup a single twofish SA for single-key operation. */ -int transop_twofish_setup( n2n_trans_op_t * ttt, +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 ); diff --git a/transform_aes.c b/transform_aes.c index 718cb4e..ba642e4 100644 --- a/transform_aes.c +++ b/transform_aes.c @@ -339,6 +339,41 @@ static int transop_decode_aes( n2n_trans_op_t * arg, return len; } +/* + * 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) { + /* pstat is number of bytes read into keybuf. */ + sa_aes_t * sa = &(priv->sa[priv->num_sa]); + size_t aes_keysize_bytes; + size_t aes_keysize_bits; + + /* Clear out any old possibly longer key matter. */ + memset( &(sa->enc_key), 0, sizeof(AES_KEY) ); + memset( &(sa->dec_key), 0, sizeof(AES_KEY) ); + + memset( &(sa->enc_ivec), 0, sizeof(N2N_AES_IVEC_SIZE) ); + memset( &(sa->dec_ivec), 0, sizeof(N2N_AES_IVEC_SIZE) ); + + aes_keysize_bytes = aes_best_keysize(pstat); + aes_keysize_bits = 8 * aes_keysize_bytes; + + /* Use N2N_MAX_KEYSIZE because the AES key needs to be of fixed + * size. If fewer bits specified then the rest will be + * zeroes. AES acceptable key sizes are 128, 192 and 256 + * bits. */ + AES_set_encrypt_key( keybuf, aes_keysize_bits, &(sa->enc_key)); + AES_set_decrypt_key( keybuf, aes_keysize_bits, &(sa->dec_key)); + /* Leave ivecs set to all zeroes */ + + traceEvent( TRACE_DEBUG, "transop_addspec_aes sa_id=%u, %u bits data=%s.\n", + priv->sa[priv->num_sa].sa_id, aes_keysize_bits, keybuf); + + ++(priv->num_sa); +} + static int transop_addspec_aes( n2n_trans_op_t * arg, const n2n_cipherspec_t * cspec ) { int retval = 1; @@ -369,33 +404,7 @@ static int transop_addspec_aes( n2n_trans_op_t * arg, const n2n_cipherspec_t * c pstat = n2n_parse_hex( keybuf, N2N_MAX_KEYSIZE, sep+1, s ); if ( pstat > 0 ) { - /* pstat is number of bytes read into keybuf. */ - sa_aes_t * sa = &(priv->sa[priv->num_sa]); - size_t aes_keysize_bytes; - size_t aes_keysize_bits; - - /* Clear out any old possibly longer key matter. */ - memset( &(sa->enc_key), 0, sizeof(AES_KEY) ); - memset( &(sa->dec_key), 0, sizeof(AES_KEY) ); - - memset( &(sa->enc_ivec), 0, sizeof(N2N_AES_IVEC_SIZE) ); - memset( &(sa->dec_ivec), 0, sizeof(N2N_AES_IVEC_SIZE) ); - - aes_keysize_bytes = aes_best_keysize(pstat); - aes_keysize_bits = 8 * aes_keysize_bytes; - - /* Use N2N_MAX_KEYSIZE because the AES key needs to be of fixed - * size. If fewer bits specified then the rest will be - * zeroes. AES acceptable key sizes are 128, 192 and 256 - * bits. */ - AES_set_encrypt_key( keybuf, aes_keysize_bits, &(sa->enc_key)); - AES_set_decrypt_key( keybuf, aes_keysize_bits, &(sa->dec_key)); - /* Leave ivecs set to all zeroes */ - - traceEvent( TRACE_DEBUG, "transop_addspec_aes sa_id=%u, %u bits data=%s.\n", - priv->sa[priv->num_sa].sa_id, aes_keysize_bits, sep+1); - - ++(priv->num_sa); + add_aes_key(priv, keybuf, pstat); retval = 0; } } @@ -510,6 +519,31 @@ int transop_aes_init( n2n_trans_op_t * ttt ) 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) { + sa_aes_t *sa; + priv->num_sa=0; + 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. */ + 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 @@ -606,5 +640,13 @@ int transop_aes_init( n2n_trans_op_t * ttt ) 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/transform_tf.c b/transform_tf.c index f2cd6b5..55ab7bd 100644 --- a/transform_tf.c +++ b/transform_tf.c @@ -384,73 +384,34 @@ static n2n_tostat_t transop_tick_twofish( n2n_trans_op_t * arg, time_t now ) return r; } - -int transop_twofish_setup( n2n_trans_op_t * ttt, +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 = NULL; - - if ( ttt->priv ) - { - transop_deinit_twofish( ttt ); - } + transop_tf_t * priv = (transop_tf_t *)ttt->priv; - memset( ttt, 0, sizeof( n2n_trans_op_t ) ); + if(priv) { + sa_twofish_t *sa; - priv = (transop_tf_t *) malloc( sizeof(transop_tf_t) ); + 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; - if ( NULL != priv ) - { - size_t i; - sa_twofish_t * sa=NULL; + /* This is a preshared key setup. Both Tx and Rx are using the same security association. */ - /* install the private structure. */ - ttt->priv = priv; + sa->enc_tf = TwoFishInit(encrypt_pwd, encrypt_pwd_len); + sa->dec_tf = TwoFishInit(encrypt_pwd, encrypt_pwd_len); - 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; - } - - 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) ) - { - ttt->transform_id = N2N_TRANSFORM_ID_TWOFISH; - ttt->deinit = transop_deinit_twofish; - ttt->addspec = transop_addspec_twofish; - ttt->tick = transop_tick_twofish; /* chooses a new tx_sa */ - ttt->fwd = transop_encode_twofish; - ttt->rev = transop_decode_twofish; - - retval = 0; - } - else - { - traceEvent( TRACE_ERROR, "TwoFishInit failed" ); - } - } - else - { - memset( ttt, 0, sizeof(n2n_trans_op_t) ); - traceEvent( TRACE_ERROR, "Failed to allocate priv for twofish" ); - } + 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; }