summaryrefslogblamecommitdiffstats
path: root/src/wireguard.h
blob: ec3dc06106c97598192821e4ff4018a6065a1a8e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16















                                                                           

                 
 
                      
                     
                        
                      
                         
                           
 


                                         
                      


                         



                                   
                            
 
                                                
                                                                              
                                                                                 








                                                         
                                
                                                                                                 
 





































                                                                        
               
                         
                                 
                                 

                               

                           

  





                           

  















                                                           




                                               




                                             





                                  


                                       
                   







                                                     


















                                                              
 




                                                  
                                                  
 
                    
                                                  




                                                  


                                                           
                                                    


                                                          

                                                                          
 
                  
                    

                                                               
                                                              
                                                                                        


                                        

                                                 



                                             
  
 

                  
                     

                       


                           


                   
                       

                 
              

  




                                            


                         


                            
                         




                        

                         




                                                                    
                                                                   
                                               
 


                                                                                    

                                                
 
                                                          
                                                 
                                                                          
                                                                          

                                                                  
 
                                                             














                                                                              

                                                                            



                                                                





                                                                        




                                                                 
 
 
                     












                                                                                
 
                      
/*
 * Copyright (c) 2019 Matt Dunwoodie <ncon@noconroy.net>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef _LIBWG_H_
#define _LIBWG_H_

#include <sys/types.h>
#include <sys/time.h>
#include <sys/timeout.h>
#include <sys/mutex.h>
#include <sys/fixedmap.h>
#include <sys/antireplay.h>

/* This is only needed for wg_keypair. */
#include <net/if_wg.h>

#define WG_KEY_SIZE 32
#define WG_MAC_SIZE 16
#define WG_HASH_SIZE 32
#define WG_COOKIE_SIZE 16
#define WG_XNONCE_SIZE 24
#define WG_MSG_PADDING_SIZE  16
#define WG_COOKIE_RAND_VAL_SIZE 16
#define WG_COOKIE_ID_VAL_MAXSIZE 32
#define WG_TIMESTAMP_SIZE 12

#define WG_ENCRYPTED_SIZE(n) ((n) + WG_MAC_SIZE)
#define WG_PADDING_SIZE(n) ((WG_MSG_PADDING_SIZE - (n)) % WG_MSG_PADDING_SIZE)
#define WG_ENCRYPTED_PADDED_SIZE(n) WG_ENCRYPTED_SIZE(WG_PADDED_SIZE(n)) //unused

/* Constant for session */
#define WG_REKEY_AFTER_MESSAGES (((uint64_t) 2<<48) - 1)
#define WG_REJECT_AFTER_MESSAGES (((uint64_t) 2<<60) - 1)
#define WG_REKEY_AFTER_TIME 120
#define WG_REJECT_AFTER_TIME 180
#define WG_REKEY_ATTEMPT_COUNT 20
#define WG_REKEY_TIMEOUT 5
#define WG_KEEPALIVE_TIMEOUT 10
#define WG_COOKIE_VALID_TIME 120
#define WG_REKEY_AFTER_TIME_RECV (WG_REJECT_AFTER_TIME - WG_KEEPALIVE_TIMEOUT - WG_REKEY_TIMEOUT)

struct wg_msg_unknown {
	uint32_t 	type;
} 		__packed;

struct wg_msg_initiation {
	uint32_t 	type;
	uint32_t 	sender;
	uint8_t 	ephemeral[WG_KEY_SIZE];
	uint8_t 	static_pub[WG_ENCRYPTED_SIZE(WG_KEY_SIZE)];
	uint8_t 	timestamp[WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE)];
	uint8_t 	mac1  [WG_MAC_SIZE];
	uint8_t 	mac2  [WG_MAC_SIZE];
} 		__packed;

struct wg_msg_response {
	uint32_t 	type;
	uint32_t 	sender;
	uint32_t 	receiver;
	uint8_t 	ephemeral[WG_KEY_SIZE];
	uint8_t 	empty [WG_ENCRYPTED_SIZE(0)];
	uint8_t 	mac1  [WG_MAC_SIZE];
	uint8_t 	mac2  [WG_MAC_SIZE];
} 		__packed;

struct wg_msg_cookie {
	uint32_t 	type;
	uint32_t 	receiver;
	uint8_t 	nonce [WG_XNONCE_SIZE];
	uint8_t 	value[WG_ENCRYPTED_SIZE(WG_COOKIE_SIZE)];
} 		__packed;

struct wg_msg_transport {
	uint32_t 	type;
	uint32_t 	receiver;
	uint64_t 	counter;
	uint8_t 	data  [];
} 		__packed;

enum wg_state {
	WG_STATE_NEW = 0,
	WG_STATE_MADE_INITIATION,
	WG_STATE_RECV_INITIATION,
	WG_STATE_MADE_RESPONSE,
	WG_STATE_RECV_RESPONSE,
	WG_STATE_INITIATOR,
	WG_STATE_RESPONDER,
};

enum wg_pkt_type {
	WG_PKT_UNKNOWN = 0,
	WG_PKT_INITIATION,
	WG_PKT_RESPONSE,
	WG_PKT_COOKIE,
	WG_PKT_TRANSPORT,
};

static int wg_pkt_len[] = {
	sizeof(struct wg_msg_unknown),
	sizeof(struct wg_msg_initiation),
	sizeof(struct wg_msg_response),
	sizeof(struct wg_msg_cookie),
	WG_ENCRYPTED_SIZE(sizeof(struct wg_msg_transport)),
};

static char *wg_pkt_str[] = {
	"unknown",
	"initiation",
	"response",
	"cookie",
	"transport",
};

struct wg_cookie {
	uint8_t		cookie[WG_COOKIE_SIZE];
	struct timespec	time;
};

struct wg_cookie_maker {
	uint8_t		seed[WG_COOKIE_SIZE];
	struct timespec	time;
};

struct wg_timers {
	struct timeout	t_ka;
	struct timeout	t_pka;
	struct timeout	t_broken;
	struct timeout	t_reinit;
	struct timeout	t_cleanup;
	uint16_t	t_pka_interval;
};

struct wg_session {
	/* Static */
	uint32_t 			 s_local_id;
	uint32_t 			 s_remote_id;
	struct wg_peer			*s_peer;
	struct timespec		 	 s_created;

	/* All protected by s_mtx */
	struct mutex			 s_mtx;
	enum wg_state 			 s_state;

	struct wg_handshake {
		uint8_t 		 h_mac[WG_MAC_SIZE];
		uint8_t 		 h_hash[WG_HASH_SIZE];
		uint8_t 		 h_ck[WG_HASH_SIZE];
		uint8_t 		 h_k[WG_HASH_SIZE];
		struct wg_pubkey	 h_remote;
		struct wg_keypair	 h_local;
	} s_handshake;

	struct wg_keyset {
		uint64_t 		 k_txcounter;
		uint64_t 		 k_rxcounter;
		struct wg_privkey 	 k_txkey;
		struct wg_privkey	 k_rxkey;
		struct antireplay	 k_ar;
	} s_keyset;
};

struct wg_peer {
	uint32_t			 p_id;
	void				*p_arg;
	struct wg_device		*p_device;
	struct wg_pubkey		 p_remote;
	struct refcnt			 p_refcnt;

	/* Atomic */
	struct wg_timers		 p_timers;

	/* All protected by p_mtx */
	struct mutex			 p_mtx;
	struct wg_privkey		 p_shared;
	struct wg_cookie		 p_cookie;
	struct timespec			 p_last_initiation;
	uint64_t		 	 p_tx_bytes;
	uint64_t		 	 p_rx_bytes;
	uint8_t				 p_attempts;
	struct wg_session		*p_hs_session;
	struct wg_session		*p_ks_session;
	struct wg_session		*p_ks_session_old;
	struct wg_timestamp { uint8_t t[WG_TIMESTAMP_SIZE]; } p_timestamp;
};

struct wg_device {
	/* Static */
	void			 *d_arg;
	void			(*d_cleanup)(struct wg_peer *);
	void			(*d_notify)(struct wg_peer *);
	void			(*d_outq)(struct wg_peer *, enum wg_pkt_type, uint32_t);

	/* Mutex */
	struct mutex		  d_mtx;
	struct wg_cookie_maker	  d_cookie_maker;
	struct wg_keypair	  d_keypair;

	/* Atomic */
	struct fixed_map	  d_peers;
	struct fixed_map	  d_sessions;
};

enum wg_error {
	WG_OK = 0,
	WG_TIMESTAMP,
	WG_HS_ATTEMPTS,
	WG_HS_RATE,
	WG_UNKNOWN_PEER,
	WG_ALLOWED_IP_FAIL,
	WG_RATELIMIT,
	WG_DECRYPT,
	WG_REPLAY,
	WG_REJECT,
	WG_INVALID_PKT,
	WG_STATE,
	WG_MAC,
	WG_ID,
};

static char *wg_error_str[] = {
	"no error",
	"invalid timestamp",
	"exceeded initiation attemts",
	"exceeded initiation transmit rate",
	"unknown peer",
	"non-allowed ip",
	"ratelimit",
	"unable to decrypt",
	"replay detected",
	"packet rejected",
	"packet invalid",
	"invalid state",
	"invalid mac",
	"invalid ID",
};

/* WireGuard functions */

void	 wg_device_init(struct wg_device *, int,
	     void (*)(struct wg_peer *),
	     void (*)(struct wg_peer *, enum wg_pkt_type, uint32_t),
	     void (*)(struct wg_peer *), void *);
void	 wg_device_setkey(struct wg_device *, struct wg_privkey *);
void	 wg_device_getkey(struct wg_device *, struct wg_keypair *);
void	 wg_device_destroy(struct wg_device *);

struct wg_peer	*wg_device_new_peer(struct wg_device *, struct wg_pubkey *, void *);
struct wg_peer	*wg_device_ref_peerkey(struct wg_device *, struct wg_pubkey *);
struct wg_peer	*wg_device_ref_peerid(struct wg_device *, uint32_t);
void		 wg_peer_put(struct wg_peer *);
void		 wg_peer_drop(struct wg_peer *);

void		 wg_peer_reset_attempts(struct wg_peer *);
void		 wg_peer_clean(struct wg_peer *);
void		 wg_peer_setshared(struct wg_peer *, struct wg_privkey *);
void		 wg_peer_getshared(struct wg_peer *, struct wg_privkey *);
struct timespec	 	 wg_peer_last_handshake(struct wg_peer *);
struct wg_session	*wg_peer_last_session(struct wg_peer *);

void			 wg_session_put(struct wg_session *);

enum wg_error	wg_device_rx_initiation(struct wg_device *,
		    struct wg_msg_initiation *, struct wg_session **);
enum wg_error	wg_device_rx_response(struct wg_device *,
		    struct wg_msg_response *, struct wg_session **);
enum wg_error	wg_device_rx_cookie(struct wg_device *,
		    struct wg_msg_cookie *, struct wg_session **);
enum wg_error	wg_device_rx_transport(struct wg_device *,
		    struct wg_msg_transport *, size_t, struct wg_session **);

enum wg_error	wg_device_tx_initiation(struct wg_device *,
		    struct wg_msg_initiation *, uint32_t,
		    struct wg_session **);
enum wg_error	wg_device_tx_response(struct wg_device *,
		    struct wg_msg_response *, uint32_t, struct wg_session **);
enum wg_error	wg_device_tx_cookie(struct wg_device *, struct wg_cookie *,
		    uint32_t, uint8_t[WG_MAC_SIZE], struct wg_msg_cookie *);
enum wg_error	wg_device_tx_transport(struct wg_device *,
		    struct wg_msg_transport *, size_t, uint32_t,
		    struct wg_session **);

enum wg_error	wg_msg_initiation_valid_mac2(struct wg_msg_initiation *,
		    struct wg_cookie *);
enum wg_error	wg_msg_response_valid_mac2(struct wg_msg_response *,
		    struct wg_cookie *);
void 		wg_cookie_from_token(struct wg_cookie *,
		    struct wg_cookie_maker *, uint8_t *, uint8_t);

void 			wg_keypair_from_key(struct wg_keypair *,
			    const struct wg_privkey *);
void 			wg_keypair_generate(struct wg_keypair *);
enum wg_pkt_type	wg_pkt_type(uint8_t *, size_t);


/* Timer functions */
void		wg_timer_setup(struct wg_timers *, void *, void (*)(void *),
		    void (*)(void *), void (*)(void *), void (*)(void *));
void		wg_timer_stop(struct wg_timers *);
void		wg_timer_persistent_keepalive_tick(struct wg_timers *);
void		wg_timer_persistent_keepalive_set(struct wg_timers *, uint16_t);
uint16_t	wg_timer_persistent_keepalive_get(struct wg_timers *);
void		wg_timer_cleanup_tick(struct wg_timers *);
void		wg_timer_keepalive_flag(struct wg_timers *);
void		wg_timer_keepalive_unflag(struct wg_timers *);
void		wg_timer_broken_flag(struct wg_timers *);
void		wg_timer_broken_unflag(struct wg_timers *);
void		wg_timer_reinit_flag(struct wg_timers *);
void		wg_timer_reinit_unflag(struct wg_timers *);

#endif /* _LIBWG_H_ */