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