aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rwxr-xr-xcontrib/examples/json/wg-json6
-rw-r--r--contrib/external-tests/go/main.go9
-rw-r--r--contrib/external-tests/haskell/src/Main.hs6
-rw-r--r--contrib/external-tests/rust/src/main.rs15
-rw-r--r--src/config.c32
-rw-r--r--src/cookie.c66
-rw-r--r--src/cookie.h5
-rw-r--r--src/noise.c177
-rw-r--r--src/noise.h9
-rw-r--r--src/peer.c6
-rw-r--r--src/peer.h2
-rwxr-xr-xsrc/tests/netns.sh4
-rw-r--r--src/tools/completion/wg.bash-completion7
-rw-r--r--src/tools/config.c43
-rw-r--r--src/tools/set.c2
-rw-r--r--src/tools/show.c19
-rw-r--r--src/tools/showconf.c8
-rw-r--r--src/tools/wg.822
-rw-r--r--src/uapi.h11
19 files changed, 224 insertions, 225 deletions
diff --git a/contrib/examples/json/wg-json b/contrib/examples/json/wg-json
index 90544e2..1b9a570 100755
--- a/contrib/examples/json/wg-json
+++ b/contrib/examples/json/wg-json
@@ -9,20 +9,20 @@ while read -r -d $'\t' device; do
if [[ $device != "$last_device" ]]; then
[[ -z $last_device ]] && printf '\n' || printf '%s,\n' "$end"
last_device="$device"
- read -r private_key public_key preshared_key listen_port fwmark
+ read -r private_key public_key listen_port fwmark
printf '\t"%s": {' "$device"
delim=$'\n'
[[ $private_key == "(none)" ]] || { printf '%s\t\t"privateKey": "%s"' "$delim" "$private_key"; delim=$',\n'; }
[[ $public_key == "(none)" ]] || { printf '%s\t\t"publicKey": "%s"' "$delim" "$public_key"; delim=$',\n'; }
- [[ $preshared_key == "(none)" ]] || { printf '%s\t\t"presharedKey": "%s"' "$delim" "$preshared_key"; delim=$',\n'; }
[[ $listen_port == "0" ]] || { printf '%s\t\t"listenPort": %u' "$delim" $(( $listen_port )); delim=$',\n'; }
[[ $fwmark == "off" ]] || { printf '%s\t\t"fwmark": %u' "$delim" $(( $fwmark )); delim=$',\n'; }
printf '%s\t\t"peers": {' "$delim"; end=$'\n\t\t}\n\t}'
delim=$'\n'
else
- read -r public_key endpoint allowed_ips latest_handshake transfer_rx transfer_tx persistent_keepalive
+ read -r public_key preshared_key endpoint allowed_ips latest_handshake transfer_rx transfer_tx persistent_keepalive
printf '%s\t\t\t"%s": {' "$delim" "$public_key"
delim=$'\n'
+ [[ $preshared_key == "(none)" ]] || { printf '%s\t\t\t\t"presharedKey": "%s"' "$delim" "$preshared_key"; delim=$',\n'; }
[[ $endpoint == "(none)" ]] || { printf '%s\t\t\t\t"endpoint": "%s"' "$delim" "$endpoint"; delim=$',\n'; }
[[ $latest_handshake == "0" ]] || { printf '%s\t\t\t\t"latestHandshake": %u' "$delim" $(( $latest_handshake )); delim=$',\n'; }
[[ $transfer_rx == "0" ]] || { printf '%s\t\t\t\t"transferRx": %u' "$delim" $(( $transfer_rx )); delim=$',\n'; }
diff --git a/contrib/external-tests/go/main.go b/contrib/external-tests/go/main.go
index 11c2f86..4b58891 100644
--- a/contrib/external-tests/go/main.go
+++ b/contrib/external-tests/go/main.go
@@ -37,16 +37,17 @@ func ipChecksum(buf []byte) uint16 {
func main() {
ourPrivate, _ := base64.StdEncoding.DecodeString("WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=")
ourPublic, _ := base64.StdEncoding.DecodeString("K5sF9yESrSBsOXPd6TcpKNgqoy1Ik3ZFKl4FolzrRyI=")
- preshared, _ := base64.StdEncoding.DecodeString("FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=")
theirPublic, _ := base64.StdEncoding.DecodeString("qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=")
+ preshared, _ := base64.StdEncoding.DecodeString("FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=")
cs := noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashBLAKE2s)
hs := noise.NewHandshakeState(noise.Config{
CipherSuite: cs,
Random: rand.Reader,
Pattern: noise.HandshakeIK,
Initiator: true,
- Prologue: []byte("WireGuard v0 zx2c4 Jason@zx2c4.com"),
+ Prologue: []byte("WireGuard v1 zx2c4 Jason@zx2c4.com"),
PresharedKey: preshared,
+ PresharedKeyPlacement: 2,
StaticKeypair: noise.DHKey{Private: ourPrivate, Public: ourPublic},
PeerStatic: theirPublic,
})
@@ -68,8 +69,10 @@ func main() {
initiationPacket[3] = 0 // Reserved
binary.LittleEndian.PutUint32(initiationPacket[4:], 28) // Sender index: 28 (arbitrary)
initiationPacket, _, _ = hs.WriteMessage(initiationPacket, tai64n)
- hasher, _ := blake2s.New(&blake2s.Config{Size: 16, Key: preshared})
+ hasher, _ := blake2s.New(&blake2s.Config{Size: 32})
+ hasher.Write([]byte("mac1----"))
hasher.Write(theirPublic)
+ hasher, _ = blake2s.New(&blake2s.Config{Size: 16, Key: hasher.Sum(nil)})
hasher.Write(initiationPacket)
initiationPacket = append(initiationPacket, hasher.Sum(nil)[:16]...)
initiationPacket = append(initiationPacket, make([]byte, 16)...)
diff --git a/contrib/external-tests/haskell/src/Main.hs b/contrib/external-tests/haskell/src/Main.hs
index 820e2f1..8983e6c 100644
--- a/contrib/external-tests/haskell/src/Main.hs
+++ b/contrib/external-tests/haskell/src/Main.hs
@@ -34,7 +34,7 @@ w :: PublicKey Curve25519
-> IO ()
w theirPub (Plaintext myPSK) sock addr msg = do
let x = "\x01\x00\x00\x00\x00\x00" `mappend` msg
- mac = hash 16 myPSK (sbToBS' (curvePubToBytes theirPub) `mappend` sbToBS' x)
+ mac = hash 16 myPSK (sbToBS' (curvePubToBytes theirPub) `mappend` sbToBS' x) -- TODO: this should actually be blake2s(key=blake2s("mac1----" || theirPub), payload=blah)
void $ NBS.sendTo sock (x `mappend` mac `mappend` replicate 16 '\0') addr
r :: MVar ByteString -> Socket -> IO ByteString
@@ -63,8 +63,8 @@ main = do
serverkey' = curveBytesToPub . bsToSB' . either undefined id . B64.decode . pack $ serverkey :: PublicKey Curve25519
psk' = Plaintext . bsToSB' . either undefined id . B64.decode . pack $ psk
hs = handshakeState $ HandshakeStateParams
- noiseIK
- "WireGuard v0 zx2c4 Jason@zx2c4.com"
+ noiseIK -- TODO: specify psk2 mode
+ "WireGuard v1 zx2c4 Jason@zx2c4.com"
(Just psk')
(Just mykey')
Nothing
diff --git a/contrib/external-tests/rust/src/main.rs b/contrib/external-tests/rust/src/main.rs
index 232375c..ceb68b8 100644
--- a/contrib/external-tests/rust/src/main.rs
+++ b/contrib/external-tests/rust/src/main.rs
@@ -39,11 +39,12 @@ fn main() {
owner.set_rs(&their_public);
let mut cipherstate1 : CipherState<CipherChaChaPoly> = Default::default();
let mut cipherstate2 : CipherState<CipherChaChaPoly> = Default::default();
- let mut handshake = HandshakeState::new_from_owner(&mut owner, true, HandshakePattern::IK, "WireGuard v0 zx2c4 Jason@zx2c4.com".as_bytes(), Some(&my_preshared[..]), &mut cipherstate1, &mut cipherstate2);
+ //TODO: specify psk2 mode
+ let mut handshake = HandshakeState::new_from_owner(&mut owner, true, HandshakePattern::IK, "WireGuard v1 zx2c4 Jason@zx2c4.com".as_bytes(), Some(&my_preshared[..]), &mut cipherstate1, &mut cipherstate2);
let now = time::get_time();
let mut tai64n = [0; 12];
- BigEndian::write_i64(&mut tai64n[0..], 4611686018427387914ULL + now.sec);
+ BigEndian::write_i64(&mut tai64n[0..], 4611686018427387914 + now.sec);
BigEndian::write_i32(&mut tai64n[8..], now.nsec);
let mut initiation_packet = [0; 148];
initiation_packet[0] = 1; /* Type: Initiation */
@@ -52,11 +53,13 @@ fn main() {
initiation_packet[3] = 0; /* Reserved */
LittleEndian::write_u32(&mut initiation_packet[4..], 28); /* Sender index: 28 (arbitrary) */
handshake.write_message(&tai64n, &mut initiation_packet[8..]);
- let mut mac_material = [0; 148];
- memcpy(&mut mac_material, &their_public);
- memcpy(&mut mac_material[32..], &initiation_packet[0..116]);
+ let mut mac_key_input = [0; 40];
+ let mut mac_key = [0; 32];
+ memcpy(&mut mac_key_input, b"mac1----");
+ memcpy(&mut mac_key_input[8..], &their_public);
+ Blake2s::blake2s(&mut mac_key, &mac_key_input, &[0; 0]);
let mut mac = [0; 16];
- Blake2s::blake2s(&mut mac, &mac_material, &my_preshared);
+ Blake2s::blake2s(&mut mac, &initiation_packet[0..116], &mac_key);
memcpy(&mut initiation_packet[116..], &mac);
socket.send_to(&initiation_packet, &send_addr).unwrap();
diff --git a/src/config.c b/src/config.c
index a5a25c9..46ee2f1 100644
--- a/src/config.c
+++ b/src/config.c
@@ -60,7 +60,9 @@ static int set_peer(struct wireguard_device *wg, void __user *user_peer, size_t
peer = pubkey_hashtable_lookup(&wg->peer_hashtable, in_peer.public_key);
if (!peer) { /* Peer doesn't exist yet. Add a new one. */
if (in_peer.flags & WGPEER_REMOVE_ME)
- return -ENODEV; /* Tried to remove a non existing peer. */
+ return -ENODEV; /* Tried to remove a non-existing peer. */
+ if (in_peer.flags & WGPEER_REMOVE_PRESHARED_KEY)
+ return -EINVAL; /* Tried to remove a psk for a non-existing peer. */
down_read(&wg->static_identity.lock);
if (wg->static_identity.has_identity && !memcmp(in_peer.public_key, wg->static_identity.static_public, NOISE_PUBLIC_KEY_LEN)) {
@@ -71,7 +73,7 @@ static int set_peer(struct wireguard_device *wg, void __user *user_peer, size_t
}
up_read(&wg->static_identity.lock);
- peer = peer_rcu_get(peer_create(wg, in_peer.public_key));
+ peer = peer_rcu_get(peer_create(wg, in_peer.public_key, in_peer.preshared_key));
if (!peer)
return -ENOMEM;
if (netdev_pub(wg)->flags & IFF_UP)
@@ -84,6 +86,16 @@ static int set_peer(struct wireguard_device *wg, void __user *user_peer, size_t
goto out;
}
+ if (in_peer.flags & WGPEER_REMOVE_PRESHARED_KEY) {
+ down_write(&peer->handshake.lock);
+ memset(&peer->handshake.preshared_key, 0, NOISE_SYMMETRIC_KEY_LEN);
+ up_write(&peer->handshake.lock);
+ } else if (memcmp(zeros, in_peer.preshared_key, WG_KEY_LEN)) {
+ down_write(&peer->handshake.lock);
+ memcpy(&peer->handshake.preshared_key, in_peer.preshared_key, NOISE_SYMMETRIC_KEY_LEN);
+ up_write(&peer->handshake.lock);
+ }
+
if (in_peer.endpoint.addr.sa_family == AF_INET || in_peer.endpoint.addr.sa_family == AF_INET6) {
struct endpoint endpoint = { { { 0 } } };
memcpy(&endpoint, &in_peer.endpoint, sizeof(in_peer.endpoint));
@@ -170,16 +182,8 @@ int config_set_device(struct wireguard_device *wg, void __user *user_device)
modified_static_identity = true;
}
- if (in_device.flags & WGDEVICE_REMOVE_PRESHARED_KEY) {
- noise_set_static_identity_preshared_key(&wg->static_identity, NULL);
- modified_static_identity = true;
- } else if (memcmp(zeros, in_device.preshared_key, WG_KEY_LEN)) {
- noise_set_static_identity_preshared_key(&wg->static_identity, in_device.preshared_key);
- modified_static_identity = true;
- }
-
if (modified_static_identity)
- cookie_checker_precompute_keys(&wg->cookie_checker, NULL);
+ cookie_checker_precompute_device_keys(&wg->cookie_checker);
for (i = 0, offset = 0, user_peer = user_device + sizeof(struct wgdevice); i < in_device.num_peers; ++i, user_peer += offset) {
ret = set_peer(wg, user_peer, &offset);
@@ -249,7 +253,11 @@ static int populate_peer(struct wireguard_peer *peer, void *ctx)
if (ret)
return ret;
+ down_read(&peer->handshake.lock);
memcpy(out_peer.public_key, peer->handshake.remote_static, NOISE_PUBLIC_KEY_LEN);
+ memcpy(out_peer.preshared_key, peer->handshake.preshared_key, NOISE_SYMMETRIC_KEY_LEN);
+ up_read(&peer->handshake.lock);
+
read_lock_bh(&peer->endpoint_lock);
if (peer->endpoint.addr.sa_family == AF_INET)
out_peer.endpoint.addr4 = peer->endpoint.addr4;
@@ -315,8 +323,6 @@ int config_get_device(struct wireguard_device *wg, void __user *user_device)
memcpy(out_device.private_key, wg->static_identity.static_private, WG_KEY_LEN);
memcpy(out_device.public_key, wg->static_identity.static_public, WG_KEY_LEN);
}
- if (wg->static_identity.has_psk)
- memcpy(out_device.preshared_key, wg->static_identity.preshared_key, WG_KEY_LEN);
up_read(&wg->static_identity.lock);
peer_data.out_len = in_device.peers_size;
diff --git a/src/cookie.c b/src/cookie.c
index 6ecc02a..2046493 100644
--- a/src/cookie.c
+++ b/src/cookie.c
@@ -23,31 +23,39 @@ int cookie_checker_init(struct cookie_checker *checker, struct wireguard_device
return 0;
}
-static int precompute_peer_key(struct wireguard_peer *peer, void *psk)
+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])
{
- blake2s(peer->latest_cookie.cookie_decryption_key, peer->handshake.remote_static, psk, NOISE_SYMMETRIC_KEY_LEN, NOISE_PUBLIC_KEY_LEN, psk ? NOISE_SYMMETRIC_KEY_LEN : 0);
- return 0;
+ 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, NOISE_SYMMETRIC_KEY_LEN);
}
-void cookie_checker_precompute_keys(struct cookie_checker *checker, struct wireguard_peer *peer)
+void cookie_checker_precompute_device_keys(struct cookie_checker *checker)
{
down_read(&checker->device->static_identity.lock);
- if (unlikely(!checker->device->static_identity.has_identity)) {
- memset(checker->cookie_encryption_key, 0, NOISE_SYMMETRIC_KEY_LEN);
- goto out;
+ 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);
}
-
- if (peer)
- precompute_peer_key(peer, checker->device->static_identity.has_psk ? checker->device->static_identity.preshared_key : NULL);
else {
- blake2s(checker->cookie_encryption_key, checker->device->static_identity.static_public, checker->device->static_identity.preshared_key, NOISE_SYMMETRIC_KEY_LEN, NOISE_PUBLIC_KEY_LEN, checker->device->static_identity.has_psk ? NOISE_SYMMETRIC_KEY_LEN : 0);
- peer_for_each_unlocked(checker->device, precompute_peer_key, checker->device->static_identity.has_psk ? checker->device->static_identity.preshared_key : NULL);
+ memset(checker->cookie_encryption_key, 0, NOISE_SYMMETRIC_KEY_LEN);
+ memset(checker->message_mac1_key, 0, NOISE_SYMMETRIC_KEY_LEN);
}
-
-out:
up_read(&checker->device->static_identity.lock);
}
+void cookie_checker_precompute_peer_keys(struct wireguard_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 cookie_checker_uninit(struct cookie_checker *checker)
{
ratelimiter_uninit(&checker->ratelimiter);
@@ -59,18 +67,10 @@ void cookie_init(struct cookie *cookie)
init_rwsem(&cookie->lock);
}
-static void compute_mac1(u8 mac1[COOKIE_LEN], const void *message, size_t len, const u8 pubkey[NOISE_PUBLIC_KEY_LEN], const u8 psk[NOISE_SYMMETRIC_KEY_LEN])
+static void compute_mac1(u8 mac1[COOKIE_LEN], const void *message, size_t len, const u8 key[NOISE_SYMMETRIC_KEY_LEN])
{
- struct blake2s_state state;
len = len - sizeof(struct message_macs) + offsetof(struct message_macs, mac1);
-
- if (psk)
- blake2s_init_key(&state, COOKIE_LEN, psk, NOISE_SYMMETRIC_KEY_LEN);
- else
- blake2s_init(&state, COOKIE_LEN);
- blake2s_update(&state, pubkey, NOISE_PUBLIC_KEY_LEN);
- blake2s_update(&state, message, len);
- blake2s_final(&state, mac1, COOKIE_LEN);
+ 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])
@@ -111,13 +111,7 @@ enum cookie_mac_state cookie_validate_packet(struct cookie_checker *checker, str
struct message_macs *macs = (struct message_macs *)(skb->data + skb->len - sizeof(struct message_macs));
ret = INVALID_MAC;
- down_read(&checker->device->static_identity.lock);
- if (unlikely(!checker->device->static_identity.has_identity)) {
- up_read(&checker->device->static_identity.lock);
- goto out;
- }
- compute_mac1(computed_mac, skb->data, skb->len, checker->device->static_identity.static_public, checker->device->static_identity.has_psk ? checker->device->static_identity.preshared_key : NULL);
- up_read(&checker->device->static_identity.lock);
+ compute_mac1(computed_mac, skb->data, skb->len, checker->message_mac1_key);
if (crypto_memneq(computed_mac, macs->mac1, COOKIE_LEN))
goto out;
@@ -146,16 +140,8 @@ void cookie_add_mac_to_packet(void *message, size_t len, struct wireguard_peer *
{
struct message_macs *macs = (struct message_macs *)((u8 *)message + len - sizeof(struct message_macs));
- down_read(&peer->device->static_identity.lock);
- if (unlikely(!peer->device->static_identity.has_identity)) {
- memset(macs, 0, sizeof(struct message_macs));
- up_read(&peer->device->static_identity.lock);
- return;
- }
- compute_mac1(macs->mac1, message, len, peer->handshake.remote_static, peer->device->static_identity.has_psk ? peer->device->static_identity.preshared_key : NULL);
- up_read(&peer->device->static_identity.lock);
-
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);
diff --git a/src/cookie.h b/src/cookie.h
index 87a0e5a..c87d3dd 100644
--- a/src/cookie.h
+++ b/src/cookie.h
@@ -14,6 +14,7 @@ struct sk_buff;
struct cookie_checker {
u8 secret[NOISE_HASH_LEN];
u8 cookie_encryption_key[NOISE_SYMMETRIC_KEY_LEN];
+ u8 message_mac1_key[NOISE_SYMMETRIC_KEY_LEN];
u64 secret_birthdate;
struct rw_semaphore secret_lock;
struct ratelimiter ratelimiter;
@@ -27,6 +28,7 @@ struct cookie {
bool have_sent_mac1;
u8 last_mac1_sent[COOKIE_LEN];
u8 cookie_decryption_key[NOISE_SYMMETRIC_KEY_LEN];
+ u8 message_mac1_key[NOISE_SYMMETRIC_KEY_LEN];
struct rw_semaphore lock;
};
@@ -39,7 +41,8 @@ enum cookie_mac_state {
int cookie_checker_init(struct cookie_checker *checker, struct wireguard_device *wg);
void cookie_checker_uninit(struct cookie_checker *checker);
-void cookie_checker_precompute_keys(struct cookie_checker *checker, struct wireguard_peer *peer);
+void cookie_checker_precompute_device_keys(struct cookie_checker *checker);
+void cookie_checker_precompute_peer_keys(struct wireguard_peer *peer);
void cookie_init(struct cookie *cookie);
enum cookie_mac_state cookie_validate_packet(struct cookie_checker *checker, struct sk_buff *skb, bool check_cookie);
diff --git a/src/noise.c b/src/noise.c
index d6c6398..6e5db8c 100644
--- a/src/noise.c
+++ b/src/noise.c
@@ -14,34 +14,38 @@
#include <linux/highmem.h>
#include <crypto/algapi.h>
-/* This implements Noise_IK:
+/* This implements Noise_IKpsk2:
*
* <- s
* ******
- * -> e, es, s, ss, t
- * <- e, ee, se
+ * -> e, es, s, ss, {t}
+ * <- e, ee, se, psk, {}
*/
-static const u8 handshake_name[33] = "Noise_IK_25519_ChaChaPoly_BLAKE2s";
-static const u8 handshake_psk_name[36] = "NoisePSK_IK_25519_ChaChaPoly_BLAKE2s";
-static u8 handshake_name_hash[NOISE_HASH_LEN] __read_mostly;
-static u8 handshake_psk_name_hash[NOISE_HASH_LEN] __read_mostly;
-static const u8 identifier_name[34] = "WireGuard v0 zx2c4 Jason@zx2c4.com";
+static const u8 handshake_name[37] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
+static const u8 identifier_name[34] = "WireGuard v1 zx2c4 Jason@zx2c4.com";
+static u8 handshake_init_hash[NOISE_HASH_LEN] __read_mostly;
+static u8 handshake_init_chaining_key[NOISE_HASH_LEN] __read_mostly;
static atomic64_t keypair_counter = ATOMIC64_INIT(0);
void noise_init(void)
{
- blake2s(handshake_name_hash, handshake_name, NULL, NOISE_HASH_LEN, sizeof(handshake_name), 0);
- blake2s(handshake_psk_name_hash, handshake_psk_name, NULL, NOISE_HASH_LEN, sizeof(handshake_psk_name), 0);
+ struct blake2s_state blake;
+ blake2s(handshake_init_chaining_key, handshake_name, NULL, NOISE_HASH_LEN, sizeof(handshake_name), 0);
+ blake2s_init(&blake, NOISE_HASH_LEN);
+ blake2s_update(&blake, handshake_init_chaining_key, NOISE_HASH_LEN);
+ blake2s_update(&blake, identifier_name, sizeof(identifier_name));
+ blake2s_final(&blake, handshake_init_hash, NOISE_HASH_LEN);
}
-void noise_handshake_init(struct noise_handshake *handshake, struct noise_static_identity *static_identity, const u8 peer_public_key[NOISE_PUBLIC_KEY_LEN], struct wireguard_peer *peer)
+void noise_handshake_init(struct noise_handshake *handshake, struct noise_static_identity *static_identity, const u8 peer_public_key[NOISE_PUBLIC_KEY_LEN], const u8 peer_preshared_key[NOISE_SYMMETRIC_KEY_LEN], struct wireguard_peer *peer)
{
memset(handshake, 0, sizeof(struct noise_handshake));
init_rwsem(&handshake->lock);
handshake->entry.type = INDEX_HASHTABLE_HANDSHAKE;
handshake->entry.peer = peer;
memcpy(handshake->remote_static, peer_public_key, NOISE_PUBLIC_KEY_LEN);
+ memcpy(handshake->preshared_key, peer_preshared_key, NOISE_SYMMETRIC_KEY_LEN);
handshake->static_identity = static_identity;
handshake->state = HANDSHAKE_ZEROED;
}
@@ -55,7 +59,6 @@ void noise_handshake_clear(struct noise_handshake *handshake)
memset(&handshake->remote_ephemeral, 0, NOISE_PUBLIC_KEY_LEN);
memset(&handshake->hash, 0, NOISE_HASH_LEN);
memset(&handshake->chaining_key, 0, NOISE_HASH_LEN);
- memset(&handshake->key, 0, NOISE_SYMMETRIC_KEY_LEN);
handshake->remote_index = 0;
handshake->state = HANDSHAKE_ZEROED;
up_write(&handshake->lock);
@@ -198,44 +201,44 @@ void noise_set_static_identity_private_key(struct noise_static_identity *static_
up_write(&static_identity->lock);
}
-void noise_set_static_identity_preshared_key(struct noise_static_identity *static_identity, const u8 preshared_key[NOISE_SYMMETRIC_KEY_LEN])
-{
- down_write(&static_identity->lock);
- if (preshared_key) {
- memcpy(static_identity->preshared_key, preshared_key, NOISE_SYMMETRIC_KEY_LEN);
- static_identity->has_psk = true;
- } else {
- memset(static_identity->preshared_key, 0, NOISE_SYMMETRIC_KEY_LEN);
- static_identity->has_psk = false;
- }
- up_write(&static_identity->lock);
-}
-
/* This is Hugo Krawczyk's HKDF:
* - https://eprint.iacr.org/2010/264.pdf
* - https://tools.ietf.org/html/rfc5869
*/
-static void kdf(u8 *first_dst, u8 *second_dst, const u8 *data,
- size_t first_len, size_t second_len, size_t data_len,
- const u8 chaining_key[NOISE_HASH_LEN])
+static void kdf(u8 *first_dst, u8 *second_dst, u8 *third_dst, const u8 *data, size_t first_len, size_t second_len, size_t third_len, size_t data_len, const u8 chaining_key[NOISE_HASH_LEN])
{
u8 secret[BLAKE2S_OUTBYTES];
u8 output[BLAKE2S_OUTBYTES + 1];
- BUG_ON(first_len > BLAKE2S_OUTBYTES || second_len > BLAKE2S_OUTBYTES);
+ BUG_ON(first_len > BLAKE2S_OUTBYTES || second_len > BLAKE2S_OUTBYTES || third_len > BLAKE2S_OUTBYTES || ((second_len || second_dst || third_len || third_dst) && (!first_len || !first_dst)) || ((third_len || third_dst) && (!second_len || !second_dst)));
/* Extract entropy from data into secret */
blake2s_hmac(secret, data, chaining_key, BLAKE2S_OUTBYTES, data_len, NOISE_HASH_LEN);
+ if (!first_dst || !first_len)
+ goto out;
+
/* Expand first key: key = secret, data = 0x1 */
output[0] = 1;
blake2s_hmac(output, output, secret, BLAKE2S_OUTBYTES, 1, BLAKE2S_OUTBYTES);
memcpy(first_dst, output, first_len);
+ if (!second_dst || !second_len)
+ goto out;
+
/* Expand second key: key = secret, data = first-key || 0x2 */
output[BLAKE2S_OUTBYTES] = 2;
blake2s_hmac(output, output, secret, BLAKE2S_OUTBYTES, BLAKE2S_OUTBYTES + 1, BLAKE2S_OUTBYTES);
memcpy(second_dst, output, second_len);
+ if (!third_dst || !third_len)
+ goto out;
+
+ /* Expand third key: key = secret, data = second-key || 0x3 */
+ output[BLAKE2S_OUTBYTES] = 3;
+ blake2s_hmac(output, output, secret, BLAKE2S_OUTBYTES, BLAKE2S_OUTBYTES + 1, BLAKE2S_OUTBYTES);
+ memcpy(third_dst, output, third_len);
+
+out:
/* Clear sensitive data from stack */
memzero_explicit(secret, BLAKE2S_OUTBYTES);
memzero_explicit(output, BLAKE2S_OUTBYTES + 1);
@@ -252,23 +255,17 @@ static void symmetric_key_init(struct noise_symmetric_key *key)
static void derive_keys(struct noise_symmetric_key *first_dst, struct noise_symmetric_key *second_dst, const u8 chaining_key[NOISE_HASH_LEN])
{
- kdf(first_dst->key, second_dst->key, NULL, NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, chaining_key);
+ kdf(first_dst->key, second_dst->key, NULL, NULL, NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 0, chaining_key);
symmetric_key_init(first_dst);
symmetric_key_init(second_dst);
}
-static void mix_key(u8 key[NOISE_SYMMETRIC_KEY_LEN], u8 chaining_key[NOISE_HASH_LEN], const u8 *src, size_t src_len)
-{
- kdf(chaining_key, key, src, NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, src_len, chaining_key);
-}
-
-static __must_check bool mix_dh(u8 key[NOISE_SYMMETRIC_KEY_LEN], u8 chaining_key[NOISE_HASH_LEN],
- const u8 private[NOISE_PUBLIC_KEY_LEN], const u8 public[NOISE_PUBLIC_KEY_LEN])
+static bool __must_check mix_dh(u8 chaining_key[NOISE_HASH_LEN], u8 key[NOISE_SYMMETRIC_KEY_LEN], const u8 private[NOISE_PUBLIC_KEY_LEN], const u8 public[NOISE_PUBLIC_KEY_LEN])
{
u8 dh_calculation[NOISE_PUBLIC_KEY_LEN];
if (unlikely(!curve25519(dh_calculation, private, public)))
return false;
- mix_key(key, chaining_key, dh_calculation, NOISE_PUBLIC_KEY_LEN);
+ kdf(chaining_key, key, NULL, dh_calculation, NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN, chaining_key);
memzero_explicit(dh_calculation, NOISE_PUBLIC_KEY_LEN);
return true;
}
@@ -282,29 +279,28 @@ static void mix_hash(u8 hash[NOISE_HASH_LEN], const u8 *src, size_t src_len)
blake2s_final(&blake, hash, NOISE_HASH_LEN);
}
-static void handshake_init(u8 key[NOISE_SYMMETRIC_KEY_LEN], u8 chaining_key[NOISE_HASH_LEN], u8 hash[NOISE_HASH_LEN],
- const u8 remote_static[NOISE_PUBLIC_KEY_LEN], const u8 psk[NOISE_SYMMETRIC_KEY_LEN])
+static void mix_psk(u8 chaining_key[NOISE_HASH_LEN], u8 hash[NOISE_HASH_LEN], u8 key[NOISE_SYMMETRIC_KEY_LEN], const u8 psk[NOISE_SYMMETRIC_KEY_LEN])
{
- memset(key, 0, NOISE_SYMMETRIC_KEY_LEN);
- memcpy(hash, psk ? handshake_psk_name_hash : handshake_name_hash, NOISE_HASH_LEN);
- mix_hash(hash, identifier_name, sizeof(identifier_name));
- if (psk) {
- u8 temp_hash[NOISE_HASH_LEN];
- kdf(chaining_key, temp_hash, psk, NOISE_HASH_LEN, NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, handshake_psk_name_hash);
- mix_hash(hash, temp_hash, NOISE_HASH_LEN);
- memzero_explicit(temp_hash, NOISE_HASH_LEN);
- } else
- memcpy(chaining_key, handshake_name_hash, NOISE_HASH_LEN);
+ u8 temp_hash[NOISE_HASH_LEN];
+ kdf(chaining_key, temp_hash, key, psk, NOISE_HASH_LEN, NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, chaining_key);
+ mix_hash(hash, temp_hash, NOISE_HASH_LEN);
+ memzero_explicit(temp_hash, NOISE_HASH_LEN);
+}
+
+static void handshake_init(u8 chaining_key[NOISE_HASH_LEN], u8 hash[NOISE_HASH_LEN], const u8 remote_static[NOISE_PUBLIC_KEY_LEN])
+{
+ memcpy(hash, handshake_init_hash, NOISE_HASH_LEN);
+ memcpy(chaining_key, handshake_init_chaining_key, NOISE_HASH_LEN);
mix_hash(hash, remote_static, NOISE_PUBLIC_KEY_LEN);
}
-static void handshake_encrypt(u8 *dst_ciphertext, const u8 *src_plaintext, size_t src_len, u8 key[NOISE_SYMMETRIC_KEY_LEN], u8 hash[NOISE_HASH_LEN])
+static void message_encrypt(u8 *dst_ciphertext, const u8 *src_plaintext, size_t src_len, u8 key[NOISE_SYMMETRIC_KEY_LEN], u8 hash[NOISE_HASH_LEN])
{
chacha20poly1305_encrypt(dst_ciphertext, src_plaintext, src_len, hash, NOISE_HASH_LEN, 0 /* Always zero for Noise_IK */, key);
mix_hash(hash, dst_ciphertext, noise_encrypted_len(src_len));
}
-static bool handshake_decrypt(u8 *dst_plaintext, const u8 *src_ciphertext, size_t src_len, u8 key[NOISE_SYMMETRIC_KEY_LEN], u8 hash[NOISE_HASH_LEN])
+static bool message_decrypt(u8 *dst_plaintext, const u8 *src_ciphertext, size_t src_len, u8 key[NOISE_SYMMETRIC_KEY_LEN], u8 hash[NOISE_HASH_LEN])
{
if (!chacha20poly1305_decrypt(dst_plaintext, src_ciphertext, src_len, hash, NOISE_HASH_LEN, 0 /* Always zero for Noise_IK */, key))
return false;
@@ -312,10 +308,11 @@ static bool handshake_decrypt(u8 *dst_plaintext, const u8 *src_ciphertext, size_
return true;
}
-static void handshake_nocrypt(u8 *dst, const u8 *src, size_t src_len, u8 hash[NOISE_HASH_LEN])
+static void message_ephemeral(u8 ephemeral_dst[NOISE_PUBLIC_KEY_LEN], const u8 ephemeral_src[NOISE_PUBLIC_KEY_LEN], u8 chaining_key[NOISE_HASH_LEN], u8 hash[NOISE_HASH_LEN])
{
- memcpy(dst, src, src_len);
- mix_hash(hash, src, src_len);
+ memcpy(ephemeral_dst, ephemeral_src, NOISE_PUBLIC_KEY_LEN);
+ mix_hash(hash, ephemeral_src, NOISE_PUBLIC_KEY_LEN);
+ kdf(chaining_key, NULL, NULL, ephemeral_src, NOISE_HASH_LEN, 0, 0, NOISE_PUBLIC_KEY_LEN, chaining_key);
}
static void tai64n_now(u8 output[NOISE_TIMESTAMP_LEN])
@@ -330,6 +327,7 @@ static void tai64n_now(u8 output[NOISE_TIMESTAMP_LEN])
bool noise_handshake_create_initiation(struct message_handshake_initiation *dst, struct noise_handshake *handshake)
{
u8 timestamp[NOISE_TIMESTAMP_LEN];
+ u8 key[NOISE_SYMMETRIC_KEY_LEN];
bool ret = false;
down_read(&handshake->static_identity->lock);
@@ -340,31 +338,28 @@ bool noise_handshake_create_initiation(struct message_handshake_initiation *dst,
dst->header.type = cpu_to_le32(MESSAGE_HANDSHAKE_INITIATION);
- handshake_init(handshake->key, handshake->chaining_key, handshake->hash, handshake->remote_static,
- handshake->static_identity->has_psk ? handshake->static_identity->preshared_key : NULL);
+ handshake_init(handshake->chaining_key, handshake->hash, handshake->remote_static);
/* e */
curve25519_generate_secret(handshake->ephemeral_private);
if (!curve25519_generate_public(handshake->ephemeral_public, handshake->ephemeral_private))
goto out;
- handshake_nocrypt(dst->unencrypted_ephemeral, handshake->ephemeral_public, NOISE_PUBLIC_KEY_LEN, handshake->hash);
- if (handshake->static_identity->has_psk)
- mix_key(handshake->key, handshake->chaining_key, handshake->ephemeral_public, NOISE_PUBLIC_KEY_LEN);
+ message_ephemeral(dst->unencrypted_ephemeral, handshake->ephemeral_public, handshake->chaining_key, handshake->hash);
/* es */
- if (!mix_dh(handshake->key, handshake->chaining_key, handshake->ephemeral_private, handshake->remote_static))
+ if (!mix_dh(handshake->chaining_key, key, handshake->ephemeral_private, handshake->remote_static))
goto out;
/* s */
- handshake_encrypt(dst->encrypted_static, handshake->static_identity->static_public, NOISE_PUBLIC_KEY_LEN, handshake->key, handshake->hash);
+ message_encrypt(dst->encrypted_static, handshake->static_identity->static_public, NOISE_PUBLIC_KEY_LEN, key, handshake->hash);
/* ss */
- if (!mix_dh(handshake->key, handshake->chaining_key, handshake->static_identity->static_private, handshake->remote_static))
+ if (!mix_dh(handshake->chaining_key, key, handshake->static_identity->static_private, handshake->remote_static))
goto out;
- /* t */
+ /* {t} */
tai64n_now(timestamp);
- handshake_encrypt(dst->encrypted_timestamp, timestamp, NOISE_TIMESTAMP_LEN, handshake->key, handshake->hash);
+ message_encrypt(dst->encrypted_timestamp, timestamp, NOISE_TIMESTAMP_LEN, key, handshake->hash);
dst->sender_index = index_hashtable_insert(&handshake->entry.peer->device->index_hashtable, &handshake->entry);
@@ -374,6 +369,7 @@ bool noise_handshake_create_initiation(struct message_handshake_initiation *dst,
out:
up_write(&handshake->lock);
up_read(&handshake->static_identity->lock);
+ memzero_explicit(key, NOISE_SYMMETRIC_KEY_LEN);
return ret;
}
@@ -393,28 +389,25 @@ struct wireguard_peer *noise_handshake_consume_initiation(struct message_handsha
if (unlikely(!wg->static_identity.has_identity))
goto out;
- handshake_init(key, chaining_key, hash, wg->static_identity.static_public,
- wg->static_identity.has_psk ? wg->static_identity.preshared_key : NULL);
+ handshake_init(chaining_key, hash, wg->static_identity.static_public);
/* e */
- handshake_nocrypt(e, src->unencrypted_ephemeral, sizeof(src->unencrypted_ephemeral), hash);
- if (wg->static_identity.has_psk)
- mix_key(key, chaining_key, e, NOISE_PUBLIC_KEY_LEN);
+ message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash);
/* es */
- if (!mix_dh(key, chaining_key, wg->static_identity.static_private, e))
+ if (!mix_dh(chaining_key, key, wg->static_identity.static_private, e))
goto out;
/* s */
- if (!handshake_decrypt(s, src->encrypted_static, sizeof(src->encrypted_static), key, hash))
+ if (!message_decrypt(s, src->encrypted_static, sizeof(src->encrypted_static), key, hash))
goto out;
/* ss */
- if (!mix_dh(key, chaining_key, wg->static_identity.static_private, s))
+ if (!mix_dh(chaining_key, key, wg->static_identity.static_private, s))
goto out;
- /* t */
- if (!handshake_decrypt(t, src->encrypted_timestamp, sizeof(src->encrypted_timestamp), key, hash))
+ /* {t} */
+ if (!message_decrypt(t, src->encrypted_timestamp, sizeof(src->encrypted_timestamp), key, hash))
goto out;
/* Lookup which peer we're actually talking to */
@@ -436,7 +429,6 @@ struct wireguard_peer *noise_handshake_consume_initiation(struct message_handsha
down_write(&handshake->lock);
memcpy(handshake->remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN);
memcpy(handshake->latest_timestamp, t, NOISE_TIMESTAMP_LEN);
- memcpy(handshake->key, key, NOISE_SYMMETRIC_KEY_LEN);
memcpy(handshake->hash, hash, NOISE_HASH_LEN);
memcpy(handshake->chaining_key, chaining_key, NOISE_HASH_LEN);
handshake->remote_index = src->sender_index;
@@ -455,6 +447,7 @@ out:
bool noise_handshake_create_response(struct message_handshake_response *dst, struct noise_handshake *handshake)
{
bool ret = false;
+ u8 key[NOISE_SYMMETRIC_KEY_LEN];
down_read(&handshake->static_identity->lock);
down_write(&handshake->lock);
@@ -468,19 +461,21 @@ bool noise_handshake_create_response(struct message_handshake_response *dst, str
curve25519_generate_secret(handshake->ephemeral_private);
if (!curve25519_generate_public(handshake->ephemeral_public, handshake->ephemeral_private))
goto out;
- handshake_nocrypt(dst->unencrypted_ephemeral, handshake->ephemeral_public, NOISE_PUBLIC_KEY_LEN, handshake->hash);
- if (handshake->static_identity->has_psk)
- mix_key(handshake->key, handshake->chaining_key, handshake->ephemeral_public, NOISE_PUBLIC_KEY_LEN);
+ message_ephemeral(dst->unencrypted_ephemeral, handshake->ephemeral_public, handshake->chaining_key, handshake->hash);
/* ee */
- if (!mix_dh(handshake->key, handshake->chaining_key, handshake->ephemeral_private, handshake->remote_ephemeral))
+ if (!mix_dh(handshake->chaining_key, NULL, handshake->ephemeral_private, handshake->remote_ephemeral))
goto out;
/* se */
- if (!mix_dh(handshake->key, handshake->chaining_key, handshake->ephemeral_private, handshake->remote_static))
+ if (!mix_dh(handshake->chaining_key, NULL, handshake->ephemeral_private, handshake->remote_static))
goto out;
- handshake_encrypt(dst->encrypted_nothing, NULL, 0, handshake->key, handshake->hash);
+ /* psk */
+ mix_psk(handshake->chaining_key, handshake->hash, key, handshake->preshared_key);
+
+ /* {} */
+ message_encrypt(dst->encrypted_nothing, NULL, 0, key, handshake->hash);
dst->sender_index = index_hashtable_insert(&handshake->entry.peer->device->index_hashtable, &handshake->entry);
@@ -490,6 +485,7 @@ bool noise_handshake_create_response(struct message_handshake_response *dst, str
out:
up_write(&handshake->lock);
up_read(&handshake->static_identity->lock);
+ memzero_explicit(key, NOISE_SYMMETRIC_KEY_LEN);
return ret;
}
@@ -516,7 +512,6 @@ struct wireguard_peer *noise_handshake_consume_response(struct message_handshake
down_read(&handshake->lock);
state = handshake->state;
- memcpy(key, handshake->key, NOISE_SYMMETRIC_KEY_LEN);
memcpy(hash, handshake->hash, NOISE_HASH_LEN);
memcpy(chaining_key, handshake->chaining_key, NOISE_HASH_LEN);
memcpy(ephemeral_private, handshake->ephemeral_private, NOISE_PUBLIC_KEY_LEN);
@@ -526,26 +521,26 @@ struct wireguard_peer *noise_handshake_consume_response(struct message_handshake
goto fail;
/* e */
- handshake_nocrypt(e, src->unencrypted_ephemeral, sizeof(src->unencrypted_ephemeral), hash);
- if (wg->static_identity.has_psk)
- mix_key(key, chaining_key, e, NOISE_PUBLIC_KEY_LEN);
+ message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash);
/* ee */
- if (!mix_dh(key, chaining_key, ephemeral_private, e))
+ if (!mix_dh(chaining_key, NULL, ephemeral_private, e))
goto out;
/* se */
- if (!mix_dh(key, chaining_key, wg->static_identity.static_private, e))
+ if (!mix_dh(chaining_key, NULL, wg->static_identity.static_private, e))
goto out;
- /* decrypt nothing */
- if (!handshake_decrypt(NULL, src->encrypted_nothing, sizeof(src->encrypted_nothing), key, hash))
+ /* psk */
+ mix_psk(chaining_key, hash, key, handshake->preshared_key);
+
+ /* {} */
+ if (!message_decrypt(NULL, src->encrypted_nothing, sizeof(src->encrypted_nothing), key, hash))
goto fail;
/* Success! Copy everything to peer */
down_write(&handshake->lock);
memcpy(handshake->remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN);
- memcpy(handshake->key, key, NOISE_SYMMETRIC_KEY_LEN);
memcpy(handshake->hash, hash, NOISE_HASH_LEN);
memcpy(handshake->chaining_key, chaining_key, NOISE_HASH_LEN);
handshake->remote_index = src->sender_index;
diff --git a/src/noise.h b/src/noise.h
index e60584b..c9b2b56 100644
--- a/src/noise.h
+++ b/src/noise.h
@@ -53,10 +53,9 @@ struct noise_keypairs {
};
struct noise_static_identity {
- bool has_identity, has_psk;
+ bool has_identity;
u8 static_public[NOISE_PUBLIC_KEY_LEN];
u8 static_private[NOISE_PUBLIC_KEY_LEN];
- u8 preshared_key[NOISE_SYMMETRIC_KEY_LEN];
struct rw_semaphore lock;
};
@@ -82,7 +81,8 @@ struct noise_handshake {
u8 remote_static[NOISE_PUBLIC_KEY_LEN];
u8 remote_ephemeral[NOISE_PUBLIC_KEY_LEN];
- u8 key[NOISE_SYMMETRIC_KEY_LEN];
+ u8 preshared_key[NOISE_SYMMETRIC_KEY_LEN];
+
u8 hash[NOISE_HASH_LEN];
u8 chaining_key[NOISE_HASH_LEN];
@@ -102,7 +102,7 @@ struct message_data;
struct message_handshake_cookie;
void noise_init(void);
-void noise_handshake_init(struct noise_handshake *handshake, struct noise_static_identity *static_identity, const u8 peer_public_key[NOISE_PUBLIC_KEY_LEN], struct wireguard_peer *peer);
+void noise_handshake_init(struct noise_handshake *handshake, struct noise_static_identity *static_identity, const u8 peer_public_key[NOISE_PUBLIC_KEY_LEN], const u8 peer_preshared_key[NOISE_SYMMETRIC_KEY_LEN], struct wireguard_peer *peer);
void noise_handshake_clear(struct noise_handshake *handshake);
void noise_keypair_put(struct noise_keypair *keypair);
struct noise_keypair *noise_keypair_get(struct noise_keypair *keypair);
@@ -110,7 +110,6 @@ void noise_keypairs_clear(struct noise_keypairs *keypairs);
bool noise_received_with_keypair(struct noise_keypairs *keypairs, struct noise_keypair *received_keypair);
void noise_set_static_identity_private_key(struct noise_static_identity *static_identity, const u8 private_key[NOISE_PUBLIC_KEY_LEN]);
-void noise_set_static_identity_preshared_key(struct noise_static_identity *static_identity, const u8 preshared_key[NOISE_SYMMETRIC_KEY_LEN]);
bool noise_handshake_create_initiation(struct message_handshake_initiation *dst, struct noise_handshake *handshake);
struct wireguard_peer *noise_handshake_consume_initiation(struct message_handshake_initiation *src, struct wireguard_device *wg);
diff --git a/src/peer.c b/src/peer.c
index 451a676..b6ead5e 100644
--- a/src/peer.c
+++ b/src/peer.c
@@ -14,7 +14,7 @@
static atomic64_t peer_counter = ATOMIC64_INIT(0);
-struct wireguard_peer *peer_create(struct wireguard_device *wg, const u8 public_key[NOISE_PUBLIC_KEY_LEN])
+struct wireguard_peer *peer_create(struct wireguard_device *wg, const u8 public_key[NOISE_PUBLIC_KEY_LEN], const u8 preshared_key[NOISE_SYMMETRIC_KEY_LEN])
{
struct wireguard_peer *peer;
lockdep_assert_held(&wg->device_update_lock);
@@ -34,8 +34,8 @@ struct wireguard_peer *peer_create(struct wireguard_device *wg, const u8 public_
peer->internal_id = atomic64_inc_return(&peer_counter);
peer->device = wg;
cookie_init(&peer->latest_cookie);
- noise_handshake_init(&peer->handshake, &wg->static_identity, public_key, peer);
- cookie_checker_precompute_keys(&wg->cookie_checker, peer);
+ noise_handshake_init(&peer->handshake, &wg->static_identity, public_key, preshared_key, peer);
+ cookie_checker_precompute_peer_keys(peer);
mutex_init(&peer->keypairs.keypair_update_lock);
INIT_WORK(&peer->transmit_handshake_work, packet_send_queued_handshakes);
rwlock_init(&peer->endpoint_lock);
diff --git a/src/peer.h b/src/peer.h
index dc4bfee..8cb759a 100644
--- a/src/peer.h
+++ b/src/peer.h
@@ -56,7 +56,7 @@ struct wireguard_peer {
#endif
};
-struct wireguard_peer *peer_create(struct wireguard_device *wg, const u8 public_key[NOISE_PUBLIC_KEY_LEN]);
+struct wireguard_peer *peer_create(struct wireguard_device *wg, const u8 public_key[NOISE_PUBLIC_KEY_LEN], const u8 preshared_key[NOISE_SYMMETRIC_KEY_LEN]);
struct wireguard_peer *peer_get(struct wireguard_peer *peer);
struct wireguard_peer *peer_rcu_get(struct wireguard_peer *peer);
diff --git a/src/tests/netns.sh b/src/tests/netns.sh
index 6dc917e..9ef83a7 100755
--- a/src/tests/netns.sh
+++ b/src/tests/netns.sh
@@ -88,15 +88,15 @@ configure_peers() {
n1 wg set wg0 \
private-key <(echo "$key1") \
- preshared-key <(echo "$psk") \
listen-port 1 \
peer "$pub2" \
+ preshared-key <(echo "$psk") \
allowed-ips 192.168.241.2/32,fd00::2/128
n2 wg set wg0 \
private-key <(echo "$key2") \
- preshared-key <(echo "$psk") \
listen-port 2 \
peer "$pub1" \
+ preshared-key <(echo "$psk") \
allowed-ips 192.168.241.1/32,fd00::1/128
ip1 link set up dev wg0
diff --git a/src/tools/completion/wg.bash-completion b/src/tools/completion/wg.bash-completion
index 355012c..5401bc3 100644
--- a/src/tools/completion/wg.bash-completion
+++ b/src/tools/completion/wg.bash-completion
@@ -21,7 +21,7 @@ _wg_completion() {
fi
if [[ $COMP_CWORD -eq 3 && ${COMP_WORDS[1]} == show && ${COMP_WORDS[2]} != interfaces ]]; then
- COMPREPLY+=( $(compgen -W "public-key private-key preshared-key listen-port peers endpoints allowed-ips fwmark latest-handshakes persistent-keepalive transfer dump" -- "${COMP_WORDS[3]}") )
+ COMPREPLY+=( $(compgen -W "public-key private-key listen-port peers preshared-keys endpoints allowed-ips fwmark latest-handshakes persistent-keepalive transfer dump" -- "${COMP_WORDS[3]}") )
return
fi
@@ -39,7 +39,6 @@ _wg_completion() {
[[ ${COMP_WORDS[i]} == listen-port ]] && has_listen_port=1
[[ ${COMP_WORDS[i]} == fwmark ]] && has_fwmark=1
[[ ${COMP_WORDS[i]} == private-key ]] && has_private_key=1
- [[ ${COMP_WORDS[i]} == preshared-key ]] && has_preshared_key=1
[[ ${COMP_WORDS[i]} == peer ]] && { has_peer=$i; break; }
done
if [[ $has_peer -eq 0 ]]; then
@@ -47,7 +46,6 @@ _wg_completion() {
[[ $has_listen_port -eq 1 ]] || words+=( listen-port )
[[ $has_fwmark -eq 1 ]] || words+=( fwmark )
[[ $has_private_key -eq 1 ]] || words+=( private-key )
- [[ $has_preshared_key -eq 1 ]] || words+=( preshared-key )
words+=( peer )
COMPREPLY+=( $(compgen -W "${words[*]}" -- "${COMP_WORDS[COMP_CWORD]}") )
elif [[ ${COMP_WORDS[COMP_CWORD-1]} == *-key ]]; then
@@ -70,6 +68,7 @@ _wg_completion() {
has_endpoint=0
has_persistent_keepalive=0
has_allowed_ips=0
+ has_preshared_key=0
[[ ${COMP_WORDS[i+2]} == = ]] && ((i+=2)) || ((i++))
continue
fi
@@ -77,6 +76,7 @@ _wg_completion() {
[[ ${COMP_WORDS[i]} == endpoint ]] && has_endpoint=1
[[ ${COMP_WORDS[i]} == persistent-keepalive ]] && has_persistent_keepalive=1
[[ ${COMP_WORDS[i]} == allowed-ips ]] && has_allowed_ips=1
+ [[ ${COMP_WORDS[i]} == preshared-key ]] && has_preshared_key=1
[[ ${COMP_WORDS[i]} == remove ]] || ((i++))
done
@@ -84,6 +84,7 @@ _wg_completion() {
((COMP_CWORD == j)) || return
if [[ $has_remove -ne 1 ]]; then
+ [[ $has_preshared_key -eq 1 ]] || words+=( preshared-key )
[[ $has_endpoint -eq 1 ]] || words+=( endpoint )
[[ $has_allowed_ips -eq 1 ]] || words+=( allowed-ips )
[[ $has_persistent_keepalive -eq 1 ]] || words+=( persistent-keepalive )
diff --git a/src/tools/config.c b/src/tools/config.c
index c00e91c..a129088 100644
--- a/src/tools/config.c
+++ b/src/tools/config.c
@@ -323,10 +323,6 @@ static bool process_line(struct config_ctx *ctx, const char *line)
ret = parse_key(ctx->buf.dev->private_key, value);
if (!ret)
memset(ctx->buf.dev->private_key, 0, WG_KEY_LEN);
- } else if (key_match("PresharedKey")) {
- ret = parse_key(ctx->buf.dev->preshared_key, value);
- if (!ret)
- memset(ctx->buf.dev->preshared_key, 0, WG_KEY_LEN);
} else
goto error;
} else if (ctx->is_peer_section) {
@@ -338,7 +334,11 @@ static bool process_line(struct config_ctx *ctx, const char *line)
ret = parse_ipmasks(&ctx->buf, ctx->peer_offset, value);
else if (key_match("PersistentKeepalive"))
ret = parse_persistent_keepalive(&peer_from_offset(ctx->buf.dev, ctx->peer_offset)->persistent_keepalive_interval, value);
- else
+ else if (key_match("PresharedKey")) {
+ ret = parse_key(peer_from_offset(ctx->buf.dev, ctx->peer_offset)->preshared_key, value);
+ if (!ret)
+ memset(peer_from_offset(ctx->buf.dev, ctx->peer_offset)->preshared_key, 0, WG_KEY_LEN);
+ } else
goto error;
} else
goto error;
@@ -408,8 +408,6 @@ bool config_read_finish(struct config_ctx *ctx)
fprintf(stderr, "No private key configured\n");
goto err;
}
- if (ctx->buf.dev->flags & WGDEVICE_REPLACE_PEERS && !key_is_valid(ctx->buf.dev->preshared_key))
- ctx->buf.dev->flags |= WGDEVICE_REMOVE_PRESHARED_KEY;
if (ctx->buf.dev->flags & WGDEVICE_REPLACE_PEERS && !ctx->buf.dev->fwmark)
ctx->buf.dev->flags |= WGDEVICE_REMOVE_FWMARK;
@@ -508,21 +506,6 @@ bool config_read_cmd(struct wgdevice **device, char *argv[], int argc)
goto error;
argv += 2;
argc -= 2;
- } else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && !buf.dev->num_peers) {
- char *line;
- int ret = read_line(&line, argv[1]);
- if (ret == 0) {
- if (!parse_key(buf.dev->preshared_key, line)) {
- free(line);
- goto error;
- }
- free(line);
- } else if (ret == 1)
- buf.dev->flags |= WGDEVICE_REMOVE_PRESHARED_KEY;
- else
- goto error;
- argv += 2;
- argc -= 2;
} else if (!strcmp(argv[0], "peer") && argc >= 2) {
peer_offset = buf.pos;
if (use_space(&buf, sizeof(struct wgpeer)) < 0) {
@@ -560,6 +543,22 @@ bool config_read_cmd(struct wgdevice **device, char *argv[], int argc)
goto error;
argv += 2;
argc -= 2;
+ } else if (!strcmp(argv[0], "preshared-key") && argc >= 2 && buf.dev->num_peers) {
+ char *line;
+ int ret = read_line(&line, argv[1]);
+ if (ret == 0) {
+ if (!parse_key(peer_from_offset(buf.dev, peer_offset)->preshared_key, line)) {
+ free(line);
+ goto error;
+ }
+ free(line);
+ } else if (ret == 1) {
+ free(line);
+ buf.dev->flags |= WGPEER_REMOVE_PRESHARED_KEY;
+ } else
+ goto error;
+ argv += 2;
+ argc -= 2;
} else {
fprintf(stderr, "Invalid argument: %s\n", argv[0]);
goto error;
diff --git a/src/tools/set.c b/src/tools/set.c
index 5e4291f..497edcc 100644
--- a/src/tools/set.c
+++ b/src/tools/set.c
@@ -13,7 +13,7 @@ int set_main(int argc, char *argv[])
int ret = 1;
if (argc < 3) {
- fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n", PROG_NAME, argv[0]);
+ fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n", PROG_NAME, argv[0]);
return 1;
}
diff --git a/src/tools/show.c b/src/tools/show.c
index 8134883..7f67dba 100644
--- a/src/tools/show.c
+++ b/src/tools/show.c
@@ -201,7 +201,7 @@ static char *bytes(uint64_t b)
static const char *COMMAND_NAME = NULL;
static void show_usage(void)
{
- fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | preshared-key | listen-port | fwmark | peers | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME);
+ fprintf(stderr, "Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\n", PROG_NAME, COMMAND_NAME);
}
static void pretty_print(struct wgdevice *device)
@@ -216,8 +216,6 @@ static void pretty_print(struct wgdevice *device)
terminal_printf(" " TERMINAL_BOLD "public key" TERMINAL_RESET ": %s\n", key(device->public_key));
if (memcmp(device->private_key, zero, WG_KEY_LEN))
terminal_printf(" " TERMINAL_BOLD "private key" TERMINAL_RESET ": %s\n", masked_key(device->private_key));
- if (memcmp(device->preshared_key, zero, WG_KEY_LEN))
- terminal_printf(" " TERMINAL_BOLD "preshared key" TERMINAL_RESET ": %s\n", masked_key(device->preshared_key));
if (device->port)
terminal_printf(" " TERMINAL_BOLD "listening port" TERMINAL_RESET ": %u\n", device->port);
if (device->fwmark)
@@ -228,6 +226,8 @@ static void pretty_print(struct wgdevice *device)
}
for_each_wgpeer(device, peer, i) {
terminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD "peer" TERMINAL_RESET ": " TERMINAL_FG_YELLOW "%s" TERMINAL_RESET "\n", key(peer->public_key));
+ if (memcmp(peer->preshared_key, zero, WG_KEY_LEN))
+ terminal_printf(" " TERMINAL_BOLD "preshared key" TERMINAL_RESET ": %s\n", masked_key(peer->preshared_key));
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
terminal_printf(" " TERMINAL_BOLD "endpoint" TERMINAL_RESET ": %s\n", endpoint(&peer->endpoint.addr));
terminal_printf(" " TERMINAL_BOLD "allowed ips" TERMINAL_RESET ": ");
@@ -260,7 +260,6 @@ static void dump_print(struct wgdevice *device, bool with_interface)
printf("%s\t", device->interface);
printf("%s\t", key(device->private_key));
printf("%s\t", key(device->public_key));
- printf("%s\t", key(device->preshared_key));
printf("%u\t", device->port);
if (device->fwmark)
printf("0x%x\n", device->fwmark);
@@ -270,6 +269,7 @@ static void dump_print(struct wgdevice *device, bool with_interface)
if (with_interface)
printf("%s\t", device->interface);
printf("%s\t", key(peer->public_key));
+ printf("%s\t", key(peer->preshared_key));
if (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)
printf("%s\t", endpoint(&peer->endpoint.addr));
else
@@ -301,10 +301,6 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int
if (with_interface)
printf("%s\t", device->interface);
printf("%s\n", key(device->private_key));
- } else if (!strcmp(param, "preshared-key")) {
- if (with_interface)
- printf("%s\t", device->interface);
- printf("%s\n", key(device->preshared_key));
} else if (!strcmp(param, "listen-port")) {
if (with_interface)
printf("%s\t", device->interface);
@@ -358,6 +354,13 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int
else
printf("%s\toff\n", key(peer->public_key));
}
+ } else if (!strcmp(param, "preshared-keys")) {
+ for_each_wgpeer(device, peer, i) {
+ if (with_interface)
+ printf("%s\t", device->interface);
+ printf("%s\t", key(peer->public_key));
+ printf("%s\n", key(peer->preshared_key));
+ }
} else if (!strcmp(param, "peers")) {
for_each_wgpeer(device, peer, i) {
if (with_interface)
diff --git a/src/tools/showconf.c b/src/tools/showconf.c
index da48486..039abee 100644
--- a/src/tools/showconf.c
+++ b/src/tools/showconf.c
@@ -50,14 +50,14 @@ int showconf_main(int argc, char *argv[])
key_to_base64(base64, device->private_key);
printf("PrivateKey = %s\n", base64);
}
- if (memcmp(device->preshared_key, zero, WG_KEY_LEN)) {
- key_to_base64(base64, device->preshared_key);
- printf("PresharedKey = %s\n", base64);
- }
printf("\n");
for_each_wgpeer(device, peer, i) {
key_to_base64(base64, peer->public_key);
printf("[Peer]\nPublicKey = %s\n", base64);
+ if (memcmp(peer->preshared_key, zero, WG_KEY_LEN)) {
+ key_to_base64(base64, peer->preshared_key);
+ printf("PresharedKey = %s\n", base64);
+ }
if (peer->num_ipmasks)
printf("AllowedIPs = ");
for_each_wgipmask(peer, ipmask, j) {
diff --git a/src/tools/wg.8 b/src/tools/wg.8
index 2aa800e..1517432 100644
--- a/src/tools/wg.8
+++ b/src/tools/wg.8
@@ -36,7 +36,7 @@ Sub-commands that take an INTERFACE must be passed a WireGuard interface.
.SH COMMANDS
.TP
-\fBshow\fP { \fI<interface>\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIpreshared-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIpeers\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP]
+\fBshow\fP { \fI<interface>\fP | \fIall\fP | \fIinterfaces\fP } [\fIpublic-key\fP | \fIprivate-key\fP | \fIlisten-port\fP | \fIfwmark\fP | \fIpeers\fP | \fIpreshared-keys\fP | \fIendpoints\fP | \fIallowed-ips\fP | \fIlatest-handshakes\fP | \fIpersistent-keepalive\fP | \fItransfer\fP | \fIdump\fP]
Shows current WireGuard configuration of specified \fI<interface>\fP.
If no \fI<interface>\fP is specified, \fI<interface>\fP defaults to \fIall\fP.
If \fIinterfaces\fP is specified, prints a list of all WireGuard interfaces,
@@ -46,16 +46,16 @@ meant for the terminal. Otherwise, prints specified information grouped by
newlines and tabs, meant to be used in scripts. For this script-friendly display,
if \fIall\fP is specified, then the first field for all categories of information
is the interface name. If \fPdump\fP is specified, then several lines are printed;
-the first contains in order separated by tab: private-key, public-key, preshared-key,
-listen-port, fwmark. Subsequent lines are printed for each peer and contain in order
-separated by tab: public-key, endpoint, allowed-ips, latest-handshake, transfer-rx,
-transfer-tx, persistent-keepalive.
+the first contains in order separated by tab: private-key, public-key, listen-port,
+fwmark. Subsequent lines are printed for each peer and contain in order separated
+by tab: public-key, preshared-key, endpoint, allowed-ips, latest-handshake,
+transfer-rx, transfer-tx, persistent-keepalive.
.TP
\fBshowconf\fP \fI<interface>\fP
Shows the current configuration of \fI<interface>\fP in the format described
by \fICONFIGURATION FILE FORMAT\fP below.
.TP
-\fBset\fP \fI<interface>\fP [\fIlisten-port\fP \fI<port>\fP] [\fIfwmark\fP \fI<fwmark>\fP] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIendpoint\fP \fI<ip>:<port>\fP] [\fIpersistent-keepalive\fP \fI<interval seconds>\fP] [\fIallowed-ips\fP \fI<ip1>/<cidr1>\fP[,\fI<ip2>/<cidr2>\fP]...] ]...
+\fBset\fP \fI<interface>\fP [\fIlisten-port\fP \fI<port>\fP] [\fIfwmark\fP \fI<fwmark>\fP] [\fIprivate-key\fP \fI<file-path>\fP] [\fIpeer\fP \fI<base64-public-key>\fP [\fIremove\fP] [\fIpreshared-key\fP \fI<file-path>\fP] [\fIendpoint\fP \fI<ip>:<port>\fP] [\fIpersistent-keepalive\fP \fI<interval seconds>\fP] [\fIallowed-ips\fP \fI<ip1>/<cidr1>\fP[,\fI<ip2>/<cidr2>\fP]...] ]...
Sets configuration values for the specified \fI<interface>\fP. Multiple
\fIpeer\fPs may be specified, and if the \fIremove\fP argument is given
for a peer, that peer is removed, not configured. If \fIlisten-port\fP
@@ -126,11 +126,6 @@ The \fIInterface\fP section may contain the following fields:
.IP \(bu
PrivateKey \(em a base64 private key generated by \fIwg genkey\fP. Required.
.IP \(bu
-PresharedKey \(em a base64 preshared key generated by \fIwg genpsk\fP. Optional,
-and may be omitted. This option adds an additional layer of symmetric-key
-cryptography to be mixed into the already existing public-key cryptography,
-for post-quantum resistance.
-.IP \(bu
ListenPort \(em a 16-bit port for listening. Optional; if not specified, chosen
randomly.
.IP \(bu
@@ -143,6 +138,11 @@ PublicKey \(em a base64 public key calculated by \fIwg pubkey\fP from a
private key, and usually transmitted out of band to the author of the
configuration file. Required.
.IP \(bu
+PresharedKey \(em a base64 preshared key generated by \fIwg genpsk\fP. Optional,
+and may be omitted. This option adds an additional layer of symmetric-key
+cryptography to be mixed into the already existing public-key cryptography,
+for post-quantum resistance.
+.IP \(bu
AllowedIPs \(em a comma-separated list of ip (v4 or v6) addresses with
CIDR masks from which this peer is allowed to send incoming traffic and
to which outgoing traffic for this peer is directed. The catch-all
diff --git a/src/uapi.h b/src/uapi.h
index a77d006..1cc2bba 100644
--- a/src/uapi.h
+++ b/src/uapi.h
@@ -99,10 +99,13 @@ struct wgipmask {
enum {
WGPEER_REMOVE_ME = (1 << 0),
- WGPEER_REPLACE_IPMASKS = (1 << 1)
+ WGPEER_REPLACE_IPMASKS = (1 << 1),
+ WGPEER_REMOVE_PRESHARED_KEY = (1 << 2)
};
+
struct wgpeer {
__u8 public_key[WG_KEY_LEN]; /* Get/Set */
+ __u8 preshared_key[WG_KEY_LEN]; /* Get/Set */
__u32 flags; /* Set */
union {
@@ -121,12 +124,11 @@ struct wgpeer {
enum {
WGDEVICE_REPLACE_PEERS = (1 << 0),
WGDEVICE_REMOVE_PRIVATE_KEY = (1 << 1),
- WGDEVICE_REMOVE_PRESHARED_KEY = (1 << 2),
- WGDEVICE_REMOVE_FWMARK = (1 << 3)
+ WGDEVICE_REMOVE_FWMARK = (1 << 2)
};
enum {
- WG_API_VERSION_MAGIC = 0xbeef0001
+ WG_API_VERSION_MAGIC = 0xbeef0002
};
struct wgdevice {
@@ -136,7 +138,6 @@ struct wgdevice {
__u8 public_key[WG_KEY_LEN]; /* Get */
__u8 private_key[WG_KEY_LEN]; /* Get/Set */
- __u8 preshared_key[WG_KEY_LEN]; /* Get/Set */
__u32 fwmark; /* Get/Set */
__u16 port; /* Get/Set */