added user-password edge authentication (#673)

This commit is contained in:
Logan oos Even 2021-06-04 03:11:22 +05:45 committed by GitHub
parent 1a523f5e1a
commit a28327a0af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1636 additions and 320 deletions

View File

@ -4,10 +4,94 @@ From a discussion on how to prevent MAC address spoofing, the need of edge authe
## Implementation ## Implementation
n2n implements two different authentication schemes the user can chose from.
### Identity Based Scheme
A very basic authentication scheme based on a uniqe identification number is put in place. This ID is randomly generated during edge start-up and remains unchanged until the edge terminates. With every REGISTER_SUPER, it is tranmitted to the supernode which remembers it from the first REGISTER_SUPER and can compare it to all the following ones. It is a verfication based on "showing the ID". The supernode accepts REGISTER_SUPER (and thus changed MAC address or internet socket) and UNREGISTER type messages only if verification passes. A very basic authentication scheme based on a uniqe identification number is put in place. This ID is randomly generated during edge start-up and remains unchanged until the edge terminates. With every REGISTER_SUPER, it is tranmitted to the supernode which remembers it from the first REGISTER_SUPER and can compare it to all the following ones. It is a verfication based on "showing the ID". The supernode accepts REGISTER_SUPER (and thus changed MAC address or internet socket) and UNREGISTER type messages only if verification passes.
This does not only prevent UNREGISTER attacks but also MAC spoofing even in case of several federated supernodes because each REGISTER_SUPER message is forwarded to all other supernodes. If a new edge (un)intentionally tries to claim a MAC address which already has been in use by another edge, this would be detected as unauthorized MAC change because the new edge presents a different authentication ID. The supernode which rejects it (based on a failed comparison) sends a REGISTER_SUPER_**NAK** type message to the new edge as well as the supernode through which the new edge tried to register. The REGISTER_SUPER_NAK will cause deletion of associated entries at the supernode and make a complying edge stop. This does not only prevent UNREGISTER attacks but also MAC spoofing even in case of several federated supernodes because each REGISTER_SUPER message is forwarded to all other supernodes. If a new edge (un)intentionally tries to claim a MAC address which already has been in use by another edge, this would be detected as unauthorized MAC change because the new edge presents a different authentication ID. The supernode which rejects it (based on a failed comparison) sends a REGISTER_SUPER_**NAK** type message to the new edge as well as the supernode through which the new edge tried to register. The REGISTER_SUPER_NAK will cause deletion of associated entries at the supernode and make a complying edge stop.
As opposed to the MAC address which is sent out with each packet also between edges, the ID remains between the edge and the supernode. Other edges do not become aware of it and thus are not able to spoof. As opposed to the MAC address which is sent out with each packet also between edges, the ID remains between the edge and the supernode. Other edges do not become aware of it and thus are not able to spoof.
A somewhat hurdling network sniffing attack aimed at observing the authentication ID could break this scheme. Thus, further development towards a more sophisticated crypto-based authentication scheme is intended. A somewhat hurdling network sniffing attack aimed at observing the authentication ID could break this scheme. Thus, further development towards a more sophisticated crypto-based authentication scheme is intended.
### User / Password Based Authentication
A more advanced scheme relies on username and especially password. Public key cryptography, namely Curve25519, ensures safety. Basically, the password along with the mixed in user name, serve as private key. The corresponding public key is generated by the `tools/n2n-keygen` utility. The such generated public key gets depoisted at the supernode.
#### Supernode Preparation
To generate a public key for the user `logan` and her very secret password `007`, the `tools/n2n-keygen` utility would be called with username and password as command line parameter:
```bash
[user@machine n2n]$ tools/n2n-keygen logan 007
* logan nHWum+r42k1qDXdIeH-WFKeylK5UyLStRzxofRNAgpG
```
The generated output line of format `* <username> <public key>` needs to be copied to the supernode's community list file, e.g. to the exemplary `community.list` file.
```
#
# List of allowed communities
# ---------------------------
#
# these could either be fixed-name communities such as the following lines ...
#
mynetwork
netleo
* logan nHWum+r42k1qDXdIeH-WFKeylK5UyLStRzxofRNAgpG
* sister HwHpPrdMft+38tFDDiunUds6927t0+zhCMMkQdJafcC
#
# ... or regular expressions that a community name must fully match
# such as ntop[0-1][0-9] for communities from "ntop00" through "ntop19"
#
ntop[0-1][0-9]
...
```
This example also lists another user `sister` (with the same secret password `007`). The users become part of the preceding community name, `netleo` in this case. The public keys are cryptographically tied only to the user name, not to the community name. That way, to switch a user from one community to another, her line can easily be copied from one community section to another. By the way, do not forget to provide the `community.list` file to the supernode through the `-c community.list` parameter.
Current supernode behavior does not limit the simultanious usage of usernames, i.e. one username can be used from several edges at the same time. However, it is recommended to use a distinct username and password for each edge or computer. Your management port output will be much more meaningful then with the HINT column showing the related username. Also, the auto IP address feature, i.e. not using `-a <IP address>` at the edge, will more likely assign unique addresses as those depend on the username.
If a user chooses a new password or needs to be excluded from accessing the community (stolen edge scenario), the corresponding line of the `community.list` file can be replaced with a newly generated one or be deleted respectively. Restarting the supernode is required after performing changes because the supernode reads in this data only once at start-up.
With a view to the detailed explanations below, your supernode should have a non-default federation name given by the `-F <federation name>` command line parameter, e.g. `-F secretFed`. It is used to derive a priavte key on the supernode side and is only to be shared among supernodes. However, a federation-wide support for this feature, i.e. across several supernodes, is possible but not implemented yet.
#### Edge
The edge takes the username with the already present, identifying command line paramater `-I <username>`. The password goes into `-J <password>`. Continuing the given example, the edge is invoked by
```
[user@host n2n]$ sudo ./edge -l <supernode:port> -c netleo -I logan -J 007 <your additional parameters>
```
Note that header encryption already is enabled automatically as this authentication scheme heavily relies on it. Also, currently only the stream ciphers work with this authentication scheme reliably in terms of security. So, `-A4` for ChaCha20 or `-A5` for SPECK along with a key `-k <key>` are required as additional parameters.
The edges need to know the public key of the supernode. By default, the edges assume the default federation name, or more specific, the corresponding public key. In case the supernode is given a custom federation name which is highly recommended, the supernode's public key is provided to the edges via command line parameter `-P <public key>. It can be generated from the federation name by using the `tools/n2n-keygen` utility as well:
```bash
[user@host n2n]$ tools/n2n-keygen -F secretFed
-P opIyaWhWjKLJSNOHNpKnGmelhHWRqkmY5pAx7lbDHp4
```
Considering all this, our example expands to
```
[user@host n2n]$ sudo ./edge -l <supernode:port> -c netleo -I logan -J 007 -A5 -k mySecretKey -P opIyaWhWjKLJSNOHNpKnGmelhHWRqkmY5pAx7lbDHp4
```
#### How Does It Work?
In order to make this authentication scheme work, the existing header encryption scheme is split into using two keys: a _static_ and a _dynamic_ one. The static key remains unchanged and is the [classic header encryption key](https://github.com/ntop/n2n/blob/dev/doc/Crypto.md#header) derived from the community name. It only is applied to the very basic registration traffic between edge and supernode (REGISTER_SUPER, REGISTER_SUPER_ACK, REGISTER_SUPER_NAK). The dynamic key is derived (among others) from the federation name keep it secret! and appplied to all the other packets, especially the data packets (PAKET) and peer-to-peer building packets (REGISTER), but also the ping and peer information (QUERY_PEER, PEER_INFO). An edge not provided with a valid dynamic key is not able to participate in the further communication.
In regular header encryption mode, static key and dynamic key are equal. With activated user-password scheme, the supernode generates and transmits a dynamic key with the REGISTER_SUPER for further use. This happens in a secure way based on public key cryptography. A non-autheticated edge, i.e. without corresponding entry at the supernode or valid credentials, will not receive a valid dynmic key for communication beyond registration.
#### Possible Extensions
Tools for automizing [`.conf` file](https://github.com/ntop/n2n/blob/dev/doc/ConfigurationFiles.md) generation for deployment ot delivery to freshly registered and approved users could greatly enhance this ecosystem; a user would not have to mess around with command line parameters but just copy a `.conf` file into a specified directory.
Implementation of a federation-wide support for user-password authentication would allow much bigger networks using this feature. Still required steps are outlined [in some earlier post](https://github.com/ntop/n2n/issues/670#issuecomment-802119568).
Let us know if you are intrerested in implementing or furthering these ideas.

View File

@ -18,7 +18,7 @@ rpmbuild -bb ./n2n.spec
- Better ming Windows build support. - Better ming Windows build support.
- Added `-E` flag to allow multicast ethernet traffic. - Added `-E` flag to allow multicast ethernet traffic.
## Draft changelog between 2.8.x and 2.9.x (as of February 8, 2021) ## Draft changelog between 2.8.x and 2.9.x (as of June 4, 2021)
### New Features ### New Features
@ -32,6 +32,7 @@ rpmbuild -bb ./n2n.spec
- Network interface metric can be set by command-line option `-x` (Windows only) - Network interface metric can be set by command-line option `-x` (Windows only)
- Re-enabled local peer detection by multicast on Windows - Re-enabled local peer detection by multicast on Windows
- Edge identifier (`-I`) helps to identify edges more easily in management port output - Edge identifier (`-I`) helps to identify edges more easily in management port output
- Optional edge user and password authentication (`-J`, `-P`)
### Improvements ### Improvements

43
include/auth.h Normal file
View File

@ -0,0 +1,43 @@
/*
* (C) 2007-21 - 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 <http://www.gnu.org/licenses/>
*
*/
#include "n2n.h"
#ifndef AUTH_H
#define AUTH_H
int bin_to_ascii (uint8_t *out, uint8_t *in, size_t in_len);
int ascii_to_bin (uint8_t *out, uint8_t *in);
int generate_private_key(n2n_private_public_key_t key, uint8_t *in);
int generate_public_key (n2n_private_public_key_t pub, n2n_private_public_key_t prv);
int generate_shared_secret (n2n_private_public_key_t shared, n2n_private_public_key_t prv, n2n_private_public_key_t pub);
int bind_private_key_to_username (n2n_private_public_key_t prv, uint8_t *username);
int calculate_dynamic_key (uint8_t out_key[N2N_AUTH_CHALLENGE_SIZE],
uint32_t key_time, n2n_community_t comm, n2n_community_t fed);
#endif

20
include/curve25519.h Normal file
View File

@ -0,0 +1,20 @@
/**
* (C) 2007-21 - 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 <http://www.gnu.org/licenses/>
*
*/
void curve25519 (unsigned char *q, const unsigned char *n, const unsigned char *p);

View File

@ -27,4 +27,9 @@ int packet_header_encrypt (uint8_t packet[], uint16_t header_len, uint16_t packe
uint64_t stamp); uint64_t stamp);
void packet_header_setup_key (const char *community_name, void packet_header_setup_key (const char *community_name,
he_context_t **ctx, he_context_t **ctx_iv); he_context_t **ctx_static, he_context_t **ctx_dynamic,
he_context_t **ctx_iv_static, he_context_t **ctx_iv_dynamic);
void packet_header_change_dynamic_key (const char *key_dynamic,
he_context_t **ctx_dynamic,
he_context_t **ctx_iv_dynamic);

View File

@ -150,9 +150,11 @@
#include "aes.h" #include "aes.h"
#include "cc20.h" #include "cc20.h"
#include "speck.h" #include "speck.h"
#include "curve25519.h"
#include "n2n_regex.h" #include "n2n_regex.h"
#include "sn_selection.h" #include "sn_selection.h"
#include "network_traffic_filter.h" #include "network_traffic_filter.h"
#include "auth.h"
/* ************************************** */ /* ************************************** */
@ -212,6 +214,7 @@ void print_n2n_version ();
int is_empty_ip_address (const n2n_sock_t * sock); int is_empty_ip_address (const n2n_sock_t * sock);
void print_edge_stats (const n2n_edge_t *eee); void print_edge_stats (const n2n_edge_t *eee);
int memrnd (uint8_t *address, size_t len); int memrnd (uint8_t *address, size_t len);
int memxor (uint8_t *destination, const uint8_t *source, size_t len);
/* Sockets */ /* Sockets */
char* sock_to_cstr (n2n_sock_str_t out, char* sock_to_cstr (n2n_sock_str_t out,

View File

@ -154,6 +154,8 @@ enum skip_add{SN_ADD = 0, SN_ADD_SKIP = 1, SN_ADD_ADDED = 2};
#define N2N_PKT_VERSION 3 #define N2N_PKT_VERSION 3
#define N2N_DEFAULT_TTL 2 /* can be forwarded twice at most */ #define N2N_DEFAULT_TTL 2 /* can be forwarded twice at most */
#define N2N_COMMUNITY_SIZE 20 #define N2N_COMMUNITY_SIZE 20
#define N2N_PRIVATE_PUBLIC_KEY_SIZE 32
#define N2N_USER_KEY_LINE_STARTER '*'
#define N2N_MAC_SIZE 6 #define N2N_MAC_SIZE 6
#define N2N_COOKIE_SIZE 4 #define N2N_COOKIE_SIZE 4
#define N2N_DESC_SIZE 16 #define N2N_DESC_SIZE 16
@ -176,9 +178,9 @@ enum skip_add{SN_ADD = 0, SN_ADD_SKIP = 1, SN_ADD_ADDED = 2};
#define N2N_TRANSFORM_ID_MAX 65535 #define N2N_TRANSFORM_ID_MAX 65535
#ifndef max #ifndef max
#define max(a, b) ((a < b) ? b : a) #define max(a, b) (((a) < (b)) ? (b) : (a))
#endif #endif
#ifndef min #ifndef min
#define min(a, b) ((a > b) ? b : a) #define min(a, b) (((a) >(b)) ? (b) : (a))
#endif #endif

View File

@ -22,6 +22,7 @@
typedef uint8_t n2n_community_t[N2N_COMMUNITY_SIZE]; typedef uint8_t n2n_community_t[N2N_COMMUNITY_SIZE];
typedef uint8_t n2n_private_public_key_t[N2N_PRIVATE_PUBLIC_KEY_SIZE];
typedef uint8_t n2n_mac_t[N2N_MAC_SIZE]; typedef uint8_t n2n_mac_t[N2N_MAC_SIZE];
typedef uint8_t n2n_cookie_t[N2N_COOKIE_SIZE]; typedef uint8_t n2n_cookie_t[N2N_COOKIE_SIZE];
typedef uint8_t n2n_desc_t[N2N_DESC_SIZE]; typedef uint8_t n2n_desc_t[N2N_DESC_SIZE];
@ -267,7 +268,11 @@ typedef enum n2n_pc {
#define IPV4_SIZE 4 #define IPV4_SIZE 4
#define IPV6_SIZE 16 #define IPV6_SIZE 16
#define N2N_AUTH_TOKEN_SIZE 32 /* bytes */
#define N2N_AUTH_MAX_TOKEN_SIZE 48 /* max token size in bytes */
#define N2N_AUTH_CHALLENGE_SIZE 16 /* challenge always is of same size as dynamic key */
#define N2N_AUTH_ID_TOKEN_SIZE 16
#define N2N_AUTH_PW_TOKEN_SIZE (N2N_PRIVATE_PUBLIC_KEY_SIZE + N2N_AUTH_CHALLENGE_SIZE)
#define N2N_EUNKNOWN -1 #define N2N_EUNKNOWN -1
#define N2N_ENOTIMPL -2 #define N2N_ENOTIMPL -2
@ -290,8 +295,9 @@ typedef struct n2n_sock {
} n2n_sock_t; } n2n_sock_t;
typedef enum { typedef enum {
n2n_auth_none = 0, n2n_auth_none = 0,
n2n_auth_simple_id = 1 n2n_auth_simple_id = 1,
n2n_auth_user_password = 2
} n2n_auth_scheme_t; } n2n_auth_scheme_t;
typedef enum { typedef enum {
@ -302,9 +308,9 @@ typedef enum {
} update_edge_ret_value_t; } update_edge_ret_value_t;
typedef struct n2n_auth { typedef struct n2n_auth {
uint16_t scheme; /* What kind of auth */ uint16_t scheme; /* What kind of auth */
uint16_t toksize; /* Size of auth token */ uint16_t token_size; /* Size of auth token */
uint8_t token[N2N_AUTH_TOKEN_SIZE]; /* Auth data interpreted based on scheme */ uint8_t token[N2N_AUTH_MAX_TOKEN_SIZE]; /* Auth data interpreted based on scheme */
} n2n_auth_t; } n2n_auth_t;
typedef struct n2n_common { typedef struct n2n_common {
@ -374,6 +380,7 @@ typedef struct n2n_REGISTER_SUPER_ACK {
typedef struct n2n_REGISTER_SUPER_NAK { typedef struct n2n_REGISTER_SUPER_NAK {
n2n_cookie_t cookie; /* Return cookie from REGISTER_SUPER */ n2n_cookie_t cookie; /* Return cookie from REGISTER_SUPER */
n2n_mac_t srcMac; n2n_mac_t srcMac;
n2n_auth_t auth; /* Authentication scheme and tokens */
} n2n_REGISTER_SUPER_NAK_t; } n2n_REGISTER_SUPER_NAK_t;
@ -580,33 +587,39 @@ typedef struct n2n_trans_op {
/* *************************************************** */ /* *************************************************** */
typedef struct n2n_edge_conf { typedef struct n2n_edge_conf {
struct peer_info *supernodes; /**< List of supernodes */ struct peer_info *supernodes; /**< List of supernodes */
n2n_route_t *routes; /**< Networks to route through n2n */ n2n_route_t *routes; /**< Networks to route through n2n */
n2n_community_t community_name; /**< The community. 16 full octets. */ n2n_community_t community_name; /**< The community. 16 full octets. */
n2n_desc_t dev_desc; /**< The device description (hint) */ n2n_desc_t dev_desc; /**< The device description (hint) */
uint8_t header_encryption; /**< Header encryption indicator. */ n2n_private_public_key_t *public_key; /**< edge's public key (for user/password based authentication) */
he_context_t *header_encryption_ctx; /**< Header encryption cipher context. */ n2n_private_public_key_t *shared_secret; /**< shared secret derived from federation public key, username and password */
he_context_t *header_iv_ctx; /**< Header IV ecnryption cipher context, REMOVE as soon as seperte fileds for checksum and replay protection available */ he_context_t *shared_secret_ctx; /**< context holding the roundkeys derived from shared secret */
n2n_transform_t transop_id; /**< The transop to use. */ n2n_private_public_key_t *federation_public_key; /**< federation public key provided by command line */
uint8_t compression; /**< Compress outgoing data packets before encryption */ uint8_t header_encryption; /**< Header encryption indicator. */
uint16_t num_routes; /**< Number of routes in routes */ he_context_t *header_encryption_ctx_static; /**< Header encryption cipher context. */
uint8_t tuntap_ip_mode; /**< Interface IP address allocated mode, eg. DHCP. */ he_context_t *header_encryption_ctx_dynamic; /**< Header encryption cipher context. */
uint8_t allow_routing; /**< Accept packet no to interface address. */ he_context_t *header_iv_ctx_static; /**< Header IV ecnryption cipher context, REMOVE as soon as separate fileds for checksum and replay protection available */
uint8_t drop_multicast; /**< Multicast ethernet addresses. */ he_context_t *header_iv_ctx_dynamic; /**< Header IV ecnryption cipher context, REMOVE as soon as separate fileds for checksum and replay protection available */
uint8_t disable_pmtu_discovery; /**< Disable the Path MTU discovery. */ n2n_transform_t transop_id; /**< The transop to use. */
uint8_t allow_p2p; /**< Allow P2P connection */ uint8_t compression; /**< Compress outgoing data packets before encryption */
uint8_t sn_num; /**< Number of supernode addresses defined. */ uint16_t num_routes; /**< Number of routes in routes */
uint8_t tos; /** TOS for sent packets */ uint8_t tuntap_ip_mode; /**< Interface IP address allocated mode, eg. DHCP. */
char *encrypt_key; uint8_t allow_routing; /**< Accept packet no to interface address. */
int register_interval; /**< Interval for supernode registration, also used for UDP NAT hole punching. */ uint8_t drop_multicast; /**< Multicast ethernet addresses. */
int register_ttl; /**< TTL for registration packet when UDP NAT hole punching through supernode. */ uint8_t disable_pmtu_discovery; /**< Disable the Path MTU discovery. */
int local_port; uint8_t allow_p2p; /**< Allow P2P connection */
int mgmt_port; uint8_t sn_num; /**< Number of supernode addresses defined. */
uint8_t connect_tcp; /** connection to supernode 0 = UDP; 1 = TCP */ uint8_t tos; /** TOS for sent packets */
n2n_auth_t auth; char *encrypt_key;
filter_rule_t *network_traffic_filter_rules; int register_interval; /**< Interval for supernode registration, also used for UDP NAT hole punching. */
int metric; /**< Network interface metric (Windows only). */ int register_ttl; /**< TTL for registration packet when UDP NAT hole punching through supernode. */
uint8_t number_max_sn_pings; /**< Number of maximum concurrently allowed supernode pings. */ int local_port;
int mgmt_port;
uint8_t connect_tcp; /** connection to supernode 0 = UDP; 1 = TCP */
n2n_auth_t auth;
filter_rule_t *network_traffic_filter_rules;
int metric; /**< Network interface metric (Windows only). */
uint8_t number_max_sn_pings; /**< Number of maximum concurrently allowed supernode pings. */
} n2n_edge_conf_t; } n2n_edge_conf_t;
@ -684,15 +697,29 @@ typedef struct node_supernode_association {
UT_hash_handle hh; /* makes this structure hashable */ UT_hash_handle hh; /* makes this structure hashable */
} node_supernode_association_t; } node_supernode_association_t;
typedef struct sn_user {
n2n_private_public_key_t public_key;
n2n_private_public_key_t shared_secret;
he_context_t *shared_secret_ctx;
n2n_desc_t name;
UT_hash_handle hh;
} sn_user_t;
struct sn_community { struct sn_community {
char community[N2N_COMMUNITY_SIZE]; char community[N2N_COMMUNITY_SIZE];
uint8_t is_federation; /* if not-zero, then the current community is the federation of supernodes */ uint8_t is_federation; /* if not-zero, then the current community is the federation of supernodes */
uint8_t purgeable; /* indicates purgeable community (fixed-name, predetermined (-c parameter) communties usually are unpurgeable) */ uint8_t purgeable; /* indicates purgeable community (fixed-name, predetermined (-c parameter) communties usually are unpurgeable) */
uint8_t header_encryption; /* Header encryption indicator. */ uint8_t header_encryption; /* Header encryption indicator. */
he_context_t *header_encryption_ctx; /* Header encryption cipher context. */ he_context_t *header_encryption_ctx_static; /* Header encryption cipher context. */
he_context_t *header_iv_ctx; /* Header IV ecnryption cipher context, REMOVE as soon as seperate fields for checksum and replay protection available */ he_context_t *header_encryption_ctx_dynamic; /* Header encryption cipher context. */
he_context_t *header_iv_ctx_static; /* Header IV encryption cipher context, REMOVE as soon as separate fields for checksum and replay protection available */
he_context_t *header_iv_ctx_dynamic; /* Header IV encryption cipher context, REMOVE as soon as separate fields for checksum and replay protection available */
uint32_t last_dynamic_key_time; /* UTC time of last dynamic key generation (second accuracy) */
uint8_t dynamic_key[N2N_AUTH_CHALLENGE_SIZE]; /* dynamic key */
struct peer_info *edges; /* Link list of registered edges. */ struct peer_info *edges; /* Link list of registered edges. */
node_supernode_association_t *assoc; /* list of other edges from this community and their supernodes */ node_supernode_association_t *assoc; /* list of other edges from this community and their supernodes */
sn_user_t *allowed_users; /* list of allowed users */
int64_t number_enc_packets; /* Number of encrypted packets handled so far, required for sorting from time to time */ int64_t number_enc_packets; /* Number of encrypted packets handled so far, required for sorting from time to time */
n2n_ip_subnet_t auto_ip_net; /* Address range of auto ip address service. */ n2n_ip_subnet_t auto_ip_net; /* Address range of auto ip address service. */
@ -744,6 +771,7 @@ typedef struct n2n_sn {
struct sn_community *communities; struct sn_community *communities;
struct sn_community_regular_expression *rules; struct sn_community_regular_expression *rules;
struct sn_community *federation; struct sn_community *federation;
n2n_private_public_key_t private_key; /* private federation key derived from federation name */
n2n_auth_t auth; n2n_auth_t auth;
} n2n_sn_t; } n2n_sn_t;

View File

@ -130,11 +130,13 @@ int speck_deinit (speck_context_t *ctx);
// cipher SPECK -- 128 bit block size -- 128 bit key size -- ECB mode // cipher SPECK -- 128 bit block size -- 128 bit key size -- ECB mode
// follows endianess rules as used in official implementation guide and NOT as in original 2013 cipher presentation // follows endianess rules as used in official implementation guide and NOT as in original 2013 cipher presentation
// used for IV in header encryption (one block); encrytion via speck_ctr with null_block as data // used for IV in header encryption (one block) and challenge encryption (user/password)
// for now: just plain C -- probably no need for AVX, SSE, NEON // for now: just plain C -- probably no need for AVX, SSE, NEON
int speck_128_decrypt (unsigned char *inout, speck_context_t *ctx); int speck_128_decrypt (unsigned char *inout, speck_context_t *ctx);
int speck_128_encrypt (unsigned char *inout, speck_context_t *ctx);
#endif // SPECK_H #endif // SPECK_H

171
src/auth.c Normal file
View File

@ -0,0 +1,171 @@
/*
* (C) 2007-21 - 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 <http://www.gnu.org/licenses/>
*
*/
#include "auth.h"
// mapping six binary bits to printable ascii character
static uint8_t b2a[64] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, /* 0 ... 9, A ... F */
0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* G ... V */
0x57, 0x58, 0x59, 0x5a, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, /* W ... Z, a ... l */
0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x2b, 0x2d }; /* m ... z, + , - */
// mapping ascii 0x30 ...0x7f back to 6 bit binary, invalids are mapped to 0xff
static uint8_t a2b[256] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0x3f, 0xff, 0xff, /* 0x20 ... 0x2f */
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0x3e, 0xff, 0x3f, 0xff, /* 0x30 ... 0x3f */
0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, /* 0x40 ... 0x4f */
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 ... 0x5f */
0xff, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, /* 0x60 ... 0x6f */
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff }; /* 0x70 ... 0x7f */
int bin_to_ascii (uint8_t *out, uint8_t *in, size_t in_len) {
// in buffer contains binary data of length in_len
// out buffer is already allocated and of size ceiling(in_len * 8 / 6) + 1
// out buffer will be filled with a string including trailing 0x00
size_t bit_count = 0;
size_t out_count = 0;
uint8_t buf1, buf2;
for(bit_count = 0; bit_count < 8 * in_len; bit_count += 6) {
buf1 = in[bit_count / 8];
buf1 <<= bit_count % 8;
buf2 = ((bit_count + 6) < (8 * in_len)) ? in[bit_count / 8 + 1] : 0;
buf2 >>= 8 - (bit_count % 8);
buf1 |= buf2;
buf1 >>= 2;
out[out_count++] = b2a[buf1];
}
out[out_count] = 0;
return 0;
}
int ascii_to_bin (uint8_t *out, uint8_t *in) {
// in buffer contains 0x00-terminated string to be decoded
// out buffer will contain decoded binary data
// out buffer is already allocated and of size floor(strlen(in) * 6 / 8)
size_t in_count, out_count, bit_count;
uint16_t buf = 0;
bit_count = 0;
out_count = 0;
for(in_count = 0; in_count < strlen(in); in_count++) {
buf <<= 6;
if((in[in_count] > 0x20) && (in[in_count] < 0x80)) {
if(a2b[in[in_count]] != 0xFF) {
buf |= a2b[in[in_count] - 0x20];
} else {
traceEvent(TRACE_NORMAL, "ascii_to_bin encountered the unknown character '%c'", in[in_count]);
}
} else {
traceEvent(TRACE_WARNING, "ascii_to_bin encountered a completely out-of-range character");
}
bit_count += 6;
if(bit_count / 8) {
bit_count -= 8;
out[out_count++] = ((uint8_t)(buf >> bit_count));
}
}
return 0;
}
int generate_private_key (n2n_private_public_key_t key, uint8_t *in) {
// hash the 0-terminated string input twice to generate private key
pearson_hash_256(key, in, strlen(in));
pearson_hash_256(key, key, sizeof(n2n_private_public_key_t));
return 0;
}
int generate_public_key (n2n_private_public_key_t pub, n2n_private_public_key_t prv) {
// generator point '9' on curve
static uint8_t gen[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9 };
curve25519(pub, prv, gen);
return 0;
}
int generate_shared_secret (n2n_private_public_key_t shared, n2n_private_public_key_t prv, n2n_private_public_key_t pub) {
curve25519(shared, prv, pub);
pearson_hash_256(shared, shared, sizeof(n2n_private_public_key_t));
return 0;
}
int bind_private_key_to_username (n2n_private_public_key_t prv, uint8_t *username) {
uint8_t tmp[32];
size_t i;
pearson_hash_256(tmp, username, strlen(username));
memxor(prv, tmp, sizeof(n2n_private_public_key_t));
return 0;
}
// calculate HASH( HASH³(time) ^ HASH²(comm) ^ HASH(fed) )
int calculate_dynamic_key (uint8_t out_key[N2N_AUTH_CHALLENGE_SIZE],
uint32_t key_time, n2n_community_t comm, n2n_community_t fed) {
uint8_t tmp[N2N_AUTH_CHALLENGE_SIZE];
memset(tmp, 0, N2N_AUTH_CHALLENGE_SIZE);
// we know that N2N_AUTH_CHALLENGE_TYPE == 16, i.e. 128 bit that can take the hash value
pearson_hash_128(out_key, (uint8_t*)&key_time, sizeof(key_time));
pearson_hash_128(out_key, out_key, N2N_AUTH_CHALLENGE_SIZE);
pearson_hash_128(out_key, out_key, N2N_AUTH_CHALLENGE_SIZE);
pearson_hash_128(tmp, comm, sizeof(n2n_community_t));
pearson_hash_128(tmp, tmp, N2N_AUTH_CHALLENGE_SIZE);
memxor(out_key, tmp, N2N_AUTH_CHALLENGE_SIZE);
pearson_hash_128(tmp, fed, sizeof(n2n_community_t));
memxor(out_key, tmp, N2N_AUTH_CHALLENGE_SIZE);
pearson_hash_128(out_key, out_key, N2N_AUTH_CHALLENGE_SIZE);
return 0;
}

357
src/curve25519.c Normal file
View File

@ -0,0 +1,357 @@
/**
* (C) 2007-21 - 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 <http://www.gnu.org/licenses/>
*
*/
/**
* version 20081011
* Matthew Dempsky
* Public domain.
* Derived from public domain code by D. J. Bernstein.
* 20140216 tweak: Mask top bit of point input.
*
*/
static void add (unsigned int out[32], const unsigned int a[32], const unsigned int b[32]) {
unsigned int j;
unsigned int u;
u = 0;
for(j = 0; j < 31; ++j) {
u += a[j] + b[j];
out[j] = u & 255;
u >>= 8;
}
u += a[31] + b[31];
out[31] = u;
}
static void sub (unsigned int out[32], const unsigned int a[32], const unsigned int b[32]) {
unsigned int j;
unsigned int u;
u = 218;
for(j = 0; j < 31; ++j) {
u += a[j] + 65280 - b[j];
out[j] = u & 255;
u >>= 8;
}
u += a[31] - b[31];
out[31] = u;
}
static void squeeze (unsigned int a[32]) {
unsigned int j;
unsigned int u;
u = 0;
for(j = 0; j < 31; ++j) {
u += a[j];
a[j] = u & 255;
u >>= 8;
}
u += a[31];
a[31] = u & 127;
u = 19 * (u >> 7);
for(j = 0; j < 31; ++j) {
u += a[j];
a[j] = u & 255;
u >>= 8;
}
u += a[31];
a[31] = u;
}
static const unsigned int minusp[32] = { 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128 };
static void freeze (unsigned int a[32]) {
unsigned int aorig[32];
unsigned int j;
unsigned int negative;
for(j = 0; j < 32; ++j)
aorig[j] = a[j];
add(a, a, minusp);
negative = -((a[31] >> 7) & 1);
for(j = 0; j < 32; ++j)
a[j] ^= negative & (aorig[j] ^ a[j]);
}
static void mult (unsigned int out[32], const unsigned int a[32], const unsigned int b[32]) {
unsigned int i;
unsigned int j;
unsigned int u;
for(i = 0; i < 32; ++i) {
u = 0;
for(j = 0; j <= i; ++j)
u += a[j] * b[i - j];
for(j = i + 1; j < 32; ++j)
u += 38 * a[j] * b[i + 32 - j];
out[i] = u;
}
squeeze(out);
}
static void mult121665 (unsigned int out[32], const unsigned int a[32]) {
unsigned int j;
unsigned int u;
u = 0;
for(j = 0; j < 31; ++j) {
u += 121665 * a[j];
out[j] = u & 255;
u >>= 8;
}
u += 121665 * a[31];
out[31] = u & 127;
u = 19 * (u >> 7);
for(j = 0; j < 31; ++j) {
u += out[j];
out[j] = u & 255;
u >>= 8;
}
u += out[j];
out[j] = u;
}
static void square (unsigned int out[32], const unsigned int a[32]) {
unsigned int i;
unsigned int j;
unsigned int u;
for(i = 0; i < 32; ++i) {
u = 0;
for(j = 0; j < i - j; ++j)
u += a[j] * a[i - j];
for(j = i + 1; j < i + 32 - j; ++j)
u += 38 * a[j] * a[i + 32 - j];
u *= 2;
if((i & 1) == 0) {
u += a[i / 2] * a[i / 2];
u += 38 * a[i / 2 + 16] * a[i / 2 + 16];
}
out[i] = u;
}
squeeze(out);
}
static void select (unsigned int p[64], unsigned int q[64], const unsigned int r[64],
const unsigned int s[64], unsigned int b) {
unsigned int j;
unsigned int t;
unsigned int bminus1;
bminus1 = b - 1;
for(j = 0; j < 64; ++j) {
t = bminus1 & (r[j] ^ s[j]);
p[j] = s[j] ^ t;
q[j] = r[j] ^ t;
}
}
static void mainloop (unsigned int work[64], const unsigned char e[32]) {
unsigned int xzm1[64];
unsigned int xzm[64];
unsigned int xzmb[64];
unsigned int xzm1b[64];
unsigned int xznb[64];
unsigned int xzn1b[64];
unsigned int a0[64];
unsigned int a1[64];
unsigned int b0[64];
unsigned int b1[64];
unsigned int c1[64];
unsigned int r[32];
unsigned int s[32];
unsigned int t[32];
unsigned int u[32];
unsigned int i;
unsigned int j;
unsigned int b;
int pos;
for(j = 0; j < 32; ++j)
xzm1[j] = work[j];
xzm1[32] = 1;
for(j = 33; j < 64; ++j)
xzm1[j] = 0;
xzm[0] = 1;
for(j = 1; j < 64; ++j)
xzm[j] = 0;
for(pos = 254; pos >= 0; --pos) {
b = e[pos / 8] >> (pos & 7);
b &= 1;
select(xzmb, xzm1b, xzm, xzm1, b);
add(a0, xzmb, xzmb + 32);
sub(a0 + 32, xzmb, xzmb + 32);
add (a1, xzm1b, xzm1b + 32);
sub(a1 + 32, xzm1b, xzm1b + 32);
square(b0, a0);
square(b0 + 32, a0 + 32);
mult(b1, a1, a0 + 32);
mult(b1 + 32, a1 + 32, a0);
add(c1, b1, b1 + 32);
sub(c1 + 32, b1, b1 + 32);
square(r, c1 + 32);
sub(s, b0, b0 + 32);
mult121665 (t, s);
add(u, t, b0);
mult(xznb, b0, b0 + 32);
mult(xznb + 32, s, u);
square(xzn1b, c1);
mult(xzn1b + 32, r, work);
select(xzm, xzm1, xznb, xzn1b, b);
}
for(j = 0; j < 64; ++j)
work[j] = xzm[j];
}
static void recip (unsigned int out[32], const unsigned int z[32]) {
unsigned int z2[32];
unsigned int z9[32];
unsigned int z11[32];
unsigned int z2_5_0[32];
unsigned int z2_10_0[32];
unsigned int z2_20_0[32];
unsigned int z2_50_0[32];
unsigned int z2_100_0[32];
unsigned int t0[32];
unsigned int t1[32];
int i;
/* 2 */ square(z2, z);
/* 4 */ square(t1, z2);
/* 8 */ square(t0, t1);
/* 9 */ mult(z9, t0, z);
/* 11 */ mult(z11, z9, z2);
/* 22 */ square(t0, z11);
/* 2^5 - 2^0 = 31 */ mult(z2_5_0, t0, z9);
/* 2^6 - 2^1 */ square(t0, z2_5_0);
/* 2^7 - 2^2 */ square(t1, t0);
/* 2^8 - 2^3 */ square(t0, t1);
/* 2^9 - 2^4 */ square(t1, t0);
/* 2^10 - 2^5 */ square(t0, t1);
/* 2^10 - 2^0 */ mult(z2_10_0, t0, z2_5_0);
/* 2^11 - 2^1 */ square(t0, z2_10_0);
/* 2^12 - 2^2 */ square(t1, t0);
/* 2^20 - 2^10 */ for(i = 2; i < 10; i += 2) {
square(t0, t1);
square(t1, t0);
}
/* 2^20 - 2^0 */ mult(z2_20_0, t1, z2_10_0);
/* 2^21 - 2^1 */ square(t0, z2_20_0);
/* 2^22 - 2^2 */ square(t1, t0);
/* 2^40 - 2^20 */ for(i = 2; i < 20; i += 2) {
square(t0, t1);
square(t1, t0);
}
/* 2^40 - 2^0 */ mult(t0, t1, z2_20_0);
/* 2^41 - 2^1 */ square(t1, t0);
/* 2^42 - 2^2 */ square(t0, t1);
/* 2^50 - 2^10 */ for(i = 2; i < 10; i += 2) {
square(t1, t0);
square(t0, t1);
}
/* 2^50 - 2^0 */ mult(z2_50_0, t0, z2_10_0);
/* 2^51 - 2^1 */ square(t0, z2_50_0);
/* 2^52 - 2^2 */ square(t1, t0);
/* 2^100 - 2^50 */ for(i = 2; i < 50; i += 2) {
square(t0, t1);
square(t1, t0);
}
/* 2^100 - 2^0 */ mult(z2_100_0, t1, z2_50_0);
/* 2^101 - 2^1 */ square(t1, z2_100_0);
/* 2^102 - 2^2 */ square(t0, t1);
/* 2^200 - 2^100 */ for(i = 2; i < 100; i += 2) {
square(t1, t0);
square(t0, t1);
}
/* 2^200 - 2^0 */ mult(t1, t0, z2_100_0);
/* 2^201 - 2^1 */ square(t0, t1);
/* 2^202 - 2^2 */ square(t1, t0);
/* 2^250 - 2^50 */ for(i = 2; i < 50; i += 2) {
square(t0, t1);
square(t1, t0);
}
/* 2^250 - 2^0 */ mult(t0, t1, z2_50_0);
/* 2^251 - 2^1 */ square(t1, t0);
/* 2^252 - 2^2 */ square(t0, t1);
/* 2^253 - 2^3 */ square(t1, t0);
/* 2^254 - 2^4 */ square(t0, t1);
/* 2^255 - 2^5 */ square(t1, t0);
/* 2^255 - 21 */ mult(out, t1, z11);
}
void curve25519 (unsigned char *q, const unsigned char *n, const unsigned char *p) {
unsigned int work[96];
unsigned char e[32];
unsigned int i;
for (i = 0; i < 32; ++i)
e[i] = n[i];
e[0] &= 248;
e[31] &= 127;
e[31] |= 64;
for (i = 0; i < 32; ++i)
work[i] = p[i];
work[31] &= 127;
mainloop(work, e);
recip(work + 32, work + 32);
mult(work + 64, work, work + 32);
freeze(work + 64);
for(i = 0; i < 32; ++i)
q[i] = work[64 + i];
}

View File

@ -186,8 +186,11 @@ static void help (int level) {
"[-E] " "[-E] "
"[-I <edge description>] " "[-I <edge description>] "
"\n " "\n "
"[-J <password>] "
"[-P <public key>] "
"[-R <rule string>] " "[-R <rule string>] "
#ifdef WIN32 #ifdef WIN32
"\n "
"[-x <metric>] " "[-x <metric>] "
#endif #endif
"\n\n local options " "\n\n local options "
@ -283,7 +286,9 @@ static void help (int level) {
printf(" -r | enable packet forwarding through n2n community\n"); printf(" -r | enable packet forwarding through n2n community\n");
printf(" -E | accept multicast MAC addresses, drop by default\n"); printf(" -E | accept multicast MAC addresses, drop by default\n");
printf(" -I <description> | annotate the edge's description used for easier\n" printf(" -I <description> | annotate the edge's description used for easier\n"
" | identification in management port output\n"); " | identification in management port output or username\n");
printf(" -J <password> | password for user-password edge authentication\n");
printf(" -P <public key> | federation public key for user-password authentication\n");
printf(" -R <rule> | drop or accept packets by rules, can be set multiple times\n"); printf(" -R <rule> | drop or accept packets by rules, can be set multiple times\n");
printf(" | rule format: 'src_ip/n:[s_port,e_port],...\n" printf(" | rule format: 'src_ip/n:[s_port,e_port],...\n"
" | |on same| ...dst_ip/n:[s_port,e_port],...\n" " | |on same| ...dst_ip/n:[s_port,e_port],...\n"
@ -536,13 +541,38 @@ static int setOption (int optkey, char *optargument, n2n_tuntap_priv_config_t *e
} }
#endif #endif
case 'I': /* Device Description (hint) */ { case 'I': /* Device Description (hint) or username */ {
memset(conf->dev_desc, 0, N2N_DESC_SIZE); memset(conf->dev_desc, 0, N2N_DESC_SIZE);
/* reserve possible last char as null terminator. */ /* reserve possible last char as null terminator. */
strncpy((char *)conf->dev_desc, optargument, N2N_DESC_SIZE-1); strncpy((char *)conf->dev_desc, optargument, N2N_DESC_SIZE-1);
break; break;
} }
case 'J': /* password for user-password authentication */ {
conf->shared_secret = calloc(1, sizeof(n2n_private_public_key_t));
if(conf->shared_secret)
generate_private_key(*(conf->shared_secret), optargument);
// the hash of the username (-I) gets xored into this key later,
// we can't be sure to already have it at this point
// also, the complete shared secret will be calculated then as we
// might still be missing the federation public key as well
break;
}
case 'P': /* federation public key for user-password authentication */ {
if(strlen(optargument) < ((N2N_PRIVATE_PUBLIC_KEY_SIZE * 8 + 5)/ 6 + 1)) {
conf->federation_public_key = calloc(1, sizeof(n2n_private_public_key_t));
if(conf->federation_public_key) {
ascii_to_bin(*(conf->federation_public_key), optargument);
}
} else {
traceEvent(TRACE_WARNING, "Public key too long");
return -1;
}
break;
}
case 'p': { case 'p': {
conf->local_port = atoi(optargument); conf->local_port = atoi(optargument);
@ -693,7 +723,7 @@ static int loadFromCLI (int argc, char *argv[], n2n_edge_conf_t *conf, n2n_tunta
u_char c; u_char c;
while ((c = getopt_long(argc, argv, while ((c = getopt_long(argc, argv,
"k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:I:S::DL:z::A::Hn:R:" "k:a:bc:Eu:g:m:M:s:d:l:p:fvhrt:i:I:J:P:S::DL:z::A::Hn:R:"
#ifdef __linux__ #ifdef __linux__
"T:" "T:"
#endif #endif
@ -708,7 +738,7 @@ static int loadFromCLI (int argc, char *argv[], n2n_edge_conf_t *conf, n2n_tunta
} }
return 0; return 0; /* REVISIT: return setOption()'s return value */
} }
/* *************************************************** */ /* *************************************************** */
@ -921,13 +951,44 @@ int main (int argc, char* argv[]) {
rc = -1; rc = -1;
#endif #endif
// --- additional crypto setup; REVISIT: move to edge_init()?
// payload
if(conf.transop_id == N2N_TRANSFORM_ID_NULL) { if(conf.transop_id == N2N_TRANSFORM_ID_NULL) {
if(conf.encrypt_key) { if(conf.encrypt_key) {
/* make sure that AES is default cipher if key only (and no cipher) is specified */ // make sure that AES is default cipher if key only (and no cipher) is specified
traceEvent(TRACE_WARNING, "Switching to AES as key was provided."); traceEvent(TRACE_WARNING, "Switching to AES as key was provided.");
conf.transop_id = N2N_TRANSFORM_ID_AES; conf.transop_id = N2N_TRANSFORM_ID_AES;
} }
} }
// user auth
if(conf.shared_secret /* containing private key only so far*/) {
// if user-password auth and no federation public key provided, use default
if(!conf.federation_public_key) {
conf.federation_public_key = calloc(1, sizeof(n2n_private_public_key_t));
if(conf.federation_public_key) {
traceEvent(TRACE_WARNING, "Using default federation public key. FOR TESTING ONLY, usage of a custom federation name and key (-P) is highly recommended!");
generate_private_key(*(conf.federation_public_key), FEDERATION_NAME + 1);
generate_public_key(*(conf.federation_public_key), *(conf.federation_public_key));
}
}
// calculate public key and shared secret
if(conf.federation_public_key) {
traceEvent(TRACE_NORMAL, "Using username and password for edge authentication.");
bind_private_key_to_username(*(conf.shared_secret), conf.dev_desc);
conf.public_key = calloc(1, sizeof(n2n_private_public_key_t));
if(conf.public_key)
generate_public_key(*conf.public_key, *(conf.shared_secret));
generate_shared_secret(*(conf.shared_secret), *(conf.shared_secret), *(conf.federation_public_key));
// prepare (first 128 bit) for use as key
conf.shared_secret_ctx = (he_context_t*)calloc(1, sizeof(speck_context_t));
speck_init((speck_context_t**)&(conf.shared_secret_ctx), *(conf.shared_secret), 128);
}
// force header encryption
if(conf.header_encryption != HEADER_ENCRYPTION_ENABLED) {
traceEvent(TRACE_NORMAL, "Enabling header encryption for edge authentication.");
conf.header_encryption = HEADER_ENCRYPTION_ENABLED;
}
}
if(rc < 0) if(rc < 0)
help(0); /* short help */ help(0); /* short help */
@ -979,8 +1040,9 @@ int main (int argc, char* argv[]) {
// for the sake of quickly establishing connection. REVISIT when a more elegant way to re-use main loop code // for the sake of quickly establishing connection. REVISIT when a more elegant way to re-use main loop code
// is found // is found
// if more than one supernode given, find at least one who is alive to faster establish connection // find at least one supernode alive to faster establish connection
if((HASH_COUNT(eee->conf.supernodes) <= 1) || (eee->conf.connect_tcp)) { // exceptions:
if((HASH_COUNT(eee->conf.supernodes) <= 1) || (eee->conf.connect_tcp) || (eee->conf.shared_secret)) {
// skip the initial supernode ping // skip the initial supernode ping
traceEvent(TRACE_DEBUG, "Skip PING to supernode."); traceEvent(TRACE_DEBUG, "Skip PING to supernode.");
runlevel = 2; runlevel = 2;

View File

@ -290,6 +290,7 @@ n2n_edge_t* edge_init (const n2n_edge_conf_t *conf, int *rv) {
int rc = -1, i = 0; int rc = -1, i = 0;
struct peer_info *scan, *tmp; struct peer_info *scan, *tmp;
size_t idx = 0; size_t idx = 0;
uint8_t tmp_key[N2N_AUTH_CHALLENGE_SIZE];
if((rc = edge_verify_conf(conf)) != 0) { if((rc = edge_verify_conf(conf)) != 0) {
traceEvent(TRACE_ERROR, "Invalid configuration"); traceEvent(TRACE_ERROR, "Invalid configuration");
@ -356,20 +357,50 @@ n2n_edge_t* edge_init (const n2n_edge_conf_t *conf, int *rv) {
goto edge_init_error; goto edge_init_error;
} }
/* Set the key schedule (context) for header encryption if enabled */ // set the key schedule (context) for header encryption if enabled
if(conf->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(conf->header_encryption == HEADER_ENCRYPTION_ENABLED) {
traceEvent(TRACE_NORMAL, "Header encryption is enabled."); traceEvent(TRACE_NORMAL, "Header encryption is enabled.");
packet_header_setup_key((char *)(eee->conf.community_name), &(eee->conf.header_encryption_ctx),&(eee->conf.header_iv_ctx)); packet_header_setup_key((char *)(eee->conf.community_name),
&(eee->conf.header_encryption_ctx_static),
&(eee->conf.header_encryption_ctx_dynamic),
&(eee->conf.header_iv_ctx_static),
&(eee->conf.header_iv_ctx_dynamic));
// in case of user/password auth, initialize a random dynamic key to prevent
// unintentional communication with only-header-encrypted community; will be
// overwritten by legit key later
if(conf->shared_secret) {
memrnd(tmp_key, N2N_AUTH_CHALLENGE_SIZE);
packet_header_change_dynamic_key(tmp_key,
&(eee->conf.header_encryption_ctx_dynamic),
&(eee->conf.header_iv_ctx_dynamic));
}
}
// setup authentication scheme
if(!conf->shared_secret) {
// id-based scheme
eee->conf.auth.scheme = n2n_auth_simple_id;
// random authentication token
memrnd(eee->conf.auth.token, N2N_AUTH_ID_TOKEN_SIZE);
eee->conf.auth.token_size = N2N_AUTH_ID_TOKEN_SIZE;
} else {
// user-password scheme
eee->conf.auth.scheme = n2n_auth_user_password;
// 'token' stores public key and the last random challenge being set upon sending REGISTER_SUPER
memcpy(eee->conf.auth.token, eee->conf.public_key, N2N_PRIVATE_PUBLIC_KEY_SIZE);
// random part of token (challenge) will be generated and filled in at each REGISTER_SUPER
eee->conf.auth.token_size = N2N_AUTH_PW_TOKEN_SIZE;
// make sure that only stream ciphers are being used
if((transop_id != N2N_TRANSFORM_ID_CHACHA20)
&& (transop_id != N2N_TRANSFORM_ID_SPECK)) {
traceEvent(TRACE_ERROR, "user-password authentication requires ChaCha20 (-A4) or SPECK (-A5) to be used.");
goto edge_init_error;
}
} }
if(eee->transop.no_encryption) if(eee->transop.no_encryption)
traceEvent(TRACE_WARNING, "Encryption is disabled in edge"); traceEvent(TRACE_WARNING, "Encryption is disabled in edge");
// setup authenitcation scheme
eee->conf.auth.scheme = n2n_auth_simple_id;
memrnd(eee->conf.auth.token, N2N_AUTH_TOKEN_SIZE);
eee->conf.auth.toksize = sizeof(eee->conf.auth.token);
// first time calling edge_init_sockets needs -1 in the sockets for it does throw an error // first time calling edge_init_sockets needs -1 in the sockets for it does throw an error
// on trying to close them (open_sockets does so for also being able to RE-open the sockets // on trying to close them (open_sockets does so for also being able to RE-open the sockets
// if called in-between, see "Supernode not responding" in update_supernode_reg(...) // if called in-between, see "Supernode not responding" in update_supernode_reg(...)
@ -719,28 +750,88 @@ static void peer_set_p2p_confirmed (n2n_edge_t * eee,
} }
/* ************************************** */
// provides the current / a new local auth token // provides the current / a new local auth token
// REVISIT: behavior should depend on some local auth scheme setting (to be implemented)
static int get_local_auth (n2n_edge_t *eee, n2n_auth_t *auth) { static int get_local_auth (n2n_edge_t *eee, n2n_auth_t *auth) {
// n2n_auth_simple_id scheme static const uint8_t null_block[16] = { 0 };
memcpy(auth, &(eee->conf.auth), sizeof(n2n_auth_t));
switch(eee->conf.auth.scheme) {
case n2n_auth_simple_id:
memcpy(auth, &(eee->conf.auth), sizeof(n2n_auth_t));
break;
case n2n_auth_user_password:
// start from the locally stored complete auth token (including type and size fields)
memcpy(auth, &(eee->conf.auth), sizeof(n2n_auth_t));
// the token data consists of
// 32 bytes public key
// 16 bytes random challenge
// generate a new random auth challenge every time
memrnd(auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, N2N_AUTH_CHALLENGE_SIZE);
// store it in local auth token (for comparison later)
memcpy(eee->conf.auth.token + N2N_PRIVATE_PUBLIC_KEY_SIZE, auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, N2N_AUTH_CHALLENGE_SIZE);
// encrypt the challenge for transmission
speck_128_encrypt(auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, (speck_context_t*)eee->conf.shared_secret_ctx);
break;
default:
break;
}
return 0; return 0;
} }
// handles an returning (remote) auth token, takes action as required by auth scheme, and // handles a returning (remote) auth token, takes action as required by auth scheme
// REVISIT: behavior should depend on some local auth scheme setting (to be implemented)
static int handle_remote_auth (n2n_edge_t *eee, struct peer_info *peer, const n2n_auth_t *remote_auth) { static int handle_remote_auth (n2n_edge_t *eee, struct peer_info *peer, const n2n_auth_t *remote_auth) {
// n2n_auth_simple_id scheme: no action required uint8_t tmp_token[N2N_AUTH_MAX_TOKEN_SIZE];
switch(eee->conf.auth.scheme) {
case n2n_auth_simple_id:
// no action required
break;
case n2n_auth_user_password:
memcpy(tmp_token, remote_auth->token, N2N_AUTH_PW_TOKEN_SIZE);
// the returning token data consists of
// 16 bytes double-encrypted challenge
// 16 bytes public key (second half)
// 16 bytes encrypted (original random challenge XOR shared secret XOR dynamic key)
// decrypt double-encrypted received challenge (first half of public key field)
speck_128_decrypt(tmp_token, (speck_context_t*)eee->conf.shared_secret_ctx);
speck_128_decrypt(tmp_token, (speck_context_t*)eee->conf.shared_secret_ctx);
// compare to original challenge
if(0 != memcmp(tmp_token, eee->conf.auth.token + N2N_PRIVATE_PUBLIC_KEY_SIZE, N2N_AUTH_CHALLENGE_SIZE))
return -1;
// decrypt the received challenge in which the dynamic key is wrapped
speck_128_decrypt(tmp_token + N2N_PRIVATE_PUBLIC_KEY_SIZE, (speck_context_t*)eee->conf.shared_secret_ctx);
// un-XOR the original challenge
memxor(tmp_token + N2N_PRIVATE_PUBLIC_KEY_SIZE, eee->conf.auth.token + N2N_PRIVATE_PUBLIC_KEY_SIZE, N2N_AUTH_CHALLENGE_SIZE);
// un-XOR the shared secret
memxor(tmp_token + N2N_PRIVATE_PUBLIC_KEY_SIZE, *(eee->conf.shared_secret), N2N_AUTH_CHALLENGE_SIZE);
// setup for use as dynamic key
packet_header_change_dynamic_key(tmp_token + N2N_PRIVATE_PUBLIC_KEY_SIZE,
&(eee->conf.header_encryption_ctx_dynamic),
&(eee->conf.header_iv_ctx_dynamic));
break;
default:
break;
}
return 0; return 0;
} }
/* ************************************** */ /* ************************************** */
int is_empty_ip_address (const n2n_sock_t * sock) { int is_empty_ip_address (const n2n_sock_t * sock) {
const uint8_t * ptr = NULL; const uint8_t * ptr = NULL;
@ -992,7 +1083,7 @@ void send_query_peer (n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(pktbuf, idx, idx, packet_header_encrypt(pktbuf, idx, idx,
eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, eee->conf.header_encryption_ctx_dynamic, eee->conf.header_iv_ctx_dynamic,
time_stamp ()); time_stamp ());
} }
@ -1003,7 +1094,7 @@ void send_query_peer (n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(pktbuf, idx, idx, packet_header_encrypt(pktbuf, idx, idx,
eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, eee->conf.header_encryption_ctx_dynamic, eee->conf.header_iv_ctx_dynamic,
time_stamp ()); time_stamp ());
} }
@ -1076,7 +1167,7 @@ void send_register_super (n2n_edge_t *eee) {
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt(pktbuf, idx, idx, packet_header_encrypt(pktbuf, idx, idx,
eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, eee->conf.header_encryption_ctx_static, eee->conf.header_iv_ctx_static,
time_stamp()); time_stamp());
/* sent = */ sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock)); /* sent = */ sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock));
@ -1112,7 +1203,7 @@ static void send_unregister_super (n2n_edge_t *eee) {
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt(pktbuf, idx, idx, packet_header_encrypt(pktbuf, idx, idx,
eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, eee->conf.header_encryption_ctx_dynamic, eee->conf.header_iv_ctx_dynamic,
time_stamp()); time_stamp());
/* sent = */ sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock)); /* sent = */ sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock));
@ -1212,7 +1303,7 @@ static void send_register (n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt(pktbuf, idx, idx, packet_header_encrypt(pktbuf, idx, idx,
eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, eee->conf.header_encryption_ctx_dynamic, eee->conf.header_iv_ctx_dynamic,
time_stamp()); time_stamp());
/* sent = */ sendto_sock(eee, pktbuf, idx, remote_peer); /* sent = */ sendto_sock(eee, pktbuf, idx, remote_peer);
@ -1257,7 +1348,7 @@ static void send_register_ack (n2n_edge_t * eee,
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt(pktbuf, idx, idx, packet_header_encrypt(pktbuf, idx, idx,
eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, eee->conf.header_encryption_ctx_dynamic, eee->conf.header_iv_ctx_dynamic,
time_stamp()); time_stamp());
/* sent = */ sendto_sock(eee, pktbuf, idx, remote_peer); /* sent = */ sendto_sock(eee, pktbuf, idx, remote_peer);
@ -2060,8 +2151,9 @@ void edge_send_packet2net (n2n_edge_t * eee,
(u_int)idx, (u_int)len, (u_int)(idx - len), tx_transop_idx); (u_int)idx, (u_int)len, (u_int)(idx - len), tx_transop_idx);
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED)
packet_header_encrypt(pktbuf, headerIdx, idx, // in case of user-password auth, also encrypt the iv of payload assuming ChaCha20 and SPECK having the same iv size
eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, packet_header_encrypt(pktbuf, headerIdx + (NULL != eee->conf.shared_secret) * min(idx - headerIdx, N2N_SPECK_IVEC_SIZE), idx,
eee->conf.header_encryption_ctx_dynamic, eee->conf.header_iv_ctx_dynamic,
time_stamp()); time_stamp());
#ifdef MTU_ASSERT_VALUE #ifdef MTU_ASSERT_VALUE
@ -2162,6 +2254,7 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const
peer_info_t *sn = NULL; peer_info_t *sn = NULL;
n2n_sock_t sender; n2n_sock_t sender;
n2n_sock_t * orig_sender = NULL; n2n_sock_t * orig_sender = NULL;
uint32_t header_enc = 0;
uint64_t stamp = 0; uint64_t stamp = 0;
int skip_add = 0; int skip_add = 0;
@ -2188,14 +2281,22 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const
(signed int)udp_size, sock_to_cstr(sockbuf1, &sender)); (signed int)udp_size, sock_to_cstr(sockbuf1, &sender));
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
if(packet_header_decrypt(udp_buf, udp_size, // match with static (1) or dynamic (2) ctx?
(char *)eee->conf.community_name, header_enc = packet_header_decrypt(udp_buf, udp_size,
eee->conf.header_encryption_ctx, eee->conf.header_iv_ctx, (char *)eee->conf.community_name,
&stamp) == 0) { eee->conf.header_encryption_ctx_static, eee->conf.header_iv_ctx_static,
&stamp);
if(!header_enc)
if(packet_header_decrypt(udp_buf, udp_size,
(char *)eee->conf.community_name,
eee->conf.header_encryption_ctx_dynamic, eee->conf.header_iv_ctx_dynamic,
&stamp)) {
header_enc = 2;
}
if(!header_enc) {
traceEvent(TRACE_DEBUG, "readFromIPSocket failed to decrypt header."); traceEvent(TRACE_DEBUG, "readFromIPSocket failed to decrypt header.");
return; return;
} }
// time stamp verification follows in the packet specific section as it requires to determine the // time stamp verification follows in the packet specific section as it requires to determine the
// sender from the hash list by its MAC, or the packet might be from the supernode, this all depends // sender from the hash list by its MAC, or the packet might be from the supernode, this all depends
// on packet type, path taken (via supernode) and packet structure (MAC is not always in the same place) // on packet type, path taken (via supernode) and packet structure (MAC is not always in the same place)
@ -2215,14 +2316,23 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const
msg_type = cmn.pc; /* packet code */ msg_type = cmn.pc; /* packet code */
// special case for user/pw auth
// community's auth scheme and message type need to match the used key (dynamic)
if((eee->conf.shared_secret)
&& (msg_type != MSG_TYPE_REGISTER_SUPER_ACK)
&& (msg_type != MSG_TYPE_REGISTER_SUPER_NAK)) {
if(header_enc != 2) {
traceEvent(TRACE_WARNING, "process_udp dropped packet encrypted with static key where dynamic key expected.");
return;
}
}
// check if packet is from supernode and find the corresponding supernode in list // check if packet is from supernode and find the corresponding supernode in list
from_supernode = cmn.flags & N2N_FLAGS_FROM_SUPERNODE; from_supernode = cmn.flags & N2N_FLAGS_FROM_SUPERNODE;
if(from_supernode) { if(from_supernode) {
skip_add = SN_ADD_SKIP; skip_add = SN_ADD_SKIP;
sn = add_sn_to_list_by_mac_or_sock (&(eee->conf.supernodes), &sender, null_mac, &skip_add); sn = add_sn_to_list_by_mac_or_sock (&(eee->conf.supernodes), &sender, null_mac, &skip_add);
// a REGISTER_SUPER_NAK could come from some supernode we do not know yet, especially not if(!sn) {
// too soon after start-up -- so, we accept it no matter from what supernode
if((!sn) && (msg_type != MSG_TYPE_REGISTER_SUPER_NAK)) {
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped incoming data from unknown supernode."); traceEvent(TRACE_DEBUG, "readFromIPSocket dropped incoming data from unknown supernode.");
return; return;
} }
@ -2362,104 +2472,104 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const
int i; int i;
int skip_add; int skip_add;
memset(&ra, 0, sizeof(n2n_REGISTER_SUPER_ACK_t)); if(!(eee->sn_wait)) {
traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER_ACK with no outstanding REGISTER_SUPER.");
if(eee->sn_wait) { return;
decode_REGISTER_SUPER_ACK(&ra, &cmn, udp_buf, &rem, &idx, tmpbuf);
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
if(!find_peer_time_stamp_and_verify(eee, sn, ra.srcMac, stamp, TIME_STAMP_NO_JITTER)) {
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_SUPER_ACK due to time stamp error.");
return;
}
}
if(is_valid_peer_sock(&ra.sock))
orig_sender = &(ra.sock);
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK from MAC %s [%s] (external %s). Attempts %u",
macaddr_str(mac_buf1, ra.srcMac),
sock_to_cstr(sockbuf1, &sender),
sock_to_cstr(sockbuf2, orig_sender),
(unsigned int)eee->sup_attempts);
if(0 == memcmp(ra.cookie, eee->curr_sn->last_cookie, N2N_COOKIE_SIZE)) {
handle_remote_auth(eee, sn, &(ra.auth));
if(is_null_mac(eee->curr_sn->mac_addr)) {
HASH_DEL(eee->conf.supernodes, eee->curr_sn);
memcpy(&eee->curr_sn->mac_addr, ra.srcMac, N2N_MAC_SIZE);
HASH_ADD_PEER(eee->conf.supernodes, eee->curr_sn);
}
payload = (n2n_REGISTER_SUPER_ACK_payload_t*)tmpbuf;
// from here on, 'sn' gets used differently
for(i = 0; i < ra.num_sn; i++) {
skip_add = SN_ADD;
sn = add_sn_to_list_by_mac_or_sock(&(eee->conf.supernodes), &(payload->sock), payload->mac, &skip_add);
if(skip_add == SN_ADD_ADDED) {
sn->ip_addr = calloc(1, N2N_EDGE_SN_HOST_SIZE);
if(sn->ip_addr != NULL) {
inet_ntop(payload->sock.family,
(payload->sock.family == AF_INET) ? (void*)&(payload->sock.addr.v4) : (void*)&(payload->sock.addr.v6),
sn->ip_addr, N2N_EDGE_SN_HOST_SIZE - 1);
sprintf (sn->ip_addr, "%s:%u", sn->ip_addr, (uint16_t)(payload->sock.port));
}
sn_selection_criterion_default(&(sn->selection_criterion));
sn->last_seen = 0; /* as opposed to payload handling in supernode */
traceEvent(TRACE_NORMAL, "Supernode '%s' added to the list of supernodes.", sn->ip_addr);
}
// shift to next payload entry
payload++;
}
if(eee->conf.tuntap_ip_mode == TUNTAP_IP_MODE_SN_ASSIGN) {
if((ra.dev_addr.net_addr != 0) && (ra.dev_addr.net_bitlen != 0)) {
net = htonl(ra.dev_addr.net_addr);
if((ip_str = inet_ntoa(*(struct in_addr *) &net)) != NULL) {
strncpy(eee->tuntap_priv_conf.ip_addr, ip_str,
N2N_NETMASK_STR_SIZE);
}
net = htonl(bitlen2mask(ra.dev_addr.net_bitlen));
if((ip_str = inet_ntoa(*(struct in_addr *) &net)) != NULL) {
strncpy(eee->tuntap_priv_conf.netmask, ip_str,
N2N_NETMASK_STR_SIZE);
}
}
}
// update last_sup only on 'real' REGISTER_SUPER_ACKs, not on bootstrap ones (own MAC address
// still null_mac) this allows reliable in/out PACKET drop if not really registered with a supernode yet
if(!is_null_mac(eee->device.mac_addr)) {
if(!eee->last_sup) {
// indicates successful connection between the edge and a supernode
traceEvent(TRACE_NORMAL, "[OK] Edge Peer <<< ================ >>> Super Node");
// send gratuitous ARP only upon first registration with supernode
send_grat_arps(eee);
}
eee->last_sup = now;
}
eee->sn_wait = 0;
reset_sup_attempts(eee); /* refresh because we got a response */
/* NOTE: the register_interval should be chosen by the edge node
* based on its NAT configuration. */
//eee->conf.register_interval = ra.lifetime;
if(eee->cb.sn_registration_updated && !is_null_mac(eee->device.mac_addr))
eee->cb.sn_registration_updated(eee, now, &sender);
} else {
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or old cookie.");
}
} else {
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with no outstanding REGISTER_SUPER.");
} }
memset(&ra, 0, sizeof(n2n_REGISTER_SUPER_ACK_t));
decode_REGISTER_SUPER_ACK(&ra, &cmn, udp_buf, &rem, &idx, tmpbuf);
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
if(!find_peer_time_stamp_and_verify(eee, sn, ra.srcMac, stamp, TIME_STAMP_NO_JITTER)) {
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_SUPER_ACK due to time stamp error.");
return;
}
}
if(memcmp(ra.cookie, eee->curr_sn->last_cookie, N2N_COOKIE_SIZE)) {
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or old cookie.");
return;
}
if(handle_remote_auth(eee, sn, &(ra.auth))) {
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or old response to challenge.");
if(eee->conf.shared_secret) {
traceEvent(TRACE_NORMAL, "Rx REGISTER_SUPER_ACK with wrong or old response to challenge, maybe indicating wrong federation public key (-P).");
}
return;
}
if(is_valid_peer_sock(&ra.sock))
orig_sender = &(ra.sock);
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK from MAC %s [%s] (external %s). Attempts %u",
macaddr_str(mac_buf1, ra.srcMac),
sock_to_cstr(sockbuf1, &sender),
sock_to_cstr(sockbuf2, orig_sender),
(unsigned int)eee->sup_attempts);
if(is_null_mac(eee->curr_sn->mac_addr)) {
HASH_DEL(eee->conf.supernodes, eee->curr_sn);
memcpy(&eee->curr_sn->mac_addr, ra.srcMac, N2N_MAC_SIZE);
HASH_ADD_PEER(eee->conf.supernodes, eee->curr_sn);
}
payload = (n2n_REGISTER_SUPER_ACK_payload_t*)tmpbuf;
// from here on, 'sn' gets used differently
for(i = 0; i < ra.num_sn; i++) {
skip_add = SN_ADD;
sn = add_sn_to_list_by_mac_or_sock(&(eee->conf.supernodes), &(payload->sock), payload->mac, &skip_add);
if(skip_add == SN_ADD_ADDED) {
sn->ip_addr = calloc(1, N2N_EDGE_SN_HOST_SIZE);
if(sn->ip_addr != NULL) {
inet_ntop(payload->sock.family,
(payload->sock.family == AF_INET) ? (void*)&(payload->sock.addr.v4) : (void*)&(payload->sock.addr.v6),
sn->ip_addr, N2N_EDGE_SN_HOST_SIZE - 1);
sprintf (sn->ip_addr, "%s:%u", sn->ip_addr, (uint16_t)(payload->sock.port));
}
sn_selection_criterion_default(&(sn->selection_criterion));
sn->last_seen = 0; /* as opposed to payload handling in supernode */
traceEvent(TRACE_NORMAL, "Supernode '%s' added to the list of supernodes.", sn->ip_addr);
}
// shift to next payload entry
payload++;
}
if(eee->conf.tuntap_ip_mode == TUNTAP_IP_MODE_SN_ASSIGN) {
if((ra.dev_addr.net_addr != 0) && (ra.dev_addr.net_bitlen != 0)) {
net = htonl(ra.dev_addr.net_addr);
if((ip_str = inet_ntoa(*(struct in_addr *) &net)) != NULL)
strncpy(eee->tuntap_priv_conf.ip_addr, ip_str, N2N_NETMASK_STR_SIZE);
net = htonl(bitlen2mask(ra.dev_addr.net_bitlen));
if((ip_str = inet_ntoa(*(struct in_addr *) &net)) != NULL)
strncpy(eee->tuntap_priv_conf.netmask, ip_str, N2N_NETMASK_STR_SIZE);
}
}
// update last_sup only on 'real' REGISTER_SUPER_ACKs, not on bootstrap ones (own MAC address
// still null_mac) this allows reliable in/out PACKET drop if not really registered with a supernode yet
if(!is_null_mac(eee->device.mac_addr)) {
if(!eee->last_sup) {
// indicates successful connection between the edge and a supernode
traceEvent(TRACE_NORMAL, "[OK] Edge Peer <<< ================ >>> Super Node");
// send gratuitous ARP only upon first registration with supernode
send_grat_arps(eee);
}
eee->last_sup = now;
}
eee->sn_wait = 0;
reset_sup_attempts(eee); /* refresh because we got a response */
// NOTE: the register_interval should be chosen by the edge node based on its NAT configuration.
// eee->conf.register_interval = ra.lifetime;
if(eee->cb.sn_registration_updated && !is_null_mac(eee->device.mac_addr))
eee->cb.sn_registration_updated(eee, now, &sender);
break; break;
} }
@ -2468,14 +2578,39 @@ void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const
n2n_REGISTER_SUPER_NAK_t nak; n2n_REGISTER_SUPER_NAK_t nak;
struct peer_info *peer, *scan; struct peer_info *peer, *scan;
memset(&nak, 0, sizeof(n2n_REGISTER_SUPER_NAK_t));
if(!(eee->sn_wait)) {
traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER_NAK with no outstanding REGISTER_SUPER.");
return;
}
memset(&nak, 0, sizeof(n2n_REGISTER_SUPER_NAK_t));
decode_REGISTER_SUPER_NAK(&nak, &cmn, udp_buf, &rem, &idx); decode_REGISTER_SUPER_NAK(&nak, &cmn, udp_buf, &rem, &idx);
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) {
if(!find_peer_time_stamp_and_verify(eee, sn, nak.srcMac, stamp, TIME_STAMP_NO_JITTER)) {
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_SUPER_NAK due to time stamp error.");
return;
}
}
if(memcmp(nak.cookie, eee->curr_sn->last_cookie, N2N_COOKIE_SIZE)) {
traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER_NAK with wrong or old cookie.");
return;
}
// REVISIT: authenticate the NAK packet really originating from the supernode along the auth token.
// this must follow a different scheme because it needs to prove authenticity although the
// edge-provided credentials are wrong
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_NAK"); traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_NAK");
if((memcmp(nak.srcMac, eee->device.mac_addr, sizeof(n2n_mac_t))) == 0) { if((memcmp(nak.srcMac, eee->device.mac_addr, sizeof(n2n_mac_t))) == 0) {
traceEvent(TRACE_ERROR, "Authentication error. MAC or IP address already in use or not released yet by supernode."); if(eee->conf.shared_secret) {
// the error description "MAC or IP ..." is true for the basic authentication scheme (1) traceEvent(TRACE_ERROR, "Authentication error. username or password not recognized by supernode.");
} else {
traceEvent(TRACE_ERROR, "Authentication error. MAC or IP address already in use or not released yet by supernode.");
}
exit(1); exit(1);
} else { } else {
HASH_FIND_PEER(eee->known_peers, nak.srcMac, peer); HASH_FIND_PEER(eee->known_peers, nak.srcMac, peer);

View File

@ -82,7 +82,7 @@ int packet_header_encrypt (uint8_t packet[], uint16_t header_len, uint16_t packe
he_context_t *ctx, he_context_t *ctx_iv, he_context_t *ctx, he_context_t *ctx_iv,
uint64_t stamp) { uint64_t stamp) {
const uint8_t null_block[16] = { 0 }; static const uint8_t null_block[16] = { 0 };
uint32_t *p32 = (uint32_t*)packet; uint32_t *p32 = (uint32_t*)packet;
uint64_t *p64 = (uint64_t*)packet; uint64_t *p64 = (uint64_t*)packet;
uint64_t checksum = 0; uint64_t checksum = 0;
@ -109,8 +109,7 @@ int packet_header_encrypt (uint8_t packet[], uint16_t header_len, uint16_t packe
p32[3] = n2n_rand(); p32[3] = n2n_rand();
// encrypt this pre-IV to IV // encrypt this pre-IV to IV
// use speck ctr with null_block as data to make it a block cipher step speck_128_encrypt(packet, (speck_context_t*)ctx_iv);
speck_ctr(packet, null_block, 16, packet, (speck_context_t*)ctx_iv);
// place IV plus magic in packet // place IV plus magic in packet
p32[4] = htobe32(magic); p32[4] = htobe32(magic);
@ -123,17 +122,50 @@ int packet_header_encrypt (uint8_t packet[], uint16_t header_len, uint16_t packe
void packet_header_setup_key (const char *community_name, void packet_header_setup_key (const char *community_name,
he_context_t **ctx, he_context_t **ctx_iv) { he_context_t **ctx_static, he_context_t **ctx_dynamic,
he_context_t **ctx_iv_static, he_context_t **ctx_iv_dynamic) {
uint8_t key[16]; uint8_t key[16];
pearson_hash_128(key, (uint8_t*)community_name, N2N_COMMUNITY_SIZE);
*ctx = (he_context_t*)calloc(1, sizeof (speck_context_t)); // for REGISTER_SUPER, REGISTER_SUPER_ACK, REGISTER_SUPER_NAK only;
speck_init((speck_context_t**)ctx, key, 128); // for all other packets, same as static by default (changed by user/pw auth scheme
// calling packet_header_change_dynamic_key later)
pearson_hash_128(key, (uint8_t*)community_name, N2N_COMMUNITY_SIZE);
if(!*ctx_static)
*ctx_static = (he_context_t*)calloc(1, sizeof(speck_context_t));
speck_init((speck_context_t**)ctx_static, key, 128);
if(!*ctx_dynamic)
*ctx_dynamic = (he_context_t*)calloc(1, sizeof(speck_context_t));
speck_init((speck_context_t**)ctx_dynamic, key, 128);
// hash again and use as key for IV encryption
pearson_hash_128(key, key, sizeof(key));
if(!*ctx_iv_static)
*ctx_iv_static = (he_context_t*)calloc(1, sizeof(speck_context_t));
speck_init((speck_context_t**)ctx_iv_static, key, 128);
if(!*ctx_iv_dynamic)
*ctx_iv_dynamic = (he_context_t*)calloc(1, sizeof(speck_context_t));
speck_init((speck_context_t**)ctx_iv_dynamic, key, 128);
}
void packet_header_change_dynamic_key (const char *key_dynamic,
he_context_t **ctx_dynamic, he_context_t **ctx_iv_dynamic) {
uint8_t key[16];
pearson_hash_128(key, (uint8_t*)key_dynamic, N2N_AUTH_CHALLENGE_SIZE);
// for REGISTER_SUPER, REGISTER_SUPER_ACK, REGISTER_SUPER_NAK only
// for all other packets, same as static by default (changed by user/pw auth scheme)
speck_init((speck_context_t**)ctx_dynamic, key, 128);
// hash again and use as key for IV encryption // hash again and use as key for IV encryption
// REMOVE as soon as checksum and replay protection get their own fields // REMOVE as soon as checksum and replay protection get their own fields
pearson_hash_128(key, key, sizeof (key)); pearson_hash_128(key, key, sizeof(key));
*ctx_iv = (he_context_t*)calloc(1, sizeof (speck_context_t)); speck_init((speck_context_t**)ctx_iv_dynamic, key, 128);
speck_init((speck_context_t**)ctx_iv, key, 128);
} }

View File

@ -613,9 +613,9 @@ int sock_equal (const n2n_sock_t * a,
// fills a specified memory area with random numbers // fills a specified memory area with random numbers
int memrnd (uint8_t *address, size_t len) { int memrnd (uint8_t *address, size_t len) {
for(; len >= 8; len -= 8) { for(; len >= 4; len -= 4) {
*(uint64_t*)address = n2n_rand(); *(uint32_t*)address = n2n_rand();
address += 8; address += 4;
} }
for(; len > 0; len--) { for(; len > 0; len--) {
@ -626,6 +626,25 @@ int memrnd (uint8_t *address, size_t len) {
return 0; return 0;
} }
// exclusive-ors a specified memory area with another
int memxor (uint8_t *destination, const uint8_t *source, size_t len) {
for(; len >= 4; len -= 4) {
*(uint32_t*)destination ^= *(uint32_t*)source;
source += 4;
destination += 4;
}
for(; len > 0; len--) {
*destination ^= *source;
source++;
destination++;
}
return 0;
}
/* *********************************************** */ /* *********************************************** */
#if defined(WIN32) #if defined(WIN32)

140
src/sn.c
View File

@ -30,13 +30,19 @@ static n2n_sn_t sss_node;
*/ */
static int load_allowed_sn_community (n2n_sn_t *sss) { static int load_allowed_sn_community (n2n_sn_t *sss) {
char buffer[4096], *line, *cmn_str, net_str[20]; char buffer[4096], *line, *cmn_str, net_str[20], format[20];
sn_user_t *user, *tmp_user;
n2n_desc_t username;
n2n_private_public_key_t public_key;
uint8_t ascii_public_key[(N2N_PRIVATE_PUBLIC_KEY_SIZE * 8 + 5) / 6 + 1];
dec_ip_str_t ip_str = {'\0'}; dec_ip_str_t ip_str = {'\0'};
uint8_t bitlen; uint8_t bitlen;
in_addr_t net; in_addr_t net;
uint32_t mask; uint32_t mask;
FILE *fd = fopen(sss->community_file, "r"); FILE *fd = fopen(sss->community_file, "r");
struct sn_community *s, *tmp; struct sn_community *comm, *tmp_comm, *last_added_comm = NULL;
uint32_t num_communities = 0; uint32_t num_communities = 0;
struct sn_community_regular_expression *re, *tmp_re; struct sn_community_regular_expression *re, *tmp_re;
uint32_t num_regex = 0; uint32_t num_regex = 0;
@ -47,22 +53,38 @@ static int load_allowed_sn_community (n2n_sn_t *sss) {
return -1; return -1;
} }
HASH_ITER(hh, sss->communities, s, tmp) { // remove communities (not: federation)
if(s->is_federation) { HASH_ITER(hh, sss->communities, comm, tmp_comm) {
if(comm->is_federation) {
continue; continue;
} }
HASH_DEL(sss->communities, s);
if(NULL != s->header_encryption_ctx) { // remove allowed users from community
free(s->header_encryption_ctx); HASH_ITER(hh, comm->allowed_users, user, tmp_user) {
free(user->shared_secret_ctx);
HASH_DEL(comm->allowed_users, user);
free(user);
} }
free(s);
// remove community
HASH_DEL(sss->communities, comm);
if(NULL != comm->header_encryption_ctx_static) {
// remove header encryption keys
free(comm->header_encryption_ctx_static);
free(comm->header_encryption_ctx_dynamic);
}
free(comm);
} }
// remove all regular expressions for allowed communities
HASH_ITER(hh, sss->rules, re, tmp_re) { HASH_ITER(hh, sss->rules, re, tmp_re) {
HASH_DEL(sss->rules, re); HASH_DEL(sss->rules, re);
free(re); free(re);
} }
// format definition for possible user-key entries
sprintf(format, "%c %%%ds %%%ds", N2N_USER_KEY_LINE_STARTER, N2N_DESC_SIZE - 1, sizeof(ascii_public_key)-1);
while((line = fgets(buffer, sizeof(buffer), fd)) != NULL) { while((line = fgets(buffer, sizeof(buffer), fd)) != NULL) {
int len = strlen(line); int len = strlen(line);
@ -82,6 +104,47 @@ static int load_allowed_sn_community (n2n_sn_t *sss) {
// the loop above does not always determine correct 'len' // the loop above does not always determine correct 'len'
len = strlen(line); len = strlen(line);
// user-key line for edge authentication?
if(line[0] == N2N_USER_KEY_LINE_STARTER) { /* special first character */
if(sscanf(line, format, username, ascii_public_key) == 2) { /* correct format */
if(last_added_comm) { /* is there a valid community to add users to */
user = (sn_user_t*)calloc(1, sizeof(sn_user_t));
if(user) {
// username
memcpy(user->name, username, sizeof(username));
// public key
ascii_to_bin(public_key, ascii_public_key);
memcpy(user->public_key, public_key, sizeof(public_key));
// common shared secret will be calculated later
// add to list
HASH_ADD(hh, last_added_comm->allowed_users, public_key, sizeof(n2n_private_public_key_t), user);
traceEvent(TRACE_INFO, "Added user '%s' with public key '%s' to community '%s'",
user->name, ascii_public_key, last_added_comm->community);
// enable header encryption
last_added_comm->header_encryption = HEADER_ENCRYPTION_ENABLED;
packet_header_setup_key(last_added_comm->community,
&(last_added_comm->header_encryption_ctx_static),
&(last_added_comm->header_encryption_ctx_dynamic),
&(last_added_comm->header_iv_ctx_static),
&(last_added_comm->header_iv_ctx_dynamic));
// dynamic key setup
last_added_comm->last_dynamic_key_time = time(NULL);
calculate_dynamic_key(last_added_comm->dynamic_key, /* destination */
last_added_comm->last_dynamic_key_time, /* time */
last_added_comm->community, /* community name */
sss->federation->community); /* federation name */
packet_header_change_dynamic_key(last_added_comm->dynamic_key,
&(last_added_comm->header_encryption_ctx_dynamic),
&(last_added_comm->header_iv_ctx_dynamic));
}
continue;
}
}
}
// --- community name or regular expression
// cut off any IP sub-network upfront // cut off any IP sub-network upfront
cmn_str = (char*)calloc(len + 1, sizeof(char)); cmn_str = (char*)calloc(len + 1, sizeof(char));
has_net = (sscanf(line, "%s %s", cmn_str, net_str) == 2); has_net = (sscanf(line, "%s %s", cmn_str, net_str) == 2);
@ -89,32 +152,38 @@ static int load_allowed_sn_community (n2n_sn_t *sss) {
// if it contains typical characters... // if it contains typical characters...
if(NULL != strpbrk(cmn_str, ".*+?[]\\")) { if(NULL != strpbrk(cmn_str, ".*+?[]\\")) {
// ...it is treated as regular expression // ...it is treated as regular expression
re = (struct sn_community_regular_expression*)calloc(1,sizeof(struct sn_community_regular_expression)); re = (struct sn_community_regular_expression*)calloc(1, sizeof(struct sn_community_regular_expression));
if(re) { if(re) {
re->rule = re_compile(cmn_str); re->rule = re_compile(cmn_str);
HASH_ADD_PTR(sss->rules, rule, re); HASH_ADD_PTR(sss->rules, rule, re);
num_regex++; num_regex++;
traceEvent(TRACE_INFO, "Added regular expression for allowed communities '%s'", cmn_str); traceEvent(TRACE_INFO, "Added regular expression for allowed communities '%s'", cmn_str);
free(cmn_str); free(cmn_str);
last_added_comm = NULL;
continue; continue;
} }
} }
s = (struct sn_community*)calloc(1,sizeof(struct sn_community)); comm = (struct sn_community*)calloc(1,sizeof(struct sn_community));
if(s != NULL) { if(comm != NULL) {
comm_init(s,cmn_str); comm_init(comm, cmn_str);
/* loaded from file, this community is unpurgeable */ /* loaded from file, this community is unpurgeable */
s->purgeable = COMMUNITY_UNPURGEABLE; comm->purgeable = COMMUNITY_UNPURGEABLE;
/* we do not know if header encryption is used in this community, /* we do not know if header encryption is used in this community,
* first packet will show. just in case, setup the key. */ * first packet will show. just in case, setup the key. */
s->header_encryption = HEADER_ENCRYPTION_UNKNOWN; comm->header_encryption = HEADER_ENCRYPTION_UNKNOWN;
packet_header_setup_key (s->community, &(s->header_encryption_ctx), &(s->header_iv_ctx)); packet_header_setup_key(comm->community,
HASH_ADD_STR(sss->communities, community, s); &(comm->header_encryption_ctx_static),
&(comm->header_encryption_ctx_dynamic),
&(comm->header_iv_ctx_static),
&(comm->header_iv_ctx_dynamic));
HASH_ADD_STR(sss->communities, community, comm);
last_added_comm = comm;
num_communities++; num_communities++;
traceEvent(TRACE_INFO, "Added allowed community '%s' [total: %u]", traceEvent(TRACE_INFO, "Added allowed community '%s' [total: %u]",
(char*)s->community, num_communities); (char*)comm->community, num_communities);
// check for sub-network address // check for sub-network address
if(has_net) { if(has_net) {
@ -138,14 +207,14 @@ static int load_allowed_sn_community (n2n_sn_t *sss) {
} }
} }
if(has_net) { if(has_net) {
s->auto_ip_net.net_addr = ntohl(net); comm->auto_ip_net.net_addr = ntohl(net);
s->auto_ip_net.net_bitlen = bitlen; comm->auto_ip_net.net_bitlen = bitlen;
traceEvent(TRACE_INFO, "Assigned sub-network %s/%u to community '%s'.", traceEvent(TRACE_INFO, "Assigned sub-network %s/%u to community '%s'.",
inet_ntoa(*(struct in_addr *) &net), inet_ntoa(*(struct in_addr *) &net),
s->auto_ip_net.net_bitlen, comm->auto_ip_net.net_bitlen,
s->community); comm->community);
} else { } else {
assign_one_ip_subnet(sss, s); assign_one_ip_subnet(sss, comm);
} }
} }
@ -419,7 +488,6 @@ static int setOption (int optkey, char *_optarg, n2n_sn_t *sss) {
snprintf(sss->federation->community, N2N_COMMUNITY_SIZE - 1 ,"*%s", _optarg); snprintf(sss->federation->community, N2N_COMMUNITY_SIZE - 1 ,"*%s", _optarg);
sss->federation->community[N2N_COMMUNITY_SIZE - 1] = '\0'; sss->federation->community[N2N_COMMUNITY_SIZE - 1] = '\0';
break; break;
} }
#ifdef SN_MANUAL_MAC #ifdef SN_MANUAL_MAC
@ -665,6 +733,8 @@ int main (int argc, char * const argv[]) {
struct passwd *pw = NULL; struct passwd *pw = NULL;
#endif #endif
struct peer_info *scan, *tmp; struct peer_info *scan, *tmp;
struct sn_community *comm, *tmp_comm;
sn_user_t *user, *tmp_user;
sn_init(&sss_node); sn_init(&sss_node);
@ -704,6 +774,30 @@ int main (int argc, char * const argv[]) {
} }
#endif /* #if defined(N2N_HAVE_DAEMON) */ #endif /* #if defined(N2N_HAVE_DAEMON) */
// warn on default federation name
if(!strcmp(sss_node.federation->community, FEDERATION_NAME)) {
traceEvent(TRACE_WARNING, "Using default federation name. FOR TESTING ONLY, usage of a custom federation name (-F) is highly recommended!");
}
// generate shared secrets for user authentication; can be done only after
// federation name is known (-F) and community list completely read (-c)
traceEvent(TRACE_INFO, "started shared secrets calculation for edge authentication");
generate_private_key(sss_node.private_key, sss_node.federation->community + 1); /* skip '*' federation leading character */
HASH_ITER(hh, sss_node.communities, comm, tmp_comm) {
if(comm->is_federation) {
continue;
}
HASH_ITER(hh, comm->allowed_users, user, tmp_user) {
// calculate common shared secret (ECDH)
generate_shared_secret(user->shared_secret, sss_node.private_key, user->public_key);
// prepare for use as key
user->shared_secret_ctx = (he_context_t*)calloc(1, sizeof(speck_context_t));
speck_init((speck_context_t**)&user->shared_secret_ctx, user->shared_secret, 128);
}
}
traceEvent(TRACE_NORMAL, "calculated shared secrets for edge authentication");
traceEvent(TRACE_DEBUG, "traceLevel is %d", getTraceLevel()); traceEvent(TRACE_DEBUG, "traceLevel is %d", getTraceLevel());
sss_node.sock = open_socket(sss_node.lport, 1 /*bind ANY*/, 0 /* UDP */); sss_node.sock = open_socket(sss_node.lport, 1 /*bind ANY*/, 0 /* UDP */);

View File

@ -397,7 +397,11 @@ int sn_init(n2n_sn_t *sss) {
/* header encryption enabled by default */ /* header encryption enabled by default */
sss->federation->header_encryption = HEADER_ENCRYPTION_ENABLED; sss->federation->header_encryption = HEADER_ENCRYPTION_ENABLED;
/*setup the encryption key */ /*setup the encryption key */
packet_header_setup_key(sss->federation->community, &(sss->federation->header_encryption_ctx), &(sss->federation->header_iv_ctx)); packet_header_setup_key(sss->federation->community,
&(sss->federation->header_encryption_ctx_static),
&(sss->federation->header_encryption_ctx_dynamic),
&(sss->federation->header_iv_ctx_static),
&(sss->federation->header_iv_ctx_dynamic));
sss->federation->edges = NULL; sss->federation->edges = NULL;
} }
@ -405,8 +409,8 @@ int sn_init(n2n_sn_t *sss) {
/* Random auth token */ /* Random auth token */
sss->auth.scheme = n2n_auth_simple_id; sss->auth.scheme = n2n_auth_simple_id;
memrnd(sss->auth.token, N2N_AUTH_TOKEN_SIZE); memrnd(sss->auth.token, N2N_AUTH_ID_TOKEN_SIZE);
sss->auth.toksize = sizeof(sss->auth.token); sss->auth.token_size = N2N_AUTH_ID_TOKEN_SIZE;
/* Random MAC address */ /* Random MAC address */
memrnd(sss->mac_addr, N2N_MAC_SIZE); memrnd(sss->mac_addr, N2N_MAC_SIZE);
@ -452,8 +456,9 @@ void sn_term (n2n_sn_t *sss) {
HASH_ITER(hh, sss->communities, community, tmp) { HASH_ITER(hh, sss->communities, community, tmp) {
clear_peer_list(&community->edges); clear_peer_list(&community->edges);
if(NULL != community->header_encryption_ctx) { if(NULL != community->header_encryption_ctx_static) {
free(community->header_encryption_ctx); free(community->header_encryption_ctx_static);
free(community->header_encryption_ctx_dynamic);
} }
// remove all associations // remove all associations
HASH_ITER(hh, community->assoc, assoc, tmp_assoc) { HASH_ITER(hh, community->assoc, assoc, tmp_assoc) {
@ -515,23 +520,51 @@ static uint16_t reg_lifetime (n2n_sn_t *sss) {
} }
/** Compare two authentication tokens. It is called by update_edge /** Verifies authentication tokens from known edges.
* and in UNREGISTER_SUPER handling to compare the stored auth token *
* with the one received from the packet. * It is called by update_edge and during UNREGISTER_SUPER handling
*/ * to verify the stored auth token.
static int auth_edge (const n2n_auth_t *auth1, const n2n_auth_t *auth2, n2n_auth_t *answer_auth) { */
static int auth_edge (const n2n_auth_t *present, const n2n_auth_t *presented, n2n_auth_t *answer, struct sn_community *community) {
if((auth1->scheme == n2n_auth_simple_id) && (auth2->scheme == n2n_auth_simple_id)) { sn_user_t *user = NULL;
if((present->scheme == n2n_auth_simple_id) && (presented->scheme == n2n_auth_simple_id)) {
// n2n_auth_simple_id scheme: if required, zero_token answer (not for NAK) // n2n_auth_simple_id scheme: if required, zero_token answer (not for NAK)
if(answer_auth) if(answer)
memset(answer_auth, 0, sizeof(n2n_auth_t)); memset(answer, 0, sizeof(n2n_auth_t));
// 0 = success (tokens are equal) // 0 = success (tokens are equal)
return (memcmp(auth1, auth2, sizeof(n2n_auth_t))); return (memcmp(present, presented, sizeof(n2n_auth_t)));
} }
// if not successful earlier: failure if((present->scheme == n2n_auth_user_password) && (presented->scheme == n2n_auth_user_password)) {
return -1; // check if submitted public key is in list of allowed users
HASH_FIND(hh, community->allowed_users, &presented->token, sizeof(n2n_private_public_key_t), user);
if(user) {
if(answer) {
memcpy(answer, presented, sizeof(n2n_auth_t));
// return a double-encrypted challenge (just encrypt again) in the (first half of) public key field so edge can verify
memcpy(answer->token, answer->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, N2N_AUTH_CHALLENGE_SIZE);
speck_128_encrypt(answer->token, (speck_context_t*)user->shared_secret_ctx);
// decrypt the challenge using user's shared secret
speck_128_decrypt(answer->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, (speck_context_t*)user->shared_secret_ctx);
// xor-in the community dynamic key
memxor(answer->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, community->dynamic_key, N2N_AUTH_CHALLENGE_SIZE);
// xor-in the user's shared secret
memxor(answer->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, user->shared_secret, N2N_AUTH_CHALLENGE_SIZE);
// encrypt it using user's shared secret
speck_128_encrypt(answer->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, (speck_context_t*)user->shared_secret_ctx);
// user in list? success! (we will see if edge can handle the key for further com)
}
return 0;
}
}
// if not successful earlier: failure
return -1;
} }
@ -546,18 +579,53 @@ static int get_local_auth (n2n_sn_t *sss, n2n_auth_t *auth) {
} }
// handles an incoming (remote) auth token, takes action as required by auth scheme, and // handles an incoming (remote) auth token from a so far unknown edge,
// takes action as required by auth scheme, and
// could provide an answer auth token for use in REGISTER_SUPER_ACK // could provide an answer auth token for use in REGISTER_SUPER_ACK
// REVISIT: behavior should depend on some local auth scheme setting (to be implemented) static int handle_remote_auth (n2n_sn_t *sss, const n2n_auth_t *remote_auth,
static int handle_remote_auth (n2n_sn_t *sss, struct peer_info *peer, const n2n_auth_t *remote_auth, n2n_auth_t *answer_auth,
n2n_auth_t *answer_auth) { struct sn_community *community) {
// n2n_auth_simple_id scheme: store the arrived token sn_user_t *user = NULL;
memcpy(&(peer->auth), remote_auth, sizeof(n2n_auth_t));
// n2n_auth_simple_id scheme: zero_token answer
memset(answer_auth, 0, sizeof(n2n_auth_t));
return 0; if((NULL == community->allowed_users) != (remote_auth->scheme != n2n_auth_user_password)) {
// received token's scheme does not match expected scheme
return -1;
}
switch(remote_auth->scheme) {
case n2n_auth_simple_id:
// zero_token answer
memset(answer_auth, 0, sizeof(n2n_auth_t));
return 0;
case n2n_auth_user_password:
// check if submitted public key is in list of allowed users
HASH_FIND(hh, community->allowed_users, &remote_auth->token, sizeof(n2n_private_public_key_t), user);
if(user) {
memcpy(answer_auth, remote_auth, sizeof(n2n_auth_t));
// return a double-encrypted challenge (just encrypt again) in the (first half of) public key field so edge can verify
memcpy(answer_auth->token, answer_auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, N2N_AUTH_CHALLENGE_SIZE);
speck_128_encrypt(answer_auth->token, (speck_context_t*)user->shared_secret_ctx);
// wrap dynamic key for transmission
// decrypt the challenge using user's shared secret
speck_128_decrypt(answer_auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, (speck_context_t*)user->shared_secret_ctx);
// xor-in the community dynamic key
memxor(answer_auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, community->dynamic_key, N2N_AUTH_CHALLENGE_SIZE);
// xor-in the user's shared secret
memxor(answer_auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, user->shared_secret, N2N_AUTH_CHALLENGE_SIZE);
// encrypt it using user's shared secret
speck_128_encrypt(answer_auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, (speck_context_t*)user->shared_secret_ctx);
return 0;
}
break;
default:
break;
}
// if not successful earlier: failure
return -1;
} }
@ -575,7 +643,6 @@ static int update_edge (n2n_sn_t *sss,
macstr_t mac_buf; macstr_t mac_buf;
n2n_sock_str_t sockbuf; n2n_sock_str_t sockbuf;
struct peer_info *scan, *iter, *tmp; struct peer_info *scan, *iter, *tmp;
int auth;
int ret; int ret;
traceEvent(TRACE_DEBUG, "update_edge for %s [%s]", traceEvent(TRACE_DEBUG, "update_edge for %s [%s]",
@ -599,29 +666,35 @@ static int update_edge (n2n_sn_t *sss,
if(NULL == scan) { if(NULL == scan) {
/* Not known */ /* Not known */
if(skip_add == SN_ADD) { if(handle_remote_auth(sss, &(reg->auth), answer_auth, comm) == 0) {
scan = (struct peer_info *) calloc(1, sizeof(struct peer_info)); /* deallocated in purge_expired_nodes */ if(skip_add == SN_ADD) {
memcpy(&(scan->mac_addr), reg->edgeMac, sizeof(n2n_mac_t)); scan = (struct peer_info *) calloc(1, sizeof(struct peer_info)); /* deallocated in purge_expired_nodes */
scan->dev_addr.net_addr = reg->dev_addr.net_addr; memcpy(&(scan->mac_addr), reg->edgeMac, sizeof(n2n_mac_t));
scan->dev_addr.net_bitlen = reg->dev_addr.net_bitlen; scan->dev_addr.net_addr = reg->dev_addr.net_addr;
memcpy((char*)scan->dev_desc, reg->dev_desc, N2N_DESC_SIZE); scan->dev_addr.net_bitlen = reg->dev_addr.net_bitlen;
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t)); memcpy((char*)scan->dev_desc, reg->dev_desc, N2N_DESC_SIZE);
scan->socket_fd = socket_fd; memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t));
memcpy(&(scan->last_cookie), reg->cookie, sizeof(N2N_COOKIE_SIZE)); scan->socket_fd = socket_fd;
handle_remote_auth(sss, scan, &(reg->auth), answer_auth); memcpy(&(scan->last_cookie), reg->cookie, sizeof(N2N_COOKIE_SIZE));
scan->last_valid_time_stamp = initial_time_stamp(); scan->last_valid_time_stamp = initial_time_stamp();
HASH_ADD_PEER(comm->edges, scan); memcpy(&(scan->auth), &(reg->auth), sizeof(n2n_auth_t));
traceEvent(TRACE_INFO, "update_edge created %s ==> %s", HASH_ADD_PEER(comm->edges, scan);
macaddr_str(mac_buf, reg->edgeMac),
sock_to_cstr(sockbuf, sender_sock)); traceEvent(TRACE_INFO, "update_edge created %s ==> %s",
macaddr_str(mac_buf, reg->edgeMac),
sock_to_cstr(sockbuf, sender_sock));
}
ret = update_edge_new_sn;
} else {
traceEvent(TRACE_INFO, "authentication failed");
ret = update_edge_auth_fail;
} }
ret = update_edge_new_sn;
} else { } else {
/* Known */ /* Known */
if(!sock_equal(sender_sock, &(scan->sock))) { if(auth_edge(&(scan->auth), &(reg->auth), answer_auth, comm) == 0) {
if((auth = auth_edge(&(scan->auth), &(reg->auth), answer_auth)) == 0) { if(!sock_equal(sender_sock, &(scan->sock))) {
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t)); memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t));
scan->socket_fd = socket_fd; scan->socket_fd = socket_fd;
memcpy(&(scan->last_cookie), reg->cookie, sizeof(N2N_COOKIE_SIZE)); memcpy(&(scan->last_cookie), reg->cookie, sizeof(N2N_COOKIE_SIZE));
@ -631,18 +704,17 @@ static int update_edge (n2n_sn_t *sss,
sock_to_cstr(sockbuf, sender_sock)); sock_to_cstr(sockbuf, sender_sock));
ret = update_edge_sock_change; ret = update_edge_sock_change;
} else { } else {
traceEvent(TRACE_INFO, "authentication failed"); memcpy(&(scan->last_cookie), reg->cookie, sizeof(N2N_COOKIE_SIZE));
ret = update_edge_auth_fail; traceEvent(TRACE_DEBUG, "update_edge unchanged %s ==> %s",
macaddr_str(mac_buf, reg->edgeMac),
sock_to_cstr(sockbuf, sender_sock));
ret = update_edge_no_change;
} }
} else { } else {
memcpy(&(scan->last_cookie), reg->cookie, sizeof(N2N_COOKIE_SIZE)); traceEvent(TRACE_INFO, "authentication failed");
ret = update_edge_auth_fail;
traceEvent(TRACE_DEBUG, "update_edge unchanged %s ==> %s",
macaddr_str(mac_buf, reg->edgeMac),
sock_to_cstr(sockbuf, sender_sock));
ret = update_edge_no_change;
} }
} }
@ -904,7 +976,7 @@ static int re_register_and_purge_supernodes (n2n_sn_t *sss, struct sn_community
sock_to_cstr(sockbuf, &(peer->sock))); sock_to_cstr(sockbuf, &(peer->sock)));
packet_header_encrypt(pktbuf, idx, idx, packet_header_encrypt(pktbuf, idx, idx,
comm->header_encryption_ctx, comm->header_iv_ctx, comm->header_encryption_ctx_static, comm->header_iv_ctx_static,
time_stamp()); time_stamp());
/* sent = */ sendto_peer(sss, peer, pktbuf, idx); /* sent = */ sendto_peer(sss, peer, pktbuf, idx);
@ -949,9 +1021,10 @@ static int purge_expired_communities (n2n_sn_t *sss,
if((comm->edges == NULL) && (comm->purgeable == COMMUNITY_PURGEABLE)) { if((comm->edges == NULL) && (comm->purgeable == COMMUNITY_PURGEABLE)) {
traceEvent(TRACE_INFO, "Purging idle community %s", comm->community); traceEvent(TRACE_INFO, "Purging idle community %s", comm->community);
if(NULL != comm->header_encryption_ctx) { if(NULL != comm->header_encryption_ctx_static) {
/* this should not happen as 'purgeable' and thus only communities w/o encrypted header here */ /* this should not happen as 'purgeable' and thus only communities w/o encrypted header here */
free(comm->header_encryption_ctx); free(comm->header_encryption_ctx_static);
free(comm->header_encryption_ctx_dynamic);
} }
// remove all associations // remove all associations
HASH_ITER(hh, comm->assoc, assoc, tmp_assoc) { HASH_ITER(hh, comm->assoc, assoc, tmp_assoc) {
@ -1148,6 +1221,7 @@ static int process_udp (n2n_sn_t * sss,
n2n_sock_str_t sockbuf; n2n_sock_str_t sockbuf;
char buf[32]; char buf[32];
struct sn_community *comm, *tmp; struct sn_community *comm, *tmp;
uint32_t header_enc = 0; /* 1 == encrypted by static key, 2 == encrypted by dynamic key */
uint64_t stamp; uint64_t stamp;
int skip_add; int skip_add;
@ -1183,22 +1257,31 @@ static int process_udp (n2n_sn_t * sss,
"unencrypted headers.", comm->community); "unencrypted headers.", comm->community);
/* set 'no encryption' in case it is not set yet */ /* set 'no encryption' in case it is not set yet */
comm->header_encryption = HEADER_ENCRYPTION_NONE; comm->header_encryption = HEADER_ENCRYPTION_NONE;
comm->header_encryption_ctx = NULL; comm->header_encryption_ctx_static = NULL;
comm->header_encryption_ctx_dynamic = NULL;
} }
} }
} else { } else {
/* most probably encrypted */ /* most probably encrypted */
/* cycle through the known communities (as keys) to eventually decrypt */ /* cycle through the known communities (as keys) to eventually decrypt */
uint32_t ret = 0;
HASH_ITER(hh, sss->communities, comm, tmp) { HASH_ITER(hh, sss->communities, comm, tmp) {
/* skip the definitely unencrypted communities */ /* skip the definitely unencrypted communities */
if(comm->header_encryption == HEADER_ENCRYPTION_NONE) { if(comm->header_encryption == HEADER_ENCRYPTION_NONE) {
continue; continue;
} }
if((ret = packet_header_decrypt(udp_buf, udp_size, // match with static (1) or dynamic (2) ctx?
comm->community, header_enc = packet_header_decrypt(udp_buf, udp_size,
comm->header_encryption_ctx, comm->header_iv_ctx, comm->community,
&stamp))) { comm->header_encryption_ctx_static, comm->header_iv_ctx_static,
&stamp);
if(!header_enc)
if(packet_header_decrypt(udp_buf, udp_size,
comm->community,
comm->header_encryption_ctx_dynamic, comm->header_iv_ctx_dynamic,
&stamp))
header_enc = 2;
if(header_enc) {
// time stamp verification follows in the packet specific section as it requires to determine the // time stamp verification follows in the packet specific section as it requires to determine the
// sender from the hash list by its MAC, this all depends on packet type and packet structure // sender from the hash list by its MAC, this all depends on packet type and packet structure
// (MAC is not always in the same place) // (MAC is not always in the same place)
@ -1216,7 +1299,7 @@ static int process_udp (n2n_sn_t * sss,
break; break;
} }
} }
if(!ret) { if(!header_enc) {
// no matching key/community // no matching key/community
traceEvent(TRACE_DEBUG, "process_udp dropped a packet with seemingly encrypted header " traceEvent(TRACE_DEBUG, "process_udp dropped a packet with seemingly encrypted header "
"for which no matching community which uses encrypted headers was found."); "for which no matching community which uses encrypted headers was found.");
@ -1242,11 +1325,23 @@ static int process_udp (n2n_sn_t * sss,
msg_type = cmn.pc; /* packet code */ msg_type = cmn.pc; /* packet code */
// special case for user/pw auth
// community's auth scheme and message type need to match the used key (dynamic)
if(comm) {
if((comm->allowed_users)
&& (msg_type != MSG_TYPE_REGISTER_SUPER)
&& (msg_type != MSG_TYPE_REGISTER_SUPER_ACK)
&& (msg_type != MSG_TYPE_REGISTER_SUPER_NAK)) {
if(header_enc != 2) {
traceEvent(TRACE_WARNING, "process_udp dropped packet encrypted with static key where expecting dynamic key.");
return -1;
}
}
}
/* REVISIT: when UDP/IPv6 is supported we will need a flag to indicate which /* REVISIT: when UDP/IPv6 is supported we will need a flag to indicate which
* IP transport version the packet arrived on. May need to UDP sockets. */ * IP transport version the packet arrived on. May need to UDP sockets. */
memset(&sender, 0, sizeof(n2n_sock_t)); memset(&sender, 0, sizeof(n2n_sock_t));
sender.family = AF_INET; /* UDP socket was opened PF_INET v4 */ sender.family = AF_INET; /* UDP socket was opened PF_INET v4 */
sender.port = ntohs(sender_sock->sin_port); sender.port = ntohs(sender_sock->sin_port);
memcpy(&(sender.addr.v4), &(sender_sock->sin_addr.s_addr), IPV4_SIZE); memcpy(&(sender.addr.v4), &(sender_sock->sin_addr.s_addr), IPV4_SIZE);
@ -1319,14 +1414,16 @@ static int process_udp (n2n_sn_t * sss,
rec_buf = encbuf; rec_buf = encbuf;
/* Re-encode the header. */ /* Re-encode the header. */
encode_PACKET(encbuf, &encx, &cmn2, &pkt); encode_PACKET(encbuf, &encx, &cmn2, &pkt);
uint16_t oldEncx = encx; uint16_t oldEncx = encx;
/* Copy the original payload unchanged */ /* Copy the original payload unchanged */
encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx)); encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx));
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(rec_buf, oldEncx, encx, // in case of user-password auth, also encrypt the iv of payload assuming ChaCha20 and SPECK having the same iv size
comm->header_encryption_ctx, comm->header_iv_ctx, packet_header_encrypt(rec_buf, oldEncx + (NULL != comm->allowed_users) * min(encx - oldEncx, N2N_SPECK_IVEC_SIZE), encx,
comm->header_encryption_ctx_dynamic, comm->header_iv_ctx_dynamic,
time_stamp()); time_stamp());
} }
} else { } else {
@ -1339,8 +1436,9 @@ static int process_udp (n2n_sn_t * sss,
encx = udp_size; encx = udp_size;
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(rec_buf, idx, encx, // in case of user-password auth, also encrypt the iv of payload assuming ChaCha20 and SPECK having the same iv size
comm->header_encryption_ctx, comm->header_iv_ctx, packet_header_encrypt(rec_buf, idx + (NULL != comm->allowed_users) * min(encx - idx, N2N_SPECK_IVEC_SIZE), encx,
comm->header_encryption_ctx_dynamic, comm->header_iv_ctx_dynamic,
time_stamp()); time_stamp());
} }
} }
@ -1412,7 +1510,7 @@ static int process_udp (n2n_sn_t * sss,
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(rec_buf, encx, encx, packet_header_encrypt(rec_buf, encx, encx,
comm->header_encryption_ctx, comm->header_iv_ctx, comm->header_encryption_ctx_dynamic, comm->header_iv_ctx_dynamic,
time_stamp()); time_stamp());
} }
try_forward(sss, comm, &cmn, reg.dstMac, from_supernode, rec_buf, encx); /* unicast only */ try_forward(sss, comm, &cmn, reg.dstMac, from_supernode, rec_buf, encx); /* unicast only */
@ -1495,7 +1593,8 @@ static int process_udp (n2n_sn_t * sss,
comm_init(comm, (char *)cmn.community); comm_init(comm, (char *)cmn.community);
/* new communities introduced by REGISTERs could not have had encrypted header... */ /* new communities introduced by REGISTERs could not have had encrypted header... */
comm->header_encryption = HEADER_ENCRYPTION_NONE; comm->header_encryption = HEADER_ENCRYPTION_NONE;
comm->header_encryption_ctx = NULL; comm->header_encryption_ctx_static = NULL;
comm->header_encryption_ctx_dynamic = NULL;
/* ... and also are purgeable during periodic purge */ /* ... and also are purgeable during periodic purge */
comm->purgeable = COMMUNITY_PURGEABLE; comm->purgeable = COMMUNITY_PURGEABLE;
comm->number_enc_packets = 0; comm->number_enc_packets = 0;
@ -1527,6 +1626,7 @@ static int process_udp (n2n_sn_t * sss,
ack.dev_addr.net_addr = ipaddr.net_addr; ack.dev_addr.net_addr = ipaddr.net_addr;
ack.dev_addr.net_bitlen = ipaddr.net_bitlen; ack.dev_addr.net_bitlen = ipaddr.net_bitlen;
} }
ack.lifetime = reg_lifetime(sss); ack.lifetime = reg_lifetime(sss);
ack.sock.family = AF_INET; ack.sock.family = AF_INET;
@ -1554,7 +1654,7 @@ static int process_udp (n2n_sn_t * sss,
continue; continue;
} }
if(memcmp(&(peer->sock), &(ack.sock), sizeof(n2n_sock_t)) == 0) continue; /* a supernode doesn't add itself to the payload */ if(memcmp(&(peer->sock), &(ack.sock), sizeof(n2n_sock_t)) == 0) continue; /* a supernode doesn't add itself to the payload */
if((now - peer->last_seen) >= LAST_SEEN_SN_NEW) continue; /* skip long-time-not-seen supernodes. if((now - peer->last_seen) >= LAST_SEEN_SN_NEW) continue; /* skip long-time-not-seen supernodes.
* We need to allow for a little extra time because supernodes sometimes exceed * We need to allow for a little extra time because supernodes sometimes exceed
* their SN_ACTIVE time before they get re-registred to. */ * their SN_ACTIVE time before they get re-registred to. */
if(((++num)*REG_SUPER_ACK_PAYLOAD_ENTRY_SIZE) > REG_SUPER_ACK_PAYLOAD_SPACE) break; /* no more space available in REGISTER_SUPER_ACK payload */ if(((++num)*REG_SUPER_ACK_PAYLOAD_ENTRY_SIZE) > REG_SUPER_ACK_PAYLOAD_SPACE) break; /* no more space available in REGISTER_SUPER_ACK payload */
@ -1570,23 +1670,23 @@ static int process_udp (n2n_sn_t * sss,
sock_to_cstr(sockbuf, &(ack.sock))); sock_to_cstr(sockbuf, &(ack.sock)));
ret_value = update_edge_no_change; ret_value = update_edge_no_change;
if(!is_null_mac(reg.edgeMac)) { if(cmn.flags & N2N_FLAGS_SOCKET) {
if(cmn.flags & N2N_FLAGS_SOCKET) { ret_value = update_edge(sss, &reg, comm, &(ack.sock), socket_fd, &(ack.auth), SN_ADD_SKIP, now);
ret_value = update_edge(sss, &reg, comm, &(ack.sock), socket_fd, &(ack.auth), SN_ADD_SKIP, now); } else {
} else { // do not add in case of null mac (edge asking for ip address)
ret_value = update_edge(sss, &reg, comm, &(ack.sock), socket_fd, &(ack.auth), SN_ADD, now); ret_value = update_edge(sss, &reg, comm, &(ack.sock), socket_fd, &(ack.auth), is_null_mac(reg.edgeMac) ? SN_ADD_SKIP : SN_ADD, now);
}
} }
if(ret_value == update_edge_auth_fail) { if(ret_value == update_edge_auth_fail) {
cmn2.pc = n2n_register_super_nak; cmn2.pc = n2n_register_super_nak;
memcpy(&(nak.cookie), &(reg.cookie), sizeof(n2n_cookie_t)); memcpy(&(nak.cookie), &(reg.cookie), sizeof(n2n_cookie_t));
memcpy(nak.srcMac, reg.edgeMac, sizeof(n2n_mac_t)); memcpy(nak.srcMac, reg.edgeMac, sizeof(n2n_mac_t));
encode_REGISTER_SUPER_NAK(ackbuf, &encx, &cmn2, &nak); encode_REGISTER_SUPER_NAK(ackbuf, &encx, &cmn2, &nak);
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(ackbuf, encx, encx, packet_header_encrypt(ackbuf, encx, encx,
comm->header_encryption_ctx, comm->header_iv_ctx, comm->header_encryption_ctx_static, comm->header_iv_ctx_static,
time_stamp()); time_stamp());
} }
sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, ackbuf, encx); sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, ackbuf, encx);
@ -1609,7 +1709,7 @@ static int process_udp (n2n_sn_t * sss,
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(ackbuf, encx, encx, packet_header_encrypt(ackbuf, encx, encx,
comm->header_encryption_ctx, comm->header_iv_ctx, comm->header_encryption_ctx_static, comm->header_iv_ctx_static,
time_stamp()); time_stamp());
} }
@ -1624,7 +1724,7 @@ static int process_udp (n2n_sn_t * sss,
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(ackbuf, encx, encx, packet_header_encrypt(ackbuf, encx, encx,
comm->header_encryption_ctx, comm->header_iv_ctx, comm->header_encryption_ctx_static, comm->header_iv_ctx_static,
time_stamp()); time_stamp());
} }
@ -1687,7 +1787,7 @@ static int process_udp (n2n_sn_t * sss,
HASH_FIND_PEER(comm->edges, unreg.srcMac, peer); HASH_FIND_PEER(comm->edges, unreg.srcMac, peer);
if(peer != NULL) { if(peer != NULL) {
if((auth = auth_edge(&(peer->auth), &unreg.auth, NULL)) == 0) { if((auth = auth_edge(&(peer->auth), &unreg.auth, NULL, comm)) == 0) {
if((peer->socket_fd != sss->sock) && (peer->socket_fd >= 0)) { if((peer->socket_fd != sss->sock) && (peer->socket_fd >= 0)) {
n2n_tcp_connection_t *conn; n2n_tcp_connection_t *conn;
HASH_FIND_INT(sss->tcp_connections, &(peer->socket_fd), conn); HASH_FIND_INT(sss->tcp_connections, &(peer->socket_fd), conn);
@ -1698,7 +1798,6 @@ static int process_udp (n2n_sn_t * sss,
} }
} }
} }
break; break;
} }
@ -1816,16 +1915,16 @@ static int process_udp (n2n_sn_t * sss,
if(comm->is_federation == IS_NO_FEDERATION) { if(comm->is_federation == IS_NO_FEDERATION) {
if(peer != NULL) { if(peer != NULL) {
// this is a NAK for one of the edges conencted to this supernode, forward, // this is a NAK for one of the edges conencted to this supernode, forward,
// i.e. re-assemble (memcpy of udpbuf to nakbuf could be sufficient as well) // i.e. re-assemble (memcpy from udpbuf to nakbuf could be sufficient as well)
// use incoming cmn (with already decreased TTL) // use incoming cmn (with already decreased TTL)
// NAK (cookie and srcMac) remains unchanged // NAK (cookie, srcMac, auth) remains unchanged
encode_REGISTER_SUPER_NAK(nakbuf, &encx, &cmn, &nak); encode_REGISTER_SUPER_NAK(nakbuf, &encx, &cmn, &nak);
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(nakbuf, encx, encx, packet_header_encrypt(nakbuf, encx, encx,
comm->header_encryption_ctx, comm->header_iv_ctx, comm->header_encryption_ctx_static, comm->header_iv_ctx_static,
time_stamp()); time_stamp());
} }
@ -1880,8 +1979,8 @@ static int process_udp (n2n_sn_t * sss,
decode_QUERY_PEER( &query, &cmn, udp_buf, &rem, &idx ); decode_QUERY_PEER( &query, &cmn, udp_buf, &rem, &idx );
// to answer a PING, it is sufficient if the provided communtiy would be a valid one, there does not // to answer a PING, it is sufficient if the provided communtiy would be a valid one, there does not
// neccessarily need to be an entry present, e.g. because there locally are no edges of the community // neccessarily need to be a comm entry present, e.g. because there locally are no edges of the
// connected (several sueprnodes in a federation setup) // community connected (several supernodes in a federation setup)
if(comm) { if(comm) {
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
if(!find_edge_time_stamp_and_verify(comm->edges, sn, query.srcMac, stamp, TIME_STAMP_ALLOW_JITTER)) { if(!find_edge_time_stamp_and_verify(comm->edges, sn, query.srcMac, stamp, TIME_STAMP_ALLOW_JITTER)) {
@ -1912,8 +2011,8 @@ static int process_udp (n2n_sn_t * sss,
if(comm) { if(comm) {
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(encbuf, encx, encx, comm->header_encryption_ctx, packet_header_encrypt(encbuf, encx, encx, comm->header_encryption_ctx_dynamic,
comm->header_iv_ctx, comm->header_iv_ctx_dynamic,
time_stamp()); time_stamp());
} }
} }
@ -1951,8 +2050,8 @@ static int process_udp (n2n_sn_t * sss,
encode_PEER_INFO(encbuf, &encx, &cmn2, &pi); encode_PEER_INFO(encbuf, &encx, &cmn2, &pi);
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(encbuf, encx, encx, comm->header_encryption_ctx, packet_header_encrypt(encbuf, encx, encx, comm->header_encryption_ctx_dynamic,
comm->header_iv_ctx, comm->header_iv_ctx_dynamic,
time_stamp()); time_stamp());
} }
// back to sender, be it edge or supernode (which will forward to edge) // back to sender, be it edge or supernode (which will forward to edge)
@ -1976,8 +2075,8 @@ static int process_udp (n2n_sn_t * sss,
encode_QUERY_PEER(encbuf, &encx, &cmn2, &query); encode_QUERY_PEER(encbuf, &encx, &cmn2, &query);
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(encbuf, encx, encx, comm->header_encryption_ctx, packet_header_encrypt(encbuf, encx, encx, comm->header_encryption_ctx_dynamic,
comm->header_iv_ctx, comm->header_iv_ctx_dynamic,
time_stamp()); time_stamp());
} }
@ -2028,7 +2127,7 @@ static int process_udp (n2n_sn_t * sss,
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) {
packet_header_encrypt(encbuf, encx, encx, packet_header_encrypt(encbuf, encx, encx,
comm->header_encryption_ctx, comm->header_iv_ctx, comm->header_encryption_ctx_dynamic, comm->header_iv_ctx_dynamic,
time_stamp()); time_stamp());
} }

View File

@ -981,14 +981,14 @@ int speck_deinit (speck_context_t *ctx) {
// cipher SPECK -- 128 bit block size -- 128 bit key size -- ECB mode (decrypt only) // cipher SPECK -- 128 bit block size -- 128 bit key size -- ECB mode (decrypt only)
// follows endianess rules as used in official implementation guide and NOT as in original 2013 cipher presentation // follows endianess rules as used in official implementation guide and NOT as in original 2013 cipher presentation
// used for IV in header encryption (one block); encrytion via speck_ctr with null_block as data // used for IV in header encryption (one block) and challenge encryption (user/password)
// for now: just plain C -- probably no need for AVX, SSE, NEON // for now: just plain C -- probably no need for AVX, SSE, NEON
#define ROTL64(x,r) (((x)<<(r))|((x)>>(64-(r)))) #define ROTL64(x,r) (((x)<<(r))|((x)>>(64-(r))))
#define ROTR64(x,r) (((x)>>(r))|((x)<<(64-(r)))) #define ROTR64(x,r) (((x)>>(r))|((x)<<(64-(r))))
#define DR128(x,y,k) (y^=x, y=ROTR64(y,3), x^=k, x-=y, x=ROTL64(x,8)) #define DR128(x,y,k) (y^=x, y=ROTR64(y,3), x^=k, x-=y, x=ROTL64(x,8))
#define ER128(x,y,k) (x=(ROTR64(x,8)+y)^k, y=ROTL64(y,3)^x)
int speck_128_decrypt (unsigned char *inout, speck_context_t *ctx) { int speck_128_decrypt (unsigned char *inout, speck_context_t *ctx) {
@ -1006,3 +1006,21 @@ int speck_128_decrypt (unsigned char *inout, speck_context_t *ctx) {
return 0; return 0;
} }
int speck_128_encrypt (unsigned char *inout, speck_context_t *ctx) {
u64 x, y;
int i;
x = le64toh( *(u64*)&inout[8] );
y = le64toh( *(u64*)&inout[0] );
for(i = 0; i < 32; i++)
ER128(x, y, ctx->key[i]);
((u64*)inout)[1] = htole64(x);
((u64*)inout)[0] = htole64(y);
return 0;
}

View File

@ -354,8 +354,8 @@ int encode_REGISTER_SUPER (uint8_t *base,
retval += encode_uint8(base, idx, reg->dev_addr.net_bitlen); retval += encode_uint8(base, idx, reg->dev_addr.net_bitlen);
retval += encode_buf(base, idx, reg->dev_desc, N2N_DESC_SIZE); retval += encode_buf(base, idx, reg->dev_desc, N2N_DESC_SIZE);
retval += encode_uint16(base, idx, reg->auth.scheme); retval += encode_uint16(base, idx, reg->auth.scheme);
retval += encode_uint16(base, idx, reg->auth.toksize); retval += encode_uint16(base, idx, reg->auth.token_size);
retval += encode_buf(base, idx, reg->auth.token, reg->auth.toksize); retval += encode_buf(base, idx, reg->auth.token, reg->auth.token_size);
return retval; return retval;
} }
@ -379,8 +379,8 @@ int decode_REGISTER_SUPER (n2n_REGISTER_SUPER_t *reg,
retval += decode_uint8(&(reg->dev_addr.net_bitlen), base, rem, idx); retval += decode_uint8(&(reg->dev_addr.net_bitlen), base, rem, idx);
retval += decode_buf(reg->dev_desc, N2N_DESC_SIZE, base, rem, idx); retval += decode_buf(reg->dev_desc, N2N_DESC_SIZE, base, rem, idx);
retval += decode_uint16(&(reg->auth.scheme), base, rem, idx); retval += decode_uint16(&(reg->auth.scheme), base, rem, idx);
retval += decode_uint16(&(reg->auth.toksize), base, rem, idx); retval += decode_uint16(&(reg->auth.token_size), base, rem, idx);
retval += decode_buf(reg->auth.token, reg->auth.toksize, base, rem, idx); retval += decode_buf(reg->auth.token, reg->auth.token_size, base, rem, idx);
return retval; return retval;
} }
@ -395,8 +395,8 @@ int encode_UNREGISTER_SUPER (uint8_t *base,
retval += encode_common(base, idx, common); retval += encode_common(base, idx, common);
retval += encode_uint16(base, idx, unreg->auth.scheme); retval += encode_uint16(base, idx, unreg->auth.scheme);
retval += encode_uint16(base, idx, unreg->auth.toksize); retval += encode_uint16(base, idx, unreg->auth.token_size);
retval += encode_buf(base, idx, unreg->auth.token, unreg->auth.toksize); retval += encode_buf(base, idx, unreg->auth.token, unreg->auth.token_size);
retval += encode_mac(base, idx, unreg->srcMac); retval += encode_mac(base, idx, unreg->srcMac);
return retval; return retval;
@ -413,8 +413,8 @@ int decode_UNREGISTER_SUPER (n2n_UNREGISTER_SUPER_t *unreg,
memset(unreg, 0, sizeof(n2n_UNREGISTER_SUPER_t)); memset(unreg, 0, sizeof(n2n_UNREGISTER_SUPER_t));
retval += decode_uint16(&(unreg->auth.scheme), base, rem, idx); retval += decode_uint16(&(unreg->auth.scheme), base, rem, idx);
retval += decode_uint16(&(unreg->auth.toksize), base, rem, idx); retval += decode_uint16(&(unreg->auth.token_size), base, rem, idx);
retval += decode_buf(unreg->auth.token, unreg->auth.toksize, base, rem, idx); retval += decode_buf(unreg->auth.token, unreg->auth.token_size, base, rem, idx);
retval += decode_mac(unreg->srcMac, base, rem, idx); retval += decode_mac(unreg->srcMac, base, rem, idx);
return retval; return retval;
@ -482,7 +482,13 @@ int encode_REGISTER_SUPER_ACK (uint8_t *base,
retval += encode_uint32(base, idx, reg->dev_addr.net_addr); retval += encode_uint32(base, idx, reg->dev_addr.net_addr);
retval += encode_uint8(base, idx, reg->dev_addr.net_bitlen); retval += encode_uint8(base, idx, reg->dev_addr.net_bitlen);
retval += encode_uint16(base, idx, reg->lifetime); retval += encode_uint16(base, idx, reg->lifetime);
retval += encode_sock(base, idx, &(reg->sock)); retval += encode_sock(base, idx, &(reg->sock));
retval += encode_uint16(base, idx, reg->auth.scheme);
retval += encode_uint16(base, idx, reg->auth.token_size);
retval += encode_buf(base, idx, reg->auth.token, reg->auth.token_size);
retval += encode_uint8(base, idx, reg->num_sn); retval += encode_uint8(base, idx, reg->num_sn);
retval += encode_buf(base, idx, tmpbuf, (reg->num_sn*REG_SUPER_ACK_PAYLOAD_ENTRY_SIZE)); retval += encode_buf(base, idx, tmpbuf, (reg->num_sn*REG_SUPER_ACK_PAYLOAD_ENTRY_SIZE));
@ -509,6 +515,10 @@ int decode_REGISTER_SUPER_ACK (n2n_REGISTER_SUPER_ACK_t *reg,
/* Socket is mandatory in this message type */ /* Socket is mandatory in this message type */
retval += decode_sock(&(reg->sock), base, rem, idx); retval += decode_sock(&(reg->sock), base, rem, idx);
retval += decode_uint16(&(reg->auth.scheme), base, rem, idx);
retval += decode_uint16(&(reg->auth.token_size), base, rem, idx);
retval += decode_buf(reg->auth.token, reg->auth.token_size, base, rem, idx);
/* Following the edge socket are an array of backup supernodes. */ /* Following the edge socket are an array of backup supernodes. */
retval += decode_uint8(&(reg->num_sn), base, rem, idx); retval += decode_uint8(&(reg->num_sn), base, rem, idx);
retval += decode_buf(tmpbuf, (reg->num_sn * REG_SUPER_ACK_PAYLOAD_ENTRY_SIZE), base, rem, idx); retval += decode_buf(tmpbuf, (reg->num_sn * REG_SUPER_ACK_PAYLOAD_ENTRY_SIZE), base, rem, idx);
@ -528,6 +538,10 @@ int encode_REGISTER_SUPER_NAK (uint8_t *base,
retval += encode_buf(base, idx, nak->cookie, N2N_COOKIE_SIZE); retval += encode_buf(base, idx, nak->cookie, N2N_COOKIE_SIZE);
retval += encode_mac(base, idx, nak->srcMac); retval += encode_mac(base, idx, nak->srcMac);
retval += encode_uint16(base, idx, nak->auth.scheme);
retval += encode_uint16(base, idx, nak->auth.token_size);
retval += encode_buf(base, idx, nak->auth.token, nak->auth.token_size);
return retval; return retval;
} }
@ -544,6 +558,10 @@ int decode_REGISTER_SUPER_NAK (n2n_REGISTER_SUPER_NAK_t *nak,
retval += decode_buf(nak->cookie, N2N_COOKIE_SIZE, base, rem, idx); retval += decode_buf(nak->cookie, N2N_COOKIE_SIZE, base, rem, idx);
retval += decode_mac(nak->srcMac, base, rem, idx); retval += decode_mac(nak->srcMac, base, rem, idx);
retval += decode_uint16(&(nak->auth.scheme), base, rem, idx);
retval += decode_uint16(&(nak->auth.token_size), base, rem, idx);
retval += decode_buf(nak->auth.token, nak->auth.token_size, base, rem, idx);
return retval; return retval;
} }

View File

@ -24,7 +24,7 @@ LDFLAGS=@LDFLAGS@
N2N_LIB=../libn2n.a N2N_LIB=../libn2n.a
TOOLS=n2n-benchmark TOOLS=n2n-benchmark n2n-keygen
TOOLS+=@ADDITIONAL_TOOLS@ TOOLS+=@ADDITIONAL_TOOLS@
.PHONY: all clean install .PHONY: all clean install
@ -33,6 +33,9 @@ all: $(TOOLS)
n2n-benchmark: benchmark.c $(N2N_LIB) $(HEADERS) n2n-benchmark: benchmark.c $(N2N_LIB) $(HEADERS)
$(CC) $(CFLAGS) $< $(LDFLAGS) $(N2N_LIB) $(LIBS_EDGE) -o $@ $(CC) $(CFLAGS) $< $(LDFLAGS) $(N2N_LIB) $(LIBS_EDGE) -o $@
n2n-keygen: keygen.c $(N2N_LIB) $(HEADERS)
$(CC) $(CFLAGS) $< $(LDFLAGS) $(N2N_LIB) $(LIBS_EDGE) -o $@
n2n-decode: n2n_decode.c $(N2N_LIB) $(HEADERS) n2n-decode: n2n_decode.c $(N2N_LIB) $(HEADERS)
$(CC) $(CFLAGS) $< $(LDFLAGS) $(N2N_LIB) $(LIBS_EDGE) -lpcap -o $@ $(CC) $(CFLAGS) $< $(LDFLAGS) $(N2N_LIB) $(LIBS_EDGE) -lpcap -o $@

View File

@ -51,6 +51,7 @@ static void init_compression_for_benchmark(void);
static void deinit_compression_for_benchmark(void); static void deinit_compression_for_benchmark(void);
static void run_compression_benchmark(void); static void run_compression_benchmark(void);
static void run_hashing_benchmark(void); static void run_hashing_benchmark(void);
static void run_ecc_benchmark(void);
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
@ -85,12 +86,15 @@ int main(int argc, char * argv[]) {
run_transop_benchmark("cc20", &transop_cc20, &conf, pktbuf); run_transop_benchmark("cc20", &transop_cc20, &conf, pktbuf);
run_transop_benchmark("speck", &transop_speck, &conf, pktbuf); run_transop_benchmark("speck", &transop_speck, &conf, pktbuf);
run_ecc_benchmark();
/* Also for compression (init moved here for ciphers get run before in case of lzo init error) */ /* Also for compression (init moved here for ciphers get run before in case of lzo init error) */
init_compression_for_benchmark(); init_compression_for_benchmark();
run_compression_benchmark(); run_compression_benchmark();
run_hashing_benchmark(); run_hashing_benchmark();
/* Cleanup */ /* Cleanup */
transop_null.deinit(&transop_null); transop_null.deinit(&transop_null);
transop_tf.deinit(&transop_tf); transop_tf.deinit(&transop_tf);
@ -281,6 +285,46 @@ static void run_hashing_benchmark(void) {
printf("\n"); printf("\n");
} }
// --- ecc benchmark ----------------------------------------------------------------------
static void run_ecc_benchmark(void) {
const float target_sec = DURATION;
struct timeval t1;
struct timeval t2;
ssize_t nw;
ssize_t target_usec = target_sec * 1e6;
ssize_t tdiff = 0; // microseconds
size_t num_packets = 0;
unsigned char b[32];
unsigned char k[32];
memset(b, 0x00, 31);
b[31] = 9;
memset(k, 0x55, 32);
printf("[%s]\t%s\t%.1f sec\t(%u bytes) ",
"curve", "25519", target_sec, 32);
fflush(stdout);
gettimeofday( &t1, NULL );
nw = 32;
while(tdiff < target_usec) {
curve25519(b, k, b);
num_packets++;
gettimeofday( &t2, NULL );
tdiff = ((t2.tv_sec - t1.tv_sec) * 1000000) + (t2.tv_usec - t1.tv_usec);
}
float mpps = num_packets / (tdiff / 1e6) / 1e6;
printf(" ---> (%u bytes)\t%12u ops\t%8.1f Kops/s\n",
(unsigned int)nw, (unsigned int)num_packets, mpps * 1e3);
printf("\n");
}
// --- cipher benchmark ------------------------------------------------------------------- // --- cipher benchmark -------------------------------------------------------------------
static void run_transop_benchmark(const char *op_name, n2n_trans_op_t *op_fn, n2n_edge_conf_t *conf, uint8_t *pktbuf) { static void run_transop_benchmark(const char *op_name, n2n_trans_op_t *op_fn, n2n_edge_conf_t *conf, uint8_t *pktbuf) {

76
tools/keygen.c Normal file
View File

@ -0,0 +1,76 @@
/*
* (C) 2007-21 - 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 <http://www.gnu.org/licenses/>
*
*/
#include "n2n.h"
int main(int argc, char * argv[]) {
n2n_private_public_key_t prv; /* 32 bytes private key */
n2n_private_public_key_t bin; /* 32 bytes public key binary output buffer */
uint8_t asc[44]; /* 43 bytes + 0-terminator ascii string output */
uint8_t fed = 0;
// exactly two parameters required
if(argc != 3) {
// error message to stderr to not interfere with batch usage
fprintf(stderr, "\n"
"n2n-keygen tool\n\n"
" usage: n2n-keygen <username> <password>\n\n"
" or n2n-keygen -F <federation name>\n\n"
" outputs a line to insert at supernode's community file for user-and-\n"
" password authentication or a command line parameter with the public\n"
" federation key for use at edge's command line, please refer to the\n"
" doc/Authentication.md document or the man pages for more details\n\n");
return 1;
}
// federation mode?
if(strcmp(argv[1], "-F") == 0)
fed = 1;
// derive private key from username and password:
// hash username once, hash password twice (so password is bound
// to username but username and password are not interchangeable),
// finally xor the result
// in federation mode: only hash federation name, twice
generate_private_key(prv, (uint8_t*)argv[2]);
// hash user name only if required
if(!fed) {
bind_private_key_to_username(prv, (uint8_t*)argv[1]);
}
// calculate the public key into binary output buffer
generate_public_key(bin, prv);
// clear out the private key
memset(prv, 0, sizeof(prv));
// convert binary output to 6-bit-ascii string output
bin_to_ascii(asc, bin, sizeof(bin));
// output
if(fed)
fprintf(stdout, "-P %s\n", asc);
else
fprintf(stdout, "%c %s %s\n", N2N_USER_KEY_LINE_STARTER, argv[1], asc);
return 0;
}