-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a basically-functioning zig port
* Approximately mirrors the C version's code, but zigified * Passes existing tests used by the C version * Seems to work correctly in the real world * Supports zig versions 0.11.0 (latest) and 0.12.0-dev (master)
- Loading branch information
Showing
5 changed files
with
802 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,5 @@ rundir.inc | |
tofurkey | ||
t/testout | ||
t/testout_slow | ||
zig-cache | ||
zig-out |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
const std = @import("std"); | ||
pub fn build(b: *std.Build) void { | ||
// Standard options | ||
const target = b.standardTargetOptions(.{}); | ||
const optimize = b.standardOptimizeOption(.{}); | ||
|
||
// Define the executable. | ||
// Later after dropping 0.11.0 support, can fold this up the "normal" way | ||
// with addExecutable(.{ ... })) | ||
var exe_opts = std.Build.ExecutableOptions{ | ||
.name = "tofurkey", | ||
.root_source_file = .{ .path = "src/tofurkey.zig" }, | ||
.single_threaded = true, | ||
.target = target, | ||
.optimize = optimize, | ||
}; | ||
if (@hasField(std.Build.ExecutableOptions, "strip")) // Not present in 0.11.0 | ||
exe_opts.strip = (optimize != .Debug); | ||
const exe = b.addExecutable(exe_opts); | ||
|
||
// Support for -Drundir=x affecting executable via config import | ||
const rundir = b.option([]const u8, "rundir", "The system rundir, default '/run', for autokey storage") orelse "/run"; | ||
const options = b.addOptions(); | ||
options.addOption([]const u8, "rundir", rundir); | ||
if (@hasDecl(@TypeOf(exe.*), "addOptions")) { | ||
exe.addOptions("config", options); // 0.11.0 | ||
} else { | ||
exe.root_module.addOptions("config", options); // master 0.12.0-dev | ||
} | ||
|
||
// Link libsodium and libc for the executable | ||
exe.linkSystemLibrary("sodium"); | ||
exe.linkLibC(); | ||
|
||
// Declare the built executable as installable and put it in an overrideable sbindir | ||
const sbindir = b.option([]const u8, "sbindir", "Prefix-relative subpath for sbin dir, default 'sbin'") orelse "sbin"; | ||
const exe_art = b.addInstallArtifact(exe, .{ .dest_dir = .{ .override = .{ .custom = sbindir } } }); | ||
b.getInstallStep().dependOn(&exe_art.step); | ||
|
||
// Install the manpage as well, with support for overriding the | ||
// prefix-relative destination directory: | ||
const man8dir = b.option([]const u8, "man8dir", "Prefix-relative subpath for man8 dir, default 'share/man/man8'") orelse "share/man/man8"; | ||
const man_page = b.addInstallFileWithDir(.{ .path = "tofurkey.8" }, .{ .custom = man8dir }, "tofurkey.8"); | ||
b.getInstallStep().dependOn(&man_page.step); | ||
|
||
// "zig build test" -> Unit testing | ||
const unit_exe = b.addTest(.{ | ||
.root_source_file = .{ .path = "src/tofurkey.zig" }, | ||
.single_threaded = true, | ||
.target = target, | ||
.optimize = optimize, | ||
}); | ||
unit_exe.linkSystemLibrary("sodium"); | ||
unit_exe.linkLibC(); | ||
const run_unit_tests = b.addRunArtifact(unit_exe); | ||
const test_step = b.step("test", "Run unit tests"); | ||
test_step.dependOn(&run_unit_tests.step); | ||
|
||
// "zig build itest" -> Quick integration testing with t/quick.sh | ||
const itest_step = b.step("itest", "Run quick integration tests"); | ||
const itest_run_quick = b.addSystemCommand(&.{"t/quick.sh"}); | ||
itest_run_quick.addArtifactArg(exe); | ||
itest_step.dependOn(&itest_run_quick.step); | ||
|
||
// "zig build itest-slow" -> Full integration testing with t/quick.sh + t/slow.sh | ||
const itest_step_slow = b.step("itest-slow", "Run full integration tests (slower)"); | ||
const itest_run_slow = b.addSystemCommand(&.{"t/slow.sh"}); | ||
itest_run_slow.addArtifactArg(exe); | ||
itest_step_slow.dependOn(&itest_run_slow.step); | ||
itest_step_slow.dependOn(&itest_run_quick.step); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
// This file encapsulates all our direct uses of libc and libsodium into more | ||
// zig-like interfaces for the main code. In theory, at least the libc parts | ||
// could be obviated by improvements to the Zig Standard Library. | ||
|
||
const std = @import("std"); | ||
const assert = std.debug.assert; | ||
const log = std.log; | ||
// Handle 0.11.0->0.12-dev switch from "os" to "posix" | ||
const posix = if (@hasDecl(std, "posix")) std.posix else std.os; | ||
const c = @cImport({ | ||
@cDefine("_GNU_SOURCE", {}); | ||
@cInclude("time.h"); // clock_nanosleep() | ||
@cInclude("unistd.h"); // getopt() | ||
@cInclude("sodium.h"); // libsodium | ||
}); | ||
|
||
pub fn clock_nanosleep_real_abs(sec: u64, nsec: u64) !void { | ||
// Assert that time_t (type of .tv_sec) can hold the same positive range as | ||
// i64. This code is intentionally not compatible with 32-bit time_t! | ||
comptime { | ||
assert(std.math.maxInt(c.time_t) >= std.math.maxInt(i64)); | ||
} | ||
// Assert the caller limits "sec" to not saturate i64 as well | ||
assert(sec < std.math.maxInt(i64)); | ||
const ts = c.struct_timespec{ .tv_sec = @intCast(sec), .tv_nsec = @intCast(nsec) }; | ||
const rv = c.clock_nanosleep(c.CLOCK_REALTIME, c.TIMER_ABSTIME, &ts, null); | ||
if (rv != 0) | ||
return error.ClockNanosleepFailed; | ||
} | ||
|
||
test "clock_nanosleep sanity" { | ||
// Get current time and sleep until then, with the nsec truncated to zero. | ||
// Should return without any significant delay. | ||
var ts = posix.timespec{ .tv_sec = 0, .tv_nsec = 0 }; | ||
try posix.clock_gettime(posix.CLOCK.REALTIME, &ts); | ||
if (ts.tv_sec < 0) | ||
return error.TimeRange; | ||
try clock_nanosleep_real_abs(@intCast(ts.tv_sec), 0); | ||
} | ||
|
||
pub const GetOptIterator = struct { | ||
c_argc: c_int, | ||
c_argv: [*c]const [*c]u8, | ||
c_optstr: [*:0]const u8, | ||
|
||
pub fn init(argv: [][*:0]const u8, optstr: [:0]const u8) !GetOptIterator { | ||
if (argv.len > std.math.maxInt(c_int)) | ||
return error.TooManyCLIArguments; | ||
return .{ | ||
.c_argc = @intCast(argv.len), | ||
.c_argv = @ptrCast(argv), | ||
.c_optstr = optstr, | ||
}; | ||
} | ||
|
||
pub fn next(self: GetOptIterator) ?u8 { | ||
const opt = c.getopt(self.c_argc, self.c_argv, self.c_optstr); | ||
if (opt < 0) | ||
return null; | ||
return @truncate(@as(c_uint, @bitCast(opt))); | ||
} | ||
|
||
pub fn optarg(self: GetOptIterator) ?[*:0]const u8 { | ||
_ = self; | ||
return c.optarg; | ||
} | ||
|
||
pub fn optind(self: GetOptIterator) usize { | ||
_ = self; | ||
if (c.optind < 1) // JIC | ||
return 1; | ||
return @intCast(c.optind); | ||
} | ||
|
||
pub fn optopt(self: GetOptIterator) u8 { | ||
_ = self; | ||
return @bitCast(@as(i8, @truncate(c.optopt))); | ||
} | ||
}; | ||
|
||
//----------------- | ||
// libsodium stuff | ||
//----------------- | ||
|
||
pub const b2b_CONTEXTBYTES = c.crypto_kdf_blake2b_CONTEXTBYTES; | ||
pub const b2b_KEYBYTES = c.crypto_kdf_blake2b_KEYBYTES; | ||
pub const b2b_BYTES_MIN = c.crypto_kdf_blake2b_BYTES_MIN; | ||
pub const b2b_BYTES_MAX = c.crypto_kdf_blake2b_BYTES_MAX; | ||
|
||
pub fn sodium_init() !void { | ||
if (c.sodium_init() < 0) | ||
return error.SodiumInitFailed; | ||
} | ||
|
||
pub fn sodium_memzero(mem: []u8) void { | ||
c.sodium_memzero(@as(*anyopaque, @ptrCast(mem.ptr)), mem.len); | ||
} | ||
|
||
pub fn sodium_rand(mem: []u8) void { | ||
c.randombytes_buf(@as(*anyopaque, @ptrCast(mem.ptr)), mem.len); | ||
} | ||
|
||
pub fn b2b_derive_from_key(out: *[16]u8, len: usize, subkey: u64, ctx: *const [8]u8, key: *const [32]u8) !void { | ||
const rv = c.crypto_kdf_blake2b_derive_from_key(out, len, subkey, ctx, key); | ||
if (rv != 0) | ||
return error.Blake2BFailed; | ||
} | ||
|
||
test "blake2b KDF alg check" { | ||
var outbuf: [16]u8 = undefined; | ||
const ctx = [_]u8{ 't', 'o', 'f', 'u', 'r', 'k', 'e', 'y' }; | ||
const key = [_]u8{ | ||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, | ||
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, | ||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, | ||
0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, | ||
}; | ||
try b2b_derive_from_key(&outbuf, 16, 1234, &ctx, &key); | ||
const expect_out = [_]u8{ | ||
0x0E, 0xB0, 0x0F, 0x64, 0x3E, 0xB0, 0x4E, 0x60, | ||
0x9D, 0x5B, 0x23, 0x18, 0xEB, 0x67, 0x52, 0x31, | ||
}; | ||
try std.testing.expectEqualSlices(u8, &expect_out, &outbuf); | ||
} | ||
|
||
// A zig allocator wrapping simple use of sodium_malloc/free | ||
pub fn SodiumAllocator() type { | ||
return struct { | ||
pub fn allocator(self: *@This()) std.mem.Allocator { | ||
return .{ | ||
.ptr = self, | ||
.vtable = &.{ | ||
.alloc = sodium_alloc, | ||
.resize = std.mem.Allocator.noResize, | ||
.free = sodium_free, | ||
}, | ||
}; | ||
} | ||
|
||
fn sodium_alloc(ctx: *anyopaque, len: usize, log2_ptr_align: u8, ret_addr: usize) ?[*]u8 { | ||
_ = ctx; | ||
_ = log2_ptr_align; | ||
_ = ret_addr; | ||
return @as(?[*]u8, @ptrCast(c.sodium_malloc(len))); | ||
} | ||
|
||
fn sodium_free(ctx: *anyopaque, old_mem: []u8, log2_old_align_u8: u8, ret_addr: usize) void { | ||
_ = ctx; | ||
_ = log2_old_align_u8; | ||
_ = ret_addr; | ||
c.sodium_free(@as(*anyopaque, @ptrCast(old_mem))); | ||
} | ||
}; | ||
} |
Oops, something went wrong.