aboutsummaryrefslogtreecommitdiffstats
path: root/src/wireguard.h
blob: ecd26e2dfa99bf8f138c4b02016e657811f26868 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/*
 * 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/rwlock.h>
#include <sys/fixedmap.h>
#include <sys/antireplay.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_WAIT_CONFIRM,
	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_keypair {
	struct wg_pubkey { uint8_t k[WG_KEY_SIZE]; } pub;
	struct wg_privkey { uint8_t k[WG_KEY_SIZE]; } priv;
};

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;
	struct timespec	t_last_handshake;
	uint16_t	t_pka_interval;
};

struct wg_session {
	uint32_t 			 s_local_id; /* Static */
	uint32_t 			 s_remote_id; /* Static */
	struct wg_peer			*s_peer; /* Static */
	struct timespec		 	 s_created;
	SLIST_ENTRY(wg_session)		 s_entry; /* Protected by p_lock */

	/* All protected by s_lock */
	struct rwlock			 s_lock;
	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;
	SLIST_ENTRY(wg_peer)		 p_entry;

	/* All protected by p_lock */
	struct rwlock			 p_lock;
	struct wg_cookie		 p_cookie;
	struct wg_timers		 p_timers;
	struct timespec			 p_last_handshake;
	struct timespec			 p_last_initiation;
	uint64_t		 	 p_tx_bytes;
	uint64_t		 	 p_rx_bytes;
	struct wg_privkey		 p_shared;
	uint8_t				 p_attempts;
	SLIST_HEAD(,wg_session)		 p_sessions;
	struct wg_timestamp { uint8_t t[WG_TIMESTAMP_SIZE]; } p_timestamp;
};

struct wg_device {
	void			(*d_outq)(struct wg_peer *, enum wg_pkt_type);
	struct fixed_map	  d_peers;
	struct fixed_map	  d_sessions;
	struct rwlock		  d_lock;
	struct wg_cookie_maker	  d_cookie_maker;
	struct wg_keypair	  d_keypair;
};

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_REKEY,
	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",
	"want rekey",
	"invalid state",
	"invalid mac",
	"invalid ID",
};

/* WireGuard functions */

void		 wg_device_init(struct wg_device *,
		     void (*)(struct wg_peer *, enum wg_pkt_type));
void		 wg_device_setkey(struct wg_device *, struct wg_privkey *);
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_ref(struct wg_peer *);
void		 wg_peer_put(struct wg_peer *);
void		 wg_peer_drop(struct wg_peer *);
void		 wg_peer_attach_session(struct wg_peer *, struct wg_session *);
void		 wg_peer_setshared(struct wg_peer *, struct wg_privkey *);
void		 wg_peer_getshared(struct wg_peer *, struct wg_privkey *);
void		 wg_peer_clean(struct wg_peer *);
struct timespec	 wg_peer_last_handshake(struct wg_peer *);
enum wg_error	 wg_peer_has_transport_session(struct wg_peer *);

struct wg_session	*wg_device_new_session(struct wg_device *);
struct wg_session	*wg_device_ref_session(struct wg_device *, uint32_t);
void		 	 wg_session_ref(struct wg_session *);
void			 wg_session_put(struct wg_session *);
void			 wg_session_drop(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_msg_cookie *, uint32_t, struct wg_session **);
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_device_make_cookie(struct wg_device *, struct wg_cookie *,
		    uint32_t, uint8_t[WG_MAC_SIZE], struct wg_msg_cookie *);

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_ */