summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/if_wg.c111
-rw-r--r--src/wireguard.c319
-rw-r--r--src/wireguard.h46
3 files changed, 275 insertions, 201 deletions
diff --git a/src/if_wg.c b/src/if_wg.c
index bb3322893ee..1674b519b00 100644
--- a/src/if_wg.c
+++ b/src/if_wg.c
@@ -315,6 +315,7 @@ wg_id_create(struct wg_softc *sc, struct wg_peer *p)
{
struct wg_id_item item = { arc4random() , p };
HASHMAP_SET(&sc->sc_id_hashmap, item);
+ DPRINTF("wg_id_create: %p - 0x%08x\n", p, item.id);
return item.id;
}
@@ -322,6 +323,7 @@ void
wg_id_destroy(struct wg_softc *sc, uint32_t id)
{
struct wg_id_item item = { id, NULL };
+ DPRINTF("wg_id_destroy: 0x%08x\n", id);
HASHMAP_UNSET(&sc->sc_id_hashmap, item);
}
@@ -590,6 +592,7 @@ void
wg_transmit_slow(struct wg_pkt *pkt)
{
uint32_t id;
+ enum wg_error e;
struct wg_msg_initiation *init;
struct wg_msg_response *resp;
struct wg_msg_cookie *cookie;
@@ -603,24 +606,29 @@ wg_transmit_slow(struct wg_pkt *pkt)
id = wg_handshake_clean(&pkt->p_p->p_hs);
wg_id_destroy(pkt->p_sc, id);
id = wg_id_create(pkt->p_sc, pkt->p_p);
- wg_handshake_make_initiation(&pkt->p_p->p_hs, id, init);
+ if ((e = wg_handshake_make_initiation(&pkt->p_p->p_hs, id, init)) != WG_OK) {
+ DPRINTF("wg_transmit_slow: unable to initiate %d\n", e);
+ goto dead;
+ }
break;
case WG_PKT_RESPONSE:
DPRINTF("wg_transmit_slow: response\n");
resp = mtod(pkt->p_m, struct wg_msg_response *);
pkt->p_m->m_len = sizeof(struct wg_msg_response);
- //id = wg_handshake_clean(&pkt->p_p->p_hs);
wg_id_destroy(pkt->p_sc, pkt->p_p->p_hs.hs_local_id);
id = wg_id_create(pkt->p_sc, pkt->p_p);
- if (wg_handshake_make_response(&pkt->p_p->p_hs, id, resp) != 0) {
- panic("temporary panic");
- DPRINTF("wg_transmit_slow: response bad state\n");
- pkt->p_state = WG_PKT_DEAD;
- return;
+
+ if ((e = wg_handshake_make_response(&pkt->p_p->p_hs, id, resp)) != WG_OK) {
+ DPRINTF("wg_transmit_slow: response bad state %d\n", e);
+ goto dead;
}
- /* TODO fix this - insecure */
- wg_session_from_handshake(&pkt->p_p->p_sess, &pkt->p_p->p_hs);
+
+ if ((e = wg_session_from_handshake(&pkt->p_p->p_sess, &pkt->p_p->p_hs)) != WG_OK) {
+ DPRINTF("wg_transmit_slow: unable to session %d\n", e);
+ goto dead;
+ };
+
wg_peer_new_session(pkt->p_sc, pkt->p_p);
break;
case WG_PKT_COOKIE:
@@ -636,6 +644,9 @@ wg_transmit_slow(struct wg_pkt *pkt)
m_calchdrlen(pkt->p_m);
pkt->p_state = WG_PKT_PASS;
+ return;
+dead:
+ pkt->p_state = WG_PKT_DEAD;
}
void
@@ -651,31 +662,21 @@ wg_receive_slow(struct wg_pkt *pkt)
switch (pkt->p_type) {
case WG_PKT_INITIATION:
init = mtod(pkt->p_m, struct wg_msg_initiation *);
- wg_handshake_init(&hs);
-
- hs.hs_skey = &pkt->p_sc->sc_kp;
-
- if ((e = wg_handshake_recv_initiation(&hs, init)) != 0) {
+ if ((e = wg_handshake_recv_initiation(&hs, &pkt->p_sc->sc_kp, init)) != WG_OK) {
DPRINTF("wg_receive_slow: bad initiation %d\n", e);
goto dead;
}
- p = wg_peer_lookup(pkt->p_sc, hs.hs_spub);
-
- if (p == NULL) {
+ if ((p = wg_peer_lookup(pkt->p_sc, hs.hs_spub)) == NULL) {
DPRINTF("wg_receive_slow: unable to find initiation peer\n");
goto dead;
}
- memcpy(hs.hs_spub, p->p_hs.hs_spub, sizeof(hs.hs_spub));
- memcpy(hs.hs_shared, p->p_hs.hs_shared, sizeof(hs.hs_shared));
-
- if (memcmp(hs.hs_timestamp, p->p_hs.hs_timestamp, WG_TIMESTAMP_SIZE) <= 0) {
- DPRINTF("wg_receive_slow: timestamp too old\n");
+ if ((e = wg_handshake_merge(&p->p_hs, &hs)) != WG_OK) {
+ DPRINTF("wg_receive_slow: unable to merge %d\n", e);
goto dead;
}
- wg_handshake_clone(&p->p_hs, &hs);
wg_send_pkt(p->p_sc, p, WG_PKT_RESPONSE);
break;
case WG_PKT_RESPONSE:
@@ -687,15 +688,16 @@ wg_receive_slow(struct wg_pkt *pkt)
goto dead;
}
- wg_handshake_init(&hs);
- wg_handshake_clone(&hs, &p->p_hs);
- if ((e = wg_handshake_recv_response(&hs, resp)) != 0) {
+ if ((e = wg_handshake_recv_response(&p->p_hs, resp)) != 0) {
DPRINTF("wg_receive_slow: bad response %d\n", e);
goto dead;
}
- wg_handshake_clone(&p->p_hs, &hs);
- wg_session_from_handshake(&p->p_sess, &p->p_hs);
+ if ((e = wg_session_from_handshake(&p->p_sess, &p->p_hs)) != WG_OK) {
+ DPRINTF("wg_receive_slow: unable to session %d\n", e);
+ goto dead;
+ };
+
wg_peer_new_session(pkt->p_sc, p);
break;
case WG_PKT_COOKIE:
@@ -743,8 +745,11 @@ wg_transport_encrypt(struct wg_pkt *pkt)
struct wg_session *s = &pkt->p_p->p_sess;
size_t encrypted_len, padding_len, plain_len;
- if (wg_session_rekey_tx(s))
- wg_want_init(pkt->p_p);
+ if (m_defrag(m, M_NOWAIT)) {
+ counters_inc(pkt->p_sc->sc_if.if_counters, ifc_oerrors);
+ pkt->p_state = WG_PKT_FREE;
+ return;
+ }
plain_len = m->m_pkthdr.len;
padding_len = WG_PADDING_SIZE(plain_len);
@@ -764,8 +769,18 @@ wg_transport_encrypt(struct wg_pkt *pkt)
memcpy(msg->data, mtod(m, void *), plain_len);
bzero(msg->data + plain_len, padding_len);
- if (wg_session_encrypt(s, msg, plain_len + padding_len) != 0) {
- m_free(m);
+ switch (wg_session_encrypt(s, msg, plain_len + padding_len)) {
+ case WG_REKEY:
+ wg_want_init(pkt->p_p);
+ case WG_OK:
+ break;
+ case WG_REJECT:
+ wg_want_init(pkt->p_p);
+ if (mq_push(&pkt->p_p->p_outgoing, m) != 0)
+ counters_inc(pkt->p_sc->sc_if.if_counters, ifc_oqdrops);
+ pkt->p_state = WG_PKT_DEAD;
+ return;
+ default:
pkt->p_state = WG_PKT_DEAD;
return;
}
@@ -806,19 +821,16 @@ wg_transport_decrypt(struct wg_pkt *pkt)
s = &pkt->p_p->p_sess;
- if (wg_session_reject_rx(s)) {
- pkt->p_state = WG_PKT_DEAD;
- return;
- }
-
- if (wg_session_decrypt(s, msg, m->m_pkthdr.len)) {
+ switch (wg_session_decrypt(s, msg, m->m_pkthdr.len)) {
+ case WG_REKEY:
+ wg_want_init(pkt->p_p);
+ case WG_OK:
+ break;
+ default:
pkt->p_state = WG_PKT_DEAD;
return;
}
- if (wg_session_rekey_rx(s))
- wg_want_init(pkt->p_p);
-
m_adj(m, sizeof(struct wg_msg_transport));
if (m->m_pkthdr.len == WG_ENCRYPTED_SIZE(0)) {
@@ -957,23 +969,6 @@ wg_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa, struct rtentry
goto exit;
}
- if (m_defrag(m, M_NOWAIT)) {
- counters_inc(sc->sc_if.if_counters, ifc_oerrors);
- error = ENOBUFS;
- goto exit;
- }
-
- if (wg_session_reject_tx(&p->p_sess)) {
- wg_want_init(p);
-
- if (mq_push(&p->p_outgoing, m) != 0) {
- error = ENOBUFS;
- counters_inc(sc->sc_if.if_counters, ifc_oqdrops);
- }
-
- goto exit;
- }
-
struct wg_pkt pkt = WG_PKT_INIT(p, m, sc, WG_PKT_TRANSPORT);
if (MPQ_ADD(wg_pkt_queue, &sc->sc_txq, &pkt) == 0) {
diff --git a/src/wireguard.c b/src/wireguard.c
index 2968e781b06..dd9082e31b3 100644
--- a/src/wireguard.c
+++ b/src/wireguard.c
@@ -42,6 +42,8 @@ CTASSERT(WG_KEY_SIZE == WG_HASH_SIZE);
CTASSERT(WG_MSG_PADDING_SIZE == CHACHA20POLY1305_AUTHTAG_SIZE);
CTASSERT(WG_XNONCE_SIZE == XCHACHA20POLY1305_NONCE_SIZE);
+#define ret_error(err) do { ret = err; goto leave; } while (0)
+
/* WireGuard crypto helper functions */
void
wg_kdf(uint8_t first[WG_HASH_SIZE], uint8_t second[WG_HASH_SIZE], uint8_t third[WG_HASH_SIZE], uint8_t key[WG_KEY_SIZE], uint8_t * input, size_t input_len)
@@ -178,13 +180,53 @@ wg_session_init(struct wg_session *s)
rw_init(&s->s_lock, "wg_session");
}
+enum wg_error
+wg_handshake_merge(struct wg_handshake *dst, struct wg_handshake *src)
+{
+ enum wg_error ret = WG_OK;
+ rw_enter_write(&dst->hs_lock);
+ if (dst->hs_state != WG_STATE_CLEAN)
+ ret_error(WG_STATE);
+
+ if (dst->hs_skey != dst->hs_skey)
+ ret_error(WG_STATE);
+
+ if (memcmp(dst->hs_skey, src->hs_skey, WG_KEY_SIZE))
+ ret_error(WG_STATE);
+
+ if (memcmp(dst->hs_timestamp, src->hs_timestamp, WG_TIMESTAMP_SIZE) >= 0)
+ ret_error(WG_TIMESTAMP);
+
+#define HS_COPY(elem) memcpy(&dst->elem, &src->elem, sizeof(dst->elem))
+ HS_COPY(hs_state);
+ HS_COPY(hs_local_id);
+ HS_COPY(hs_remote_id);
+ HS_COPY(hs_mac);
+ HS_COPY(hs_hash);
+ HS_COPY(hs_ck);
+ HS_COPY(hs_k);
+ HS_COPY(hs_timestamp);
+ //HS_COPY(hs_cookie);
+ //HS_COPY(hs_shared);
+ //HS_COPY(hs_spub);
+ //HS_COPY(hs_skey);
+ HS_COPY(hs_epub);
+ HS_COPY(hs_ekey);
+#undef HS_COPY
+
+leave:
+ rw_exit_write(&dst->hs_lock);
+ explicit_bzero(src, sizeof(struct wg_handshake));
+ return ret;
+}
+
uint32_t
-wg_handshake_clean(struct wg_handshake *hs)
+wg_handshake_clean_nolock(struct wg_handshake *hs)
{
-#define HS_CLEAN(elem) explicit_bzero(&hs->elem, sizeof(hs->elem))
- rw_enter_write(&hs->hs_lock);
uint32_t id = hs->hs_local_id;
- HS_CLEAN(hs_role);
+ rw_assert_wrlock(&hs->hs_lock);
+#define HS_CLEAN(elem) explicit_bzero(&hs->elem, sizeof(hs->elem))
+ HS_CLEAN(hs_state);
HS_CLEAN(hs_local_id);
HS_CLEAN(hs_remote_id);
HS_CLEAN(hs_mac);
@@ -198,34 +240,18 @@ wg_handshake_clean(struct wg_handshake *hs)
//HS_CLEAN(hs_skey);
HS_CLEAN(hs_epub);
HS_CLEAN(hs_ekey);
- rw_exit_write(&hs->hs_lock);
#undef HS_CLEAN
return id;
}
-void
-wg_handshake_clone(struct wg_handshake *dst, struct wg_handshake *src)
+uint32_t
+wg_handshake_clean(struct wg_handshake *hs)
{
-#define HS_COPY_ELEM(elem) memcpy(&dst->elem, &src->elem, sizeof(dst->elem))
- rw_enter_write(&dst->hs_lock);
- rw_enter_read(&src->hs_lock);
- HS_COPY_ELEM(hs_role);
- HS_COPY_ELEM(hs_local_id);
- HS_COPY_ELEM(hs_remote_id);
- HS_COPY_ELEM(hs_mac);
- HS_COPY_ELEM(hs_hash);
- HS_COPY_ELEM(hs_ck);
- HS_COPY_ELEM(hs_k);
- HS_COPY_ELEM(hs_timestamp);
- HS_COPY_ELEM(hs_cookie);
- HS_COPY_ELEM(hs_shared);
- HS_COPY_ELEM(hs_spub);
- HS_COPY_ELEM(hs_skey);
- HS_COPY_ELEM(hs_epub);
- HS_COPY_ELEM(hs_ekey);
- rw_exit_read(&src->hs_lock);
- rw_exit_write(&dst->hs_lock);
-#undef HS_COPY_ELEM
+ uint32_t ret;
+ rw_enter_write(&hs->hs_lock);
+ ret = wg_handshake_clean_nolock(hs);
+ rw_exit_write(&hs->hs_lock);
+ return ret;
}
uint32_t
@@ -234,7 +260,7 @@ wg_session_clean(struct wg_session *s)
#define S_CLEAN(elem) explicit_bzero(&s->elem, sizeof(s->elem))
rw_enter_write(&s->s_lock);
uint32_t id = s->s_local_id;
- S_CLEAN(s_role);
+ S_CLEAN(s_state);
S_CLEAN(s_txcounter);
S_CLEAN(s_rxcounter);
S_CLEAN(s_local_id);
@@ -248,33 +274,36 @@ wg_session_clean(struct wg_session *s)
return id;
}
-void
+enum wg_error
wg_session_from_handshake(struct wg_session *s, struct wg_handshake *hs)
{
+ enum wg_error ret = WG_OK;
+
rw_enter_write(&s->s_lock);
- rw_enter_read(&hs->hs_lock);
- s->s_role = hs->hs_role;
+ rw_enter_write(&hs->hs_lock);
s->s_txcounter = s->s_rxcounter = 0;
s->s_local_id = hs->hs_local_id;
s->s_remote_id = hs->hs_remote_id;
getnanotime(&s->s_created);
- if (s->s_role == WG_ROLE_INITIATOR) {
+ if (hs->hs_state == WG_STATE_RECV_RESPONSE) {
+ s->s_state = WG_STATE_INITIATOR;
wg_kdf(s->s_txkey, s->s_rxkey, NULL, hs->hs_ck, NULL, 0);
- } else if (s->s_role == WG_ROLE_RESPONDER) {
+ } else if (hs->hs_state == WG_STATE_MADE_RESPONSE) {
+ s->s_state = WG_STATE_RESPONDER;
wg_kdf(s->s_rxkey, s->s_txkey, NULL, hs->hs_ck, NULL, 0);
} else {
- panic("invalid session role: %d\n", s->s_role);
+ ret_error(WG_STATE);
}
wg_antireplay_init(&s->s_ar);
+ wg_handshake_clean_nolock(hs);
- /* TODO fix lock gap */
- rw_exit_read(&hs->hs_lock);
+leave:
+ rw_exit_write(&hs->hs_lock);
rw_exit_write(&s->s_lock);
-
- wg_handshake_clean(hs);
+ return ret;
}
int
@@ -312,7 +341,7 @@ wg_session_reject_rx(struct wg_session *s)
int
wg_session_rekey_tx(struct wg_session *s)
{
- if (s->s_role == WG_ROLE_INITIATOR &&
+ if (s->s_state == WG_STATE_INITIATOR &&
wg_timespec_timedout(&s->s_created, WG_REKEY_AFTER_TIME))
return 1;
if (s->s_txcounter > WG_REKEY_AFTER_MESSAGES)
@@ -323,7 +352,7 @@ wg_session_rekey_tx(struct wg_session *s)
int
wg_session_rekey_rx(struct wg_session *s)
{
- if (s->s_role == WG_ROLE_RESPONDER &&
+ if (s->s_state == WG_STATE_RESPONDER &&
wg_timespec_timedout(&s->s_created, WG_REKEY_AFTER_TIME_RECV))
return 1;
return 0;
@@ -332,15 +361,19 @@ wg_session_rekey_rx(struct wg_session *s)
enum wg_error
wg_handshake_make_initiation(struct wg_handshake *hs, uint32_t id, struct wg_msg_initiation *m)
{
- if (hs->hs_role != WG_ROLE_UNKNOWN)
- return WG_STATE;
+ enum wg_error ret = WG_OK;
- hs->hs_role = WG_ROLE_INITIATOR;
- hs->hs_local_id = id;
- hs->hs_remote_id = 0;
+ rw_enter_write(&hs->hs_lock);
+
+ if (hs->hs_state != WG_STATE_CLEAN)
+ ret_error(WG_STATE);
wg_keypair_generate(&hs->hs_ekey);
+ m->type = WG_MSG_INITIATION;
+ m->sender = hs->hs_local_id = id;
+ memcpy(m->ephemeral, hs->hs_ekey.pub, WG_KEY_SIZE);
+
wg_hash2(hs->hs_ck, WG_CONSTRUCTION, strlen(WG_CONSTRUCTION), NULL, 0);
memcpy(hs->hs_hash, hs->hs_ck, WG_HASH_SIZE);
wg_mix_hash(hs, WG_IDENTIFIER, strlen(WG_IDENTIFIER));
@@ -350,39 +383,42 @@ wg_handshake_make_initiation(struct wg_handshake *hs, uint32_t id, struct wg_msg
wg_mix_hash(hs, hs->hs_ekey.pub, WG_KEY_SIZE);
wg_mix_dh(hs, hs->hs_ekey.priv, hs->hs_spub);
- m->type = WG_MSG_INITIATION;
- m->sender = hs->hs_local_id;
- memcpy(m->ephemeral, hs->hs_ekey.pub, WG_KEY_SIZE);
-
wg_handshake_encrypt(hs, m->static_pub, hs->hs_skey->pub, WG_KEY_SIZE);
wg_mix_hash(hs, m->static_pub, WG_ENCRYPTED_SIZE(WG_KEY_SIZE));
-
wg_mix_dh(hs, hs->hs_skey->priv, hs->hs_spub);
wg_timestamp_get(m->timestamp);
wg_handshake_encrypt(hs, m->timestamp, m->timestamp, WG_TIMESTAMP_SIZE);
wg_mix_hash(hs, m->timestamp, WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE));
-
wg_hash2(hs->hs_k, WG_MAC1, strlen(WG_MAC1), hs->hs_spub, WG_KEY_SIZE);
+
blake2s(m->mac1, (void *) m, hs->hs_k, sizeof(m->mac1), offsetof(struct wg_msg_initiation, mac1), sizeof(hs->hs_k));
memcpy(hs->hs_mac, m->mac1, sizeof(hs->hs_mac));
-
if (wg_handshake_need_cookie(hs))
blake2s(m->mac2, (void *)m, hs->hs_cookie.cookie, sizeof(m->mac2), offsetof(struct wg_msg_initiation, mac2), sizeof(hs->hs_cookie.cookie));
else
bzero(m->mac2, WG_MAC_SIZE);
- return WG_OK;
+ hs->hs_state = WG_STATE_MADE_INITIATION;
+ hs->hs_remote_id = 0;
+
+leave:
+ rw_exit_write(&hs->hs_lock);
+ return ret;
}
enum wg_error
wg_handshake_make_response(struct wg_handshake *hs, uint32_t id, struct wg_msg_response *m)
{
- if (hs->hs_role != WG_ROLE_RESPONDER)
- return WG_STATE;
+ enum wg_error ret = WG_OK;
+
+ rw_enter_write(&hs->hs_lock);
+
+ if (hs->hs_state != WG_STATE_RECV_INITIATION)
+ ret_error(WG_STATE);
m->type = WG_MSG_RESPONSE;
m->sender = id;
@@ -411,7 +447,10 @@ wg_handshake_make_response(struct wg_handshake *hs, uint32_t id, struct wg_msg_r
else
bzero(m->mac2, WG_MAC_SIZE);
- return WG_OK;
+ hs->hs_state = WG_STATE_MADE_RESPONSE;
+leave:
+ rw_exit_write(&hs->hs_lock);
+ return ret;
}
enum wg_error
@@ -439,20 +478,6 @@ wg_handshake_make_cookie(struct wg_keypair *kp, struct wg_cookie *c, uint32_t id
}
enum wg_error
-wg_session_encrypt(struct wg_session *s, struct wg_msg_transport *m, size_t len)
-{
- if (wg_session_reject_tx(s))
- return WG_REJECT;
-
- m->type = WG_MSG_TRANSPORT;
- m->receiver = s->s_remote_id;
- m->counter = s->s_txcounter++;
-
- chacha20poly1305_encrypt(m->data, m->data, len, NULL, 0, m->counter, s->s_txkey);
- return WG_OK;
-}
-
-enum wg_error
wg_handshake_valid_mac2(struct wg_cookie *c, uint8_t *m, uint8_t mlen)
{
uint8_t mac[WG_MAC_SIZE];
@@ -462,74 +487,94 @@ wg_handshake_valid_mac2(struct wg_cookie *c, uint8_t *m, uint8_t mlen)
}
enum wg_error
-wg_handshake_recv_initiation(struct wg_handshake *hs, struct wg_msg_initiation *m)
+wg_handshake_recv_initiation(struct wg_handshake *ths, struct wg_keypair *kp, struct wg_msg_initiation *m)
{
- hs->hs_role = WG_ROLE_RESPONDER;
- hs->hs_local_id = 0;
- hs->hs_remote_id = m->sender;
- memcpy(hs->hs_epub, m->ephemeral, WG_KEY_SIZE);
+ enum wg_error ret = WG_OK;
- wg_keypair_generate(&hs->hs_ekey);
+ /* Setup */
+ ths->hs_state = WG_STATE_RECV_INITIATION;
+ ths->hs_skey = kp;
+ ths->hs_local_id = 0;
+ ths->hs_remote_id = m->sender;
+ memcpy(ths->hs_epub, m->ephemeral, WG_KEY_SIZE);
- wg_hash2(hs->hs_ck, WG_CONSTRUCTION, strlen(WG_CONSTRUCTION), NULL, 0);
- memcpy(hs->hs_hash, hs->hs_ck, WG_HASH_SIZE);
- wg_mix_hash(hs, WG_IDENTIFIER, strlen(WG_IDENTIFIER));
+ wg_keypair_generate(&ths->hs_ekey);
- wg_mix_hash(hs, hs->hs_skey->pub, WG_KEY_SIZE);
- wg_kdf(hs->hs_ck, NULL, NULL, hs->hs_ck, hs->hs_epub, WG_KEY_SIZE);
- wg_mix_hash(hs, hs->hs_epub, WG_KEY_SIZE);
- wg_mix_dh(hs, hs->hs_skey->priv, hs->hs_epub);
+ /* Initialise hash */
+ wg_hash2(ths->hs_ck, WG_CONSTRUCTION, strlen(WG_CONSTRUCTION), NULL, 0);
+ memcpy(ths->hs_hash, ths->hs_ck, WG_HASH_SIZE);
+ wg_mix_hash(ths, WG_IDENTIFIER, strlen(WG_IDENTIFIER));
- if (!wg_handshake_decrypt(hs, hs->hs_spub, m->static_pub, WG_ENCRYPTED_SIZE(WG_KEY_SIZE)))
- return WG_DECRYPT;
+ /* KDF and mix */
+ wg_mix_hash(ths, ths->hs_skey->pub, WG_KEY_SIZE);
+ wg_kdf(ths->hs_ck, NULL, NULL, ths->hs_ck, ths->hs_epub, WG_KEY_SIZE);
+ wg_mix_hash(ths, ths->hs_epub, WG_KEY_SIZE);
+ wg_mix_dh(ths, ths->hs_skey->priv, ths->hs_epub);
- wg_mix_hash(hs, m->static_pub, WG_ENCRYPTED_SIZE(WG_KEY_SIZE));
- wg_mix_dh(hs, hs->hs_skey->priv, hs->hs_spub);
+ if (!wg_handshake_decrypt(ths, ths->hs_spub, m->static_pub, WG_ENCRYPTED_SIZE(WG_KEY_SIZE)))
+ ret_error(WG_DECRYPT);
- if (!wg_handshake_decrypt(hs, hs->hs_timestamp, m->timestamp, WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE)))
- return WG_DECRYPT;
+ wg_mix_hash(ths, m->static_pub, WG_ENCRYPTED_SIZE(WG_KEY_SIZE));
+ wg_mix_dh(ths, ths->hs_skey->priv, ths->hs_spub);
- wg_mix_hash(hs, m->timestamp, WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE));
- wg_hash2(hs->hs_k, WG_MAC1, strlen(WG_MAC1), hs->hs_skey->pub, WG_KEY_SIZE);
- blake2s(hs->hs_mac, (void *)m, hs->hs_k, sizeof(hs->hs_mac), offsetof(struct wg_msg_initiation, mac1), sizeof(hs->hs_k));
+ if (!wg_handshake_decrypt(ths, ths->hs_timestamp, m->timestamp, WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE)))
+ ret_error(WG_DECRYPT);
- if (timingsafe_bcmp(hs->hs_mac, m->mac1, WG_MAC_SIZE))
- return WG_MAC;
+ wg_mix_hash(ths, m->timestamp, WG_ENCRYPTED_SIZE(WG_TIMESTAMP_SIZE));
+ wg_hash2(ths->hs_k, WG_MAC1, strlen(WG_MAC1), ths->hs_skey->pub, WG_KEY_SIZE);
- return WG_OK;
+ blake2s(ths->hs_mac, (void *)m, ths->hs_k, sizeof(ths->hs_mac), offsetof(struct wg_msg_initiation, mac1), sizeof(ths->hs_k));
+
+ if (timingsafe_bcmp(ths->hs_mac, m->mac1, WG_MAC_SIZE))
+ ret_error(WG_MAC);
+
+leave:
+ return ret;
}
+
enum wg_error
wg_handshake_recv_response(struct wg_handshake *hs, struct wg_msg_response *m)
{
+ enum wg_error ret = WG_OK;
uint8_t mac1[WG_MAC_SIZE];
+ struct wg_handshake tmp_hs;
- if (hs->hs_role != WG_ROLE_INITIATOR)
- return WG_STATE;
+ rw_enter_write(&hs->hs_lock);
- hs->hs_remote_id = m->sender;
- memcpy(hs->hs_epub, m->ephemeral, WG_KEY_SIZE);
+ if (hs->hs_state != WG_STATE_MADE_INITIATION)
+ ret_error(WG_STATE);
- wg_kdf(hs->hs_ck, NULL, NULL, hs->hs_ck, hs->hs_epub, WG_KEY_SIZE);
- wg_mix_hash(hs, hs->hs_epub, WG_KEY_SIZE);
+ tmp_hs = *hs;
- wg_mix_dh(hs, hs->hs_ekey.priv, hs->hs_epub);
- wg_mix_dh(hs, hs->hs_skey->priv, hs->hs_epub);
+ tmp_hs.hs_remote_id = m->sender;
+ memcpy(tmp_hs.hs_epub, m->ephemeral, WG_KEY_SIZE);
- wg_mix_psk(hs, hs->hs_shared);
+ wg_kdf(tmp_hs.hs_ck, NULL, NULL, tmp_hs.hs_ck, tmp_hs.hs_epub, WG_KEY_SIZE);
+ wg_mix_hash(&tmp_hs, tmp_hs.hs_epub, WG_KEY_SIZE);
- if (!wg_handshake_decrypt(hs, NULL, m->empty, WG_MAC_SIZE))
- return WG_DECRYPT;
+ wg_mix_dh(&tmp_hs, tmp_hs.hs_ekey.priv, tmp_hs.hs_epub);
+ wg_mix_dh(&tmp_hs, tmp_hs.hs_skey->priv, tmp_hs.hs_epub);
- wg_mix_hash(hs, m->empty, WG_ENCRYPTED_SIZE(0));
+ wg_mix_psk(&tmp_hs, tmp_hs.hs_shared);
+
+ if (!wg_handshake_decrypt(&tmp_hs, NULL, m->empty, WG_MAC_SIZE))
+ ret_error(WG_DECRYPT);
- wg_hash2(hs->hs_k, WG_MAC1, strlen(WG_MAC1), hs->hs_skey->pub, WG_KEY_SIZE);
- blake2s(mac1, (void *)m, hs->hs_k, sizeof(mac1), offsetof(struct wg_msg_response, mac1), sizeof(hs->hs_k));
+ wg_mix_hash(&tmp_hs, m->empty, WG_ENCRYPTED_SIZE(0));
+
+ wg_hash2(tmp_hs.hs_k, WG_MAC1, strlen(WG_MAC1), tmp_hs.hs_skey->pub, WG_KEY_SIZE);
+ blake2s(mac1, (void *)m, tmp_hs.hs_k, sizeof(mac1), offsetof(struct wg_msg_response, mac1), sizeof(tmp_hs.hs_k));
if (timingsafe_bcmp(mac1, m->mac1, WG_MAC_SIZE))
- return WG_MAC;
+ ret_error(WG_MAC);
- return WG_OK;
+ *hs = tmp_hs;
+ hs->hs_state = WG_STATE_RECV_RESPONSE;
+leave:
+ rw_exit_write(&hs->hs_lock);
+ explicit_bzero(&tmp_hs, sizeof(tmp_hs));
+ return ret;
}
enum wg_error
@@ -537,38 +582,76 @@ wg_handshake_recv_cookie(struct wg_handshake *hs, struct wg_msg_cookie *m)
{
uint8_t key[WG_KEY_SIZE];
uint8_t value[WG_COOKIE_SIZE];
+ enum wg_error ret = WG_OK;
+
+ rw_enter_write(&hs->hs_lock);
wg_hash2(key, WG_COOKIE, strlen(WG_COOKIE), hs->hs_spub, WG_KEY_SIZE);
if(!xchacha20poly1305_decrypt(value, m->cookie, sizeof(m->cookie),
hs->hs_mac, WG_MAC_SIZE, m->nonce, key))
- return WG_DECRYPT;
+ ret_error(WG_DECRYPT);
memcpy(hs->hs_cookie.cookie, value, sizeof(hs->hs_cookie.cookie));
getnanotime(&hs->hs_cookie.time);
- return WG_OK;
+leave:
+ rw_exit_write(&hs->hs_lock);
+ return ret;
+}
+
+enum wg_error
+wg_session_encrypt(struct wg_session *s, struct wg_msg_transport *m, size_t len)
+{
+ enum wg_error ret = WG_OK;
+
+ rw_enter_read(&s->s_lock);
+
+ if (wg_session_reject_tx(s))
+ ret_error(WG_REJECT);
+
+ m->type = WG_MSG_TRANSPORT;
+ m->receiver = s->s_remote_id;
+ m->counter = s->s_txcounter++; /* TODO make atomic */
+
+ chacha20poly1305_encrypt(m->data, m->data, len, NULL, 0, m->counter, s->s_txkey);
+
+ /* Packet OK, but we do want a rekey */
+ if (wg_session_rekey_tx(s))
+ ret = WG_REKEY;
+leave:
+ rw_exit_read(&s->s_lock);
+ return ret;
}
enum wg_error
wg_session_decrypt(struct wg_session *s, struct wg_msg_transport *m, size_t len)
{
- size_t data_len = len - offsetof(struct wg_msg_transport, data);
+ enum wg_error ret = WG_OK;
+ size_t data_len = len - offsetof(struct wg_msg_transport, data);
+
+ rw_enter_read(&s->s_lock);
/* Check session is valid */
if (wg_session_reject_rx(s))
- return WG_REJECT;
+ ret_error(WG_REJECT);
/* Decrypt session */
if (!chacha20poly1305_decrypt(m->data, m->data, data_len, NULL, 0, m->counter, s->s_rxkey))
- return WG_DECRYPT;
+ ret_error(WG_DECRYPT);
/* Check for replay */
if (wg_antireplay_check(&s->s_ar, m->counter))
- return WG_REPLAY;
+ ret_error(WG_REPLAY);
s->s_rxcounter = m->counter;
- return WG_OK;
+
+ /* Packet OK, but we do want a rekey */
+ if (wg_session_rekey_tx(s))
+ ret = WG_REKEY;
+leave:
+ rw_exit_read(&s->s_lock);
+ return ret;
}
void
diff --git a/src/wireguard.h b/src/wireguard.h
index 0be4645345d..c0d8834bc5b 100644
--- a/src/wireguard.h
+++ b/src/wireguard.h
@@ -33,16 +33,14 @@
#define WG_COOKIE_VALID_TIME 120
#define WG_REKEY_AFTER_TIME_RECV (WG_REJECT_AFTER_TIME - WG_KEEPALIVE_TIMEOUT - WG_REKEY_TIMEOUT)
-enum wg_role {
- WG_ROLE_UNKNOWN = 0,
- WG_ROLE_INITIATOR,
- WG_ROLE_RESPONDER,
-};
-
-enum wg_keytype {
- WG_KEY_PSK,
- WG_KEY_LOCAL,
- WG_KEY_REMOTE,
+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 {
@@ -66,7 +64,7 @@ struct wg_cookie {
struct wg_handshake {
struct rwlock hs_lock;
- enum wg_role hs_role;
+ enum wg_state hs_state;
uint32_t hs_local_id;
uint32_t hs_remote_id;
uint8_t hs_mac[WG_MAC_SIZE];
@@ -86,7 +84,7 @@ struct wg_handshake {
struct wg_session {
struct rwlock s_lock;
- enum wg_role s_role;
+ enum wg_state s_state;
uint64_t s_txcounter;
uint64_t s_rxcounter;
uint32_t s_local_id;
@@ -142,9 +140,11 @@ struct wg_msg_transport {
enum wg_error {
WG_OK = 0,
+ WG_TIMESTAMP,
WG_DECRYPT,
WG_REPLAY,
WG_REJECT,
+ WG_REKEY,
WG_STATE,
WG_MAC,
};
@@ -157,28 +157,24 @@ uint32_t 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 *kp, struct wg_cookie *, uint32_t, uint8_t *, uint8_t, uint8_t mac[WG_MAC_SIZE], struct wg_msg_cookie *);
-enum wg_error wg_session_encrypt(struct wg_session *, struct wg_msg_transport *, size_t);
-enum wg_error wg_handshake_valid_mac2(struct wg_cookie *, uint8_t *, uint8_t);
-
-enum wg_error wg_handshake_recv_initiation(struct wg_handshake *, struct wg_msg_initiation *);
+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_session_decrypt(struct wg_session *, struct wg_msg_transport *, size_t);
-void wg_handshake_clone(struct wg_handshake *, struct wg_handshake *);
-void wg_session_from_handshake(struct wg_session *, struct wg_handshake *);
+enum wg_error wg_handshake_valid_mac2(struct wg_cookie *, uint8_t *, uint8_t);
+enum wg_error wg_handshake_merge(struct wg_handshake *, struct wg_handshake *);
-int wg_session_reject_tx(struct wg_session *);
-int wg_session_reject_rx(struct wg_session *);
-int wg_session_rekey_tx(struct wg_session *);
-int wg_session_rekey_rx(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 *);
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);
+
/* TODO make better */
+void wg_keypair_generate(struct wg_keypair *);
int wg_timespec_timedout(struct timespec *, time_t);
-enum wg_pkt_type wg_pkt_type(uint8_t *, size_t);
#endif /* _LIBWG_H_ */