| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | From: "Jason A. Donenfeld" <Jason@zx2c4.com> |
| 3 | Date: Wed, 18 Mar 2020 18:30:47 -0600 |
| 4 | Subject: [PATCH] wireguard: noise: error out precomputed DH during handshake |
| 5 | rather than config |
| 6 | |
| 7 | commit 11a7686aa99c7fe4b3f80f6dcccd54129817984d upstream. |
| 8 | |
| 9 | We precompute the static-static ECDH during configuration time, in order |
| 10 | to save an expensive computation later when receiving network packets. |
| 11 | However, not all ECDH computations yield a contributory result. Prior, |
| 12 | we were just not letting those peers be added to the interface. However, |
| 13 | this creates a strange inconsistency, since it was still possible to add |
| 14 | other weird points, like a valid public key plus a low-order point, and, |
| 15 | like points that result in zeros, a handshake would not complete. In |
| 16 | order to make the behavior more uniform and less surprising, simply |
| 17 | allow all peers to be added. Then, we'll error out later when doing the |
| 18 | crypto if there's an issue. This also adds more separation between the |
| 19 | crypto layer and the configuration layer. |
| 20 | |
| 21 | Discussed-with: Mathias Hall-Andersen <mathias@hall-andersen.dk> |
| 22 | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> |
| 23 | Signed-off-by: David S. Miller <davem@davemloft.net> |
| 24 | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com> |
| 25 | --- |
| 26 | drivers/net/wireguard/netlink.c | 8 +--- |
| 27 | drivers/net/wireguard/noise.c | 55 ++++++++++++---------- |
| 28 | drivers/net/wireguard/noise.h | 12 ++--- |
| 29 | drivers/net/wireguard/peer.c | 7 +-- |
| 30 | tools/testing/selftests/wireguard/netns.sh | 15 ++++-- |
| 31 | 5 files changed, 49 insertions(+), 48 deletions(-) |
| 32 | |
| 33 | --- a/drivers/net/wireguard/netlink.c |
| 34 | +++ b/drivers/net/wireguard/netlink.c |
| 35 | @@ -417,11 +417,7 @@ static int set_peer(struct wg_device *wg |
| 36 | |
| 37 | peer = wg_peer_create(wg, public_key, preshared_key); |
| 38 | if (IS_ERR(peer)) { |
| 39 | - /* Similar to the above, if the key is invalid, we skip |
| 40 | - * it without fanfare, so that services don't need to |
| 41 | - * worry about doing key validation themselves. |
| 42 | - */ |
| 43 | - ret = PTR_ERR(peer) == -EKEYREJECTED ? 0 : PTR_ERR(peer); |
| 44 | + ret = PTR_ERR(peer); |
| 45 | peer = NULL; |
| 46 | goto out; |
| 47 | } |
| 48 | @@ -575,7 +571,7 @@ static int wg_set_device(struct sk_buff |
| 49 | private_key); |
| 50 | list_for_each_entry_safe(peer, temp, &wg->peer_list, |
| 51 | peer_list) { |
| 52 | - BUG_ON(!wg_noise_precompute_static_static(peer)); |
| 53 | + wg_noise_precompute_static_static(peer); |
| 54 | wg_noise_expire_current_peer_keypairs(peer); |
| 55 | } |
| 56 | wg_cookie_checker_precompute_device_keys(&wg->cookie_checker); |
| 57 | --- a/drivers/net/wireguard/noise.c |
| 58 | +++ b/drivers/net/wireguard/noise.c |
| 59 | @@ -44,32 +44,23 @@ void __init wg_noise_init(void) |
| 60 | } |
| 61 | |
| 62 | /* Must hold peer->handshake.static_identity->lock */ |
| 63 | -bool wg_noise_precompute_static_static(struct wg_peer *peer) |
| 64 | +void wg_noise_precompute_static_static(struct wg_peer *peer) |
| 65 | { |
| 66 | - bool ret; |
| 67 | - |
| 68 | down_write(&peer->handshake.lock); |
| 69 | - if (peer->handshake.static_identity->has_identity) { |
| 70 | - ret = curve25519( |
| 71 | - peer->handshake.precomputed_static_static, |
| 72 | + if (!peer->handshake.static_identity->has_identity || |
| 73 | + !curve25519(peer->handshake.precomputed_static_static, |
| 74 | peer->handshake.static_identity->static_private, |
| 75 | - peer->handshake.remote_static); |
| 76 | - } else { |
| 77 | - u8 empty[NOISE_PUBLIC_KEY_LEN] = { 0 }; |
| 78 | - |
| 79 | - ret = curve25519(empty, empty, peer->handshake.remote_static); |
| 80 | + peer->handshake.remote_static)) |
| 81 | memset(peer->handshake.precomputed_static_static, 0, |
| 82 | NOISE_PUBLIC_KEY_LEN); |
| 83 | - } |
| 84 | up_write(&peer->handshake.lock); |
| 85 | - return ret; |
| 86 | } |
| 87 | |
| 88 | -bool wg_noise_handshake_init(struct noise_handshake *handshake, |
| 89 | - struct noise_static_identity *static_identity, |
| 90 | - const u8 peer_public_key[NOISE_PUBLIC_KEY_LEN], |
| 91 | - const u8 peer_preshared_key[NOISE_SYMMETRIC_KEY_LEN], |
| 92 | - struct wg_peer *peer) |
| 93 | +void wg_noise_handshake_init(struct noise_handshake *handshake, |
| 94 | + struct noise_static_identity *static_identity, |
| 95 | + const u8 peer_public_key[NOISE_PUBLIC_KEY_LEN], |
| 96 | + const u8 peer_preshared_key[NOISE_SYMMETRIC_KEY_LEN], |
| 97 | + struct wg_peer *peer) |
| 98 | { |
| 99 | memset(handshake, 0, sizeof(*handshake)); |
| 100 | init_rwsem(&handshake->lock); |
| 101 | @@ -81,7 +72,7 @@ bool wg_noise_handshake_init(struct nois |
| 102 | NOISE_SYMMETRIC_KEY_LEN); |
| 103 | handshake->static_identity = static_identity; |
| 104 | handshake->state = HANDSHAKE_ZEROED; |
| 105 | - return wg_noise_precompute_static_static(peer); |
| 106 | + wg_noise_precompute_static_static(peer); |
| 107 | } |
| 108 | |
| 109 | static void handshake_zero(struct noise_handshake *handshake) |
| 110 | @@ -403,6 +394,19 @@ static bool __must_check mix_dh(u8 chain |
| 111 | return true; |
| 112 | } |
| 113 | |
| 114 | +static bool __must_check mix_precomputed_dh(u8 chaining_key[NOISE_HASH_LEN], |
| 115 | + u8 key[NOISE_SYMMETRIC_KEY_LEN], |
| 116 | + const u8 precomputed[NOISE_PUBLIC_KEY_LEN]) |
| 117 | +{ |
| 118 | + static u8 zero_point[NOISE_PUBLIC_KEY_LEN]; |
| 119 | + if (unlikely(!crypto_memneq(precomputed, zero_point, NOISE_PUBLIC_KEY_LEN))) |
| 120 | + return false; |
| 121 | + kdf(chaining_key, key, NULL, precomputed, NOISE_HASH_LEN, |
| 122 | + NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN, |
| 123 | + chaining_key); |
| 124 | + return true; |
| 125 | +} |
| 126 | + |
| 127 | static void mix_hash(u8 hash[NOISE_HASH_LEN], const u8 *src, size_t src_len) |
| 128 | { |
| 129 | struct blake2s_state blake; |
| 130 | @@ -531,10 +535,9 @@ wg_noise_handshake_create_initiation(str |
| 131 | NOISE_PUBLIC_KEY_LEN, key, handshake->hash); |
| 132 | |
| 133 | /* ss */ |
| 134 | - kdf(handshake->chaining_key, key, NULL, |
| 135 | - handshake->precomputed_static_static, NOISE_HASH_LEN, |
| 136 | - NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN, |
| 137 | - handshake->chaining_key); |
| 138 | + if (!mix_precomputed_dh(handshake->chaining_key, key, |
| 139 | + handshake->precomputed_static_static)) |
| 140 | + goto out; |
| 141 | |
| 142 | /* {t} */ |
| 143 | tai64n_now(timestamp); |
| 144 | @@ -595,9 +598,9 @@ wg_noise_handshake_consume_initiation(st |
| 145 | handshake = &peer->handshake; |
| 146 | |
| 147 | /* ss */ |
| 148 | - kdf(chaining_key, key, NULL, handshake->precomputed_static_static, |
| 149 | - NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN, |
| 150 | - chaining_key); |
| 151 | + if (!mix_precomputed_dh(chaining_key, key, |
| 152 | + handshake->precomputed_static_static)) |
| 153 | + goto out; |
| 154 | |
| 155 | /* {t} */ |
| 156 | if (!message_decrypt(t, src->encrypted_timestamp, |
| 157 | --- a/drivers/net/wireguard/noise.h |
| 158 | +++ b/drivers/net/wireguard/noise.h |
| 159 | @@ -94,11 +94,11 @@ struct noise_handshake { |
| 160 | struct wg_device; |
| 161 | |
| 162 | void wg_noise_init(void); |
| 163 | -bool wg_noise_handshake_init(struct noise_handshake *handshake, |
| 164 | - struct noise_static_identity *static_identity, |
| 165 | - const u8 peer_public_key[NOISE_PUBLIC_KEY_LEN], |
| 166 | - const u8 peer_preshared_key[NOISE_SYMMETRIC_KEY_LEN], |
| 167 | - struct wg_peer *peer); |
| 168 | +void wg_noise_handshake_init(struct noise_handshake *handshake, |
| 169 | + struct noise_static_identity *static_identity, |
| 170 | + const u8 peer_public_key[NOISE_PUBLIC_KEY_LEN], |
| 171 | + const u8 peer_preshared_key[NOISE_SYMMETRIC_KEY_LEN], |
| 172 | + struct wg_peer *peer); |
| 173 | void wg_noise_handshake_clear(struct noise_handshake *handshake); |
| 174 | static inline void wg_noise_reset_last_sent_handshake(atomic64_t *handshake_ns) |
| 175 | { |
| 176 | @@ -116,7 +116,7 @@ void wg_noise_expire_current_peer_keypai |
| 177 | void wg_noise_set_static_identity_private_key( |
| 178 | struct noise_static_identity *static_identity, |
| 179 | const u8 private_key[NOISE_PUBLIC_KEY_LEN]); |
| 180 | -bool wg_noise_precompute_static_static(struct wg_peer *peer); |
| 181 | +void wg_noise_precompute_static_static(struct wg_peer *peer); |
| 182 | |
| 183 | bool |
| 184 | wg_noise_handshake_create_initiation(struct message_handshake_initiation *dst, |
| 185 | --- a/drivers/net/wireguard/peer.c |
| 186 | +++ b/drivers/net/wireguard/peer.c |
| 187 | @@ -34,11 +34,8 @@ struct wg_peer *wg_peer_create(struct wg |
| 188 | return ERR_PTR(ret); |
| 189 | peer->device = wg; |
| 190 | |
| 191 | - if (!wg_noise_handshake_init(&peer->handshake, &wg->static_identity, |
| 192 | - public_key, preshared_key, peer)) { |
| 193 | - ret = -EKEYREJECTED; |
| 194 | - goto err_1; |
| 195 | - } |
| 196 | + wg_noise_handshake_init(&peer->handshake, &wg->static_identity, |
| 197 | + public_key, preshared_key, peer); |
| 198 | if (dst_cache_init(&peer->endpoint_cache, GFP_KERNEL)) |
| 199 | goto err_1; |
| 200 | if (wg_packet_queue_init(&peer->tx_queue, wg_packet_tx_worker, false, |
| 201 | --- a/tools/testing/selftests/wireguard/netns.sh |
| 202 | +++ b/tools/testing/selftests/wireguard/netns.sh |
| 203 | @@ -527,11 +527,16 @@ n0 wg set wg0 peer "$pub2" allowed-ips 0 |
| 204 | n0 wg set wg0 peer "$pub2" allowed-ips ::/0,1700::/111,5000::/4,e000::/37,9000::/75 |
| 205 | n0 wg set wg0 peer "$pub2" allowed-ips ::/0 |
| 206 | n0 wg set wg0 peer "$pub2" remove |
| 207 | -low_order_points=( AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 4Ot6fDtBuK4WVuP68Z/EatoJjeucMrH9hmIFFl9JuAA= X5yVvKNQjCSx0LFVnIPvWwREXMRYHI6G2CJO3dCfEVc= 7P///////////////////////////////////////38= 7f///////////////////////////////////////38= 7v///////////////////////////////////////38= ) |
| 208 | -n0 wg set wg0 private-key /dev/null ${low_order_points[@]/#/peer } |
| 209 | -[[ -z $(n0 wg show wg0 peers) ]] |
| 210 | -n0 wg set wg0 private-key <(echo "$key1") ${low_order_points[@]/#/peer } |
| 211 | -[[ -z $(n0 wg show wg0 peers) ]] |
| 212 | +for low_order_point in AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 4Ot6fDtBuK4WVuP68Z/EatoJjeucMrH9hmIFFl9JuAA= X5yVvKNQjCSx0LFVnIPvWwREXMRYHI6G2CJO3dCfEVc= 7P///////////////////////////////////////38= 7f///////////////////////////////////////38= 7v///////////////////////////////////////38=; do |
| 213 | + n0 wg set wg0 peer "$low_order_point" persistent-keepalive 1 endpoint 127.0.0.1:1111 |
| 214 | +done |
| 215 | +[[ -n $(n0 wg show wg0 peers) ]] |
| 216 | +exec 4< <(n0 ncat -l -u -p 1111) |
| 217 | +ncat_pid=$! |
| 218 | +waitncatudp $netns0 $ncat_pid |
| 219 | +ip0 link set wg0 up |
| 220 | +! read -r -n 1 -t 2 <&4 || false |
| 221 | +kill $ncat_pid |
| 222 | ip0 link del wg0 |
| 223 | |
| 224 | declare -A objects |