Konečně se mi podařilo rozchodit Wireguard na Raspberry Pi Pico spolu s protokolem IPv6. Problém byl v knihovně pi-pico-wireguard-lwip.
Program měří teplotu interním čidlem Raspberry Pi Pico a posílá naměřené hodnoty UDP protokolem na server. Celá komunikace je zabezpečena virtuální privátní sítí Wireguard.
Co je potřeba vylepšit (dodělat)
-
Konfigurace Wireguardu nezávisle na programu.
-
Zadání známých wifi sítí.
-
JSON formát posílání dat.
-
Čas v časové zóně (čas je zatím natvrdo středoevropský) .
Sestavení programu
Knihovna pi-pico-wireguard-lwip.tar.gz fungující na IPv6 protokolu.
Knihovna pro SSD1306 displej display_ssd1306.tar.gz (bez UTF8 podpory).
mkdir wireguard_cidlo
cd wireguard_cidlo
wget https://sspvc.lixis.cz/pmp/wifi/picow_wireguard6/CMakeLists.txt
wget https://sspvc.lixis.cz/pmp/wifi/picow_wireguard6/flash_utils.h
wget https://sspvc.lixis.cz/pmp/wifi/picow_wireguard6/lwipopts.h
wget https://sspvc.lixis.cz/pmp/wifi/picow_wireguard6/main.c
wget https://sspvc.lixis.cz/pmp/wifi/picow_wireguard6/memmap_custom.ld
wget https://sspvc.lixis.cz/pmp/wifi/picow_wireguard6/pico_sdk_import.cmake
wget https://sspvc.lixis.cz/pmp/wifi/picow_wireguard6/pico_wireguard_import.cmake
wget https://sspvc.lixis.cz/pmp/wifi/picow_wireguard6/setupWifi.h
wget https://sspvc.lixis.cz/pmp/wifi/picow_wireguard6/pi-pico-wireguard-lwip.tar.gz
wget https://sspvc.lixis.cz/pmp/wifi/picow_wireguard6/display_ssd1306.tar.gz
tar xzvf pi-pico-wireguard-lwip.tar.gz
export PICO_WIREGUARD_PATH=$PWD/pi-pico-wireguard-lwip
tar xzvf display_ssd1306.tar.gz
mkdir build
cd build
cmake ..
make -j4
Nastavení známých Wifi a jejich uložení na konec flash paměti Pica se provádí jednorázově jiným programem, který zde není. Nechci zvěřejňovat hesla ke svým AP. Program pro uložení přístupových údajů k Wifi sítím je zde.
Prototyp


cidlo2-ipv6: AP scan...
ssid: chorche5 rssi: -44 chan: 3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche5 rssi: -43 chan: 3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche5 rssi: -43 chan: 3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche3 rssi: -50 chan: 1 mac: 2c:fd:a1:dd:e4:a4 sec: WPA2_MIXED_PSK known: yes
ssid: chorche3 rssi: -55 chan: 1 mac: 2c:fd:a1:dd:e4:a4 sec: WPA2_MIXED_PSK known: yes
ssid: chorche5 rssi: -45 chan: 3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche5 rssi: -45 chan: 3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche5 rssi: -45 chan: 3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche3 rssi: -59 chan: 1 mac: 2c:fd:a1:dd:e4:a4 sec: WPA2_MIXED_PSK known: yes
ssid: chorche5 rssi: -44 chan: 3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche5 rssi: -44 chan: 3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche3 rssi: -51 chan: 1 mac: 2c:fd:a1:dd:e4:a4 sec: WPA2_MIXED_PSK known: yes
ssid: chorche3 rssi: -54 chan: 1 mac: 2c:fd:a1:dd:e4:a4 sec: WPA2_MIXED_PSK known: yes
ssid: chorche6 rssi: -68 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -67 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -67 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -70 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -67 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -70 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -69 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -68 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -69 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -69 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -66 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -67 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -67 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -69 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche5 rssi: -85 chan: 3 mac: c8:e7:d8:a0:ae:0a sec: WPA_TKIP_PSK known: yes
ssid: chorche6 rssi: -70 chan: 8 mac: c0:fd:84:c6:e8:5f sec: WPA_TKIP_PSK known: yes
ssid: chorche rssi: -77 chan: 11 mac: a0:e4:cb:89:fa:77 sec: WPA2_MIXED_PSK known: yes
ssid: chorche rssi: -77 chan: 11 mac: a0:e4:cb:89:fa:77 sec: WPA2_MIXED_PSK known: yes
ssid: chorche rssi: -73 chan: 11 mac: a0:e4:cb:89:fa:77 sec: WPA2_MIXED_PSK known: yes
ssid: chorche rssi: -74 chan: 11 mac: a0:e4:cb:89:fa:77 sec: WPA2_MIXED_PSK known: yes
Skenujeme Wifi...
Scan je hotov.
Nejlepší signál -43 má ssid chorche5 sem se připojíme.
Připojuji se k AP chorche5.
Stav připojení: 1 500
Stav připojení: 2 166
Stav připojení: 3 41
Připojeno.
Linková IPv6 adresa: FE80::2ACD:C1FF:FE03:37EF
Globální IPv6 adresa: 2A0E:5340:4:1:2ACD:C1FF:FE03:37EF
Host Name: cidlo2-ipv6
DNS server: 2A0E:5340:4:1::1
DNS ::1002:A54C:2004:1D54:2000:D478 err=-5
IP NTP serveru z DNS 2001:718:1:1::144:201
SNTP odpovídá
Device init took 35ms
Adding peer took 35ms
wireguardif_connect OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 15.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 15.4 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 15.6 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 15.7 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 15.8 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 15.8 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 15.8 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.0 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.1 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.1 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.4 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 ERR_CONN: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 OK: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 OK: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 OK: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 OK: udp_connect OK: Poslal jsem: 'Teplota: 16.4 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 OK: udp_connect OK: Poslal jsem: 'Teplota: 16.4 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 OK: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 OK: udp_connect OK: Poslal jsem: 'Teplota: 16.3 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 OK: udp_connect OK: Poslal jsem: 'Teplota: 16.5 ˚C', OK
MojeIP: FD68:ABCD::2 IPv6 adresa na posílání: FD68:ABCD::1 peer 2A0E:5340:4:1::801 48484 OK: udp_connect OK: Poslal jsem: 'Teplota: 16.2 ˚C', OK
Připojení Wireguardu chvíli trvá, na výpisu z minicomu je to vidět na řádcích, které obsahují peer 2A0E:5340:4:1::801 48484 ERR_CONN.
Zdrojové kódy
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
set(PICO_BOARD pico_w)
include(pico_sdk_import.cmake)
#set(PROJECT_NAME wireguard6_cidlo)
project(wireguard_cidlo1 C CXX ASM)
set (CMAKE_C_STANDARD 11)
set (CMAKE_CXX_STANDARD 17)
set (CMAKE_EXPORT_COMPILE_COMMANDS ON)
pico_sdk_init()
include(pico_wireguard_import.cmake)
add_executable(wireguard6_cidlo
main.c
disp/ssd1306.c
)
target_include_directories(wireguard6_cidlo PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${pico_wireguard_SOURCE_DIR}/src
)
#include(arguments.cmake)
# nastaveni vyhrazeneho mista ve XIP flash pro konfiguracni data
pico_set_linker_script(wireguard6_cidlo ${CMAKE_SOURCE_DIR}/memmap_custom.ld)
pico_enable_stdio_usb(wireguard6_cidlo 1)
pico_enable_stdio_uart(wireguard6_cidlo 0)
pico_add_extra_outputs(wireguard6_cidlo)
#target_include_directories(wireguard_cidlo PRIVATE ${CMAKE_CURRENT_LIST_DIR})
target_link_libraries(wireguard6_cidlo
pico_stdlib
pico_cyw43_arch_lwip_threadsafe_background
pico_wireguard
hardware_flash
hardware_sync
hardware_rtc
hardware_i2c
hardware_adc
)
target_link_libraries(pico_stdlib)
# volby pro linker
target_link_options(wireguard6_cidlo PRIVATE -Xlinker --print-memory-usage)
pico_sdk_import.cmake
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
# GIT_SUBMODULES_RECURSE was added in 3.17
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
GIT_SUBMODULES_RECURSE FALSE
)
else ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
)
endif ()
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})
pico_wireguard_import.cmake
if (DEFINED ENV{PICO_WIREGUARD_PATH} AND (NOT PICO_WIREGUARD_PATH))
set(PICO_WIREGUARD_PATH $ENV{PICO_WIREGUARD_PATH})
message("Using PICO_WIREGUARD_PATH from environment ('${PICO_WIREGUARD_PATH}')")
endif ()
if (NOT PICO_WIREGUARD_PATH)
message(FATAL_ERROR "No pico wireguard found")
endif ()
add_subdirectory(${PICO_WIREGUARD_PATH} build/lib/pico_wireguard)
main.c
/* main.c
* Pico W wireguard IPv6:
* skenování sítí a nalezení sítí, ke ktrým se můžeme připojit
* SNTP klient a seřízení času
* Měření teploty interním čidlem a posílání hodnot Wireguardem na server.
*
* (c) Jirka Chráska 2026; <jirka@lixis.cz>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Lixis s.r.o." nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Jirka Chráska <jirka@lixis.cz>
*/
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "wireguardif.h"
#include "pico/cyw43_arch.h"
#include "flash_utils.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include "lwip/pbuf.h"
#include "lwip/ip6_addr.h"
#include "lwip/udp.h"
#include "lwip/dns.h"
#include "hardware/rtc.h"
#include "time.h"
#include "hardware/adc.h"
#include "setupWifi.h"
#define HOSTNAME "cidlo2-ipv6"
#define MERENI_PORT 4848
#define KAM_POSLAT "fd68:abcd::1"
#define MOJE_WG_IP "fd68:abcd::2"
#define PEER_PUBKEY "XSqRUMJxLgFu9hejbzKzDpjCkibzNaek3Ql6sMRQBXk="
#define MUJ_PRIVKEY "wKXLsUiN8B196cWy5e+i90JIYyI8a/UG1+O++Bb4ImI="
#define WG_ADDRESS "fd68:abcd::2"
#define WG_GATEWAY_IP "fd68:abcd::1"
#define IPV6_ANY "::"
#define WG_SUBNET_MASK_IP "ffff:ffff:ffff:ffff:0:0:0:0"
#define PEER_HOSTNAME "m1.jr.lixis.cz"
#define PEER_IPV6 "2a0e:5340:4:1::801"
#define WG_ENDPOINT_PORT 48484
#define WG_KEEPALIVE 15
#define WG_ALLOWED_IP1 "fd68:abcd::1"
#define WG_ALLOWED_MASK1 "ffff:ffff:ffff:ffff:0:0:0:0"
// SNTP
#define NTP_SERVER "tik.cesnet.cz"
#define TIMEZONE 3600
// převod chyb z LWIP na řetezec
char * error_to_string( err_t e )
{
static char * err_ok = "OK";
static char * err_mem = "ERR_MEM";
static char * err_buf = "ERR_BUF";
static char * err_timeout = "ERR_TIMEOUT";
static char * err_rte = "ERR_RTE";
static char * err_inprogress = "ERR_INPROGRESS";
static char * err_val = "ERR_VAL";
static char * err_wouldblock = "ERR_WOULDBLOCK";
static char * err_use = "ERR_USE";
static char * err_already = "ERR_ALREADY";
static char * err_isconn = "ERR_ISCONN";
static char * err_conn = "ERR_CONN";
static char * err_if = "ERR_IF";
static char * err_abrt = "ERR_ABRT";
static char * err_rst = "ERR_RST";
static char * err_clsd = "ERR_CLSD";
static char * err_arg = "ERR_ARG";
char * err_str = err_ok;
switch(e) {
case 0: err_str = err_ok; break; /* No error, everything OK. */
case -1: err_str = err_mem; break; /* Out of memory error. */
case -2: err_str = err_buf; break; /* Buffer error. */
case -3: err_str = err_timeout; break; /* Timeout error. */
case -4: err_str = err_rte; break; /* Routing problem */
case -5: err_str = err_inprogress; break; /* Operation in progress */
case -6: err_str = err_val; break; /* Illegal value */
case -7: err_str = err_wouldblock; break; /* Operation would block */
case -8: err_str = err_use; break; /* Address in use */
case -9: err_str = err_already; break; /* Already connecting */
case -10: err_str = err_isconn; break; /* Connection already established */
case -11: err_str = err_conn; break; /* Not connected */
case -12: err_str = err_if; break; /* Low-level netif error */
case -13: err_str = err_abrt; break; /* Connection aborted */
case -14: err_str = err_rst; break; /* Connection reset. */
case -15: err_str = err_clsd; break; /* Connection closed. */
case -16: err_str = err_arg; break; /* Illegal argument */
}
return err_str;
}
// --------------------------------------------------------------------------------------------
// připojení wireguardu
static struct netif wg_netif_struct = {0};
static struct netif *wg_netif = NULL;
static uint8_t wireguard_peer_index = WIREGUARDIF_INVALID_INDEX;
void connect_wireguard()
{
struct wireguardif_init_data wg;
struct wireguardif_peer peer;
ip_addr_t ipaddr;
ip_addr_t netmask;
ip_addr_t allowed_ip;
ip_addr_t gateway;
s8_t route_idx;
err_t e;
char *err_str = "";
ip6addr_aton(WG_ADDRESS, &ipaddr);
ip6addr_aton(WG_SUBNET_MASK_IP, &netmask);
ip6addr_aton(WG_ALLOWED_IP1, &allowed_ip);
ip6addr_aton(WG_GATEWAY_IP, &gateway);
wg.private_key = MUJ_PRIVKEY;
wg.listen_port = 51881;
wg.bind_netif = NULL;
wg_netif = netif_add(&wg_netif_struct, &wg, &wireguardif_init, &ip6_input);
netif_add_ip6_address(wg_netif, &ipaddr, NETIF_NO_INDEX );
netif_set_up(wg_netif);
wireguardif_peer_init(&peer);
peer.public_key = PEER_PUBKEY;
peer.preshared_key = NULL;
peer.keep_alive = WG_KEEPALIVE;
peer.allowed_ip = allowed_ip;
peer.allowed_mask = netmask;
ip6addr_aton(PEER_IPV6, &peer.endpoint_ip);
peer.endport_port = WG_ENDPOINT_PORT;
wireguardif_add_peer(wg_netif, &peer, &wireguard_peer_index);
if ((wireguard_peer_index != WIREGUARDIF_INVALID_INDEX) &&
!ip_addr_isany(&peer.endpoint_ip)) {
err_t e = wireguardif_connect(wg_netif, wireguard_peer_index);
printf("wireguardif_connect %s\n",error_to_string(e));
}
}
// --------------------------------------------------------------------------------------------
// OLED displej
#include "disp/ssd1306.h"
#include "disp/font_spleen_8x5.h"
#include "disp/font_spleen_16x8a.h"
#define DISPLAY_WIDTH 128
#define DISPLAY_HEIGHT 32
#define I2C_ADDRESS 0x3C
#define I2C_FREQ 2000000
uint SDA_PIN=4;
uint SCL_PIN=5;
ssd1306_t disp;
// nastavení I2C sběrnice
void setup_i2c(void)
{
i2c_init(i2c0, I2C_FREQ);
gpio_set_function(SDA_PIN, GPIO_FUNC_I2C);
gpio_set_function(SCL_PIN, GPIO_FUNC_I2C);
gpio_pull_up(SDA_PIN);
gpio_pull_up(SCL_PIN);
}
void zapnuti_displeje( void )
{
ssd1306_init(&disp, DISPLAY_WIDTH, DISPLAY_HEIGHT, I2C_ADDRESS, i2c0); // inicializace
ssd1306_poweron(&disp); // zapnutí displeje
ssd1306_clear(&disp); // vymazání displeje
ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_16x8a, HOSTNAME);
ssd1306_show(&disp); // zobrazíme písmenka na displeji
}
// ---------------------------------------------------------------------------
// známé sítě jsou uloženy ve flash paměti
const uint8_t *flash_target_content;
// struktura údajů o známé síti
typedef struct wifi_zname {
char* ssid; // jméno sítě
char* pass; // heslo v otevřeném textu
char* auth; // způsob autorizace
char* ipv6; // umí síť IPv6 (y/n)
char* ipv4; // umí síť IPv4 (y/n)
int rssi; // aktuální síla signálu
struct wifi_zname * next; // ukazatel na další prvek seznamu
} ZNAME_WIFI;
// ukazatel na počátek seznamu známých sítí
ZNAME_WIFI* zwifi = NULL;
// vytvoří seznam známých Wifi, ke kterým se můžeme připojit
void napln_zname_wifi(void)
{
char *wifidata; // pomocný ukazatel do XIP flash
ZNAME_WIFI *zw;
flash_target_content = (const uint8_t *) getAddressPersistent();
for( int i=0; i<8; i++ ){
wifidata = (char*) flash_target_content+(i*128);
zw = malloc(sizeof(ZNAME_WIFI));
if(zw != NULL) {
zw->ssid = wifidata+5;
// printf("%p ssid='%s' ", wifidata, zw->ssid);
int len = strlen(zw->ssid)+6;
zw->pass = wifidata+len+5;
// printf("%p pass='%s' ", wifidata, zw->pass);
len += strlen(zw->pass)+6;
zw->auth = wifidata+len+5;
// printf("%p auth='%s' ", wifidata, zw->auth);
len += strlen(zw->auth)+6;
zw->ipv6 = wifidata+len+5;
//printf("%p ipv6='%s' ", wifidata, zw->ipv6);
len += strlen(zw->ipv6)+6;
zw->ipv4 = wifidata+len+5;
// printf("%p ipv4='%s'\n", wifidata, zw->ipv4);
// počáteční signál -120 dBm
zw->rssi = -120;
if( zwifi != NULL ) { // v seznamu už něco máme
zw->next = zwifi;
} else { // vkládáme první prvek (v seznamu bude poslední)
zw->next = NULL;
}
zwifi = zw;
} else {
printf("Nedostatek paměti.\n");
break;
}
}
}
// najde v seznamu známou WiFi podle ssid
ZNAME_WIFI * najdi_wifi(const char *ssid)
{
for( ZNAME_WIFI *zw = zwifi; zw!=NULL; zw=zw->next ) {
if(strcmp(zw->ssid, ssid) == 0 ) {
return zw;
}
}
return NULL;
}
// najde v seznamu Wifi s nejlepším signálem
ZNAME_WIFI *najdi_nejvetsi_rssi( void )
{
int rssi = -120;
ZNAME_WIFI *res = NULL;
for( ZNAME_WIFI *zw = zwifi; zw!=NULL; zw=zw->next ) {
if(zw->rssi > rssi) {
res = zw;
rssi = zw->rssi;
}
}
return res;
}
// uvolní seznam známých wifi
void zrus_zname_wifi( void )
{
for( ZNAME_WIFI *ptr=zwifi; ptr!=NULL; ) {
ZNAME_WIFI *ptr2 = ptr;
ptr = ptr->next;
free(ptr2);
}
}
// callback, výsledky skenování
static int scan_result(void *env, const cyw43_ev_scan_result_t *result)
{
if (result)
{
ZNAME_WIFI *znam = najdi_wifi( result->ssid );
if( znam != NULL) {
if( result->rssi > znam->rssi ) {
znam->rssi = result->rssi;
}
printf("ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %s known: %s\n",
result->ssid, result->rssi, result->channel,
result->bssid[0], result->bssid[1], result->bssid[2],
result->bssid[3], result->bssid[4], result->bssid[5],
(result->auth_mode == 5) ? "WPA2_MIXED_PSK" :
(result->auth_mode == 7) ? "WPA_TKIP_PSK" :
(result->auth_mode == 0) ? "OPEN" : "UNKNOWN",
"yes" );
} else {
printf("ssid: %-32s rssi: %4d chan: %3d mac: %02x:%02x:%02x:%02x:%02x:%02x sec: %s known: %s\n",
result->ssid, result->rssi, result->channel,
result->bssid[0], result->bssid[1], result->bssid[2],
result->bssid[3], result->bssid[4], result->bssid[5],
(result->auth_mode == 5) ? "WPA2_MIXED_PSK" :
(result->auth_mode == 7) ? "WPA_TKIP_PSK" :
(result->auth_mode == 0) ? "OPEN" : "UNKNOWN",
"no" );
}
}
return 0;
}
// připojení k AP
int connect( ZNAME_WIFI *zw, char * hostname )
{
uint32_t country = CYW43_COUNTRY_CZECH_REPUBLIC;
uint32_t auth;
if( strcmp(zw->auth,"WPA2_MIXED_PSK") == 0) auth = CYW43_AUTH_WPA2_MIXED_PSK;
if( strcmp(zw->auth,"WPA_TKIP_PSK") == 0) auth = CYW43_AUTH_WPA_TKIP_PSK;
// používá se nastavení pomocí SLAAC+RD
return setup(country, zw->ssid, zw->pass, auth, hostname );
}
// --------------------------------------------------------------------------------------------
// SNTP
// získání času z nastavených hodin Pica
bool getDateNow(struct tm *t)
{
datetime_t rtc;
bool state = rtc_get_datetime(&rtc);
if (state)
{
t->tm_sec = rtc.sec;
t->tm_min = rtc.min;
t->tm_hour = rtc.hour;
t->tm_mday = rtc.day;
t->tm_mon = rtc.month - 1;
t->tm_year = rtc.year - 1900;
t->tm_wday = rtc.dotw;
t->tm_yday = 0;
t->tm_isdst = -1;
}
return state;
}
// nastavení hodin reálného času na Picu
void setRTC(struct tm *datetime)
{
datetime_t t;
t.year = datetime->tm_year + 1900;
t.month = datetime->tm_mon + 1;
t.day = datetime->tm_mday;
t.dotw = datetime->tm_wday;
t.hour = datetime->tm_hour;
t.min = datetime->tm_min;
t.sec = datetime->tm_sec;
rtc_init();
rtc_set_datetime(&t);
}
struct timeStatus
{
bool ready;
struct udp_pcb *pcb;
};
// příjem SNTP odpovědi
void recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
struct timeStatus *tstatus = (struct timeStatus *)arg;
printf("SNTP odpovídá\n");
if (p != NULL)
{
uint8_t seconds_buf[4];
pbuf_copy_partial(p, seconds_buf, sizeof(seconds_buf), 40);
uint32_t seconds_since_1900 = seconds_buf[0] << 24 | seconds_buf[1] << 16 | seconds_buf[2] << 8 | seconds_buf[3];
time_t seconds_since_1970 = seconds_since_1900 - 2208988800 + TIMEZONE;
struct tm *datetime = gmtime(&seconds_since_1970);
setRTC(datetime);
pbuf_free(p);
udp_remove(pcb);
tstatus->pcb=NULL;
tstatus->ready = true;
}
}
bool pollSNTP(struct timeStatus *tstatus)
{
if (tstatus == NULL)
return true;
if (tstatus->ready)
{
free(tstatus);
tstatus = NULL;
return true;
}
return false;
}
// získání IP adresy NTP serveru z DNS
void dns_found(const char *name, const ip6_addr_t *ip, void *arg)
{
struct timeStatus *tstatus = (struct timeStatus *)arg;
printf("IP NTP serveru z DNS %s\n", ip6addr_ntoa(ip));
struct udp_pcb *pcb = udp_new_ip_type(6);
tstatus->pcb = pcb;
udp_recv(pcb, recv, arg);
udp_bind(pcb, IP_ADDR_ANY, 123);
udp_connect(pcb, ip, 123);
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 48, PBUF_RAM);
uint8_t *payload = (uint8_t *)p->payload;
memset(payload, 0, 48);
*payload = 0x1b;
err_t er = udp_send(pcb, p);
pbuf_free(p);
}
struct timeStatus *getSNTP()
{
struct timeStatus *tstatus = malloc(sizeof(struct timeStatus));
tstatus->ready = false;
tstatus->pcb=NULL;
ip6_addr_t ip;
cyw43_arch_lwip_begin();
// err_t err = dns_gethostbyname(xstr(NTP_SERVER), &ip, dns_found, tstatus);
err_t err = dns_gethostbyname_addrtype(NTP_SERVER, &ip, dns_found, tstatus, LWIP_DNS_ADDRTYPE_IPV6);
cyw43_arch_lwip_end();
if (err == ERR_OK)
{
printf("DNS %s\n", ip6addr_ntoa(&ip));
dns_found(NTP_SERVER, &ip, tstatus);
} else {
printf("DNS %s err=%d\n", ip6addr_ntoa(&ip), err);
}
return tstatus;
}
void cancelSNTP(struct timeStatus *tstatus)
{
if (tstatus != NULL)
{
udp_remove(tstatus->pcb);
free(tstatus);
tstatus = NULL;
printf("zrušeno\n");
}
}
// ----------------------------------------------------------------------------------------
// měření teploty interním čidlem
static struct udp_pcb *pcb_mereni;
void setup_udp_pcb()
{
pcb_mereni = udp_new_ip_type(6);
}
// poslání naměřené teploty wireguardem na server
int posli_mereni(float teplota)
{
char moje_ip_buf[64];
char message[256];
ip6_addr_t moje_ip;
ip6_addr_t ip_kam;
ip_addr_t curwip;
uint16_t curwport;
int len;
printf("MojeIP: %s ", ip6addr_ntoa(netif_ip6_addr(wg_netif,1)));
sprintf(moje_ip_buf,"%s", ip6addr_ntoa(netif_ip6_addr(wg_netif,1)));
ip6addr_aton(moje_ip_buf, &moje_ip);
IP_SET_TYPE_VAL(&moje_ip, IPADDR_TYPE_V6);
ip6addr_aton(KAM_POSLAT, &ip_kam);
IP_SET_TYPE_VAL(&ip_kam, IPADDR_TYPE_V6);
printf("IPv6 adresa na posílání: %s ", ip6addr_ntoa(&ip_kam));
err_t e = 0;
err_t ew = wireguardif_peer_is_up(wg_netif, wireguard_peer_index, &curwip, &curwport);
printf("peer %s %d %s: ",ip6addr_ntoa(&curwip), curwport, error_to_string(ew));
err_t er = udp_connect( pcb_mereni, &ip_kam, MERENI_PORT);
e += er;
printf("udp_connect %s: ", error_to_string(er));
memset(message,'\0',256);
sprintf(message, "Teplota: %5.1f ˚C\n", teplota);
len = strlen(message);
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len+1, PBUF_RAM);
snprintf(p->payload, len+1, "%s", message);
err_t er2 = udp_send(pcb_mereni,p);
e += er2;
pbuf_free(p);
printf("Poslal jsem: 'Teplota: %5.1f ˚C', %s\n", teplota, error_to_string(er2));
udp_disconnect(pcb_mereni);
return e;
}
// ----------------------------------------------------------------------------------------
int main()
{
uint16_t result;
float teplota;
float teplota_arr[10] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
float adc;
const float conversion_factor = 3.3306f / ( 1 << 12 );
char buf1[80]; // buffery pro displej
char buf2[80];
char buf3[80];
char buf4[80];
stdio_init_all();
sleep_ms(2000);
setup_i2c();
zapnuti_displeje();
// nastavení a výběr interního čidla
adc_init();
adc_set_temp_sensor_enabled(true);
adc_select_input(4);
// nastavení měniče na malý šum
gpio_set_function(23, GPIO_FUNC_SIO);
gpio_set_dir( 23, true );
gpio_put( 23, 1 );
printf("%s: AP scan...\n",HOSTNAME);
napln_zname_wifi();
// inicializace cyw43 chipu
if (cyw43_arch_init())
{
printf("Nemohu nahodit wifi.\n");
return 1;
}
// Picow Wifi nastavíme jako stanici
cyw43_arch_enable_sta_mode();
cyw43_wifi_set_up(&cyw43_state, CYW43_ITF_STA, true, CYW43_COUNTRY_CZECH_REPUBLIC);
cyw43_wifi_scan_options_t scan_options = {0};
// začínáme skenovat
int err = cyw43_wifi_scan(&cyw43_state, &scan_options, NULL, scan_result);
while (true)
{
if (!cyw43_wifi_scan_active(&cyw43_state))
break;
sleep_ms(1000);
printf("Skenujeme Wifi... \n");
}
printf("Scan je hotov.\n");
cyw43_arch_deinit();
// najdeme nejlepší signál mezi známými WiFi
ZNAME_WIFI *zw = najdi_nejvetsi_rssi();
if( zw != NULL ) { // máme wifi k připojení
printf("Nejlepší signál %d má ssid %s sem se připojíme.\n", zw->rssi, zw->ssid);
int connection_status = 0;
do {
printf("Připojuji se k AP %s.\n",zw->ssid);
ssd1306_clear(&disp);
sprintf(buf1,"Pripojuji AP %s",zw->ssid);
ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_8x5, buf1);
ssd1306_show(&disp); // zobrazíme písmenka na displeji
connection_status = connect(zw, HOSTNAME );
} while ( connection_status != CYW43_LINK_UP);
printf("Připojeno.\n");
printf("Linková IPv6 adresa: %s\n", ip6addr_ntoa(netif_ip6_addr(netif_default,0)));
printf("Globální IPv6 adresa: %s\n", ip6addr_ntoa(netif_ip6_addr(netif_default,1)));
printf("Host Name: %s\n", netif_get_hostname(netif_default));
ssd1306_clear(&disp);
sprintf(buf1,"Pripojeno AP %s",zw->ssid);
ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_8x5, buf1);
sprintf(buf2,"%s",ip6addr_ntoa(netif_ip6_addr(netif_default,1)));
ssd1306_show(&disp); // zobrazíme písmenka na displeji
ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_8x5, buf2);
// nechce-li chodit automatické nastavení DNS serveru
// dá se nastavit ručně
// ip6_addr_t dnsserver_ip ;
// ip6addr_aton("2a0e:5340:4:1::1", &dnsserver_ip);
// IP_SET_TYPE_VAL(&dnsserver_ip, IPADDR_TYPE_V6);
// dns_setserver(0,&dnsserver_ip);
const ip6_addr_t *dnsip = dns_getserver(0);
printf("DNS server: %s\n", ip6addr_ntoa(dnsip));
while (true)
{
struct timeStatus *tstatus = getSNTP();
sleep_ms(500);
if (pollSNTP(tstatus))
break;
cancelSNTP(tstatus);
}
// nahození wireguardu
connect_wireguard();
// nahození UDP pro odesílání
setup_udp_pcb();
int i = 0;
err_t e = 0;
// vypneme LED
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
// nastavení svícení displeje
ssd1306_contrast(&disp, 20);
while (true)
{
// smyčka trvá přesně 1s
absolute_time_t cekam = make_timeout_time_ms(1000);
result = adc_read();
adc = result * conversion_factor;
// teplotu budeme průměrovat 1x za 10s
teplota_arr[i%10] = 27.0f - (adc - 0.706f) / 0.001721f;
struct tm t;
getDateNow(&t);
char Date[100];
strftime(Date, sizeof(Date), "%a, %d %b %Y %k:%M:%S %Z", &t);
strftime(buf1, sizeof(buf1), "%a, %d %b %Y", &t);
strftime(buf2, sizeof(buf2), "%k:%M", &t);
strftime(buf3, sizeof(buf3), ":%S", &t);
sprintf(buf4,"%4.1fC", teplota);
ssd1306_clear(&disp);
ssd1306_draw_string_with_font(&disp, 0, 0, 1, font_spleen_8x5, buf1);
ssd1306_draw_string_with_font(&disp, 0, 8, 2, font_spleen_16x8a, buf2);
ssd1306_draw_string_with_font(&disp, 90, 20, 1, font_spleen_16x8a, buf3);
ssd1306_draw_string_with_font(&disp, 90, 10, 1, font_spleen_8x5, buf4);
printf("Čas: %s teplota: %4.1f ˚C\r", Date, teplota);
if( i!=0 && i%10 == 0 ) {
teplota = 0.0;
for(int j=0; j<10; j++) {
teplota += teplota_arr[j];
}
teplota *= 0.1;
// při posílání teploty blikneme LEDkou
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
e = posli_mereni(teplota);
ssd1306_contrast(&disp, 40);
} else {
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
ssd1306_contrast(&disp, 1);
}
sprintf(buf4, "%s", e==0?"OK":"ERR");
ssd1306_draw_string_with_font(&disp, 100, 0, 1, font_spleen_8x5, buf4);
ssd1306_show(&disp); // zobrazíme písmenka na displeji
i++;
busy_wait_until(cekam);
}
} else {
printf("Nemohu se připojit, žádná známá Wifi není v dosahu.\n");
ssd1306_clear(&disp);
sprintf(buf1,"Bez Wifi :-{");
ssd1306_draw_string_with_font(&disp,0,0,1,font_spleen_16x8a,buf1);
ssd1306_show(&disp);
sleep_ms(10000);
}
// úklid seznamu (uvolnění paměti)
zrus_zname_wifi();
cyw43_arch_deinit();
return 0;
}
// --------------------------------------------------------------------------------------------
lwipopts.h
#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H
#define _LWIPOPTS_EXAMPLE_COMMONH_H
// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
// allow override in some examples
#ifndef NO_SYS
#define NO_SYS 1
#endif
// allow override in some examples
#ifndef LWIP_SOCKET
#define LWIP_SOCKET 0
#endif
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC 1
#else
// MEM_LIBC_MALLOC is incompatible with non polling versions
#define MEM_LIBC_MALLOC 0
#endif
#define MEM_ALIGNMENT 4
#define MEM_SIZE 4000
#define MEMP_NUM_TCP_SEG 32
#define MEMP_NUM_ARP_QUEUE 10
#define PBUF_POOL_SIZE 24
#define LWIP_ARP 1
#define LWIP_ETHERNET 1
#define LWIP_ICMP 1
#define LWIP_RAW 1
#define TCP_WND (8 * TCP_MSS)
#define TCP_MSS 1460
#define TCP_SND_BUF (8 * TCP_MSS)
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK 1
#define LWIP_NETIF_HOSTNAME 1
#define LWIP_NETCONN 0
#define MEM_STATS 0
#define SYS_STATS 0
#define MEMP_STATS 0
#define LINK_STATS 0
// #define ETH_PAD_SIZE 2
#define LWIP_CHKSUM_ALGORITHM 3
#define LWIP_DHCP 0
#define LWIP_IPV4 0
#define LWIP_IPV6 1
#define LWIP_IPV6_AUTOCONFIG 1
#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS 2
#define LWIP_IPV6_FORWARD 1
#define LWIP_TCP 0
#define LWIP_UDP 1
#define LWIP_DNS 1
#define LWIP_TCP_KEEPALIVE 0
#define LWIP_NETIF_TX_SINGLE_PBUF 1
#define DHCP_DOES_ARP_CHECK 0
#define LWIP_DHCP_DOES_ACD_CHECK 0
#define DNS_TABLE_SIZE 1
#ifndef NDEBUG
#define LWIP_DEBUG 1
#define LWIP_STATS 1
#define LWIP_STATS_DISPLAY 1
#endif
#define ETHARP_DEBUG LWIP_DBG_OFF
#define NETIF_DEBUG LWIP_DBG_OFF
#define PBUF_DEBUG LWIP_DBG_OFF
#define API_LIB_DEBUG LWIP_DBG_OFF
#define API_MSG_DEBUG LWIP_DBG_OFF
#define SOCKETS_DEBUG LWIP_DBG_OFF
#define ICMP_DEBUG LWIP_DBG_OFF
#define INET_DEBUG LWIP_DBG_OFF
#define IP_DEBUG LWIP_DBG_OFF
#define IP_REASS_DEBUG LWIP_DBG_OFF
#define RAW_DEBUG LWIP_DBG_OFF
#define MEM_DEBUG LWIP_DBG_OFF
#define MEMP_DEBUG LWIP_DBG_OFF
#define SYS_DEBUG LWIP_DBG_OFF
#define TCP_DEBUG LWIP_DBG_OFF
#define TCP_INPUT_DEBUG LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
#define TCP_RTO_DEBUG LWIP_DBG_OFF
#define TCP_CWND_DEBUG LWIP_DBG_OFF
#define TCP_WND_DEBUG LWIP_DBG_OFF
#define TCP_FR_DEBUG LWIP_DBG_OFF
#define TCP_QLEN_DEBUG LWIP_DBG_OFF
#define TCP_RST_DEBUG LWIP_DBG_OFF
#define UDP_DEBUG LWIP_DBG_ON
#define TCPIP_DEBUG LWIP_DBG_OFF
#define PPP_DEBUG LWIP_DBG_OFF
#define SLIP_DEBUG LWIP_DBG_OFF
#define DHCP_DEBUG LWIP_DBG_OFF
#endif /* __LWIPOPTS_H__ */
setupWifi.h
/* Nastavení Wifi karty */
int setup(uint32_t country, const char *ssid, const char *pass, uint32_t auth, const char *hostname)
{
if (cyw43_arch_init_with_country(country))
{
return 1;
}
cyw43_arch_enable_sta_mode();
if (hostname != NULL)
{
netif_set_hostname(netif_default, hostname);
}
if (cyw43_arch_wifi_connect_async(ssid, pass, auth))
{
return 2;
}
int flashrate = 1000;
int status = CYW43_LINK_UP + 1;
while (status >= 0 && status != CYW43_LINK_UP)
{
int new_status = cyw43_tcpip_link_status(&cyw43_state, CYW43_ITF_STA);
if (new_status != status)
{
status = new_status;
flashrate = flashrate / (status + 1);
printf("Stav připojení: %d %d\n", status, flashrate);
}
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
sleep_ms(flashrate);
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
sleep_ms(flashrate);
}
if (status < 0)
{
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
}
else
{
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
}
return status;
}
flash_utils.h
#include <stdint.h>
inline uint32_t *getAddressPersistent()
{
extern uint32_t ADDR_PERSISTENT[];
return ADDR_PERSISTENT;
}
Knihovna pi-pico-wireguard-lwip (funkční IPv6)
pi-pico-wireguard-lwip/CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
include(pico_sdk_import.cmake)
project(pico_wireguard C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory(src/crypto)
pico_sdk_init()
add_library(pico_wireguard
src/crypto.c
src/wireguard.c
src/wireguardif.c
src/wireguard-platform.c
)
# use μNaCl
target_compile_definitions(pico_wireguard PUBLIC USE_mNaCl)
target_include_directories(pico_wireguard PRIVATE
${CMAKE_CURRENT_LIST_DIR}/src
)
target_link_libraries(pico_wireguard
pico_lwip_nosys
pico_wireguard_crypto
pico_stdlib
pico_lwip_arch_headers
pico_lwip_arch
pico_lwip_api
pico_lwip
)
target_link_libraries(pico_stdlib)
get_property(dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
foreach(dir ${dirs})
message(STATUS "dir='${dir}'")
endforeach()
pi-pico-wireguard-lwip/pico_sdk_import.cmake
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
# This can be dropped into an external project to help locate this SDK
# It should be include()ed prior to project()
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
endif ()
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
endif ()
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
if (NOT PICO_SDK_PATH)
if (PICO_SDK_FETCH_FROM_GIT)
include(FetchContent)
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
if (PICO_SDK_FETCH_FROM_GIT_PATH)
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
endif ()
# GIT_SUBMODULES_RECURSE was added in 3.17
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
GIT_SUBMODULES_RECURSE FALSE
)
else ()
FetchContent_Declare(
pico_sdk
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
GIT_TAG master
)
endif ()
if (NOT pico_sdk)
message("Downloading Raspberry Pi Pico SDK")
FetchContent_Populate(pico_sdk)
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
endif ()
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
else ()
message(FATAL_ERROR
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
)
endif ()
endif ()
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
if (NOT EXISTS ${PICO_SDK_PATH})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
endif ()
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
endif ()
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
include(${PICO_SDK_INIT_CMAKE_FILE})
pi-pico-wireguard-lwip/src/crypto.h
#ifndef _CRYPTO_H_
#define _CRYPTO_H_
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
// BLAKE2S IMPLEMENTATION
#include "crypto/refc/blake2s.h"
#define wireguard_blake2s_ctx blake2s_ctx
#define wireguard_blake2s_init(ctx,outlen,key,keylen) blake2s_init(ctx,outlen,key,keylen)
#define wireguard_blake2s_update(ctx,in,inlen) blake2s_update(ctx,in,inlen)
#define wireguard_blake2s_final(ctx,out) blake2s_final(ctx,out)
#define wireguard_blake2s(out,outlen,key,keylen,in,inlen) blake2s(out,outlen,key,keylen,in,inlen)
#ifndef USE_mNaCl
// X25519 IMPLEMENTATION
#include "crypto/refc/x25519.h"
#define wireguard_x25519(a,b,c) x25519(a,b,c,1)
#else
#include "crypto/cortex/scalarmult.h"
#define wireguard_x25519(a,b,c) crypto_scalarmult_curve25519(a,b,c)
#endif
// CHACHA20POLY1305 IMPLEMENTATION
#include "crypto/refc/chacha20poly1305.h"
#define wireguard_aead_encrypt(dst,src,srclen,ad,adlen,nonce,key) chacha20poly1305_encrypt(dst,src,srclen,ad,adlen,nonce,key)
#define wireguard_aead_decrypt(dst,src,srclen,ad,adlen,nonce,key) chacha20poly1305_decrypt(dst,src,srclen,ad,adlen,nonce,key)
#define wireguard_xaead_encrypt(dst,src,srclen,ad,adlen,nonce,key) xchacha20poly1305_encrypt(dst,src,srclen,ad,adlen,nonce,key)
#define wireguard_xaead_decrypt(dst,src,srclen,ad,adlen,nonce,key) xchacha20poly1305_decrypt(dst,src,srclen,ad,adlen,nonce,key)
// Endian / unaligned helper macros
#define U8C(v) (v##U)
#define U32C(v) (v##U)
#define U8V(v) ((uint8_t)(v) & U8C(0xFF))
#define U32V(v) ((uint32_t)(v) & U32C(0xFFFFFFFF))
#define U8TO32_LITTLE(p) \
(((uint32_t)((p)[0]) ) | \
((uint32_t)((p)[1]) << 8) | \
((uint32_t)((p)[2]) << 16) | \
((uint32_t)((p)[3]) << 24))
#define U8TO64_LITTLE(p) \
(((uint64_t)((p)[0]) ) | \
((uint64_t)((p)[1]) << 8) | \
((uint64_t)((p)[2]) << 16) | \
((uint64_t)((p)[3]) << 24) | \
((uint64_t)((p)[4]) << 32) | \
((uint64_t)((p)[5]) << 40) | \
((uint64_t)((p)[6]) << 48) | \
((uint64_t)((p)[7]) << 56))
#define U16TO8_BIG(p, v) \
do { \
(p)[1] = U8V((v) ); \
(p)[0] = U8V((v) >> 8); \
} while (0)
#define U32TO8_LITTLE(p, v) \
do { \
(p)[0] = U8V((v) ); \
(p)[1] = U8V((v) >> 8); \
(p)[2] = U8V((v) >> 16); \
(p)[3] = U8V((v) >> 24); \
} while (0)
#define U32TO8_BIG(p, v) \
do { \
(p)[3] = U8V((v) ); \
(p)[2] = U8V((v) >> 8); \
(p)[1] = U8V((v) >> 16); \
(p)[0] = U8V((v) >> 24); \
} while (0)
#define U64TO8_LITTLE(p, v) \
do { \
(p)[0] = U8V((v) ); \
(p)[1] = U8V((v) >> 8); \
(p)[2] = U8V((v) >> 16); \
(p)[3] = U8V((v) >> 24); \
(p)[4] = U8V((v) >> 32); \
(p)[5] = U8V((v) >> 40); \
(p)[6] = U8V((v) >> 48); \
(p)[7] = U8V((v) >> 56); \
} while (0)
#define U64TO8_BIG(p, v) \
do { \
(p)[7] = U8V((v) ); \
(p)[6] = U8V((v) >> 8); \
(p)[5] = U8V((v) >> 16); \
(p)[4] = U8V((v) >> 24); \
(p)[3] = U8V((v) >> 32); \
(p)[2] = U8V((v) >> 40); \
(p)[1] = U8V((v) >> 48); \
(p)[0] = U8V((v) >> 56); \
} while (0)
void crypto_zero(void *dest, size_t len);
bool crypto_equal(const void *a, const void *b, size_t size);
#endif /* _CRYPTO_H_ */
pi-pico-wireguard-lwip/src/crypto.c
#include "crypto.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
void crypto_zero(void *dest, size_t len) {
volatile uint8_t *p = (uint8_t *)dest;
while (len--) {
*p++ = 0;
}
}
bool crypto_equal(const void *a, const void *b, size_t size) {
uint8_t neq = 0;
while (size > 0) {
neq |= *(uint8_t *)a ^ *(uint8_t *)b;
a += 1;
b += 1;
size -= 1;
}
return (neq) ? false : true;
}
pi-pico-wireguard-lwip/src/lwipopts.h
#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H
#define _LWIPOPTS_EXAMPLE_COMMONH_H
// Common settings used in most of the pico_w examples
// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
// allow override in some examples
#ifndef NO_SYS
#define NO_SYS 1
#endif
// allow override in some examples
#ifndef LWIP_SOCKET
#define LWIP_SOCKET 0
#endif
#if PICO_CYW43_ARCH_POLL
#define MEM_LIBC_MALLOC 1
#else
// MEM_LIBC_MALLOC is incompatible with non polling versions
#define MEM_LIBC_MALLOC 0
#endif
#define MEM_ALIGNMENT 4
#define MEM_SIZE 4000
#define MEMP_NUM_TCP_SEG 32
#define MEMP_NUM_ARP_QUEUE 10
#define PBUF_POOL_SIZE 24
#define LWIP_ARP 1
#define LWIP_ETHERNET 1
#define LWIP_ICMP 1
#define LWIP_RAW 1
#define TCP_WND (8 * TCP_MSS)
#define TCP_MSS 1460
#define TCP_SND_BUF (8 * TCP_MSS)
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
#define LWIP_NETIF_STATUS_CALLBACK 1
#define LWIP_NETIF_LINK_CALLBACK 1
#define LWIP_NETIF_HOSTNAME 1
#define LWIP_NETCONN 0
#define MEM_STATS 0
#define SYS_STATS 0
#define MEMP_STATS 0
#define LINK_STATS 0
// #define ETH_PAD_SIZE 2
#define LWIP_CHKSUM_ALGORITHM 3
#define LWIP_DHCP 0
#define LWIP_IPV4 0
#define LWIP_IPV6 1
#define LWIP_IPV6_AUTOCONFIG 1
#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS 2
#define LWIP_IPV6_FORWARD 1
#define LWIP_TCP 0
#define LWIP_UDP 1
#define LWIP_DNS 1
#define LWIP_TCP_KEEPALIVE 0
#define LWIP_NETIF_TX_SINGLE_PBUF 1
#define DHCP_DOES_ARP_CHECK 0
#define LWIP_DHCP_DOES_ACD_CHECK 0
#define DNS_TABLE_SIZE 1
#ifndef NDEBUG
#define LWIP_DEBUG 1
#define LWIP_STATS 1
#define LWIP_STATS_DISPLAY 1
#endif
#define ETHARP_DEBUG LWIP_DBG_OFF
#define NETIF_DEBUG LWIP_DBG_OFF
#define PBUF_DEBUG LWIP_DBG_OFF
#define API_LIB_DEBUG LWIP_DBG_OFF
#define API_MSG_DEBUG LWIP_DBG_OFF
#define SOCKETS_DEBUG LWIP_DBG_OFF
#define ICMP_DEBUG LWIP_DBG_OFF
#define INET_DEBUG LWIP_DBG_OFF
#define IP_DEBUG LWIP_DBG_OFF
#define IP_REASS_DEBUG LWIP_DBG_OFF
#define RAW_DEBUG LWIP_DBG_OFF
#define MEM_DEBUG LWIP_DBG_OFF
#define MEMP_DEBUG LWIP_DBG_OFF
#define SYS_DEBUG LWIP_DBG_OFF
#define TCP_DEBUG LWIP_DBG_OFF
#define TCP_INPUT_DEBUG LWIP_DBG_OFF
#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
#define TCP_RTO_DEBUG LWIP_DBG_OFF
#define TCP_CWND_DEBUG LWIP_DBG_OFF
#define TCP_WND_DEBUG LWIP_DBG_OFF
#define TCP_FR_DEBUG LWIP_DBG_OFF
#define TCP_QLEN_DEBUG LWIP_DBG_OFF
#define TCP_RST_DEBUG LWIP_DBG_OFF
#define UDP_DEBUG LWIP_DBG_ON
#define TCPIP_DEBUG LWIP_DBG_OFF
#define PPP_DEBUG LWIP_DBG_OFF
#define SLIP_DEBUG LWIP_DBG_OFF
#define DHCP_DEBUG LWIP_DBG_OFF
#endif /* __LWIPOPTS_H__ */
pi-pico-wireguard-lwip/src/wireguard-platform.c
#include "wireguard-platform.h"
#include "pico/time.h"
#include "pico/rand.h"
#include <pico/types.h>
#include <stdbool.h>
#include <stdint.h>
#include "crypto.h"
uint32_t wireguard_sys_now() {
absolute_time_t time = get_absolute_time();
return to_ms_since_boot(time);
}
void wireguard_random_bytes(void *bytes, size_t size) {
size_t i = 0;
while (i < size) {
size_t remaining = size - i;
if (remaining >= 128 / 8) {
get_rand_128(bytes + i);
i += 128 / 8;
} else if (remaining >= 64 / 8) {
((uint64_t *) bytes)[i] = get_rand_64();
i += 64 / 8;
} else if (remaining >= 32 / 8){
((uint32_t *) bytes)[i] = get_rand_32();
i += 32 / 8;
} else {
char buf[4];
*((uint32_t *) buf) = get_rand_32();
for (; remaining > 0; remaining--) {
((char *)bytes)[size - 1 - remaining] = buf[remaining - 1];
}
}
}
}
void wireguard_tai64n_now(uint8_t *output) {
// See https://cr.yp.to/libtai/tai64.html
// 64 bit seconds from 1970 = 8 bytes
// 32 bit nano seconds from current second
absolute_time_t now = get_absolute_time();
uint64_t millis = absolute_time_diff_us(nil_time, now);
// Split into seconds offset + nanos
uint64_t seconds = 0x400000000000000aULL + (millis / 1000);
uint32_t nanos = (millis % 1000) * 1000;
U64TO8_BIG(output + 0, seconds);
U32TO8_BIG(output + 8, nanos);
}
bool wireguard_is_under_load() {
return false;
}
pi-pico-wireguard-lwip/src/wireguard-platform.h
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
#ifndef _WIREGUARD_PLATFORM_H_
#define _WIREGUARD_PLATFORM_H_
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
// Peers are allocated statically inside the device structure to avoid malloc
#define WIREGUARD_MAX_PEERS 1
#define WIREGUARD_MAX_SRC_IPS 2
// Per device limit on accepting (valid) initiation requests - per peer
#define MAX_INITIATIONS_PER_SECOND (2)
//
// Your platform integration needs to provide implementations of these functions
//
// The number of milliseconds since system boot - for LwIP systems this could be sys_now()
uint32_t wireguard_sys_now();
// Fill the supplied buffer with random data - random data is used for generating new session keys periodically
void wireguard_random_bytes(void *bytes, size_t size);
// Get the current time in tai64n format - 8 byte seconds, 4 byte nano sub-second - see https://cr.yp.to/libtai/tai64.html for details
// Output buffer passed is 12 bytes
// The Wireguard implementation doesn't strictly need this to be a time, but instead an increasing value
// The remote end of the Wireguard tunnel will use this value in handshake replay detection
void wireguard_tai64n_now(uint8_t *output);
// Is the system under load - i.e. should we generate cookie reply message in response to initiation messages
bool wireguard_is_under_load();
#endif /* _WIREGUARD_PLATFORM_H_ */
pi-pico-wireguard-lwip/src/wireguard.c
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
#include "wireguard.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "crypto.h"
// For HMAC calculation
#define WIREGUARD_BLAKE2S_BLOCK_SIZE (64)
// 5.4 Messages
// Constants
static const uint8_t CONSTRUCTION[37] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"; // The UTF-8 string literal "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s", 37 bytes of output
static const uint8_t IDENTIFIER[34] = "WireGuard v1 zx2c4 Jason@zx2c4.com"; // The UTF-8 string literal "WireGuard v1 zx2c4 Jason@zx2c4.com", 34 bytes of output
static const uint8_t LABEL_MAC1[8] = "mac1----"; // Label-Mac1 The UTF-8 string literal "mac1----", 8 bytes of output.
static const uint8_t LABEL_COOKIE[8] = "cookie--"; // Label-Cookie The UTF-8 string literal "cookie--", 8 bytes of output
static const char *base64_lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const uint8_t zero_key[WIREGUARD_PUBLIC_KEY_LEN] = { 0 };
// Calculated in wireguard_init
static uint8_t construction_hash[WIREGUARD_HASH_LEN];
static uint8_t identifier_hash[WIREGUARD_HASH_LEN];
void wireguard_init() {
wireguard_blake2s_ctx ctx;
// Pre-calculate chaining key hash
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);
wireguard_blake2s_update(&ctx, CONSTRUCTION, sizeof(CONSTRUCTION));
wireguard_blake2s_final(&ctx, construction_hash);
// Pre-calculate initial handshake hash - uses construction_hash calculated above
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);
wireguard_blake2s_update(&ctx, construction_hash, sizeof(construction_hash));
wireguard_blake2s_update(&ctx, IDENTIFIER, sizeof(IDENTIFIER));
wireguard_blake2s_final(&ctx, identifier_hash);
}
struct wireguard_peer *peer_alloc(struct wireguard_device *device) {
struct wireguard_peer *result = NULL;
struct wireguard_peer *tmp;
int x;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
tmp = &device->peers[x];
if (!tmp->valid) {
result = tmp;
break;
}
}
return result;
}
struct wireguard_peer *peer_lookup_by_pubkey(struct wireguard_device *device, uint8_t *public_key) {
struct wireguard_peer *result = NULL;
struct wireguard_peer *tmp;
int x;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
tmp = &device->peers[x];
if (tmp->valid) {
if (memcmp(tmp->public_key, public_key, WIREGUARD_PUBLIC_KEY_LEN) == 0) {
result = tmp;
break;
}
}
}
return result;
}
uint8_t wireguard_peer_index(struct wireguard_device *device, struct wireguard_peer *peer) {
uint8_t result = 0xFF;
uint8_t x;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
if (peer == &device->peers[x]) {
result = x;
break;
}
}
return result;
}
struct wireguard_peer *peer_lookup_by_peer_index(struct wireguard_device *device, uint8_t peer_index) {
struct wireguard_peer *result = NULL;
if (peer_index < WIREGUARD_MAX_PEERS) {
if (device->peers[peer_index].valid) {
result = &device->peers[peer_index];
}
}
return result;
}
struct wireguard_peer *peer_lookup_by_receiver(struct wireguard_device *device, uint32_t receiver) {
struct wireguard_peer *result = NULL;
struct wireguard_peer *tmp;
int x;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
tmp = &device->peers[x];
if (tmp->valid) {
if ((tmp->curr_keypair.valid && (tmp->curr_keypair.local_index == receiver)) ||
(tmp->next_keypair.valid && (tmp->next_keypair.local_index == receiver)) ||
(tmp->prev_keypair.valid && (tmp->prev_keypair.local_index == receiver))
) {
result = tmp;
break;
}
}
}
return result;
}
struct wireguard_peer *peer_lookup_by_handshake(struct wireguard_device *device, uint32_t receiver) {
struct wireguard_peer *result = NULL;
struct wireguard_peer *tmp;
int x;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
tmp = &device->peers[x];
if (tmp->valid) {
if (tmp->handshake.valid && tmp->handshake.initiator && (tmp->handshake.local_index == receiver)) {
result = tmp;
break;
}
}
}
return result;
}
bool wireguard_expired(uint32_t created_millis, uint32_t valid_seconds) {
uint32_t diff = wireguard_sys_now() - created_millis;
return (diff >= (valid_seconds * 1000));
}
static void generate_cookie_secret(struct wireguard_device *device) {
wireguard_random_bytes(device->cookie_secret, WIREGUARD_HASH_LEN);
device->cookie_secret_millis = wireguard_sys_now();
}
static void generate_peer_cookie(struct wireguard_device *device, uint8_t *cookie, uint8_t *source_addr_port, size_t source_length) {
wireguard_blake2s_ctx ctx;
if (wireguard_expired(device->cookie_secret_millis, COOKIE_SECRET_MAX_AGE)) {
// Generate new random bytes
generate_cookie_secret(device);
}
// Mac(key, input) Keyed-Blake2s(key, input, 16), the keyed MAC variant of the BLAKE2s hash function, returning 16 bytes of output
wireguard_blake2s_init(&ctx, WIREGUARD_COOKIE_LEN, device->cookie_secret, WIREGUARD_HASH_LEN);
// 5.4.7 Under Load: Cookie Reply Message
// Mix in the IP address and port - have the IP layer pass this in as byte array to avoid using Lwip specific APIs in this module
if ((source_addr_port) && (source_length > 0)) {
wireguard_blake2s_update(&ctx, source_addr_port, source_length);
}
wireguard_blake2s_final(&ctx, cookie);
}
static void wireguard_mac(uint8_t *dst, const void *message, size_t len, const uint8_t *key, size_t keylen) {
wireguard_blake2s(dst, WIREGUARD_COOKIE_LEN, key, keylen, message, len);
}
static void wireguard_mac_key(uint8_t *key, const uint8_t *public_key, const uint8_t *label, size_t label_len) {
blake2s_ctx ctx;
blake2s_init(&ctx, WIREGUARD_SESSION_KEY_LEN, NULL, 0);
blake2s_update(&ctx, label, label_len);
blake2s_update(&ctx, public_key, WIREGUARD_PUBLIC_KEY_LEN);
blake2s_final(&ctx, key);
}
static void wireguard_mix_hash(uint8_t *hash, const uint8_t *src, size_t src_len) {
wireguard_blake2s_ctx ctx;
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);
wireguard_blake2s_update(&ctx, hash, WIREGUARD_HASH_LEN);
wireguard_blake2s_update(&ctx, src, src_len);
wireguard_blake2s_final(&ctx, hash);
}
static void wireguard_hmac(uint8_t *digest, const uint8_t *key, size_t key_len, const uint8_t *text, size_t text_len) {
// Adapted from appendix example in RFC2104 to use BLAKE2S instead of MD5 - https://tools.ietf.org/html/rfc2104
wireguard_blake2s_ctx ctx;
uint8_t k_ipad[WIREGUARD_BLAKE2S_BLOCK_SIZE]; // inner padding - key XORd with ipad
uint8_t k_opad[WIREGUARD_BLAKE2S_BLOCK_SIZE]; // outer padding - key XORd with opad
uint8_t tk[WIREGUARD_HASH_LEN];
int i;
// if key is longer than BLAKE2S_BLOCK_SIZE bytes reset it to key=BLAKE2S(key)
if (key_len > WIREGUARD_BLAKE2S_BLOCK_SIZE) {
wireguard_blake2s_ctx tctx;
wireguard_blake2s_init(&tctx, WIREGUARD_HASH_LEN, NULL, 0);
wireguard_blake2s_update(&tctx, key, key_len);
wireguard_blake2s_final(&tctx, tk);
key = tk;
key_len = WIREGUARD_HASH_LEN;
}
// the HMAC transform looks like:
// HASH(K XOR opad, HASH(K XOR ipad, text))
// where K is an n byte key
// ipad is the byte 0x36 repeated BLAKE2S_BLOCK_SIZE times
// opad is the byte 0x5c repeated BLAKE2S_BLOCK_SIZE times
// and text is the data being protected
memset(k_ipad, 0, sizeof(k_ipad));
memset(k_opad, 0, sizeof(k_opad));
memcpy(k_ipad, key, key_len);
memcpy(k_opad, key, key_len);
// XOR key with ipad and opad values
for (i=0; i < WIREGUARD_BLAKE2S_BLOCK_SIZE; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
// perform inner HASH
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0); // init context for 1st pass
wireguard_blake2s_update(&ctx, k_ipad, WIREGUARD_BLAKE2S_BLOCK_SIZE); // start with inner pad
wireguard_blake2s_update(&ctx, text, text_len); // then text of datagram
wireguard_blake2s_final(&ctx, digest); // finish up 1st pass
// perform outer HASH
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0); // init context for 2nd pass
wireguard_blake2s_update(&ctx, k_opad, WIREGUARD_BLAKE2S_BLOCK_SIZE); // start with outer pad
wireguard_blake2s_update(&ctx, digest, WIREGUARD_HASH_LEN); // then results of 1st hash
wireguard_blake2s_final(&ctx, digest); // finish up 2nd pass
}
static void wireguard_kdf1(uint8_t *tau1, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {
uint8_t tau0[WIREGUARD_HASH_LEN];
uint8_t output[WIREGUARD_HASH_LEN + 1];
// tau0 = Hmac(key, input)
wireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);
// tau1 := Hmac(tau0, 0x1)
output[0] = 1;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);
memcpy(tau1, output, WIREGUARD_HASH_LEN);
// Wipe intermediates
crypto_zero(tau0, sizeof(tau0));
crypto_zero(output, sizeof(output));
}
static void wireguard_kdf2(uint8_t *tau1, uint8_t *tau2, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {
uint8_t tau0[WIREGUARD_HASH_LEN];
uint8_t output[WIREGUARD_HASH_LEN + 1];
// tau0 = Hmac(key, input)
wireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);
// tau1 := Hmac(tau0, 0x1)
output[0] = 1;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);
memcpy(tau1, output, WIREGUARD_HASH_LEN);
// tau2 := Hmac(tau0,tau1 || 0x2)
output[WIREGUARD_HASH_LEN] = 2;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);
memcpy(tau2, output, WIREGUARD_HASH_LEN);
// Wipe intermediates
crypto_zero(tau0, sizeof(tau0));
crypto_zero(output, sizeof(output));
}
static void wireguard_kdf3(uint8_t *tau1, uint8_t *tau2, uint8_t *tau3, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {
uint8_t tau0[WIREGUARD_HASH_LEN];
uint8_t output[WIREGUARD_HASH_LEN + 1];
// tau0 = Hmac(key, input)
wireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);
// tau1 := Hmac(tau0, 0x1)
output[0] = 1;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);
memcpy(tau1, output, WIREGUARD_HASH_LEN);
// tau2 := Hmac(tau0,tau1 || 0x2)
output[WIREGUARD_HASH_LEN] = 2;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);
memcpy(tau2, output, WIREGUARD_HASH_LEN);
// tau3 := Hmac(tau0,tau1,tau2 || 0x3)
output[WIREGUARD_HASH_LEN] = 3;
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);
memcpy(tau3, output, WIREGUARD_HASH_LEN);
// Wipe intermediates
crypto_zero(tau0, sizeof(tau0));
crypto_zero(output, sizeof(output));
}
bool wireguard_check_replay(struct wireguard_keypair *keypair, uint64_t seq) {
// Implementation of packet replay window - as per RFC2401
// Adapted from code in Appendix C at https://tools.ietf.org/html/rfc2401
uint32_t diff;
bool result = false;
size_t ReplayWindowSize = sizeof(keypair->replay_bitmap) * CHAR_BIT; // 32 bits
// WireGuard data packet counter starts from 0 but algorithm expects packet numbers to start from 1
seq++;
if (seq != 0) {
if (seq > keypair->replay_counter) {
// new larger sequence number
diff = seq - keypair->replay_counter;
if (diff < ReplayWindowSize) {
// In window
keypair->replay_bitmap <<= diff;
// set bit for this packet
keypair->replay_bitmap |= 1;
} else {
// This packet has a "way larger"
keypair->replay_bitmap = 1;
}
keypair->replay_counter = seq;
// larger is good
result = true;
} else {
diff = keypair->replay_counter - seq;
if (diff < ReplayWindowSize) {
if (keypair->replay_bitmap & ((uint32_t)1 << diff)) {
// already seen
} else {
// mark as seen
keypair->replay_bitmap |= ((uint32_t)1 << diff);
// out of order but good
result = true;
}
} else {
// too old or wrapped
}
}
} else {
// first == 0 or wrapped
}
return result;
}
struct wireguard_keypair *get_peer_keypair_for_idx(struct wireguard_peer *peer, uint32_t idx) {
if (peer->curr_keypair.valid && peer->curr_keypair.local_index == idx) {
return &peer->curr_keypair;
} else if (peer->next_keypair.valid && peer->next_keypair.local_index == idx) {
return &peer->next_keypair;
} else if (peer->prev_keypair.valid && peer->prev_keypair.local_index == idx) {
return &peer->prev_keypair;
}
return NULL;
}
static uint32_t wireguard_generate_unique_index(struct wireguard_device *device) {
// We need a random 32-bit number but make sure it's not already been used in the context of this device
uint32_t result;
uint8_t buf[4];
int x;
struct wireguard_peer *peer;
bool existing;
do {
do {
wireguard_random_bytes(buf, 4);
result = U8TO32_LITTLE(buf);
} while ((result == 0) || (result == 0xFFFFFFFF)); // Don't allow 0 or 0xFFFFFFFF as valid values
existing = false;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
peer = &device->peers[x];
existing = (result == peer->curr_keypair.local_index) ||
(result == peer->prev_keypair.local_index) ||
(result == peer->next_keypair.local_index) ||
(result == peer->handshake.local_index);
}
} while (existing);
return result;
}
static void wireguard_clamp_private_key(uint8_t *key) {
key[0] &= 248;
key[31] = (key[31] & 127) | 64;
}
static void wireguard_generate_private_key(uint8_t *key) {
wireguard_random_bytes(key, WIREGUARD_PRIVATE_KEY_LEN);
wireguard_clamp_private_key(key);
}
static bool wireguard_generate_public_key(uint8_t *public_key, const uint8_t *private_key) {
static const uint8_t basepoint[WIREGUARD_PUBLIC_KEY_LEN] = { 9 };
bool result = false;
if (memcmp(private_key, zero_key, WIREGUARD_PUBLIC_KEY_LEN) != 0) {
result = (wireguard_x25519(public_key, private_key, basepoint) == 0);
}
return result;
}
bool wireguard_check_mac1(struct wireguard_device *device, const uint8_t *data, size_t len, const uint8_t *mac1) {
bool result = false;
uint8_t calculated[WIREGUARD_COOKIE_LEN];
wireguard_mac(calculated, data, len, device->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);
if (crypto_equal(calculated, mac1, WIREGUARD_COOKIE_LEN)) {
result = true;
}
return result;
}
bool wireguard_check_mac2(struct wireguard_device *device, const uint8_t *data, size_t len, uint8_t *source_addr_port, size_t source_length, const uint8_t *mac2) {
bool result = false;
uint8_t cookie[WIREGUARD_COOKIE_LEN];
uint8_t calculated[WIREGUARD_COOKIE_LEN];
generate_peer_cookie(device, cookie, source_addr_port, source_length);
wireguard_mac(calculated, data, len, cookie, WIREGUARD_COOKIE_LEN);
if (crypto_equal(calculated, mac2, WIREGUARD_COOKIE_LEN)) {
result = true;
}
return result;
}
void keypair_destroy(struct wireguard_keypair *keypair) {
crypto_zero(keypair, sizeof(struct wireguard_keypair));
keypair->valid = false;
}
void keypair_update(struct wireguard_peer *peer, struct wireguard_keypair *received_keypair) {
bool key_is_next = (received_keypair == &peer->next_keypair);
if (key_is_next) {
peer->prev_keypair = peer->curr_keypair;
peer->curr_keypair = peer->next_keypair;
keypair_destroy(&peer->next_keypair);
}
}
static void add_new_keypair(struct wireguard_peer *peer, struct wireguard_keypair new_keypair) {
if (new_keypair.initiator) {
if (peer->next_keypair.valid) {
peer->prev_keypair = peer->next_keypair;
keypair_destroy(&peer->next_keypair);
} else {
peer->prev_keypair = peer->curr_keypair;
}
peer->curr_keypair = new_keypair;
} else {
peer->next_keypair = new_keypair;
keypair_destroy(&peer->prev_keypair);
}
}
void wireguard_start_session(struct wireguard_peer *peer, bool initiator) {
struct wireguard_handshake *handshake = &peer->handshake;
struct wireguard_keypair new_keypair;
crypto_zero(&new_keypair, sizeof(struct wireguard_keypair));
new_keypair.initiator = initiator;
new_keypair.local_index = handshake->local_index;
new_keypair.remote_index = handshake->remote_index;
new_keypair.keypair_millis = wireguard_sys_now();
new_keypair.sending_valid = true;
new_keypair.receiving_valid = true;
// 5.4.5 Transport Data Key Derivation
// (Tsendi = Trecvr, Trecvi = Tsendr) := Kdf2(Ci = Cr,E)
if (new_keypair.initiator) {
wireguard_kdf2(new_keypair.sending_key, new_keypair.receiving_key, handshake->chaining_key, NULL, 0);
} else {
wireguard_kdf2(new_keypair.receiving_key, new_keypair.sending_key, handshake->chaining_key, NULL, 0);
}
new_keypair.replay_bitmap = 0;
new_keypair.replay_counter = 0;
new_keypair.last_tx = 0;
new_keypair.last_rx = 0; // No packets received yet
new_keypair.valid = true;
// Eprivi = Epubi = Eprivr = Epubr = Ci = Cr := E
crypto_zero(handshake->ephemeral_private, WIREGUARD_PUBLIC_KEY_LEN);
crypto_zero(handshake->remote_ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
crypto_zero(handshake->hash, WIREGUARD_HASH_LEN);
crypto_zero(handshake->chaining_key, WIREGUARD_HASH_LEN);
handshake->remote_index = 0;
handshake->local_index = 0;
handshake->valid = false;
add_new_keypair(peer, new_keypair);
}
uint8_t wireguard_get_message_type(const uint8_t *data, size_t len) {
uint8_t result = MESSAGE_INVALID;
if (len >= 4) {
if ((data[1] == 0) && (data[2] == 0) && (data[3] == 0)) {
switch (data[0]) {
case MESSAGE_HANDSHAKE_INITIATION:
if (len == sizeof(struct message_handshake_initiation)) {
result = MESSAGE_HANDSHAKE_INITIATION;
}
break;
case MESSAGE_HANDSHAKE_RESPONSE:
if (len == sizeof(struct message_handshake_response)) {
result = MESSAGE_HANDSHAKE_RESPONSE;
}
break;
case MESSAGE_COOKIE_REPLY:
if (len == sizeof(struct message_cookie_reply)) {
result = MESSAGE_COOKIE_REPLY;
}
break;
case MESSAGE_TRANSPORT_DATA:
if (len >= sizeof(struct message_transport_data) + WIREGUARD_AUTHTAG_LEN) {
result = MESSAGE_TRANSPORT_DATA;
}
break;
default:
break;
}
}
}
return result;
}
struct wireguard_peer *wireguard_process_initiation_message(struct wireguard_device *device, struct message_handshake_initiation *msg) {
struct wireguard_peer *ret_peer = NULL;
struct wireguard_peer *peer = NULL;
struct wireguard_handshake *handshake;
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
uint8_t chaining_key[WIREGUARD_HASH_LEN];
uint8_t hash[WIREGUARD_HASH_LEN];
uint8_t s[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t e[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t t[WIREGUARD_TAI64N_LEN];
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
uint32_t now;
bool rate_limit;
bool replay;
// We are the responder, other end is the initiator
// Ci := Hash(Construction) (precalculated hash)
memcpy(chaining_key, construction_hash, WIREGUARD_HASH_LEN);
// Hi := Hash(Ci || Identifier
memcpy(hash, identifier_hash, WIREGUARD_HASH_LEN);
// Hi := Hash(Hi || Spubr)
wireguard_mix_hash(hash, device->public_key, WIREGUARD_PUBLIC_KEY_LEN);
// Ci := Kdf1(Ci, Epubi)
wireguard_kdf1(chaining_key, chaining_key, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// msg.ephemeral := Epubi
memcpy(e, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Hi := Hash(Hi || msg.ephemeral)
wireguard_mix_hash(hash, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Calculate DH(Eprivi,Spubr)
wireguard_x25519(dh_calculation, device->private_key, e);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
// (Ci,k) := Kdf2(Ci,DH(Eprivi,Spubr))
wireguard_kdf2(chaining_key, key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// msg.static := AEAD(k, 0, Spubi, Hi)
if (wireguard_aead_decrypt(s, msg->enc_static, sizeof(msg->enc_static), hash, WIREGUARD_HASH_LEN, 0, key)) {
// Hi := Hash(Hi || msg.static)
wireguard_mix_hash(hash, msg->enc_static, sizeof(msg->enc_static));
peer = peer_lookup_by_pubkey(device, s);
if (peer) {
handshake = &peer->handshake;
// (Ci,k) := Kdf2(Ci,DH(Sprivi,Spubr))
wireguard_kdf2(chaining_key, key, chaining_key, peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);
// msg.timestamp := AEAD(k, 0, Timestamp(), Hi)
if (wireguard_aead_decrypt(t, msg->enc_timestamp, sizeof(msg->enc_timestamp), hash, WIREGUARD_HASH_LEN, 0, key)) {
// Hi := Hash(Hi || msg.timestamp)
wireguard_mix_hash(hash, msg->enc_timestamp, sizeof(msg->enc_timestamp));
now = wireguard_sys_now();
// Check that timestamp is increasing and we haven't had too many initiations (should only get one per peer every 5 seconds max?)
replay = (memcmp(t, peer->greatest_timestamp, WIREGUARD_TAI64N_LEN) <= 0); // tai64n is big endian so we can use memcmp to compare
rate_limit = (peer->last_initiation_rx - now) < (1000 / MAX_INITIATIONS_PER_SECOND);
if (!replay && !rate_limit) {
// Success! Copy everything to peer
peer->last_initiation_rx = now;
if (memcmp(t, peer->greatest_timestamp, WIREGUARD_TAI64N_LEN) > 0) {
memcpy(peer->greatest_timestamp, t, WIREGUARD_TAI64N_LEN);
// TODO: Need to notify if the higher layers want to persist latest timestamp/nonce somewhere
}
memcpy(handshake->remote_ephemeral, e, WIREGUARD_PUBLIC_KEY_LEN);
memcpy(handshake->hash, hash, WIREGUARD_HASH_LEN);
memcpy(handshake->chaining_key, chaining_key, WIREGUARD_HASH_LEN);
handshake->remote_index = msg->sender;
handshake->valid = true;
handshake->initiator = false;
ret_peer = peer;
} else {
// Ignore
}
} else {
// Failed to decrypt
}
} else {
// peer not found
}
} else {
// Failed to decrypt
}
} else {
// Bad X25519
}
crypto_zero(key, sizeof(key));
crypto_zero(hash, sizeof(hash));
crypto_zero(chaining_key, sizeof(chaining_key));
crypto_zero(dh_calculation, sizeof(dh_calculation));
return ret_peer;
}
bool wireguard_process_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *src) {
struct wireguard_handshake *handshake = &peer->handshake;
bool result = false;
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
uint8_t hash[WIREGUARD_HASH_LEN];
uint8_t chaining_key[WIREGUARD_HASH_LEN];
uint8_t e[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t ephemeral_private[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t static_private[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t preshared_key[WIREGUARD_SESSION_KEY_LEN];
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t tau[WIREGUARD_PUBLIC_KEY_LEN];
if (handshake->valid && handshake->initiator) {
memcpy(hash, handshake->hash, WIREGUARD_HASH_LEN);
memcpy(chaining_key, handshake->chaining_key, WIREGUARD_HASH_LEN);
memcpy(ephemeral_private, handshake->ephemeral_private, WIREGUARD_PUBLIC_KEY_LEN);
memcpy(preshared_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
// (Eprivr, Epubr) := DH-Generate()
// Not required
// Cr := Kdf1(Cr,Epubr)
wireguard_kdf1(chaining_key, chaining_key, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// msg.ephemeral := Epubr
memcpy(e, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Hr := Hash(Hr || msg.ephemeral)
wireguard_mix_hash(hash, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Cr := Kdf1(Cr, DH(Eprivr, Epubi))
// Calculate DH(Eprivr, Epubi)
wireguard_x25519(dh_calculation, ephemeral_private, e);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
wireguard_kdf1(chaining_key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// Cr := Kdf1(Cr, DH(Eprivr, Spubi))
// CalculateDH(Eprivr, Spubi)
wireguard_x25519(dh_calculation, device->private_key, e);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
wireguard_kdf1(chaining_key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// (Cr, t, k) := Kdf3(Cr, Q)
wireguard_kdf3(chaining_key, tau, key, chaining_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
// Hr := Hash(Hr | t)
wireguard_mix_hash(hash, tau, WIREGUARD_HASH_LEN);
// msg.empty := AEAD(k, 0, E, Hr)
if (wireguard_aead_decrypt(NULL, src->enc_empty, sizeof(src->enc_empty), hash, WIREGUARD_HASH_LEN, 0, key)) {
// Hr := Hash(Hr | msg.empty)
// Not required as discarded
//Copy details to handshake
memcpy(handshake->remote_ephemeral, e, WIREGUARD_HASH_LEN);
memcpy(handshake->hash, hash, WIREGUARD_HASH_LEN);
memcpy(handshake->chaining_key, chaining_key, WIREGUARD_HASH_LEN);
handshake->remote_index = src->sender;
result = true;
} else {
// Decrypt failed
}
} else {
// X25519 fail
}
} else {
// X25519 fail
}
}
crypto_zero(key, sizeof(key));
crypto_zero(hash, sizeof(hash));
crypto_zero(chaining_key, sizeof(chaining_key));
crypto_zero(ephemeral_private, sizeof(ephemeral_private));
crypto_zero(static_private, sizeof(static_private));
crypto_zero(preshared_key, sizeof(preshared_key));
crypto_zero(tau, sizeof(tau));
return result;
}
bool wireguard_process_cookie_message(struct wireguard_device *device, struct wireguard_peer *peer, struct message_cookie_reply *src) {
uint8_t cookie[WIREGUARD_COOKIE_LEN];
bool result = false;
if (peer->handshake_mac1_valid) {
result = wireguard_xaead_decrypt(cookie, src->enc_cookie, sizeof(src->enc_cookie), peer->handshake_mac1, WIREGUARD_COOKIE_LEN, src->nonce, peer->label_cookie_key);
if (result) {
// 5.4.7 Under Load: Cookie Reply Message
// Upon receiving this message, if it is valid, the only thing the recipient of this message should do is store the cookie along with the time at which it was received
memcpy(peer->cookie, cookie, WIREGUARD_COOKIE_LEN);
peer->cookie_millis = wireguard_sys_now();
peer->handshake_mac1_valid = false;
}
} else {
// We didn't send any initiation packet so we shouldn't be getting a cookie reply!
}
return result;
}
bool wireguard_create_handshake_initiation(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_initiation *dst) {
uint8_t timestamp[WIREGUARD_TAI64N_LEN];
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
bool result = false;
struct wireguard_handshake *handshake = &peer->handshake;
memset(dst, 0, sizeof(struct message_handshake_initiation));
// Ci := Hash(Construction) (precalculated hash)
memcpy(handshake->chaining_key, construction_hash, WIREGUARD_HASH_LEN);
// Hi := Hash(Ci || Identifier)
memcpy(handshake->hash, identifier_hash, WIREGUARD_HASH_LEN);
// Hi := Hash(Hi || Spubr)
wireguard_mix_hash(handshake->hash, peer->public_key, WIREGUARD_PUBLIC_KEY_LEN);
// (Eprivi, Epubi) := DH-Generate()
wireguard_generate_private_key(handshake->ephemeral_private);
if (wireguard_generate_public_key(dst->ephemeral, handshake->ephemeral_private)) {
// Ci := Kdf1(Ci, Epubi)
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// msg.ephemeral := Epubi
// Done above - public keys is calculated into dst->ephemeral
// Hi := Hash(Hi || msg.ephemeral)
wireguard_mix_hash(handshake->hash, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Calculate DH(Eprivi,Spubr)
wireguard_x25519(dh_calculation, handshake->ephemeral_private, peer->public_key);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
// (Ci,k) := Kdf2(Ci,DH(Eprivi,Spubr))
wireguard_kdf2(handshake->chaining_key, key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// msg.static := AEAD(k,0,Spubi, Hi)
wireguard_aead_encrypt(dst->enc_static, device->public_key, WIREGUARD_PUBLIC_KEY_LEN, handshake->hash, WIREGUARD_HASH_LEN, 0, key);
// Hi := Hash(Hi || msg.static)
wireguard_mix_hash(handshake->hash, dst->enc_static, sizeof(dst->enc_static));
// (Ci,k) := Kdf2(Ci,DH(Sprivi,Spubr))
// note DH(Sprivi,Spubr) is precomputed per peer
wireguard_kdf2(handshake->chaining_key, key, handshake->chaining_key, peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);
// msg.timestamp := AEAD(k, 0, Timestamp(), Hi)
wireguard_tai64n_now(timestamp);
wireguard_aead_encrypt(dst->enc_timestamp, timestamp, WIREGUARD_TAI64N_LEN, handshake->hash, WIREGUARD_HASH_LEN, 0, key);
// Hi := Hash(Hi || msg.timestamp)
wireguard_mix_hash(handshake->hash, dst->enc_timestamp, sizeof(dst->enc_timestamp));
dst->type = MESSAGE_HANDSHAKE_INITIATION;
dst->sender = wireguard_generate_unique_index(device);
handshake->valid = true;
handshake->initiator = true;
handshake->local_index = dst->sender;
result = true;
}
}
if (result) {
// 5.4.4 Cookie MACs
// msg.mac1 := Mac(Hash(Label-Mac1 || Spubm' ), msgA)
// The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed
wireguard_mac(dst->mac1, dst, (sizeof(struct message_handshake_initiation)-(2*WIREGUARD_COOKIE_LEN)), peer->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);
// if Lm = E or Lm ≥ 120:
if ((peer->cookie_millis == 0) || wireguard_expired(peer->cookie_millis, COOKIE_SECRET_MAX_AGE)) {
// msg.mac2 := 0
crypto_zero(dst->mac2, WIREGUARD_COOKIE_LEN);
} else {
// msg.mac2 := Mac(Lm, msgB)
wireguard_mac(dst->mac2, dst, (sizeof(struct message_handshake_initiation)-(WIREGUARD_COOKIE_LEN)), peer->cookie, WIREGUARD_COOKIE_LEN);
}
}
crypto_zero(key, sizeof(key));
crypto_zero(dh_calculation, sizeof(dh_calculation));
return result;
}
bool wireguard_create_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *dst) {
struct wireguard_handshake *handshake = &peer->handshake;
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t tau[WIREGUARD_HASH_LEN];
bool result = false;
memset(dst, 0, sizeof(struct message_handshake_response));
if (handshake->valid && !handshake->initiator) {
// (Eprivr, Epubr) := DH-Generate()
wireguard_generate_private_key(handshake->ephemeral_private);
if (wireguard_generate_public_key(dst->ephemeral, handshake->ephemeral_private)) {
// Cr := Kdf1(Cr,Epubr)
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// msg.ephemeral := Epubr
// Copied above when generated
// Hr := Hash(Hr || msg.ephemeral)
wireguard_mix_hash(handshake->hash, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
// Cr := Kdf1(Cr, DH(Eprivr, Epubi))
// Calculate DH(Eprivi,Spubr)
wireguard_x25519(dh_calculation, handshake->ephemeral_private, handshake->remote_ephemeral);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// Cr := Kdf1(Cr, DH(Eprivr, Spubi))
// Calculate DH(Eprivi,Spubr)
wireguard_x25519(dh_calculation, handshake->ephemeral_private, peer->public_key);
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
// (Cr, t, k) := Kdf3(Cr, Q)
wireguard_kdf3(handshake->chaining_key, tau, key, handshake->chaining_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
// Hr := Hash(Hr | t)
wireguard_mix_hash(handshake->hash, tau, WIREGUARD_HASH_LEN);
// msg.empty := AEAD(k, 0, E, Hr)
wireguard_aead_encrypt(dst->enc_empty, NULL, 0, handshake->hash, WIREGUARD_HASH_LEN, 0, key);
// Hr := Hash(Hr | msg.empty)
wireguard_mix_hash(handshake->hash, dst->enc_empty, sizeof(dst->enc_empty));
dst->type = MESSAGE_HANDSHAKE_RESPONSE;
dst->receiver = handshake->remote_index;
dst->sender = wireguard_generate_unique_index(device);
// Update handshake object too
handshake->local_index = dst->sender;
result = true;
} else {
// Bad x25519
}
} else {
// Bad x25519
}
} else {
// Failed to generate DH
}
}
if (result) {
// 5.4.4 Cookie MACs
// msg.mac1 := Mac(Hash(Label-Mac1 || Spubm' ), msgA)
// The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed
wireguard_mac(dst->mac1, dst, (sizeof(struct message_handshake_response)-(2*WIREGUARD_COOKIE_LEN)), peer->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);
// if Lm = E or Lm ≥ 120:
if ((peer->cookie_millis == 0) || wireguard_expired(peer->cookie_millis, COOKIE_SECRET_MAX_AGE)) {
// msg.mac2 := 0
crypto_zero(dst->mac2, WIREGUARD_COOKIE_LEN);
} else {
// msg.mac2 := Mac(Lm, msgB)
wireguard_mac(dst->mac2, dst, (sizeof(struct message_handshake_response)-(WIREGUARD_COOKIE_LEN)), peer->cookie, WIREGUARD_COOKIE_LEN);
}
}
crypto_zero(key, sizeof(key));
crypto_zero(dh_calculation, sizeof(dh_calculation));
crypto_zero(tau, sizeof(tau));
return result;
}
void wireguard_create_cookie_reply(struct wireguard_device *device, struct message_cookie_reply *dst, const uint8_t *mac1, uint32_t index, uint8_t *source_addr_port, size_t source_length) {
uint8_t cookie[WIREGUARD_COOKIE_LEN];
crypto_zero(dst, sizeof(struct message_cookie_reply));
dst->type = MESSAGE_COOKIE_REPLY;
dst->receiver = index;
wireguard_random_bytes(dst->nonce, COOKIE_NONCE_LEN);
generate_peer_cookie(device, cookie, source_addr_port, source_length);
wireguard_xaead_encrypt(dst->enc_cookie, cookie, WIREGUARD_COOKIE_LEN, mac1, WIREGUARD_COOKIE_LEN, dst->nonce, device->label_cookie_key);
}
bool wireguard_peer_init(struct wireguard_device *device, struct wireguard_peer *peer, const uint8_t *public_key, const uint8_t *preshared_key) {
// Clear out structure
memset(peer, 0, sizeof(struct wireguard_peer));
if (device->valid) {
// Copy across the public key into our peer structure
memcpy(peer->public_key, public_key, WIREGUARD_PUBLIC_KEY_LEN);
if (preshared_key) {
memcpy(peer->preshared_key, preshared_key, WIREGUARD_SESSION_KEY_LEN);
} else {
crypto_zero(peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
}
if (wireguard_x25519(peer->public_key_dh, device->private_key, peer->public_key) == 0) {
// Zero out handshake
memset(&peer->handshake, 0, sizeof(struct wireguard_handshake));
peer->handshake.valid = false;
// Zero out any cookie info - we haven't received one yet
peer->cookie_millis = 0;
memset(&peer->cookie, 0, WIREGUARD_COOKIE_LEN);
// Precompute keys to deal with mac1/2 calculation
wireguard_mac_key(peer->label_mac1_key, peer->public_key, LABEL_MAC1, sizeof(LABEL_MAC1));
wireguard_mac_key(peer->label_cookie_key, peer->public_key, LABEL_COOKIE, sizeof(LABEL_COOKIE));
peer->valid = true;
} else {
crypto_zero(peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);
}
}
return peer->valid;
}
bool wireguard_device_init(struct wireguard_device *device, const uint8_t *private_key) {
// Set the private key and calculate public key from it
memcpy(device->private_key, private_key, WIREGUARD_PRIVATE_KEY_LEN);
// Ensure private key is correctly "clamped"
wireguard_clamp_private_key(device->private_key);
device->valid = wireguard_generate_public_key(device->public_key, private_key);
if (device->valid) {
generate_cookie_secret(device);
// 5.4.4 Cookie MACs - The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed.
wireguard_mac_key(device->label_mac1_key, device->public_key, LABEL_MAC1, sizeof(LABEL_MAC1));
// 5.4.7 Under Load: Cookie Reply Message - The value Hash(Label-Cookie || Spubm) above can be pre-computed.
wireguard_mac_key(device->label_cookie_key, device->public_key, LABEL_COOKIE, sizeof(LABEL_COOKIE));
} else {
crypto_zero(device->private_key, WIREGUARD_PRIVATE_KEY_LEN);
}
return device->valid;
}
void wireguard_encrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, struct wireguard_keypair *keypair) {
wireguard_aead_encrypt(dst, src, src_len, NULL, 0, keypair->sending_counter, keypair->sending_key);
keypair->sending_counter++;
}
bool wireguard_decrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, uint64_t counter, struct wireguard_keypair *keypair) {
return wireguard_aead_decrypt(dst, src, src_len, NULL, 0, counter, keypair->receiving_key);
}
bool wireguard_base64_decode(const char *str, uint8_t *out, size_t *outlen) {
uint32_t accum = 0; // We accumulate upto four blocks of 6 bits into this to form 3 bytes output
uint8_t char_count = 0; // How many characters have we processed in this block
int byte_count = 3; // How many bytes are we expecting in current 4 char block
int len = 0; // result length in bytes
bool result = true;
uint8_t bits;
char c;
char *ptr;
int x;
size_t inlen;
if (!str) {
return false;
}
inlen = strlen(str);
for (x = 0; x < inlen; x++) {
c = str[x];
if (c == '=') {
// This is '=' padding at end of string - decrease the number of bytes to write
bits = 0;
byte_count--;
if (byte_count < 0) {
// Too much padding!
result = false;
break;
}
} else {
if (byte_count != 3) {
// Padding only allowed at end - this is a valid byte and we have already seen padding
result = false;
break;
}
ptr = strchr(base64_lookup, c);
if (ptr) {
bits = (uint8_t)((ptr - base64_lookup) & 0x3F);
} else {
// invalid character in input string
result = false;
break;
}
}
accum = (accum << 6) | bits;
char_count++;
if (char_count == 4) {
if (len + byte_count > *outlen) {
// Output buffer overflow
result = false;
break;
}
out[len++] = (uint8_t)((accum >> 16) & 0xFF);
if (byte_count > 1) {
out[len++] = (uint8_t)((accum >> 8) & 0xFF);
}
if (byte_count > 2) {
out[len++] = (uint8_t)(accum & 0xFF);
}
char_count = 0;
accum = 0;
}
}
if (char_count != 0) {
// We require padding to multiple of 3 input length - bytes are missing from output!
result = false;
}
*outlen = len;
return result;
}
bool wireguard_base64_encode(const uint8_t *in, size_t inlen, char *out, size_t *outlen) {
bool result = false;
int read_offset = 0;
int write_offset = 0;
uint8_t byte1, byte2, byte3;
uint32_t tmp;
char c;
size_t len = 4 * ((inlen + 2) / 3);
int padding = (3 - (inlen % 3));
if (padding > 2) padding = 0;
if (*outlen > len) {
while (read_offset < inlen) {
// Read three bytes
byte1 = (read_offset < inlen) ? in[read_offset++] : 0;
byte2 = (read_offset < inlen) ? in[read_offset++] : 0;
byte3 = (read_offset < inlen) ? in[read_offset++] : 0;
// Turn into 24 bit intermediate
tmp = (byte1 << 16) | (byte2 << 8) | (byte3);
// Write out 4 characters each representing 6 bits of input
out[write_offset++] = base64_lookup[(tmp >> 18) & 0x3F];
out[write_offset++] = base64_lookup[(tmp >> 12) & 0x3F];
c = (write_offset < len - padding) ? base64_lookup[(tmp >> 6) & 0x3F] : '=';
out[write_offset++] = c;
c = (write_offset < len - padding) ? base64_lookup[(tmp) & 0x3F] : '=';
out[write_offset++] = c;
}
out[len] = '\0';
*outlen = len;
result = true;
} else {
// Not enough data to put in base64 and null terminate
}
return result;
}
pi-pico-wireguard-lwip/src/wireguard.h
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
#ifndef _WIREGUARD_H_
#define _WIREGUARD_H_
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
// Note: these are only required for definitions in device/peer for netif, udp_pcb, ip_addr_t and u16_t
#include "lwip/netif.h"
#include "lwip/udp.h"
#include "lwip/ip_addr.h"
#include "lwip/arch.h"
// Platform-specific functions that need to be implemented per-platform
#include "wireguard-platform.h"
// tai64n contains 64-bit seconds and 32-bit nano offset (12 bytes)
#define WIREGUARD_TAI64N_LEN (12)
// Auth algorithm is chacha20pol1305 which is 128bit (16 byte) authenticator
#define WIREGUARD_AUTHTAG_LEN (16)
// Hash algorithm is blake2s which makes 32 byte hashes
#define WIREGUARD_HASH_LEN (32)
// Public key algo is curve22519 which uses 32 byte keys
#define WIREGUARD_PUBLIC_KEY_LEN (32)
// Public key algo is curve22519 which uses 32 byte keys
#define WIREGUARD_PRIVATE_KEY_LEN (32)
// Symmetric session keys are chacha20/poly1305 which uses 32 byte keys
#define WIREGUARD_SESSION_KEY_LEN (32)
// Timers / Limits
#define WIREGUARD_COOKIE_LEN (16)
#define COOKIE_SECRET_MAX_AGE (2 * 60)
#define COOKIE_NONCE_LEN (24)
#define REKEY_AFTER_MESSAGES (1ULL << 60)
#define REJECT_AFTER_MESSAGES (0xFFFFFFFFFFFFFFFFULL - (1ULL << 13))
#define REKEY_AFTER_TIME (120)
#define REJECT_AFTER_TIME (180)
#define REKEY_TIMEOUT (5)
#define KEEPALIVE_TIMEOUT (10)
struct wireguard_keypair {
bool valid;
bool initiator; // Did we initiate this session (send the initiation packet rather than sending the response packet)
uint32_t keypair_millis;
uint8_t sending_key[WIREGUARD_SESSION_KEY_LEN];
bool sending_valid;
uint64_t sending_counter;
uint8_t receiving_key[WIREGUARD_SESSION_KEY_LEN];
bool receiving_valid;
uint32_t last_tx;
uint32_t last_rx;
uint32_t replay_bitmap;
uint64_t replay_counter;
uint32_t local_index; // This is the index we generated for our end
uint32_t remote_index; // This is the index on the other end
};
struct wireguard_handshake {
bool valid;
bool initiator;
uint32_t local_index;
uint32_t remote_index;
uint8_t ephemeral_private[WIREGUARD_PRIVATE_KEY_LEN];
uint8_t remote_ephemeral[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t hash[WIREGUARD_HASH_LEN];
uint8_t chaining_key[WIREGUARD_HASH_LEN];
};
struct wireguard_allowed_ip {
bool valid;
ip_addr_t ip;
ip_addr_t mask;
};
struct wireguard_peer {
bool valid; // Is this peer initialised?
bool active; // Should we be actively trying to connect?
// This is the configured IP of the peer (endpoint)
ip_addr_t connect_ip;
u16_t connect_port;
// This is the latest received IP/port
ip_addr_t ip;
u16_t port;
// keep-alive interval in seconds, 0 is disable
uint16_t keepalive_interval;
struct wireguard_allowed_ip allowed_source_ips[WIREGUARD_MAX_SRC_IPS];
uint8_t public_key[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t preshared_key[WIREGUARD_SESSION_KEY_LEN];
// Precomputed DH(Sprivi,Spubr) with device private key, and peer public key
uint8_t public_key_dh[WIREGUARD_PUBLIC_KEY_LEN];
// Session keypairs
struct wireguard_keypair curr_keypair;
struct wireguard_keypair prev_keypair;
struct wireguard_keypair next_keypair;
// 5.1 Silence is a Virtue: The responder keeps track of the greatest timestamp received per peer
uint8_t greatest_timestamp[WIREGUARD_TAI64N_LEN];
// The active handshake that is happening
struct wireguard_handshake handshake;
// Decrypted cookie from the responder
uint32_t cookie_millis;
uint8_t cookie[WIREGUARD_COOKIE_LEN];
// The latest mac1 we sent with initiation
bool handshake_mac1_valid;
uint8_t handshake_mac1[WIREGUARD_COOKIE_LEN];
// Precomputed keys for use in mac validation
uint8_t label_cookie_key[WIREGUARD_SESSION_KEY_LEN];
uint8_t label_mac1_key[WIREGUARD_SESSION_KEY_LEN];
// The last time we received a valid initiation message
uint32_t last_initiation_rx;
// The last time we sent an initiation message to this peer
uint32_t last_initiation_tx;
// last_tx and last_rx of data packets
uint32_t last_tx;
uint32_t last_rx;
// We set this flag on RX/TX of packets if we think that we should initiate a new handshake
bool send_handshake;
};
struct wireguard_device {
// Maybe have a "Device private" member to abstract these?
struct netif *netif;
struct udp_pcb *udp_pcb;
uint8_t public_key[WIREGUARD_PUBLIC_KEY_LEN];
uint8_t private_key[WIREGUARD_PRIVATE_KEY_LEN];
uint8_t cookie_secret[WIREGUARD_HASH_LEN];
uint32_t cookie_secret_millis;
// Precalculated
uint8_t label_cookie_key[WIREGUARD_SESSION_KEY_LEN];
uint8_t label_mac1_key[WIREGUARD_SESSION_KEY_LEN];
// List of peers associated with this device
struct wireguard_peer peers[WIREGUARD_MAX_PEERS];
bool valid;
};
#define MESSAGE_INVALID 0
#define MESSAGE_HANDSHAKE_INITIATION 1
#define MESSAGE_HANDSHAKE_RESPONSE 2
#define MESSAGE_COOKIE_REPLY 3
#define MESSAGE_TRANSPORT_DATA 4
// 5.4.2 First Message: Initiator to Responder
struct message_handshake_initiation {
uint8_t type;
uint8_t reserved[3];
uint32_t sender;
uint8_t ephemeral[32];
uint8_t enc_static[32 + WIREGUARD_AUTHTAG_LEN];
uint8_t enc_timestamp[WIREGUARD_TAI64N_LEN + WIREGUARD_AUTHTAG_LEN];
uint8_t mac1[WIREGUARD_COOKIE_LEN];
uint8_t mac2[WIREGUARD_COOKIE_LEN];
} __attribute__ ((__packed__));
// 5.4.3 Second Message: Responder to Initiator
struct message_handshake_response {
uint8_t type;
uint8_t reserved[3];
uint32_t sender;
uint32_t receiver;
uint8_t ephemeral[32];
uint8_t enc_empty[0 + WIREGUARD_AUTHTAG_LEN];
uint8_t mac1[WIREGUARD_COOKIE_LEN];
uint8_t mac2[WIREGUARD_COOKIE_LEN];
} __attribute__ ((__packed__));
// 5.4.7 Under Load: Cookie Reply Message
struct message_cookie_reply {
uint8_t type;
uint8_t reserved[3];
uint32_t receiver;
uint8_t nonce[COOKIE_NONCE_LEN];
uint8_t enc_cookie[WIREGUARD_COOKIE_LEN + WIREGUARD_AUTHTAG_LEN];
} __attribute__ ((__packed__));
// 5.4.6 Subsequent Messages: Transport Data Messages
struct message_transport_data {
uint8_t type;
uint8_t reserved[3];
uint32_t receiver;
uint8_t counter[8];
// Followed by encrypted data
uint8_t enc_packet[];
} __attribute__ ((__packed__));
// Initialise the WireGuard system - need to call this before anything else
void wireguard_init();
bool wireguard_device_init(struct wireguard_device *device, const uint8_t *private_key);
bool wireguard_peer_init(struct wireguard_device *device, struct wireguard_peer *peer, const uint8_t *public_key, const uint8_t *preshared_key);
struct wireguard_peer *peer_alloc(struct wireguard_device *device);
uint8_t wireguard_peer_index(struct wireguard_device *device, struct wireguard_peer *peer);
struct wireguard_peer *peer_lookup_by_pubkey(struct wireguard_device *device, uint8_t *public_key);
struct wireguard_peer *peer_lookup_by_peer_index(struct wireguard_device *device, uint8_t peer_index);
struct wireguard_peer *peer_lookup_by_receiver(struct wireguard_device *device, uint32_t receiver);
struct wireguard_peer *peer_lookup_by_handshake(struct wireguard_device *device, uint32_t receiver);
void wireguard_start_session(struct wireguard_peer *peer, bool initiator);
void keypair_update(struct wireguard_peer *peer, struct wireguard_keypair *received_keypair);
void keypair_destroy(struct wireguard_keypair *keypair);
struct wireguard_keypair *get_peer_keypair_for_idx(struct wireguard_peer *peer, uint32_t idx);
bool wireguard_check_replay(struct wireguard_keypair *keypair, uint64_t seq);
uint8_t wireguard_get_message_type(const uint8_t *data, size_t len);
struct wireguard_peer *wireguard_process_initiation_message(struct wireguard_device *device, struct message_handshake_initiation *msg);
bool wireguard_process_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *src);
bool wireguard_process_cookie_message(struct wireguard_device *device, struct wireguard_peer *peer, struct message_cookie_reply *src);
bool wireguard_create_handshake_initiation(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_initiation *dst);
bool wireguard_create_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *dst);
void wireguard_create_cookie_reply(struct wireguard_device *device, struct message_cookie_reply *dst, const uint8_t *mac1, uint32_t index, uint8_t *source_addr_port, size_t source_length);
bool wireguard_check_mac1(struct wireguard_device *device, const uint8_t *data, size_t len, const uint8_t *mac1);
bool wireguard_check_mac2(struct wireguard_device *device, const uint8_t *data, size_t len, uint8_t *source_addr_port, size_t source_length, const uint8_t *mac2);
bool wireguard_expired(uint32_t created_millis, uint32_t valid_seconds);
void wireguard_encrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, struct wireguard_keypair *keypair);
bool wireguard_decrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, uint64_t counter, struct wireguard_keypair *keypair);
bool wireguard_base64_decode(const char *str, uint8_t *out, size_t *outlen);
bool wireguard_base64_encode(const uint8_t *in, size_t inlen, char *out, size_t *outlen);
#endif /* _WIREGUARD_H_ */
pi-pico-wireguard-lwip/src/wireguardif.c
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
/*
* Adapting wireguardif.c to work with IPv6.
* (c) Jirka Chráska 2026, <jirka@lixis.cz>
*/
#include "wireguardif.h"
#include <string.h>
#include <stdlib.h>
#include "lwip/netif.h"
#include "lwip/ip.h"
#include "lwip/udp.h"
#include "lwip/mem.h"
#include "lwip/sys.h"
#include "lwip/timeouts.h"
#include "wireguard.h"
#include "crypto.h"
#if LWIP_IPV6
#include "lwip/prot/ip6.h"
#endif
#include <stdio.h> // TODO: Remove
#define WIREGUARDIF_TIMER_MSECS 200
static void update_peer_addr(struct wireguard_peer *peer, const ip_addr_t *addr, u16_t port) {
peer->ip = *addr;
peer->port = port;
}
static struct wireguard_peer *peer_lookup_by_allowed_ip(struct wireguard_device *device, const ip_addr_t *ipaddr)
{
struct wireguard_peer *result = NULL;
struct wireguard_peer *tmp;
int x;
int y;
for (x=0; (!result) && (x < WIREGUARD_MAX_PEERS); x++) {
tmp = &device->peers[x];
if (tmp->valid) {
for (y=0; y < WIREGUARD_MAX_SRC_IPS; y++) {
#if LWIP_IPV6
// printf("peer_lookup_by_allowed_ip: %s =? %s \n", ip6addr_ntoa(ipaddr), ip6addr_ntoa(&tmp->allowed_source_ips[y].ip));
if ((tmp->allowed_source_ips[y].valid) && ip6_addr_netcmp(ipaddr, &tmp->allowed_source_ips[y].ip)) {
#else
if ((tmp->allowed_source_ips[y].valid) && ip_addr_netcmp(ipaddr, &tmp->allowed_source_ips[y].ip, &tmp->allowed_source_ips[y].mask)) {
#endif
result = tmp;
break;
}
}
}
}
return result;
}
static bool wireguardif_can_send_initiation(struct wireguard_peer *peer) {
return ((peer->last_initiation_tx == 0) || (wireguard_expired(peer->last_initiation_tx, REKEY_TIMEOUT)));
}
static err_t wireguardif_peer_output(struct netif *netif, struct pbuf *q, struct wireguard_peer *peer) {
struct wireguard_device *device = (struct wireguard_device *)netif->state;
// Send to last know port, not the connect port
//TODO: Support DSCP and ECN - lwip requires this set on PCB globally, not per packet
return udp_sendto(device->udp_pcb, q, &peer->ip, peer->port);
}
static err_t wireguardif_device_output(struct wireguard_device *device, struct pbuf *q, const ip_addr_t *ipaddr, u16_t port) {
return udp_sendto(device->udp_pcb, q, ipaddr, port);
}
static err_t wireguardif_output_to_peer(struct netif *netif, struct pbuf *q, const ip_addr_t *ipaddr, struct wireguard_peer *peer) {
// The LWIP IP layer wants to send an IP packet out over the interface - we need to encrypt and send it to the peer
struct message_transport_data *hdr;
struct pbuf *pbuf;
err_t result;
size_t unpadded_len;
size_t padded_len;
size_t header_len = 16;
uint8_t *dst;
uint32_t now;
struct wireguard_keypair *keypair = &peer->curr_keypair;
// printf("wireguardif_output_to_peer.\n");
// Note: We may not be able to use the current keypair if we haven't received data, may need to resort to using previous keypair
if (keypair->valid && (!keypair->initiator) && (keypair->last_rx == 0)) {
keypair = &peer->prev_keypair;
}
if (keypair->valid && (keypair->initiator || keypair->last_rx != 0)) {
if (
!wireguard_expired(keypair->keypair_millis, REJECT_AFTER_TIME) &&
(keypair->sending_counter < REJECT_AFTER_MESSAGES)
) {
// Calculate the outgoing packet size - round up to next 16 bytes, add 16 bytes for header
if (q) {
// This is actual transport data
unpadded_len = q->tot_len;
} else {
// This is a keep-alive
unpadded_len = 0;
}
padded_len = (unpadded_len + 15) & 0xFFFFFFF0; // Round up to next 16 byte boundary
// The buffer needs to be allocated from "transport" pool to leave room for LwIP generated IP headers
// The IP packet consists of 16 byte header (struct message_transport_data), data padded upto 16 byte boundary + encrypted auth tag (16 bytes)
pbuf = pbuf_alloc(PBUF_TRANSPORT, header_len + padded_len + WIREGUARD_AUTHTAG_LEN, PBUF_RAM);
if (pbuf) {
// Note: allocating pbuf from RAM above guarantees that the pbuf is in one section and not chained
// - i.e payload points to the contiguous memory region
memset(pbuf->payload, 0, pbuf->tot_len);
hdr = (struct message_transport_data *)pbuf->payload;
hdr->type = MESSAGE_TRANSPORT_DATA;
hdr->receiver = keypair->remote_index;
// Alignment required... pbuf_alloc has probably aligned data, but want to be sure
U64TO8_LITTLE(hdr->counter, keypair->sending_counter);
// Copy the encrypted (padded) data to the output packet - chacha20poly1305_encrypt() can encrypt data in-place which avoids call to mem_malloc
dst = &hdr->enc_packet[0];
if ((padded_len > 0) && q) {
// Note: before copying make sure we have inserted the IP header checksum
// The IP header checksum (and other checksums in the IP packet - e.g. ICMP) need to be calculated by LWIP before calling
// The Wireguard interface always needs checksums to be generated in software but the base netif may have some checksums generated by hardware
// Copy pbuf to memory - handles case where pbuf is chained
pbuf_copy_partial(q, dst, unpadded_len, 0);
}
// Then encrypt
wireguard_encrypt_packet(dst, dst, padded_len, keypair);
result = wireguardif_peer_output(netif, pbuf, peer);
if (result == ERR_OK) {
now = wireguard_sys_now();
peer->last_tx = now;
keypair->last_tx = now;
}
pbuf_free(pbuf);
// Check to see if we should rekey
if (keypair->sending_counter >= REKEY_AFTER_MESSAGES) {
peer->send_handshake = true;
} else if (keypair->initiator && wireguard_expired(keypair->keypair_millis, REKEY_AFTER_TIME)) {
peer->send_handshake = true;
}
} else {
// Failed to allocate memory
result = ERR_MEM;
}
} else {
// key has expired...
keypair_destroy(keypair);
result = ERR_CONN;
}
} else {
// No valid keys!
result = ERR_CONN;
}
return result;
}
// This is used as the output function for the Wireguard netif
// The ipaddr here is the one inside the VPN which we use to lookup the correct peer/endpoint
static err_t wireguardif_output(struct netif *netif, struct pbuf *q, const ip_addr_t *ipaddr)
{
struct wireguard_device *device = (struct wireguard_device *)netif->state;
// Send to peer that matches dest IP
struct wireguard_peer *peer = peer_lookup_by_allowed_ip(device, ipaddr);
if (peer) {
return wireguardif_output_to_peer(netif, q, ipaddr, peer);
} else {
//printf("Cannot find peer for address %s\n", ip6addr_ntoa(ipaddr));
return ERR_RTE;
}
}
static void wireguardif_send_keepalive(struct wireguard_device *device, struct wireguard_peer *peer) {
// Send a NULL packet as a keep-alive
wireguardif_output_to_peer(device->netif, NULL, NULL, peer);
}
static void wireguardif_process_response_message(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_response *response, const ip_addr_t *addr, u16_t port) {
if (wireguard_process_handshake_response(device, peer, response)) {
// Packet is good
// Update the peer location
update_peer_addr(peer, addr, port);
wireguard_start_session(peer, true);
wireguardif_send_keepalive(device, peer);
// Set the IF-UP flag on netif
netif_set_link_up(device->netif);
} else {
// Packet bad
}
}
static bool peer_add_ip(struct wireguard_peer *peer, ip_addr_t ip, ip_addr_t mask) {
bool result = false;
struct wireguard_allowed_ip *allowed;
int x;
// Look for existing match first
for (x=0; x < WIREGUARD_MAX_SRC_IPS; x++) {
allowed = &peer->allowed_source_ips[x];
if ((allowed->valid) && ip_addr_cmp(&allowed->ip, &ip) && ip_addr_cmp(&allowed->mask, &mask)) {
result = true;
break;
}
}
if (!result) {
// Look for a free slot
for (x=0; x < WIREGUARD_MAX_SRC_IPS; x++) {
allowed = &peer->allowed_source_ips[x];
if (!allowed->valid) {
allowed->valid = true;
allowed->ip = ip;
allowed->mask = mask;
result = true;
break;
}
}
}
return result;
}
static void wireguardif_process_data_message(struct wireguard_device *device, struct wireguard_peer *peer, struct message_transport_data *data_hdr, size_t data_len, const ip_addr_t *addr, u16_t port)
{
struct wireguard_keypair *keypair;
uint64_t nonce;
uint8_t *src;
size_t src_len;
struct pbuf *pbuf;
#if LWIP_IPV4
struct ip_hdr *iphdr;
#endif
#if LWIP_IPV6
struct ip6_hdr *iphdr;
#endif
ip_addr_t dest;
bool dest_ok = false;
int x;
uint32_t now;
uint16_t header_len = 0xFFFF;
uint32_t idx = data_hdr->receiver;
keypair = get_peer_keypair_for_idx(peer, idx);
if (keypair) {
if (
(keypair->receiving_valid) &&
!wireguard_expired(keypair->keypair_millis, REJECT_AFTER_TIME) &&
(keypair->sending_counter < REJECT_AFTER_MESSAGES)
) {
nonce = U8TO64_LITTLE(data_hdr->counter);
src = &data_hdr->enc_packet[0];
src_len = data_len;
// We don't know the unpadded size until we have decrypted the packet and validated/inspected the IP header
pbuf = pbuf_alloc(PBUF_TRANSPORT, src_len - WIREGUARD_AUTHTAG_LEN, PBUF_RAM);
if (pbuf) {
// Decrypt the packet
memset(pbuf->payload, 0, pbuf->tot_len);
if (wireguard_decrypt_packet(pbuf->payload, src, src_len, nonce, keypair)) {
// 3. Since the packet has authenticated correctly, the source IP of the outer UDP/IP packet is used to update the endpoint for peer TrMv...WXX0.
// Update the peer location
update_peer_addr(peer, addr, port);
now = wireguard_sys_now();
keypair->last_rx = now;
peer->last_rx = now;
// Might need to shuffle next key --> current keypair
keypair_update(peer, keypair);
// Check to see if we should rekey
if (keypair->initiator && wireguard_expired(keypair->keypair_millis, REJECT_AFTER_TIME - peer->keepalive_interval - REKEY_TIMEOUT)) {
peer->send_handshake = true;
}
// Make sure that link is reported as up
netif_set_link_up(device->netif);
if (pbuf->tot_len > 0) {
//4a. Once the packet payload is decrypted, the interface has a plaintext packet. If this is not an IP packet, it is dropped.
#if LWIP_IPV4
iphdr = (struct ip_hdr *)pbuf->payload;
#endif
#if LWIP_IPV6
iphdr = (struct ip6_hdr *)pbuf->payload;
#endif
// Check for packet replay / dupes
if (wireguard_check_replay(keypair, nonce)) {
// 4b. Otherwise, WireGuard checks to see if the source IP address of the plaintext inner-packet routes correspondingly in the cryptokey routing table
// Also check packet length!
#if LWIP_IPV4
if (IP4H_V(iphdr) == 4) {
ip_addr_copy_from_ip4(dest, iphdr->dest);
for (x=0; x < WIREGUARD_MAX_SRC_IPS; x++) {
if (peer->allowed_source_ips[x].valid) {
if (ip_addr_netcmp(&dest, &peer->allowed_source_ips[x].ip, &peer->allowed_source_ips[x].mask)) {
dest_ok = true;
header_len = PP_NTOHS(IPH_LEN(iphdr));
break;
}
}
}
}
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
if (IP6H_V(iphdr) == 6) {
// TODO: IPV6 support for route filtering
header_len = IP6_HLEN;
ip_addr_copy_from_ip6_packed(dest, iphdr->dest);
for (x=0; x < WIREGUARD_MAX_SRC_IPS; x++) {
if (peer->allowed_source_ips[x].valid) {
if (ip_addr_netcmp(&dest, &peer->allowed_source_ips[x].ip, &peer->allowed_source_ips[x].mask)) {
dest_ok = true;
header_len = IP6_HLEN;
break;
}
}
}
//dest_ok = true;
}
#endif /* LWIP_IPV6 */
if (header_len <= pbuf->tot_len) {
// 5. If the plaintext packet has not been dropped, it is inserted into the receive queue of the wg0 interface.
if (dest_ok) {
// Send packet to be process by LWIP
ip_input(pbuf, device->netif);
// pbuf is owned by IP layer now
pbuf = NULL;
}
} else {
// IP header is corrupt or lied about packet size
}
} else {
// This is a duplicate packet / replayed / too far out of order
}
} else {
// This was a keep-alive packet
}
}
if (pbuf) {
pbuf_free(pbuf);
}
}
} else {
//After Reject-After-Messages transport data messages or after the current secure session is Reject- After-Time seconds old,
// whichever comes first, WireGuard will refuse to send or receive any more transport data messages using the current secure session,
// until a new secure session is created through the 1-RTT handshake
keypair_destroy(keypair);
}
} else {
// Could not locate valid keypair for remote index
}
}
static struct pbuf *wireguardif_initiate_handshake(struct wireguard_device *device, struct wireguard_peer *peer, struct message_handshake_initiation *msg, err_t *error) {
struct pbuf *pbuf = NULL;
err_t err = ERR_OK;
if (wireguard_create_handshake_initiation(device, peer, msg)) {
// Send this packet out!
pbuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct message_handshake_initiation), PBUF_RAM);
if (pbuf) {
err = pbuf_take(pbuf, msg, sizeof(struct message_handshake_initiation));
if (err == ERR_OK) {
// OK!
} else {
pbuf_free(pbuf);
pbuf = NULL;
}
} else {
err = ERR_MEM;
}
} else {
err = ERR_ARG;
}
if (error) {
*error = err;
}
return pbuf;
}
static void wireguardif_send_handshake_response(struct wireguard_device *device, struct wireguard_peer *peer)
{
struct message_handshake_response packet;
struct pbuf *pbuf = NULL;
err_t err = ERR_OK;
if (wireguard_create_handshake_response(device, peer, &packet)) {
wireguard_start_session(peer, false);
// Send this packet out!
pbuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct message_handshake_response), PBUF_RAM);
if (pbuf) {
err = pbuf_take(pbuf, &packet, sizeof(struct message_handshake_response));
if (err == ERR_OK) {
// OK!
wireguardif_peer_output(device->netif, pbuf, peer);
}
pbuf_free(pbuf);
}
}
}
static size_t get_source_addr_port(const ip_addr_t *addr, u16_t port, uint8_t *buf, size_t buflen)
{
size_t result = 0;
#if LWIP_IPV4
if (IP_IS_V4(addr) && (buflen >= 4)) {
U32TO8_BIG(buf + result, PP_NTOHL(ip4_addr_get_u32(addr)));
result += 4;
}
#endif
#if LWIP_IPV6
if (IP_IS_V6(addr) && (buflen >= 16)) {
U16TO8_BIG(buf + result + 0, IP6_ADDR_BLOCK1(addr));
U16TO8_BIG(buf + result + 2, IP6_ADDR_BLOCK2(addr));
U16TO8_BIG(buf + result + 4, IP6_ADDR_BLOCK3(addr));
U16TO8_BIG(buf + result + 6, IP6_ADDR_BLOCK4(addr));
U16TO8_BIG(buf + result + 8, IP6_ADDR_BLOCK5(addr));
U16TO8_BIG(buf + result + 10, IP6_ADDR_BLOCK6(addr));
U16TO8_BIG(buf + result + 12, IP6_ADDR_BLOCK7(addr));
U16TO8_BIG(buf + result + 14, IP6_ADDR_BLOCK8(addr));
result += 16;
}
#endif
if (buflen >= result + 2) {
U16TO8_BIG(buf + result, port);
result += 2;
}
return result;
}
static void wireguardif_send_handshake_cookie(struct wireguard_device *device, const uint8_t *mac1, uint32_t index, const ip_addr_t *addr, u16_t port) {
struct message_cookie_reply packet;
struct pbuf *pbuf = NULL;
err_t err = ERR_OK;
uint8_t source_buf[18];
size_t source_len = get_source_addr_port(addr, port, source_buf, sizeof(source_buf));
wireguard_create_cookie_reply(device, &packet, mac1, index, source_buf, source_len);
// Send this packet out!
pbuf = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct message_cookie_reply), PBUF_RAM);
if (pbuf) {
err = pbuf_take(pbuf, &packet, sizeof(struct message_cookie_reply));
if (err == ERR_OK) {
wireguardif_device_output(device, pbuf, addr, port);
}
pbuf_free(pbuf);
}
}
static bool wireguardif_check_initiation_message(struct wireguard_device *device, struct message_handshake_initiation *msg, const ip_addr_t *addr, u16_t port) {
bool result = false;
uint8_t *data = (uint8_t *)msg;
uint8_t source_buf[18];
size_t source_len;
// We received an initiation packet check it is valid
if (wireguard_check_mac1(device, data, sizeof(struct message_handshake_initiation) - (2 * WIREGUARD_COOKIE_LEN), msg->mac1)) {
// mac1 is valid!
if (!wireguard_is_under_load()) {
// If we aren't under load we only need mac1 to be correct
result = true;
} else {
// If we are under load then check mac2
source_len = get_source_addr_port(addr, port, source_buf, sizeof(source_buf));
result = wireguard_check_mac2(device, data, sizeof(struct message_handshake_initiation) - (WIREGUARD_COOKIE_LEN), source_buf, source_len, msg->mac2);
if (!result) {
// mac2 is invalid (cookie may have expired) or not present
// 5.3 Denial of Service Mitigation & Cookies
// If the responder receives a message with a valid msg.mac1 yet with an invalid msg.mac2, and is under load, it may respond with a cookie reply message
wireguardif_send_handshake_cookie(device, msg->mac1, msg->sender, addr, port);
}
}
} else {
// mac1 is invalid
}
return result;
}
static bool wireguardif_check_response_message(struct wireguard_device *device, struct message_handshake_response *msg, const ip_addr_t *addr, u16_t port) {
bool result = false;
uint8_t *data = (uint8_t *)msg;
uint8_t source_buf[18];
size_t source_len;
// We received an initiation packet check it is valid
if (wireguard_check_mac1(device, data, sizeof(struct message_handshake_response) - (2 * WIREGUARD_COOKIE_LEN), msg->mac1)) {
// mac1 is valid!
if (!wireguard_is_under_load()) {
// If we aren't under load we only need mac1 to be correct
result = true;
} else {
// If we are under load then check mac2
source_len = get_source_addr_port(addr, port, source_buf, sizeof(source_buf));
result = wireguard_check_mac2(device, data, sizeof(struct message_handshake_response) - (WIREGUARD_COOKIE_LEN), source_buf, source_len, msg->mac2);
if (!result) {
// mac2 is invalid (cookie may have expired) or not present
// 5.3 Denial of Service Mitigation & Cookies
// If the responder receives a message with a valid msg.mac1 yet with an invalid msg.mac2, and is under load, it may respond with a cookie reply message
wireguardif_send_handshake_cookie(device, msg->mac1, msg->sender, addr, port);
}
}
} else {
// mac1 is invalid
}
return result;
}
void wireguardif_network_rx(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) {
LWIP_ASSERT("wireguardif_network_rx: invalid arg", arg != NULL);
LWIP_ASSERT("wireguardif_network_rx: invalid pbuf", p != NULL);
// We have received a packet from the base_netif to our UDP port - process this as a possible Wireguard packet
struct wireguard_device *device = (struct wireguard_device *)arg;
struct wireguard_peer *peer;
uint8_t *data = p->payload;
size_t len = p->len; // This buf, not chained ones
struct message_handshake_initiation *msg_initiation;
struct message_handshake_response *msg_response;
struct message_cookie_reply *msg_cookie;
struct message_transport_data *msg_data;
uint8_t type = wireguard_get_message_type(data, len);
switch (type) {
case MESSAGE_HANDSHAKE_INITIATION:
msg_initiation = (struct message_handshake_initiation *)data;
// Check mac1 (and optionally mac2) are correct - note it may internally generate a cookie reply packet
if (wireguardif_check_initiation_message(device, msg_initiation, addr, port)) {
peer = wireguard_process_initiation_message(device, msg_initiation);
if (peer) {
// Update the peer location
update_peer_addr(peer, addr, port);
// Send back a handshake response
wireguardif_send_handshake_response(device, peer);
}
}
break;
case MESSAGE_HANDSHAKE_RESPONSE:
msg_response = (struct message_handshake_response *)data;
// Check mac1 (and optionally mac2) are correct - note it may internally generate a cookie reply packet
if (wireguardif_check_response_message(device, msg_response, addr, port)) {
peer = peer_lookup_by_handshake(device, msg_response->receiver);
if (peer) {
// Process the handshake response
wireguardif_process_response_message(device, peer, msg_response, addr, port);
}
}
break;
case MESSAGE_COOKIE_REPLY:
msg_cookie = (struct message_cookie_reply *)data;
peer = peer_lookup_by_handshake(device, msg_cookie->receiver);
if (peer) {
if (wireguard_process_cookie_message(device, peer, msg_cookie)) {
// Update the peer location
update_peer_addr(peer, addr, port);
// Don't send anything out - we stay quiet until the next initiation message
}
}
break;
case MESSAGE_TRANSPORT_DATA:
msg_data = (struct message_transport_data *)data;
peer = peer_lookup_by_receiver(device, msg_data->receiver);
if (peer) {
// header is 16 bytes long so take that off the length
wireguardif_process_data_message(device, peer, msg_data, len - 16, addr, port);
}
break;
default:
// Unknown or bad packet header
break;
}
// Release data!
pbuf_free(p);
}
static err_t wireguard_start_handshake(struct netif *netif, struct wireguard_peer *peer) {
struct wireguard_device *device = (struct wireguard_device *)netif->state;
err_t result;
struct pbuf *pbuf;
struct message_handshake_initiation msg;
pbuf = wireguardif_initiate_handshake(device, peer, &msg, &result);
if (pbuf) {
result = wireguardif_peer_output(netif, pbuf, peer);
pbuf_free(pbuf);
peer->send_handshake = false;
peer->last_initiation_tx = wireguard_sys_now();
memcpy(peer->handshake_mac1, msg.mac1, WIREGUARD_COOKIE_LEN);
peer->handshake_mac1_valid = true;
}
return result;
}
static err_t wireguardif_lookup_peer(struct netif *netif, u8_t peer_index, struct wireguard_peer **out) {
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("state != NULL", (netif->state != NULL));
struct wireguard_device *device = (struct wireguard_device *)netif->state;
struct wireguard_peer *peer = NULL;
err_t result;
if (device->valid) {
peer = peer_lookup_by_peer_index(device, peer_index);
if (peer) {
result = ERR_OK;
} else {
result = ERR_ARG;
}
} else {
result = ERR_ARG;
}
*out = peer;
return result;
}
err_t wireguardif_connect(struct netif *netif, u8_t peer_index)
{
struct wireguard_peer *peer;
err_t result = wireguardif_lookup_peer(netif, peer_index, &peer);
if (result == ERR_OK) {
// Check that a valid connect ip and port have been set
if (!ip_addr_isany(&peer->connect_ip) && (peer->connect_port > 0)) {
// Set the flag that we want to try connecting
peer->active = true;
peer->ip = peer->connect_ip;
peer->port = peer->connect_port;
result = ERR_OK;
} else {
result = ERR_ARG;
}
}
return result;
}
err_t wireguardif_disconnect(struct netif *netif, u8_t peer_index)
{
struct wireguard_peer *peer;
err_t result = wireguardif_lookup_peer(netif, peer_index, &peer);
if (result == ERR_OK) {
// Set the flag that we want to try connecting
peer->active = false;
// Wipe out current keys
keypair_destroy(&peer->next_keypair);
keypair_destroy(&peer->curr_keypair);
keypair_destroy(&peer->prev_keypair);
result = ERR_OK;
}
return result;
}
err_t wireguardif_peer_is_up(struct netif *netif, u8_t peer_index, ip_addr_t *current_ip, u16_t *current_port)
{
struct wireguard_peer *peer;
err_t result = wireguardif_lookup_peer(netif, peer_index, &peer);
if (result == ERR_OK) {
if ((peer->curr_keypair.valid) || (peer->prev_keypair.valid)) {
result = ERR_OK;
} else {
result = ERR_CONN;
}
if (current_ip) {
*current_ip = peer->ip;
}
if (current_port) {
*current_port = peer->port;
}
}
return result;
}
err_t wireguardif_remove_peer(struct netif *netif, u8_t peer_index)
{
struct wireguard_peer *peer;
err_t result = wireguardif_lookup_peer(netif, peer_index, &peer);
if (result == ERR_OK) {
crypto_zero(peer, sizeof(struct wireguard_peer));
peer->valid = false;
result = ERR_OK;
}
return result;
}
err_t wireguardif_update_endpoint(struct netif *netif, u8_t peer_index, const ip_addr_t *ip, u16_t port)
{
struct wireguard_peer *peer;
err_t result = wireguardif_lookup_peer(netif, peer_index, &peer);
if (result == ERR_OK) {
peer->connect_ip = *ip;
peer->connect_port = port;
result = ERR_OK;
}
return result;
}
err_t wireguardif_add_peer(struct netif *netif, struct wireguardif_peer *p, u8_t *peer_index)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("state != NULL", (netif->state != NULL));
LWIP_ASSERT("p != NULL", (p != NULL));
struct wireguard_device *device = (struct wireguard_device *)netif->state;
err_t result;
uint8_t public_key[WIREGUARD_PUBLIC_KEY_LEN];
size_t public_key_len = sizeof(public_key);
struct wireguard_peer *peer = NULL;
uint32_t t1 = wireguard_sys_now();
if (wireguard_base64_decode(p->public_key, public_key, &public_key_len)
&& (public_key_len == WIREGUARD_PUBLIC_KEY_LEN)) {
// See if the peer is already registered
peer = peer_lookup_by_pubkey(device, public_key);
if (!peer) {
// Not active - see if we have room to allocate a new one
peer = peer_alloc(device);
if (peer) {
if (wireguard_peer_init(device, peer, public_key, p->preshared_key)) {
peer->connect_ip = p->endpoint_ip;
peer->connect_port = p->endport_port;
peer->ip = peer->connect_ip;
peer->port = peer->connect_port;
if (p->keep_alive == WIREGUARDIF_KEEPALIVE_DEFAULT) {
peer->keepalive_interval = KEEPALIVE_TIMEOUT;
} else {
peer->keepalive_interval = p->keep_alive;
}
peer_add_ip(peer, p->allowed_ip, p->allowed_mask);
memcpy(peer->greatest_timestamp, p->greatest_timestamp, sizeof(peer->greatest_timestamp));
result = ERR_OK;
} else {
result = ERR_ARG;
}
} else {
result = ERR_MEM;
}
} else {
result = ERR_OK;
}
} else {
result = ERR_ARG;
}
uint32_t t2 = wireguard_sys_now();
printf("Adding peer took %ldms\r\n", (t2-t1));
if (peer_index) {
if (peer) {
*peer_index = wireguard_peer_index(device, peer);
} else {
*peer_index = WIREGUARDIF_INVALID_INDEX;
}
}
return result;
}
static bool should_send_initiation(struct wireguard_peer *peer) {
bool result = false;
if (wireguardif_can_send_initiation(peer)) {
if (peer->send_handshake) {
result = true;
} else if (peer->curr_keypair.valid && !peer->curr_keypair.initiator && wireguard_expired(peer->curr_keypair.keypair_millis, REJECT_AFTER_TIME - peer->keepalive_interval)) {
result = true;
} else if (!peer->curr_keypair.valid && peer->active) {
result = true;
}
}
return result;
}
static bool should_send_keepalive(struct wireguard_peer *peer) {
bool result = false;
if (peer->keepalive_interval > 0) {
if ((peer->curr_keypair.valid) || (peer->prev_keypair.valid)) {
if (wireguard_expired(peer->last_tx, peer->keepalive_interval)) {
result = true;
}
}
}
return result;
}
static bool should_destroy_current_keypair(struct wireguard_peer *peer) {
bool result = false;
if (peer->curr_keypair.valid &&
(wireguard_expired(peer->curr_keypair.keypair_millis, REJECT_AFTER_TIME) ||
(peer->curr_keypair.sending_counter >= REJECT_AFTER_MESSAGES))
) {
result = true;
}
return result;
}
static bool should_reset_peer(struct wireguard_peer *peer) {
bool result = false;
if (peer->curr_keypair.valid && (wireguard_expired(peer->curr_keypair.keypair_millis, REJECT_AFTER_TIME * 3))) {
result = true;
}
return result;
}
static void wireguardif_tmr(void *arg) {
struct wireguard_device *device = (struct wireguard_device *)arg;
struct wireguard_peer *peer;
int x;
// Reschedule this timer
sys_timeout(WIREGUARDIF_TIMER_MSECS, wireguardif_tmr, device);
// Check periodic things
bool link_up = false;
for (x=0; x < WIREGUARD_MAX_PEERS; x++) {
peer = &device->peers[x];
if (peer->valid) {
// Do we need to rekey / send a handshake?
if (should_reset_peer(peer)) {
// Nothing back for too long - we should wipe out all crypto state
keypair_destroy(&peer->next_keypair);
keypair_destroy(&peer->curr_keypair);
keypair_destroy(&peer->prev_keypair);
// TODO: Also destroy handshake?
// Revert back to default IP/port if these were altered
peer->ip = peer->connect_ip;
peer->port = peer->connect_port;
}
if (should_destroy_current_keypair(peer)) {
// Destroy current keypair
keypair_destroy(&peer->curr_keypair);
}
if (should_send_keepalive(peer)) {
wireguardif_send_keepalive(device, peer);
}
if (should_send_initiation(peer)) {
wireguard_start_handshake(device->netif, peer);
}
if ((peer->curr_keypair.valid) || (peer->prev_keypair.valid)) {
link_up = true;
}
}
}
if (!link_up) {
// Clear the IF-UP flag on netif
netif_set_link_down(device->netif);
}
}
err_t wireguardif_init(struct netif *netif)
{
err_t result;
struct wireguardif_init_data *init_data;
struct wireguard_device *device;
struct udp_pcb *udp;
uint8_t private_key[WIREGUARD_PRIVATE_KEY_LEN];
size_t private_key_len = sizeof(private_key);
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("state != NULL", (netif->state != NULL));
// We need to initialise the wireguard module
wireguard_init();
if (netif && netif->state) {
// The init data is passed into the netif_add call as the 'state' - we will replace this with our private state data
init_data = (struct wireguardif_init_data *)netif->state;
// Clear out and set if function is successful
netif->state = NULL;
if (wireguard_base64_decode(init_data->private_key, private_key, &private_key_len)
&& (private_key_len == WIREGUARD_PRIVATE_KEY_LEN)) {
udp = udp_new();
if (udp) {
result = udp_bind(udp, IP_ADDR_ANY, init_data->listen_port); // Note this listens on all interfaces! Really just want the passed netif
if (result == ERR_OK) {
device = (struct wireguard_device *)mem_calloc(1, sizeof(struct wireguard_device));
if (device) {
device->netif = netif;
if (init_data->bind_netif) {
udp_bind_netif(udp, init_data->bind_netif);
}
device->udp_pcb = udp;
// Per-wireguard netif/device setup
uint32_t t1 = wireguard_sys_now();
if (wireguard_device_init(device, private_key)) {
uint32_t t2 = wireguard_sys_now();
printf("Device init took %ldms\r\n", (t2-t1));
#if LWIP_CHECKSUM_CTRL_PER_NETIF
NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);
#endif
netif->state = device;
netif->name[0] = 'w';
netif->name[1] = 'g';
#if LWIP_IPV4
netif->output = wireguardif_output;
#else
netif->output_ip6 = wireguardif_output;
#endif
netif->linkoutput = NULL;
netif->hwaddr_len = 0;
netif->mtu = WIREGUARDIF_MTU;
// We set up no state flags here - caller should set them
// NETIF_FLAG_LINK_UP is automatically set/cleared when at least one peer is connected
netif->flags = 0;
udp_recv(udp, wireguardif_network_rx, device);
// Start a periodic timer for this wireguard device
sys_timeout(WIREGUARDIF_TIMER_MSECS, wireguardif_tmr, device);
result = ERR_OK;
} else {
mem_free(device);
device = NULL;
udp_remove(udp);
result = ERR_ARG;
}
} else {
udp_remove(udp);
result = ERR_MEM;
}
} else {
udp_remove(udp);
}
} else {
result = ERR_MEM;
}
} else {
result = ERR_ARG;
}
} else {
result = ERR_ARG;
}
return result;
}
void wireguardif_peer_init(struct wireguardif_peer *peer)
{
LWIP_ASSERT("peer != NULL", (peer != NULL));
memset(peer, 0, sizeof(struct wireguardif_peer));
// Caller must provide 'public_key'
peer->public_key = NULL;
#if LWIP_IPV4
ip4_addr_set_any(&peer->endpoint_ip);
#else
ip6_addr_set_any(&peer->endpoint_ip);
#endif
peer->endport_port = WIREGUARDIF_DEFAULT_PORT;
peer->keep_alive = WIREGUARDIF_KEEPALIVE_DEFAULT;
#if LWIP_IPV4
ip4_addr_set_any(&peer->allowed_ip);
ip4_addr_set_any(&peer->allowed_mask);
#else
ip6_addr_set_any(&peer->allowed_ip);
ip6_addr_set_any(&peer->allowed_mask);
#endif
memset(peer->greatest_timestamp, 0, sizeof(peer->greatest_timestamp));
peer->preshared_key = NULL;
}
pi-pico-wireguard-lwip/src/wireguardif.h
/*
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* 3. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
* its contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Author: Daniel Hope <daniel.hope@smartalock.com>
*/
#ifndef _WIREGUARDIF_H_
#define _WIREGUARDIF_H_
#include "lwip/netif.h"
#include "lwip/arch.h"
#include "lwip/ip_addr.h"
// Default MTU for WireGuard is 1420 bytes
#define WIREGUARDIF_MTU (1420)
#define WIREGUARDIF_DEFAULT_PORT (51820)
#define WIREGUARDIF_KEEPALIVE_DEFAULT (0xFFFF)
struct wireguardif_init_data {
// Required: the private key of this WireGuard network interface
const char *private_key;
// Required: What UDP port to listen on
u16_t listen_port;
// Optional: restrict send/receive of encapsulated WireGuard traffic to this network interface only (NULL to use routing table)
struct netif *bind_netif;
};
struct wireguardif_peer {
const char *public_key;
// Optional pre-shared key (32 bytes) - make sure this is NULL if not to be used
const uint8_t *preshared_key;
// tai64n of largest timestamp we have seen during handshake to avoid replays
uint8_t greatest_timestamp[12];
// Allowed ip/netmask (can add additional later but at least one is required)
ip_addr_t allowed_ip;
ip_addr_t allowed_mask;
// End-point details (may be blank)
ip_addr_t endpoint_ip;
u16_t endport_port;
u16_t keep_alive;
};
#define WIREGUARDIF_INVALID_INDEX (0xFF)
/* static struct netif wg_netif_struct = {0};
* struct wireguard_interface wg;
* wg.private_key = "abcdefxxx..xxxxx=";
* wg.listen_port = 51820;
* wg.bind_netif = NULL; // Pass netif to listen on, NULL for all interfaces
*
* netif = netif_add(&netif_struct, &ipaddr, &netmask, &gateway, &wg, &wireguardif_init, &ip_input);
*
* netif_set_up(wg_net);
*
* struct wireguardif_peer peer;
* wireguardif_peer_init(&peer);
* peer.public_key = "apoehc...4322abcdfejg=;
* peer.preshared_key = NULL;
* peer.allowed_ip = allowed_ip;
* peer.allowed_mask = allowed_mask;
*
* // If you want to enable output connection
* peer.endpoint_ip = peer_ip;
* peer.endport_port = 12345;
*
* uint8_t wireguard_peer_index;
* wireguardif_add_peer(netif, &peer, &wireguard_peer_index);
*
* if ((wireguard_peer_index != WIREGUARDIF_INVALID_INDEX) && !ip_addr_isany(&peer.endpoint_ip)) {
* // Start outbound connection to peer
* wireguardif_connect(wg_net, wireguard_peer_index);
* }
*
*/
// Initialise a new WireGuard network interface (netif)
err_t wireguardif_init(struct netif *netif);
// Helper to initialise the peer struct with defaults
void wireguardif_peer_init(struct wireguardif_peer *peer);
// Add a new peer to the specified interface - see wireguard.h for maximum number of peers allowed
// On success the peer_index can be used to reference this peer in future function calls
err_t wireguardif_add_peer(struct netif *netif, struct wireguardif_peer *peer, u8_t *peer_index);
// Remove the given peer from the network interface
err_t wireguardif_remove_peer(struct netif *netif, u8_t peer_index);
// Update the "connect" IP of the given peer
err_t wireguardif_update_endpoint(struct netif *netif, u8_t peer_index, const ip_addr_t *ip, u16_t port);
// Try and connect to the given peer
err_t wireguardif_connect(struct netif *netif, u8_t peer_index);
// Stop trying to connect to the given peer
err_t wireguardif_disconnect(struct netif *netif, u8_t peer_index);
// Is the given peer "up"? A peer is up if it has a valid session key it can communicate with
err_t wireguardif_peer_is_up(struct netif *netif, u8_t peer_index, ip_addr_t *current_ip, u16_t *current_port);
#endif /* _WIREGUARDIF_H_ */
Server
[Interface]
PrivateKey = cIQqBEAkmZ3uugQGkqN5ADAEMjdLVhjRgfj6VK8tg14=
ListenPort = 48484
Address = fd68:abcd::1/64
# cidlo 1
[Peer]
PublicKey = fcmh6Mqg5ccXQ/dtCSOkyvhpQws028bo9jQ1ozgUVyI=
AllowedIPs = fd68:abcd::2/128
PersistentKeepalive = 10
m1 ~ # wg
interface: wg0
public key: XSqRUMJxLgFu9hejbzKzDpjCkibzNaek3Ql6sMRQBXk=
private key: (hidden)
listening port: 48484
peer: fcmh6Mqg5ccXQ/dtCSOkyvhpQws028bo9jQ1ozgUVyI=
endpoint: [2a00:1028:f500:71:2acd:c1ff:fe03:37ef]:51881
allowed ips: fd68:abcd::2/128
latest handshake: 22 seconds ago
transfer: 283.73 KiB received, 95.75 KiB sent
persistent keepalive: every 10 seconds
m1 ~ #
import asyncio
class ClientDatagramProtocol(asyncio.DatagramProtocol):
def datagram_received(self, data, addr):
message = data.decode("utf8")
print("Received", message, "from", addr)
async def main():
loop = asyncio.get_running_loop()
transport, protocol = await loop.create_datagram_endpoint(
lambda: ClientDatagramProtocol(),
local_addr=('::', 4848))
await asyncio.sleep(100000)
transport.close()
asyncio.run(main())
Received Teplota: 3.7 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 4.0 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 4.0 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.9 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 4.0 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 4.0 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 4.0 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.9 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.7 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.7 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.5 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.6 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.6 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.7 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.8 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 4.0 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.9 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.7 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.6 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.7 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.6 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.9 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.9 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.9 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.9 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.7 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 3.7 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 4.0 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
Received Teplota: 4.0 ˚C
from ('fd68:abcd::2', 64987, 0, 0)
m1 ~ # tcpdump -vv -i wg0
dropped privs to pcap
tcpdump: listening on wg0, link-type RAW (Raw IP), snapshot length 262144 bytes
03:50:56.655054 IP6 (hlim 255, next-header UDP (17), payload length 28) fd68:abcd::2.64987 > fd68:abcd::1.4848: [udp sum ok] UDP, length 20
03:51:06.631375 IP6 (hlim 255, next-header UDP (17), payload length 28) fd68:abcd::2.64987 > fd68:abcd::1.4848: [udp sum ok] UDP, length 20
03:51:16.631625 IP6 (hlim 255, next-header UDP (17), payload length 28) fd68:abcd::2.64987 > fd68:abcd::1.4848: [udp sum ok] UDP, length 20
03:51:26.633447 IP6 (hlim 255, next-header UDP (17), payload length 28) fd68:abcd::2.64987 > fd68:abcd::1.4848: [udp sum ok] UDP, length 20
03:51:36.633739 IP6 (hlim 255, next-header UDP (17), payload length 28) fd68:abcd::2.64987 > fd68:abcd::1.4848: [udp sum ok] UDP, length 20
03:51:46.632708 IP6 (hlim 255, next-header UDP (17), payload length 28) fd68:abcd::2.64987 > fd68:abcd::1.4848: [udp sum ok] UDP, length 20
03:51:56.634037 IP6 (hlim 255, next-header UDP (17), payload length 28) fd68:abcd::2.64987 > fd68:abcd::1.4848: [udp sum ok] UDP, length 20
03:52:06.634431 IP6 (hlim 255, next-header UDP (17), payload length 28) fd68:abcd::2.64987 > fd68:abcd::1.4848: [udp sum ok] UDP, length 20
03:52:16.635605 IP6 (hlim 255, next-header UDP (17), payload length 28) fd68:abcd::2.64987 > fd68:abcd::1.4848: [udp sum ok] UDP, length 20
Zdroje a odkazy
WireGuard Implementation for lwIP by Daniel Hope