Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions .github/workflows/wolfguard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: wolfguard tests (non-FIPS)

on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]

jobs:
wolfguard:
runs-on: ubuntu-latest
timeout-minutes: 30

steps:
- uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential autoconf libtool \
linux-headers-$(uname -r) \
iproute2 check \
gawk kmod

- name: Clone wolfSSL and wolfguard sources
run: |
mkdir -p wolf-sources
# nightly-snapshot is the branch required by wolfguard for its latest
# kernel-module API. Pin to a tagged release once wolfguard stabilises.
git clone --depth=1 https://github.com/wolfssl/wolfssl \
--branch nightly-snapshot wolf-sources/wolfssl
git clone --depth=1 https://github.com/wolfssl/wolfguard \
wolf-sources/wolfguard

- name: Build and install wolfssl user library
working-directory: wolf-sources/wolfssl
run: |
./autogen.sh
./configure --quiet --enable-wolfguard --enable-all-asm
make -j$(nproc)
sudo make install
sudo ldconfig

- name: Build and install wg-fips user tool
run: |
make -j$(nproc) -C wolf-sources/wolfguard/user-src
sudo make -C wolf-sources/wolfguard/user-src install

- name: Build and install libwolfssl.ko (non-FIPS)
working-directory: wolf-sources/wolfssl
run: |
make distclean
./configure --quiet --enable-wolfguard --enable-cryptonly \
--enable-intelasm --enable-linuxkm \
--with-linux-source=/lib/modules/$(uname -r)/build \
--prefix=$(pwd)/linuxkm/build
make -j$(nproc) module
sudo make install

- name: Build wolfguard.ko
working-directory: wolf-sources/wolfguard/kernel-src
run: |
make -j$(nproc) \
KERNELDIR=/lib/modules/$(uname -r)/build \
KERNELRELEASE=$(uname -r) \
EXTRA_CFLAGS="-I$(realpath ../../wolfssl)"

- name: Load kernel modules
run: |
sudo insmod /lib/modules/$(uname -r)/wolfssl/libwolfssl.ko
sudo insmod wolf-sources/wolfguard/kernel-src/wolfguard.ko

- name: Build wolfguard unit tests
run: make unit-wolfguard

- name: Run wolfguard unit tests
timeout-minutes: 5
run: |
set -euo pipefail
timeout --preserve-status 5m ./build/test/unit-wolfguard

- name: Build wolfguard functional test
run: make build/test-wolfguard

- name: Run wolfguard functional test
timeout-minutes: 5
run: |
set -euo pipefail
timeout --preserve-status 5m sudo ./build/test-wolfguard

- name: Unload kernel modules
if: always()
run: |
sudo rmmod wolfguard || true
sudo rmmod libwolfssl || true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ tags
cppcheck_results.xml
src/port/stm32h563/app.elf
src/port/va416xx/app.elf
wolf-sources/
56 changes: 55 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ leaksan:LDFLAGS+=-fsanitize=leak
ESP_CFLAGS = \
-DWOLFIP_ESP -DWOLFSSL_WOLFIP

WOLFGUARD_CFLAGS = -DWOLFIP_WOLFGUARD

WG_OBJ = build/wolfguard/wolfip.o \
build/wolfguard/wolfip_wolfguard.o \
$(TAP_OBJ)

# Test

ifeq ($(CHECK_PKG_LIBS),)
Expand Down Expand Up @@ -255,6 +261,53 @@ build/ipfilter/wolfip.o: src/wolfip.c

build/test/ipfilter_logger.o: CFLAGS+=-DCONFIG_IPFILTER=1

# wolfguard
build/wolfguard/wolfip.o: src/wolfip.c
@mkdir -p `dirname $@` || true
@echo "[CC] $< (wolfguard)"
@$(CC) $(CFLAGS) $(WOLFGUARD_CFLAGS) -c $< -o $@

build/wolfguard/wolfip_wolfguard.o: src/wolfip_wolfguard.c
@mkdir -p `dirname $@` || true
@echo "[CC] $< (wolfguard)"
@$(CC) $(CFLAGS) $(WOLFGUARD_CFLAGS) -c $< -o $@

build/test/wolfguard/test_wolfguard.o: src/test/wolfguard/test_wolfguard.c
@mkdir -p `dirname $@` || true
@echo "[CC] $@"
@$(CC) $(CFLAGS) $(WOLFGUARD_CFLAGS) -c $< -o $@

build/test-wolfguard: $(WG_OBJ) build/test/wolfguard/test_wolfguard.o
@echo "[LD] $@"
@$(CC) $(CFLAGS) $(WOLFGUARD_CFLAGS) $(LDFLAGS) -o $@ \
$(BEGIN_GROUP) $(^) $(END_GROUP) -lwolfssl

unit-wolfguard: build/test/unit-wolfguard

build/test/unit-wolfguard: src/test/unit/unit_wolfguard.c
@mkdir -p build/test/
@echo "[CC] unit_wolfguard.c"
@$(CC) $(CFLAGS) $(WOLFGUARD_CFLAGS) $(UNIT_CFLAGS) \
-c src/test/unit/unit_wolfguard.c -o build/test/unit_wolfguard.o
@echo "[LD] $@"
@$(CC) build/test/unit_wolfguard.o -o $@ \
$(UNIT_LDFLAGS) $(LDFLAGS) $(UNIT_LIBS)

clean-unit-wolfguard:
@rm -f build/test/unit-wolfguard build/test/unit_wolfguard.o

unit-wolfguard-asan: CFLAGS+=-fsanitize=address
unit-wolfguard-asan: LDFLAGS+=-fsanitize=address $(UNIT_LIBS)
unit-wolfguard-asan: clean-unit-wolfguard build/test/unit-wolfguard

unit-wolfguard-ubsan: CFLAGS+=-fsanitize=undefined -fno-sanitize-recover=all
unit-wolfguard-ubsan: LDFLAGS+=-fsanitize=undefined $(UNIT_LIBS)
unit-wolfguard-ubsan: clean-unit-wolfguard build/test/unit-wolfguard

unit-wolfguard-leaksan: CFLAGS+=-fsanitize=leak
unit-wolfguard-leaksan: LDFLAGS+=-fsanitize=leak $(UNIT_LIBS)
unit-wolfguard-leaksan: clean-unit-wolfguard build/test/unit-wolfguard

# ipsec esp
build/esp/wolfip.o: src/wolfip.c
@mkdir -p `dirname $@` || true
Expand Down Expand Up @@ -438,7 +491,8 @@ install:
ldconfig

.PHONY: clean all static cppcheck cov autocov unit-asan unit-ubsan unit-leaksan clean-unit \
unit-esp-asan unit-esp-ubsan unit-esp-leaksan clean-unit-esp
unit-esp-asan unit-esp-ubsan unit-esp-leaksan clean-unit-esp \
unit-wolfguard unit-wolfguard-asan unit-wolfguard-ubsan unit-wolfguard-leaksan clean-unit-wolfguard

cppcheck:
$(CPPCHECK) $(CPPCHECK_FLAGS) src/ 2>cppcheck_results.xml
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ A single network interface can be associated with the device.
| **Network** | IPv4 Forwarding | Multi-interface routing (optional) | [RFC 1812](https://datatracker.ietf.org/doc/html/rfc1812) |
| **Network** | ICMP | Echo request/reply, TTL exceeded | [RFC 792](https://datatracker.ietf.org/doc/html/rfc792) |
| **Network** | IPsec | ESP Transport mode | [RFC 4303](https://datatracker.ietf.org/doc/html/rfc4303) |
| **Network** | WolfGuard | VPN tunnel via wolfguard kernel module (SECP256R1, AES-256-GCM, SHA-256) | See `wolf-sources/wolfssl/wolfguard/README.md` |
| **Transport** | UDP | Unicast datagrams, checksum | [RFC 768](https://datatracker.ietf.org/doc/html/rfc768) |
| **Transport** | TCP | Connection management, reliable delivery | [RFC 793](https://datatracker.ietf.org/doc/html/rfc793), [RFC 9293](https://datatracker.ietf.org/doc/html/rfc9293) |
| **Transport** | TCP | Maximum Segment Size negotiation | [RFC 793](https://datatracker.ietf.org/doc/html/rfc793) |
Expand Down Expand Up @@ -69,6 +70,92 @@ The `-I wtcp0` flag pins the test to the injected interface and `-c5`
generates five echo requests. Successful replies confirm the ICMP
datagram socket support end-to-end through the tap device.

## WolfGuard support

wolfIP can use [WolfGuard](wolf-sources/wolfssl/wolfguard/README.md) as its
link-layer driver, giving every socket opened on the stack transparent
WireGuard-compatible encryption without any changes to application code.

### How it works

WolfGuard is a kernel module (`wolfguard.ko`) that registers a standard Linux
network interface (`ARPHRD_NONE`, identical in structure to the upstream
WireGuard driver) and performs the handshake and encryption inside the kernel
using wolfSSL's FIPS-ready primitives (SECP256R1, AES-256-GCM, SHA-256).

`wolfip_wolfguard.c` is a wolfIP ll_dev driver that bridges the two:

1. Creates the wolfguard interface via Netlink (`RTM_NEWLINK type=wolfguard`).
2. Configures keys and peers via the wolfguard Generic Netlink family
(`WG_CMD_SET_DEVICE`).
3. Connects to the interface with an `AF_PACKET/SOCK_DGRAM` socket, injecting
and receiving raw IP packets that the kernel module encrypts/decrypts
transparently.
4. Provides a synthetic ARP proxy so wolfIP's Ethernet layer can resolve peer
IPs without kernel ARP involvement.

### Prerequisites

Build and load the wolfguard kernel module and its wolfSSL dependency by
following the instructions in
[wolf-sources/wolfssl/wolfguard/README.md](wolf-sources/wolfssl/wolfguard/README.md).
Then load the modules before running any wolfguard-enabled binary:

```sh
insmod /lib/modules/$(uname -r)/wolfssl/libwolfssl.ko
insmod /path/to/wolfguard.ko
modprobe udp_tunnel
modprobe ip6_udp_tunnel
```

### Enabling wolfguard support

Add `-DWOLFIP_WOLFGUARD` to your `CFLAGS` and link `wolfip_wolfguard.c` into
your build. In the wolfIP Makefile the dedicated targets handle this
automatically:

```sh
make unit-wolfguard # driver unit tests (no kernel module required)
make build/test-wolfguard # functional test binary
```

To integrate into your own application, call `wolfIP_wg_init()` in place of
`tap_init()`:

```c
#include "wolfip_wolfguard.h"

struct wolfIP_wg_config cfg = { ... }; /* keys, peers, listen port */
struct wolfIP *stack;

wolfIP_init_static(&stack);
wolfIP_wg_init(&cfg, wolfIP_getdev(stack));
wolfIP_ipconfig_set(stack, atoip4("10.8.0.1"), atoip4("255.255.255.0"),
atoip4("10.8.0.1"));
/* use wolfIP sockets normally — all traffic is encrypted by wolfguard */
```

### Running the tests

**Unit tests** exercise the driver logic (ARP proxy, L2/L3 bridging) entirely
in userspace with a mock pipe — no kernel module required:

```sh
make unit-wolfguard
./build/test/unit-wolfguard
```

**Functional test** performs a full loopback: wolfIP sends a UDP packet,
wolfguard encrypts it, a kernel-side peer decrypts and echoes it back, and
wolfIP verifies the payload. Requires the kernel modules to be loaded,
`wg-fips` in `PATH`, and root (or `NET_ADMIN` capability) to create network
interfaces:

```sh
make build/test-wolfguard
sudo ./build/test-wolfguard
```

## FreeRTOS Port

wolfIP now includes a dedicated FreeRTOS wrapper port at:
Expand Down
6 changes: 6 additions & 0 deletions src/test/unit/unit_esp.c
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ START_TEST(test_unwrap_pad_too_big)
}
END_TEST


/*
* full enc/dec round-trips
* */
Expand Down Expand Up @@ -771,6 +772,7 @@ START_TEST(test_roundtrip_aes128_cbc_sha1)
}
END_TEST

#ifndef HAVE_FIPS
START_TEST(test_roundtrip_aes128_cbc_md5)
{
do_roundtrip_cbc_hmac(k_aes128, sizeof(k_aes128),
Expand All @@ -779,6 +781,7 @@ START_TEST(test_roundtrip_aes128_cbc_md5)
ESP_ICVLEN_HMAC_96);
}
END_TEST
#endif

START_TEST(test_roundtrip_aes256_cbc_sha256_128)
{
Expand Down Expand Up @@ -1140,7 +1143,10 @@ static Suite *esp_suite(void)
tcase_add_test(tc, test_roundtrip_aes128_cbc_sha256_128);
tcase_add_test(tc, test_roundtrip_aes128_cbc_sha256_96);
tcase_add_test(tc, test_roundtrip_aes128_cbc_sha1);
/* run this test only if the build is not in FIPS mode, since md5 is not approved. */
#ifndef HAVE_FIPS
tcase_add_test(tc, test_roundtrip_aes128_cbc_md5);
#endif
tcase_add_test(tc, test_roundtrip_aes256_cbc_sha256_128);
#ifndef NO_DES3
tcase_add_test(tc, test_roundtrip_des3_sha256);
Expand Down
Loading