Skip to content
Merged
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
61 changes: 47 additions & 14 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,40 @@ pub fn build(b: *std.Build) void {
const is_macos = resolved_os == .macos;
const is_freebsd = resolved_os == .freebsd;

// Build option: disable libsodium linkage (for cross-compilation CI where
// the target arch's libsodium is not available). Falls back to std.crypto.
const no_sodium = b.option(bool, "no-sodium", "Disable libsodium linkage (use std.crypto)") orelse false;

// Platforms that never use libsodium
const skip_sodium = no_sodium or is_android or is_macos or is_ios or is_freebsd;
// ─── Crypto backend selection (meshguard#102) ───
// libsodium is an OPTIONAL accelerator (AVX2 ChaCha20-Poly1305 on Linux),
// not a required dependency. std.crypto is the portable fallback.
// auto → libsodium on Linux desktop (non-Android), std.crypto elsewhere
// std → std.crypto everywhere (no libsodium link)
// sodium → force libsodium (link must be available for the target)
const CryptoBackend = enum { auto, std, sodium };
const crypto_backend_opt = b.option(CryptoBackend, "crypto-backend", "Crypto backend: auto|std|sodium (default auto)");
// Back-compat alias: -Dno-sodium=true is equivalent to -Dcrypto-backend=std.
const no_sodium = b.option(bool, "no-sodium", "Alias for -Dcrypto-backend=std") orelse false;

// Fail fast on contradictory flags rather than silently letting one win.
if (no_sodium and (crypto_backend_opt orelse .std) == .sodium) {
std.debug.print("error: -Dno-sodium=true conflicts with -Dcrypto-backend=sodium\n" ++
" (-Dno-sodium is an alias for -Dcrypto-backend=std)\n", .{});
std.process.exit(1);
}
const crypto_backend = crypto_backend_opt orelse .auto;

// libsodium is only auto-selected on Linux desktop (non-Android), where the
// vendored/system .so provides the AVX2 assembly. Everywhere else → std.crypto.
const auto_libsodium = (resolved_os == .linux and resolved_abi != .android);
const use_libsodium = if (no_sodium) false else switch (crypto_backend) {
.std => false,
.sodium => true,
.auto => auto_libsodium,
};
Comment on lines +40 to +44

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — fixed in c843ff4. The build now fails fast with a clear message when -Dno-sodium=true is combined with -Dcrypto-backend=sodium (only that pairing; -Dno-sodium with std/auto is still accepted as a no-op/alias).


// Exposed to source via @import("build_options") so tunnel.zig / main.zig
// select the same backend the linker is wired for. Without this the source
// chose the backend from builtin.os.tag alone and ignored the build option.
const build_options = b.addOptions();
build_options.addOption(bool, "use_libsodium", use_libsodium);
const build_options_mod = build_options.createModule();

// ─── FFI module (for mobile embedding — not built on Windows) ───
if (!is_windows) {
Expand All @@ -31,6 +59,7 @@ pub fn build(b: *std.Build) void {
.link_libc = true,
.strip = if (is_android) true else null,
});
ffi_mod.addImport("build_options", build_options_mod);

const ffi_lib = b.addLibrary(.{
.name = "meshguard-ffi",
Expand All @@ -39,9 +68,9 @@ pub fn build(b: *std.Build) void {
.linkage = if (is_ios) .static else .dynamic,
});

// Link libsodium on Linux desktop targets for AVX2-accelerated crypto.
// On Android, macOS, iOS, FreeBSD, and Windows, the Zig std.crypto software fallback is used.
if (!skip_sodium) {
// Link libsodium only when the resolved backend uses it (Linux desktop).
// Otherwise the Zig std.crypto software path is used (no link needed).
if (use_libsodium) {
ffi_mod.linkSystemLibrary("sodium", .{});
}

Expand All @@ -57,22 +86,24 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
.link_libc = true,
});
exe_mod.addImport("build_options", build_options_mod);

const lib_mod = b.createModule(.{
.root_source_file = b.path("src/lib.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
});
lib_mod.addImport("build_options", build_options_mod);

// ─── Main executable ───
const exe = b.addExecutable(.{
.name = "meshguard",
.root_module = exe_mod,
});
// Link libsodium on Linux desktop only (AVX2 ChaCha20-Poly1305 assembly)
// macOS, FreeBSD, and Windows use std.crypto
if (!is_windows and !skip_sodium) {
// Link libsodium only when the resolved backend uses it (Linux desktop,
// AVX2 ChaCha20-Poly1305 assembly). std.crypto everywhere else.
if (use_libsodium) {
exe_mod.linkSystemLibrary("sodium", .{});
}
// On Windows, link ws2_32 for Winsock2 sockets
Expand All @@ -94,11 +125,12 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
.link_libc = true,
});
interop_mod.addImport("build_options", build_options_mod);
const interop_exe = b.addExecutable(.{
.name = "wg-interop-test",
.root_module = interop_mod,
});
if (!skip_sodium) {
if (use_libsodium) {
interop_mod.linkSystemLibrary("sodium", .{});
}
b.installArtifact(interop_exe);
Expand Down Expand Up @@ -128,11 +160,12 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
.link_libc = true,
});
test_mod.addImport("build_options", build_options_mod);

const unit_tests = b.addTest(.{
.root_module = test_mod,
});
if (!is_windows and !skip_sodium) {
if (use_libsodium) {
test_mod.linkSystemLibrary("sodium", .{});
}
if (is_windows) {
Expand Down
6 changes: 3 additions & 3 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ pub fn main(init: std.process.Init) !void {
const allocator = init.gpa;
const arena = init.arena.allocator();

// Initialize libsodium (AVX2 ChaCha20-Poly1305 assembly) — Linux only
// macOS and Windows use std.crypto (no libsodium dependency)
if (comptime @import("builtin").os.tag == .linux) {
// Initialize libsodium (AVX2 ChaCha20-Poly1305 assembly) only when the
// resolved backend uses it. std.crypto path needs no init (meshguard#102).
if (comptime @import("build_options").use_libsodium) {
@import("crypto/sodium.zig").init();
}

Expand Down
11 changes: 6 additions & 5 deletions src/wireguard/tunnel.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
const std = @import("std");
const noise = @import("noise.zig");

// Compile-time AEAD backend selection:
// - Linux/native (non-Android): libsodium (AVX2 assembly, ~2× faster)
// - Android/Windows/other: std.crypto (no libsodium dependency)
const builtin = @import("builtin");
const use_libsodium = (builtin.os.tag == .linux and builtin.target.abi != .android);
// Compile-time AEAD backend selection, resolved in build.zig and passed in via
// build_options so the source and the linker always agree (meshguard#102).
// Default (auto): libsodium on Linux desktop (AVX2 assembly, ~2× faster at MTU/
// bulk), std.crypto everywhere else. -Dcrypto-backend=std / -Dno-sodium forces
// the std.crypto path on Linux too (no libsodium link required).
const use_libsodium = @import("build_options").use_libsodium;

/// Returns a blocking Io instance for synchronous operations.
fn zio() std.Io {
Expand Down
Loading