summaryrefslogtreecommitdiffstats
path: root/src/wireguard.h
blob: 60c101b1d5ae1ccbd56e49a595064630b24243c6 (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
/*
 * 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>

#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_ARI_BITS (sizeof(uint64_t) * 8)
#define WG_ARB_BITS (1<<10) /* 1024 bitmap (960 usable) */

#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)

enum wg_state {
	WG_STATE_CLEAN = 0,
	WG_STATE_MADE_INITIATION,
	WG_STATE_MADE_RESPONSE,
	WG_STATE_RECV_INITIATION,
	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,
};

struct wg_keypair {
	uint8_t pub[WG_KEY_SIZE];
	uint8_t priv[WG_KEY_SIZE];
};

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_handshake {
	struct rwlock	hs_lock;

	uint8_t	hs_attempts;
	uint8_t	hs_spub[WG_KEY_SIZE];
	uint8_t	hs_shared[WG_KEY_SIZE];
	struct wg_cookie	hs_cookie;
	struct wg_keypair	hs_skey;
	struct timespec		hs_last_initiation;

	struct {
		enum wg_state	ss_state;
		uint32_t 	ss_local_id;
		uint32_t 	ss_remote_id;
		uint8_t 	ss_mac[WG_MAC_SIZE];
		uint8_t 	ss_hash[WG_HASH_SIZE];
		uint8_t 	ss_ck[WG_HASH_SIZE];
		uint8_t 	ss_k[WG_HASH_SIZE];
		uint8_t		ss_timestamp[WG_TIMESTAMP_SIZE];

		uint8_t 	ss_epub[WG_KEY_SIZE];
		struct wg_keypair	ss_ekey;
	} hs_ss;
};

#define hs_state hs_ss.ss_state
#define hs_local_id hs_ss.ss_local_id
#define hs_remote_id hs_ss.ss_remote_id
#define hs_mac hs_ss.ss_mac
#define hs_hash hs_ss.ss_hash
#define hs_ck hs_ss.ss_ck
#define hs_k hs_ss.ss_k
#define hs_timestamp hs_ss.ss_timestamp
#define hs_epub hs_ss.ss_epub
#define hs_ekey hs_ss.ss_ekey

struct wg_session {
	struct rwlock	s_lock;
	struct {
		enum wg_state 	ss_state;
		uint64_t 	ss_txcounter;
		uint64_t 	ss_rxcounter;
		uint32_t 	ss_local_id;
		uint32_t 	ss_remote_id;
		struct timespec ss_created;

		uint8_t 	ss_txkey[WG_KEY_SIZE];
		uint8_t 	ss_rxkey[WG_KEY_SIZE];

		struct wg_antireplay {
			uint64_t 	ar_head;
			uint64_t 	ar_bitmap[WG_ARB_BITS / WG_ARI_BITS];
		} ss_ar;
	} s_ss;
};

#define s_state s_ss.ss_state
#define s_txcounter s_ss.ss_txcounter
#define s_rxcounter s_ss.ss_rxcounter
#define s_local_id s_ss.ss_local_id
#define s_remote_id s_ss.ss_remote_id
#define s_created s_ss.ss_created
#define s_txkey s_ss.ss_txkey
#define s_rxkey s_ss.ss_rxkey
#define s_ar s_ss.ss_ar

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_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 	cookie[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_error {
	WG_OK = 0,
	WG_TIMESTAMP,
	WG_HS_ATTEMPTS,
	WG_HS_RATE,
	WG_DECRYPT,
	WG_REPLAY,
	WG_REJECT,
	WG_REKEY,
	WG_STATE,
	WG_MAC,
	WG_ID,
};

static char *wg_error_str[] = {
	"no error",
	"invalid timestamp",
	"exceeded initiation attemts",
	"exceeded initiation transmit rate",
	"unable to decrypt",
	"replay detected",
	"packet rejected",
	"want rekey",
	"invalid state",
	"invalid mac",
	"invalid ID",
};

void wg_handshake_init(struct wg_handshake *);
void wg_session_init(struct wg_session *);
void wg_handshake_clean(struct wg_handshake *);
void wg_session_clean(struct wg_session *);

enum wg_error wg_handshake_make_initiation(struct wg_handshake *, uint32_t, struct wg_msg_initiation *);
enum wg_error wg_handshake_make_response(struct wg_handshake *, uint32_t, struct wg_msg_response *);
enum wg_error wg_handshake_make_cookie(struct wg_keypair *, struct wg_cookie *, uint32_t, uint8_t [WG_MAC_SIZE], struct wg_msg_cookie *);

enum wg_error wg_handshake_recv_initiation(struct wg_handshake *, struct wg_keypair *, struct wg_msg_initiation *);
enum wg_error wg_handshake_recv_response(struct wg_handshake *, struct wg_msg_response *);
enum wg_error wg_handshake_recv_cookie(struct wg_handshake *, struct wg_msg_cookie *);

enum wg_error wg_handshake_initiation_valid_mac2(struct wg_cookie *, struct wg_msg_initiation *);
enum wg_error wg_handshake_response_valid_mac2(struct wg_cookie *, struct wg_msg_response *);
enum wg_error wg_handshake_merge(struct wg_handshake *, struct wg_handshake *);
void wg_handshake_reset_attempts(struct wg_handshake *);

uint32_t wg_handshake_id(struct wg_handshake *);
uint32_t wg_handshake_shortkey(struct wg_handshake *);
void wg_handshake_setkeypair(struct wg_handshake *, struct wg_keypair *);
void wg_handshake_setkey(struct wg_handshake *, uint8_t[WG_KEY_SIZE]);
void wg_handshake_setshared(struct wg_handshake *, uint8_t[WG_KEY_SIZE]);
void wg_handshake_getkey(struct wg_handshake *, uint8_t[WG_KEY_SIZE]);
void wg_handshake_getshared(struct wg_handshake *, uint8_t[WG_KEY_SIZE]);
int wg_handshake_keycmp(struct wg_handshake *, struct wg_handshake *);

uint32_t wg_session_id(struct wg_session *);
enum wg_error wg_session_encrypt(struct wg_session *, struct wg_msg_transport *, size_t);
enum wg_error wg_session_decrypt(struct wg_session *, struct wg_msg_transport *, size_t);
enum wg_error wg_session_from_handshake(struct wg_session *, struct wg_handshake *);
enum wg_error wg_session_confirm(struct wg_session *);
enum wg_error wg_session_ready(struct wg_session *);

void wg_keypair_from_bytes(struct wg_keypair *, const uint8_t [WG_KEY_SIZE]);
void wg_keypair_generate(struct wg_keypair *);
enum wg_pkt_type wg_pkt_type(uint8_t *, size_t);
void wg_cookie_from_token(struct wg_cookie *, struct wg_cookie_maker *, uint8_t *, uint8_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 *);
void wg_timer_session_made(struct wg_timers *);
struct timespec wg_timer_session_last(struct wg_timers *);

#endif /* _LIBWG_H_ */