18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include "cookie.h"
78c2ecf20Sopenharmony_ci#include "peer.h"
88c2ecf20Sopenharmony_ci#include "device.h"
98c2ecf20Sopenharmony_ci#include "messages.h"
108c2ecf20Sopenharmony_ci#include "ratelimiter.h"
118c2ecf20Sopenharmony_ci#include "timers.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <crypto/blake2s.h>
148c2ecf20Sopenharmony_ci#include <crypto/chacha20poly1305.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <net/ipv6.h>
178c2ecf20Sopenharmony_ci#include <crypto/algapi.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_civoid wg_cookie_checker_init(struct cookie_checker *checker,
208c2ecf20Sopenharmony_ci			    struct wg_device *wg)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	init_rwsem(&checker->secret_lock);
238c2ecf20Sopenharmony_ci	checker->secret_birthdate = ktime_get_coarse_boottime_ns();
248c2ecf20Sopenharmony_ci	get_random_bytes(checker->secret, NOISE_HASH_LEN);
258c2ecf20Sopenharmony_ci	checker->device = wg;
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cienum { COOKIE_KEY_LABEL_LEN = 8 };
298c2ecf20Sopenharmony_cistatic const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] = "mac1----";
308c2ecf20Sopenharmony_cistatic const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] = "cookie--";
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic void precompute_key(u8 key[NOISE_SYMMETRIC_KEY_LEN],
338c2ecf20Sopenharmony_ci			   const u8 pubkey[NOISE_PUBLIC_KEY_LEN],
348c2ecf20Sopenharmony_ci			   const u8 label[COOKIE_KEY_LABEL_LEN])
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	struct blake2s_state blake;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	blake2s_init(&blake, NOISE_SYMMETRIC_KEY_LEN);
398c2ecf20Sopenharmony_ci	blake2s_update(&blake, label, COOKIE_KEY_LABEL_LEN);
408c2ecf20Sopenharmony_ci	blake2s_update(&blake, pubkey, NOISE_PUBLIC_KEY_LEN);
418c2ecf20Sopenharmony_ci	blake2s_final(&blake, key);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci/* Must hold peer->handshake.static_identity->lock */
458c2ecf20Sopenharmony_civoid wg_cookie_checker_precompute_device_keys(struct cookie_checker *checker)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	if (likely(checker->device->static_identity.has_identity)) {
488c2ecf20Sopenharmony_ci		precompute_key(checker->cookie_encryption_key,
498c2ecf20Sopenharmony_ci			       checker->device->static_identity.static_public,
508c2ecf20Sopenharmony_ci			       cookie_key_label);
518c2ecf20Sopenharmony_ci		precompute_key(checker->message_mac1_key,
528c2ecf20Sopenharmony_ci			       checker->device->static_identity.static_public,
538c2ecf20Sopenharmony_ci			       mac1_key_label);
548c2ecf20Sopenharmony_ci	} else {
558c2ecf20Sopenharmony_ci		memset(checker->cookie_encryption_key, 0,
568c2ecf20Sopenharmony_ci		       NOISE_SYMMETRIC_KEY_LEN);
578c2ecf20Sopenharmony_ci		memset(checker->message_mac1_key, 0, NOISE_SYMMETRIC_KEY_LEN);
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_civoid wg_cookie_checker_precompute_peer_keys(struct wg_peer *peer)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	precompute_key(peer->latest_cookie.cookie_decryption_key,
648c2ecf20Sopenharmony_ci		       peer->handshake.remote_static, cookie_key_label);
658c2ecf20Sopenharmony_ci	precompute_key(peer->latest_cookie.message_mac1_key,
668c2ecf20Sopenharmony_ci		       peer->handshake.remote_static, mac1_key_label);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_civoid wg_cookie_init(struct cookie *cookie)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	memset(cookie, 0, sizeof(*cookie));
728c2ecf20Sopenharmony_ci	init_rwsem(&cookie->lock);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic void compute_mac1(u8 mac1[COOKIE_LEN], const void *message, size_t len,
768c2ecf20Sopenharmony_ci			 const u8 key[NOISE_SYMMETRIC_KEY_LEN])
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	len = len - sizeof(struct message_macs) +
798c2ecf20Sopenharmony_ci	      offsetof(struct message_macs, mac1);
808c2ecf20Sopenharmony_ci	blake2s(mac1, message, key, COOKIE_LEN, len, NOISE_SYMMETRIC_KEY_LEN);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic void compute_mac2(u8 mac2[COOKIE_LEN], const void *message, size_t len,
848c2ecf20Sopenharmony_ci			 const u8 cookie[COOKIE_LEN])
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	len = len - sizeof(struct message_macs) +
878c2ecf20Sopenharmony_ci	      offsetof(struct message_macs, mac2);
888c2ecf20Sopenharmony_ci	blake2s(mac2, message, cookie, COOKIE_LEN, len, COOKIE_LEN);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void make_cookie(u8 cookie[COOKIE_LEN], struct sk_buff *skb,
928c2ecf20Sopenharmony_ci			struct cookie_checker *checker)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct blake2s_state state;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (wg_birthdate_has_expired(checker->secret_birthdate,
978c2ecf20Sopenharmony_ci				     COOKIE_SECRET_MAX_AGE)) {
988c2ecf20Sopenharmony_ci		down_write(&checker->secret_lock);
998c2ecf20Sopenharmony_ci		checker->secret_birthdate = ktime_get_coarse_boottime_ns();
1008c2ecf20Sopenharmony_ci		get_random_bytes(checker->secret, NOISE_HASH_LEN);
1018c2ecf20Sopenharmony_ci		up_write(&checker->secret_lock);
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	down_read(&checker->secret_lock);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	blake2s_init_key(&state, COOKIE_LEN, checker->secret, NOISE_HASH_LEN);
1078c2ecf20Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP))
1088c2ecf20Sopenharmony_ci		blake2s_update(&state, (u8 *)&ip_hdr(skb)->saddr,
1098c2ecf20Sopenharmony_ci			       sizeof(struct in_addr));
1108c2ecf20Sopenharmony_ci	else if (skb->protocol == htons(ETH_P_IPV6))
1118c2ecf20Sopenharmony_ci		blake2s_update(&state, (u8 *)&ipv6_hdr(skb)->saddr,
1128c2ecf20Sopenharmony_ci			       sizeof(struct in6_addr));
1138c2ecf20Sopenharmony_ci	blake2s_update(&state, (u8 *)&udp_hdr(skb)->source, sizeof(__be16));
1148c2ecf20Sopenharmony_ci	blake2s_final(&state, cookie);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	up_read(&checker->secret_lock);
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cienum cookie_mac_state wg_cookie_validate_packet(struct cookie_checker *checker,
1208c2ecf20Sopenharmony_ci						struct sk_buff *skb,
1218c2ecf20Sopenharmony_ci						bool check_cookie)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	struct message_macs *macs = (struct message_macs *)
1248c2ecf20Sopenharmony_ci		(skb->data + skb->len - sizeof(*macs));
1258c2ecf20Sopenharmony_ci	enum cookie_mac_state ret;
1268c2ecf20Sopenharmony_ci	u8 computed_mac[COOKIE_LEN];
1278c2ecf20Sopenharmony_ci	u8 cookie[COOKIE_LEN];
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	ret = INVALID_MAC;
1308c2ecf20Sopenharmony_ci	compute_mac1(computed_mac, skb->data, skb->len,
1318c2ecf20Sopenharmony_ci		     checker->message_mac1_key);
1328c2ecf20Sopenharmony_ci	if (crypto_memneq(computed_mac, macs->mac1, COOKIE_LEN))
1338c2ecf20Sopenharmony_ci		goto out;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	ret = VALID_MAC_BUT_NO_COOKIE;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (!check_cookie)
1388c2ecf20Sopenharmony_ci		goto out;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	make_cookie(cookie, skb, checker);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	compute_mac2(computed_mac, skb->data, skb->len, cookie);
1438c2ecf20Sopenharmony_ci	if (crypto_memneq(computed_mac, macs->mac2, COOKIE_LEN))
1448c2ecf20Sopenharmony_ci		goto out;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	ret = VALID_MAC_WITH_COOKIE_BUT_RATELIMITED;
1478c2ecf20Sopenharmony_ci	if (!wg_ratelimiter_allow(skb, dev_net(checker->device->dev)))
1488c2ecf20Sopenharmony_ci		goto out;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	ret = VALID_MAC_WITH_COOKIE;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ciout:
1538c2ecf20Sopenharmony_ci	return ret;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_civoid wg_cookie_add_mac_to_packet(void *message, size_t len,
1578c2ecf20Sopenharmony_ci				 struct wg_peer *peer)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct message_macs *macs = (struct message_macs *)
1608c2ecf20Sopenharmony_ci		((u8 *)message + len - sizeof(*macs));
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	down_write(&peer->latest_cookie.lock);
1638c2ecf20Sopenharmony_ci	compute_mac1(macs->mac1, message, len,
1648c2ecf20Sopenharmony_ci		     peer->latest_cookie.message_mac1_key);
1658c2ecf20Sopenharmony_ci	memcpy(peer->latest_cookie.last_mac1_sent, macs->mac1, COOKIE_LEN);
1668c2ecf20Sopenharmony_ci	peer->latest_cookie.have_sent_mac1 = true;
1678c2ecf20Sopenharmony_ci	up_write(&peer->latest_cookie.lock);
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	down_read(&peer->latest_cookie.lock);
1708c2ecf20Sopenharmony_ci	if (peer->latest_cookie.is_valid &&
1718c2ecf20Sopenharmony_ci	    !wg_birthdate_has_expired(peer->latest_cookie.birthdate,
1728c2ecf20Sopenharmony_ci				COOKIE_SECRET_MAX_AGE - COOKIE_SECRET_LATENCY))
1738c2ecf20Sopenharmony_ci		compute_mac2(macs->mac2, message, len,
1748c2ecf20Sopenharmony_ci			     peer->latest_cookie.cookie);
1758c2ecf20Sopenharmony_ci	else
1768c2ecf20Sopenharmony_ci		memset(macs->mac2, 0, COOKIE_LEN);
1778c2ecf20Sopenharmony_ci	up_read(&peer->latest_cookie.lock);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_civoid wg_cookie_message_create(struct message_handshake_cookie *dst,
1818c2ecf20Sopenharmony_ci			      struct sk_buff *skb, __le32 index,
1828c2ecf20Sopenharmony_ci			      struct cookie_checker *checker)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	struct message_macs *macs = (struct message_macs *)
1858c2ecf20Sopenharmony_ci		((u8 *)skb->data + skb->len - sizeof(*macs));
1868c2ecf20Sopenharmony_ci	u8 cookie[COOKIE_LEN];
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	dst->header.type = cpu_to_le32(MESSAGE_HANDSHAKE_COOKIE);
1898c2ecf20Sopenharmony_ci	dst->receiver_index = index;
1908c2ecf20Sopenharmony_ci	get_random_bytes_wait(dst->nonce, COOKIE_NONCE_LEN);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	make_cookie(cookie, skb, checker);
1938c2ecf20Sopenharmony_ci	xchacha20poly1305_encrypt(dst->encrypted_cookie, cookie, COOKIE_LEN,
1948c2ecf20Sopenharmony_ci				  macs->mac1, COOKIE_LEN, dst->nonce,
1958c2ecf20Sopenharmony_ci				  checker->cookie_encryption_key);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_civoid wg_cookie_message_consume(struct message_handshake_cookie *src,
1998c2ecf20Sopenharmony_ci			       struct wg_device *wg)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct wg_peer *peer = NULL;
2028c2ecf20Sopenharmony_ci	u8 cookie[COOKIE_LEN];
2038c2ecf20Sopenharmony_ci	bool ret;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (unlikely(!wg_index_hashtable_lookup(wg->index_hashtable,
2068c2ecf20Sopenharmony_ci						INDEX_HASHTABLE_HANDSHAKE |
2078c2ecf20Sopenharmony_ci						INDEX_HASHTABLE_KEYPAIR,
2088c2ecf20Sopenharmony_ci						src->receiver_index, &peer)))
2098c2ecf20Sopenharmony_ci		return;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	down_read(&peer->latest_cookie.lock);
2128c2ecf20Sopenharmony_ci	if (unlikely(!peer->latest_cookie.have_sent_mac1)) {
2138c2ecf20Sopenharmony_ci		up_read(&peer->latest_cookie.lock);
2148c2ecf20Sopenharmony_ci		goto out;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci	ret = xchacha20poly1305_decrypt(
2178c2ecf20Sopenharmony_ci		cookie, src->encrypted_cookie, sizeof(src->encrypted_cookie),
2188c2ecf20Sopenharmony_ci		peer->latest_cookie.last_mac1_sent, COOKIE_LEN, src->nonce,
2198c2ecf20Sopenharmony_ci		peer->latest_cookie.cookie_decryption_key);
2208c2ecf20Sopenharmony_ci	up_read(&peer->latest_cookie.lock);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (ret) {
2238c2ecf20Sopenharmony_ci		down_write(&peer->latest_cookie.lock);
2248c2ecf20Sopenharmony_ci		memcpy(peer->latest_cookie.cookie, cookie, COOKIE_LEN);
2258c2ecf20Sopenharmony_ci		peer->latest_cookie.birthdate = ktime_get_coarse_boottime_ns();
2268c2ecf20Sopenharmony_ci		peer->latest_cookie.is_valid = true;
2278c2ecf20Sopenharmony_ci		peer->latest_cookie.have_sent_mac1 = false;
2288c2ecf20Sopenharmony_ci		up_write(&peer->latest_cookie.lock);
2298c2ecf20Sopenharmony_ci	} else {
2308c2ecf20Sopenharmony_ci		net_dbg_ratelimited("%s: Could not decrypt invalid cookie response\n",
2318c2ecf20Sopenharmony_ci				    wg->dev->name);
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ciout:
2358c2ecf20Sopenharmony_ci	wg_peer_put(peer);
2368c2ecf20Sopenharmony_ci}
237