From 51eec3ab2954ac2f167299597d061d6d27b24456 Mon Sep 17 00:00:00 2001
From: Maxime Puys <drakes00@gmail.com>
Date: Thu, 14 Mar 2024 11:23:55 +0100
Subject: [PATCH] Temp: backup correct alpine config

---
 Vagrantfile                      |  17 +-
 linux/APKBUILD                   | 353 ++++++++++++
 linux/noise.c                    | 929 +++++++++++++++++++++++++++++++
 linux/noise.c.original           | 861 ++++++++++++++++++++++++++++
 linux/wireguard-leak.patch       | 238 ++++++++
 wireguard-attacker/precomutation | 145 +++--
 wireguard-server/wg0.conf        |   1 +
 7 files changed, 2477 insertions(+), 67 deletions(-)
 create mode 100644 linux/APKBUILD
 create mode 100644 linux/noise.c
 create mode 100644 linux/noise.c.original
 create mode 100644 linux/wireguard-leak.patch
 create mode 120000 wireguard-server/wg0.conf

diff --git a/Vagrantfile b/Vagrantfile
index bd6edf6..d57c36b 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -28,7 +28,13 @@ Vagrant.configure("2") do |config|
 
       # Mount local directories on VMs
       if vm_config[:name] == "wireguard-client1"
+        node.vm.box = "generic/alpine318"
         node.vm.synced_folder "./wireguard-client1", "/etc/wireguard"
+        node.vm.synced_folder "./linux", "/linux"
+        node.vm.provider "virtualbox" do |vb|
+          vb.memory = "4096"
+          vb.cpus = 8
+        end
       end
       if vm_config[:name] == "wireguard-client2"
         node.vm.synced_folder "./wireguard-client2", "/etc/wireguard"
@@ -92,17 +98,14 @@ Vagrant.configure("2") do |config|
           echo "Hello from #{vm_config[:name]} with IP: #{vm_config[:ip]}!"
 
           # Update repository
-          sudo apt-get -y update
+          sudo apk update
 
-            # Install wireguard along with 
-          sudo apt-get -y install wireguard 
+          # Install wireguard along with
+          sudo apk add wireguard-tools-wg-quick iptables
 
           # Some customizations
-          sudo apt-get -y install vim
+          sudo apk add vim
           sed -ri 's/^( *)#alias (.*)/\\1alias \\2/' /home/vagrant/.bashrc
-
-          # Install resolvconf for Wireguard to work
-          sudo apt-get -y install resolvconf
         SHELL
       end
 
diff --git a/linux/APKBUILD b/linux/APKBUILD
new file mode 100644
index 0000000..416625d
--- /dev/null
+++ b/linux/APKBUILD
@@ -0,0 +1,353 @@
+# Maintainer: Natanael Copa <ncopa@alpinelinux.org>
+
+_flavor=lts
+pkgname=linux-$_flavor
+pkgver=6.1.77
+case $pkgver in
+	*.*.*)	_kernver=${pkgver%.*};;
+	*.*) _kernver=$pkgver;;
+esac
+pkgrel=0
+pkgdesc="Linux lts kernel"
+url="https://www.kernel.org"
+depends="initramfs-generator"
+_depends_dev="perl gmp-dev mpc1-dev mpfr-dev elfutils-dev bash flex bison zstd"
+makedepends="$_depends_dev sed installkernel bc linux-headers linux-firmware-any openssl-dev>3 mawk
+	diffutils findutils zstd pahole>=1.25 python3"
+options="!strip"
+_config=${config:-config-lts.${CARCH}}
+source="https://cdn.kernel.org/pub/linux/kernel/v${pkgver%%.*}.x/linux-$_kernver.tar.xz
+	0001-powerpc-config-defang-gcc-check-for-stack-protector-.patch
+	0001-x86-Compress-vmlinux-with-zstd-19-instead-of-22.patch
+	0001-kexec-add-kexec_load_disabled-boot-option.patch
+	awk.patch
+	wireguard-leak.patch
+	ppc-notext.patch
+	0001-tty-Move-sysctl-setup-into-core-tty-logic.patch
+	0002-tty-Allow-TIOCSTI-to-be-disabled.patch
+	0003-tty-Move-TIOCSTI-toggle-variable-before-kerndoc.patch
+
+	virt.aarch64.config
+	virt.armv7.config
+	virt.ppc64le.config
+	virt.x86.config
+	virt.x86_64.config
+	"
+# subpackages="$pkgname-dev:_dev:$CBUILD_ARCH"
+for _i in $source; do
+	case $_i in
+	*.$CARCH.config)
+		_f=${_i%."$CARCH".config}
+		_flavors="$_flavors $_f"
+		if [ "linux-$_f" != "$pkgname" ]; then
+			subpackages="$subpackages linux-$_f::$CBUILD_ARCH linux-$_f-dev:_dev:$CBUILD_ARCH"
+		fi
+		;;
+	esac
+done
+builddir="$srcdir"/linux-$_kernver
+
+if [ "${pkgver%.0}" = "$pkgver" ]; then
+	source="$source
+	https://cdn.kernel.org/pub/linux/kernel/v${pkgver%%.*}.x/patch-$pkgver.xz"
+fi
+arch="all !armhf !riscv64"
+license="GPL-2.0-only"
+
+# secfixes:
+#   5.10.4-r0:
+#     - CVE-2020-29568
+#     - CVE-2020-29569
+#   5.15.74-r0:
+#     - CVE-2022-41674
+#     - CVE-2022-42719
+#     - CVE-2022-42720
+#     - CVE-2022-42721
+#     - CVE-2022-42722
+#   6.1.27-r3:
+#     - CVE-2023-32233
+#   6.1.74-r0:
+#     - CVE-2023-46838
+
+prepare() {
+	if [ "$_kernver" != "$pkgver" ]; then
+		msg "Applying patch-$pkgver.xz"
+		unxz -c < "$srcdir"/patch-$pkgver.xz | patch -p1 -N
+	fi
+
+	default_prepare
+
+	# remove localversion from patch if any
+	rm -f localversion*
+}
+
+_kernelarch() {
+	local arch="$1"
+	case "$arch" in
+		aarch64*) arch="arm64" ;;
+		arm*) arch="arm" ;;
+		mips*) arch="mips" ;;
+		ppc*) arch="powerpc" ;;
+		s390*) arch="s390" ;;
+	esac
+	echo "$arch"
+}
+
+_prepareconfig() {
+	local _flavor="$1"
+	local _arch="$2"
+	local _config=$_flavor.$_arch.config
+	local _builddir="$srcdir"/build-$_flavor.$_arch
+	mkdir -p "$_builddir"
+	echo "-$pkgrel-$_flavor" > "$_builddir"/localversion-alpine
+
+	cp "$srcdir"/$_config "$_builddir"/.config
+	msg "Configuring $_flavor kernel ($_arch)"
+	make -C "$srcdir"/linux-$_kernver \
+		O="$_builddir" \
+		ARCH="$(_kernelarch $_arch)" \
+		olddefconfig
+
+	if grep "CONFIG_MODULE_SIG=y" "$_builddir"/.config >/dev/null; then
+		if [ -f "$KERNEL_SIGNING_KEY" ]; then
+			sed -i -e "s:^CONFIG_MODULE_SIG_KEY=.*:CONFIG_MODULE_SIG_KEY=\"$KERNEL_SIGNING_KEY\":" \
+				"$_builddir"/.config
+			msg "Using $KERNEL_SIGNING_KEY to sign $_flavor kernel ($_arch) modules"
+		else
+			warning "KERNEL_SIGNING_KEY was not set. A signing key will be generated, but 3rd"
+			warning "party modules can not be signed"
+		fi
+	fi
+}
+
+listconfigs() {
+	for i in $source; do
+		case "$i" in
+			*.config) echo $i;;
+		esac
+	done
+}
+
+prepareconfigs() {
+	for _config in $(listconfigs); do
+		local _flavor=${_config%%.*}
+		local _arch=${_config%.config}
+		_arch=${_arch#*.}
+		local _builddir="$srcdir"/build-$_flavor.$_arch
+		_prepareconfig "$_flavor" "$_arch"
+	done
+}
+
+# this is supposed to be run before version is bumped so we can compare
+# what new kernel config knobs are introduced
+prepareupdate() {
+	clean && fetch && unpack && prepare && deps
+	prepareconfigs
+	rm -r "$srcdir"/linux-$_kernver
+}
+
+updateconfigs() {
+	if ! [ -d "$srcdir"/linux-$_kernver ]; then
+		deps && fetch && unpack && prepare
+	fi
+	for _config in ${CONFIGS:-$(listconfigs)}; do
+		msg "updating $_config"
+		local _flavor=${_config%%.*}
+		local _arch=${_config%.config}
+		_arch=${_arch#*.}
+		local _builddir="$srcdir"/build-$_flavor.$_arch
+		mkdir -p "$_builddir"
+		echo "-$pkgrel-$_flavor" > "$_builddir"/localversion-alpine
+		local actions="listnewconfig oldconfig"
+		if ! [ -f "$_builddir"/.config ]; then
+			cp "$srcdir"/$_config "$_builddir"/.config
+			actions="olddefconfig"
+		fi
+		env | grep ^CONFIG_ >> "$_builddir"/.config || true
+		make -j1 -C "$srcdir"/linux-$_kernver \
+			O="$_builddir" \
+			ARCH="$(_kernelarch $_arch)" \
+			$actions savedefconfig
+
+		cp "$_builddir"/defconfig "$startdir"/$_config
+	done
+}
+
+build() {
+	unset LDFLAGS
+	# for some reason these sometimes leak into the kernel build,
+	# -Werror=format-security breaks some stuff
+	unset CFLAGS CPPFLAGS CXXFLAGS
+	export KBUILD_BUILD_TIMESTAMP="$(date -Ru${SOURCE_DATE_EPOCH:+d @$SOURCE_DATE_EPOCH})"
+	for i in $_flavors; do
+		_prepareconfig "$i" "$CARCH"
+	done
+	for i in $_flavors; do
+		msg "Building $i kernel"
+		cd "$srcdir"/build-$i.$CARCH
+
+		# set org in cert for modules signing
+		# https://www.kernel.org/doc/html/v6.1/admin-guide/module-signing.html#generating-signing-keys
+		mkdir -p certs
+		sed -e 's/#O = Unspecified company/O = alpinelinux.org/' \
+			"$srcdir"/linux-$_kernver/certs/default_x509.genkey \
+			> certs/x509.genkey
+
+		make ARCH="$(_kernelarch $CARCH)" \
+			CC="${CC:-gcc}" \
+			AWK="${AWK:-mawk}" \
+			KBUILD_BUILD_VERSION="$((pkgrel + 1 ))-Alpine"
+	done
+}
+
+_package() {
+	local _buildflavor="$1" _outdir="$2"
+	export KBUILD_BUILD_TIMESTAMP="$(date -Ru${SOURCE_DATE_EPOCH:+d @$SOURCE_DATE_EPOCH})"
+
+	cd "$srcdir"/build-$_buildflavor.$CARCH
+	local _abi_release="$(make -s kernelrelease)"
+	# modules_install seems to regenerate a defect Modules.symvers on s390x. Work
+	# around it by backing it up and restore it after modules_install
+	cp Module.symvers Module.symvers.backup
+
+	mkdir -p "$_outdir"/boot "$_outdir"/lib/modules
+
+	local _install
+	case "$CARCH" in
+		arm*|aarch64) _install="zinstall dtbs_install";;
+		*) _install=install;;
+	esac
+
+	make modules_install $_install \
+		ARCH="$(_kernelarch $CARCH)" \
+		INSTALL_MOD_PATH="$_outdir" \
+		INSTALL_MOD_STRIP=1 \
+		INSTALL_PATH="$_outdir"/boot \
+		INSTALL_DTBS_PATH="$_outdir/boot/dtbs-$_buildflavor"
+
+	cp Module.symvers.backup Module.symvers
+
+	rm -f "$_outdir"/lib/modules/"$_abi_release"/build \
+		"$_outdir"/lib/modules/"$_abi_release"/source
+	rm -rf "$_outdir"/lib/firmware
+
+	install -D -m644 include/config/kernel.release \
+		"$_outdir"/usr/share/kernel/$_buildflavor/kernel.release
+}
+
+# main flavor installs in $pkgdir
+package() {
+	depends="$depends linux-firmware-any"
+
+	_package lts "$pkgdir"
+
+	# copy files for linux-lts-doc sub package
+	mkdir -p "$pkgdir"/usr/share/doc
+	cp -r "$srcdir"/linux-"$_kernver"/Documentation \
+		"$pkgdir"/usr/share/doc/linux-doc-"$pkgver"/
+	# remove files that aren't part of the documentation itself
+	for nondoc in \
+		.gitignore conf.py docutils.conf \
+		dontdiff Kconfig Makefile
+	do
+		rm "$pkgdir"/usr/share/doc/linux-doc-"$pkgver"/"$nondoc"
+	done
+	# create /usr/share/doc/linux-doc symlink
+	cd "$pkgdir"/usr/share/doc; ln -s linux-doc-"$pkgver" linux-doc
+}
+
+# subflavors install in $subpkgdir
+virt() {
+	_package virt "$subpkgdir"
+}
+
+_dev() {
+	local _flavor=$(echo $subpkgname | sed -E 's/(^linux-|-dev$)//g')
+	local _builddir="$srcdir"/build-$_flavor.$CARCH
+	local _abi_release="$(make -C "$_builddir" -s kernelrelease)"
+	# copy the only the parts that we really need for build 3rd party
+	# kernel modules and install those as /usr/src/linux-headers,
+	# simlar to what ubuntu does
+	#
+	# this way you dont need to install the 300-400 kernel sources to
+	# build a tiny kernel module
+	#
+	pkgdesc="Headers and script for third party modules for $_flavor kernel"
+	depends="$_depends_dev"
+	local dir="$subpkgdir"/usr/src/linux-headers-"$_abi_release"
+	export KBUILD_BUILD_TIMESTAMP="$(date -Ru${SOURCE_DATE_EPOCH:+d @$SOURCE_DATE_EPOCH})"
+
+	# first we import config, run prepare to set up for building
+	# external modules, and create the scripts
+	mkdir -p "$dir"
+	cp -a "$_builddir"/.config "$_builddir"/localversion-alpine \
+		"$dir"/
+
+	install -D -t "$dir"/certs "$_builddir"/certs/signing_key.x509 || :
+
+	make -C "$srcdir"/linux-$_kernver \
+		O="$dir" \
+		ARCH="$(_kernelarch $CARCH)" \
+		AWK="${AWK:-mawk}" \
+		prepare modules_prepare scripts
+
+	# remove the stuff that points to real sources. we want 3rd party
+	# modules to believe this is the sources
+	rm "$dir"/Makefile "$dir"/source
+
+	# copy the needed stuff from real sources
+	#
+	# this is taken from ubuntu kernel build script
+	# http://kernel.ubuntu.com/git/ubuntu/ubuntu-zesty.git/tree/debian/rules.d/3-binary-indep.mk
+	cd "$srcdir"/linux-$_kernver
+	find .  -path './include/*' -prune \
+		-o -path './scripts/*' -prune -o -type f \
+		\( -name 'Makefile*' -o -name 'Kconfig*' -o -name 'Kbuild*' -o \
+		   -name '*.sh' -o -name '*.pl' -o -name '*.lds' -o -name 'Platform' \) \
+		-print | cpio -pdm "$dir"
+
+	cp -a scripts include "$dir"
+
+	find "arch/$_karch" -name include -type d -print | while IFS='' read -r folder; do
+		find "$folder" -type f
+	done | sort -u | cpio -pdm "$dir"
+
+	install -Dm644 "$srcdir"/build-$_flavor.$CARCH/Module.symvers \
+		"$dir"/Module.symvers
+
+	# remove unneeded things
+	msg "Removing documentation..."
+	rm -r "$dir"/Documentation
+	find "$dir" -type f -name '*.o' -printf 'Removing %P\n' -delete
+	local _karch="$(_kernelarch $CARCH | sed 's/x86_64/x86/')"
+	msg "Removing unneeded arch headers..."
+	for i in "$dir"/arch/*; do
+		if [ "${i##*/}" != "$_karch" ]; then
+			echo "  ${i##*/}"
+			rm -r "$i"
+		fi
+	done
+
+	mkdir -p "$subpkgdir"/lib/modules/"$_abi_release"
+	ln -sf /usr/src/linux-headers-"$_abi_release" \
+		"$subpkgdir"/lib/modules/"$_abi_release"/build
+}
+
+sha512sums="
+6ed2a73c2699d0810e54753715635736fc370288ad5ce95c594f2379959b0e418665cd71bc512a0273fe226fe90074d8b10d14c209080a6466498417a4fdda68  linux-6.1.tar.xz
+a6c826e8c3884550fbe1dee3771b201b3395b073369f7fbfb5dc4aa5501e848b0bbc3a5ad80d932a6a50cbe0cfd579c75662f0c43ef79a38c1c6848db395c4c8  0001-powerpc-config-defang-gcc-check-for-stack-protector-.patch
+55f732c01e10948b283eb1cc77ce3f1169428251075f8f5dd3100d8fda1f08b6b3850969b84b42a52468d4f7c7ec7b49d01f0dff3c20c30f2e9ac0c07071b9d3  0001-x86-Compress-vmlinux-with-zstd-19-instead-of-22.patch
+5464cf4b2e251972091cc9d5138291a94298737e98ec24829829203fc879593b440a7cd1ca08e6d01debaab2887976cb5d38fae2fbe5149cef1735d5d73bb086  0001-kexec-add-kexec_load_disabled-boot-option.patch
+15fca2343b57dcab247c5ad86175ae638bace41a803984170a4aca4f3ec70edf6399ce5c6de82ae0dfabd3430a7a7567941718806b6d5d56515ecc7d55b2a800  awk.patch
+a1bc0109ad4b429e5189eaa3fd96891ec5769bea4cc5c6e86ce523c20ad7641fd5f937e1405680a8276c3e4e1deec3c5ae62361675decc9b2a9ff5b6f75111e0  wireguard-leak.patch
+c74488940244ba032e741c370767467cfab93b89077b5dfccfed39b658f381e0995527e6c61de53b1c112b04ba647bfccf00f2e05c0637d3c703680a508821cc  ppc-notext.patch
+f8582538a8482656138a0ae56b67a6d3cec09ff8d82a3f790ffa3fa7c37d0ae24a04f01f0ae1aec09838f86490907995c2568be7841a3821f0e64704f60153e5  0001-tty-Move-sysctl-setup-into-core-tty-logic.patch
+0bfd2c138e997f25f821cd518263ad515fc303bbde37bcdb7dd651d30316c77652fec6fedb195102b8476b92b1e7269c143dad5df6431db49c4318375ad2e802  0002-tty-Allow-TIOCSTI-to-be-disabled.patch
+8b245ec672a56dae7ce82f488880f6f3c0b65776177db663a29f26de752bf2cefe6b677e2cedbebd85e98d6f6e3d66b9f198a0d7a6042a20191b5ca8f4a749e7  0003-tty-Move-TIOCSTI-toggle-variable-before-kerndoc.patch
+f03a4ed754fc0bede7a4279fbe4fdc47b5945e336e41fce57f7130bf7e52995a08e33e1a881586cc5e260bf68ff5de3ed1aa1318b77ac9e50ba6895fe0d3be9d  virt.aarch64.config
+d4323576ffeb7605346673f3f1bfe97bc8da8d00afc272ae6017ba2b5d6e4170004fddec155c993a85f66a3d0e9abeb7da95bbae1c1c9c6cc55235db7bc6d2d3  virt.armv7.config
+f16ba6d9cb8d0e5b5527c6e4df468951237ee6d9c5af432b3a25b72c167bfb83d5941c54788ca9d6610a9cc203da4aa4ebb06e20e7a1d72263b12bd0996a8197  virt.ppc64le.config
+f24432150e2bf3565a04be5034cfb7e6ae91012ca8cfe97c844055db6b637a8e35cc7273cc29c3168421e8636f0160c622e85e4ba858acc4276f9de434bbadff  virt.x86.config
+d90e3fed0584accae7289c392d57c656aa2c03529ab1e27f6dc06a4a2e6047daf6f4319a911982a2d587c6f506d018b65c4f52e7a3b4870f2af351d4300c3f4a  virt.x86_64.config
+83cbff3cefebf4c7629f8dc58fcbacd1cd2bc73b277850376b6c63ff09c6036e9d464c0b84c0975e5a1fea8929fe9882f19843fec1e803517544411c4ff662ac  patch-6.1.77.xz
+"
diff --git a/linux/noise.c b/linux/noise.c
new file mode 100644
index 0000000..1f9b1e0
--- /dev/null
+++ b/linux/noise.c
@@ -0,0 +1,929 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include "noise.h"
+#include "device.h"
+#include "peer.h"
+#include "messages.h"
+#include "queueing.h"
+#include "peerlookup.h"
+
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+#include <linux/bitmap.h>
+#include <linux/scatterlist.h>
+#include <linux/highmem.h>
+#include <crypto/algapi.h>
+
+/* This implements Noise_IKpsk2:
+ *
+ * <- s
+ * ******
+ * -> e, es, s, ss, {t}
+ * <- e, ee, se, psk, {}
+ */
+
+void printHex(const char *varName, const u8 *ptr, size_t size) {
+    printk("%s = \'", varName);
+    for (size_t i = 0; i < size; i++) {
+        printk(KERN_CONT "%.2x", ptr[i]);
+    }
+    printk(KERN_CONT "\'");
+}
+
+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] __ro_after_init;
+static u8 handshake_init_chaining_key[NOISE_HASH_LEN] __ro_after_init;
+static atomic64_t keypair_counter = ATOMIC64_INIT(0);
+
+void __init wg_noise_init(void)
+{
+	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);
+}
+
+/* Must hold peer->handshake.static_identity->lock */
+void wg_noise_precompute_static_static(struct wg_peer *peer)
+{
+	down_write(&peer->handshake.lock);
+	if (!peer->handshake.static_identity->has_identity ||
+	    !curve25519(peer->handshake.precomputed_static_static,
+			peer->handshake.static_identity->static_private,
+			peer->handshake.remote_static))
+		memset(peer->handshake.precomputed_static_static, 0,
+		       NOISE_PUBLIC_KEY_LEN);
+	up_write(&peer->handshake.lock);
+}
+
+void wg_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 wg_peer *peer)
+{
+	memset(handshake, 0, sizeof(*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);
+	if (peer_preshared_key)
+		memcpy(handshake->preshared_key, peer_preshared_key,
+		       NOISE_SYMMETRIC_KEY_LEN);
+	handshake->static_identity = static_identity;
+	handshake->state = HANDSHAKE_ZEROED;
+	wg_noise_precompute_static_static(peer);
+}
+
+static void handshake_zero(struct noise_handshake *handshake)
+{
+	memset(&handshake->ephemeral_private, 0, NOISE_PUBLIC_KEY_LEN);
+	memset(&handshake->remote_ephemeral, 0, NOISE_PUBLIC_KEY_LEN);
+	memset(&handshake->hash, 0, NOISE_HASH_LEN);
+	memset(&handshake->chaining_key, 0, NOISE_HASH_LEN);
+	handshake->remote_index = 0;
+	handshake->state = HANDSHAKE_ZEROED;
+}
+
+void wg_noise_handshake_clear(struct noise_handshake *handshake)
+{
+	down_write(&handshake->lock);
+	wg_index_hashtable_remove(
+			handshake->entry.peer->device->index_hashtable,
+			&handshake->entry);
+	handshake_zero(handshake);
+	up_write(&handshake->lock);
+}
+
+static struct noise_keypair *keypair_create(struct wg_peer *peer)
+{
+	struct noise_keypair *keypair = kzalloc(sizeof(*keypair), GFP_KERNEL);
+
+	if (unlikely(!keypair))
+		return NULL;
+	spin_lock_init(&keypair->receiving_counter.lock);
+	keypair->internal_id = atomic64_inc_return(&keypair_counter);
+	keypair->entry.type = INDEX_HASHTABLE_KEYPAIR;
+	keypair->entry.peer = peer;
+	kref_init(&keypair->refcount);
+	return keypair;
+}
+
+static void keypair_free_rcu(struct rcu_head *rcu)
+{
+	kfree_sensitive(container_of(rcu, struct noise_keypair, rcu));
+}
+
+static void keypair_free_kref(struct kref *kref)
+{
+	struct noise_keypair *keypair =
+		container_of(kref, struct noise_keypair, refcount);
+
+	net_dbg_ratelimited("%s: Keypair %llu destroyed for peer %llu\n",
+			    keypair->entry.peer->device->dev->name,
+			    keypair->internal_id,
+			    keypair->entry.peer->internal_id);
+	wg_index_hashtable_remove(keypair->entry.peer->device->index_hashtable,
+				  &keypair->entry);
+	call_rcu(&keypair->rcu, keypair_free_rcu);
+}
+
+void wg_noise_keypair_put(struct noise_keypair *keypair, bool unreference_now)
+{
+	if (unlikely(!keypair))
+		return;
+	if (unlikely(unreference_now))
+		wg_index_hashtable_remove(
+			keypair->entry.peer->device->index_hashtable,
+			&keypair->entry);
+	kref_put(&keypair->refcount, keypair_free_kref);
+}
+
+struct noise_keypair *wg_noise_keypair_get(struct noise_keypair *keypair)
+{
+	RCU_LOCKDEP_WARN(!rcu_read_lock_bh_held(),
+		"Taking noise keypair reference without holding the RCU BH read lock");
+	if (unlikely(!keypair || !kref_get_unless_zero(&keypair->refcount)))
+		return NULL;
+	return keypair;
+}
+
+void wg_noise_keypairs_clear(struct noise_keypairs *keypairs)
+{
+	struct noise_keypair *old;
+
+	spin_lock_bh(&keypairs->keypair_update_lock);
+
+	/* We zero the next_keypair before zeroing the others, so that
+	 * wg_noise_received_with_keypair returns early before subsequent ones
+	 * are zeroed.
+	 */
+	old = rcu_dereference_protected(keypairs->next_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	RCU_INIT_POINTER(keypairs->next_keypair, NULL);
+	wg_noise_keypair_put(old, true);
+
+	old = rcu_dereference_protected(keypairs->previous_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	RCU_INIT_POINTER(keypairs->previous_keypair, NULL);
+	wg_noise_keypair_put(old, true);
+
+	old = rcu_dereference_protected(keypairs->current_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	RCU_INIT_POINTER(keypairs->current_keypair, NULL);
+	wg_noise_keypair_put(old, true);
+
+	spin_unlock_bh(&keypairs->keypair_update_lock);
+}
+
+void wg_noise_expire_current_peer_keypairs(struct wg_peer *peer)
+{
+	struct noise_keypair *keypair;
+
+	wg_noise_handshake_clear(&peer->handshake);
+	wg_noise_reset_last_sent_handshake(&peer->last_sent_handshake);
+
+	spin_lock_bh(&peer->keypairs.keypair_update_lock);
+	keypair = rcu_dereference_protected(peer->keypairs.next_keypair,
+			lockdep_is_held(&peer->keypairs.keypair_update_lock));
+	if (keypair)
+		keypair->sending.is_valid = false;
+	keypair = rcu_dereference_protected(peer->keypairs.current_keypair,
+			lockdep_is_held(&peer->keypairs.keypair_update_lock));
+	if (keypair)
+		keypair->sending.is_valid = false;
+	spin_unlock_bh(&peer->keypairs.keypair_update_lock);
+}
+
+static void add_new_keypair(struct noise_keypairs *keypairs,
+			    struct noise_keypair *new_keypair)
+{
+	struct noise_keypair *previous_keypair, *next_keypair, *current_keypair;
+
+	spin_lock_bh(&keypairs->keypair_update_lock);
+	previous_keypair = rcu_dereference_protected(keypairs->previous_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	next_keypair = rcu_dereference_protected(keypairs->next_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	current_keypair = rcu_dereference_protected(keypairs->current_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	if (new_keypair->i_am_the_initiator) {
+		/* If we're the initiator, it means we've sent a handshake, and
+		 * received a confirmation response, which means this new
+		 * keypair can now be used.
+		 */
+		if (next_keypair) {
+			/* If there already was a next keypair pending, we
+			 * demote it to be the previous keypair, and free the
+			 * existing current. Note that this means KCI can result
+			 * in this transition. It would perhaps be more sound to
+			 * always just get rid of the unused next keypair
+			 * instead of putting it in the previous slot, but this
+			 * might be a bit less robust. Something to think about
+			 * for the future.
+			 */
+			RCU_INIT_POINTER(keypairs->next_keypair, NULL);
+			rcu_assign_pointer(keypairs->previous_keypair,
+					   next_keypair);
+			wg_noise_keypair_put(current_keypair, true);
+		} else /* If there wasn't an existing next keypair, we replace
+			* the previous with the current one.
+			*/
+			rcu_assign_pointer(keypairs->previous_keypair,
+					   current_keypair);
+		/* At this point we can get rid of the old previous keypair, and
+		 * set up the new keypair.
+		 */
+		wg_noise_keypair_put(previous_keypair, true);
+		rcu_assign_pointer(keypairs->current_keypair, new_keypair);
+	} else {
+		/* If we're the responder, it means we can't use the new keypair
+		 * until we receive confirmation via the first data packet, so
+		 * we get rid of the existing previous one, the possibly
+		 * existing next one, and slide in the new next one.
+		 */
+		rcu_assign_pointer(keypairs->next_keypair, new_keypair);
+		wg_noise_keypair_put(next_keypair, true);
+		RCU_INIT_POINTER(keypairs->previous_keypair, NULL);
+		wg_noise_keypair_put(previous_keypair, true);
+	}
+	spin_unlock_bh(&keypairs->keypair_update_lock);
+}
+
+bool wg_noise_received_with_keypair(struct noise_keypairs *keypairs,
+				    struct noise_keypair *received_keypair)
+{
+	struct noise_keypair *old_keypair;
+	bool key_is_new;
+
+	/* We first check without taking the spinlock. */
+	key_is_new = received_keypair ==
+		     rcu_access_pointer(keypairs->next_keypair);
+	if (likely(!key_is_new))
+		return false;
+
+	spin_lock_bh(&keypairs->keypair_update_lock);
+	/* After locking, we double check that things didn't change from
+	 * beneath us.
+	 */
+	if (unlikely(received_keypair !=
+		    rcu_dereference_protected(keypairs->next_keypair,
+			    lockdep_is_held(&keypairs->keypair_update_lock)))) {
+		spin_unlock_bh(&keypairs->keypair_update_lock);
+		return false;
+	}
+
+	/* When we've finally received the confirmation, we slide the next
+	 * into the current, the current into the previous, and get rid of
+	 * the old previous.
+	 */
+	old_keypair = rcu_dereference_protected(keypairs->previous_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	rcu_assign_pointer(keypairs->previous_keypair,
+		rcu_dereference_protected(keypairs->current_keypair,
+			lockdep_is_held(&keypairs->keypair_update_lock)));
+	wg_noise_keypair_put(old_keypair, true);
+	rcu_assign_pointer(keypairs->current_keypair, received_keypair);
+	RCU_INIT_POINTER(keypairs->next_keypair, NULL);
+
+	spin_unlock_bh(&keypairs->keypair_update_lock);
+	return true;
+}
+
+/* Must hold static_identity->lock */
+void wg_noise_set_static_identity_private_key(
+	struct noise_static_identity *static_identity,
+	const u8 private_key[NOISE_PUBLIC_KEY_LEN])
+{
+	memcpy(static_identity->static_private, private_key,
+	       NOISE_PUBLIC_KEY_LEN);
+	curve25519_clamp_secret(static_identity->static_private);
+	static_identity->has_identity = curve25519_generate_public(
+		static_identity->static_public, private_key);
+}
+
+static void hmac(u8 *out, const u8 *in, const u8 *key, const size_t inlen, const size_t keylen)
+{
+	struct blake2s_state state;
+	u8 x_key[BLAKE2S_BLOCK_SIZE] __aligned(__alignof__(u32)) = { 0 };
+	u8 i_hash[BLAKE2S_HASH_SIZE] __aligned(__alignof__(u32));
+	int i;
+
+	// printHex("[HMAC] out", out, BLAKE2S_HASH_SIZE);
+	// printHex("[HMAC] in", in, inlen);
+	// printHex("[HMAC] key", key, keylen);
+
+	if (keylen > BLAKE2S_BLOCK_SIZE) {
+		blake2s_init(&state, BLAKE2S_HASH_SIZE);
+		blake2s_update(&state, key, keylen);
+		blake2s_final(&state, x_key);
+	} else
+		memcpy(x_key, key, keylen);
+
+	for (i = 0; i < BLAKE2S_BLOCK_SIZE; ++i)
+		x_key[i] ^= 0x36;
+
+	blake2s_init(&state, BLAKE2S_HASH_SIZE);
+	blake2s_update(&state, x_key, BLAKE2S_BLOCK_SIZE);
+	blake2s_update(&state, in, inlen);
+	blake2s_final(&state, i_hash);
+
+	for (i = 0; i < BLAKE2S_BLOCK_SIZE; ++i)
+		x_key[i] ^= 0x5c ^ 0x36;
+
+	blake2s_init(&state, BLAKE2S_HASH_SIZE);
+	blake2s_update(&state, x_key, BLAKE2S_BLOCK_SIZE);
+	blake2s_update(&state, i_hash, BLAKE2S_HASH_SIZE);
+	blake2s_final(&state, i_hash);
+
+	memcpy(out, i_hash, BLAKE2S_HASH_SIZE);
+	memzero_explicit(x_key, BLAKE2S_BLOCK_SIZE);
+	memzero_explicit(i_hash, BLAKE2S_HASH_SIZE);
+}
+
+/* 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, 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 output[BLAKE2S_HASH_SIZE + 1];
+	u8 secret[BLAKE2S_HASH_SIZE];
+
+	WARN_ON(IS_ENABLED(DEBUG) &&
+		(first_len > BLAKE2S_HASH_SIZE ||
+		 second_len > BLAKE2S_HASH_SIZE ||
+		 third_len > BLAKE2S_HASH_SIZE ||
+		 ((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 */
+	hmac(secret, data, chaining_key, data_len, NOISE_HASH_LEN);
+        //printHex("[KDF] secret", secret, BLAKE2S_HASH_SIZE);
+
+	if (!first_dst || !first_len)
+		goto out;
+
+	/* Expand first key: key = secret, data = 0x1 */
+	output[0] = 1;
+	hmac(output, output, secret, 1, BLAKE2S_HASH_SIZE);
+        //printHex("[KDF] output", output, BLAKE2S_HASH_SIZE+1);
+	memcpy(first_dst, output, first_len);
+
+	if (!second_dst || !second_len)
+		goto out;
+
+	/* Expand second key: key = secret, data = first-key || 0x2 */
+	output[BLAKE2S_HASH_SIZE] = 2;
+	hmac(output, output, secret, BLAKE2S_HASH_SIZE + 1, BLAKE2S_HASH_SIZE);
+        //printHex("[KDF] output2", output, BLAKE2S_HASH_SIZE+1);
+	memcpy(second_dst, output, second_len);
+
+	if (!third_dst || !third_len)
+		goto out;
+
+	/* Expand third key: key = secret, data = second-key || 0x3 */
+	output[BLAKE2S_HASH_SIZE] = 3;
+	hmac(output, output, secret, BLAKE2S_HASH_SIZE + 1, BLAKE2S_HASH_SIZE);
+	memcpy(third_dst, output, third_len);
+
+out:
+	/* Clear sensitive data from stack */
+	memzero_explicit(secret, BLAKE2S_HASH_SIZE);
+	memzero_explicit(output, BLAKE2S_HASH_SIZE + 1);
+}
+
+static void derive_keys(struct noise_symmetric_key *first_dst,
+			struct noise_symmetric_key *second_dst,
+			const u8 chaining_key[NOISE_HASH_LEN])
+{
+	u64 birthdate = ktime_get_coarse_boottime_ns();
+	const u8 absurd_value = 0x55;
+	kdf(first_dst->key, second_dst->key, NULL, &absurd_value,
+	    NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 1,
+	    chaining_key);
+	first_dst->birthdate = second_dst->birthdate = birthdate;
+	first_dst->is_valid = second_dst->is_valid = true;
+}
+
+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;
+    // printHex("dh_calculation", dh_calculation, NOISE_PUBLIC_KEY_LEN);
+    // printHex("private", private, NOISE_PUBLIC_KEY_LEN);
+    // printHex("public", public, 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;
+}
+
+static bool __must_check mix_precomputed_dh(u8 chaining_key[NOISE_HASH_LEN],
+					    u8 key[NOISE_SYMMETRIC_KEY_LEN],
+					    const u8 precomputed[NOISE_PUBLIC_KEY_LEN])
+{
+	static u8 zero_point[NOISE_PUBLIC_KEY_LEN];
+	if (unlikely(!crypto_memneq(precomputed, zero_point, NOISE_PUBLIC_KEY_LEN)))
+		return false;
+    printHex("precomputed", precomputed, NOISE_PUBLIC_KEY_LEN);
+    // printHex("private", private, NOISE_PUBLIC_KEY_LEN);
+    // printHex("public", public, NOISE_PUBLIC_KEY_LEN);
+	kdf(chaining_key, key, NULL, precomputed, NOISE_HASH_LEN,
+	    NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN,
+	    chaining_key);
+	return true;
+}
+
+static void mix_hash(u8 hash[NOISE_HASH_LEN], const u8 *src, size_t src_len)
+{
+	struct blake2s_state blake;
+
+	blake2s_init(&blake, NOISE_HASH_LEN);
+	blake2s_update(&blake, hash, NOISE_HASH_LEN);
+	blake2s_update(&blake, src, src_len);
+	blake2s_final(&blake, hash);
+}
+
+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])
+{
+	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);
+    // printHex("Hi", 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 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);
+    // printHex("dst_ciphertext", dst_ciphertext, noise_encrypted_len(src_len));
+	mix_hash(hash, dst_ciphertext, noise_encrypted_len(src_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;
+	mix_hash(hash, src_ciphertext, src_len);
+	return true;
+}
+
+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])
+{
+	if (ephemeral_dst != ephemeral_src)
+		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])
+{
+	struct timespec64 now;
+
+	ktime_get_real_ts64(&now);
+
+	/* In order to prevent some sort of infoleak from precise timers, we
+	 * round down the nanoseconds part to the closest rounded-down power of
+	 * two to the maximum initiations per second allowed anyway by the
+	 * implementation.
+	 */
+	now.tv_nsec = ALIGN_DOWN(now.tv_nsec,
+		rounddown_pow_of_two(NSEC_PER_SEC / INITIATIONS_PER_SECOND));
+
+	/* https://cr.yp.to/libtai/tai64.html */
+	*(__be64 *)output = cpu_to_be64(0x400000000000000aULL + now.tv_sec);
+	*(__be32 *)(output + sizeof(__be64)) = cpu_to_be32(now.tv_nsec);
+}
+
+bool
+wg_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;
+
+	/* We need to wait for crng _before_ taking any locks, since
+	 * curve25519_generate_secret uses get_random_bytes_wait.
+	 */
+	wait_for_random_bytes();
+
+	down_read(&handshake->static_identity->lock);
+	down_write(&handshake->lock);
+
+	if (unlikely(!handshake->static_identity->has_identity))
+		goto out;
+
+	dst->header.type = cpu_to_le32(MESSAGE_HANDSHAKE_INITIATION);
+
+	handshake_init(handshake->chaining_key, handshake->hash,
+		       handshake->remote_static);
+    // printHex("C_i", handshake->chaining_key, NOISE_HASH_LEN);
+    // printHex("H_i", handshake->hash, NOISE_HASH_LEN);
+
+	/* e */
+	curve25519_generate_secret(handshake->ephemeral_private);
+	if (!curve25519_generate_public(dst->unencrypted_ephemeral,
+					handshake->ephemeral_private))
+		goto out;
+    printHex("E^priv_i", handshake->ephemeral_private, NOISE_PUBLIC_KEY_LEN);
+    // printHex("E^pub_i", dst->unencrypted_ephemeral, NOISE_PUBLIC_KEY_LEN);
+
+	message_ephemeral(dst->unencrypted_ephemeral,
+			  dst->unencrypted_ephemeral, handshake->chaining_key,
+			  handshake->hash);
+    // printHex("dst->unencrypted_ephemeral", dst->unencrypted_ephemeral, NOISE_PUBLIC_KEY_LEN);
+    // printHex("handshake->chaining_key", handshake->chaining_key, NOISE_HASH_LEN);
+    // printHex("handshake->hash", handshake->hash, NOISE_HASH_LEN);
+
+	/* es */
+	if (!mix_dh(handshake->chaining_key, key, handshake->ephemeral_private,
+		    handshake->remote_static))
+		goto out;
+    // printHex("key", key, NOISE_SYMMETRIC_KEY_LEN);
+    // printHex("handshake->chaining_key", handshake->chaining_key, NOISE_HASH_LEN);
+
+	/* s */
+	message_encrypt(dst->encrypted_static,
+			handshake->static_identity->static_public,
+			NOISE_PUBLIC_KEY_LEN, key, handshake->hash);
+    // printHex("dst->encrypted_static", dst->encrypted_static, (size_t)(noise_encrypted_len(dst->encrypted_static)));
+    // printHex("handshake->hash", handshake->hash, NOISE_HASH_LEN);
+
+	/* ss */
+	if (!mix_precomputed_dh(handshake->chaining_key, key,
+				handshake->precomputed_static_static))
+		goto out;
+    // printHex("key", key, NOISE_SYMMETRIC_KEY_LEN);
+    // printHex("handshake->chaining_key", handshake->chaining_key, NOISE_HASH_LEN);
+
+	/* {t} */
+	tai64n_now(timestamp);
+	message_encrypt(dst->encrypted_timestamp, timestamp,
+			NOISE_TIMESTAMP_LEN, key, handshake->hash);
+    // printHex("timestamp", timestamp, NOISE_TIMESTAMP_LEN);
+    // printHex("dst->encrypted_timestamp", dst->encrypted_timestamp, NOISE_TIMESTAMP_LEN);
+    // printHex("handshake->hash", handshake->hash, NOISE_HASH_LEN);
+
+	dst->sender_index = wg_index_hashtable_insert(
+		handshake->entry.peer->device->index_hashtable,
+		&handshake->entry);
+
+	handshake->state = HANDSHAKE_CREATED_INITIATION;
+	ret = true;
+
+out:
+	up_write(&handshake->lock);
+	up_read(&handshake->static_identity->lock);
+	memzero_explicit(key, NOISE_SYMMETRIC_KEY_LEN);
+	return ret;
+}
+
+struct wg_peer *
+wg_noise_handshake_consume_initiation(struct message_handshake_initiation *src,
+				      struct wg_device *wg)
+{
+	struct wg_peer *peer = NULL, *ret_peer = NULL;
+	struct noise_handshake *handshake;
+	bool replay_attack, flood_attack;
+	u8 key[NOISE_SYMMETRIC_KEY_LEN];
+	u8 chaining_key[NOISE_HASH_LEN];
+	u8 hash[NOISE_HASH_LEN];
+	u8 s[NOISE_PUBLIC_KEY_LEN];
+	u8 e[NOISE_PUBLIC_KEY_LEN];
+	u8 t[NOISE_TIMESTAMP_LEN];
+	u64 initiation_consumption;
+
+	down_read(&wg->static_identity.lock);
+	if (unlikely(!wg->static_identity.has_identity))
+		goto out;
+
+	handshake_init(chaining_key, hash, wg->static_identity.static_public);
+    // printHex("C_i", handshake->chaining_key, NOISE_HASH_LEN);
+    // printHex("H_i", handshake->hash, NOISE_HASH_LEN);
+
+	/* e */
+	message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash);
+    // printHex("dst->unencrypted_ephemeral", src->unencrypted_ephemeral, NOISE_PUBLIC_KEY_LEN);
+    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
+    // printHex("hash", hash, NOISE_HASH_LEN);
+
+	/* es */
+	if (!mix_dh(chaining_key, key, wg->static_identity.static_private, e))
+		goto out;
+    // printHex("key", key, NOISE_SYMMETRIC_KEY_LEN);
+    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
+    // printHex("E", e, NOISE_PUBLIC_KEY_LEN);
+    // printHex("wg->static_identity.static_private", wg->static_identity.static_private, NOISE_PUBLIC_KEY_LEN);
+
+	/* s */
+	if (!message_decrypt(s, src->encrypted_static,
+			     sizeof(src->encrypted_static), key, hash))
+		goto out;
+    // printHex("hash", hash, NOISE_HASH_LEN);
+
+	/* Lookup which peer we're actually talking to */
+	peer = wg_pubkey_hashtable_lookup(wg->peer_hashtable, s);
+	if (!peer)
+		goto out;
+	handshake = &peer->handshake;
+
+	/* ss */
+	if (!mix_precomputed_dh(chaining_key, key,
+				handshake->precomputed_static_static))
+	    goto out;
+
+	/* {t} */
+	if (!message_decrypt(t, src->encrypted_timestamp,
+			     sizeof(src->encrypted_timestamp), key, hash))
+		goto out;
+
+	down_read(&handshake->lock);
+	replay_attack = memcmp(t, handshake->latest_timestamp,
+			       NOISE_TIMESTAMP_LEN) <= 0;
+	flood_attack = (s64)handshake->last_initiation_consumption +
+			       NSEC_PER_SEC / INITIATIONS_PER_SECOND >
+		       (s64)ktime_get_coarse_boottime_ns();
+	up_read(&handshake->lock);
+	if (replay_attack || flood_attack)
+		goto out;
+
+	/* Success! Copy everything to peer */
+	down_write(&handshake->lock);
+	memcpy(handshake->remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN);
+	if (memcmp(t, handshake->latest_timestamp, NOISE_TIMESTAMP_LEN) > 0)
+		memcpy(handshake->latest_timestamp, t, NOISE_TIMESTAMP_LEN);
+	memcpy(handshake->hash, hash, NOISE_HASH_LEN);
+	memcpy(handshake->chaining_key, chaining_key, NOISE_HASH_LEN);
+	handshake->remote_index = src->sender_index;
+	initiation_consumption = ktime_get_coarse_boottime_ns();
+	if ((s64)(handshake->last_initiation_consumption - initiation_consumption) < 0)
+		handshake->last_initiation_consumption = initiation_consumption;
+	handshake->state = HANDSHAKE_CONSUMED_INITIATION;
+	up_write(&handshake->lock);
+	ret_peer = peer;
+
+out:
+	memzero_explicit(key, NOISE_SYMMETRIC_KEY_LEN);
+	memzero_explicit(hash, NOISE_HASH_LEN);
+	memzero_explicit(chaining_key, NOISE_HASH_LEN);
+	up_read(&wg->static_identity.lock);
+	if (!ret_peer)
+		wg_peer_put(peer);
+	return ret_peer;
+}
+
+bool wg_noise_handshake_create_response(struct message_handshake_response *dst,
+					struct noise_handshake *handshake)
+{
+	u8 key[NOISE_SYMMETRIC_KEY_LEN];
+	bool ret = false;
+
+	/* We need to wait for crng _before_ taking any locks, since
+	 * curve25519_generate_secret uses get_random_bytes_wait.
+	 */
+	wait_for_random_bytes();
+
+	down_read(&handshake->static_identity->lock);
+	down_write(&handshake->lock);
+
+	if (handshake->state != HANDSHAKE_CONSUMED_INITIATION)
+		goto out;
+
+	dst->header.type = cpu_to_le32(MESSAGE_HANDSHAKE_RESPONSE);
+	dst->receiver_index = handshake->remote_index;
+
+	/* e */
+	curve25519_generate_secret(handshake->ephemeral_private);
+	if (!curve25519_generate_public(dst->unencrypted_ephemeral,
+					handshake->ephemeral_private))
+		goto out;
+
+	message_ephemeral(dst->unencrypted_ephemeral,
+			  dst->unencrypted_ephemeral, handshake->chaining_key,
+			  handshake->hash);
+
+	/* ee */
+	if (!mix_dh(handshake->chaining_key, NULL, handshake->ephemeral_private,
+		    handshake->remote_ephemeral))
+		goto out;
+
+	/* se */
+	if (!mix_dh(handshake->chaining_key, NULL, handshake->ephemeral_private,
+		    handshake->remote_static))
+		goto out;
+
+	/* 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 = wg_index_hashtable_insert(
+		handshake->entry.peer->device->index_hashtable,
+		&handshake->entry);
+
+	handshake->state = HANDSHAKE_CREATED_RESPONSE;
+	ret = true;
+
+out:
+	up_write(&handshake->lock);
+	up_read(&handshake->static_identity->lock);
+	memzero_explicit(key, NOISE_SYMMETRIC_KEY_LEN);
+	return ret;
+}
+
+struct wg_peer *
+wg_noise_handshake_consume_response(struct message_handshake_response *src,
+				    struct wg_device *wg)
+{
+	enum noise_handshake_state state = HANDSHAKE_ZEROED;
+	struct wg_peer *peer = NULL, *ret_peer = NULL;
+	struct noise_handshake *handshake;
+	u8 key[NOISE_SYMMETRIC_KEY_LEN];
+	u8 hash[NOISE_HASH_LEN];
+	u8 chaining_key[NOISE_HASH_LEN];
+	u8 e[NOISE_PUBLIC_KEY_LEN];
+	u8 ephemeral_private[NOISE_PUBLIC_KEY_LEN];
+	u8 static_private[NOISE_PUBLIC_KEY_LEN];
+	u8 preshared_key[NOISE_SYMMETRIC_KEY_LEN];
+
+	down_read(&wg->static_identity.lock);
+
+	if (unlikely(!wg->static_identity.has_identity))
+		goto out;
+
+	handshake = (struct noise_handshake *)wg_index_hashtable_lookup(
+		wg->index_hashtable, INDEX_HASHTABLE_HANDSHAKE,
+		src->receiver_index, &peer);
+	if (unlikely(!handshake))
+		goto out;
+
+	down_read(&handshake->lock);
+	state = handshake->state;
+	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);
+	memcpy(preshared_key, handshake->preshared_key,
+	       NOISE_SYMMETRIC_KEY_LEN);
+	up_read(&handshake->lock);
+
+	if (state != HANDSHAKE_CREATED_INITIATION)
+		goto fail;
+
+	/* e */
+	message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash);
+    // printHex("src->unencrypted_ephemeral", src->unencrypted_ephemeral, NOISE_PUBLIC_KEY_LEN);
+    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
+    // printHex("hash", hash, NOISE_HASH_LEN);
+
+	/* ee */
+	if (!mix_dh(chaining_key, NULL, ephemeral_private, e))
+		goto fail;
+    // printHex("ephemeral_private", ephemeral_private, NOISE_PUBLIC_KEY_LEN);
+    // printHex("e", e, NOISE_PUBLIC_KEY_LEN);
+    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
+
+	/* se */
+	if (!mix_dh(chaining_key, NULL, wg->static_identity.static_private, e))
+		goto fail;
+    // printHex("wg->static_identity.static_private", wg->static_identity.static_private, NOISE_PUBLIC_KEY_LEN);
+    // printHex("e", e, NOISE_PUBLIC_KEY_LEN);
+    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
+
+	/* psk */
+	mix_psk(chaining_key, hash, key, preshared_key);
+    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
+    // printHex("key", key, NOISE_SYMMETRIC_KEY_LEN);
+    // printHex("hash", hash, NOISE_HASH_LEN);
+
+	/* {} */
+	if (!message_decrypt(NULL, src->encrypted_nothing,
+			     sizeof(src->encrypted_nothing), key, hash))
+		goto fail;
+
+	/* Success! Copy everything to peer */
+	down_write(&handshake->lock);
+	/* It's important to check that the state is still the same, while we
+	 * have an exclusive lock.
+	 */
+	if (handshake->state != state) {
+		up_write(&handshake->lock);
+		goto fail;
+	}
+	memcpy(handshake->remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN);
+	memcpy(handshake->hash, hash, NOISE_HASH_LEN);
+	memcpy(handshake->chaining_key, chaining_key, NOISE_HASH_LEN);
+	handshake->remote_index = src->sender_index;
+	handshake->state = HANDSHAKE_CONSUMED_RESPONSE;
+	up_write(&handshake->lock);
+	ret_peer = peer;
+	goto out;
+
+fail:
+	wg_peer_put(peer);
+out:
+	memzero_explicit(key, NOISE_SYMMETRIC_KEY_LEN);
+	memzero_explicit(hash, NOISE_HASH_LEN);
+	memzero_explicit(chaining_key, NOISE_HASH_LEN);
+	memzero_explicit(ephemeral_private, NOISE_PUBLIC_KEY_LEN);
+	memzero_explicit(static_private, NOISE_PUBLIC_KEY_LEN);
+	memzero_explicit(preshared_key, NOISE_SYMMETRIC_KEY_LEN);
+	up_read(&wg->static_identity.lock);
+	return ret_peer;
+}
+
+bool wg_noise_handshake_begin_session(struct noise_handshake *handshake,
+				      struct noise_keypairs *keypairs)
+{
+	struct noise_keypair *new_keypair;
+	bool ret = false;
+
+	down_write(&handshake->lock);
+	if (handshake->state != HANDSHAKE_CREATED_RESPONSE &&
+	    handshake->state != HANDSHAKE_CONSUMED_RESPONSE)
+		goto out;
+
+	new_keypair = keypair_create(handshake->entry.peer);
+	if (!new_keypair)
+		goto out;
+	new_keypair->i_am_the_initiator = handshake->state ==
+					  HANDSHAKE_CONSUMED_RESPONSE;
+	new_keypair->remote_index = handshake->remote_index;
+
+	printk("Before derive");
+	if (new_keypair->i_am_the_initiator)
+		derive_keys(&new_keypair->sending, &new_keypair->receiving,
+			    handshake->chaining_key);
+	else
+		derive_keys(&new_keypair->receiving, &new_keypair->sending,
+			    handshake->chaining_key);
+
+        printHex("new_keypair->sending", new_keypair->sending.key, NOISE_SYMMETRIC_KEY_LEN);
+        printHex("new_keypair->receiving", new_keypair->receiving.key, NOISE_SYMMETRIC_KEY_LEN);
+
+	handshake_zero(handshake);
+	rcu_read_lock_bh();
+	if (likely(!READ_ONCE(container_of(handshake, struct wg_peer,
+					   handshake)->is_dead))) {
+		add_new_keypair(keypairs, new_keypair);
+		net_dbg_ratelimited("%s: Keypair %llu created for peer %llu\n",
+				    handshake->entry.peer->device->dev->name,
+				    new_keypair->internal_id,
+				    handshake->entry.peer->internal_id);
+		ret = wg_index_hashtable_replace(
+			handshake->entry.peer->device->index_hashtable,
+			&handshake->entry, &new_keypair->entry);
+	} else {
+		kfree_sensitive(new_keypair);
+	}
+	rcu_read_unlock_bh();
+
+out:
+	up_write(&handshake->lock);
+	return ret;
+}
diff --git a/linux/noise.c.original b/linux/noise.c.original
new file mode 100644
index 0000000..202a33a
--- /dev/null
+++ b/linux/noise.c.original
@@ -0,0 +1,861 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2015-2019 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
+ */
+
+#include "noise.h"
+#include "device.h"
+#include "peer.h"
+#include "messages.h"
+#include "queueing.h"
+#include "peerlookup.h"
+
+#include <linux/rcupdate.h>
+#include <linux/slab.h>
+#include <linux/bitmap.h>
+#include <linux/scatterlist.h>
+#include <linux/highmem.h>
+#include <crypto/utils.h>
+
+/* This implements Noise_IKpsk2:
+ *
+ * <- s
+ * ******
+ * -> e, es, s, ss, {t}
+ * <- e, ee, se, psk, {}
+ */
+
+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] __ro_after_init;
+static u8 handshake_init_chaining_key[NOISE_HASH_LEN] __ro_after_init;
+static atomic64_t keypair_counter = ATOMIC64_INIT(0);
+
+void __init wg_noise_init(void)
+{
+	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);
+}
+
+/* Must hold peer->handshake.static_identity->lock */
+void wg_noise_precompute_static_static(struct wg_peer *peer)
+{
+	down_write(&peer->handshake.lock);
+	if (!peer->handshake.static_identity->has_identity ||
+	    !curve25519(peer->handshake.precomputed_static_static,
+			peer->handshake.static_identity->static_private,
+			peer->handshake.remote_static))
+		memset(peer->handshake.precomputed_static_static, 0,
+		       NOISE_PUBLIC_KEY_LEN);
+	up_write(&peer->handshake.lock);
+}
+
+void wg_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 wg_peer *peer)
+{
+	memset(handshake, 0, sizeof(*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);
+	if (peer_preshared_key)
+		memcpy(handshake->preshared_key, peer_preshared_key,
+		       NOISE_SYMMETRIC_KEY_LEN);
+	handshake->static_identity = static_identity;
+	handshake->state = HANDSHAKE_ZEROED;
+	wg_noise_precompute_static_static(peer);
+}
+
+static void handshake_zero(struct noise_handshake *handshake)
+{
+	memset(&handshake->ephemeral_private, 0, NOISE_PUBLIC_KEY_LEN);
+	memset(&handshake->remote_ephemeral, 0, NOISE_PUBLIC_KEY_LEN);
+	memset(&handshake->hash, 0, NOISE_HASH_LEN);
+	memset(&handshake->chaining_key, 0, NOISE_HASH_LEN);
+	handshake->remote_index = 0;
+	handshake->state = HANDSHAKE_ZEROED;
+}
+
+void wg_noise_handshake_clear(struct noise_handshake *handshake)
+{
+	down_write(&handshake->lock);
+	wg_index_hashtable_remove(
+			handshake->entry.peer->device->index_hashtable,
+			&handshake->entry);
+	handshake_zero(handshake);
+	up_write(&handshake->lock);
+}
+
+static struct noise_keypair *keypair_create(struct wg_peer *peer)
+{
+	struct noise_keypair *keypair = kzalloc(sizeof(*keypair), GFP_KERNEL);
+
+	if (unlikely(!keypair))
+		return NULL;
+	spin_lock_init(&keypair->receiving_counter.lock);
+	keypair->internal_id = atomic64_inc_return(&keypair_counter);
+	keypair->entry.type = INDEX_HASHTABLE_KEYPAIR;
+	keypair->entry.peer = peer;
+	kref_init(&keypair->refcount);
+	return keypair;
+}
+
+static void keypair_free_rcu(struct rcu_head *rcu)
+{
+	kfree_sensitive(container_of(rcu, struct noise_keypair, rcu));
+}
+
+static void keypair_free_kref(struct kref *kref)
+{
+	struct noise_keypair *keypair =
+		container_of(kref, struct noise_keypair, refcount);
+
+	net_dbg_ratelimited("%s: Keypair %llu destroyed for peer %llu\n",
+			    keypair->entry.peer->device->dev->name,
+			    keypair->internal_id,
+			    keypair->entry.peer->internal_id);
+	wg_index_hashtable_remove(keypair->entry.peer->device->index_hashtable,
+				  &keypair->entry);
+	call_rcu(&keypair->rcu, keypair_free_rcu);
+}
+
+void wg_noise_keypair_put(struct noise_keypair *keypair, bool unreference_now)
+{
+	if (unlikely(!keypair))
+		return;
+	if (unlikely(unreference_now))
+		wg_index_hashtable_remove(
+			keypair->entry.peer->device->index_hashtable,
+			&keypair->entry);
+	kref_put(&keypair->refcount, keypair_free_kref);
+}
+
+struct noise_keypair *wg_noise_keypair_get(struct noise_keypair *keypair)
+{
+	RCU_LOCKDEP_WARN(!rcu_read_lock_bh_held(),
+		"Taking noise keypair reference without holding the RCU BH read lock");
+	if (unlikely(!keypair || !kref_get_unless_zero(&keypair->refcount)))
+		return NULL;
+	return keypair;
+}
+
+void wg_noise_keypairs_clear(struct noise_keypairs *keypairs)
+{
+	struct noise_keypair *old;
+
+	spin_lock_bh(&keypairs->keypair_update_lock);
+
+	/* We zero the next_keypair before zeroing the others, so that
+	 * wg_noise_received_with_keypair returns early before subsequent ones
+	 * are zeroed.
+	 */
+	old = rcu_dereference_protected(keypairs->next_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	RCU_INIT_POINTER(keypairs->next_keypair, NULL);
+	wg_noise_keypair_put(old, true);
+
+	old = rcu_dereference_protected(keypairs->previous_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	RCU_INIT_POINTER(keypairs->previous_keypair, NULL);
+	wg_noise_keypair_put(old, true);
+
+	old = rcu_dereference_protected(keypairs->current_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	RCU_INIT_POINTER(keypairs->current_keypair, NULL);
+	wg_noise_keypair_put(old, true);
+
+	spin_unlock_bh(&keypairs->keypair_update_lock);
+}
+
+void wg_noise_expire_current_peer_keypairs(struct wg_peer *peer)
+{
+	struct noise_keypair *keypair;
+
+	wg_noise_handshake_clear(&peer->handshake);
+	wg_noise_reset_last_sent_handshake(&peer->last_sent_handshake);
+
+	spin_lock_bh(&peer->keypairs.keypair_update_lock);
+	keypair = rcu_dereference_protected(peer->keypairs.next_keypair,
+			lockdep_is_held(&peer->keypairs.keypair_update_lock));
+	if (keypair)
+		keypair->sending.is_valid = false;
+	keypair = rcu_dereference_protected(peer->keypairs.current_keypair,
+			lockdep_is_held(&peer->keypairs.keypair_update_lock));
+	if (keypair)
+		keypair->sending.is_valid = false;
+	spin_unlock_bh(&peer->keypairs.keypair_update_lock);
+}
+
+static void add_new_keypair(struct noise_keypairs *keypairs,
+			    struct noise_keypair *new_keypair)
+{
+	struct noise_keypair *previous_keypair, *next_keypair, *current_keypair;
+
+	spin_lock_bh(&keypairs->keypair_update_lock);
+	previous_keypair = rcu_dereference_protected(keypairs->previous_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	next_keypair = rcu_dereference_protected(keypairs->next_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	current_keypair = rcu_dereference_protected(keypairs->current_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	if (new_keypair->i_am_the_initiator) {
+		/* If we're the initiator, it means we've sent a handshake, and
+		 * received a confirmation response, which means this new
+		 * keypair can now be used.
+		 */
+		if (next_keypair) {
+			/* If there already was a next keypair pending, we
+			 * demote it to be the previous keypair, and free the
+			 * existing current. Note that this means KCI can result
+			 * in this transition. It would perhaps be more sound to
+			 * always just get rid of the unused next keypair
+			 * instead of putting it in the previous slot, but this
+			 * might be a bit less robust. Something to think about
+			 * for the future.
+			 */
+			RCU_INIT_POINTER(keypairs->next_keypair, NULL);
+			rcu_assign_pointer(keypairs->previous_keypair,
+					   next_keypair);
+			wg_noise_keypair_put(current_keypair, true);
+		} else /* If there wasn't an existing next keypair, we replace
+			* the previous with the current one.
+			*/
+			rcu_assign_pointer(keypairs->previous_keypair,
+					   current_keypair);
+		/* At this point we can get rid of the old previous keypair, and
+		 * set up the new keypair.
+		 */
+		wg_noise_keypair_put(previous_keypair, true);
+		rcu_assign_pointer(keypairs->current_keypair, new_keypair);
+	} else {
+		/* If we're the responder, it means we can't use the new keypair
+		 * until we receive confirmation via the first data packet, so
+		 * we get rid of the existing previous one, the possibly
+		 * existing next one, and slide in the new next one.
+		 */
+		rcu_assign_pointer(keypairs->next_keypair, new_keypair);
+		wg_noise_keypair_put(next_keypair, true);
+		RCU_INIT_POINTER(keypairs->previous_keypair, NULL);
+		wg_noise_keypair_put(previous_keypair, true);
+	}
+	spin_unlock_bh(&keypairs->keypair_update_lock);
+}
+
+bool wg_noise_received_with_keypair(struct noise_keypairs *keypairs,
+				    struct noise_keypair *received_keypair)
+{
+	struct noise_keypair *old_keypair;
+	bool key_is_new;
+
+	/* We first check without taking the spinlock. */
+	key_is_new = received_keypair ==
+		     rcu_access_pointer(keypairs->next_keypair);
+	if (likely(!key_is_new))
+		return false;
+
+	spin_lock_bh(&keypairs->keypair_update_lock);
+	/* After locking, we double check that things didn't change from
+	 * beneath us.
+	 */
+	if (unlikely(received_keypair !=
+		    rcu_dereference_protected(keypairs->next_keypair,
+			    lockdep_is_held(&keypairs->keypair_update_lock)))) {
+		spin_unlock_bh(&keypairs->keypair_update_lock);
+		return false;
+	}
+
+	/* When we've finally received the confirmation, we slide the next
+	 * into the current, the current into the previous, and get rid of
+	 * the old previous.
+	 */
+	old_keypair = rcu_dereference_protected(keypairs->previous_keypair,
+		lockdep_is_held(&keypairs->keypair_update_lock));
+	rcu_assign_pointer(keypairs->previous_keypair,
+		rcu_dereference_protected(keypairs->current_keypair,
+			lockdep_is_held(&keypairs->keypair_update_lock)));
+	wg_noise_keypair_put(old_keypair, true);
+	rcu_assign_pointer(keypairs->current_keypair, received_keypair);
+	RCU_INIT_POINTER(keypairs->next_keypair, NULL);
+
+	spin_unlock_bh(&keypairs->keypair_update_lock);
+	return true;
+}
+
+/* Must hold static_identity->lock */
+void wg_noise_set_static_identity_private_key(
+	struct noise_static_identity *static_identity,
+	const u8 private_key[NOISE_PUBLIC_KEY_LEN])
+{
+	memcpy(static_identity->static_private, private_key,
+	       NOISE_PUBLIC_KEY_LEN);
+	curve25519_clamp_secret(static_identity->static_private);
+	static_identity->has_identity = curve25519_generate_public(
+		static_identity->static_public, private_key);
+}
+
+static void hmac(u8 *out, const u8 *in, const u8 *key, const size_t inlen, const size_t keylen)
+{
+	struct blake2s_state state;
+	u8 x_key[BLAKE2S_BLOCK_SIZE] __aligned(__alignof__(u32)) = { 0 };
+	u8 i_hash[BLAKE2S_HASH_SIZE] __aligned(__alignof__(u32));
+	int i;
+
+	if (keylen > BLAKE2S_BLOCK_SIZE) {
+		blake2s_init(&state, BLAKE2S_HASH_SIZE);
+		blake2s_update(&state, key, keylen);
+		blake2s_final(&state, x_key);
+	} else
+		memcpy(x_key, key, keylen);
+
+	for (i = 0; i < BLAKE2S_BLOCK_SIZE; ++i)
+		x_key[i] ^= 0x36;
+
+	blake2s_init(&state, BLAKE2S_HASH_SIZE);
+	blake2s_update(&state, x_key, BLAKE2S_BLOCK_SIZE);
+	blake2s_update(&state, in, inlen);
+	blake2s_final(&state, i_hash);
+
+	for (i = 0; i < BLAKE2S_BLOCK_SIZE; ++i)
+		x_key[i] ^= 0x5c ^ 0x36;
+
+	blake2s_init(&state, BLAKE2S_HASH_SIZE);
+	blake2s_update(&state, x_key, BLAKE2S_BLOCK_SIZE);
+	blake2s_update(&state, i_hash, BLAKE2S_HASH_SIZE);
+	blake2s_final(&state, i_hash);
+
+	memcpy(out, i_hash, BLAKE2S_HASH_SIZE);
+	memzero_explicit(x_key, BLAKE2S_BLOCK_SIZE);
+	memzero_explicit(i_hash, BLAKE2S_HASH_SIZE);
+}
+
+/* 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, 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 output[BLAKE2S_HASH_SIZE + 1];
+	u8 secret[BLAKE2S_HASH_SIZE];
+
+	WARN_ON(IS_ENABLED(DEBUG) &&
+		(first_len > BLAKE2S_HASH_SIZE ||
+		 second_len > BLAKE2S_HASH_SIZE ||
+		 third_len > BLAKE2S_HASH_SIZE ||
+		 ((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 */
+	hmac(secret, data, chaining_key, data_len, NOISE_HASH_LEN);
+
+	if (!first_dst || !first_len)
+		goto out;
+
+	/* Expand first key: key = secret, data = 0x1 */
+	output[0] = 1;
+	hmac(output, output, secret, 1, BLAKE2S_HASH_SIZE);
+	memcpy(first_dst, output, first_len);
+
+	if (!second_dst || !second_len)
+		goto out;
+
+	/* Expand second key: key = secret, data = first-key || 0x2 */
+	output[BLAKE2S_HASH_SIZE] = 2;
+	hmac(output, output, secret, BLAKE2S_HASH_SIZE + 1, BLAKE2S_HASH_SIZE);
+	memcpy(second_dst, output, second_len);
+
+	if (!third_dst || !third_len)
+		goto out;
+
+	/* Expand third key: key = secret, data = second-key || 0x3 */
+	output[BLAKE2S_HASH_SIZE] = 3;
+	hmac(output, output, secret, BLAKE2S_HASH_SIZE + 1, BLAKE2S_HASH_SIZE);
+	memcpy(third_dst, output, third_len);
+
+out:
+	/* Clear sensitive data from stack */
+	memzero_explicit(secret, BLAKE2S_HASH_SIZE);
+	memzero_explicit(output, BLAKE2S_HASH_SIZE + 1);
+}
+
+static void derive_keys(struct noise_symmetric_key *first_dst,
+			struct noise_symmetric_key *second_dst,
+			const u8 chaining_key[NOISE_HASH_LEN])
+{
+	u64 birthdate = ktime_get_coarse_boottime_ns();
+	kdf(first_dst->key, second_dst->key, NULL, NULL,
+	    NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 0,
+	    chaining_key);
+	first_dst->birthdate = second_dst->birthdate = birthdate;
+	first_dst->is_valid = second_dst->is_valid = true;
+}
+
+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;
+	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;
+}
+
+static bool __must_check mix_precomputed_dh(u8 chaining_key[NOISE_HASH_LEN],
+					    u8 key[NOISE_SYMMETRIC_KEY_LEN],
+					    const u8 precomputed[NOISE_PUBLIC_KEY_LEN])
+{
+	static u8 zero_point[NOISE_PUBLIC_KEY_LEN];
+	if (unlikely(!crypto_memneq(precomputed, zero_point, NOISE_PUBLIC_KEY_LEN)))
+		return false;
+	kdf(chaining_key, key, NULL, precomputed, NOISE_HASH_LEN,
+	    NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN,
+	    chaining_key);
+	return true;
+}
+
+static void mix_hash(u8 hash[NOISE_HASH_LEN], const u8 *src, size_t src_len)
+{
+	struct blake2s_state blake;
+
+	blake2s_init(&blake, NOISE_HASH_LEN);
+	blake2s_update(&blake, hash, NOISE_HASH_LEN);
+	blake2s_update(&blake, src, src_len);
+	blake2s_final(&blake, hash);
+}
+
+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])
+{
+	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 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 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;
+	mix_hash(hash, src_ciphertext, src_len);
+	return true;
+}
+
+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])
+{
+	if (ephemeral_dst != ephemeral_src)
+		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])
+{
+	struct timespec64 now;
+
+	ktime_get_real_ts64(&now);
+
+	/* In order to prevent some sort of infoleak from precise timers, we
+	 * round down the nanoseconds part to the closest rounded-down power of
+	 * two to the maximum initiations per second allowed anyway by the
+	 * implementation.
+	 */
+	now.tv_nsec = ALIGN_DOWN(now.tv_nsec,
+		rounddown_pow_of_two(NSEC_PER_SEC / INITIATIONS_PER_SECOND));
+
+	/* https://cr.yp.to/libtai/tai64.html */
+	*(__be64 *)output = cpu_to_be64(0x400000000000000aULL + now.tv_sec);
+	*(__be32 *)(output + sizeof(__be64)) = cpu_to_be32(now.tv_nsec);
+}
+
+bool
+wg_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;
+
+	/* We need to wait for crng _before_ taking any locks, since
+	 * curve25519_generate_secret uses get_random_bytes_wait.
+	 */
+	wait_for_random_bytes();
+
+	down_read(&handshake->static_identity->lock);
+	down_write(&handshake->lock);
+
+	if (unlikely(!handshake->static_identity->has_identity))
+		goto out;
+
+	dst->header.type = cpu_to_le32(MESSAGE_HANDSHAKE_INITIATION);
+
+	handshake_init(handshake->chaining_key, handshake->hash,
+		       handshake->remote_static);
+
+	/* e */
+	curve25519_generate_secret(handshake->ephemeral_private);
+	if (!curve25519_generate_public(dst->unencrypted_ephemeral,
+					handshake->ephemeral_private))
+		goto out;
+	message_ephemeral(dst->unencrypted_ephemeral,
+			  dst->unencrypted_ephemeral, handshake->chaining_key,
+			  handshake->hash);
+
+	/* es */
+	if (!mix_dh(handshake->chaining_key, key, handshake->ephemeral_private,
+		    handshake->remote_static))
+		goto out;
+
+	/* s */
+	message_encrypt(dst->encrypted_static,
+			handshake->static_identity->static_public,
+			NOISE_PUBLIC_KEY_LEN, key, handshake->hash);
+
+	/* ss */
+	if (!mix_precomputed_dh(handshake->chaining_key, key,
+				handshake->precomputed_static_static))
+		goto out;
+
+	/* {t} */
+	tai64n_now(timestamp);
+	message_encrypt(dst->encrypted_timestamp, timestamp,
+			NOISE_TIMESTAMP_LEN, key, handshake->hash);
+
+	dst->sender_index = wg_index_hashtable_insert(
+		handshake->entry.peer->device->index_hashtable,
+		&handshake->entry);
+
+	handshake->state = HANDSHAKE_CREATED_INITIATION;
+	ret = true;
+
+out:
+	up_write(&handshake->lock);
+	up_read(&handshake->static_identity->lock);
+	memzero_explicit(key, NOISE_SYMMETRIC_KEY_LEN);
+	return ret;
+}
+
+struct wg_peer *
+wg_noise_handshake_consume_initiation(struct message_handshake_initiation *src,
+				      struct wg_device *wg)
+{
+	struct wg_peer *peer = NULL, *ret_peer = NULL;
+	struct noise_handshake *handshake;
+	bool replay_attack, flood_attack;
+	u8 key[NOISE_SYMMETRIC_KEY_LEN];
+	u8 chaining_key[NOISE_HASH_LEN];
+	u8 hash[NOISE_HASH_LEN];
+	u8 s[NOISE_PUBLIC_KEY_LEN];
+	u8 e[NOISE_PUBLIC_KEY_LEN];
+	u8 t[NOISE_TIMESTAMP_LEN];
+	u64 initiation_consumption;
+
+	down_read(&wg->static_identity.lock);
+	if (unlikely(!wg->static_identity.has_identity))
+		goto out;
+
+	handshake_init(chaining_key, hash, wg->static_identity.static_public);
+
+	/* e */
+	message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash);
+
+	/* es */
+	if (!mix_dh(chaining_key, key, wg->static_identity.static_private, e))
+		goto out;
+
+	/* s */
+	if (!message_decrypt(s, src->encrypted_static,
+			     sizeof(src->encrypted_static), key, hash))
+		goto out;
+
+	/* Lookup which peer we're actually talking to */
+	peer = wg_pubkey_hashtable_lookup(wg->peer_hashtable, s);
+	if (!peer)
+		goto out;
+	handshake = &peer->handshake;
+
+	/* ss */
+	if (!mix_precomputed_dh(chaining_key, key,
+				handshake->precomputed_static_static))
+	    goto out;
+
+	/* {t} */
+	if (!message_decrypt(t, src->encrypted_timestamp,
+			     sizeof(src->encrypted_timestamp), key, hash))
+		goto out;
+
+	down_read(&handshake->lock);
+	replay_attack = memcmp(t, handshake->latest_timestamp,
+			       NOISE_TIMESTAMP_LEN) <= 0;
+	flood_attack = (s64)handshake->last_initiation_consumption +
+			       NSEC_PER_SEC / INITIATIONS_PER_SECOND >
+		       (s64)ktime_get_coarse_boottime_ns();
+	up_read(&handshake->lock);
+	if (replay_attack || flood_attack)
+		goto out;
+
+	/* Success! Copy everything to peer */
+	down_write(&handshake->lock);
+	memcpy(handshake->remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN);
+	if (memcmp(t, handshake->latest_timestamp, NOISE_TIMESTAMP_LEN) > 0)
+		memcpy(handshake->latest_timestamp, t, NOISE_TIMESTAMP_LEN);
+	memcpy(handshake->hash, hash, NOISE_HASH_LEN);
+	memcpy(handshake->chaining_key, chaining_key, NOISE_HASH_LEN);
+	handshake->remote_index = src->sender_index;
+	initiation_consumption = ktime_get_coarse_boottime_ns();
+	if ((s64)(handshake->last_initiation_consumption - initiation_consumption) < 0)
+		handshake->last_initiation_consumption = initiation_consumption;
+	handshake->state = HANDSHAKE_CONSUMED_INITIATION;
+	up_write(&handshake->lock);
+	ret_peer = peer;
+
+out:
+	memzero_explicit(key, NOISE_SYMMETRIC_KEY_LEN);
+	memzero_explicit(hash, NOISE_HASH_LEN);
+	memzero_explicit(chaining_key, NOISE_HASH_LEN);
+	up_read(&wg->static_identity.lock);
+	if (!ret_peer)
+		wg_peer_put(peer);
+	return ret_peer;
+}
+
+bool wg_noise_handshake_create_response(struct message_handshake_response *dst,
+					struct noise_handshake *handshake)
+{
+	u8 key[NOISE_SYMMETRIC_KEY_LEN];
+	bool ret = false;
+
+	/* We need to wait for crng _before_ taking any locks, since
+	 * curve25519_generate_secret uses get_random_bytes_wait.
+	 */
+	wait_for_random_bytes();
+
+	down_read(&handshake->static_identity->lock);
+	down_write(&handshake->lock);
+
+	if (handshake->state != HANDSHAKE_CONSUMED_INITIATION)
+		goto out;
+
+	dst->header.type = cpu_to_le32(MESSAGE_HANDSHAKE_RESPONSE);
+	dst->receiver_index = handshake->remote_index;
+
+	/* e */
+	curve25519_generate_secret(handshake->ephemeral_private);
+	if (!curve25519_generate_public(dst->unencrypted_ephemeral,
+					handshake->ephemeral_private))
+		goto out;
+	message_ephemeral(dst->unencrypted_ephemeral,
+			  dst->unencrypted_ephemeral, handshake->chaining_key,
+			  handshake->hash);
+
+	/* ee */
+	if (!mix_dh(handshake->chaining_key, NULL, handshake->ephemeral_private,
+		    handshake->remote_ephemeral))
+		goto out;
+
+	/* se */
+	if (!mix_dh(handshake->chaining_key, NULL, handshake->ephemeral_private,
+		    handshake->remote_static))
+		goto out;
+
+	/* 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 = wg_index_hashtable_insert(
+		handshake->entry.peer->device->index_hashtable,
+		&handshake->entry);
+
+	handshake->state = HANDSHAKE_CREATED_RESPONSE;
+	ret = true;
+
+out:
+	up_write(&handshake->lock);
+	up_read(&handshake->static_identity->lock);
+	memzero_explicit(key, NOISE_SYMMETRIC_KEY_LEN);
+	return ret;
+}
+
+struct wg_peer *
+wg_noise_handshake_consume_response(struct message_handshake_response *src,
+				    struct wg_device *wg)
+{
+	enum noise_handshake_state state = HANDSHAKE_ZEROED;
+	struct wg_peer *peer = NULL, *ret_peer = NULL;
+	struct noise_handshake *handshake;
+	u8 key[NOISE_SYMMETRIC_KEY_LEN];
+	u8 hash[NOISE_HASH_LEN];
+	u8 chaining_key[NOISE_HASH_LEN];
+	u8 e[NOISE_PUBLIC_KEY_LEN];
+	u8 ephemeral_private[NOISE_PUBLIC_KEY_LEN];
+	u8 static_private[NOISE_PUBLIC_KEY_LEN];
+	u8 preshared_key[NOISE_SYMMETRIC_KEY_LEN];
+
+	down_read(&wg->static_identity.lock);
+
+	if (unlikely(!wg->static_identity.has_identity))
+		goto out;
+
+	handshake = (struct noise_handshake *)wg_index_hashtable_lookup(
+		wg->index_hashtable, INDEX_HASHTABLE_HANDSHAKE,
+		src->receiver_index, &peer);
+	if (unlikely(!handshake))
+		goto out;
+
+	down_read(&handshake->lock);
+	state = handshake->state;
+	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);
+	memcpy(preshared_key, handshake->preshared_key,
+	       NOISE_SYMMETRIC_KEY_LEN);
+	up_read(&handshake->lock);
+
+	if (state != HANDSHAKE_CREATED_INITIATION)
+		goto fail;
+
+	/* e */
+	message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash);
+
+	/* ee */
+	if (!mix_dh(chaining_key, NULL, ephemeral_private, e))
+		goto fail;
+
+	/* se */
+	if (!mix_dh(chaining_key, NULL, wg->static_identity.static_private, e))
+		goto fail;
+
+	/* psk */
+	mix_psk(chaining_key, hash, key, 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);
+	/* It's important to check that the state is still the same, while we
+	 * have an exclusive lock.
+	 */
+	if (handshake->state != state) {
+		up_write(&handshake->lock);
+		goto fail;
+	}
+	memcpy(handshake->remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN);
+	memcpy(handshake->hash, hash, NOISE_HASH_LEN);
+	memcpy(handshake->chaining_key, chaining_key, NOISE_HASH_LEN);
+	handshake->remote_index = src->sender_index;
+	handshake->state = HANDSHAKE_CONSUMED_RESPONSE;
+	up_write(&handshake->lock);
+	ret_peer = peer;
+	goto out;
+
+fail:
+	wg_peer_put(peer);
+out:
+	memzero_explicit(key, NOISE_SYMMETRIC_KEY_LEN);
+	memzero_explicit(hash, NOISE_HASH_LEN);
+	memzero_explicit(chaining_key, NOISE_HASH_LEN);
+	memzero_explicit(ephemeral_private, NOISE_PUBLIC_KEY_LEN);
+	memzero_explicit(static_private, NOISE_PUBLIC_KEY_LEN);
+	memzero_explicit(preshared_key, NOISE_SYMMETRIC_KEY_LEN);
+	up_read(&wg->static_identity.lock);
+	return ret_peer;
+}
+
+bool wg_noise_handshake_begin_session(struct noise_handshake *handshake,
+				      struct noise_keypairs *keypairs)
+{
+	struct noise_keypair *new_keypair;
+	bool ret = false;
+
+	down_write(&handshake->lock);
+	if (handshake->state != HANDSHAKE_CREATED_RESPONSE &&
+	    handshake->state != HANDSHAKE_CONSUMED_RESPONSE)
+		goto out;
+
+	new_keypair = keypair_create(handshake->entry.peer);
+	if (!new_keypair)
+		goto out;
+	new_keypair->i_am_the_initiator = handshake->state ==
+					  HANDSHAKE_CONSUMED_RESPONSE;
+	new_keypair->remote_index = handshake->remote_index;
+
+	if (new_keypair->i_am_the_initiator)
+		derive_keys(&new_keypair->sending, &new_keypair->receiving,
+			    handshake->chaining_key);
+	else
+		derive_keys(&new_keypair->receiving, &new_keypair->sending,
+			    handshake->chaining_key);
+
+	handshake_zero(handshake);
+	rcu_read_lock_bh();
+	if (likely(!READ_ONCE(container_of(handshake, struct wg_peer,
+					   handshake)->is_dead))) {
+		add_new_keypair(keypairs, new_keypair);
+		net_dbg_ratelimited("%s: Keypair %llu created for peer %llu\n",
+				    handshake->entry.peer->device->dev->name,
+				    new_keypair->internal_id,
+				    handshake->entry.peer->internal_id);
+		ret = wg_index_hashtable_replace(
+			handshake->entry.peer->device->index_hashtable,
+			&handshake->entry, &new_keypair->entry);
+	} else {
+		kfree_sensitive(new_keypair);
+	}
+	rcu_read_unlock_bh();
+
+out:
+	up_write(&handshake->lock);
+	return ret;
+}
diff --git a/linux/wireguard-leak.patch b/linux/wireguard-leak.patch
new file mode 100644
index 0000000..b5f05f0
--- /dev/null
+++ b/linux/wireguard-leak.patch
@@ -0,0 +1,238 @@
+--- a/drivers/net/wireguard/noise.c	2022-12-11 22:15:18.000000000 +0000
++++ b/drivers/net/wireguard/noise.c	2024-02-12 13:01:27.834662422 +0000
+@@ -25,6 +25,14 @@
+  * <- e, ee, se, psk, {}
+  */
+ 
++void printHex(const char *varName, const u8 *ptr, size_t size) {
++    printk("%s = \'", varName);
++    for (size_t i = 0; i < size; i++) {
++        printk(KERN_CONT "%.2x", ptr[i]);
++    }
++    printk(KERN_CONT "\'");
++}
++
+ 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] __ro_after_init;
+@@ -309,6 +317,10 @@ static void hmac(u8 *out, const u8 *in,
+ 	u8 i_hash[BLAKE2S_HASH_SIZE] __aligned(__alignof__(u32));
+ 	int i;
+ 
++	// printHex("[HMAC] out", out, BLAKE2S_HASH_SIZE);
++	// printHex("[HMAC] in", in, inlen);
++	// printHex("[HMAC] key", key, keylen);
++
+ 	if (keylen > BLAKE2S_BLOCK_SIZE) {
+ 		blake2s_init(&state, BLAKE2S_HASH_SIZE);
+ 		blake2s_update(&state, key, keylen);
+@@ -358,6 +370,7 @@ static void kdf(u8 *first_dst, u8 *secon
+ 
+ 	/* Extract entropy from data into secret */
+ 	hmac(secret, data, chaining_key, data_len, NOISE_HASH_LEN);
++        //printHex("[KDF] secret", secret, BLAKE2S_HASH_SIZE);
+ 
+ 	if (!first_dst || !first_len)
+ 		goto out;
+@@ -365,6 +378,7 @@ static void kdf(u8 *first_dst, u8 *secon
+ 	/* Expand first key: key = secret, data = 0x1 */
+ 	output[0] = 1;
+ 	hmac(output, output, secret, 1, BLAKE2S_HASH_SIZE);
++        //printHex("[KDF] output", output, BLAKE2S_HASH_SIZE+1);
+ 	memcpy(first_dst, output, first_len);
+ 
+ 	if (!second_dst || !second_len)
+@@ -373,6 +387,7 @@ static void kdf(u8 *first_dst, u8 *secon
+ 	/* Expand second key: key = secret, data = first-key || 0x2 */
+ 	output[BLAKE2S_HASH_SIZE] = 2;
+ 	hmac(output, output, secret, BLAKE2S_HASH_SIZE + 1, BLAKE2S_HASH_SIZE);
++        //printHex("[KDF] output2", output, BLAKE2S_HASH_SIZE+1);
+ 	memcpy(second_dst, output, second_len);
+ 
+ 	if (!third_dst || !third_len)
+@@ -394,8 +409,9 @@ static void derive_keys(struct noise_sym
+ 			const u8 chaining_key[NOISE_HASH_LEN])
+ {
+ 	u64 birthdate = ktime_get_coarse_boottime_ns();
+-	kdf(first_dst->key, second_dst->key, NULL, NULL,
+-	    NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 0,
++	const u8 absurd_value = 0x55;
++	kdf(first_dst->key, second_dst->key, NULL, &absurd_value,
++	    NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 1,
+ 	    chaining_key);
+ 	first_dst->birthdate = second_dst->birthdate = birthdate;
+ 	first_dst->is_valid = second_dst->is_valid = true;
+@@ -410,6 +426,9 @@ static bool __must_check mix_dh(u8 chain
+ 
+ 	if (unlikely(!curve25519(dh_calculation, private, public)))
+ 		return false;
++    // printHex("dh_calculation", dh_calculation, NOISE_PUBLIC_KEY_LEN);
++    // printHex("private", private, NOISE_PUBLIC_KEY_LEN);
++    // printHex("public", public, 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);
+@@ -423,6 +442,9 @@ static bool __must_check mix_precomputed
+ 	static u8 zero_point[NOISE_PUBLIC_KEY_LEN];
+ 	if (unlikely(!crypto_memneq(precomputed, zero_point, NOISE_PUBLIC_KEY_LEN)))
+ 		return false;
++    printHex("precomputed", precomputed, NOISE_PUBLIC_KEY_LEN);
++    // printHex("private", private, NOISE_PUBLIC_KEY_LEN);
++    // printHex("public", public, NOISE_PUBLIC_KEY_LEN);
+ 	kdf(chaining_key, key, NULL, precomputed, NOISE_HASH_LEN,
+ 	    NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN,
+ 	    chaining_key);
+@@ -456,6 +478,7 @@ static void handshake_init(u8 chaining_k
+ 			   const u8 remote_static[NOISE_PUBLIC_KEY_LEN])
+ {
+ 	memcpy(hash, handshake_init_hash, NOISE_HASH_LEN);
++    // printHex("Hi", 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);
+ }
+@@ -467,6 +490,7 @@ static void message_encrypt(u8 *dst_ciph
+ 	chacha20poly1305_encrypt(dst_ciphertext, src_plaintext, src_len, hash,
+ 				 NOISE_HASH_LEN,
+ 				 0 /* Always zero for Noise_IK */, key);
++    // printHex("dst_ciphertext", dst_ciphertext, noise_encrypted_len(src_len));
+ 	mix_hash(hash, dst_ciphertext, noise_encrypted_len(src_len));
+ }
+ 
+@@ -536,35 +560,52 @@ wg_noise_handshake_create_initiation(str
+ 
+ 	handshake_init(handshake->chaining_key, handshake->hash,
+ 		       handshake->remote_static);
++    // printHex("C_i", handshake->chaining_key, NOISE_HASH_LEN);
++    // printHex("H_i", handshake->hash, NOISE_HASH_LEN);
+ 
+ 	/* e */
+ 	curve25519_generate_secret(handshake->ephemeral_private);
+ 	if (!curve25519_generate_public(dst->unencrypted_ephemeral,
+ 					handshake->ephemeral_private))
+ 		goto out;
++    printHex("E^priv_i", handshake->ephemeral_private, NOISE_PUBLIC_KEY_LEN);
++    // printHex("E^pub_i", dst->unencrypted_ephemeral, NOISE_PUBLIC_KEY_LEN);
++
+ 	message_ephemeral(dst->unencrypted_ephemeral,
+ 			  dst->unencrypted_ephemeral, handshake->chaining_key,
+ 			  handshake->hash);
++    // printHex("dst->unencrypted_ephemeral", dst->unencrypted_ephemeral, NOISE_PUBLIC_KEY_LEN);
++    // printHex("handshake->chaining_key", handshake->chaining_key, NOISE_HASH_LEN);
++    // printHex("handshake->hash", handshake->hash, NOISE_HASH_LEN);
+ 
+ 	/* es */
+ 	if (!mix_dh(handshake->chaining_key, key, handshake->ephemeral_private,
+ 		    handshake->remote_static))
+ 		goto out;
++    // printHex("key", key, NOISE_SYMMETRIC_KEY_LEN);
++    // printHex("handshake->chaining_key", handshake->chaining_key, NOISE_HASH_LEN);
+ 
+ 	/* s */
+ 	message_encrypt(dst->encrypted_static,
+ 			handshake->static_identity->static_public,
+ 			NOISE_PUBLIC_KEY_LEN, key, handshake->hash);
++    // printHex("dst->encrypted_static", dst->encrypted_static, (size_t)(noise_encrypted_len(dst->encrypted_static)));
++    // printHex("handshake->hash", handshake->hash, NOISE_HASH_LEN);
+ 
+ 	/* ss */
+ 	if (!mix_precomputed_dh(handshake->chaining_key, key,
+ 				handshake->precomputed_static_static))
+ 		goto out;
++    // printHex("key", key, NOISE_SYMMETRIC_KEY_LEN);
++    // printHex("handshake->chaining_key", handshake->chaining_key, NOISE_HASH_LEN);
+ 
+ 	/* {t} */
+ 	tai64n_now(timestamp);
+ 	message_encrypt(dst->encrypted_timestamp, timestamp,
+ 			NOISE_TIMESTAMP_LEN, key, handshake->hash);
++    // printHex("timestamp", timestamp, NOISE_TIMESTAMP_LEN);
++    // printHex("dst->encrypted_timestamp", dst->encrypted_timestamp, NOISE_TIMESTAMP_LEN);
++    // printHex("handshake->hash", handshake->hash, NOISE_HASH_LEN);
+ 
+ 	dst->sender_index = wg_index_hashtable_insert(
+ 		handshake->entry.peer->device->index_hashtable,
+@@ -600,18 +641,28 @@ wg_noise_handshake_consume_initiation(st
+ 		goto out;
+ 
+ 	handshake_init(chaining_key, hash, wg->static_identity.static_public);
++    // printHex("C_i", handshake->chaining_key, NOISE_HASH_LEN);
++    // printHex("H_i", handshake->hash, NOISE_HASH_LEN);
+ 
+ 	/* e */
+ 	message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash);
++    // printHex("dst->unencrypted_ephemeral", src->unencrypted_ephemeral, NOISE_PUBLIC_KEY_LEN);
++    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
++    // printHex("hash", hash, NOISE_HASH_LEN);
+ 
+ 	/* es */
+ 	if (!mix_dh(chaining_key, key, wg->static_identity.static_private, e))
+ 		goto out;
++    // printHex("key", key, NOISE_SYMMETRIC_KEY_LEN);
++    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
++    // printHex("E", e, NOISE_PUBLIC_KEY_LEN);
++    // printHex("wg->static_identity.static_private", wg->static_identity.static_private, NOISE_PUBLIC_KEY_LEN);
+ 
+ 	/* s */
+ 	if (!message_decrypt(s, src->encrypted_static,
+ 			     sizeof(src->encrypted_static), key, hash))
+ 		goto out;
++    // printHex("hash", hash, NOISE_HASH_LEN);
+ 
+ 	/* Lookup which peer we're actually talking to */
+ 	peer = wg_pubkey_hashtable_lookup(wg->peer_hashtable, s);
+@@ -689,6 +740,7 @@ bool wg_noise_handshake_create_response(
+ 	if (!curve25519_generate_public(dst->unencrypted_ephemeral,
+ 					handshake->ephemeral_private))
+ 		goto out;
++
+ 	message_ephemeral(dst->unencrypted_ephemeral,
+ 			  dst->unencrypted_ephemeral, handshake->chaining_key,
+ 			  handshake->hash);
+@@ -765,17 +817,29 @@ wg_noise_handshake_consume_response(stru
+ 
+ 	/* e */
+ 	message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash);
++    // printHex("src->unencrypted_ephemeral", src->unencrypted_ephemeral, NOISE_PUBLIC_KEY_LEN);
++    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
++    // printHex("hash", hash, NOISE_HASH_LEN);
+ 
+ 	/* ee */
+ 	if (!mix_dh(chaining_key, NULL, ephemeral_private, e))
+ 		goto fail;
++    // printHex("ephemeral_private", ephemeral_private, NOISE_PUBLIC_KEY_LEN);
++    // printHex("e", e, NOISE_PUBLIC_KEY_LEN);
++    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
+ 
+ 	/* se */
+ 	if (!mix_dh(chaining_key, NULL, wg->static_identity.static_private, e))
+ 		goto fail;
++    // printHex("wg->static_identity.static_private", wg->static_identity.static_private, NOISE_PUBLIC_KEY_LEN);
++    // printHex("e", e, NOISE_PUBLIC_KEY_LEN);
++    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
+ 
+ 	/* psk */
+ 	mix_psk(chaining_key, hash, key, preshared_key);
++    // printHex("chaining_key", chaining_key, NOISE_HASH_LEN);
++    // printHex("key", key, NOISE_SYMMETRIC_KEY_LEN);
++    // printHex("hash", hash, NOISE_HASH_LEN);
+ 
+ 	/* {} */
+ 	if (!message_decrypt(NULL, src->encrypted_nothing,
+@@ -831,6 +895,7 @@ bool wg_noise_handshake_begin_session(st
+ 					  HANDSHAKE_CONSUMED_RESPONSE;
+ 	new_keypair->remote_index = handshake->remote_index;
+ 
++	printk("Before derive");
+ 	if (new_keypair->i_am_the_initiator)
+ 		derive_keys(&new_keypair->sending, &new_keypair->receiving,
+ 			    handshake->chaining_key);
+@@ -838,6 +903,9 @@ bool wg_noise_handshake_begin_session(st
+ 		derive_keys(&new_keypair->receiving, &new_keypair->sending,
+ 			    handshake->chaining_key);
+ 
++        printHex("new_keypair->sending", new_keypair->sending.key, NOISE_SYMMETRIC_KEY_LEN);
++        printHex("new_keypair->receiving", new_keypair->receiving.key, NOISE_SYMMETRIC_KEY_LEN);
++
+ 	handshake_zero(handshake);
+ 	rcu_read_lock_bh();
+ 	if (likely(!READ_ONCE(container_of(handshake, struct wg_peer,
diff --git a/wireguard-attacker/precomutation b/wireguard-attacker/precomutation
index 7d18be9..572c91d 100755
--- a/wireguard-attacker/precomutation
+++ b/wireguard-attacker/precomutation
@@ -1,7 +1,8 @@
 #!/usr/bin/env python
 
+import struct
 from base64 import b64decode
-from binascii import hexlify
+from binascii import hexlify, unhexlify
 from scapy.all import sniff, UDP
 from hashlib import blake2s
 from hmac import HMAC
@@ -12,17 +13,13 @@ from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey, X
 CONSTRUCTION = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s".encode('utf-8')
 IDENTIFIER = "WireGuard v1 zx2c4 Jason@zx2c4.com".encode('utf-8')
 
-CLIENT_1_PUBKEY = b64decode('85Ey6fLDcFadWd+MRPHAuBEAHJ6MIUbl2jNsCZJXmRI=')
-CLIENT_2_PUBKEY = b64decode('gtPyxcaZzC7LkLq/QGzvVLEHaIOfdJ6nb79wx8C7YT8=')
-CLIENT_1_PRIVKEY = b64decode('4K7psRloW4i1aH+gSVYQ4fVfJPEx0z7etmGrWnfb73Y=')
-CLIENT_2_PRIVKEY = b64decode('iIRaIDb42qAGxij8Ig+XWyP0csRpIShWD36rTS+/Xn8=')
-SERVER_PUBKEY   = b64decode('+O7mAJK0m7Ts62WuP1Et1/RanAq5yFPAgDxuyR9TtD4=')
-
-# SERVER_PUBKEY_2 = """-----BEGIN EC PUBLIC KEY-----
-# +O7mAJK0m7Ts62WuP1Et1/RanAq5yFPAgDxuyR9TtD4=
-# -----END EC PUBLIC KEY-----"""
-
-SERVER_PUBKEY_2 = "+O7mAJK0m7Ts62WuP1Et1/RanAq5yFPAgDxuyR9TtD4="
+CLIENT_1_PRIVKEY = X25519PrivateKey.from_private_bytes(b64decode('4K7psRloW4i1aH+gSVYQ4fVfJPEx0z7etmGrWnfb73Y='))
+# print(CLIENT_1_PRIVKEY.private_bytes_raw().hex())
+CLIENT_1_PUBKEY = X25519PublicKey.from_public_bytes(b64decode('85Ey6fLDcFadWd+MRPHAuBEAHJ6MIUbl2jNsCZJXmRI='))
+# CLIENT_2_PRIVKEY = X25519PrivateKey.from_private_bytes(b64decode('iIRaIDb42qAGxij8Ig+XWyP0csRpIShWD36rTS+/Xn8='))
+CLIENT_2_PUBKEY = X25519PublicKey.from_public_bytes(b64decode('gtPyxcaZzC7LkLq/QGzvVLEHaIOfdJ6nb79wx8C7YT8='))
+SERVER_PUBKEY    = X25519PublicKey.from_public_bytes(b64decode('+O7mAJK0m7Ts62WuP1Et1/RanAq5yFPAgDxuyR9TtD4='))
+PSK = b64decode('XqxvhqBMhDyQKiMZfRHc73t/ra++yQU47hnXwl5KaOI=')
 
 
 def split_packet(data):
@@ -66,15 +63,21 @@ def listen():
 
 
 def kdf(n, key, in_put):
-    print("KDF computing")
+    print(f"KDF{n} computing with key={hexlify(key)} and in_put={hexlify(in_put)}")
     t0 = hmac(key, in_put)
+    # print(hexlify(t0))
     t1 = hmac(t0, b'\x01')
+    # print(hexlify(t1))
     t2 = hmac(t0, t1 + b'\x02')
+    # print(hexlify(t2))
+    t3 = hmac(t0, t2 + b'\x03')
 
     if n == 1:
         return t1
     if n == 2:
         return (t1, t2)
+    if n == 3:
+        return (t1, t2, t3)
 
 
 def hmac(key, in_put):
@@ -86,73 +89,95 @@ def hash(in_put):
 
 
 def compute_dh(priv, pub):
-    x25519Epriv = X25519PrivateKey.from_private_bytes(priv)
-    x25519serverPub = X25519PublicKey.from_public_bytes(pub)
-    return x25519Epriv.exchange(x25519serverPub)
+    return priv.exchange(pub)
 
 
-# # Network listening for initialization and response packets
-# (initialization, response) = listen()
+def handshake_cli(e_priv_i, e_pub_r, precomputed):
+    # Hashings
+    Ci = hash(CONSTRUCTION)
+    print("Ci : ", hexlify(Ci))
 
-# print('ENC_STATIC:' + initialization['enc_static'])
-# print('ENC_TS:' + initialization['enc_ts'])
+    Hi = hash(Ci + IDENTIFIER)
+    print("Hi : ", hexlify(Hi))
 
-# Hashings
-Ci = hash(CONSTRUCTION)
-print("Ci : ", hexlify(Ci))
+    Hi = hash(Hi + SERVER_PUBKEY.public_bytes_raw())
+    print("SERVER_PUBKEY : ", SERVER_PUBKEY.public_bytes_raw().hex())
+    print("Hi : ", hexlify(Hi))
 
-Hi = hash(Ci + IDENTIFIER)
-print("Hi : ", hexlify(Hi))
+    ephemeral_privkey = X25519PrivateKey.from_private_bytes(bytes.fromhex(e_priv_i))
+    ephemeral_pubkey = ephemeral_privkey.public_key()
 
-Hi = hash(Hi + SERVER_PUBKEY)
-print("SERVER_PUBKEY : ", hexlify(SERVER_PUBKEY))
-print("Hi : ", hexlify(Hi))
+    print("Epriv : " + ephemeral_privkey.private_bytes_raw().hex())
+    print("Epub : " + ephemeral_pubkey.public_bytes_raw().hex())
 
-ephemeral_privkey = bytes.fromhex('208a9149b5ab1864bc01c5105db8f832a99b2355c4ea6182f919c564000f1659')
-ephemeral_pubkey = bytes.fromhex('c9b64a50e368003a054b3870b00464a7293148ae57ce6eb6d0df7d6b84ec4638')
+    # Key derivation function (KDF) for ephemeral public key and Ci
+    Ci = kdf(1, Ci, bytes(ephemeral_pubkey.public_bytes_raw()))
+    print("Ci : ", hexlify(Ci))
 
-print("Epriv : " + bytearray(ephemeral_privkey).hex())
-print("Epub : " + bytearray(ephemeral_pubkey).hex())
+    # Hashing
+    # Hi = hash(Hi + bytes(ephemeral_pubkey.public_bytes_raw()))
+    # print("Hi : ", hexlify(Hi))
 
-# Key derivation function (KDF) for ephemeral public key and Ci
-Ci = kdf(1, Ci, bytes(ephemeral_pubkey))
-print("Ci : ", hexlify(Ci))
+    # DH Computation en X25519
+    result_dh = compute_dh(ephemeral_privkey, SERVER_PUBKEY)
+    print("result_dh : ", hexlify(result_dh))
 
-# Hashing
-Hi = hash(Hi + bytes(ephemeral_pubkey))
-print("Hi : ", hexlify(Hi))
+    # Key derivation function (KDF) for Diffie Helmann result and Ci
+    (Ci, k) = kdf(2, Ci, bytes(result_dh))
+    print("Ci : ", hexlify(Ci))
+    print("k : ", hexlify(k))
+    print("len(k) : ", len(k))
 
-# DH Computation en X25519
-result_dh = compute_dh(ephemeral_privkey, SERVER_PUBKEY)
-print("result_dh : ", hexlify(result_dh))
+    # KDF with precomputed DH
+    (Ci, k) = kdf(2, Ci, unhexlify(precomputed))
+    print("Ci : ", hexlify(Ci))
+    print("k : ", hexlify(k))
 
-# Key derivation function (KDF) for Diffie Helmann result and Ci
-(Ci, k) = kdf(2, Ci, bytes(result_dh))
+    ## Second message consumption
+    # KDF with server ephemeral
+    Ci = kdf(1, Ci, unhexlify(e_pub_r))
+    print("Ci : ", hexlify(Ci))
 
-print("Ci : ", hexlify(Ci))
-print("k : ", hexlify(k))
-print("len(k) : ", len(k))
+    # KDF with ephe,eral DH
+    server_ephemeral_pubkey = X25519PublicKey.from_public_bytes(bytes.fromhex(e_pub_r))
+    result_dh = compute_dh(ephemeral_privkey, server_ephemeral_pubkey)
+    Ci = kdf(1, Ci, result_dh)
+    print("Ci : ", hexlify(Ci))
 
-# ChaCha20Poly1305 encryption of k
-chacha = ChaCha20Poly1305(k)
+    # KDF with static DH
+    result_dh = compute_dh(CLIENT_1_PRIVKEY, server_ephemeral_pubkey)
+    Ci = kdf(1, Ci, result_dh)
+    print("Ci : ", hexlify(Ci))
 
-msg_static = chacha.encrypt(b"\x00"*12, CLIENT_1_PUBKEY, Hi)
-print("msg_static : ", hexlify(msg_static))
+    # KDF with PSK
+    (Ci, t, k) = kdf(3, Ci, PSK)
+    print("Ci : ", hexlify(Ci))
+    print("k : ", hexlify(k))
 
-Hi = hash(Hi + msg_static)
-print("Hi : ", hexlify(Hi))
+    return Ci
 
-(Ci, k) = kdf(2, Ci, compute_dh(CLIENT_1_PRIVKEY, SERVER_PUBKEY))
 
-print("Ci : ", hexlify(Ci))
-print("k : ", hexlify(k))
+def begin_session(Ci):
+    # Key derivation
+    tkey_send_i, tkey_recv_i = kdf(2, Ci, b"\x55")
+    n_send_i = 0
 
-chacha = ChaCha20Poly1305(k)
+    print("tkey_send_i : ", hexlify(tkey_send_i))
+    print("tkey_recv_i : ", hexlify(tkey_recv_i))
+    return tkey_send_i, tkey_recv_i, n_send_i
 
 
-msg_timestamp = chacha.encrypt(b"\x00"*12, bytes.fromhex("4000000065b8d6ae18000000"), Hi)
-print("msg_timestamp : ", hexlify(msg_timestamp))
+def decrypt_msg(k, nonce, ciphertext, aad):
+    chacha = ChaCha20Poly1305(k)
+    padded_nonce = b"\x00" * 8 + struct.pack(">I", nonce)
+    plaintext = chacha.encrypt(padded_nonce, ciphertext, aad)
+    print(hexlify(plaintext))
 
-Hi = hash(Hi + msg_timestamp)
 
-print("Hi : ", hexlify(Hi))
+Ci = handshake_cli(
+    e_priv_i="20841af4d7baef5d89f349f1b8941ecb6d83078271db34d54842cc235431de61",
+    e_pub_r="4392fa7c29c9c4cbf05197a299aed58e6e87626c97c3ffd698bdb89ce0e79432",
+    precomputed="87c897a180509924565936a36eaff8a3ed76b7d4ab819b3050da353ca1d59d1a",
+)
+begin_session(Ci)
+# decrypt_msg(None, 5, b"\x00", None)
diff --git a/wireguard-server/wg0.conf b/wireguard-server/wg0.conf
new file mode 120000
index 0000000..b0be480
--- /dev/null
+++ b/wireguard-server/wg0.conf
@@ -0,0 +1 @@
+./config/wg_confs/wg0.conf
\ No newline at end of file
-- 
GitLab