M build.zig +19 -10
@@ 1,19 1,28 @@
-const Builder = @import("std").build.Builder;
+const Build = @import("std").Build;
const std = @import("std");
-pub fn build(b: *Builder) void {
+pub fn build(b: *Build) void {
const target = b.standardTargetOptions(.{});
- const mode = b.standardReleaseOptions();
- const lib = b.addStaticLibrary("sodium", "src/sodium.zig");
- lib.setBuildMode(mode);
- lib.install();
+ const mode = b.standardOptimizeOption(.{});
+ const lib = b.addStaticLibrary(.{
+ .name = "sodium",
+ .root_source_file = .{ .path = "src/sodium.zig" },
+ .target = target,
+ .optimize = mode,
+ });
+ b.installArtifact(lib);
- var tests = b.addTest("src/sodium.zig");
- tests.setBuildMode(mode);
- tests.setTarget(target);
+ var tests = b.addTest(.{
+ .root_source_file = .{ .path = "src/sodium.zig" },
+ .target = target,
+ .optimize = mode,
+ });
+
tests.linkSystemLibrary("c");
tests.linkSystemLibrary("sodium");
+ const run_tests = b.addRunArtifact(tests);
+
const test_step = b.step("test", "Run library tests");
- test_step.dependOn(&tests.step);
+ test_step.dependOn(&run_tests.step);
}
M src/crypto_box.zig +9 -9
@@ 156,11 156,11 @@ pub fn open_easy(
pub const Sealer = struct {
const Self = @This();
pubkey: [PUBLICKEYBYTES]u8,
- allocator: *Allocator,
+ allocator: Allocator,
/// Initialize and return an instance. Note that pubkey is not a
/// pointer.
- pub fn init(allocator: *Allocator, pubkey: [PUBLICKEYBYTES]u8) Self {
+ pub fn init(allocator: Allocator, pubkey: [PUBLICKEYBYTES]u8) Self {
return Self{
.pubkey = pubkey,
.allocator = allocator,
@@ 171,7 171,7 @@ pub const Sealer = struct {
/// ciphertext is a pointer to memory allocated with the
/// allocator, and is owned by the caller.
pub fn encrypt(self: Self, msg: []const u8) ![]u8 {
- var ctext = try self.allocator.alloc(u8, SEALBYTES + msg.len);
+ const ctext = try self.allocator.alloc(u8, SEALBYTES + msg.len);
try seal(ctext, msg, &self.pubkey);
return ctext;
}
@@ 189,11 189,11 @@ pub const Unsealer = struct {
const Self = @This();
pubkey: [PUBLICKEYBYTES]u8,
privkey: [SECRETKEYBYTES]u8,
- allocator: *Allocator,
+ allocator: Allocator,
/// Initialize and return an instance.
pub fn init(
- allocator: *Allocator,
+ allocator: Allocator,
pubkey: [PUBLICKEYBYTES]u8,
privkey: [SECRETKEYBYTES]u8,
) Self {
@@ 208,7 208,7 @@ pub const Unsealer = struct {
/// caller owns the returned value and is responsible for freeing
/// it. It was allocated with the Unsealer's allocator.
pub fn decrypt(self: Self, ciphertext: []const u8) ![]u8 {
- var msg = try self.allocator.alloc(u8, ciphertext.len - SEALBYTES);
+ const msg = try self.allocator.alloc(u8, ciphertext.len - SEALBYTES);
try sealOpen(msg, ciphertext, &self.pubkey, &self.privkey);
return msg;
}
@@ 228,7 228,7 @@ test "seal" {
try sealOpen(clear[0..], ciphertext[0..], &pk, &sk);
- testing.expectEqualSlices(u8, msg, clear[0..]);
+ try testing.expectEqualSlices(u8, msg, clear[0..]);
}
test "sealer" {
@@ 247,7 247,7 @@ test "sealer" {
const clear = try unsealer.decrypt(cipher);
defer malloc.free(clear);
- testing.expectEqualSlices(u8, msg, clear);
+ try testing.expectEqualSlices(u8, msg, clear);
}
test "authenticated encryption" {
@@ 272,5 272,5 @@ test "authenticated encryption" {
// the nonce.
var rcvd: [encrypted.len - MACBYTES]u8 = undefined;
try open_easy(rcvd[0..], encrypted[0..], &nonce, &bob_pub, &alice_priv);
- testing.expectEqualSlices(u8, rcvd[0..], msg);
+ try testing.expectEqualSlices(u8, rcvd[0..], msg);
}
M src/crypto_sign.zig +1 -1
@@ 89,5 89,5 @@ test "signature" {
try sign(signed[0..], msg, &secret_key);
var received: [signed.len - BYTES]u8 = undefined;
try open(received[0..], signed[0..], &pub_key);
- testing.expectEqualSlices(u8, msg, received[0..]);
+ try testing.expectEqualSlices(u8, msg, received[0..]);
}
M src/mem.zig +44 -35
@@ 34,61 34,70 @@ pub fn zero(arr: []u8) void {
/// won't free any memory if you try to make a buffer smaller. It
/// does, however, mlock allocated pages and zero freed memory.
pub const sodium_allocator: *Allocator = &sodium_allocator_state;
+const vtable = .{
+ .alloc = sodiumAlloc,
+ .resize = sodiumResize,
+ .free = sodiumFree,
+};
+
+var ctxt: void = {};
+
var sodium_allocator_state = Allocator{
- .allocFn = sodiumAlloc,
- .resizeFn = sodiumResize,
+ .vtable = &vtable,
+ .ptr = &ctxt,
};
+fn sodiumFree(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void {
+ _ = ctx;
+ _ = buf_align;
+ _ = ret_addr;
+ c.sodium_free(buf.ptr);
+}
+
fn sodiumAlloc(
- self: *Allocator,
+ ctx: *anyopaque,
len: usize,
- ptr_align: u29,
- len_align: u29,
+ ptr_align: u8,
ret_addr: usize,
-) Error![]u8 {
+) ?[*]u8 {
+ _ = ctx;
+ _ = ret_addr;
var bytes = len;
- const alignment = std.math.max(ptr_align, len_align);
+ const alignment = ptr_align;
if (alignment != 0) {
- bytes = std.mem.alignForward(len, alignment);
+ bytes = std.mem.alignForward(usize, len, alignment);
}
// Zig helps prevent access past the end of the slice, but that's
// the only mechanism in cases where len_align < ptr_align. Sodium
// will catch writes past the end of the allocated buffer.
- const slice_len = if (len_align >= ptr_align)
- bytes
- else if (len_align == 0)
- len
- else
- std.mem.alignForward(len, len_align);
-
const allocated = c.sodium_malloc(bytes);
if (allocated) |ptr| {
- return @ptrCast([*]u8, ptr)[0..slice_len];
+ return @as([*]u8, @ptrCast(ptr));
} else {
- return Error.OutOfMemory;
+ return null;
}
}
fn sodiumResize(
- self: *Allocator,
+ ctx: *anyopaque,
buf: []u8,
- buf_align: u29,
+ buf_align: u8,
new_len: usize,
- len_align: u29,
ret_addr: usize,
-) Error!usize {
+) bool {
+ _ = ret_addr;
+ _ = buf_align;
+ _ = ctx;
if (new_len > buf.len) {
- return Error.OutOfMemory;
+ return false;
}
if (new_len == 0) {
c.sodium_free(buf.ptr);
- return 0;
+ return true;
}
- const real_new_len = std.mem.alignAllocLen(buf.len, new_len, len_align);
-
- zero(buf[real_new_len..]);
- return real_new_len;
+ zero(buf[new_len..]);
+ return true;
}
test "mem lock" {
@@ 97,7 106,7 @@ test "mem lock" {
try sodium.init();
try mlock(data[0..]);
try munlock(data[0..]);
- testing.expectEqual(data[0], 0);
+ try testing.expectEqual(data[0], 0);
}
test "mem zero" {
@@ 106,7 115,7 @@ test "mem zero" {
try sodium.init();
zero(data[0..]);
- testing.expectEqualSlices(u8, data[0..], zeroes[0..]);
+ try testing.expectEqualSlices(u8, data[0..], zeroes[0..]);
}
test "sodium allocator" {
@@ 117,15 126,15 @@ test "sodium allocator" {
var other_slice = try sodium_allocator.alloc(u8, buf_size);
defer sodium_allocator.free(other_slice);
- for (slice) |_, idx| {
- slice[idx] = @intCast(u8, idx);
- other_slice[idx] = @intCast(u8, idx) ^ 0xff;
+ for (slice, 0..) |_, idx| {
+ slice[idx] = @as(u8, @intCast(idx));
+ other_slice[idx] = @as(u8, @intCast(idx)) ^ 0xff;
}
var sum: usize = 0;
for (slice) |val| {
sum += val;
}
- testing.expectEqual(sum, 1225);
+ try testing.expectEqual(sum, 1225);
}
// expectIllegalBehavior Stolen from https://github.com/fengb/zee_alloc
@@ 145,7 154,7 @@ fn expectIllegalBehavior(context: anytyp
const child_pid = try std.os.fork();
if (child_pid == 0) {
- const null_fd = std.os.openZ("/dev/null", std.os.O_RDWR, 0) catch {
+ const null_fd = std.posix.openZ("/dev/null", .{.ACCMODE=.RDWR}, 0) catch {
std.debug.print("Cannot open /dev/null\n", .{});
std.os.exit(0);
};
@@ 166,7 175,7 @@ fn expectIllegalBehavior(context: anytyp
}
fn accessPastEnd(slice: []u8) void {
- const ptr = @ptrCast([*]u8, slice.ptr);
+ const ptr = @as([*]u8, @ptrCast(slice.ptr));
ptr[slice.len] = 1;
}
M src/randombytes.zig +3 -3
@@ 37,13 37,13 @@ pub fn bufDeterministic(buffer: []u8, se
/// Deallocate global resources used by the prng. Probably don't call
/// this.
-pub fn close(void) void {
+pub fn close() void {
c.randombytes_close();
}
/// Reseed the prng, if it supports this operation. This should not be
/// necessary, even after fork().
-pub fn stir(void) void {
+pub fn stir() void {
c.randombytes_stir();
}
@@ 56,5 56,5 @@ test "random numbers" {
const seed: [SEEDBYTES]u8 = [_]u8{0} ** SEEDBYTES;
try bufDeterministic(buffer[0..], &seed);
const nonrandom = [_]u8{ 0xa1, 0x1f, 0x8f, 0x12, 0xd0 };
- testing.expectEqualSlices(u8, buffer[0..], nonrandom[0..]);
+ try testing.expectEqualSlices(u8, buffer[0..], nonrandom[0..]);
}
M src/secretstream.zig +23 -19
@@ 9,7 9,7 @@ const c = @cImport({
@cInclude("sodium.h");
});
-const Tag = enum {
+const Tag = enum(u8) {
MESSAGE = c.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE,
PUSH = c.crypto_secretstream_xchacha20poly1305_TAG_PUSH,
REKEY = c.crypto_secretstream_xchacha20poly1305_TAG_REKEY,
@@ 65,7 65,7 @@ pub fn push(
message.len,
if (additional_data) |ad| ad.ptr else null,
if (additional_data) |ad| ad.len else 0,
- @enumToInt(tag),
+ @intFromEnum(tag),
);
if (clen > ciphertext.len) {
// I don't trust guarantees that aren't in the source code.
@@ 107,7 107,7 @@ pub fn pull(
state,
message.ptr,
&mlen,
- if (tag) |t| @ptrCast(*u8, t) else null,
+ if (tag) |t| @as(*u8, @ptrCast(t)) else null,
ciphertext.ptr,
ciphertext.len,
if (additional_data) |ad| ad.ptr else null,
@@ 156,7 156,7 @@ pub fn ChunkEncrypter(chunk_size: usize,
// There's probably a better way to do this than to copy
// the whole array over to the stack, byte by byte.
var m: [chunk_size]u8 = [_]u8{0} ** chunk_size;
- for (msg) |val, idx| {
+ for (msg, 0..) |val, idx| {
m[idx] = val;
}
var ctxt: [chunk_size + ABYTES]u8 = undefined;
@@ 171,7 171,9 @@ pub fn ChunkEncrypter(chunk_size: usize,
}
/// Free up all held resources.
- pub fn deinit(self: *Self) void {}
+ pub fn deinit(self: *Self) void {
+ _ = self;
+ }
};
}
@@ 229,7 231,9 @@ pub fn ChunkDecrypter(
}
/// Free all held resources.
- pub fn deinit(self: *Self) void {}
+ pub fn deinit(self: *Self) void {
+ _ = self;
+ }
};
}
@@ 252,24 256,23 @@ test "stream" {
var clear2: [ciphertext2.len - ABYTES]u8 = undefined;
try initPull(&state, &hdr, &key);
const len = try pull(&state, clear[0..], null, ciphertext[0..cl], null);
- testing.expectEqual(msg.len, len);
+ try testing.expectEqual(msg.len, len);
const len2 = try pull(&state, clear2[0..], null, ciphertext2[0..c2], null);
- testing.expectEqual(msg2.len, len2);
- testing.expectEqualSlices(u8, msg[0..], clear[0..]);
- testing.expectEqualSlices(u8, msg2[0..], clear2[0..]);
+ try testing.expectEqual(msg2.len, len2);
+ try testing.expectEqualSlices(u8, msg[0..], clear[0..]);
+ try testing.expectEqualSlices(u8, msg2[0..], clear2[0..]);
}
test "chunks" {
try sodium.init();
const msg = "This message is longer than my chunk size!!";
- const malloc = std.heap.c_allocator;
var key: [KEYBYTES]u8 = undefined;
keygen(&key);
const chunk_size = 4;
var buf: [chunk_size * 1024]u8 = undefined;
var stream = std.io.fixedBufferStream(&buf);
- const out = stream.outStream();
+ const out = stream.writer();
const Encrypter = ChunkEncrypter(chunk_size, @TypeOf(out));
var encrypter = try Encrypter.init(out, key);
defer encrypter.deinit();
@@ 283,27 286,28 @@ test "chunks" {
}
const cipherlen = try stream.getPos();
- var cipherstream = std.io.fixedBufferStream(buf[0..cipherlen]).inStream();
+ var cipherstream = std.io.fixedBufferStream(buf[0..cipherlen]);
+ const in = cipherstream.reader();
var decrypted: [chunk_size * 1024]u8 = undefined;
var cleartext = std.io.fixedBufferStream(&decrypted);
- var clearstream = cleartext.outStream();
+ const clearstream = cleartext.writer();
const Decrypter = ChunkDecrypter(
chunk_size,
- @TypeOf(cipherstream),
+ @TypeOf(in),
@TypeOf(clearstream),
);
- var decrypter = try Decrypter.init(cipherstream, clearstream, key);
+ var decrypter = try Decrypter.init(in, clearstream, key);
defer decrypter.deinit();
while (chunk_count > 0) {
decrypter.pullChunk() catch |err| {
- std.debug.warn("Unexpected error: {}\n", .{err});
+ std.log.warn("Unexpected error: {}\n", .{err});
return err;
} orelse break;
chunk_count -= 1;
}
const len = try cleartext.getPos();
- testing.expectEqual(len, msg.len);
- testing.expectEqualSlices(u8, msg[0..], decrypted[0..len]);
+ try testing.expectEqual(len, msg.len);
+ try testing.expectEqualSlices(u8, msg[0..], decrypted[0..len]);
}