forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
236 lines
7.0 KiB
236 lines
7.0 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (C) 2015-2019 Jason A. Donenfeld <[email protected]>. All Rights Reserved. |
|
*/ |
|
|
|
#include "cookie.h" |
|
#include "peer.h" |
|
#include "device.h" |
|
#include "messages.h" |
|
#include "ratelimiter.h" |
|
#include "timers.h" |
|
|
|
#include <zinc/blake2s.h> |
|
#include <zinc/chacha20poly1305.h> |
|
|
|
#include <net/ipv6.h> |
|
#include <crypto/algapi.h> |
|
|
|
void wg_cookie_checker_init(struct cookie_checker *checker, |
|
struct wg_device *wg) |
|
{ |
|
init_rwsem(&checker->secret_lock); |
|
checker->secret_birthdate = ktime_get_coarse_boottime_ns(); |
|
get_random_bytes(checker->secret, NOISE_HASH_LEN); |
|
checker->device = wg; |
|
} |
|
|
|
enum { COOKIE_KEY_LABEL_LEN = 8 }; |
|
static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] = "mac1----"; |
|
static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] = "cookie--"; |
|
|
|
static void precompute_key(u8 key[NOISE_SYMMETRIC_KEY_LEN], |
|
const u8 pubkey[NOISE_PUBLIC_KEY_LEN], |
|
const u8 label[COOKIE_KEY_LABEL_LEN]) |
|
{ |
|
struct blake2s_state blake; |
|
|
|
blake2s_init(&blake, NOISE_SYMMETRIC_KEY_LEN); |
|
blake2s_update(&blake, label, COOKIE_KEY_LABEL_LEN); |
|
blake2s_update(&blake, pubkey, NOISE_PUBLIC_KEY_LEN); |
|
blake2s_final(&blake, key); |
|
} |
|
|
|
/* Must hold peer->handshake.static_identity->lock */ |
|
void wg_cookie_checker_precompute_device_keys(struct cookie_checker *checker) |
|
{ |
|
if (likely(checker->device->static_identity.has_identity)) { |
|
precompute_key(checker->cookie_encryption_key, |
|
checker->device->static_identity.static_public, |
|
cookie_key_label); |
|
precompute_key(checker->message_mac1_key, |
|
checker->device->static_identity.static_public, |
|
mac1_key_label); |
|
} else { |
|
memset(checker->cookie_encryption_key, 0, |
|
NOISE_SYMMETRIC_KEY_LEN); |
|
memset(checker->message_mac1_key, 0, NOISE_SYMMETRIC_KEY_LEN); |
|
} |
|
} |
|
|
|
void wg_cookie_checker_precompute_peer_keys(struct wg_peer *peer) |
|
{ |
|
precompute_key(peer->latest_cookie.cookie_decryption_key, |
|
peer->handshake.remote_static, cookie_key_label); |
|
precompute_key(peer->latest_cookie.message_mac1_key, |
|
peer->handshake.remote_static, mac1_key_label); |
|
} |
|
|
|
void wg_cookie_init(struct cookie *cookie) |
|
{ |
|
memset(cookie, 0, sizeof(*cookie)); |
|
init_rwsem(&cookie->lock); |
|
} |
|
|
|
static void compute_mac1(u8 mac1[COOKIE_LEN], const void *message, size_t len, |
|
const u8 key[NOISE_SYMMETRIC_KEY_LEN]) |
|
{ |
|
len = len - sizeof(struct message_macs) + |
|
offsetof(struct message_macs, mac1); |
|
blake2s(mac1, message, key, COOKIE_LEN, len, NOISE_SYMMETRIC_KEY_LEN); |
|
} |
|
|
|
static void compute_mac2(u8 mac2[COOKIE_LEN], const void *message, size_t len, |
|
const u8 cookie[COOKIE_LEN]) |
|
{ |
|
len = len - sizeof(struct message_macs) + |
|
offsetof(struct message_macs, mac2); |
|
blake2s(mac2, message, cookie, COOKIE_LEN, len, COOKIE_LEN); |
|
} |
|
|
|
static void make_cookie(u8 cookie[COOKIE_LEN], struct sk_buff *skb, |
|
struct cookie_checker *checker) |
|
{ |
|
struct blake2s_state state; |
|
|
|
if (wg_birthdate_has_expired(checker->secret_birthdate, |
|
COOKIE_SECRET_MAX_AGE)) { |
|
down_write(&checker->secret_lock); |
|
checker->secret_birthdate = ktime_get_coarse_boottime_ns(); |
|
get_random_bytes(checker->secret, NOISE_HASH_LEN); |
|
up_write(&checker->secret_lock); |
|
} |
|
|
|
down_read(&checker->secret_lock); |
|
|
|
blake2s_init_key(&state, COOKIE_LEN, checker->secret, NOISE_HASH_LEN); |
|
if (skb->protocol == htons(ETH_P_IP)) |
|
blake2s_update(&state, (u8 *)&ip_hdr(skb)->saddr, |
|
sizeof(struct in_addr)); |
|
else if (skb->protocol == htons(ETH_P_IPV6)) |
|
blake2s_update(&state, (u8 *)&ipv6_hdr(skb)->saddr, |
|
sizeof(struct in6_addr)); |
|
blake2s_update(&state, (u8 *)&udp_hdr(skb)->source, sizeof(__be16)); |
|
blake2s_final(&state, cookie); |
|
|
|
up_read(&checker->secret_lock); |
|
} |
|
|
|
enum cookie_mac_state wg_cookie_validate_packet(struct cookie_checker *checker, |
|
struct sk_buff *skb, |
|
bool check_cookie) |
|
{ |
|
struct message_macs *macs = (struct message_macs *) |
|
(skb->data + skb->len - sizeof(*macs)); |
|
enum cookie_mac_state ret; |
|
u8 computed_mac[COOKIE_LEN]; |
|
u8 cookie[COOKIE_LEN]; |
|
|
|
ret = INVALID_MAC; |
|
compute_mac1(computed_mac, skb->data, skb->len, |
|
checker->message_mac1_key); |
|
if (crypto_memneq(computed_mac, macs->mac1, COOKIE_LEN)) |
|
goto out; |
|
|
|
ret = VALID_MAC_BUT_NO_COOKIE; |
|
|
|
if (!check_cookie) |
|
goto out; |
|
|
|
make_cookie(cookie, skb, checker); |
|
|
|
compute_mac2(computed_mac, skb->data, skb->len, cookie); |
|
if (crypto_memneq(computed_mac, macs->mac2, COOKIE_LEN)) |
|
goto out; |
|
|
|
ret = VALID_MAC_WITH_COOKIE_BUT_RATELIMITED; |
|
if (!wg_ratelimiter_allow(skb, dev_net(checker->device->dev))) |
|
goto out; |
|
|
|
ret = VALID_MAC_WITH_COOKIE; |
|
|
|
out: |
|
return ret; |
|
} |
|
|
|
void wg_cookie_add_mac_to_packet(void *message, size_t len, |
|
struct wg_peer *peer) |
|
{ |
|
struct message_macs *macs = (struct message_macs *) |
|
((u8 *)message + len - sizeof(*macs)); |
|
|
|
down_write(&peer->latest_cookie.lock); |
|
compute_mac1(macs->mac1, message, len, |
|
peer->latest_cookie.message_mac1_key); |
|
memcpy(peer->latest_cookie.last_mac1_sent, macs->mac1, COOKIE_LEN); |
|
peer->latest_cookie.have_sent_mac1 = true; |
|
up_write(&peer->latest_cookie.lock); |
|
|
|
down_read(&peer->latest_cookie.lock); |
|
if (peer->latest_cookie.is_valid && |
|
!wg_birthdate_has_expired(peer->latest_cookie.birthdate, |
|
COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY)) |
|
compute_mac2(macs->mac2, message, len, |
|
peer->latest_cookie.cookie); |
|
else |
|
memset(macs->mac2, 0, COOKIE_LEN); |
|
up_read(&peer->latest_cookie.lock); |
|
} |
|
|
|
void wg_cookie_message_create(struct message_handshake_cookie *dst, |
|
struct sk_buff *skb, __le32 index, |
|
struct cookie_checker *checker) |
|
{ |
|
struct message_macs *macs = (struct message_macs *) |
|
((u8 *)skb->data + skb->len - sizeof(*macs)); |
|
u8 cookie[COOKIE_LEN]; |
|
|
|
dst->header.type = cpu_to_le32(MESSAGE_HANDSHAKE_COOKIE); |
|
dst->receiver_index = index; |
|
get_random_bytes_wait(dst->nonce, COOKIE_NONCE_LEN); |
|
|
|
make_cookie(cookie, skb, checker); |
|
xchacha20poly1305_encrypt(dst->encrypted_cookie, cookie, COOKIE_LEN, |
|
macs->mac1, COOKIE_LEN, dst->nonce, |
|
checker->cookie_encryption_key); |
|
} |
|
|
|
void wg_cookie_message_consume(struct message_handshake_cookie *src, |
|
struct wg_device *wg) |
|
{ |
|
struct wg_peer *peer = NULL; |
|
u8 cookie[COOKIE_LEN]; |
|
bool ret; |
|
|
|
if (unlikely(!wg_index_hashtable_lookup(wg->index_hashtable, |
|
INDEX_HASHTABLE_HANDSHAKE | |
|
INDEX_HASHTABLE_KEYPAIR, |
|
src->receiver_index, &peer))) |
|
return; |
|
|
|
down_read(&peer->latest_cookie.lock); |
|
if (unlikely(!peer->latest_cookie.have_sent_mac1)) { |
|
up_read(&peer->latest_cookie.lock); |
|
goto out; |
|
} |
|
ret = xchacha20poly1305_decrypt( |
|
cookie, src->encrypted_cookie, sizeof(src->encrypted_cookie), |
|
peer->latest_cookie.last_mac1_sent, COOKIE_LEN, src->nonce, |
|
peer->latest_cookie.cookie_decryption_key); |
|
up_read(&peer->latest_cookie.lock); |
|
|
|
if (ret) { |
|
down_write(&peer->latest_cookie.lock); |
|
memcpy(peer->latest_cookie.cookie, cookie, COOKIE_LEN); |
|
peer->latest_cookie.birthdate = ktime_get_coarse_boottime_ns(); |
|
peer->latest_cookie.is_valid = true; |
|
peer->latest_cookie.have_sent_mac1 = false; |
|
up_write(&peer->latest_cookie.lock); |
|
} else { |
|
net_dbg_ratelimited("%s: Could not decrypt invalid cookie response\n", |
|
wg->dev->name); |
|
} |
|
|
|
out: |
|
wg_peer_put(peer); |
|
}
|
|
|